From 97e01009d69b8fbebfebf68f51e3d126d0ed43fc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 30 Nov 2022 19:47:05 +0100 Subject: Merging upstream version 1.37.0. Signed-off-by: Daniel Baumann --- .github/CODEOWNERS | 22 +- .github/codeql/python-config.yml | 10 + .github/data/distros.yml | 29 +- .github/scripts/pkg-test.sh | 8 +- .github/workflows/add-to-project.yml | 4 +- .github/workflows/build.yml | 87 +- .github/workflows/codeql.yml | 117 + .github/workflows/packaging.yml | 11 +- .github/workflows/review.yml | 23 +- .github/workflows/tests.yml | 56 - .gitignore | 1 + .gitmodules | 2 +- .lgtm.yml | 2 + CHANGELOG.md | 563 +-- CMakeLists.txt | 336 +- Makefile.am | 244 +- aclk/README.md | 8 +- aclk/aclk.c | 209 +- aclk/aclk.h | 21 +- aclk/aclk_alarm_api.c | 3 - aclk/aclk_api.c | 88 - aclk/aclk_api.h | 45 - aclk/aclk_capas.c | 47 + aclk/aclk_capas.h | 14 + aclk/aclk_charts_api.c | 77 - aclk/aclk_charts_api.h | 22 - aclk/aclk_contexts_api.c | 18 + aclk/aclk_contexts_api.h | 2 + aclk/aclk_otp.c | 11 +- aclk/aclk_query.c | 32 +- aclk/aclk_query.h | 2 +- aclk/aclk_query_queue.c | 16 - aclk/aclk_rx_msgs.c | 70 +- aclk/aclk_stats.c | 33 +- aclk/aclk_tx_msgs.c | 94 +- aclk/aclk_util.c | 1 + aclk/helpers/mqtt_wss_pal.h | 19 + aclk/helpers/ringbuffer_pal.h | 11 + aclk/schema-wrappers/capability.cc | 2 +- aclk/schema-wrappers/capability.h | 2 +- aclk/schema-wrappers/chart_config.cc | 105 - aclk/schema-wrappers/chart_config.h | 50 - aclk/schema-wrappers/chart_stream.cc | 337 -- aclk/schema-wrappers/chart_stream.h | 121 - aclk/schema-wrappers/connection.cc | 10 +- aclk/schema-wrappers/connection.h | 2 +- aclk/schema-wrappers/node_connection.cc | 4 +- aclk/schema-wrappers/node_connection.h | 2 +- aclk/schema-wrappers/node_creation.cc | 6 +- aclk/schema-wrappers/node_creation.h | 6 +- aclk/schema-wrappers/node_info.cc | 4 +- aclk/schema-wrappers/node_info.h | 54 +- aclk/schema-wrappers/proto_2_json.cc | 16 - aclk/schema-wrappers/schema_wrappers.h | 2 - build/subst.inc | 2 + claim/README.md | 18 +- claim/claim.c | 15 +- claim/netdata-claim.sh.in | 2 +- cli/cli.c | 2 +- collectors/COLLECTORS.md | 5 +- collectors/Makefile.am | 1 - collectors/REFERENCE.md | 5 - collectors/all.h | 10 +- collectors/apps.plugin/apps_groups.conf | 119 +- collectors/apps.plugin/apps_plugin.c | 915 ++++- collectors/cgroups.plugin/cgroup-name.sh | 5 +- collectors/cgroups.plugin/cgroup-network-helper.sh | 1 + collectors/cgroups.plugin/cgroup-network.c | 12 +- collectors/cgroups.plugin/sys_fs_cgroup.c | 377 +- collectors/cgroups.plugin/tests/test_doubles.c | 18 +- collectors/charts.d.plugin/sensors/README.md | 15 +- collectors/checks.plugin/Makefile.am | 8 - collectors/checks.plugin/README.md | 10 - collectors/checks.plugin/plugin_checks.c | 125 - collectors/cups.plugin/README.md | 4 +- collectors/cups.plugin/cups_plugin.c | 13 +- collectors/diskspace.plugin/plugin_diskspace.c | 28 +- collectors/ebpf.plugin/README.md | 19 + collectors/ebpf.plugin/ebpf.c | 446 ++- collectors/ebpf.plugin/ebpf.d.conf | 11 +- collectors/ebpf.plugin/ebpf.d/cachestat.conf | 6 + collectors/ebpf.plugin/ebpf.d/dcstat.conf | 6 + collectors/ebpf.plugin/ebpf.d/fd.conf | 4 +- collectors/ebpf.plugin/ebpf.d/process.conf | 6 + collectors/ebpf.plugin/ebpf.d/vfs.conf | 4 +- collectors/ebpf.plugin/ebpf.h | 59 +- collectors/ebpf.plugin/ebpf_apps.h | 20 +- collectors/ebpf.plugin/ebpf_cachestat.c | 141 +- collectors/ebpf.plugin/ebpf_cachestat.h | 3 +- collectors/ebpf.plugin/ebpf_cgroup.c | 27 +- collectors/ebpf.plugin/ebpf_cgroup.h | 8 +- collectors/ebpf.plugin/ebpf_dcstat.c | 141 +- collectors/ebpf.plugin/ebpf_dcstat.h | 5 +- collectors/ebpf.plugin/ebpf_disk.c | 97 +- collectors/ebpf.plugin/ebpf_disk.h | 2 +- collectors/ebpf.plugin/ebpf_fd.c | 419 ++- collectors/ebpf.plugin/ebpf_fd.h | 5 +- collectors/ebpf.plugin/ebpf_filesystem.c | 94 +- collectors/ebpf.plugin/ebpf_filesystem.h | 2 +- collectors/ebpf.plugin/ebpf_hardirq.c | 82 +- collectors/ebpf.plugin/ebpf_hardirq.h | 2 +- collectors/ebpf.plugin/ebpf_mdflush.c | 72 +- collectors/ebpf.plugin/ebpf_mdflush.h | 2 +- collectors/ebpf.plugin/ebpf_mount.c | 85 +- collectors/ebpf.plugin/ebpf_mount.h | 2 +- collectors/ebpf.plugin/ebpf_oomkill.c | 41 +- collectors/ebpf.plugin/ebpf_oomkill.h | 4 +- collectors/ebpf.plugin/ebpf_process.c | 203 +- collectors/ebpf.plugin/ebpf_shm.c | 149 +- collectors/ebpf.plugin/ebpf_shm.h | 4 +- collectors/ebpf.plugin/ebpf_socket.c | 121 +- collectors/ebpf.plugin/ebpf_socket.h | 10 +- collectors/ebpf.plugin/ebpf_softirq.c | 83 +- collectors/ebpf.plugin/ebpf_softirq.h | 2 +- collectors/ebpf.plugin/ebpf_swap.c | 138 +- collectors/ebpf.plugin/ebpf_swap.h | 4 +- collectors/ebpf.plugin/ebpf_sync.c | 92 +- collectors/ebpf.plugin/ebpf_sync.h | 2 +- collectors/ebpf.plugin/ebpf_vfs.c | 483 ++- collectors/ebpf.plugin/ebpf_vfs.h | 21 +- collectors/fping.plugin/README.md | 20 +- collectors/freebsd.plugin/freebsd_devstat.c | 50 +- collectors/freebsd.plugin/freebsd_getifaddrs.c | 48 +- collectors/freebsd.plugin/freebsd_getmntinfo.c | 10 +- collectors/freebsd.plugin/freebsd_ipfw.c | 31 +- collectors/freebsd.plugin/freebsd_kstat_zfs.c | 8 - collectors/freebsd.plugin/freebsd_sysctl.c | 276 +- collectors/freebsd.plugin/plugin_freebsd.h | 70 +- collectors/freeipmi.plugin/README.md | 30 +- collectors/idlejitter.plugin/plugin_idlejitter.c | 10 +- collectors/macos.plugin/macos_fw.c | 46 +- collectors/macos.plugin/macos_mach_smi.c | 16 +- collectors/macos.plugin/macos_sysctl.c | 153 +- collectors/macos.plugin/plugin_macos.h | 6 +- collectors/plugins.d/README.md | 86 +- collectors/plugins.d/plugins_d.c | 128 +- collectors/plugins.d/plugins_d.h | 78 +- collectors/plugins.d/pluginsd_parser.c | 1476 +++++--- collectors/plugins.d/pluginsd_parser.h | 33 +- collectors/proc.plugin/ipc.c | 34 +- collectors/proc.plugin/plugin_proc.c | 6 +- collectors/proc.plugin/plugin_proc.h | 77 +- collectors/proc.plugin/proc_diskstats.c | 162 +- collectors/proc.plugin/proc_interrupts.c | 15 +- collectors/proc.plugin/proc_loadavg.c | 19 +- collectors/proc.plugin/proc_mdstat.c | 40 +- collectors/proc.plugin/proc_meminfo.c | 35 - collectors/proc.plugin/proc_net_dev.c | 121 +- collectors/proc.plugin/proc_net_ip_vs_stats.c | 10 - collectors/proc.plugin/proc_net_netstat.c | 3405 +++++++++++++++--- collectors/proc.plugin/proc_net_rpc_nfs.c | 15 - collectors/proc.plugin/proc_net_rpc_nfsd.c | 133 +- collectors/proc.plugin/proc_net_sctp_snmp.c | 6 - collectors/proc.plugin/proc_net_snmp.c | 1130 ------ collectors/proc.plugin/proc_net_snmp6.c | 1293 ------- collectors/proc.plugin/proc_net_sockstat.c | 21 +- collectors/proc.plugin/proc_net_sockstat6.c | 5 - collectors/proc.plugin/proc_net_softnet_stat.c | 2 - collectors/proc.plugin/proc_net_stat_conntrack.c | 10 +- collectors/proc.plugin/proc_net_stat_synproxy.c | 3 - collectors/proc.plugin/proc_net_wireless.c | 15 +- collectors/proc.plugin/proc_pagetypeinfo.c | 23 +- collectors/proc.plugin/proc_pressure.c | 6 +- collectors/proc.plugin/proc_pressure.h | 2 +- collectors/proc.plugin/proc_self_mountinfo.c | 3 +- collectors/proc.plugin/proc_self_mountinfo.h | 10 +- collectors/proc.plugin/proc_softirqs.c | 15 +- collectors/proc.plugin/proc_spl_kstat_zfs.c | 9 +- collectors/proc.plugin/proc_stat.c | 28 +- .../proc_sys_kernel_random_entropy_avail.c | 2 - collectors/proc.plugin/proc_uptime.c | 4 - collectors/proc.plugin/proc_vmstat.c | 5 - collectors/proc.plugin/sys_block_zram.c | 87 +- collectors/proc.plugin/sys_class_infiniband.c | 21 +- collectors/proc.plugin/sys_class_power_supply.c | 6 +- .../proc.plugin/sys_devices_system_edac_mc.c | 8 +- collectors/proc.plugin/sys_devices_system_node.c | 3 +- collectors/proc.plugin/sys_fs_btrfs.c | 8 +- collectors/proc.plugin/sys_kernel_mm_ksm.c | 7 - collectors/proc.plugin/zfs_common.c | 262 +- collectors/python.d.plugin/Makefile.am | 3 +- collectors/python.d.plugin/alarms/README.md | 3 + collectors/python.d.plugin/alarms/alarms.chart.py | 6 + collectors/python.d.plugin/alarms/alarms.conf | 3 + collectors/python.d.plugin/nginx_plus/Makefile.inc | 13 - collectors/python.d.plugin/nginx_plus/README.md | 165 - .../python.d.plugin/nginx_plus/nginx_plus.chart.py | 487 --- .../python.d.plugin/nginx_plus/nginx_plus.conf | 85 - collectors/python.d.plugin/nvidia_smi/README.md | 2 + .../python.d.plugin/nvidia_smi/nvidia_smi.chart.py | 27 +- collectors/python.d.plugin/pandas/Makefile.inc | 13 + collectors/python.d.plugin/pandas/README.md | 92 + collectors/python.d.plugin/pandas/pandas.chart.py | 89 + collectors/python.d.plugin/pandas/pandas.conf | 191 + collectors/python.d.plugin/postfix/README.md | 27 +- collectors/python.d.plugin/postgres/Makefile.inc | 13 - collectors/python.d.plugin/postgres/README.md | 145 - .../python.d.plugin/postgres/postgres.chart.py | 1436 -------- collectors/python.d.plugin/postgres/postgres.conf | 134 - collectors/python.d.plugin/python.d.conf | 3 +- collectors/python.d.plugin/python.d.plugin.in | 17 +- .../bases/FrameworkServices/SimpleService.py | 18 +- .../python.d.plugin/python_modules/bases/charts.py | 32 +- collectors/python.d.plugin/rabbitmq/README.md | 22 + collectors/python.d.plugin/sensors/README.md | 2 + .../python.d.plugin/sensors/sensors.chart.py | 123 +- collectors/python.d.plugin/tor/README.md | 2 + collectors/python.d.plugin/tor/tor.conf | 2 + collectors/statsd.plugin/statsd.c | 600 ++-- collectors/tc.plugin/plugin_tc.c | 792 ++-- collectors/timex.plugin/plugin_timex.c | 10 +- collectors/xenstat.plugin/xenstat_plugin.c | 32 +- config.cmake.h.in | 56 + configure.ac | 58 +- contrib/debian/netdata.postinst | 2 +- coverity-scan.sh | 2 +- daemon/README.md | 7 +- daemon/analytics.c | 86 +- daemon/analytics.h | 24 +- daemon/buildinfo.c | 23 +- daemon/buildinfo.h | 8 +- daemon/commands.c | 4 +- daemon/commands.h | 4 +- daemon/common.h | 2 +- daemon/daemon.h | 10 +- daemon/global_statistics.c | 1711 +++++++-- daemon/global_statistics.h | 26 +- daemon/main.c | 91 +- daemon/main.h | 8 +- daemon/service.c | 266 +- daemon/signals.c | 6 +- daemon/signals.h | 12 +- daemon/static_threads.c | 74 +- daemon/static_threads.h | 6 + daemon/static_threads_linux.c | 36 +- daemon/static_threads_macos.c | 10 +- daemon/system-info.sh | 3 +- daemon/unit_test.c | 287 +- daemon/unit_test.h | 26 +- database/engine/Makefile.am | 1 - database/engine/datafile.c | 31 +- database/engine/datafile.h | 22 +- database/engine/journalfile.c | 113 +- database/engine/journalfile.h | 22 +- database/engine/metadata_log/Makefile.am | 8 - database/engine/metadata_log/compaction.c | 86 - database/engine/metadata_log/compaction.h | 14 - database/engine/metadata_log/logfile.c | 447 --- database/engine/metadata_log/logfile.h | 39 - database/engine/metadata_log/metadatalog.h | 28 - database/engine/metadata_log/metadatalogapi.c | 39 - database/engine/metadata_log/metadatalogapi.h | 12 - database/engine/metadata_log/metadatalogprotocol.h | 53 - database/engine/metadata_log/metalogpluginsd.c | 140 - database/engine/metadata_log/metalogpluginsd.h | 33 - database/engine/pagecache.c | 256 +- database/engine/pagecache.h | 131 +- database/engine/rrddiskprotocol.h | 4 +- database/engine/rrdengine.c | 188 +- database/engine/rrdengine.h | 50 +- database/engine/rrdengineapi.c | 606 ++-- database/engine/rrdengineapi.h | 67 +- database/engine/rrdenginelib.c | 57 +- database/engine/rrdenginelib.h | 14 +- database/engine/rrdenglocking.h | 10 +- database/ram/rrddim_mem.c | 97 +- database/ram/rrddim_mem.h | 32 +- database/rrd.c | 12 + database/rrd.h | 1159 +++--- database/rrdcalc.c | 1120 +++--- database/rrdcalc.h | 246 +- database/rrdcalctemplate.c | 253 +- database/rrdcalctemplate.h | 85 +- database/rrdcontext.c | 3768 +++++++++++++------- database/rrdcontext.h | 208 +- database/rrddim.c | 758 ++-- database/rrddimvar.c | 291 +- database/rrddimvar.h | 45 +- database/rrdfamily.c | 87 +- database/rrdfunctions.c | 758 ++++ database/rrdfunctions.h | 35 + database/rrdhost.c | 1159 +++--- database/rrdlabels.c | 141 +- database/rrdset.c | 1817 +++++----- database/rrdsetvar.c | 317 +- database/rrdsetvar.h | 36 +- database/rrdvar.c | 304 +- database/rrdvar.h | 83 +- database/sqlite/sqlite_aclk.c | 323 +- database/sqlite/sqlite_aclk.h | 98 +- database/sqlite/sqlite_aclk_alert.c | 117 +- database/sqlite/sqlite_aclk_alert.h | 2 +- database/sqlite/sqlite_aclk_chart.c | 1311 ------- database/sqlite/sqlite_aclk_chart.h | 71 - database/sqlite/sqlite_aclk_node.c | 41 +- database/sqlite/sqlite_context.c | 92 +- database/sqlite/sqlite_context.h | 20 +- database/sqlite/sqlite_db_migration.c | 80 +- database/sqlite/sqlite_functions.c | 1859 ++-------- database/sqlite/sqlite_functions.h | 122 +- database/sqlite/sqlite_health.c | 217 +- database/sqlite/sqlite_health.h | 16 +- database/sqlite/sqlite_metadata.c | 1580 ++++++++ database/sqlite/sqlite_metadata.h | 28 + database/storage_engine.c | 46 +- database/storage_engine.h | 24 +- docs/Demo-Sites.md | 2 +- docs/collect/system-metrics.md | 10 +- docs/dashboard/interact-charts.mdx | 57 + docs/get-started.mdx | 4 +- docs/guides/configure/performance.md | 48 +- .../export/export-netdata-metrics-graphite.md | 2 +- docs/guides/monitor/lamp-stack.md | 2 +- docs/guides/monitor/pi-hole-raspberry-pi.md | 2 +- docs/guides/python-collector.md | 10 +- docs/guides/step-by-step/step-00.md | 2 +- docs/guides/step-by-step/step-09.md | 16 +- .../troubleshooting-agent-with-cloud-connection.md | 10 +- docs/netdata-for-IoT.md | 2 +- docs/store/change-metrics-storage.md | 6 +- exporting/check_filters.c | 14 +- exporting/exporting_engine.c | 2 +- exporting/exporting_engine.h | 4 +- exporting/graphite/graphite.c | 20 +- exporting/json/json.c | 46 +- exporting/opentsdb/opentsdb.c | 38 +- exporting/process_data.c | 39 +- exporting/prometheus/README.md | 2 +- exporting/prometheus/prometheus.c | 122 +- exporting/prometheus/prometheus.h | 6 +- exporting/prometheus/remote_write/remote_write.c | 47 +- exporting/prometheus/remote_write/remote_write.h | 2 +- exporting/send_data.c | 6 +- exporting/send_internal_metrics.c | 34 +- exporting/tests/exporting_fixtures.c | 95 +- exporting/tests/netdata_doubles.c | 40 +- exporting/tests/test_exporting_engine.c | 217 +- exporting/tests/test_exporting_engine.h | 3 +- health/Makefile.am | 3 + health/REFERENCE.md | 38 +- health/health.c | 1431 +++++--- health/health.d/dns_query.conf | 17 +- health/health.d/go.d.plugin.conf | 2 +- health/health.d/ml.conf | 21 +- health/health.d/mysql.conf | 34 +- health/health.d/nvme.conf | 15 + health/health.d/pihole.conf | 23 +- health/health.d/ping.conf | 50 + health/health.d/postgres.conf | 214 ++ health/health.d/python.d.plugin.conf | 2 +- health/health.d/redis.conf | 29 + health/health.d/systemdunits.conf | 105 +- health/health.d/tcp_resets.conf | 4 +- health/health.d/timex.conf | 2 +- health/health.h | 116 +- health/health_config.c | 705 ++-- health/health_json.c | 157 +- health/health_log.c | 316 +- health/notifications/alarm-notify.sh.in | 60 +- health/notifications/health_alarm_notify.conf | 5 + libnetdata/Makefile.am | 1 + .../adaptive_resortable_list.h | 16 +- libnetdata/arrayalloc/arrayalloc.c | 340 +- libnetdata/arrayalloc/arrayalloc.h | 27 +- libnetdata/buffer/buffer.c | 60 + libnetdata/buffer/buffer.h | 42 +- libnetdata/circular_buffer/circular_buffer.c | 11 + libnetdata/circular_buffer/circular_buffer.h | 13 +- libnetdata/clocks/clocks.c | 31 +- libnetdata/clocks/clocks.h | 54 +- libnetdata/config/appconfig.c | 10 +- libnetdata/config/appconfig.h | 56 +- libnetdata/dictionary/README.md | 116 +- libnetdata/dictionary/dictionary.c | 3688 ++++++++++++------- libnetdata/dictionary/dictionary.h | 305 +- libnetdata/ebpf/ebpf.c | 202 +- libnetdata/ebpf/ebpf.h | 96 +- libnetdata/eval/eval.c | 79 +- libnetdata/eval/eval.h | 13 +- libnetdata/health/health.h | 10 +- libnetdata/inlined.h | 77 +- libnetdata/json/json.c | 5 +- libnetdata/libnetdata.c | 576 ++- libnetdata/libnetdata.h | 240 +- libnetdata/locks/README.md | 6 +- libnetdata/locks/locks.c | 31 + libnetdata/locks/locks.h | 62 +- libnetdata/log/log.c | 166 +- libnetdata/log/log.h | 45 +- libnetdata/onewayalloc/onewayalloc.h | 16 +- libnetdata/os.h | 16 +- libnetdata/popen/popen.c | 441 ++- libnetdata/popen/popen.h | 40 +- libnetdata/procfile/procfile.c | 42 +- libnetdata/procfile/procfile.h | 16 +- libnetdata/required_dummies.h | 9 +- libnetdata/simple_pattern/simple_pattern.c | 4 +- libnetdata/simple_pattern/simple_pattern.h | 16 +- libnetdata/socket/security.c | 76 +- libnetdata/socket/security.h | 16 +- libnetdata/socket/socket.c | 169 +- libnetdata/socket/socket.h | 78 +- libnetdata/statistical/statistical.h | 30 +- libnetdata/storage_number/storage_number.c | 3 +- libnetdata/string/Makefile.am | 8 + libnetdata/string/README.md | 20 + libnetdata/string/string.c | 595 ++++ libnetdata/string/string.h | 30 + libnetdata/threads/threads.c | 35 +- libnetdata/threads/threads.h | 29 +- libnetdata/url/url.h | 18 +- libnetdata/worker_utilization/worker_utilization.c | 218 +- libnetdata/worker_utilization/worker_utilization.h | 39 +- ml/ADCharts.cc | 233 ++ ml/ADCharts.h | 23 + ml/BitBufferCounter.cc | 29 - ml/BitBufferCounter.h | 54 - ml/BitRateWindow.cc | 75 - ml/BitRateWindow.h | 170 - ml/Config.cc | 52 +- ml/Config.h | 11 +- ml/Database.cc | 127 - ml/Database.h | 131 - ml/Dimension.cc | 76 +- ml/Dimension.h | 165 +- ml/Host.cc | 374 +- ml/Host.h | 51 +- ml/KMeans.cc | 43 + ml/KMeans.h | 41 + ml/Makefile.am | 8 - ml/Query.h | 27 +- ml/README.md | 2 - ml/SamplesBuffer.cc | 150 + ml/SamplesBuffer.h | 146 + ml/SamplesBufferTests.cc | 146 + ml/Tests.cc | 301 -- ml/kmeans/KMeans.cc | 55 - ml/kmeans/KMeans.h | 34 - ml/kmeans/Makefile.am | 4 - ml/kmeans/SamplesBuffer.cc | 150 - ml/kmeans/SamplesBuffer.h | 146 - ml/kmeans/Tests.cc | 143 - ml/ml-dummy.c | 35 +- ml/ml-private.h | 2 +- ml/ml.cc | 99 +- ml/ml.h | 13 +- netdata-installer.sh | 67 +- netdata.spec.in | 2 +- packaging/PLATFORM_SUPPORT.md | 16 +- packaging/building-native-packages-locally.md | 106 + packaging/current_libbpf.checksums | 2 +- packaging/current_libbpf.version | 2 +- packaging/docker/README.md | 5 + packaging/ebpf-co-re.checksums | 2 +- packaging/ebpf-co-re.version | 2 +- packaging/ebpf.checksums | 6 +- packaging/ebpf.version | 2 +- packaging/go.d.checksums | 33 +- packaging/go.d.version | 2 +- packaging/installer/README.md | 4 +- packaging/installer/UNINSTALL.md | 52 +- packaging/installer/dependencies/alpine.sh | 1 - packaging/installer/dependencies/arch.sh | 1 - packaging/installer/dependencies/centos.sh | 1 - packaging/installer/dependencies/debian.sh | 1 - packaging/installer/dependencies/fedora.sh | 1 - packaging/installer/dependencies/freebsd.sh | 1 - packaging/installer/dependencies/gentoo.sh | 1 - packaging/installer/dependencies/ol.sh | 1 - packaging/installer/dependencies/opensuse.sh | 1 - packaging/installer/dependencies/rockylinux.sh | 1 - packaging/installer/dependencies/ubuntu.sh | 1 - packaging/installer/functions.sh | 239 +- packaging/installer/install-required-packages.sh | 85 +- packaging/installer/kickstart.sh | 251 +- packaging/installer/methods/kickstart.md | 4 +- packaging/installer/methods/macos.md | 4 +- packaging/installer/methods/manual.md | 3 +- packaging/installer/netdata-updater.sh | 25 +- packaging/libbpf.checksums | 2 +- packaging/libbpf.version | 2 +- packaging/makeself/install-or-update.sh | 2 +- packaging/version | 2 +- parser/parser.c | 160 +- parser/parser.h | 109 +- registry/registry.c | 17 +- registry/registry.h | 26 +- registry/registry_db.c | 11 +- registry/registry_init.c | 18 +- registry/registry_internals.h | 24 +- registry/registry_machine.c | 10 +- registry/registry_machine.h | 10 +- registry/registry_person.h | 20 +- registry/registry_url.h | 12 +- spawn/spawn.c | 4 +- spawn/spawn.h | 2 +- streaming/README.md | 3 +- streaming/compression.c | 301 +- streaming/receiver.c | 708 ++-- streaming/replication.c | 1407 ++++++++ streaming/replication.h | 33 + streaming/rrdpush.c | 613 ++-- streaming/rrdpush.h | 232 +- streaming/sender.c | 1255 ++++--- streaming/stream.conf | 78 +- system/Makefile.am | 14 +- system/install-service.sh.in | 786 ++++ system/netdata-freebsd.in | 1 + system/netdata-init-d.in | 1 + system/netdata-lsb.in | 2 + system/netdata-openrc.in | 64 +- system/netdata.service.in | 12 +- tests/Makefile.am | 32 +- .../alarm_repetition/netdata.conf_with_repetition | 2 +- .../netdata.conf_without_repetition | 2 +- tests/profile/benchmark-dictionary.c | 4 +- web/api/badges/web_buffer_svg.c | 23 +- web/api/badges/web_buffer_svg.h | 6 +- web/api/exporters/allmetrics.h | 2 +- web/api/exporters/shell/allmetrics_shell.c | 59 +- web/api/exporters/shell/allmetrics_shell.h | 4 +- web/api/formatters/charts2json.c | 37 +- web/api/formatters/charts2json.h | 6 +- web/api/formatters/csv/csv.c | 17 +- web/api/formatters/csv/csv.h | 2 +- web/api/formatters/json/json.c | 30 +- web/api/formatters/json/json.h | 2 +- web/api/formatters/json_wrapper.c | 192 +- web/api/formatters/json_wrapper.h | 8 +- web/api/formatters/rrd2json.c | 276 +- web/api/formatters/rrd2json.h | 79 +- web/api/formatters/rrdset2json.c | 52 +- web/api/formatters/rrdset2json.h | 2 +- web/api/formatters/ssv/ssv.c | 4 +- web/api/formatters/ssv/ssv.h | 2 +- web/api/formatters/value/value.c | 70 +- web/api/formatters/value/value.h | 23 +- web/api/health/health_cmdapi.h | 2 +- web/api/netdata-swagger.json | 63 +- web/api/netdata-swagger.yaml | 42 +- web/api/queries/average/average.h | 10 +- web/api/queries/countif/countif.h | 10 +- web/api/queries/des/des.h | 12 +- web/api/queries/incremental_sum/incremental_sum.h | 10 +- web/api/queries/max/max.h | 10 +- web/api/queries/median/median.h | 26 +- web/api/queries/min/min.h | 10 +- web/api/queries/percentile/percentile.h | 26 +- web/api/queries/query.c | 1012 +++--- web/api/queries/query.h | 8 +- web/api/queries/rrdr.c | 60 +- web/api/queries/rrdr.h | 111 +- web/api/queries/ses/ses.h | 12 +- web/api/queries/stddev/stddev.h | 16 +- web/api/queries/sum/sum.h | 10 +- web/api/queries/trimmed_mean/trimmed_mean.h | 24 +- web/api/queries/weights.c | 763 ++-- web/api/queries/weights.h | 14 +- web/api/tests/valid_urls.c | 13 - web/api/tests/web_api.c | 12 - web/api/web_api_v1.c | 443 ++- web/api/web_api_v1.h | 49 +- web/gui/Makefile.am | 80 +- web/gui/bundle_dashboard.py | 6 +- web/gui/dashboard/Makefile.am | 338 +- web/gui/dashboard/asset-manifest.json | 64 +- web/gui/dashboard/index.html | 4 +- ...he-manifest.5fec6109084644adf7bf854243e1a044.js | 190 + ...he-manifest.e95d658eed560f9e0189217cc6919238.js | 190 - web/gui/dashboard/service-worker.js | 2 +- web/gui/dashboard/static/css/2.20fd0a40.chunk.css | 15 - .../dashboard/static/css/2.20fd0a40.chunk.css.map | 1 - web/gui/dashboard/static/css/2.c454aab8.chunk.css | 15 + .../dashboard/static/css/2.c454aab8.chunk.css.map | 1 + web/gui/dashboard/static/js/10.44d9d40b.chunk.js | 2 - .../dashboard/static/js/10.44d9d40b.chunk.js.map | 1 - web/gui/dashboard/static/js/10.a5cd7d0e.chunk.js | 2 + .../dashboard/static/js/10.a5cd7d0e.chunk.js.map | 1 + web/gui/dashboard/static/js/2.3123f37d.chunk.js | 3 - .../static/js/2.3123f37d.chunk.js.LICENSE | 260 -- .../dashboard/static/js/2.3123f37d.chunk.js.map | 1 - web/gui/dashboard/static/js/2.92ca8446.chunk.js | 3 + .../static/js/2.92ca8446.chunk.js.LICENSE | 278 ++ .../dashboard/static/js/2.92ca8446.chunk.js.map | 1 + web/gui/dashboard/static/js/3.d49f0857.chunk.js | 2 - .../dashboard/static/js/3.d49f0857.chunk.js.map | 1 - web/gui/dashboard/static/js/3.f137faca.chunk.js | 2 + .../dashboard/static/js/3.f137faca.chunk.js.map | 1 + web/gui/dashboard/static/js/4.2dbcd906.chunk.js | 2 + .../dashboard/static/js/4.2dbcd906.chunk.js.map | 1 + web/gui/dashboard/static/js/4.8b70c754.chunk.js | 2 - .../dashboard/static/js/4.8b70c754.chunk.js.map | 1 - web/gui/dashboard/static/js/5.29dab1cd.chunk.js | 3 - .../static/js/5.29dab1cd.chunk.js.LICENSE | 3 - .../dashboard/static/js/5.29dab1cd.chunk.js.map | 1 - web/gui/dashboard/static/js/5.2f783a54.chunk.js | 3 + .../static/js/5.2f783a54.chunk.js.LICENSE | 3 + .../dashboard/static/js/5.2f783a54.chunk.js.map | 1 + web/gui/dashboard/static/js/6.7b15cdf3.chunk.js | 2 - .../dashboard/static/js/6.7b15cdf3.chunk.js.map | 1 - web/gui/dashboard/static/js/6.e1951239.chunk.js | 2 + .../dashboard/static/js/6.e1951239.chunk.js.map | 1 + web/gui/dashboard/static/js/7.c2417fb0.chunk.js | 2 + .../dashboard/static/js/7.c2417fb0.chunk.js.map | 1 + web/gui/dashboard/static/js/7.cf6bc66f.chunk.js | 2 - .../dashboard/static/js/7.cf6bc66f.chunk.js.map | 1 - web/gui/dashboard/static/js/8.b1a4b595.chunk.js | 2 - .../dashboard/static/js/8.b1a4b595.chunk.js.map | 1 - web/gui/dashboard/static/js/8.b4161ea2.chunk.js | 2 + .../dashboard/static/js/8.b4161ea2.chunk.js.map | 1 + web/gui/dashboard/static/js/9.50358509.chunk.js | 2 - .../dashboard/static/js/9.50358509.chunk.js.map | 1 - web/gui/dashboard/static/js/9.a4363968.chunk.js | 2 + .../dashboard/static/js/9.a4363968.chunk.js.map | 1 + web/gui/dashboard/static/js/main.7d1bdca1.chunk.js | 3 + .../static/js/main.7d1bdca1.chunk.js.LICENSE | 8 + .../dashboard/static/js/main.7d1bdca1.chunk.js.map | 1 + web/gui/dashboard/static/js/main.cc0e57d1.chunk.js | 3 - .../static/js/main.cc0e57d1.chunk.js.LICENSE | 8 - .../dashboard/static/js/main.cc0e57d1.chunk.js.map | 1 - .../dashboard/static/js/runtime-main.08abed8f.js | 2 + .../static/js/runtime-main.08abed8f.js.map | 1 + .../dashboard/static/js/runtime-main.b352aa47.js | 2 - .../static/js/runtime-main.b352aa47.js.map | 1 - web/gui/dashboard_info.js | 617 +++- web/server/static/static-threaded.c | 4 +- web/server/static/static-threaded.h | 2 +- web/server/web_client.c | 69 +- web/server/web_client.h | 21 +- web/server/web_client_cache.c | 8 +- web/server/web_client_cache.h | 8 +- web/server/web_server.h | 14 +- 632 files changed, 41213 insertions(+), 33680 deletions(-) create mode 100644 .github/codeql/python-config.yml create mode 100644 .github/workflows/codeql.yml delete mode 100644 aclk/aclk_api.c delete mode 100644 aclk/aclk_api.h create mode 100644 aclk/aclk_capas.c create mode 100644 aclk/aclk_capas.h delete mode 100644 aclk/aclk_charts_api.c delete mode 100644 aclk/aclk_charts_api.h create mode 100644 aclk/helpers/mqtt_wss_pal.h create mode 100644 aclk/helpers/ringbuffer_pal.h delete mode 100644 aclk/schema-wrappers/chart_config.cc delete mode 100644 aclk/schema-wrappers/chart_config.h delete mode 100644 aclk/schema-wrappers/chart_stream.cc delete mode 100644 aclk/schema-wrappers/chart_stream.h delete mode 100644 collectors/checks.plugin/Makefile.am delete mode 100644 collectors/checks.plugin/README.md delete mode 100644 collectors/checks.plugin/plugin_checks.c delete mode 100644 collectors/proc.plugin/proc_net_snmp.c delete mode 100644 collectors/proc.plugin/proc_net_snmp6.c delete mode 100644 collectors/python.d.plugin/nginx_plus/Makefile.inc delete mode 100644 collectors/python.d.plugin/nginx_plus/README.md delete mode 100644 collectors/python.d.plugin/nginx_plus/nginx_plus.chart.py delete mode 100644 collectors/python.d.plugin/nginx_plus/nginx_plus.conf create mode 100644 collectors/python.d.plugin/pandas/Makefile.inc create mode 100644 collectors/python.d.plugin/pandas/README.md create mode 100644 collectors/python.d.plugin/pandas/pandas.chart.py create mode 100644 collectors/python.d.plugin/pandas/pandas.conf delete mode 100644 collectors/python.d.plugin/postgres/Makefile.inc delete mode 100644 collectors/python.d.plugin/postgres/README.md delete mode 100644 collectors/python.d.plugin/postgres/postgres.chart.py delete mode 100644 collectors/python.d.plugin/postgres/postgres.conf create mode 100644 config.cmake.h.in delete mode 100644 database/engine/metadata_log/Makefile.am delete mode 100644 database/engine/metadata_log/compaction.c delete mode 100644 database/engine/metadata_log/compaction.h delete mode 100644 database/engine/metadata_log/logfile.c delete mode 100644 database/engine/metadata_log/logfile.h delete mode 100644 database/engine/metadata_log/metadatalog.h delete mode 100755 database/engine/metadata_log/metadatalogapi.c delete mode 100644 database/engine/metadata_log/metadatalogapi.h delete mode 100644 database/engine/metadata_log/metadatalogprotocol.h delete mode 100755 database/engine/metadata_log/metalogpluginsd.c delete mode 100644 database/engine/metadata_log/metalogpluginsd.h create mode 100644 database/rrdfunctions.c create mode 100644 database/rrdfunctions.h delete mode 100644 database/sqlite/sqlite_aclk_chart.c delete mode 100644 database/sqlite/sqlite_aclk_chart.h create mode 100644 database/sqlite/sqlite_metadata.c create mode 100644 database/sqlite/sqlite_metadata.h create mode 100644 health/health.d/nvme.conf create mode 100644 health/health.d/ping.conf create mode 100644 health/health.d/postgres.conf create mode 100644 libnetdata/string/Makefile.am create mode 100644 libnetdata/string/README.md create mode 100644 libnetdata/string/string.c create mode 100644 libnetdata/string/string.h create mode 100644 ml/ADCharts.cc create mode 100644 ml/ADCharts.h delete mode 100644 ml/BitBufferCounter.cc delete mode 100644 ml/BitBufferCounter.h delete mode 100644 ml/BitRateWindow.cc delete mode 100644 ml/BitRateWindow.h delete mode 100644 ml/Database.cc delete mode 100644 ml/Database.h create mode 100644 ml/KMeans.cc create mode 100644 ml/KMeans.h delete mode 100644 ml/Makefile.am create mode 100644 ml/SamplesBuffer.cc create mode 100644 ml/SamplesBuffer.h create mode 100644 ml/SamplesBufferTests.cc delete mode 100644 ml/Tests.cc delete mode 100644 ml/kmeans/KMeans.cc delete mode 100644 ml/kmeans/KMeans.h delete mode 100644 ml/kmeans/Makefile.am delete mode 100644 ml/kmeans/SamplesBuffer.cc delete mode 100644 ml/kmeans/SamplesBuffer.h delete mode 100644 ml/kmeans/Tests.cc create mode 100644 packaging/building-native-packages-locally.md create mode 100644 streaming/replication.c create mode 100644 streaming/replication.h create mode 100755 system/install-service.sh.in create mode 100644 web/gui/dashboard/precache-manifest.5fec6109084644adf7bf854243e1a044.js delete mode 100644 web/gui/dashboard/precache-manifest.e95d658eed560f9e0189217cc6919238.js delete mode 100644 web/gui/dashboard/static/css/2.20fd0a40.chunk.css delete mode 100644 web/gui/dashboard/static/css/2.20fd0a40.chunk.css.map create mode 100644 web/gui/dashboard/static/css/2.c454aab8.chunk.css create mode 100644 web/gui/dashboard/static/css/2.c454aab8.chunk.css.map delete mode 100644 web/gui/dashboard/static/js/10.44d9d40b.chunk.js delete mode 100644 web/gui/dashboard/static/js/10.44d9d40b.chunk.js.map create mode 100644 web/gui/dashboard/static/js/10.a5cd7d0e.chunk.js create mode 100644 web/gui/dashboard/static/js/10.a5cd7d0e.chunk.js.map delete mode 100644 web/gui/dashboard/static/js/2.3123f37d.chunk.js delete mode 100644 web/gui/dashboard/static/js/2.3123f37d.chunk.js.LICENSE delete mode 100644 web/gui/dashboard/static/js/2.3123f37d.chunk.js.map create mode 100644 web/gui/dashboard/static/js/2.92ca8446.chunk.js create mode 100644 web/gui/dashboard/static/js/2.92ca8446.chunk.js.LICENSE create mode 100644 web/gui/dashboard/static/js/2.92ca8446.chunk.js.map delete mode 100644 web/gui/dashboard/static/js/3.d49f0857.chunk.js delete mode 100644 web/gui/dashboard/static/js/3.d49f0857.chunk.js.map create mode 100644 web/gui/dashboard/static/js/3.f137faca.chunk.js create mode 100644 web/gui/dashboard/static/js/3.f137faca.chunk.js.map create mode 100644 web/gui/dashboard/static/js/4.2dbcd906.chunk.js create mode 100644 web/gui/dashboard/static/js/4.2dbcd906.chunk.js.map delete mode 100644 web/gui/dashboard/static/js/4.8b70c754.chunk.js delete mode 100644 web/gui/dashboard/static/js/4.8b70c754.chunk.js.map delete mode 100644 web/gui/dashboard/static/js/5.29dab1cd.chunk.js delete mode 100644 web/gui/dashboard/static/js/5.29dab1cd.chunk.js.LICENSE delete mode 100644 web/gui/dashboard/static/js/5.29dab1cd.chunk.js.map create mode 100644 web/gui/dashboard/static/js/5.2f783a54.chunk.js create mode 100644 web/gui/dashboard/static/js/5.2f783a54.chunk.js.LICENSE create mode 100644 web/gui/dashboard/static/js/5.2f783a54.chunk.js.map delete mode 100644 web/gui/dashboard/static/js/6.7b15cdf3.chunk.js delete mode 100644 web/gui/dashboard/static/js/6.7b15cdf3.chunk.js.map create mode 100644 web/gui/dashboard/static/js/6.e1951239.chunk.js create mode 100644 web/gui/dashboard/static/js/6.e1951239.chunk.js.map create mode 100644 web/gui/dashboard/static/js/7.c2417fb0.chunk.js create mode 100644 web/gui/dashboard/static/js/7.c2417fb0.chunk.js.map delete mode 100644 web/gui/dashboard/static/js/7.cf6bc66f.chunk.js delete mode 100644 web/gui/dashboard/static/js/7.cf6bc66f.chunk.js.map delete mode 100644 web/gui/dashboard/static/js/8.b1a4b595.chunk.js delete mode 100644 web/gui/dashboard/static/js/8.b1a4b595.chunk.js.map create mode 100644 web/gui/dashboard/static/js/8.b4161ea2.chunk.js create mode 100644 web/gui/dashboard/static/js/8.b4161ea2.chunk.js.map delete mode 100644 web/gui/dashboard/static/js/9.50358509.chunk.js delete mode 100644 web/gui/dashboard/static/js/9.50358509.chunk.js.map create mode 100644 web/gui/dashboard/static/js/9.a4363968.chunk.js create mode 100644 web/gui/dashboard/static/js/9.a4363968.chunk.js.map create mode 100644 web/gui/dashboard/static/js/main.7d1bdca1.chunk.js create mode 100644 web/gui/dashboard/static/js/main.7d1bdca1.chunk.js.LICENSE create mode 100644 web/gui/dashboard/static/js/main.7d1bdca1.chunk.js.map delete mode 100644 web/gui/dashboard/static/js/main.cc0e57d1.chunk.js delete mode 100644 web/gui/dashboard/static/js/main.cc0e57d1.chunk.js.LICENSE delete mode 100644 web/gui/dashboard/static/js/main.cc0e57d1.chunk.js.map create mode 100644 web/gui/dashboard/static/js/runtime-main.08abed8f.js create mode 100644 web/gui/dashboard/static/js/runtime-main.08abed8f.js.map delete mode 100644 web/gui/dashboard/static/js/runtime-main.b352aa47.js delete mode 100644 web/gui/dashboard/static/js/runtime-main.b352aa47.js.map diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dac9f84cb..c513b71dc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -10,28 +10,28 @@ aclk/ @stelfrag @underhood build/ @Ferroin contrib/debian @Ferroin -collectors/ @vlvkobal -collectors/ebpf.plugin/ @thiagoftsm @vlvkobal +collectors/ @thiagoftsm +collectors/ebpf.plugin/ @thiagoftsm collectors/charts.d.plugin/ @ilyam8 @Ferroin -collectors/freebsd.plugin/ @vlvkobal @thiagoftsm -collectors/macos.plugin/ @vlvkobal @thiagoftsm +collectors/freebsd.plugin/ @thiagoftsm +collectors/macos.plugin/ @thiagoftsm collectors/python.d.plugin/ @ilyam8 -collectors/cups.plugin/ @simonnagl @vlvkobal @thiagoftsm -exporting/ @vlvkobal @thiagoftsm +collectors/cups.plugin/ @simonnagl @thiagoftsm +exporting/ @thiagoftsm daemon/ @thiagoftsm @vkalintiris database/ @thiagoftsm @vkalintiris docs/ @DShreve2 -health/ @thiagoftsm @vlvkobal @vkalintiris -health/health.d/ @thiagoftsm @vlvkobal -health/notifications/ @Ferroin @thiagoftsm +health/ @thiagoftsm @vkalintiris @MrZammler +health/health.d/ @thiagoftsm @MrZammler +health/notifications/ @Ferroin @thiagoftsm @MrZammler ml/ @andrewm4894 @vkalintiris libnetdata/ @thiagoftsm @vkalintiris packaging/ @Ferroin registry/ @jacekkolasa -streaming/ @thiagoftsm @vlvkobal +streaming/ @thiagoftsm system/ @Ferroin tests/ @Ferroin @vkalintiris -web/ @thiagoftsm @vlvkobal @vkalintiris +web/ @thiagoftsm @vkalintiris web/gui/ @jacekkolasa # Ownership by filetype (overwrites ownership by directory) diff --git a/.github/codeql/python-config.yml b/.github/codeql/python-config.yml new file mode 100644 index 000000000..c82727ce3 --- /dev/null +++ b/.github/codeql/python-config.yml @@ -0,0 +1,10 @@ +paths-ignore: + - .github + - build_external/ + - ml/dlib + - ml/json + - tests/api + - web/gui + - collectors/python.d.plugin/python_modules/pyyaml* + - collectors/python.d.plugin/python_modules/third_party + - collectors/python.d.plugin/python_modules/urllib3 diff --git a/.github/data/distros.yml b/.github/data/distros.yml index 8ef32785b..0f5718645 100644 --- a/.github/data/distros.yml +++ b/.github/data/distros.yml @@ -26,14 +26,14 @@ include: apk del json-c-dev test: ebpf-core: true + - <<: *alpine + version: "3.17" - <<: *alpine version: "3.16" - <<: *alpine version: "3.15" - <<: *alpine version: "3.14" - - <<: *alpine - version: "3.13" - distro: archlinux version: latest @@ -99,12 +99,22 @@ include: - &fedora distro: fedora - version: "36" + version: "37" jsonc_removal: | dnf remove -y json-c-devel packages: &fedora_packages type: rpm repo_distro: fedora/36 + arches: + - x86_64 + - aarch64 + test: + ebpf-core: true + - <<: *fedora + version: "36" + packages: + <<: *fedora_packages + repo_distro: fedora/36 arches: - x86_64 - armhfp @@ -116,6 +126,10 @@ include: packages: <<: *fedora_packages repo_distro: fedora/35 + arches: + - x86_64 + - armhfp + - aarch64 test: ebpf-core: true @@ -162,20 +176,25 @@ include: - &ubuntu distro: ubuntu - version: "22.04" + version: "22.10" env_prep: | rm -f /etc/apt/apt.conf.d/docker && apt-get update jsonc_removal: | apt-get remove -y libjson-c-dev packages: &ubuntu_packages type: deb - repo_distro: ubuntu/jammy + repo_distro: ubuntu/kinetic arches: - amd64 - armhf - arm64 test: ebpf-core: true + - <<: *ubuntu + version: "22.04" + packages: + <<: *ubuntu_packages + repo_distro: ubuntu/jammy - <<: *ubuntu version: "20.04" packages: diff --git a/.github/scripts/pkg-test.sh b/.github/scripts/pkg-test.sh index 179c0c41e..e3bc3e7d4 100755 --- a/.github/scripts/pkg-test.sh +++ b/.github/scripts/pkg-test.sh @@ -4,13 +4,19 @@ install_debian_like() { # This is needed to ensure package installs don't prompt for any user input. export DEBIAN_FRONTEND=noninteractive + if apt-cache show netcat 2>&1 | grep -q "No packages found"; then + netcat="netcat-traditional" + else + netcat="netcat" + fi + apt-get update # Install Netdata apt-get install -y /netdata/artifacts/netdata_"${VERSION}"*_*.deb || exit 1 # Install testing tools - apt-get install -y --no-install-recommends curl netcat jq || exit 1 + apt-get install -y --no-install-recommends curl "${netcat}" jq || exit 1 } install_fedora_like() { diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yml index ae58cfce2..a80d8b41d 100644 --- a/.github/workflows/add-to-project.yml +++ b/.github/workflows/add-to-project.yml @@ -13,13 +13,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Add issues to Agent project board - uses: actions/add-to-project@v0.3.0 + uses: actions/add-to-project@v0.4.0 with: project-url: https://github.com/orgs/netdata/projects/32 github-token: ${{ secrets.NETDATABOT_ORG_GITHUB_TOKEN }} - name: Add issues to Product Bug project board - uses: actions/add-to-project@v0.3.0 + uses: actions/add-to-project@v0.4.0 with: project-url: https://github.com/orgs/netdata/projects/45 github-token: ${{ secrets.NETDATABOT_ORG_GITHUB_TOKEN }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 26849312f..53f1590f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -670,16 +670,18 @@ jobs: with: name: final-artifacts path: final-artifacts - - name: Setup Gcloud - id: gcloud - uses: google-github-actions/setup-gcloud@v0.6.0 + - name: Authenticate to GCS + id: gcs-auth + uses: google-github-actions/auth@v1 with: project_id: ${{ secrets.GCP_NIGHTLY_STORAGE_PROJECT }} - service_account_key: ${{ secrets.GCP_STORAGE_SERVICE_ACCOUNT_KEY }} - export_default_credentials: true + credentials_json: ${{ secrets.GCS_STORAGE_SERVICE_KEY_JSON }} + - name: Setup GCS + id: gcs-setup + uses: google-github-actions/setup-gcloud@v1.0.1 - name: Upload Artifacts id: upload - uses: google-github-actions/upload-cloud-storage@v0.10.2 + uses: google-github-actions/upload-cloud-storage@v1.0.0 with: destination: ${{ secrets.GCP_NIGHTLY_STORAGE_BUCKET }} gzip: false @@ -696,7 +698,8 @@ jobs: SLACK_MESSAGE: |- ${{ github.repository }}: Failed to upload nightly release artifacts. Fetch artifacts: ${{ steps.fetch.outcome }} - Setup GCloud: ${{ steps.gcloud.outcome }} + Authenticatie GCS: ${{ steps.gcs-auth.outcome }} + Setup GCS: ${{ steps.gcs-setup.outcome }} Upload artifacts: ${{ steps.upload.outcome }} SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} if: >- @@ -706,6 +709,76 @@ jobs: && github.event_name != 'pull_request' }} + create-nightly: # Create a nightly build release in netdata/netdata-nightlies + name: Create Nightly Release + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' && github.event.inputs.type == 'nightly' && github.repository == 'netdata/netdata' + needs: + - updater-check + - source-build + - artifact-verification-dist + - artifact-verification-static + steps: + - name: Checkout Main Repo + id: checkout-main + uses: actions/checkout@v3 + with: + path: main + - name: Checkout Nightly Repo + id: checkout-nightly + uses: actions/checkout@v3 + with: + repository: netdata/netdata-nightlies + path: nightlies + token: ${{ secrets.NETDATABOT_GITHUB_TOKEN }} + - name: Retrieve Artifacts + id: fetch + uses: actions/download-artifact@v3 + with: + name: final-artifacts + path: final-artifacts + - name: Prepare version info + id: version + run: | + # shellcheck disable=SC2129 + echo "version=$(cat main/packaging/version)" >> "${GITHUB_OUTPUT}" + echo "commit=$(cd nightlies && git rev-parse HEAD)" >> "${GITHUB_OUTPUT}" + echo "date=$(date +%F)" >> "${GITHUB_OUTPUT}" + - name: Create Release + id: create-release + uses: ncipollo/release-action@v1 + with: + allowUpdates: false + artifactErrorsFailBuild: true + artifacts: 'final-artifacts/sha256sums.txt,final-artifacts/netdata-*.tar.gz,final-artifacts/netdata-*.gz.run' + owner: netdata + repo: netdata-nightlies + body: Netdata nightly build for ${{ steps.version.outputs.date }}. + commit: ${{ steps.version.outputs.commit }} + tag: ${{ steps.version.outputs.version }} + token: ${{ secrets.NETDATABOT_GITHUB_TOKEN }} + - name: Failure Notification + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_COLOR: 'danger' + SLACK_FOOTER: '' + SLACK_ICON_EMOJI: ':github-actions:' + SLACK_TITLE: 'Failed to draft release:' + SLACK_USERNAME: 'GitHub Actions' + SLACK_MESSAGE: |- + ${{ github.repository }}: Failed to create nightly release or attach artifacts. + Checkout netdata/netdata: ${{ steps.checkout-main.outcome }} + Checkout netdata/netdata-nightlies: ${{ steps.checkout-nightly.outcome }} + Fetch artifacts: ${{ steps.fetch.outcome }} + Prepare version info: ${{ steps.version.outcome }} + Create release: ${{ steps.create-release.outcome }} + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + if: >- + ${{ + failure() + && github.event_name == 'workflow_dispatch' + }} + normalize-tag: # Fix the release tag if needed name: Normalize Release Tag runs-on: ubuntu-latest diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..021376a2d --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,117 @@ +--- +# Run CodeQL to analyze C/C++ and Python code. +name: CodeQL +on: + pull_request: + types: [opened, reopened, labeled, synchronize] + branches: [master] + push: + branches: [master] + schedule: + - cron: "27 2 * * 1" +env: + DISABLE_TELEMETRY: 1 +concurrency: + group: codeql-${{ github.ref }} + cancel-in-progress: true +jobs: + prepare: + name: Prepare Jobs + runs-on: ubuntu-latest + outputs: + cpp: ${{ steps.cpp.outputs.run }} + python: ${{ steps.python.outputs.run }} + steps: + - name: Clone repository + uses: actions/checkout@v3 + with: + submodules: recursive + fetch-depth: 0 + - name: Check if we should always run + id: always + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + if [ "${{ contains(github.event.pull_request.labels.*.name, 'run-ci/codeql') }}" = "true" ]; then + echo '::set-output name=run::true' + echo '::notice::Found ci/codeql label, unconditionally running all CodeQL checks.' + else + echo '::set-output name=run::false' + fi + else + echo '::set-output name=run::true' + fi + - name: Check for C/C++ changes + id: cpp + run: | + if [ "${{ steps.always.outputs.run }}" = "false" ]; then + if git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq '.*\.[ch](xx|\+\+)?' ; then + echo '::set-output name=run::true' + echo '::notice::C/C++ code has changed, need to run CodeQL.' + else + echo '::set-output name=run::false' + fi + else + echo '::set-output name=run::true' + fi + - name: Check for python changes + id: python + run: | + if [ "${{ steps.always.outputs.run }}" = "false" ]; then + if git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq 'collectors/python.d.plugin/.*\.py' ; then + echo '::set-output name=run::true' + echo '::notice::Python code has changed, need to run CodeQL.' + else + echo '::set-output name=run::false' + fi + else + echo '::set-output name=run::true' + fi + + analyze-cpp: + name: Analyze C/C++ + runs-on: ubuntu-latest + needs: prepare + if: needs.prepare.outputs.cpp == 'true' + permissions: + security-events: write + steps: + - name: Git clone repository + uses: actions/checkout@v3 + with: + submodules: recursive + fetch-depth: 0 + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: cpp + - name: Prepare environment + run: ./packaging/installer/install-required-packages.sh --dont-wait --non-interactive netdata + - name: Build netdata + run: ./netdata-installer.sh --dont-start-it --disable-telemetry --dont-wait --install /tmp/install --one-time-build + - name: Run CodeQL + uses: github/codeql-action/analyze@v2 + with: + category: "/language:cpp" + + analyze-python: + name: Analyze Python + runs-on: ubuntu-latest + needs: prepare + if: needs.prepare.outputs.python == 'true' + permissions: + security-events: write + steps: + - name: Git clone repository + uses: actions/checkout@v3 + with: + submodules: recursive + fetch-depth: 0 + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + config-file: ./.github/codeql/python-config.yml + languages: python + - name: Run CodeQL + uses: github/codeql-action/analyze@v2 + with: + category: "/language:python" diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml index ec4e42c00..ddd8356e4 100644 --- a/.github/workflows/packaging.yml +++ b/.github/workflows/packaging.yml @@ -3,9 +3,13 @@ name: Packages on: pull_request: + types: + - opened + - reopened + - labeled + - synchronize branches: - master - - develop push: branches: - master @@ -45,9 +49,8 @@ jobs: from ruamel.yaml import YAML import json import re - FULL_CI_REGEX = '/actions run full ci' + import os ALWAYS_RUN_ARCHES = ["amd64", "x86_64"] - PR_BODY = """${{ github.event.pull_request.body }}""" yaml = YAML(typ='safe') entries = list() run_limited = False @@ -55,7 +58,7 @@ jobs: with open('.github/data/distros.yml') as f: data = yaml.load(f) - if "${{ github.event_name }}" == "pull_request" and re.search(FULL_CI_REGEX, PR_BODY, re.I) is None: + if "${{ github.event_name }}" == "pull_request" and "${{ !contains(github.event.pull_request.labels.*.name, 'run-ci/packaging') }}": run_limited = True for i, v in enumerate(data['include']): diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml index f631b929a..5679b246c 100644 --- a/.github/workflows/review.yml +++ b/.github/workflows/review.yml @@ -2,7 +2,8 @@ # Runs various ReviewDog based checks against PR with suggested changes to improve quality name: Review on: - pull_request: null + pull_request: + types: [opened, reopened, labeled, synchronize] env: DISABLE_TELEMETRY: 1 concurrency: @@ -27,7 +28,9 @@ jobs: - name: Check files for actionlint id: actionlint run: | - if git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq '\.github/workflows/.*' ; then + if [ "${{ contains(github.event.pull_request.labels.*.name, 'run-ci/actionlint') }}" = "true" ]; then + echo '::set-output name=run::true' + elif git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq '\.github/workflows/.*' ; then echo '::set-output name=run::true' echo 'GitHub Actions workflows have changed, need to run actionlint.' else @@ -36,7 +39,9 @@ jobs: - name: Check files for eslint id: eslint run: | - if git diff --name-only origin/${{ github.base_ref }} HEAD | grep -v "web/gui/dashboard" | grep -Eq '.*\.js|node\.d\.plugin\.in' ; then + if [ "${{ contains(github.event.pull_request.labels.*.name, 'run-ci/eslint') }}" = "true" ]; then + echo '::set-output name=run::true' + elif git diff --name-only origin/${{ github.base_ref }} HEAD | grep -v "web/gui/dashboard" | grep -Eq '.*\.js|node\.d\.plugin\.in' ; then echo '::set-output name=run::true' echo 'JS files have changed, need to run ESLint.' else @@ -45,7 +50,9 @@ jobs: - name: Check files for hadolint id: hadolint run: | - if git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq '.*Dockerfile.*' ; then + if [ "${{ contains(github.event.pull_request.labels.*.name, 'run-ci/hadolint') }}" = "true" ]; then + echo '::set-output name=run::true' + elif git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq '.*Dockerfile.*' ; then echo '::set-output name=run::true' echo 'Dockerfiles have changed, need to run Hadolint.' else @@ -54,7 +61,9 @@ jobs: - name: Check files for shellcheck id: shellcheck run: | - if git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq '.*\.sh.*' ; then + if [ "${{ contains(github.event.pull_request.labels.*.name, 'run-ci/shellcheck') }}" = "true" ]; then + echo '::set-output name=run::true' + elif git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq '.*\.sh.*' ; then echo '::set-output name=run::true' echo 'Shell scripts have changed, need to run shellcheck.' else @@ -63,7 +72,9 @@ jobs: - name: Check files for yamllint id: yamllint run: | - if git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq '.*\.ya?ml|python\.d/.*\.conf' ; then + if [ "${{ contains(github.event.pull_request.labels.*.name, 'run-ci/yamllint') }}" = "true" ]; then + echo '::set-output name=run::true' + elif git diff --name-only origin/${{ github.base_ref }} HEAD | grep -Eq '.*\.ya?ml|python\.d/.*\.conf' ; then echo '::set-output name=run::true' echo 'YAML files have changed, need to run yamllint.' else diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6272a6d68..d48386855 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,59 +39,3 @@ jobs: CFLAGS: "-O1 -DNETDATA_INTERNAL_CHECKS=1 -DNETDATA_VERIFY_LOCKS=1" run: | ./tests/run-unit-tests.sh - - unit-tests-cmocka: - name: Unit Tests (cmocka) - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Prepare environment - run: | - ./packaging/installer/install-required-packages.sh --dont-wait --non-interactive netdata-all - sudo apt-get install -y libjson-c-dev libipmimonitoring-dev libcups2-dev libsnappy-dev \ - libprotobuf-dev libprotoc-dev libssl-dev protobuf-compiler \ - libnetfilter-acct-dev libmongoc-dev libcmocka-dev libzstd-dev - - name: Configure - run: | - autoreconf -ivf - ./configure --disable-ml --disable-dependency-tracking - # XXX: Work-around for bug with libbson-1.0 in Ubuntu 18.04 - # See: https://bugs.launchpad.net/ubuntu/+source/libmongoc/+bug/1790771 - # https://jira.mongodb.org/browse/CDRIVER-2818 - - name: Fix libbson - run: | - pushd /usr/lib || exit 1 - sudo ln -s /usr/include . - popd || exit 1 - - name: Build - run: | - mkdir build-tmp - cd build-tmp - cmake \ - -D UNIT_TESTING=1 \ - -D BUILD_TESTING=1 \ - -D CMAKE_BUILD_TYPE="Debug" \ - -D BSON_LIBRARY=/usr/lib/x86_64-linux-gnu/libbson-1.0.so \ - -D MONGOC_LIBRARY=/usr/lib/x86_64-linux-gnu/libmongoc-1.0.so \ - .. - make - - name: Run ctest - run: | - cd build-tmp - ctest - - name: Prepare Artifacts - if: always() - run: | - mkdir logs - pushd build-tmp || exit 1 - find . -type f -name '*.log' -exec cp {} ../logs/ \; - popd || exit 1 - - name: Upload Artifacts - uses: actions/upload-artifact@v3 - if: always() - with: - name: logs - path: logs diff --git a/.gitignore b/.gitignore index 275b5d007..b90428a14 100644 --- a/.gitignore +++ b/.gitignore @@ -128,6 +128,7 @@ system/netdata.plist system/netdata-freebsd system/edit-config system/netdata.crontab +system/install-service.sh daemon/anonymous-statistics.sh daemon/get-kubernetes-labels.sh diff --git a/.gitmodules b/.gitmodules index 0f01d3d68..d3c7ace40 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,7 +5,7 @@ path = aclk/aclk-schemas url = https://github.com/netdata/aclk-schemas.git [submodule "ml/kmeans/dlib"] - path = ml/kmeans/dlib + path = ml/dlib url = https://github.com/davisking/dlib.git shallow = true ignore = dirty diff --git a/.lgtm.yml b/.lgtm.yml index dcbd7853d..6fdab0e80 100644 --- a/.lgtm.yml +++ b/.lgtm.yml @@ -13,10 +13,12 @@ path_classifiers: - collectors/python.d.plugin/python_modules/pyyaml2/ - collectors/python.d.plugin/python_modules/pyyaml3/ - ml/kmeans/dlib/ + - ml/dlib/dlib/ - ml/json/ - web/gui/lib/ - web/gui/src/ - web/gui/css/ + - web/gui/dashboard/lib/ - libnetdata/libjudy/ test: - tests/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f48ec5d..a46b77108 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,292 @@ # Changelog -## [v1.36.1](https://github.com/netdata/netdata/tree/v1.36.1) (2022-08-15) +## [v1.37.0](https://github.com/netdata/netdata/tree/v1.37.0) (2022-11-30) -[Full Changelog](https://github.com/netdata/netdata/compare/v1.36.0...v1.36.1) +[Full Changelog](https://github.com/netdata/netdata/compare/v1.36.1...v1.37.0) **Merged pull requests:** +- fix spinlock [\#14068](https://github.com/netdata/netdata/pull/14068) ([ktsaou](https://github.com/ktsaou)) +- Sanitize command arguments. [\#14064](https://github.com/netdata/netdata/pull/14064) ([vkalintiris](https://github.com/vkalintiris)) +- Strict control of streaming API keys and MACHINE GUIDs in stream.conf [\#14063](https://github.com/netdata/netdata/pull/14063) ([ktsaou](https://github.com/ktsaou)) +- replication fixes No 8 [\#14061](https://github.com/netdata/netdata/pull/14061) ([ktsaou](https://github.com/ktsaou)) +- fix\(updater\): don't produce any output if static update completed successfully [\#14058](https://github.com/netdata/netdata/pull/14058) ([ilyam8](https://github.com/ilyam8)) +- Add Alpine 3.17 to supported distros. [\#14056](https://github.com/netdata/netdata/pull/14056) ([Ferroin](https://github.com/Ferroin)) +- replication fixes No 7 [\#14053](https://github.com/netdata/netdata/pull/14053) ([ktsaou](https://github.com/ktsaou)) +- bump go.d.plugin to v0.45.0 [\#14052](https://github.com/netdata/netdata/pull/14052) ([ilyam8](https://github.com/ilyam8)) +- Remove eBPF plugin warning [\#14047](https://github.com/netdata/netdata/pull/14047) ([thiagoftsm](https://github.com/thiagoftsm)) +- replication fixes \#6 [\#14046](https://github.com/netdata/netdata/pull/14046) ([ktsaou](https://github.com/ktsaou)) +- fix build on old openssl versions on centos [\#14045](https://github.com/netdata/netdata/pull/14045) ([underhood](https://github.com/underhood)) +- Don't let slow disk plugin thread delay shutdown [\#14044](https://github.com/netdata/netdata/pull/14044) ([MrZammler](https://github.com/MrZammler)) +- minor - wss to point to master instead of branch [\#14043](https://github.com/netdata/netdata/pull/14043) ([underhood](https://github.com/underhood)) +- fix dictionaries unittest [\#14042](https://github.com/netdata/netdata/pull/14042) ([ktsaou](https://github.com/ktsaou)) +- minor - Adds better information in case of SSL error [\#14041](https://github.com/netdata/netdata/pull/14041) ([underhood](https://github.com/underhood)) +- replication fixes \#5 [\#14038](https://github.com/netdata/netdata/pull/14038) ([ktsaou](https://github.com/ktsaou)) +- do not merge duplicate replication requests [\#14037](https://github.com/netdata/netdata/pull/14037) ([ktsaou](https://github.com/ktsaou)) +- Replication fixes \#3 [\#14035](https://github.com/netdata/netdata/pull/14035) ([ktsaou](https://github.com/ktsaou)) +- improve performance of worker utilization statistics [\#14034](https://github.com/netdata/netdata/pull/14034) ([ktsaou](https://github.com/ktsaou)) +- use 2 levels of judy arrays to speed up replication on very busy parents [\#14031](https://github.com/netdata/netdata/pull/14031) ([ktsaou](https://github.com/ktsaou)) +- bump go.d.plugin v0.44.0 [\#14030](https://github.com/netdata/netdata/pull/14030) ([ilyam8](https://github.com/ilyam8)) +- remove retries from SSL [\#14026](https://github.com/netdata/netdata/pull/14026) ([ktsaou](https://github.com/ktsaou)) +- Fix documentation TLS streaming [\#14024](https://github.com/netdata/netdata/pull/14024) ([thiagoftsm](https://github.com/thiagoftsm)) +- streaming compression, query planner and replication fixes [\#14023](https://github.com/netdata/netdata/pull/14023) ([ktsaou](https://github.com/ktsaou)) +- Change relative links to absolute for learn components [\#14015](https://github.com/netdata/netdata/pull/14015) ([tkatsoulas](https://github.com/tkatsoulas)) +- allow statsd tags to modify chart metadata on the fly [\#14014](https://github.com/netdata/netdata/pull/14014) ([ktsaou](https://github.com/ktsaou)) +- minor - silence misleading error [\#14013](https://github.com/netdata/netdata/pull/14013) ([underhood](https://github.com/underhood)) +- Improve eBPF exit [\#14012](https://github.com/netdata/netdata/pull/14012) ([thiagoftsm](https://github.com/thiagoftsm)) +- Change static image urls to app.netdata.cloud in alarm-notify.sh [\#14007](https://github.com/netdata/netdata/pull/14007) ([MrZammler](https://github.com/MrZammler)) +- Fix connection resets on big parents [\#14004](https://github.com/netdata/netdata/pull/14004) ([underhood](https://github.com/underhood)) +- minor typo in the uninstall command [\#14002](https://github.com/netdata/netdata/pull/14002) ([tkatsoulas](https://github.com/tkatsoulas)) +- Revert "New journal disk based indexing for agent memory reduction" [\#14000](https://github.com/netdata/netdata/pull/14000) ([ktsaou](https://github.com/ktsaou)) +- Revert "enable dbengine tiering by default" [\#13999](https://github.com/netdata/netdata/pull/13999) ([ktsaou](https://github.com/ktsaou)) +- fixes MQTT-NG QoS0 [\#13997](https://github.com/netdata/netdata/pull/13997) ([underhood](https://github.com/underhood)) +- remove python.d/nginx\_plus [\#13995](https://github.com/netdata/netdata/pull/13995) ([ilyam8](https://github.com/ilyam8)) +- bump go.d.plugin to v0.43.1 [\#13993](https://github.com/netdata/netdata/pull/13993) ([ilyam8](https://github.com/ilyam8)) +- Add 'funcs' capability [\#13992](https://github.com/netdata/netdata/pull/13992) ([underhood](https://github.com/underhood)) +- added debug info on left-over query targets [\#13990](https://github.com/netdata/netdata/pull/13990) ([ktsaou](https://github.com/ktsaou)) +- replication improvements [\#13989](https://github.com/netdata/netdata/pull/13989) ([ktsaou](https://github.com/ktsaou)) +- use calculator app instead of spreadsheet [\#13981](https://github.com/netdata/netdata/pull/13981) ([andrewm4894](https://github.com/andrewm4894)) +- apps.plugin function processes: keys should not have spaces [\#13980](https://github.com/netdata/netdata/pull/13980) ([ktsaou](https://github.com/ktsaou)) +- dont crash when netdata cannot execute its external plugins [\#13978](https://github.com/netdata/netdata/pull/13978) ([ktsaou](https://github.com/ktsaou)) +- Add \_total suffix to raw increment metrics for remote write [\#13977](https://github.com/netdata/netdata/pull/13977) ([vlvkobal](https://github.com/vlvkobal)) +- add Cassandra icon to dashboard info [\#13975](https://github.com/netdata/netdata/pull/13975) ([ilyam8](https://github.com/ilyam8)) +- Change the db-engine calculator to a read only gsheet [\#13974](https://github.com/netdata/netdata/pull/13974) ([tkatsoulas](https://github.com/tkatsoulas)) +- enable collecting ECC memory errors by default [\#13970](https://github.com/netdata/netdata/pull/13970) ([ilyam8](https://github.com/ilyam8)) +- break active-active loop from replicating non-existing child to each other [\#13968](https://github.com/netdata/netdata/pull/13968) ([ktsaou](https://github.com/ktsaou)) +- document password param for tor collector [\#13966](https://github.com/netdata/netdata/pull/13966) ([andrewm4894](https://github.com/andrewm4894)) +- Fallback to ar and ranlib if llvm-ar and llvm-ranlib are not there [\#13959](https://github.com/netdata/netdata/pull/13959) ([MrZammler](https://github.com/MrZammler)) +- require -DENABLE\_DLSYM=1 to use dlsym\(\) [\#13958](https://github.com/netdata/netdata/pull/13958) ([ktsaou](https://github.com/ktsaou)) +- health/ping: use 'host' label in alerts info [\#13955](https://github.com/netdata/netdata/pull/13955) ([ilyam8](https://github.com/ilyam8)) +- bump go.d.plugin to v0.43.0 [\#13954](https://github.com/netdata/netdata/pull/13954) ([ilyam8](https://github.com/ilyam8)) +- Fix local dashboard cloud links [\#13953](https://github.com/netdata/netdata/pull/13953) ([underhood](https://github.com/underhood)) +- Remove health\_thread\_stop [\#13948](https://github.com/netdata/netdata/pull/13948) ([MrZammler](https://github.com/MrZammler)) +- Provide improved messaging in the kickstart script for existing installs managed by the system package manager. [\#13947](https://github.com/netdata/netdata/pull/13947) ([Ferroin](https://github.com/Ferroin)) +- do not resend charts upstream when chart variables are being updated [\#13946](https://github.com/netdata/netdata/pull/13946) ([ktsaou](https://github.com/ktsaou)) +- recalculate last\_collected\_total [\#13945](https://github.com/netdata/netdata/pull/13945) ([ktsaou](https://github.com/ktsaou)) +- fix chart definition end time\_t printing and parsing [\#13942](https://github.com/netdata/netdata/pull/13942) ([ktsaou](https://github.com/ktsaou)) +- Setup default certificates path [\#13941](https://github.com/netdata/netdata/pull/13941) ([MrZammler](https://github.com/MrZammler)) +- update link to point at demo spaces [\#13939](https://github.com/netdata/netdata/pull/13939) ([andrewm4894](https://github.com/andrewm4894)) +- Statsd dictionaries should be multi-threaded [\#13938](https://github.com/netdata/netdata/pull/13938) ([ktsaou](https://github.com/ktsaou)) +- Fix oracle linux \(eBPF plugin\) [\#13935](https://github.com/netdata/netdata/pull/13935) ([thiagoftsm](https://github.com/thiagoftsm)) +- Update print message on startup [\#13934](https://github.com/netdata/netdata/pull/13934) ([andrewm4894](https://github.com/andrewm4894)) +- Rrddim acquire on replay set [\#13932](https://github.com/netdata/netdata/pull/13932) ([ktsaou](https://github.com/ktsaou)) +- fix compiling without dbengine [\#13931](https://github.com/netdata/netdata/pull/13931) ([ilyam8](https://github.com/ilyam8)) +- bump go.d.plugin to v0.42.1 [\#13930](https://github.com/netdata/netdata/pull/13930) ([ilyam8](https://github.com/ilyam8)) +- Remove pluginsd action param & dead code. [\#13928](https://github.com/netdata/netdata/pull/13928) ([vkalintiris](https://github.com/vkalintiris)) +- Do not force internal collectors to call rrdset\_next. [\#13926](https://github.com/netdata/netdata/pull/13926) ([vkalintiris](https://github.com/vkalintiris)) +- Return accidentaly removed 32bit RPi keep alive fix [\#13925](https://github.com/netdata/netdata/pull/13925) ([underhood](https://github.com/underhood)) +- error\_limit\(\) function to limit number of error lines per instance [\#13924](https://github.com/netdata/netdata/pull/13924) ([ktsaou](https://github.com/ktsaou)) +- fix crash on query plan switch [\#13920](https://github.com/netdata/netdata/pull/13920) ([ktsaou](https://github.com/ktsaou)) +- Enable aclk conversation log even without NETDATA\_INTERNAL CHECKS [\#13917](https://github.com/netdata/netdata/pull/13917) ([MrZammler](https://github.com/MrZammler)) +- add ping dashboard info and alarms [\#13916](https://github.com/netdata/netdata/pull/13916) ([ilyam8](https://github.com/ilyam8)) +- enable dbengine tiering by default [\#13914](https://github.com/netdata/netdata/pull/13914) ([ktsaou](https://github.com/ktsaou)) +- bump go.d.plugin to v0.42.0 [\#13913](https://github.com/netdata/netdata/pull/13913) ([ilyam8](https://github.com/ilyam8)) +- do not free hosts if a change on db mode is not needed [\#13912](https://github.com/netdata/netdata/pull/13912) ([ktsaou](https://github.com/ktsaou)) +- timeframe matching should take into account the update frequency of the chart [\#13911](https://github.com/netdata/netdata/pull/13911) ([ktsaou](https://github.com/ktsaou)) +- WMI Process \(Dashboard, Documentation\) [\#13910](https://github.com/netdata/netdata/pull/13910) ([thiagoftsm](https://github.com/thiagoftsm)) +- feat\(packaging\): add CAP\_NET\_RAW to go.d.plugin [\#13909](https://github.com/netdata/netdata/pull/13909) ([ilyam8](https://github.com/ilyam8)) +- Reference the bash collector for RPi [\#13907](https://github.com/netdata/netdata/pull/13907) ([cakrit](https://github.com/cakrit)) +- Improve intro paragraph [\#13906](https://github.com/netdata/netdata/pull/13906) ([cakrit](https://github.com/cakrit)) +- bump go.d.plugin v0.41.2 [\#13903](https://github.com/netdata/netdata/pull/13903) ([ilyam8](https://github.com/ilyam8)) +- apps.plugin function add max value on all value columns [\#13899](https://github.com/netdata/netdata/pull/13899) ([ktsaou](https://github.com/ktsaou)) +- Reduce unnecessary alert events to the cloud [\#13897](https://github.com/netdata/netdata/pull/13897) ([MrZammler](https://github.com/MrZammler)) +- add pandas collector to collectors.md [\#13895](https://github.com/netdata/netdata/pull/13895) ([andrewm4894](https://github.com/andrewm4894)) +- Fix reading health "enable" from the configuration [\#13894](https://github.com/netdata/netdata/pull/13894) ([stelfrag](https://github.com/stelfrag)) +- fix\(proc.plugin\): fix read retry logic when reading interface speed [\#13893](https://github.com/netdata/netdata/pull/13893) ([ilyam8](https://github.com/ilyam8)) +- Record installation command in telemetry events. [\#13892](https://github.com/netdata/netdata/pull/13892) ([Ferroin](https://github.com/Ferroin)) +- Prompt users about updates/claiming on unknown install types. [\#13890](https://github.com/netdata/netdata/pull/13890) ([Ferroin](https://github.com/Ferroin)) +- tune rrdcontext timings [\#13889](https://github.com/netdata/netdata/pull/13889) ([ktsaou](https://github.com/ktsaou)) +- filtering out charts in context queries, includes them in full\_xxx variables [\#13886](https://github.com/netdata/netdata/pull/13886) ([ktsaou](https://github.com/ktsaou)) +- New journal disk based indexing for agent memory reduction [\#13885](https://github.com/netdata/netdata/pull/13885) ([stelfrag](https://github.com/stelfrag)) +- Fix systemd chart update \(eBPF\) [\#13884](https://github.com/netdata/netdata/pull/13884) ([thiagoftsm](https://github.com/thiagoftsm)) +- apps.plugin function processes cosmetic changes [\#13880](https://github.com/netdata/netdata/pull/13880) ([ktsaou](https://github.com/ktsaou)) +- allow single chart to be filtered in context queries [\#13879](https://github.com/netdata/netdata/pull/13879) ([ktsaou](https://github.com/ktsaou)) +- Add description for TCP WMI [\#13878](https://github.com/netdata/netdata/pull/13878) ([thiagoftsm](https://github.com/thiagoftsm)) +- Use print macros [\#13876](https://github.com/netdata/netdata/pull/13876) ([MrZammler](https://github.com/MrZammler)) +- Suppress ML and dlib ABI warnings [\#13875](https://github.com/netdata/netdata/pull/13875) ([Dim-P](https://github.com/Dim-P)) +- bump go.d.plugin v0.41.1 [\#13874](https://github.com/netdata/netdata/pull/13874) ([ilyam8](https://github.com/ilyam8)) +- Replication of metrics \(gaps filling\) during streaming [\#13873](https://github.com/netdata/netdata/pull/13873) ([vkalintiris](https://github.com/vkalintiris)) +- Don't create a REMOVED alert event after a REMOVED. [\#13871](https://github.com/netdata/netdata/pull/13871) ([MrZammler](https://github.com/MrZammler)) +- Store hidden status when creating / updating dimension metadata [\#13869](https://github.com/netdata/netdata/pull/13869) ([stelfrag](https://github.com/stelfrag)) +- Find the chart and dimension UUID from the context [\#13868](https://github.com/netdata/netdata/pull/13868) ([stelfrag](https://github.com/stelfrag)) +- fix\(cgroup.plugin\): handle qemu-1- prefix when extracting virsh domain [\#13866](https://github.com/netdata/netdata/pull/13866) ([ilyam8](https://github.com/ilyam8)) +- Update step-09 for dbmode update.md [\#13864](https://github.com/netdata/netdata/pull/13864) ([DShreve2](https://github.com/DShreve2)) +- add ACLK access to ml\_info \(fix anomalies tab in cloud\) [\#13863](https://github.com/netdata/netdata/pull/13863) ([underhood](https://github.com/underhood)) +- bump go.d.plugin to v0.41.0 [\#13861](https://github.com/netdata/netdata/pull/13861) ([ilyam8](https://github.com/ilyam8)) +- app to api netdata cloud [\#13856](https://github.com/netdata/netdata/pull/13856) ([underhood](https://github.com/underhood)) +- Use llvm's ar and ranlib when compiling with clang [\#13854](https://github.com/netdata/netdata/pull/13854) ([MrZammler](https://github.com/MrZammler)) +- fix typo [\#13853](https://github.com/netdata/netdata/pull/13853) ([andrewm4894](https://github.com/andrewm4894)) +- Retry reading carrier, duplex, and speed files periodically [\#13850](https://github.com/netdata/netdata/pull/13850) ([vlvkobal](https://github.com/vlvkobal)) +- Properly guard commands when installing services for offline service managers. [\#13848](https://github.com/netdata/netdata/pull/13848) ([Ferroin](https://github.com/Ferroin)) +- fix tiers update frequency [\#13844](https://github.com/netdata/netdata/pull/13844) ([ktsaou](https://github.com/ktsaou)) +- Fix service installation on FreeBSD. [\#13842](https://github.com/netdata/netdata/pull/13842) ([Ferroin](https://github.com/Ferroin)) +- Cassandra dashboard description [\#13835](https://github.com/netdata/netdata/pull/13835) ([thiagoftsm](https://github.com/thiagoftsm)) +- Use mmap to read an extent from a datafile [\#13834](https://github.com/netdata/netdata/pull/13834) ([stelfrag](https://github.com/stelfrag)) +- chore\(health\): rm pihole\_blocklist\_gravity\_file\_existence\_state [\#13826](https://github.com/netdata/netdata/pull/13826) ([ilyam8](https://github.com/ilyam8)) +- Improve error and warning messages in the kickstart script. [\#13825](https://github.com/netdata/netdata/pull/13825) ([Ferroin](https://github.com/Ferroin)) +- Remove option to use MQTT 3 [\#13824](https://github.com/netdata/netdata/pull/13824) ([underhood](https://github.com/underhood)) +- extended processes function info from apps.plugin [\#13822](https://github.com/netdata/netdata/pull/13822) ([ktsaou](https://github.com/ktsaou)) +- Fix crash on child reconnect and lost metrics [\#13821](https://github.com/netdata/netdata/pull/13821) ([stelfrag](https://github.com/stelfrag)) +- Remove NFS readahead histogram [\#13819](https://github.com/netdata/netdata/pull/13819) ([vlvkobal](https://github.com/vlvkobal)) +- minor - add trace alloc to buildinfo [\#13817](https://github.com/netdata/netdata/pull/13817) ([underhood](https://github.com/underhood)) +- Fix exporting unit tests [\#13816](https://github.com/netdata/netdata/pull/13816) ([vlvkobal](https://github.com/vlvkobal)) +- Inject costallocz to mqtt\_websockets library and its children [\#13813](https://github.com/netdata/netdata/pull/13813) ([underhood](https://github.com/underhood)) +- overload libc memory allocators with custom ones to trace all allocations [\#13810](https://github.com/netdata/netdata/pull/13810) ([ktsaou](https://github.com/ktsaou)) +- bump go.d.plugin v0.40.4 [\#13808](https://github.com/netdata/netdata/pull/13808) ([ilyam8](https://github.com/ilyam8)) +- fix post-processing of contexts [\#13807](https://github.com/netdata/netdata/pull/13807) ([ktsaou](https://github.com/ktsaou)) +- Merge netstat, snmp, and snmp6 modules [\#13806](https://github.com/netdata/netdata/pull/13806) ([vlvkobal](https://github.com/vlvkobal)) +- fix warning when -Wfree-nonheap-object is used [\#13805](https://github.com/netdata/netdata/pull/13805) ([underhood](https://github.com/underhood)) +- ARAL optimal alloc size [\#13804](https://github.com/netdata/netdata/pull/13804) ([ktsaou](https://github.com/ktsaou)) +- internal log error, when passing NULL dictionary [\#13803](https://github.com/netdata/netdata/pull/13803) ([ktsaou](https://github.com/ktsaou)) +- Properly propagate errors from installer/updater to kickstart script. [\#13802](https://github.com/netdata/netdata/pull/13802) ([Ferroin](https://github.com/Ferroin)) +- Add up to date info on improving performance [\#13801](https://github.com/netdata/netdata/pull/13801) ([cakrit](https://github.com/cakrit)) +- Return memory freed properly [\#13799](https://github.com/netdata/netdata/pull/13799) ([stelfrag](https://github.com/stelfrag)) +- Use string\_freez instead of freez in rrdhost\_init\_timezone [\#13798](https://github.com/netdata/netdata/pull/13798) ([MrZammler](https://github.com/MrZammler)) +- Fix runtime directory ownership when installed as non-root user. [\#13797](https://github.com/netdata/netdata/pull/13797) ([Ferroin](https://github.com/Ferroin)) +- Fix minor typo in systemdunits.conf alert [\#13796](https://github.com/netdata/netdata/pull/13796) ([tkatsoulas](https://github.com/tkatsoulas)) +- Also enable rrdvars from health [\#13795](https://github.com/netdata/netdata/pull/13795) ([MrZammler](https://github.com/MrZammler)) +- feat\(python.d\): respect NETDATA\_INTERNALS\_MONITORING [\#13793](https://github.com/netdata/netdata/pull/13793) ([ilyam8](https://github.com/ilyam8)) +- Array Allocator Memory Leak Fix [\#13792](https://github.com/netdata/netdata/pull/13792) ([ktsaou](https://github.com/ktsaou)) +- Add variants of functions allowing callers to specify the time to use. [\#13791](https://github.com/netdata/netdata/pull/13791) ([vkalintiris](https://github.com/vkalintiris)) +- Remove extern from function declared in headers. [\#13790](https://github.com/netdata/netdata/pull/13790) ([vkalintiris](https://github.com/vkalintiris)) +- full memory tracking and profiling of Netdata Agent [\#13789](https://github.com/netdata/netdata/pull/13789) ([ktsaou](https://github.com/ktsaou)) +- allow disabling netdata monitoring section of the dashboard [\#13788](https://github.com/netdata/netdata/pull/13788) ([ktsaou](https://github.com/ktsaou)) +- Stop pulling in netcat as a mandatory dependency. [\#13787](https://github.com/netdata/netdata/pull/13787) ([Ferroin](https://github.com/Ferroin)) +- Initialize st-\>rrdvars from rrdset insert callback [\#13786](https://github.com/netdata/netdata/pull/13786) ([MrZammler](https://github.com/MrZammler)) +- Add Ubuntu 22.10 to supported distros, CI, and package builds. [\#13785](https://github.com/netdata/netdata/pull/13785) ([Ferroin](https://github.com/Ferroin)) +- minor - add host labels for ephemerality and nodes with unstable connections [\#13784](https://github.com/netdata/netdata/pull/13784) ([underhood](https://github.com/underhood)) +- Add a thread to asynchronously process metadata updates [\#13783](https://github.com/netdata/netdata/pull/13783) ([stelfrag](https://github.com/stelfrag)) +- Parser cleanup [\#13782](https://github.com/netdata/netdata/pull/13782) ([stelfrag](https://github.com/stelfrag)) +- allow netdata installer to install and run netdata as any user [\#13780](https://github.com/netdata/netdata/pull/13780) ([ktsaou](https://github.com/ktsaou)) +- Update libbpf 1.0.1 [\#13778](https://github.com/netdata/netdata/pull/13778) ([thiagoftsm](https://github.com/thiagoftsm)) +- Bump websockets submodule [\#13776](https://github.com/netdata/netdata/pull/13776) ([underhood](https://github.com/underhood)) +- Rename variable for old CentOS version [\#13775](https://github.com/netdata/netdata/pull/13775) ([thiagoftsm](https://github.com/thiagoftsm)) +- Further improvements to the new service installation code. [\#13774](https://github.com/netdata/netdata/pull/13774) ([Ferroin](https://github.com/Ferroin)) +- Pandas collector [\#13773](https://github.com/netdata/netdata/pull/13773) ([andrewm4894](https://github.com/andrewm4894)) +- dbengine free from RRDSET and RRDDIM [\#13772](https://github.com/netdata/netdata/pull/13772) ([ktsaou](https://github.com/ktsaou)) +- bump go.d.plugin v0.40.3 [\#13771](https://github.com/netdata/netdata/pull/13771) ([ilyam8](https://github.com/ilyam8)) +- Update fping plugin documentation with better details about the required version. [\#13765](https://github.com/netdata/netdata/pull/13765) ([Ferroin](https://github.com/Ferroin)) +- fix bad merge [\#13764](https://github.com/netdata/netdata/pull/13764) ([ktsaou](https://github.com/ktsaou)) +- Remove anomaly rates chart. [\#13763](https://github.com/netdata/netdata/pull/13763) ([vkalintiris](https://github.com/vkalintiris)) +- add 1m delay for tcp reset alarms [\#13761](https://github.com/netdata/netdata/pull/13761) ([ilyam8](https://github.com/ilyam8)) +- Use /bin/sh instead of ls to detect glibc [\#13758](https://github.com/netdata/netdata/pull/13758) ([MrZammler](https://github.com/MrZammler)) +- Add ZFS rate charts [\#13757](https://github.com/netdata/netdata/pull/13757) ([vlvkobal](https://github.com/vlvkobal)) +- Count currently streaming senders on the localhost [\#13755](https://github.com/netdata/netdata/pull/13755) ([MrZammler](https://github.com/MrZammler)) +- Fix streaming crash when child reconnects and is archived on the parent [\#13754](https://github.com/netdata/netdata/pull/13754) ([stelfrag](https://github.com/stelfrag)) +- Add CloudLinux OS detection to the updater script [\#13752](https://github.com/netdata/netdata/pull/13752) ([Pulseeey](https://github.com/Pulseeey)) +- Add CloudLinux OS detection to kickstart [\#13750](https://github.com/netdata/netdata/pull/13750) ([Pulseeey](https://github.com/Pulseeey)) +- bump go.d v0.40.2 [\#13747](https://github.com/netdata/netdata/pull/13747) ([ilyam8](https://github.com/ilyam8)) +- fix\(python.d\): set correct label source for \_collect\_job label [\#13746](https://github.com/netdata/netdata/pull/13746) ([ilyam8](https://github.com/ilyam8)) +- Provide Details on Label Filtering/Custom Labels [\#13745](https://github.com/netdata/netdata/pull/13745) ([DShreve2](https://github.com/DShreve2)) +- Fix handling of temporary directories in kickstart code. [\#13744](https://github.com/netdata/netdata/pull/13744) ([Ferroin](https://github.com/Ferroin)) +- Dont send NodeInfo during first database cleanup [\#13740](https://github.com/netdata/netdata/pull/13740) ([MrZammler](https://github.com/MrZammler)) +- CMake - add possibility to build without ACLK [\#13736](https://github.com/netdata/netdata/pull/13736) ([underhood](https://github.com/underhood)) +- Change cast to remove coverity warnings [\#13735](https://github.com/netdata/netdata/pull/13735) ([thiagoftsm](https://github.com/thiagoftsm)) +- Do not try to start an archived host in dbengine if dbengine is not compiled [\#13724](https://github.com/netdata/netdata/pull/13724) ([stelfrag](https://github.com/stelfrag)) +- Allow netdata plugins to expose functions for querying more information about specific charts [\#13720](https://github.com/netdata/netdata/pull/13720) ([ktsaou](https://github.com/ktsaou)) +- feat\(health\): add new Redis alarms [\#13715](https://github.com/netdata/netdata/pull/13715) ([ilyam8](https://github.com/ilyam8)) +- Health thread per host [\#13712](https://github.com/netdata/netdata/pull/13712) ([MrZammler](https://github.com/MrZammler)) +- Faster streaming by 25% on the child [\#13708](https://github.com/netdata/netdata/pull/13708) ([ktsaou](https://github.com/ktsaou)) +- Do not create train/predict dimensions meant for tracking anomaly rates. [\#13707](https://github.com/netdata/netdata/pull/13707) ([vkalintiris](https://github.com/vkalintiris)) +- Update exporting unit tests [\#13706](https://github.com/netdata/netdata/pull/13706) ([vlvkobal](https://github.com/vlvkobal)) +- bump go.d.plugin to v0.40.1 [\#13704](https://github.com/netdata/netdata/pull/13704) ([ilyam8](https://github.com/ilyam8)) +- Build judy even without dbengine [\#13703](https://github.com/netdata/netdata/pull/13703) ([underhood](https://github.com/underhood)) +- alarms collector: ability to exclude certain alarms via config [\#13701](https://github.com/netdata/netdata/pull/13701) ([andrewm4894](https://github.com/andrewm4894)) +- Fix inconsistent alert class names [\#13699](https://github.com/netdata/netdata/pull/13699) ([ralphm](https://github.com/ralphm)) +- disable Postgres last vacuum/analyze alarms [\#13698](https://github.com/netdata/netdata/pull/13698) ([ilyam8](https://github.com/ilyam8)) +- QUERY\_TARGET: new query engine for Netdata Agent [\#13697](https://github.com/netdata/netdata/pull/13697) ([ktsaou](https://github.com/ktsaou)) +- Update dashboard to version v2.29.1. [\#13696](https://github.com/netdata/netdata/pull/13696) ([netdatabot](https://github.com/netdatabot)) +- docs: nvidia-smi in a container limitation note [\#13695](https://github.com/netdata/netdata/pull/13695) ([ilyam8](https://github.com/ilyam8)) +- Use CMake generated config.h also in out of tree CMake build [\#13692](https://github.com/netdata/netdata/pull/13692) ([underhood](https://github.com/underhood)) +- Add info for Docker containers about using hostname from host. [\#13685](https://github.com/netdata/netdata/pull/13685) ([Ferroin](https://github.com/Ferroin)) +- add node level AR based example [\#13684](https://github.com/netdata/netdata/pull/13684) ([andrewm4894](https://github.com/andrewm4894)) +- Store nulls instead of empty strings in health tables [\#13683](https://github.com/netdata/netdata/pull/13683) ([MrZammler](https://github.com/MrZammler)) +- Fix warnings during compilation time on ARM \(32 bits\) [\#13681](https://github.com/netdata/netdata/pull/13681) ([thiagoftsm](https://github.com/thiagoftsm)) +- dictionary updated documentation and cosmetics [\#13679](https://github.com/netdata/netdata/pull/13679) ([ktsaou](https://github.com/ktsaou)) +- disable internal log [\#13678](https://github.com/netdata/netdata/pull/13678) ([ktsaou](https://github.com/ktsaou)) +- bump go.d.plugin v0.40.0 [\#13675](https://github.com/netdata/netdata/pull/13675) ([ilyam8](https://github.com/ilyam8)) +- remove \_instance\_family label [\#13674](https://github.com/netdata/netdata/pull/13674) ([ilyam8](https://github.com/ilyam8)) +- fix typo not deleting collected flag; force removing collected flag on child disconnect [\#13672](https://github.com/netdata/netdata/pull/13672) ([ktsaou](https://github.com/ktsaou)) +- feat\(health\): add Postgres alarms [\#13671](https://github.com/netdata/netdata/pull/13671) ([ilyam8](https://github.com/ilyam8)) +- add proxysql dashboard info [\#13669](https://github.com/netdata/netdata/pull/13669) ([ilyam8](https://github.com/ilyam8)) +- Additional sqlite statistics [\#13668](https://github.com/netdata/netdata/pull/13668) ([stelfrag](https://github.com/stelfrag)) +- Advance the buffer properly to scan the journal file [\#13666](https://github.com/netdata/netdata/pull/13666) ([stelfrag](https://github.com/stelfrag)) +- Add sqlite page cache hit and miss statistics [\#13665](https://github.com/netdata/netdata/pull/13665) ([stelfrag](https://github.com/stelfrag)) +- bump go.d.plugin to v0.39.0 [\#13662](https://github.com/netdata/netdata/pull/13662) ([ilyam8](https://github.com/ilyam8)) +- update Postgres dashboard info [\#13661](https://github.com/netdata/netdata/pull/13661) ([ilyam8](https://github.com/ilyam8)) +- Use mmap if possible during startup for journal replay [\#13660](https://github.com/netdata/netdata/pull/13660) ([stelfrag](https://github.com/stelfrag)) +- Remove anomaly detector [\#13657](https://github.com/netdata/netdata/pull/13657) ([vkalintiris](https://github.com/vkalintiris)) +- Update dashboard to version v2.29.0. [\#13654](https://github.com/netdata/netdata/pull/13654) ([netdatabot](https://github.com/netdatabot)) +- Fix container virtualization info [\#13653](https://github.com/netdata/netdata/pull/13653) ([vlvkobal](https://github.com/vlvkobal)) +- Do not free AR dimensions from within ML. [\#13651](https://github.com/netdata/netdata/pull/13651) ([vkalintiris](https://github.com/vkalintiris)) +- Remove Chart/Dim based communication [\#13650](https://github.com/netdata/netdata/pull/13650) ([underhood](https://github.com/underhood)) +- Improve agent shutdown time [\#13649](https://github.com/netdata/netdata/pull/13649) ([stelfrag](https://github.com/stelfrag)) +- add \_collect\_job label to python.d/\* charts [\#13648](https://github.com/netdata/netdata/pull/13648) ([ilyam8](https://github.com/ilyam8)) +- RRD structures managed by dictionaries [\#13646](https://github.com/netdata/netdata/pull/13646) ([ktsaou](https://github.com/ktsaou)) +- fix rrdcontexts left in the post-processing queue from the garbage collector [\#13645](https://github.com/netdata/netdata/pull/13645) ([ktsaou](https://github.com/ktsaou)) +- apps.plugin: Re-add `chrome` to the `webbrowser` group. [\#13642](https://github.com/netdata/netdata/pull/13642) ([Ferroin](https://github.com/Ferroin)) +- Fix a memory leak on archived host creation [\#13641](https://github.com/netdata/netdata/pull/13641) ([stelfrag](https://github.com/stelfrag)) +- fix compile issues [\#13640](https://github.com/netdata/netdata/pull/13640) ([ktsaou](https://github.com/ktsaou)) +- Obsolete RRDSET state [\#13635](https://github.com/netdata/netdata/pull/13635) ([ktsaou](https://github.com/ktsaou)) +- Updated tc.plugin \(linux bandwidth QoS\) [\#13634](https://github.com/netdata/netdata/pull/13634) ([ktsaou](https://github.com/ktsaou)) +- Fix worker utilization cleanup [\#13633](https://github.com/netdata/netdata/pull/13633) ([stelfrag](https://github.com/stelfrag)) +- remove forgotten avl structure from rrdcalc [\#13632](https://github.com/netdata/netdata/pull/13632) ([ktsaou](https://github.com/ktsaou)) +- Deaggregate the `gui` and `email` app groupx and improve GUI coverage. [\#13631](https://github.com/netdata/netdata/pull/13631) ([Ferroin](https://github.com/Ferroin)) +- Faster rrdcontext [\#13629](https://github.com/netdata/netdata/pull/13629) ([ktsaou](https://github.com/ktsaou)) +- Update uninstaller documentation. [\#13627](https://github.com/netdata/netdata/pull/13627) ([Ferroin](https://github.com/Ferroin)) +- eBPF different improvements [\#13624](https://github.com/netdata/netdata/pull/13624) ([thiagoftsm](https://github.com/thiagoftsm)) +- adjust systemdunits alarms [\#13623](https://github.com/netdata/netdata/pull/13623) ([ilyam8](https://github.com/ilyam8)) +- fix apps plugin users charts descriptipon [\#13621](https://github.com/netdata/netdata/pull/13621) ([ilyam8](https://github.com/ilyam8)) +- add Postgres total connection utilization alarm [\#13620](https://github.com/netdata/netdata/pull/13620) ([ilyam8](https://github.com/ilyam8)) +- update Postgres "connections" dashboard info [\#13619](https://github.com/netdata/netdata/pull/13619) ([ilyam8](https://github.com/ilyam8)) +- Assorted updates for apps\_groups.conf. [\#13618](https://github.com/netdata/netdata/pull/13618) ([Ferroin](https://github.com/Ferroin)) +- add spiceproxy to proxmox group [\#13615](https://github.com/netdata/netdata/pull/13615) ([ilyam8](https://github.com/ilyam8)) +- Improve coverage of Linux kernel threads in apps\_groups.conf [\#13612](https://github.com/netdata/netdata/pull/13612) ([Ferroin](https://github.com/Ferroin)) +- Clean chart hash map [\#13611](https://github.com/netdata/netdata/pull/13611) ([stelfrag](https://github.com/stelfrag)) +- Update dashboard to version v2.28.8. [\#13609](https://github.com/netdata/netdata/pull/13609) ([netdatabot](https://github.com/netdatabot)) +- Don't try to load db rows when chart\_id or dim\_id is null [\#13608](https://github.com/netdata/netdata/pull/13608) ([MrZammler](https://github.com/MrZammler)) +- Add info text for wal and update for checkpoints [\#13607](https://github.com/netdata/netdata/pull/13607) ([shyamvalsan](https://github.com/shyamvalsan)) +- bump go.d.plugin to v0.38.0 [\#13603](https://github.com/netdata/netdata/pull/13603) ([ilyam8](https://github.com/ilyam8)) +- Use prepared statements for context related queries [\#13602](https://github.com/netdata/netdata/pull/13602) ([stelfrag](https://github.com/stelfrag)) +- fix\(cgroups.plugin\): fix chart id length check [\#13601](https://github.com/netdata/netdata/pull/13601) ([ilyam8](https://github.com/ilyam8)) +- Temporary fix for command injection vulnerability in GHA workflow. [\#13600](https://github.com/netdata/netdata/pull/13600) ([Ferroin](https://github.com/Ferroin)) +- update logind dashboard info [\#13597](https://github.com/netdata/netdata/pull/13597) ([ilyam8](https://github.com/ilyam8)) +- Add link to the performance optimization guide [\#13595](https://github.com/netdata/netdata/pull/13595) ([cakrit](https://github.com/cakrit)) +- sqlite3 global statistics [\#13594](https://github.com/netdata/netdata/pull/13594) ([ktsaou](https://github.com/ktsaou)) +- feat\(python.d/nvidia\_smi\): collect power state [\#13580](https://github.com/netdata/netdata/pull/13580) ([ilyam8](https://github.com/ilyam8)) +- fix\(python.d/nvidia\_smi\): repsect update\_every for polling [\#13579](https://github.com/netdata/netdata/pull/13579) ([ilyam8](https://github.com/ilyam8)) +- prevent crash on rrdcontext apis when rrdcontexts is not initialized [\#13578](https://github.com/netdata/netdata/pull/13578) ([ktsaou](https://github.com/ktsaou)) +- CMake improvements part 1 [\#13575](https://github.com/netdata/netdata/pull/13575) ([underhood](https://github.com/underhood)) +- bump go.d.plugin to v0.37.2 [\#13574](https://github.com/netdata/netdata/pull/13574) ([ilyam8](https://github.com/ilyam8)) +- Updating info for postgreqsql metrics [\#13573](https://github.com/netdata/netdata/pull/13573) ([shyamvalsan](https://github.com/shyamvalsan)) +- add `apt` to `apps_groups.conf` [\#13571](https://github.com/netdata/netdata/pull/13571) ([andrewm4894](https://github.com/andrewm4894)) +- Deduplicate all netdata strings [\#13570](https://github.com/netdata/netdata/pull/13570) ([ktsaou](https://github.com/ktsaou)) +- eBPF cmake missing include dir [\#13568](https://github.com/netdata/netdata/pull/13568) ([underhood](https://github.com/underhood)) +- chore: removing logging that a chart collection in the same interpolation point [\#13567](https://github.com/netdata/netdata/pull/13567) ([ilyam8](https://github.com/ilyam8)) +- add more monitoring tools to `apps_groups.conf` [\#13566](https://github.com/netdata/netdata/pull/13566) ([andrewm4894](https://github.com/andrewm4894)) +- fix\(health.d/mysql\): adjust `mysql_galera_cluster_size_max_2m` lookup to make time in warn/crit predictable [\#13563](https://github.com/netdata/netdata/pull/13563) ([ilyam8](https://github.com/ilyam8)) +- Update dashboard to version v2.28.6. [\#13562](https://github.com/netdata/netdata/pull/13562) ([netdatabot](https://github.com/netdatabot)) +- Prefer context attributes from non archived charts [\#13559](https://github.com/netdata/netdata/pull/13559) ([MrZammler](https://github.com/MrZammler)) +- bump go.d.plugin to v0.37.1 [\#13555](https://github.com/netdata/netdata/pull/13555) ([ilyam8](https://github.com/ilyam8)) +- Fix coverity 380387 [\#13551](https://github.com/netdata/netdata/pull/13551) ([MrZammler](https://github.com/MrZammler)) +- add docker dashboard info [\#13547](https://github.com/netdata/netdata/pull/13547) ([ilyam8](https://github.com/ilyam8)) +- bump go.d.plugin to v0.37.0 [\#13546](https://github.com/netdata/netdata/pull/13546) ([ilyam8](https://github.com/ilyam8)) +- feat\(python.d/sensors\): discover chips, features at runtime [\#13545](https://github.com/netdata/netdata/pull/13545) ([ilyam8](https://github.com/ilyam8)) +- Remove aclk\_api.\[ch\] [\#13540](https://github.com/netdata/netdata/pull/13540) ([underhood](https://github.com/underhood)) +- Cleanup of APIs [\#13539](https://github.com/netdata/netdata/pull/13539) ([underhood](https://github.com/underhood)) +- bump go.d version to v0.36.0 [\#13538](https://github.com/netdata/netdata/pull/13538) ([ilyam8](https://github.com/ilyam8)) +- chore\(python.d\): rename dockerd job on lock registration [\#13537](https://github.com/netdata/netdata/pull/13537) ([ilyam8](https://github.com/ilyam8)) +- Update MacOS community support details [\#13536](https://github.com/netdata/netdata/pull/13536) ([DShreve2](https://github.com/DShreve2)) +- Fix a crash when xen libraries are misconfigured [\#13535](https://github.com/netdata/netdata/pull/13535) ([vlvkobal](https://github.com/vlvkobal)) +- Add summary dashboard for PostgreSQL [\#13534](https://github.com/netdata/netdata/pull/13534) ([shyamvalsan](https://github.com/shyamvalsan)) +- add `jupyter` to `apps_groups.conf` [\#13533](https://github.com/netdata/netdata/pull/13533) ([andrewm4894](https://github.com/andrewm4894)) +- Schedule next rotation based on absolute time [\#13531](https://github.com/netdata/netdata/pull/13531) ([MrZammler](https://github.com/MrZammler)) +- Improve PID monitoring \(step 2\) [\#13530](https://github.com/netdata/netdata/pull/13530) ([thiagoftsm](https://github.com/thiagoftsm)) +- fix\(health\): set default curl connection timeout if not set [\#13529](https://github.com/netdata/netdata/pull/13529) ([ilyam8](https://github.com/ilyam8)) +- Update FreeIPMI and CUPS plugin documentation. [\#13526](https://github.com/netdata/netdata/pull/13526) ([Ferroin](https://github.com/Ferroin)) +- Use LVM UUIDs in chart ids for logical volumes [\#13525](https://github.com/netdata/netdata/pull/13525) ([vlvkobal](https://github.com/vlvkobal)) +- fix\(cgroups.plugin\): use Docker API for name resolution when Docker is a snap package [\#13523](https://github.com/netdata/netdata/pull/13523) ([ilyam8](https://github.com/ilyam8)) +- remove reference to charts now in netdata monitoring [\#13521](https://github.com/netdata/netdata/pull/13521) ([andrewm4894](https://github.com/andrewm4894)) - fix\(ci\): fix fetching tags in Build workflow [\#13517](https://github.com/netdata/netdata/pull/13517) ([ilyam8](https://github.com/ilyam8)) - docs\(postfix\): add a note about `authorized_mailq_users` [\#13515](https://github.com/netdata/netdata/pull/13515) ([ilyam8](https://github.com/ilyam8)) - Remove extra U from log message [\#13514](https://github.com/netdata/netdata/pull/13514) ([uplime](https://github.com/uplime)) @@ -14,8 +295,14 @@ - fix\(packaging\): add CAP\_NET\_ADMIN for go.d.plugin [\#13507](https://github.com/netdata/netdata/pull/13507) ([ilyam8](https://github.com/ilyam8)) - netdata.service: Update PIDFile to avoid systemd legacy path warning [\#13504](https://github.com/netdata/netdata/pull/13504) ([candrews](https://github.com/candrews)) - chore\(python.d\): remove python.d/\* announced in v1.36.0 deprecation notice [\#13503](https://github.com/netdata/netdata/pull/13503) ([ilyam8](https://github.com/ilyam8)) +- Add Fedora 37 to CI and package builds. [\#13489](https://github.com/netdata/netdata/pull/13489) ([Ferroin](https://github.com/Ferroin)) +- Overhaul handling of installation of Netdata as a system service. [\#13451](https://github.com/netdata/netdata/pull/13451) ([Ferroin](https://github.com/Ferroin)) - reduce memcpy and memory usage on mqtt5 [\#13450](https://github.com/netdata/netdata/pull/13450) ([underhood](https://github.com/underhood)) -- Modify PID monitoring \(ebpf.plugin\) [\#13397](https://github.com/netdata/netdata/pull/13397) ([thiagoftsm](https://github.com/thiagoftsm)) +- Remove Alpine 3.13 from CI and official support. [\#13415](https://github.com/netdata/netdata/pull/13415) ([Ferroin](https://github.com/Ferroin)) + +## [v1.36.1](https://github.com/netdata/netdata/tree/v1.36.1) (2022-08-15) + +[Full Changelog](https://github.com/netdata/netdata/compare/v1.36.0...v1.36.1) ## [v1.36.0](https://github.com/netdata/netdata/tree/v1.36.0) (2022-08-10) @@ -67,165 +354,6 @@ - Set value to SN\_EMPTY\_SLOT if flags is SN\_EMPTY\_SLOT [\#13417](https://github.com/netdata/netdata/pull/13417) ([MrZammler](https://github.com/MrZammler)) - Add missing comma \(handle coverity warning CID 379360\) [\#13413](https://github.com/netdata/netdata/pull/13413) ([stelfrag](https://github.com/stelfrag)) - codacy/lgtm ignore judy sources [\#13411](https://github.com/netdata/netdata/pull/13411) ([underhood](https://github.com/underhood)) -- Send chart context with alert events to the cloud [\#13409](https://github.com/netdata/netdata/pull/13409) ([MrZammler](https://github.com/MrZammler)) -- Remove SIGSEGV and SIGABRT \(ebpf.plugin\) [\#13407](https://github.com/netdata/netdata/pull/13407) ([thiagoftsm](https://github.com/thiagoftsm)) -- minor fixes on metadata fields [\#13406](https://github.com/netdata/netdata/pull/13406) ([tkatsoulas](https://github.com/tkatsoulas)) -- chore\(health\): remove py web\_log alarms [\#13404](https://github.com/netdata/netdata/pull/13404) ([ilyam8](https://github.com/ilyam8)) -- Store host system information in the database [\#13402](https://github.com/netdata/netdata/pull/13402) ([stelfrag](https://github.com/stelfrag)) -- Fix coverity issue 379240 \(Unchecked return value\) [\#13401](https://github.com/netdata/netdata/pull/13401) ([stelfrag](https://github.com/stelfrag)) -- Fix netdata-updater.sh sha256sum on BSDs [\#13391](https://github.com/netdata/netdata/pull/13391) ([tnyeanderson](https://github.com/tnyeanderson)) -- chore\(python.d\): clarify haproxy module readme [\#13388](https://github.com/netdata/netdata/pull/13388) ([ilyam8](https://github.com/ilyam8)) -- Fix bitmap unit tests [\#13374](https://github.com/netdata/netdata/pull/13374) ([stelfrag](https://github.com/stelfrag)) -- chore\(dashboard\): update chrony dashboard info [\#13371](https://github.com/netdata/netdata/pull/13371) ([ilyam8](https://github.com/ilyam8)) -- chore\(python.d\): remove python.d/\* announced in v1.35.0 deprecation notice [\#13370](https://github.com/netdata/netdata/pull/13370) ([ilyam8](https://github.com/ilyam8)) -- bump go.d.plugin version to v0.33.1 [\#13369](https://github.com/netdata/netdata/pull/13369) ([ilyam8](https://github.com/ilyam8)) -- Add Oracle Linux 9 to officially supported platforms. [\#13367](https://github.com/netdata/netdata/pull/13367) ([Ferroin](https://github.com/Ferroin)) -- Address Coverity issues [\#13364](https://github.com/netdata/netdata/pull/13364) ([stelfrag](https://github.com/stelfrag)) -- chore\(python.d\): improve config file parsing error message [\#13363](https://github.com/netdata/netdata/pull/13363) ([ilyam8](https://github.com/ilyam8)) -- in source Judy [\#13362](https://github.com/netdata/netdata/pull/13362) ([underhood](https://github.com/underhood)) -- Add additional Docker image build with debug info included. [\#13359](https://github.com/netdata/netdata/pull/13359) ([Ferroin](https://github.com/Ferroin)) -- Move host tags to netdata\_info [\#13358](https://github.com/netdata/netdata/pull/13358) ([vlvkobal](https://github.com/vlvkobal)) -- Get rid of extra comma in OpenTSDB exporting [\#13355](https://github.com/netdata/netdata/pull/13355) ([vlvkobal](https://github.com/vlvkobal)) -- Fix chart update ebpf.plugin [\#13351](https://github.com/netdata/netdata/pull/13351) ([thiagoftsm](https://github.com/thiagoftsm)) -- add job to move bugs to project board [\#13350](https://github.com/netdata/netdata/pull/13350) ([hugovalente-pm](https://github.com/hugovalente-pm)) -- added another way to get ansible plays [\#13349](https://github.com/netdata/netdata/pull/13349) ([mhkarimi1383](https://github.com/mhkarimi1383)) -- Send node info message sooner [\#13348](https://github.com/netdata/netdata/pull/13348) ([MrZammler](https://github.com/MrZammler)) -- query engine: omit first point if not needed [\#13345](https://github.com/netdata/netdata/pull/13345) ([ktsaou](https://github.com/ktsaou)) -- fix 32bit calculation on array allocator [\#13343](https://github.com/netdata/netdata/pull/13343) ([ktsaou](https://github.com/ktsaou)) -- fix crash on start on slow disks because ml is initialized before dbengine starts [\#13342](https://github.com/netdata/netdata/pull/13342) ([ktsaou](https://github.com/ktsaou)) -- fix\(packaging\): respect CFLAGS arg when building Docker image [\#13340](https://github.com/netdata/netdata/pull/13340) ([ilyam8](https://github.com/ilyam8)) -- add github stars badge to readme [\#13338](https://github.com/netdata/netdata/pull/13338) ([andrewm4894](https://github.com/andrewm4894)) -- Fix coverity 379241 [\#13336](https://github.com/netdata/netdata/pull/13336) ([MrZammler](https://github.com/MrZammler)) -- Rrdcontext [\#13335](https://github.com/netdata/netdata/pull/13335) ([ktsaou](https://github.com/ktsaou)) -- Detect stored metric size by page type [\#13334](https://github.com/netdata/netdata/pull/13334) ([stelfrag](https://github.com/stelfrag)) -- Silence compile warnings on external source [\#13332](https://github.com/netdata/netdata/pull/13332) ([MrZammler](https://github.com/MrZammler)) -- UpdateNodeCollectors message [\#13330](https://github.com/netdata/netdata/pull/13330) ([MrZammler](https://github.com/MrZammler)) -- Cid 379238 379238 [\#13328](https://github.com/netdata/netdata/pull/13328) ([stelfrag](https://github.com/stelfrag)) -- Docs fix metric storage [\#13327](https://github.com/netdata/netdata/pull/13327) ([tkatsoulas](https://github.com/tkatsoulas)) -- Fix two helgrind reports [\#13325](https://github.com/netdata/netdata/pull/13325) ([vkalintiris](https://github.com/vkalintiris)) -- fix\(cgroups.plugin\): adjust kubepods patterns to filter pods when using Kind cluster [\#13324](https://github.com/netdata/netdata/pull/13324) ([ilyam8](https://github.com/ilyam8)) -- Add link to docker config section [\#13323](https://github.com/netdata/netdata/pull/13323) ([cakrit](https://github.com/cakrit)) -- Guide for troubleshooting Agent with Cloud connection for new nodes [\#13322](https://github.com/netdata/netdata/pull/13322) ([Ancairon](https://github.com/Ancairon)) -- fix\(apps.plugin\): adjust `zmstat*` pattern to exclude zoneminder scripts [\#13314](https://github.com/netdata/netdata/pull/13314) ([ilyam8](https://github.com/ilyam8)) -- array allocator for dbengine page descriptors [\#13312](https://github.com/netdata/netdata/pull/13312) ([ktsaou](https://github.com/ktsaou)) -- fix\(health\): disable go.d last collected alarm for prometheus module [\#13309](https://github.com/netdata/netdata/pull/13309) ([ilyam8](https://github.com/ilyam8)) -- Explicitly skip uploads and notifications in third-party repositories. [\#13308](https://github.com/netdata/netdata/pull/13308) ([Ferroin](https://github.com/Ferroin)) -- Protect shared variables with log lock. [\#13306](https://github.com/netdata/netdata/pull/13306) ([vkalintiris](https://github.com/vkalintiris)) -- Keep rc before freeing it during labels unlink alarms [\#13305](https://github.com/netdata/netdata/pull/13305) ([MrZammler](https://github.com/MrZammler)) -- fix\(cgroups.plugin\): adjust kubepods regex to fix name resolution in a kind cluster [\#13302](https://github.com/netdata/netdata/pull/13302) ([ilyam8](https://github.com/ilyam8)) -- Null terminate string if file read was not successful [\#13299](https://github.com/netdata/netdata/pull/13299) ([stelfrag](https://github.com/stelfrag)) -- fix\(health\): fix incorrect Redis dimension names [\#13296](https://github.com/netdata/netdata/pull/13296) ([ilyam8](https://github.com/ilyam8)) -- chore: remove python-mysql from install-required-packages.sh [\#13288](https://github.com/netdata/netdata/pull/13288) ([ilyam8](https://github.com/ilyam8)) -- Use new MQTT as default \(revert \#13258\)" [\#13287](https://github.com/netdata/netdata/pull/13287) ([underhood](https://github.com/underhood)) -- query engine fixes for alarms and dashboards [\#13282](https://github.com/netdata/netdata/pull/13282) ([ktsaou](https://github.com/ktsaou)) -- Better ACLK debug communication log [\#13281](https://github.com/netdata/netdata/pull/13281) ([underhood](https://github.com/underhood)) -- bump go.d.plugin version to v0.33.0 [\#13280](https://github.com/netdata/netdata/pull/13280) ([ilyam8](https://github.com/ilyam8)) -- Fixes vbi parser in mqtt5 implementation [\#13277](https://github.com/netdata/netdata/pull/13277) ([underhood](https://github.com/underhood)) -- Fix alignment in charts endpoint [\#13275](https://github.com/netdata/netdata/pull/13275) ([thiagoftsm](https://github.com/thiagoftsm)) -- Dont print io errors for cgroups [\#13274](https://github.com/netdata/netdata/pull/13274) ([vlvkobal](https://github.com/vlvkobal)) -- Pluginsd doc [\#13273](https://github.com/netdata/netdata/pull/13273) ([thiagoftsm](https://github.com/thiagoftsm)) -- Remove obsolete --use-system-lws option from netdata-installer.sh help [\#13272](https://github.com/netdata/netdata/pull/13272) ([Dim-P](https://github.com/Dim-P)) -- Rename the chart of real memory usage in FreeBSD [\#13271](https://github.com/netdata/netdata/pull/13271) ([vlvkobal](https://github.com/vlvkobal)) -- Update documentation about our REST API documentation. [\#13269](https://github.com/netdata/netdata/pull/13269) ([Ferroin](https://github.com/Ferroin)) -- Fix package build filtering on PRs. [\#13267](https://github.com/netdata/netdata/pull/13267) ([Ferroin](https://github.com/Ferroin)) -- Add document explaining how to proxy Netdata via H2O [\#13266](https://github.com/netdata/netdata/pull/13266) ([Ferroin](https://github.com/Ferroin)) -- chore\(python.d\): remove deprecated modules from python.d.conf [\#13264](https://github.com/netdata/netdata/pull/13264) ([ilyam8](https://github.com/ilyam8)) -- Multi-Tier database backend for long term metrics storage [\#13263](https://github.com/netdata/netdata/pull/13263) ([stelfrag](https://github.com/stelfrag)) -- Get rid of extra semicolon in Graphite exporting [\#13261](https://github.com/netdata/netdata/pull/13261) ([vlvkobal](https://github.com/vlvkobal)) -- fix RAM calculation on macOS in system-info [\#13260](https://github.com/netdata/netdata/pull/13260) ([ilyam8](https://github.com/ilyam8)) -- Ebpf issues [\#13259](https://github.com/netdata/netdata/pull/13259) ([thiagoftsm](https://github.com/thiagoftsm)) -- Use old mqtt implementation as default [\#13258](https://github.com/netdata/netdata/pull/13258) ([MrZammler](https://github.com/MrZammler)) -- Remove warnings while compiling ML on FreeBSD [\#13255](https://github.com/netdata/netdata/pull/13255) ([thiagoftsm](https://github.com/thiagoftsm)) -- Fix issues with DEB postinstall script. [\#13252](https://github.com/netdata/netdata/pull/13252) ([Ferroin](https://github.com/Ferroin)) -- Remove strftime from statements and use unixepoch instead [\#13250](https://github.com/netdata/netdata/pull/13250) ([stelfrag](https://github.com/stelfrag)) -- Query engine with natural and virtual points [\#13248](https://github.com/netdata/netdata/pull/13248) ([ktsaou](https://github.com/ktsaou)) -- Add fstype labels to disk charts [\#13245](https://github.com/netdata/netdata/pull/13245) ([vlvkobal](https://github.com/vlvkobal)) -- Don’t pull in GCC for build if Clang is already present. [\#13244](https://github.com/netdata/netdata/pull/13244) ([Ferroin](https://github.com/Ferroin)) -- Delay health until obsoletions check is complete [\#13239](https://github.com/netdata/netdata/pull/13239) ([MrZammler](https://github.com/MrZammler)) -- Improve anomaly detection guide [\#13238](https://github.com/netdata/netdata/pull/13238) ([andrewm4894](https://github.com/andrewm4894)) -- Implement PackageCloud cleanup [\#13236](https://github.com/netdata/netdata/pull/13236) ([maneamarius](https://github.com/maneamarius)) -- Bump repoconfig package version used in kickstart.sh [\#13235](https://github.com/netdata/netdata/pull/13235) ([Ferroin](https://github.com/Ferroin)) -- Updates the sqlite version in the agent [\#13233](https://github.com/netdata/netdata/pull/13233) ([stelfrag](https://github.com/stelfrag)) -- Migrate data when machine GUID changes [\#13232](https://github.com/netdata/netdata/pull/13232) ([stelfrag](https://github.com/stelfrag)) -- Add more sqlite unittests [\#13227](https://github.com/netdata/netdata/pull/13227) ([stelfrag](https://github.com/stelfrag)) -- ci: add issues to the Agent Board project workflow [\#13225](https://github.com/netdata/netdata/pull/13225) ([ilyam8](https://github.com/ilyam8)) -- Exporting/send variables [\#13221](https://github.com/netdata/netdata/pull/13221) ([boxjan](https://github.com/boxjan)) -- fix\(cgroups.plugin\): fix qemu VMs and LXC containers name resolution [\#13220](https://github.com/netdata/netdata/pull/13220) ([ilyam8](https://github.com/ilyam8)) -- netdata doubles [\#13217](https://github.com/netdata/netdata/pull/13217) ([ktsaou](https://github.com/ktsaou)) -- deduplicate mountinfo based on mount point [\#13215](https://github.com/netdata/netdata/pull/13215) ([ktsaou](https://github.com/ktsaou)) -- feat\(python.d\): load modules from user plugin directories \(NETDATA\_USER\_PLUGINS\_DIRS\) [\#13214](https://github.com/netdata/netdata/pull/13214) ([ilyam8](https://github.com/ilyam8)) -- Properly handle interactivity in the updater code. [\#13209](https://github.com/netdata/netdata/pull/13209) ([Ferroin](https://github.com/Ferroin)) -- Don’t use realpath to find kickstart source path. [\#13208](https://github.com/netdata/netdata/pull/13208) ([Ferroin](https://github.com/Ferroin)) -- Print INTERNAL BUG messages only when NETDATA\_INTERNAL\_CHECKS is enabled [\#13207](https://github.com/netdata/netdata/pull/13207) ([MrZammler](https://github.com/MrZammler)) -- Ensure tmpdir is set for every function that uses it. [\#13206](https://github.com/netdata/netdata/pull/13206) ([Ferroin](https://github.com/Ferroin)) -- Add user plugin dirs to environment [\#13203](https://github.com/netdata/netdata/pull/13203) ([vlvkobal](https://github.com/vlvkobal)) -- Fix cgroups netdev chart labels [\#13200](https://github.com/netdata/netdata/pull/13200) ([vlvkobal](https://github.com/vlvkobal)) -- Add hostname in the worker structure to avoid constant lookups [\#13199](https://github.com/netdata/netdata/pull/13199) ([stelfrag](https://github.com/stelfrag)) -- Rpm group creation [\#13197](https://github.com/netdata/netdata/pull/13197) ([iigorkarpov](https://github.com/iigorkarpov)) -- Allow for an easy way to do metadata migrations [\#13196](https://github.com/netdata/netdata/pull/13196) ([stelfrag](https://github.com/stelfrag)) -- Dictionaries with reference counters and full deletion support during traversal [\#13195](https://github.com/netdata/netdata/pull/13195) ([ktsaou](https://github.com/ktsaou)) -- Add configuration for dbengine page fetch timeout and retry count [\#13194](https://github.com/netdata/netdata/pull/13194) ([stelfrag](https://github.com/stelfrag)) -- Clean sqlite prepared statements on thread shutdown [\#13193](https://github.com/netdata/netdata/pull/13193) ([stelfrag](https://github.com/stelfrag)) -- Update dashboard to version v2.26.5. [\#13192](https://github.com/netdata/netdata/pull/13192) ([netdatabot](https://github.com/netdatabot)) -- chore\(netdata-installer\): remove a call to 'cleanup\_old\_netdata\_updater\(\)' because it is no longer exists [\#13189](https://github.com/netdata/netdata/pull/13189) ([ilyam8](https://github.com/ilyam8)) -- feat\(python.d/smartd\_log\): add 2nd job that tries to read from '/var/lib/smartmontools/' [\#13188](https://github.com/netdata/netdata/pull/13188) ([ilyam8](https://github.com/ilyam8)) -- Add type label for network interfaces [\#13187](https://github.com/netdata/netdata/pull/13187) ([vlvkobal](https://github.com/vlvkobal)) -- fix\(freebsd.plugin\): fix wired/cached/avail memory calculation on FreeBSD with ZFS [\#13183](https://github.com/netdata/netdata/pull/13183) ([ilyam8](https://github.com/ilyam8)) -- make configuration example clearer [\#13182](https://github.com/netdata/netdata/pull/13182) ([andrewm4894](https://github.com/andrewm4894)) -- add k8s\_state dashboard\_info [\#13181](https://github.com/netdata/netdata/pull/13181) ([ilyam8](https://github.com/ilyam8)) -- Docs housekeeping [\#13179](https://github.com/netdata/netdata/pull/13179) ([tkatsoulas](https://github.com/tkatsoulas)) -- Update dashboard to version v2.26.2. [\#13177](https://github.com/netdata/netdata/pull/13177) ([netdatabot](https://github.com/netdatabot)) -- feat\(proc/proc\_net\_dev\): add dim per phys link state to the "Interface Physical Link State" chart [\#13176](https://github.com/netdata/netdata/pull/13176) ([ilyam8](https://github.com/ilyam8)) -- set default for `minimum num samples to train` to `900` [\#13174](https://github.com/netdata/netdata/pull/13174) ([andrewm4894](https://github.com/andrewm4894)) -- Add ml alerts examples [\#13173](https://github.com/netdata/netdata/pull/13173) ([andrewm4894](https://github.com/andrewm4894)) -- Revert "Configurable storage engine for Netdata agents: step 3 \(\#12892\)" [\#13171](https://github.com/netdata/netdata/pull/13171) ([vkalintiris](https://github.com/vkalintiris)) -- Remove warnings when openssl 3 is used. [\#13170](https://github.com/netdata/netdata/pull/13170) ([thiagoftsm](https://github.com/thiagoftsm)) -- Don’t manipulate positional parameters in DEB postinst script. [\#13169](https://github.com/netdata/netdata/pull/13169) ([Ferroin](https://github.com/Ferroin)) -- Fix coverity issues [\#13168](https://github.com/netdata/netdata/pull/13168) ([stelfrag](https://github.com/stelfrag)) -- feat\(proc/proc\_net\_dev\): add dim per operstate to the "Interface Operational State" chart [\#13167](https://github.com/netdata/netdata/pull/13167) ([ilyam8](https://github.com/ilyam8)) -- feat\(proc/proc\_net\_dev\): add dim per duplex state to the "Interface Duplex State" chart [\#13165](https://github.com/netdata/netdata/pull/13165) ([ilyam8](https://github.com/ilyam8)) -- allow traversing null-value dictionaries [\#13162](https://github.com/netdata/netdata/pull/13162) ([ktsaou](https://github.com/ktsaou)) -- Use memset to mark the empty words in the quoted\_strings\_splitter function [\#13161](https://github.com/netdata/netdata/pull/13161) ([stelfrag](https://github.com/stelfrag)) -- Fix data query on stale chart [\#13159](https://github.com/netdata/netdata/pull/13159) ([stelfrag](https://github.com/stelfrag)) -- enable ml by default [\#13158](https://github.com/netdata/netdata/pull/13158) ([andrewm4894](https://github.com/andrewm4894)) -- Fix labels unit test [\#13156](https://github.com/netdata/netdata/pull/13156) ([stelfrag](https://github.com/stelfrag)) -- Query Engine multi-granularity support \(and MC improvements\) [\#13155](https://github.com/netdata/netdata/pull/13155) ([ktsaou](https://github.com/ktsaou)) -- add CAP\_SYS\_RAWIO to Netdata's systemd unit CapabilityBoundingSet [\#13154](https://github.com/netdata/netdata/pull/13154) ([ilyam8](https://github.com/ilyam8)) -- Update docs on what to do if collector not there [\#13152](https://github.com/netdata/netdata/pull/13152) ([cakrit](https://github.com/cakrit)) -- revert to default of `host anomaly rate threshold=0.01` [\#13150](https://github.com/netdata/netdata/pull/13150) ([andrewm4894](https://github.com/andrewm4894)) -- fix conditions for nightly build triggers [\#13145](https://github.com/netdata/netdata/pull/13145) ([maneamarius](https://github.com/maneamarius)) -- Add cargo/rustc/bazel/buck to apps\_groups.conf [\#13143](https://github.com/netdata/netdata/pull/13143) ([vkalintiris](https://github.com/vkalintiris)) -- Add an option to use malloc for page cache instead of mmap [\#13142](https://github.com/netdata/netdata/pull/13142) ([stelfrag](https://github.com/stelfrag)) -- Add mem.available chart to FreeBSD [\#13140](https://github.com/netdata/netdata/pull/13140) ([MrZammler](https://github.com/MrZammler)) -- fix crashes due to misaligned allocations [\#13137](https://github.com/netdata/netdata/pull/13137) ([ktsaou](https://github.com/ktsaou)) -- fix\(python.d\): urllib3 import collection for py3.10+ [\#13136](https://github.com/netdata/netdata/pull/13136) ([ilyam8](https://github.com/ilyam8)) -- fix\(python.d/mongodb\): set `serverSelectionTimeoutMS` for pymongo4+ [\#13135](https://github.com/netdata/netdata/pull/13135) ([ilyam8](https://github.com/ilyam8)) -- Use new mqtt implementation as default [\#13132](https://github.com/netdata/netdata/pull/13132) ([underhood](https://github.com/underhood)) -- use ks2 as MC default [\#13131](https://github.com/netdata/netdata/pull/13131) ([andrewm4894](https://github.com/andrewm4894)) -- allow label names to have slashes [\#13125](https://github.com/netdata/netdata/pull/13125) ([ktsaou](https://github.com/ktsaou)) -- fixed coveriry 379136 379135 379134 379133 [\#13123](https://github.com/netdata/netdata/pull/13123) ([ktsaou](https://github.com/ktsaou)) -- buffer overflow detected by the compiler [\#13120](https://github.com/netdata/netdata/pull/13120) ([ktsaou](https://github.com/ktsaou)) -- Ci coverage [\#13118](https://github.com/netdata/netdata/pull/13118) ([maneamarius](https://github.com/maneamarius)) -- Add missing control to streaming [\#13112](https://github.com/netdata/netdata/pull/13112) ([thiagoftsm](https://github.com/thiagoftsm)) -- Removes Legacy JSON Cloud Protocol Support In Agent [\#13111](https://github.com/netdata/netdata/pull/13111) ([underhood](https://github.com/underhood)) -- Re-enable updates for systems using static builds. [\#13110](https://github.com/netdata/netdata/pull/13110) ([Ferroin](https://github.com/Ferroin)) -- Add user netdata to secondary group in DEB package [\#13109](https://github.com/netdata/netdata/pull/13109) ([iigorkarpov](https://github.com/iigorkarpov)) -- Remove pinned page reference [\#13108](https://github.com/netdata/netdata/pull/13108) ([stelfrag](https://github.com/stelfrag)) -- 73x times faster metrics correlations at the agent [\#13107](https://github.com/netdata/netdata/pull/13107) ([ktsaou](https://github.com/ktsaou)) -- fix\(updater\): fix updating when using `--force-update` and new version of the updater script is available [\#13104](https://github.com/netdata/netdata/pull/13104) ([ilyam8](https://github.com/ilyam8)) -- Remove unnescesary ‘cleanup’ code. [\#13103](https://github.com/netdata/netdata/pull/13103) ([Ferroin](https://github.com/Ferroin)) -- Temporarily disable updates for static builds. [\#13100](https://github.com/netdata/netdata/pull/13100) ([Ferroin](https://github.com/Ferroin)) -- docs\(statsd.plugin\): fix indentation [\#13096](https://github.com/netdata/netdata/pull/13096) ([ilyam8](https://github.com/ilyam8)) -- Statistics on bytes recvd and sent [\#13091](https://github.com/netdata/netdata/pull/13091) ([underhood](https://github.com/underhood)) -- fix virtualization detection on FreeBSD [\#13087](https://github.com/netdata/netdata/pull/13087) ([ilyam8](https://github.com/ilyam8)) -- Update netdata commands [\#13080](https://github.com/netdata/netdata/pull/13080) ([tkatsoulas](https://github.com/tkatsoulas)) -- fix: fix a base64\_encode bug [\#13074](https://github.com/netdata/netdata/pull/13074) ([kklionz](https://github.com/kklionz)) -- Labels with dictionary [\#13070](https://github.com/netdata/netdata/pull/13070) ([ktsaou](https://github.com/ktsaou)) -- Use a separate thread for slow mountpoints in the diskspace plugin [\#13067](https://github.com/netdata/netdata/pull/13067) ([vlvkobal](https://github.com/vlvkobal)) -- Remove official support for Debian 9. [\#13065](https://github.com/netdata/netdata/pull/13065) ([Ferroin](https://github.com/Ferroin)) -- Fix coverity 378587 [\#13024](https://github.com/netdata/netdata/pull/13024) ([MrZammler](https://github.com/MrZammler)) -- Remove Ubuntu 21.10 from CI and package builds. [\#12918](https://github.com/netdata/netdata/pull/12918) ([Ferroin](https://github.com/Ferroin)) -- Configurable storage engine for Netdata agents: step 3 [\#12892](https://github.com/netdata/netdata/pull/12892) ([aberaud](https://github.com/aberaud)) ## [v1.35.1](https://github.com/netdata/netdata/tree/v1.35.1) (2022-06-10) @@ -235,117 +363,6 @@ [Full Changelog](https://github.com/netdata/netdata/compare/v1.34.1...v1.35.0) -**Merged pull requests:** - -- Update README.md [\#13089](https://github.com/netdata/netdata/pull/13089) ([ktsaou](https://github.com/ktsaou)) -- Update README.md [\#13088](https://github.com/netdata/netdata/pull/13088) ([ktsaou](https://github.com/ktsaou)) -- fix\(updater\): return 0 on successful update for native packages when running interactively [\#13083](https://github.com/netdata/netdata/pull/13083) ([ilyam8](https://github.com/ilyam8)) -- Fix Coverity errors in mqtt\_websockets submodule [\#13082](https://github.com/netdata/netdata/pull/13082) ([underhood](https://github.com/underhood)) -- fix\(updater\): don't produce any output if binpkg update completed successfully [\#13081](https://github.com/netdata/netdata/pull/13081) ([ilyam8](https://github.com/ilyam8)) -- Fix handling of DEB package naming in CI. [\#13076](https://github.com/netdata/netdata/pull/13076) ([Ferroin](https://github.com/Ferroin)) -- Update default value for "host anomaly rate threshold" [\#13075](https://github.com/netdata/netdata/pull/13075) ([shyamvalsan](https://github.com/shyamvalsan)) -- Fix locking access to chart labels [\#13064](https://github.com/netdata/netdata/pull/13064) ([stelfrag](https://github.com/stelfrag)) -- fix\(cgroup.plugin\): read k8s\_cluster\_name label from the correct file [\#13062](https://github.com/netdata/netdata/pull/13062) ([ilyam8](https://github.com/ilyam8)) -- Initialize chart label key parameter correctly [\#13061](https://github.com/netdata/netdata/pull/13061) ([stelfrag](https://github.com/stelfrag)) -- Added Alma Linux 9 and RHEL 9 support to CI and packaging. [\#13058](https://github.com/netdata/netdata/pull/13058) ([Ferroin](https://github.com/Ferroin)) -- Fix handling of temp directory in kickstart when uninstalling. [\#13056](https://github.com/netdata/netdata/pull/13056) ([Ferroin](https://github.com/Ferroin)) -- Fix coverity 378625 [\#13055](https://github.com/netdata/netdata/pull/13055) ([MrZammler](https://github.com/MrZammler)) -- add the ability to merge dictionary items [\#13054](https://github.com/netdata/netdata/pull/13054) ([ktsaou](https://github.com/ktsaou)) -- Check for host labels when linking alerts for children [\#13053](https://github.com/netdata/netdata/pull/13053) ([MrZammler](https://github.com/MrZammler)) -- dictionary improvements [\#13052](https://github.com/netdata/netdata/pull/13052) ([ktsaou](https://github.com/ktsaou)) -- Fix dictionary crash walkthrough empty [\#13051](https://github.com/netdata/netdata/pull/13051) ([ktsaou](https://github.com/ktsaou)) -- coverity fixes about statsd; removal of strsame [\#13049](https://github.com/netdata/netdata/pull/13049) ([ktsaou](https://github.com/ktsaou)) -- Add improved reinstall documentation. [\#13047](https://github.com/netdata/netdata/pull/13047) ([Ferroin](https://github.com/Ferroin)) -- Fix disabled apps \(ebpf.plugin\) [\#13044](https://github.com/netdata/netdata/pull/13044) ([thiagoftsm](https://github.com/thiagoftsm)) -- add note about anomaly advisor [\#13042](https://github.com/netdata/netdata/pull/13042) ([andrewm4894](https://github.com/andrewm4894)) -- replace `history` with relevant `dbengine` params [\#13041](https://github.com/netdata/netdata/pull/13041) ([andrewm4894](https://github.com/andrewm4894)) -- Fix the retry count and netdata\_exit check when running an sqlite3\_step command [\#13040](https://github.com/netdata/netdata/pull/13040) ([stelfrag](https://github.com/stelfrag)) -- Schedule retention message calculation to a worker thread [\#13039](https://github.com/netdata/netdata/pull/13039) ([stelfrag](https://github.com/stelfrag)) -- Check return value and log an error on failure [\#13037](https://github.com/netdata/netdata/pull/13037) ([stelfrag](https://github.com/stelfrag)) -- Add additional metadata to the data response [\#13036](https://github.com/netdata/netdata/pull/13036) ([stelfrag](https://github.com/stelfrag)) -- When sending a dimension for the first time, make sure there is a non zero created\_at timestamp [\#13035](https://github.com/netdata/netdata/pull/13035) ([stelfrag](https://github.com/stelfrag)) -- Update apps\_groups.conf [\#13033](https://github.com/netdata/netdata/pull/13033) ([fqx](https://github.com/fqx)) -- Dictionary with JudyHS and double linked list [\#13032](https://github.com/netdata/netdata/pull/13032) ([ktsaou](https://github.com/ktsaou)) -- add hostname to mirrored hosts [\#13030](https://github.com/netdata/netdata/pull/13030) ([ktsaou](https://github.com/ktsaou)) -- Update dashboard to version v2.25.6. [\#13028](https://github.com/netdata/netdata/pull/13028) ([netdatabot](https://github.com/netdatabot)) -- prevent gap filling on dbengine gaps [\#13027](https://github.com/netdata/netdata/pull/13027) ([ktsaou](https://github.com/ktsaou)) -- Initialize a pointer and add a check for it [\#13023](https://github.com/netdata/netdata/pull/13023) ([vlvkobal](https://github.com/vlvkobal)) -- Fix coverity issue 378598 [\#13022](https://github.com/netdata/netdata/pull/13022) ([MrZammler](https://github.com/MrZammler)) -- Skip collecting network interface speed and duplex if carrier is down [\#13019](https://github.com/netdata/netdata/pull/13019) ([vlvkobal](https://github.com/vlvkobal)) -- fix COVERITY\_PATH added with INSTALL\_DIR into PATH [\#13014](https://github.com/netdata/netdata/pull/13014) ([maneamarius](https://github.com/maneamarius)) -- Only try to update repo metadata in updater script if needed. [\#13009](https://github.com/netdata/netdata/pull/13009) ([Ferroin](https://github.com/Ferroin)) -- Treat dimensions as normal when we don't have enough/valid data. [\#13005](https://github.com/netdata/netdata/pull/13005) ([vkalintiris](https://github.com/vkalintiris)) -- Use printf instead of echo for printing collected warnings in kickstart.sh. [\#13002](https://github.com/netdata/netdata/pull/13002) ([Ferroin](https://github.com/Ferroin)) -- Update dashboard to version v2.25.4. [\#13000](https://github.com/netdata/netdata/pull/13000) ([netdatabot](https://github.com/netdatabot)) -- Run the /net/dev module of the proc plugin in a separate thread [\#12996](https://github.com/netdata/netdata/pull/12996) ([vlvkobal](https://github.com/vlvkobal)) -- Autodetect coverity install path to increase robustness [\#12995](https://github.com/netdata/netdata/pull/12995) ([maneamarius](https://github.com/maneamarius)) -- Fix compilation warnings [\#12993](https://github.com/netdata/netdata/pull/12993) ([vlvkobal](https://github.com/vlvkobal)) -- Delay children chart obsoletion check [\#12992](https://github.com/netdata/netdata/pull/12992) ([MrZammler](https://github.com/MrZammler)) -- Fix nanosleep on platforms other than Linux [\#12991](https://github.com/netdata/netdata/pull/12991) ([vlvkobal](https://github.com/vlvkobal)) -- Don't expose the chart definition to streaming if there is no metadata change [\#12990](https://github.com/netdata/netdata/pull/12990) ([stelfrag](https://github.com/stelfrag)) -- Faster queries [\#12988](https://github.com/netdata/netdata/pull/12988) ([ktsaou](https://github.com/ktsaou)) -- Improve reconnect node instructions [\#12987](https://github.com/netdata/netdata/pull/12987) ([cakrit](https://github.com/cakrit)) -- Make heartbeat a static chart [\#12986](https://github.com/netdata/netdata/pull/12986) ([MrZammler](https://github.com/MrZammler)) -- chore\(apps.plugin\): change cpu\_guest chart context [\#12983](https://github.com/netdata/netdata/pull/12983) ([ilyam8](https://github.com/ilyam8)) -- fix: don't kill Netdata PIDs if successfully stopped Netdata [\#12982](https://github.com/netdata/netdata/pull/12982) ([ilyam8](https://github.com/ilyam8)) -- add dictionary support to statsd [\#12980](https://github.com/netdata/netdata/pull/12980) ([ktsaou](https://github.com/ktsaou)) -- fix\(kickstart.sh\): handle the case when `tput colors` doesn't return a number [\#12979](https://github.com/netdata/netdata/pull/12979) ([ilyam8](https://github.com/ilyam8)) -- query engine optimizations and cleanup [\#12978](https://github.com/netdata/netdata/pull/12978) ([ktsaou](https://github.com/ktsaou)) -- optimize poll\_events\(\) to spread the work over the threads more evenly [\#12975](https://github.com/netdata/netdata/pull/12975) ([ktsaou](https://github.com/ktsaou)) -- chore: check link local address before querying cloud instance metadata [\#12973](https://github.com/netdata/netdata/pull/12973) ([ilyam8](https://github.com/ilyam8)) -- Alarms py collector add filtering [\#12972](https://github.com/netdata/netdata/pull/12972) ([andrewm4894](https://github.com/andrewm4894)) -- Don't permanetly disable a destination because of denied access [\#12971](https://github.com/netdata/netdata/pull/12971) ([MrZammler](https://github.com/MrZammler)) -- modify code to resolve compile warning issue [\#12969](https://github.com/netdata/netdata/pull/12969) ([kklionz](https://github.com/kklionz)) -- Return rc-\>last\_update from alarms\_values api [\#12968](https://github.com/netdata/netdata/pull/12968) ([MrZammler](https://github.com/MrZammler)) -- cleanup and optimize rrdeng\_load\_metric\_next\(\) [\#12966](https://github.com/netdata/netdata/pull/12966) ([ktsaou](https://github.com/ktsaou)) -- feat\(charts.d/apcupds\): add load usage chart \(Watts\) [\#12965](https://github.com/netdata/netdata/pull/12965) ([ilyam8](https://github.com/ilyam8)) -- fix: keep virtualization unknown if all used commands are not available [\#12964](https://github.com/netdata/netdata/pull/12964) ([ilyam8](https://github.com/ilyam8)) -- statsd sets should count unique values [\#12963](https://github.com/netdata/netdata/pull/12963) ([ktsaou](https://github.com/ktsaou)) -- Add automatic retries fo static builds during nightly and release builds. [\#12961](https://github.com/netdata/netdata/pull/12961) ([Ferroin](https://github.com/Ferroin)) -- Cleanup chart hash and map tables on startup [\#12956](https://github.com/netdata/netdata/pull/12956) ([stelfrag](https://github.com/stelfrag)) -- Suppress warning when freeing a NULL pointer in onewayalloc\_freez [\#12955](https://github.com/netdata/netdata/pull/12955) ([stelfrag](https://github.com/stelfrag)) -- Trigger queue removed alerts on health log exchange with cloud [\#12954](https://github.com/netdata/netdata/pull/12954) ([MrZammler](https://github.com/MrZammler)) -- Optimize the dimensions option store to the metadata database [\#12952](https://github.com/netdata/netdata/pull/12952) ([stelfrag](https://github.com/stelfrag)) -- Defer the dimension payload check to the ACLK sync thread [\#12951](https://github.com/netdata/netdata/pull/12951) ([stelfrag](https://github.com/stelfrag)) -- detailed dbengine stats [\#12948](https://github.com/netdata/netdata/pull/12948) ([ktsaou](https://github.com/ktsaou)) -- Prevent command\_to\_be\_logged from overflowing [\#12947](https://github.com/netdata/netdata/pull/12947) ([MrZammler](https://github.com/MrZammler)) -- Update libbpf version [\#12945](https://github.com/netdata/netdata/pull/12945) ([thiagoftsm](https://github.com/thiagoftsm)) -- Reduce timeout to 1 second for getting cloud instance info [\#12941](https://github.com/netdata/netdata/pull/12941) ([MrZammler](https://github.com/MrZammler)) -- Stream and advertise metric correlations to the cloud [\#12940](https://github.com/netdata/netdata/pull/12940) ([MrZammler](https://github.com/MrZammler)) -- feat: move dirs, logs, and env vars config options to separate sections [\#12935](https://github.com/netdata/netdata/pull/12935) ([ilyam8](https://github.com/ilyam8)) -- Adjust the dimension liveness status check [\#12933](https://github.com/netdata/netdata/pull/12933) ([stelfrag](https://github.com/stelfrag)) -- chore\(fping.plugin\): bump default fping version to 5.1 [\#12930](https://github.com/netdata/netdata/pull/12930) ([ilyam8](https://github.com/ilyam8)) -- Restore a broken symbolic link [\#12923](https://github.com/netdata/netdata/pull/12923) ([vlvkobal](https://github.com/vlvkobal)) -- collectors: apps.plugin: apps\_groups: update net, aws, ha groups [\#12921](https://github.com/netdata/netdata/pull/12921) ([k0ste](https://github.com/k0ste)) -- Remove Alpine 3.12 from CI. [\#12919](https://github.com/netdata/netdata/pull/12919) ([Ferroin](https://github.com/Ferroin)) -- user configurable sqlite PRAGMAs [\#12917](https://github.com/netdata/netdata/pull/12917) ([ktsaou](https://github.com/ktsaou)) -- fix `[global statistics]` section in netdata.conf [\#12916](https://github.com/netdata/netdata/pull/12916) ([ilyam8](https://github.com/ilyam8)) -- chore\(streaming\): bump default "buffer size bytes" to 10MB [\#12913](https://github.com/netdata/netdata/pull/12913) ([ilyam8](https://github.com/ilyam8)) -- fix\(cgroups.plugin\): improve check for uninitialized containers in k8s [\#12912](https://github.com/netdata/netdata/pull/12912) ([ilyam8](https://github.com/ilyam8)) -- fix virtualization detection when `systemd-detect-virt` is not available [\#12911](https://github.com/netdata/netdata/pull/12911) ([ilyam8](https://github.com/ilyam8)) -- added worker jobs for cgroup-rename, cgroup-network and cgroup-first-time [\#12910](https://github.com/netdata/netdata/pull/12910) ([ktsaou](https://github.com/ktsaou)) -- Fix the log entry for incoming cloud start streaming commands [\#12908](https://github.com/netdata/netdata/pull/12908) ([stelfrag](https://github.com/stelfrag)) -- chore\(cgroups.plugin\): remove "enable new cgroups detected at run time" config option [\#12906](https://github.com/netdata/netdata/pull/12906) ([ilyam8](https://github.com/ilyam8)) -- Fix release channel in the node info message [\#12905](https://github.com/netdata/netdata/pull/12905) ([stelfrag](https://github.com/stelfrag)) -- chore\(worker\_utilization\): log an error when re-registering an already registered job [\#12903](https://github.com/netdata/netdata/pull/12903) ([ilyam8](https://github.com/ilyam8)) -- fix\(cgroups.plugin\): use correct identifier when registering the main thread "chart" worker job [\#12902](https://github.com/netdata/netdata/pull/12902) ([ilyam8](https://github.com/ilyam8)) -- Remove CPU-specific info from cpuidle dimensions [\#12898](https://github.com/netdata/netdata/pull/12898) ([vlvkobal](https://github.com/vlvkobal)) -- Adjust alarms count [\#12896](https://github.com/netdata/netdata/pull/12896) ([MrZammler](https://github.com/MrZammler)) -- Return stable or nightly based on version if the file check fails [\#12894](https://github.com/netdata/netdata/pull/12894) ([stelfrag](https://github.com/stelfrag)) -- Update reconnect node with kickstart info [\#12891](https://github.com/netdata/netdata/pull/12891) ([cakrit](https://github.com/cakrit)) -- Fix compilation warnings in FreeBSD [\#12887](https://github.com/netdata/netdata/pull/12887) ([vlvkobal](https://github.com/vlvkobal)) -- Fix compilation warnings [\#12886](https://github.com/netdata/netdata/pull/12886) ([vlvkobal](https://github.com/vlvkobal)) -- Take into account the in queue wait time when executing a data query [\#12885](https://github.com/netdata/netdata/pull/12885) ([stelfrag](https://github.com/stelfrag)) -- Update dashboard to version v2.25.2. [\#12884](https://github.com/netdata/netdata/pull/12884) ([netdatabot](https://github.com/netdatabot)) -- Consider ZFS ARC shrinkable as cache on FreeBSD [\#12879](https://github.com/netdata/netdata/pull/12879) ([vlvkobal](https://github.com/vlvkobal)) -- Remove Fedora 34 from CI and package builds. [\#12875](https://github.com/netdata/netdata/pull/12875) ([Ferroin](https://github.com/Ferroin)) -- fix\(health\): change duplicate health template message logging level to 'info' [\#12873](https://github.com/netdata/netdata/pull/12873) ([ilyam8](https://github.com/ilyam8)) -- docs: fix unresolved file references [\#12872](https://github.com/netdata/netdata/pull/12872) ([ilyam8](https://github.com/ilyam8)) -- Set trust durations to have data from children properly aligned [\#12870](https://github.com/netdata/netdata/pull/12870) ([stelfrag](https://github.com/stelfrag)) -- feat\(proc/cgroups.plugin\): add PSI stall time charts [\#12869](https://github.com/netdata/netdata/pull/12869) ([ilyam8](https://github.com/ilyam8)) -- Update README.md [\#12868](https://github.com/netdata/netdata/pull/12868) ([tkatsoulas](https://github.com/tkatsoulas)) -- fix for negative per job busy time [\#12867](https://github.com/netdata/netdata/pull/12867) ([ktsaou](https://github.com/ktsaou)) - ## [v1.34.1](https://github.com/netdata/netdata/tree/v1.34.1) (2022-04-15) [Full Changelog](https://github.com/netdata/netdata/compare/1.34.0...v1.34.1) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9752f3718..c12e9c81b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,31 +9,25 @@ project(netdata C CXX) find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) +include(CheckFunctionExists) +include(CheckLibraryExists) + # default is "Debug" #set(CMAKE_BUILD_TYPE "Release") # set this to see the compilation commands -# set(CMAKE_VERBOSE_MAKEFILE 1) - +#set(CMAKE_VERBOSE_MAKEFILE 1) # ----------------------------------------------------------------------------- # Set compilation options according to build type +set(CMAKE_C_STANDARD 11) + IF("${CMAKE_BUILD_TYPE}" MATCHES "Debug") + set(CXX_DEFAULT_CFLAGS "-O0 -g -DNETDATA_INTERNAL_CHECKS=1 -DNETDATA_DEV_MODE=1 -fstack-protector-all -fno-omit-frame-pointer") message(STATUS "building for: debugging") - - ## unfortunately these produce errors - #include(CheckCXXCompilerFlag) - #CHECK_CXX_COMPILER_FLAG("-Wformat-signedness" CXX_FORMAT_SIGNEDNESS) - #CHECK_CXX_COMPILER_FLAG("-Werror=format-security" CXX_FORMAT_SECURITY) - #CHECK_CXX_COMPILER_FLAG("-fstack-protector-all" CXX_STACK_PROTECTOR) - set(CXX_FORMAT_SIGNEDNESS "-Wformat-signedness") - set(CXX_FORMAT_SECURITY "-Werror=format-security") - set(CXX_STACK_PROTECTOR "-fstack-protector-all") - set(CXX_FLAGS_DEBUG "-O0") - set(CMAKE_C_STANDARD 99) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O1 -ggdb -Wall -Wextra -DNETDATA_INTERNAL_CHECKS=1 -DNETDATA_VERIFY_LOCKS=1 ${CXX_FORMAT_SIGNEDNESS} ${CXX_FORMAT_SECURITY} ${CXX_STACK_PROTECTOR} ${CXX_FLAGS_DEBUG}") ELSE() + set(CXX_DEFAULT_CFLAGS "-O2") message(STATUS "building for: release") cmake_policy(SET CMP0069 "NEW") include(CheckIPOSupported) @@ -46,6 +40,7 @@ ELSE() ENDIF() ENDIF() +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O1 -ggdb -Wall -Wextra -Wformat-signedness -Werror=format-security -Wunused-result ${CXX_DEFAULT_CFLAGS}") # ----------------------------------------------------------------------------- # O/S Detection @@ -67,6 +62,15 @@ ENDIF() # show the operating system on the console message(STATUS "system name: ${CMAKE_SYSTEM_NAME}") +set(GENERATED_CONFIG_H_DIR ${CMAKE_BINARY_DIR}) +set(GENERATED_CONFIG_H ${GENERATED_CONFIG_H_DIR}/config.h) + +# ----------------------------------------------------------------------------- +# Math + +set(NETDATA_COMMON_LIBRARIES ${NETDATA_COMMON_LIBRARIES} m) +set(NETDATA_REQUIRED_DEFINES "-DSTORAGE_WITH_MATH=1 ${NETDATA_REQUIRED_DEFINES}") + # ----------------------------------------------------------------------------- # Detect libuuid @@ -82,6 +86,7 @@ pkg_check_modules(ZLIB REQUIRED zlib) set(NETDATA_COMMON_CFLAGS ${NETDATA_COMMON_CFLAGS} ${ZLIB_CFLAGS_OTHER}) set(NETDATA_COMMON_LIBRARIES ${NETDATA_COMMON_LIBRARIES} ${ZLIB_LIBRARIES}) set(NETDATA_COMMON_INCLUDE_DIRS ${NETDATA_COMMON_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}) +# set(NETDATA_REQUIRED_DEFINES "${NETDATA_REQUIRED_DEFINES} -DNETDATA_WITH_ZLIB=1") # ----------------------------------------------------------------------------- # libuv multi-platform support library with a focus on asynchronous I/O @@ -98,6 +103,7 @@ pkg_check_modules(LIBLZ4 REQUIRED liblz4) set(NETDATA_COMMON_CFLAGS ${NETDATA_COMMON_CFLAGS} ${LIBLZ4_CFLAGS_OTHER}) set(NETDATA_COMMON_LIBRARIES ${NETDATA_COMMON_LIBRARIES} ${LIBLZ4_LIBRARIES}) set(NETDATA_COMMON_INCLUDE_DIRS ${NETDATA_COMMON_INCLUDE_DIRS} ${LIBLZ4_INCLUDE_DIRS}) +# set(NETDATA_REQUIRED_DEFINES "${NETDATA_REQUIRED_DEFINES} -DENABLE_COMPRESSION=1") # ----------------------------------------------------------------------------- # Judy General purpose dynamic array @@ -111,6 +117,7 @@ pkg_check_modules(OPENSSL REQUIRED openssl) set(NETDATA_COMMON_CFLAGS ${NETDATA_COMMON_CFLAGS} ${OPENSSL_CFLAGS_OTHER}) set(NETDATA_COMMON_LIBRARIES ${NETDATA_COMMON_LIBRARIES} ${OPENSSL_LIBRARIES}) set(NETDATA_COMMON_INCLUDE_DIRS ${NETDATA_COMMON_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS}) +# set(NETDATA_REQUIRED_DEFINES "${NETDATA_REQUIRED_DEFINES} -DENABLE_HTTPS=1") # ----------------------------------------------------------------------------- # JSON-C used to health @@ -220,7 +227,11 @@ pkg_check_modules(CURL libcurl) # ----------------------------------------------------------------------------- # Detect libcups -pkg_check_modules(CURL libcups) +pkg_check_modules(CUPS libcups) +IF(NOT CUPS_LIBRARIES) + pkg_check_modules(CUPS cups) +ENDIF() + # later we use: # ${CUPS_LIBRARIES} # ${CUPS_CFLAGS_OTHER} @@ -328,8 +339,9 @@ IF(LINUX AND EXISTS "${CMAKE_SOURCE_DIR}/externaldeps/libbpf/libbpf.a") ${CMAKE_SOURCE_DIR}/externaldeps/libbpf/libbpf.a ${ELF_LIBRARIES}) list(APPEND NETDATA_COMMON_INCLUDE_DIRS ${ELF_INCLUDE_DIRS}) - include_directories(BEFORE ${CMAKE_SOURCE_DIR}/externaldeps/libbpf/include) + include_directories(BEFORE ${CMAKE_SOURCE_DIR}/externaldeps/libbpf/include ${CMAKE_SOURCE_DIR}/externaldeps/libbpf/include/uapi) set(ENABLE_PLUGIN_EBPF True) + set(HAVE_LIBBPF True) ELSE(ELF_LIBRARIES) set(ENABLE_PLUGIN_EBPF False) message(STATUS "ebpf plugin: disabled (requires libelf)") @@ -340,11 +352,11 @@ ENDIF() # Detect ml dependencies file(STRINGS "${CMAKE_SOURCE_DIR}/config.h" DEFINE_ENABLE_ML REGEX "^#define ENABLE_ML 1$") IF(DEFINE_ENABLE_ML MATCHES ".+" AND - EXISTS "${CMAKE_SOURCE_DIR}/ml/kmeans/dlib/dlib/all/source.cpp" AND + EXISTS "${CMAKE_SOURCE_DIR}/ml/dlib/dlib/all/source.cpp" AND EXISTS "${CMAKE_SOURCE_DIR}/ml/json/single_include/nlohmann/json.hpp") set(ENABLE_ML True) list(APPEND NETDATA_COMMON_CFLAGS "-DDLIB_NO_GUI_SUPPORT") - list(APPEND NETDATA_COMMON_INCLUDE_DIRS "ml/kmeans/dlib") + list(APPEND NETDATA_COMMON_INCLUDE_DIRS "ml/dlib") ELSE() set(ENABLE_ML False) ENDIF() @@ -409,6 +421,8 @@ target_include_directories(judy PUBLIC libnetdata/libjudy/src libnetdata/libjudy/src/JudyCommon) +target_include_directories(judy BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) + target_compile_definitions(judy PUBLIC JU_64BIT JUDYL) @@ -445,6 +459,8 @@ set(LIBNETDATA_FILES libnetdata/avl/avl.h libnetdata/buffer/buffer.c libnetdata/buffer/buffer.h + libnetdata/circular_buffer/circular_buffer.c + libnetdata/circular_buffer/circular_buffer.h libnetdata/clocks/clocks.c libnetdata/clocks/clocks.h libnetdata/completion/completion.c @@ -453,10 +469,15 @@ set(LIBNETDATA_FILES libnetdata/dictionary/dictionary.h libnetdata/eval/eval.c libnetdata/eval/eval.h + libnetdata/health/health.c + libnetdata/health/health.h libnetdata/inlined.h + libnetdata/json/json.c + libnetdata/json/json.h + libnetdata/json/jsmn.c + libnetdata/json/jsmn.h libnetdata/libnetdata.c libnetdata/libnetdata.h - libnetdata/required_dummies.h libnetdata/locks/locks.c libnetdata/locks/locks.h libnetdata/log/log.c @@ -469,6 +490,9 @@ set(LIBNETDATA_FILES libnetdata/popen/popen.h libnetdata/procfile/procfile.c libnetdata/procfile/procfile.h + libnetdata/required_dummies.h + libnetdata/socket/security.c + libnetdata/socket/security.h libnetdata/simple_pattern/simple_pattern.c libnetdata/simple_pattern/simple_pattern.h libnetdata/socket/socket.c @@ -477,23 +501,16 @@ set(LIBNETDATA_FILES libnetdata/statistical/statistical.h libnetdata/storage_number/storage_number.c libnetdata/storage_number/storage_number.h + libnetdata/string/string.c + libnetdata/string/string.h libnetdata/threads/threads.c libnetdata/threads/threads.h libnetdata/url/url.c libnetdata/url/url.h - libnetdata/json/json.c - libnetdata/json/json.h - libnetdata/json/jsmn.c - libnetdata/json/jsmn.h - libnetdata/health/health.c - libnetdata/health/health.h libnetdata/string/utf8.h - libnetdata/socket/security.c - libnetdata/socket/security.h libnetdata/worker_utilization/worker_utilization.c libnetdata/worker_utilization/worker_utilization.h - libnetdata/circular_buffer/circular_buffer.c - libnetdata/circular_buffer/circular_buffer.h) + ) IF(ENABLE_PLUGIN_EBPF) list(APPEND LIBNETDATA_FILES @@ -503,13 +520,11 @@ ENDIF() add_library(libnetdata OBJECT ${LIBNETDATA_FILES}) +target_include_directories(libnetdata BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) + set(APPS_PLUGIN_FILES collectors/apps.plugin/apps_plugin.c) -set(CHECKS_PLUGIN_FILES - collectors/checks.plugin/plugin_checks.c - ) - set(FREEBSD_PLUGIN_FILES collectors/freebsd.plugin/plugin_freebsd.c collectors/freebsd.plugin/plugin_freebsd.h @@ -633,8 +648,6 @@ set(PROC_PLUGIN_FILES collectors/proc.plugin/proc_net_netstat.c collectors/proc.plugin/proc_net_rpc_nfs.c collectors/proc.plugin/proc_net_rpc_nfsd.c - collectors/proc.plugin/proc_net_snmp.c - collectors/proc.plugin/proc_net_snmp6.c collectors/proc.plugin/proc_net_sctp_snmp.c collectors/proc.plugin/proc_net_sockstat.c collectors/proc.plugin/proc_net_sockstat6.c @@ -716,6 +729,8 @@ set(RRD_PLUGIN_FILES database/rrddimvar.c database/rrddimvar.h database/rrdfamily.c + database/rrdfunctions.c + database/rrdfunctions.h database/rrdhost.c database/rrdlabels.c database/rrd.c @@ -729,6 +744,8 @@ set(RRD_PLUGIN_FILES database/storage_engine.h database/ram/rrddim_mem.c database/ram/rrddim_mem.h + database/sqlite/sqlite_metadata.c + database/sqlite/sqlite_metadata.h database/sqlite/sqlite_functions.c database/sqlite/sqlite_functions.h database/sqlite/sqlite_context.c @@ -741,8 +758,6 @@ set(RRD_PLUGIN_FILES database/sqlite/sqlite_health.h database/sqlite/sqlite_aclk_node.c database/sqlite/sqlite_aclk_node.h - database/sqlite/sqlite_aclk_chart.c - database/sqlite/sqlite_aclk_chart.h database/sqlite/sqlite_aclk_alert.c database/sqlite/sqlite_aclk_alert.h database/sqlite/sqlite3.c @@ -762,16 +777,6 @@ set(RRD_PLUGIN_FILES database/engine/pagecache.h database/engine/rrdenglocking.c database/engine/rrdenglocking.h - database/engine/metadata_log/metadatalog.h - database/engine/metadata_log/metadatalogapi.c - database/engine/metadata_log/metadatalogapi.h - database/engine/metadata_log/logfile.h - database/engine/metadata_log/logfile.c - database/engine/metadata_log/metadatalogprotocol.h - database/engine/metadata_log/metalogpluginsd.c - database/engine/metadata_log/metalogpluginsd.h - database/engine/metadata_log/compaction.c - database/engine/metadata_log/compaction.h database/KolmogorovSmirnovDist.c database/KolmogorovSmirnovDist.h ) @@ -851,6 +856,8 @@ set(STREAMING_PLUGIN_FILES streaming/compression.c streaming/receiver.c streaming/sender.c + streaming/replication.c + streaming/replication.h ) set(CLAIM_PLUGIN_FILES @@ -860,15 +867,13 @@ set(CLAIM_PLUGIN_FILES set(ACLK_ALWAYS_BUILD aclk/aclk_rrdhost_state.h - aclk/aclk_api.c - aclk/aclk_api.h aclk/aclk_proxy.c aclk/aclk_proxy.h + aclk/aclk.c + aclk/aclk.h ) set(ACLK_FILES - aclk/aclk.c - aclk/aclk.h aclk/aclk_util.c aclk/aclk_util.h aclk/aclk_stats.c @@ -885,38 +890,18 @@ set(ACLK_FILES aclk/aclk_rx_msgs.h aclk/https_client.c aclk/https_client.h - aclk/aclk_charts_api.c - aclk/aclk_charts_api.h aclk/aclk_alarm_api.c aclk/aclk_alarm_api.h aclk/aclk_contexts_api.c aclk/aclk_contexts_api.h - mqtt_websockets/src/mqtt_wss_client.c - mqtt_websockets/src/include/mqtt_wss_client.h - mqtt_websockets/src/mqtt_wss_log.c - mqtt_websockets/src/include/mqtt_wss_log.h - mqtt_websockets/src/ws_client.c - mqtt_websockets/src/include/ws_client.h - mqtt_websockets/src/mqtt_ng.c - mqtt_websockets/src/include/mqtt_ng.h - mqtt_websockets/src/common_public.c - mqtt_websockets/src/include/common_public.h - mqtt_websockets/src/include/common_internal.h - mqtt_websockets/c-rbuf/src/ringbuffer.c - mqtt_websockets/c-rbuf/include/ringbuffer.h - mqtt_websockets/c-rbuf/src/ringbuffer_internal.h - mqtt_websockets/MQTT-C/src/mqtt.c - mqtt_websockets/MQTT-C/include/mqtt.h + aclk/aclk_capas.c + aclk/aclk_capas.h aclk/schema-wrappers/connection.cc aclk/schema-wrappers/connection.h aclk/schema-wrappers/node_connection.cc aclk/schema-wrappers/node_connection.h aclk/schema-wrappers/node_creation.cc aclk/schema-wrappers/node_creation.h - aclk/schema-wrappers/chart_stream.cc - aclk/schema-wrappers/chart_stream.h - aclk/schema-wrappers/chart_config.cc - aclk/schema-wrappers/chart_config.h aclk/schema-wrappers/alarm_stream.cc aclk/schema-wrappers/alarm_stream.h aclk/schema-wrappers/alarm_config.cc @@ -934,6 +919,27 @@ set(ACLK_FILES aclk/schema-wrappers/schema_wrappers.h aclk/schema-wrappers/schema_wrapper_utils.cc aclk/schema-wrappers/schema_wrapper_utils.h + aclk/helpers/mqtt_wss_pal.h + aclk/helpers/ringbuffer_pal.h + ) + +set(MQTT_WEBSOCKETS_FILES + mqtt_websockets/src/mqtt_wss_client.c + mqtt_websockets/src/include/mqtt_wss_client.h + mqtt_websockets/src/mqtt_wss_log.c + mqtt_websockets/src/include/mqtt_wss_log.h + mqtt_websockets/src/ws_client.c + mqtt_websockets/src/include/ws_client.h + mqtt_websockets/src/mqtt_ng.c + mqtt_websockets/src/include/mqtt_ng.h + mqtt_websockets/src/common_public.c + mqtt_websockets/src/include/common_public.h + mqtt_websockets/src/include/common_internal.h + mqtt_websockets/c-rbuf/src/ringbuffer.c + mqtt_websockets/c-rbuf/include/ringbuffer.h + mqtt_websockets/c-rbuf/src/ringbuffer_internal.h + mqtt_websockets/MQTT-C/src/mqtt.c + mqtt_websockets/MQTT-C/include/mqtt.h ) set(SPAWN_PLUGIN_FILES @@ -1018,30 +1024,30 @@ set(ML_FILES ml/ml-dummy.c ) +# ----------------------------------------------------------------------------- +# ML + IF(ENABLE_ML) + message(STATUS "ML: enabled") list(APPEND ML_FILES - ml/BitBufferCounter.h - ml/BitBufferCounter.cc - ml/BitRateWindow.h - ml/BitRateWindow.cc ml/Config.h ml/Config.cc - ml/Database.h - ml/Database.cc ml/Dimension.cc ml/Dimension.h ml/Host.h ml/Host.cc ml/Query.h - ml/kmeans/KMeans.h - ml/kmeans/KMeans.cc - ml/kmeans/SamplesBuffer.h - ml/kmeans/SamplesBuffer.cc - ml/kmeans/dlib/dlib/all/source.cpp + ml/KMeans.h + ml/KMeans.cc + ml/SamplesBuffer.h + ml/SamplesBuffer.cc + ml/dlib/dlib/all/source.cpp ml/json/single_include/nlohmann/json.hpp ml/ml.cc ml/ml-private.h ) +ELSE() + message(STATUS "ML: disabled") ENDIF() set(NETDATA_FILES @@ -1049,7 +1055,6 @@ set(NETDATA_FILES ${DAEMON_FILES} ${API_PLUGIN_FILES} ${EXPORTING_ENGINE_FILES} - ${CHECKS_PLUGIN_FILES} ${HEALTH_PLUGIN_FILES} ${IDLEJITTER_PLUGIN_FILES} ${ML_FILES} @@ -1194,6 +1199,10 @@ ENDIF() set(NETDATA_COMMON_LIBRARIES ${NETDATA_COMMON_LIBRARIES} m ${CMAKE_THREAD_LIBS_INIT}) +option(ENABLE_ACLK "Enables functionality required to connect to cloud (must be activated by user at run time)" ON) + +if(ENABLE_ACLK) + find_package(Protobuf REQUIRED) function(PROTOBUF_ACLK_GENERATE_CPP SRCS HDRS) @@ -1238,10 +1247,6 @@ set(ACLK_PROTO_DEFS aclk/aclk-schemas/proto/agent/v1/connection.proto aclk/aclk-schemas/proto/alarm/v1/config.proto aclk/aclk-schemas/proto/alarm/v1/stream.proto - aclk/aclk-schemas/proto/chart/v1/config.proto - aclk/aclk-schemas/proto/chart/v1/dimension.proto - aclk/aclk-schemas/proto/chart/v1/instance.proto - aclk/aclk-schemas/proto/chart/v1/stream.proto aclk/aclk-schemas/proto/nodeinstance/connection/v1/connection.proto aclk/aclk-schemas/proto/nodeinstance/create/v1/creation.proto aclk/aclk-schemas/proto/nodeinstance/info/v1/info.proto @@ -1253,14 +1258,29 @@ PROTOBUF_ACLK_GENERATE_CPP(ACLK_PROTO_BUILT_SRCS ACLK_PROTO_BUILT_HDRS ${ACLK_PR list(APPEND NETDATA_COMMON_LIBRARIES ${PROTOBUF_LIBRARIES}) list(APPEND NETDATA_COMMON_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIRS}) list(APPEND NETDATA_COMMON_CFLAGS ${PROTOBUF_CFLAGS_OTHER}) -list(APPEND NETDATA_FILES ${ACLK_ALWAYS_BUILD}) -list(APPEND NETDATA_FILES ${TIMEX_PLUGIN_FILES}) list(APPEND NETDATA_FILES ${ACLK_FILES} ${ACLK_PROTO_BUILT_SRCS} ${ACLK_PROTO_BUILT_HDRS}) include_directories(BEFORE ${CMAKE_SOURCE_DIR}/aclk/aclk-schemas) include_directories(BEFORE ${CMAKE_SOURCE_DIR}/mqtt_websockets/MQTT-C/include) include_directories(BEFORE ${CMAKE_SOURCE_DIR}/mqtt_websockets/src/include) include_directories(BEFORE ${CMAKE_SOURCE_DIR}/mqtt_websockets/c-rbuf/include) +ADD_LIBRARY(mqttwebsockets STATIC + ${MQTT_WEBSOCKETS_FILES}) + +target_compile_options(mqttwebsockets PUBLIC + -DMQTT_WSS_CUSTOM_ALLOC + -DRBUF_CUSTOM_MALLOC) + +target_include_directories(mqttwebsockets PUBLIC + ${CMAKE_SOURCE_DIR}/aclk/helpers) + +set(NETDATA_COMMON_LIBRARIES ${NETDATA_COMMON_LIBRARIES} mqttwebsockets) + +ENDIF() + +list(APPEND NETDATA_FILES ${ACLK_ALWAYS_BUILD}) +list(APPEND NETDATA_FILES ${TIMEX_PLUGIN_FILES}) + # ----------------------------------------------------------------------------- # netdata @@ -1268,7 +1288,7 @@ IF(LINUX) list(APPEND NETDATA_FILES daemon/static_threads_linux.c) list(APPEND NETDATA_COMMON_LIBRARIES rt) - add_executable(netdata config.h ${NETDATA_FILES} + add_executable(netdata ${GENERATED_CONFIG_H} ${NETDATA_FILES} ${CGROUPS_PLUGIN_FILES} ${DISKSPACE_PLUGIN_FILES} ${PROC_PLUGIN_FILES} @@ -1276,6 +1296,7 @@ IF(LINUX) ) target_link_libraries (netdata libnetdata ${NETDATA_COMMON_LIBRARIES}) target_include_directories(netdata PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS}) + target_include_directories(netdata BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(netdata PUBLIC ${NETDATA_COMMON_CFLAGS}) SET(ENABLE_PLUGIN_CGROUP_NETWORK True) @@ -1286,9 +1307,10 @@ IF(LINUX) ELSEIF(FREEBSD) list(APPEND NETDATA_FILES daemon/static_threads_freebsd.c) - add_executable(netdata config.h ${NETDATA_FILES} ${FREEBSD_PLUGIN_FILES}) + add_executable(netdata ${GENERATED_CONFIG_H} ${NETDATA_FILES} ${FREEBSD_PLUGIN_FILES}) target_link_libraries (netdata libnetdata ${NETDATA_COMMON_LIBRARIES}) target_include_directories(netdata PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS}) + target_include_directories(netdata BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(netdata PUBLIC ${NETDATA_COMMON_CFLAGS}) SET(ENABLE_PLUGIN_CGROUP_NETWORK False) SET(ENABLE_PLUGIN_APPS True) @@ -1299,9 +1321,10 @@ ELSEIF(FREEBSD) ELSEIF(MACOS) list(APPEND NETDATA_FILES daemon/static_threads_macos.c) - add_executable(netdata config.h ${NETDATA_FILES} ${MACOS_PLUGIN_FILES}) + add_executable(netdata ${GENERATED_CONFIG_H} ${NETDATA_FILES} ${MACOS_PLUGIN_FILES}) target_link_libraries (netdata libnetdata ${NETDATA_COMMON_LIBRARIES} ${IOKIT} ${FOUNDATION}) target_include_directories(netdata PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS}) + target_include_directories(netdata BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(netdata PUBLIC ${NETDATA_COMMON_CFLAGS}) SET(ENABLE_PLUGIN_CGROUP_NETWORK False) SET(ENABLE_PLUGIN_APPS False) @@ -1344,9 +1367,10 @@ ENDIF() # ----------------------------------------------------------------------------- # netdatacli -add_executable(netdatacli config.h ${NETDATACLI_FILES}) +add_executable(netdatacli ${GENERATED_CONFIG_H} ${NETDATACLI_FILES}) target_link_libraries (netdatacli libnetdata ${NETDATA_COMMON_LIBRARIES}) target_include_directories(netdatacli PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS}) +target_include_directories(netdatacli BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(netdatacli PUBLIC ${NETDATA_COMMON_CFLAGS}) @@ -1355,9 +1379,10 @@ target_compile_options(netdatacli PUBLIC ${NETDATA_COMMON_CFLAGS}) IF(ENABLE_PLUGIN_APPS) message(STATUS "apps.plugin: enabled") - add_executable(apps.plugin config.h ${APPS_PLUGIN_FILES}) + add_executable(apps.plugin ${GENERATED_CONFIG_H} ${APPS_PLUGIN_FILES}) target_link_libraries (apps.plugin libnetdata ${NETDATA_COMMON_LIBRARIES} ${CAP_LIBRARIES}) target_include_directories(apps.plugin PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS} ${CAP_INCLUDE_DIRS}) + target_include_directories(apps.plugin BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(apps.plugin PUBLIC ${NETDATA_COMMON_CFLAGS} ${CAP_CFLAGS_OTHER}) ELSE() message(STATUS "apps.plugin: disabled") @@ -1369,9 +1394,10 @@ ENDIF() IF(ENABLE_PLUGIN_FREEIPMI) message(STATUS "freeipmi.plugin: enabled") - add_executable(freeipmi.plugin config.h ${FREEIPMI_PLUGIN_FILES}) + add_executable(freeipmi.plugin ${GENERATED_CONFIG_H} ${FREEIPMI_PLUGIN_FILES}) target_link_libraries (freeipmi.plugin libnetdata ${NETDATA_COMMON_LIBRARIES} ${IPMI_LIBRARIES}) target_include_directories(freeipmi.plugin PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS} ${IPMI_INCLUDE_DIRS}) + target_include_directories(freeipmi.plugin BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(freeipmi.plugin PUBLIC ${NETDATA_COMMON_CFLAGS} ${IPMI_CFLAGS_OTHER}) ELSE() message(STATUS "freeipmi.plugin: disabled (depends on libipmimonitoring)") @@ -1383,9 +1409,10 @@ ENDIF() IF(ENABLE_PLUGIN_NFACCT) message(STATUS "nfacct.plugin: enabled") - add_executable(nfacct.plugin config.h ${NFACCT_PLUGIN_FILES}) + add_executable(nfacct.plugin ${GENERATED_CONFIG_H} ${NFACCT_PLUGIN_FILES}) target_link_libraries (nfacct.plugin libnetdata ${NETDATA_COMMON_LIBRARIES} ${MNL_LIBRARIES} ${NFACCT_LIBRARIES}) target_include_directories(nfacct.plugin PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS} ${MNL_INCLUDE_DIRS} ${NFACCT_INCLUDE_DIRS}) + target_include_directories(nfacct.plugin BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(nfacct.plugin PUBLIC ${NETDATA_COMMON_CFLAGS} ${MNL_CFLAGS_OTHER} ${NFACCT_CFLAGS_OTHER}) ELSE() message(STATUS "nfacct.plugin: disabled (requires libmnl and libnetfilter_acct)") @@ -1397,9 +1424,10 @@ ENDIF() IF(ENABLE_PLUGIN_XENSTAT) message(STATUS "xenstat.plugin: enabled") - add_executable(xenstat.plugin config.h ${XENSTAT_PLUGIN_FILES}) + add_executable(xenstat.plugin ${GENERATED_CONFIG_H} ${XENSTAT_PLUGIN_FILES}) target_link_libraries (xenstat.plugin libnetdata ${NETDATA_COMMON_LIBRARIES} ${XENSTAT_LIBRARIES}) target_include_directories(xenstat.plugin PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS} ${XENSTAT_INCLUDE_DIRS}) + target_include_directories(xenstat.plugin BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(xenstat.plugin PUBLIC ${NETDATA_COMMON_CFLAGS} ${XENSTAT_CFLAGS_OTHER}) ELSE() message(STATUS "xenstat.plugin: disabled (requires libxenstat)") @@ -1411,9 +1439,10 @@ ENDIF() IF(ENABLE_PLUGIN_PERF) message(STATUS "perf.plugin: enabled") - add_executable(perf.plugin config.h ${PERF_PLUGIN_FILES}) + add_executable(perf.plugin ${GENERATED_CONFIG_H} ${PERF_PLUGIN_FILES}) target_link_libraries (perf.plugin libnetdata ${NETDATA_COMMON_LIBRARIES}) target_include_directories(perf.plugin PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS}) + target_include_directories(perf.plugin BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(perf.plugin PUBLIC ${NETDATA_COMMON_CFLAGS}) ELSE() message(STATUS "perf.plugin: disabled") @@ -1425,9 +1454,10 @@ ENDIF() IF(ENABLE_PLUGIN_EBPF) message(STATUS "ebpf.plugin: enabled") - add_executable(ebpf.plugin config.h ${EBPF_PROCESS_PLUGIN_FILES}) + add_executable(ebpf.plugin ${GENERATED_CONFIG_H} ${EBPF_PROCESS_PLUGIN_FILES}) target_link_libraries (ebpf.plugin libnetdata ${NETDATA_COMMON_LIBRARIES}) target_include_directories(ebpf.plugin PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS}) + target_include_directories(ebpf.plugin BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(ebpf.plugin PUBLIC ${NETDATA_COMMON_CFLAGS}) ELSE() message(STATUS "ebpf.plugin: disabled") @@ -1439,9 +1469,10 @@ ENDIF() IF(ENABLE_PLUGIN_SLABINFO) message(STATUS "slabinfo.plugin: enabled") - add_executable(slabinfo.plugin config.h ${SLABINFO_PLUGIN_FILES}) + add_executable(slabinfo.plugin ${GENERATED_CONFIG_H} ${SLABINFO_PLUGIN_FILES}) target_link_libraries (slabinfo.plugin libnetdata ${NETDATA_COMMON_LIBRARIES}) target_include_directories(slabinfo.plugin PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS}) + target_include_directories(slabinfo.plugin BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(slabinfo.plugin PUBLIC ${NETDATA_COMMON_CFLAGS}) ELSE() message(STATUS "slabinfo.plugin: disabled") @@ -1453,9 +1484,10 @@ ENDIF() IF(ENABLE_PLUGIN_CUPS) message(STATUS "cups.plugin: enabled") - add_executable(cups.plugin config.h ${CUPS_PLUGIN_FILES}) + add_executable(cups.plugin ${GENERATED_CONFIG_H} ${CUPS_PLUGIN_FILES}) target_link_libraries (cups.plugin libnetdata ${NETDATA_COMMON_LIBRARIES} ${CUPS_LIBRARIES}) target_include_directories(cups.plugin PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS} ${CUPS_INCLUDE_DIRS}) + target_include_directories(cups.plugin BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(cups.plugin PUBLIC ${NETDATA_COMMON_CFLAGS} ${CUPS_CFLAGS_OTHER}) ELSE() message(STATUS "cups.plugin: disabled") @@ -1467,9 +1499,10 @@ ENDIF() IF(ENABLE_PLUGIN_CGROUP_NETWORK) message(STATUS "cgroup-network: enabled") - add_executable(cgroup-network config.h ${CGROUP_NETWORK_FILES}) + add_executable(cgroup-network ${GENERATED_CONFIG_H} ${CGROUP_NETWORK_FILES}) target_link_libraries (cgroup-network libnetdata ${NETDATA_COMMON_LIBRARIES}) target_include_directories(cgroup-network PUBLIC ${NETDATA_COMMON_INCLUDE_DIRS}) + target_include_directories(cgroup-network BEFORE PUBLIC ${GENERATED_CONFIG_H_DIR}) target_compile_options(cgroup-network PUBLIC ${NETDATA_COMMON_CFLAGS}) ELSE() message(STATUS "cgroup-network: disabled (requires Linux)") @@ -1500,6 +1533,7 @@ if(BUILD_TESTING) exporting/tests/netdata_doubles.c exporting/tests/system_doubles.c database/rrdlabels.c + database/rrdvar.c ) set(TEST_NAME exporting_engine) set(PROMETHEUS_REMOTE_WRITE_LINK_OPTIONS) @@ -1672,3 +1706,97 @@ endif() endif() endif() + + +# generate config.h so that CMake becomes independent of automake + +## netdata version +set(GIT_EXECUTABLE "git") +execute_process(COMMAND ${GIT_EXECUTABLE} describe OUTPUT_VARIABLE NETDATA_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) + +IF("${NETDATA_VERSION}" STREQUAL "") + file(STRINGS "packaging/version" NETDATA_VERSION LIMIT_COUNT 1) +ENDIF() + +set(NETDATA_USER "netdata" CACHE STRING "use this user to drop privileges") + +if(MAC_OS) + set(ENABLE_APPS_PLUGIN False) +else() + set(ENABLE_APPS_PLUGIN True) +endif() + +check_include_file(time.h HAVE_TIME_H) +check_include_file(unistd.h HAVE_UNISTD_H) +if (HAVE_TIME_H) + include(CheckStructHasMember) + check_struct_has_member("struct timespec" tv_sec "time.h" HAVE_STRUCT_TIMESPEC) +endif () + +check_include_file(sys/statfs.h HAVE_SYS_STATFS_H) +check_include_file(sys/statvfs.h HAVE_SYS_STATVFS_H) +check_include_file(inttypes.h HAVE_INTTYPES_H) +check_include_file(stdint.h HAVE_STDINT_H) + +include(CheckSymbolExists) +check_include_file(sys/mkdev.h HAVE_SYS_MKDEV_H) +if (HAVE_SYS_MKDEV_H) + check_symbol_exists(major "sys/mkdev.h" MAJOR_IN_MKDEV) +endif() +check_include_file(sys/sysmacros.h HAVE_SYS_SYSMACROS_H) +if (HAVE_SYS_SYSMACROS_H) + check_symbol_exists(major "sys/sysmacros.h" MAJOR_IN_SYSMACROS) +endif() + +if (CRYPTO_FOUND) + set(HAVE_CRYPTO True) + FIND_LIBRARY(CRYPTO_LIBRARY_LOCATION NAMES crypto) + check_library_exists(crypto X509_VERIFY_PARAM_set1_host ${CRYPTO_LIBRARY_LOCATION} HAVE_X509_VERIFY_PARAM_set1_host) + if (HAVE_X509_VERIFY_PARAM_set1_host) + set(HAVE_X509_VERIFY_PARAM_set1_host True) + endif() +endif() + +include(CheckCSourceCompiles) +check_c_source_compiles(" + #define _GNU_SOURCE + #include + int main() { char x = *strerror_r(0, &x, sizeof(x)); return 0; } + " STRERROR_R_CHAR_P) + +IF(${CMAKE_C_COMPILER_ID} STREQUAL "GNU") + SET(LIKELY_MACRO "__builtin_expect(!!(x), 1)") + SET(UNLIKELY_MACRO "__builtin_expect(!!(x), 0)") +ELSE() + SET(LIKELY_MACRO "(x)") + SET(UNLIKELY_MACRO "(x)") +ENDIF() + +IF(${CMAKE_C_COMPILER_ID} STREQUAL "GNU") + SET(ALWAYS_UNUSED_MACRO "__attribute__((unused))") + SET(MAYBE_UNUSED_MACRO "__attribute__((unused))") +ELSE() + SET(ALWAYS_UNUSED_MACRO "") + SET(MAYBE_UNUSED_MACRO "") +ENDIF() + +# ----------------------------------------------------------------------------- + +check_library_exists(c clock_gettime "" HAVE_CLOCK_GETTIME) + +IF(NOT HAVE_CLOCK_GETTIME) + CHECK_LIBRARY_EXISTS(rt clock_gettime "" HAVE_CLOCK_GETTIME) +ENDIF() + +IF(HAVE_CLOCK_GETTIME) + message("-- clock_gettime(): found") + set(NETDATA_REQUIRED_DEFINES "-DHAVE_CLOCK_GETTIME=1 ${NETDATA_REQUIRED_DEFINES}") +ELSE() + message("-- clock_gettime(): not found") +ENDIF() + +# ----------------------------------------------------------------------------- +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NETDATA_REQUIRED_DEFINES}") +message("CFLAGS=\"${CMAKE_C_FLAGS}\"") + +configure_file(config.cmake.h.in config.h) diff --git a/Makefile.am b/Makefile.am index 7f8adf6af..5e1605237 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,7 +38,7 @@ EXTRA_DIST = \ build/m4/ax_c_mallopt.m4 \ build/m4/tcmalloc.m4 \ build/m4/ax_c__generic.m4 \ - ml/kmeans/dlib \ + ml/dlib \ README.md \ LICENSE \ REDISTRIBUTED.md \ @@ -109,7 +109,6 @@ SUBDIRS += \ claim \ parser \ spawn \ - ml \ $(NULL) AM_CFLAGS = \ @@ -173,6 +172,8 @@ LIBNETDATA_FILES = \ libnetdata/socket/security.h \ libnetdata/statistical/statistical.c \ libnetdata/statistical/statistical.h \ + libnetdata/string/string.c \ + libnetdata/string/string.h \ libnetdata/storage_number/storage_number.c \ libnetdata/storage_number/storage_number.h \ libnetdata/threads/threads.c \ @@ -202,10 +203,6 @@ APPS_PLUGIN_FILES = \ $(LIBNETDATA_FILES) \ $(NULL) -CHECKS_PLUGIN_FILES = \ - collectors/checks.plugin/plugin_checks.c \ - $(NULL) - FREEBSD_PLUGIN_FILES = \ collectors/freebsd.plugin/plugin_freebsd.c \ collectors/freebsd.plugin/plugin_freebsd.h \ @@ -235,39 +232,40 @@ ML_FILES = \ if ENABLE_ML ML_FILES += \ - ml/BitBufferCounter.h \ - ml/BitBufferCounter.cc \ - ml/BitRateWindow.h \ - ml/BitRateWindow.cc \ + ml/ADCharts.h \ + ml/ADCharts.cc \ ml/Config.h \ ml/Config.cc \ - ml/Database.h \ - ml/Database.cc \ ml/Dimension.cc \ ml/Dimension.h \ ml/Host.h \ ml/Host.cc \ ml/Query.h \ - ml/kmeans/KMeans.h \ - ml/kmeans/KMeans.cc \ - ml/kmeans/SamplesBuffer.h \ - ml/kmeans/SamplesBuffer.cc \ - ml/kmeans/dlib/dlib/all/source.cpp \ + ml/KMeans.h \ + ml/KMeans.cc \ + ml/SamplesBuffer.h \ + ml/SamplesBuffer.cc \ + ml/dlib/dlib/all/source.cpp \ ml/json/single_include/nlohmann/json.hpp \ ml/ml.cc \ ml/ml-private.h \ $(NULL) # Disable warnings from dlib library -ml/kmeans/dlib/dlib/all/source.$(OBJEXT) : CXXFLAGS += -Wno-sign-compare -Wno-type-limits -Wno-aggressive-loop-optimizations -Wno-stringop-overflow +ml/dlib/dlib/all/source.$(OBJEXT) : CXXFLAGS += -Wno-sign-compare -Wno-type-limits -Wno-aggressive-loop-optimizations -Wno-stringop-overflow -Wno-psabi + +# Disable ml warnings +ml/Dimension.$(OBJEXT) : CXXFLAGS += -Wno-psabi +ml/Host.$(OBJEXT) : CXXFLAGS += -Wno-psabi +ml/KMeans.$(OBJEXT) : CXXFLAGS += -Wno-psabi +ml/ml.$(OBJEXT) : CXXFLAGS += -Wno-psabi endif if ENABLE_ML_TESTS ML_TESTS_FILES = \ - ml/kmeans/Tests.cc \ - ml/Tests.cc \ + ml/SamplesBufferTests.cc \ $(NULL) endif @@ -384,8 +382,6 @@ PROC_PLUGIN_FILES = \ collectors/proc.plugin/proc_net_netstat.c \ collectors/proc.plugin/proc_net_rpc_nfs.c \ collectors/proc.plugin/proc_net_rpc_nfsd.c \ - collectors/proc.plugin/proc_net_snmp.c \ - collectors/proc.plugin/proc_net_snmp6.c \ collectors/proc.plugin/proc_net_sctp_snmp.c \ collectors/proc.plugin/proc_net_sockstat.c \ collectors/proc.plugin/proc_net_sockstat6.c \ @@ -445,6 +441,8 @@ RRD_PLUGIN_FILES = \ database/rrd.c \ database/rrd.h \ database/rrdset.c \ + database/rrdfunctions.c \ + database/rrdfunctions.h \ database/rrdsetvar.c \ database/rrdsetvar.h \ database/rrdvar.c \ @@ -460,13 +458,13 @@ RRD_PLUGIN_FILES = \ database/sqlite/sqlite_db_migration.c \ database/sqlite/sqlite_db_migration.h \ database/sqlite/sqlite_aclk.c \ - database/sqlite/sqlite_aclk.h \ + database/sqlite/sqlite_aclk.h \ + database/sqlite/sqlite_metadata.c \ + database/sqlite/sqlite_metadata.h \ database/sqlite/sqlite_health.c \ database/sqlite/sqlite_health.h \ database/sqlite/sqlite_aclk_node.c \ database/sqlite/sqlite_aclk_node.h \ - database/sqlite/sqlite_aclk_chart.c \ - database/sqlite/sqlite_aclk_chart.h \ database/sqlite/sqlite_aclk_alert.c \ database/sqlite/sqlite_aclk_alert.h \ database/sqlite/sqlite3.c \ @@ -478,65 +476,65 @@ RRD_PLUGIN_FILES = \ database/sqlite/sqlite3.$(OBJEXT) : CFLAGS += -Wno-cast-function-type database/KolmogorovSmirnovDist.$(OBJEXT) : CFLAGS += -Wno-maybe-uninitialized -if ENABLE_DBENGINE - noinst_LIBRARIES = libjudy.a - - libjudy_a_SOURCES = libnetdata/libjudy/src/Judy.h \ - libnetdata/libjudy/src/JudyCommon/JudyMalloc.c \ - libnetdata/libjudy/src/JudyCommon/JudyPrivate.h \ - libnetdata/libjudy/src/JudyCommon/JudyPrivate1L.h \ - libnetdata/libjudy/src/JudyCommon/JudyPrivateBranch.h \ - libnetdata/libjudy/src/JudyL/JudyL.h \ - libnetdata/libjudy/src/JudyL/JudyLByCount.c \ - libnetdata/libjudy/src/JudyL/JudyLCascade.c \ - libnetdata/libjudy/src/JudyL/JudyLCount.c \ - libnetdata/libjudy/src/JudyL/JudyLCreateBranch.c \ - libnetdata/libjudy/src/JudyL/JudyLDecascade.c \ - libnetdata/libjudy/src/JudyL/JudyLDel.c \ - libnetdata/libjudy/src/JudyL/JudyLFirst.c \ - libnetdata/libjudy/src/JudyL/JudyLFreeArray.c \ - libnetdata/libjudy/src/JudyL/j__udyLGet.c \ - libnetdata/libjudy/src/JudyL/JudyLGet.c \ - libnetdata/libjudy/src/JudyL/JudyLInsArray.c \ - libnetdata/libjudy/src/JudyL/JudyLIns.c \ - libnetdata/libjudy/src/JudyL/JudyLInsertBranch.c \ - libnetdata/libjudy/src/JudyL/JudyLMallocIF.c \ - libnetdata/libjudy/src/JudyL/JudyLMemActive.c \ - libnetdata/libjudy/src/JudyL/JudyLMemUsed.c \ - libnetdata/libjudy/src/JudyL/JudyLNext.c \ - libnetdata/libjudy/src/JudyL/JudyLNextEmpty.c \ - libnetdata/libjudy/src/JudyL/JudyLPrev.c \ - libnetdata/libjudy/src/JudyL/JudyLPrevEmpty.c \ - libnetdata/libjudy/src/JudyHS/JudyHS.c \ - $(NULL) +noinst_LIBRARIES = libjudy.a + +libjudy_a_SOURCES = libnetdata/libjudy/src/Judy.h \ + libnetdata/libjudy/src/JudyCommon/JudyMalloc.c \ + libnetdata/libjudy/src/JudyCommon/JudyPrivate.h \ + libnetdata/libjudy/src/JudyCommon/JudyPrivate1L.h \ + libnetdata/libjudy/src/JudyCommon/JudyPrivateBranch.h \ + libnetdata/libjudy/src/JudyL/JudyL.h \ + libnetdata/libjudy/src/JudyL/JudyLByCount.c \ + libnetdata/libjudy/src/JudyL/JudyLCascade.c \ + libnetdata/libjudy/src/JudyL/JudyLCount.c \ + libnetdata/libjudy/src/JudyL/JudyLCreateBranch.c \ + libnetdata/libjudy/src/JudyL/JudyLDecascade.c \ + libnetdata/libjudy/src/JudyL/JudyLDel.c \ + libnetdata/libjudy/src/JudyL/JudyLFirst.c \ + libnetdata/libjudy/src/JudyL/JudyLFreeArray.c \ + libnetdata/libjudy/src/JudyL/j__udyLGet.c \ + libnetdata/libjudy/src/JudyL/JudyLGet.c \ + libnetdata/libjudy/src/JudyL/JudyLInsArray.c \ + libnetdata/libjudy/src/JudyL/JudyLIns.c \ + libnetdata/libjudy/src/JudyL/JudyLInsertBranch.c \ + libnetdata/libjudy/src/JudyL/JudyLMallocIF.c \ + libnetdata/libjudy/src/JudyL/JudyLMemActive.c \ + libnetdata/libjudy/src/JudyL/JudyLMemUsed.c \ + libnetdata/libjudy/src/JudyL/JudyLNext.c \ + libnetdata/libjudy/src/JudyL/JudyLNextEmpty.c \ + libnetdata/libjudy/src/JudyL/JudyLPrev.c \ + libnetdata/libjudy/src/JudyL/JudyLPrevEmpty.c \ + libnetdata/libjudy/src/JudyHS/JudyHS.c \ + $(NULL) - nodist_libjudy_a_SOURCES = JudyLTables.c +nodist_libjudy_a_SOURCES = JudyLTables.c - BUILT_SOURCES += JudyLTables.c +BUILT_SOURCES += JudyLTables.c - CLEANFILES += JudyLTables.c +CLEANFILES += JudyLTables.c - libjudy_a_CFLAGS = $(LIBJUDY_CFLAGS) -DJUDYL -I$(abs_top_srcdir)/libnetdata/libjudy/src -I$(abs_top_srcdir)/libnetdata/libjudy/src/JudyCommon -Wno-sign-compare -Wno-implicit-fallthrough +libjudy_a_CFLAGS = $(LIBJUDY_CFLAGS) -DJUDYL -I$(abs_top_srcdir)/libnetdata/libjudy/src -I$(abs_top_srcdir)/libnetdata/libjudy/src/JudyCommon -Wno-sign-compare -Wno-implicit-fallthrough - libnetdata/libjudy/src/JudyL/libjudy_a-JudyLPrev.$(OBJEXT) : CFLAGS += -DJUDYPREV - libnetdata/libjudy/src/JudyL/libjudy_a-JudyLPrevEmpty.$(OBJEXT) : CFLAGS += -DJUDYPREV - libnetdata/libjudy/src/JudyL/libjudy_a-JudyLNext.$(OBJEXT) : CFLAGS += -DJUDYNEXT - libnetdata/libjudy/src/JudyL/libjudy_a-JudyLNextEmpty.$(OBJEXT) : CFLAGS += -DJUDYNEXT - libnetdata/libjudy/src/JudyL/libjudy_a-JudyLByCount.$(OBJEXT) : CFLAGS += -DNOSMARTJBB -DNOSMARTJBU -DNOSMARTJLB - libnetdata/libjudy/src/JudyL/libjudy_a-j__udyLGet.$(OBJEXT) : CFLAGS += -DJUDYGETINLINE +libnetdata/libjudy/src/JudyL/libjudy_a-JudyLPrev.$(OBJEXT) : CFLAGS += -DJUDYPREV +libnetdata/libjudy/src/JudyL/libjudy_a-JudyLPrevEmpty.$(OBJEXT) : CFLAGS += -DJUDYPREV +libnetdata/libjudy/src/JudyL/libjudy_a-JudyLNext.$(OBJEXT) : CFLAGS += -DJUDYNEXT +libnetdata/libjudy/src/JudyL/libjudy_a-JudyLNextEmpty.$(OBJEXT) : CFLAGS += -DJUDYNEXT +libnetdata/libjudy/src/JudyL/libjudy_a-JudyLByCount.$(OBJEXT) : CFLAGS += -DNOSMARTJBB -DNOSMARTJBU -DNOSMARTJLB +libnetdata/libjudy/src/JudyL/libjudy_a-j__udyLGet.$(OBJEXT) : CFLAGS += -DJUDYGETINLINE - noinst_PROGRAMS = judyltablesgen +noinst_PROGRAMS = judyltablesgen - judyltablesgen_SOURCES = libnetdata/libjudy/src/JudyL/JudyLTablesGen.c - judyltablesgen_CFLAGS = $(LIBJUDY_CFLAGS) -DJUDYL -I$(abs_top_srcdir)/libnetdata/libjudy/src -I$(abs_top_srcdir)/libnetdata/libjudy/src/JudyCommon -Wno-sign-compare -Wno-implicit-fallthrough +judyltablesgen_SOURCES = libnetdata/libjudy/src/JudyL/JudyLTablesGen.c +judyltablesgen_CFLAGS = $(LIBJUDY_CFLAGS) -DJUDYL -I$(abs_top_srcdir)/libnetdata/libjudy/src -I$(abs_top_srcdir)/libnetdata/libjudy/src/JudyCommon -Wno-sign-compare -Wno-implicit-fallthrough - judyltablesgen$(EXEEXT) : CFLAGS += -Wno-format -Wno-format-security +$(builddir)/judyltablesgen$(EXEEXT) : CFLAGS += -Wno-format -Wno-format-security JudyLTables.c: $(abs_top_srcdir)/libnetdata/libjudy/src/JudyL/JudyLTablesGen.c $(builddir)/judyltablesgen$(EXEEXT) $(builddir)/judyltablesgen$(EXEEXT) - libjudy_a-JudyLTables.$(OBJEXT) : CFLAGS += -I$(abs_top_srcdir)/libnetdata/libjudy/src/JudyL +libjudy_a-JudyLTables.$(OBJEXT) : CFLAGS += -I$(abs_top_srcdir)/libnetdata/libjudy/src/JudyL +if ENABLE_DBENGINE RRD_PLUGIN_FILES += \ database/engine/rrdengine.c \ database/engine/rrdengine.h \ @@ -553,16 +551,6 @@ JudyLTables.c: $(abs_top_srcdir)/libnetdata/libjudy/src/JudyL/JudyLTablesGen.c $ database/engine/pagecache.h \ database/engine/rrdenglocking.c \ database/engine/rrdenglocking.h \ - database/engine/metadata_log/metadatalog.h \ - database/engine/metadata_log/metadatalogapi.c \ - database/engine/metadata_log/metadatalogapi.h \ - database/engine/metadata_log/logfile.h \ - database/engine/metadata_log/logfile.c \ - database/engine/metadata_log/metadatalogprotocol.h \ - database/engine/metadata_log/metalogpluginsd.c \ - database/engine/metadata_log/metalogpluginsd.h \ - database/engine/metadata_log/compaction.c \ - database/engine/metadata_log/compaction.h \ $(NULL) endif @@ -630,6 +618,8 @@ STREAMING_PLUGIN_FILES = \ streaming/compression.c \ streaming/sender.c \ streaming/receiver.c \ + streaming/replication.h \ + streaming/replication.c \ streaming/rrdpush.h \ $(NULL) @@ -676,8 +666,6 @@ PARSER_FILES = \ if ENABLE_ACLK ACLK_FILES = \ - aclk/aclk.c \ - aclk/aclk.h \ aclk/aclk_util.c \ aclk/aclk_util.h \ aclk/aclk_stats.c \ @@ -694,38 +682,20 @@ ACLK_FILES = \ aclk/aclk_rx_msgs.h \ aclk/https_client.c \ aclk/https_client.h \ - mqtt_websockets/src/mqtt_wss_client.c \ - mqtt_websockets/src/include/mqtt_wss_client.h \ - mqtt_websockets/src/mqtt_wss_log.c \ - mqtt_websockets/src/include/mqtt_wss_log.h \ - mqtt_websockets/src/ws_client.c \ - mqtt_websockets/src/include/ws_client.h \ - mqtt_websockets/src/mqtt_ng.c \ - mqtt_websockets/src/include/mqtt_ng.h \ - mqtt_websockets/src/common_public.c \ - mqtt_websockets/src/include/common_public.h \ - mqtt_websockets/src/include/common_internal.h \ - mqtt_websockets/c-rbuf/src/ringbuffer.c \ - mqtt_websockets/c-rbuf/include/ringbuffer.h \ - mqtt_websockets/c-rbuf/src/ringbuffer_internal.h \ - mqtt_websockets/MQTT-C/src/mqtt.c \ - mqtt_websockets/MQTT-C/include/mqtt.h \ - aclk/aclk_charts_api.c \ - aclk/aclk_charts_api.h \ aclk/aclk_alarm_api.c \ aclk/aclk_alarm_api.h \ aclk/aclk_contexts_api.c \ aclk/aclk_contexts_api.h \ + aclk/aclk_capas.c \ + aclk/aclk_capas.h \ + aclk/helpers/mqtt_wss_pal.h \ + aclk/helpers/ringbuffer_pal.h \ aclk/schema-wrappers/connection.cc \ aclk/schema-wrappers/connection.h \ aclk/schema-wrappers/node_connection.cc \ aclk/schema-wrappers/node_connection.h \ aclk/schema-wrappers/node_creation.cc \ aclk/schema-wrappers/node_creation.h \ - aclk/schema-wrappers/chart_stream.cc \ - aclk/schema-wrappers/chart_stream.h \ - aclk/schema-wrappers/chart_config.cc \ - aclk/schema-wrappers/chart_config.h \ aclk/schema-wrappers/alarm_stream.cc \ aclk/schema-wrappers/alarm_stream.h \ aclk/schema-wrappers/alarm_config.cc \ @@ -745,6 +715,28 @@ ACLK_FILES = \ aclk/schema-wrappers/context.h \ $(NULL) +noinst_LIBRARIES += libmqttwebsockets.a + +libmqttwebsockets_a_SOURCES = \ + mqtt_websockets/src/mqtt_wss_client.c \ + mqtt_websockets/src/include/mqtt_wss_client.h \ + mqtt_websockets/src/mqtt_wss_log.c \ + mqtt_websockets/src/include/mqtt_wss_log.h \ + mqtt_websockets/src/ws_client.c \ + mqtt_websockets/src/include/ws_client.h \ + mqtt_websockets/src/mqtt_ng.c \ + mqtt_websockets/src/include/mqtt_ng.h \ + mqtt_websockets/src/common_public.c \ + mqtt_websockets/src/include/common_public.h \ + mqtt_websockets/src/include/common_internal.h \ + mqtt_websockets/c-rbuf/src/ringbuffer.c \ + mqtt_websockets/c-rbuf/include/ringbuffer.h \ + mqtt_websockets/c-rbuf/src/ringbuffer_internal.h \ + mqtt_websockets/MQTT-C/src/mqtt.c \ + mqtt_websockets/MQTT-C/include/mqtt.h + +libmqttwebsockets_a_CFLAGS = $(CFLAGS) -DMQTT_WSS_CUSTOM_ALLOC -DRBUF_CUSTOM_MALLOC -I$(srcdir)/aclk/helpers + mqtt_websockets/src/mqtt_wss_client.$(OBJEXT) : CFLAGS += -Wno-unused-result ACLK_PROTO_DEFINITIONS = \ @@ -753,10 +745,6 @@ ACLK_PROTO_DEFINITIONS = \ aclk/aclk-schemas/proto/agent/v1/connection.proto \ aclk/aclk-schemas/proto/alarm/v1/config.proto \ aclk/aclk-schemas/proto/alarm/v1/stream.proto \ - aclk/aclk-schemas/proto/chart/v1/config.proto \ - aclk/aclk-schemas/proto/chart/v1/dimension.proto \ - aclk/aclk-schemas/proto/chart/v1/instance.proto \ - aclk/aclk-schemas/proto/chart/v1/stream.proto \ aclk/aclk-schemas/proto/nodeinstance/connection/v1/connection.proto \ aclk/aclk-schemas/proto/nodeinstance/create/v1/creation.proto \ aclk/aclk-schemas/proto/nodeinstance/info/v1/info.proto \ @@ -772,14 +760,6 @@ ACLK_PROTO_BUILT_FILES = aclk/aclk-schemas/proto/agent/v1/connection.pb.cc \ aclk/aclk-schemas/proto/nodeinstance/connection/v1/connection.pb.h \ aclk/aclk-schemas/proto/nodeinstance/create/v1/creation.pb.cc \ aclk/aclk-schemas/proto/nodeinstance/create/v1/creation.pb.h \ - aclk/aclk-schemas/proto/chart/v1/stream.pb.cc \ - aclk/aclk-schemas/proto/chart/v1/stream.pb.h \ - aclk/aclk-schemas/proto/chart/v1/instance.pb.cc \ - aclk/aclk-schemas/proto/chart/v1/instance.pb.h \ - aclk/aclk-schemas/proto/chart/v1/dimension.pb.cc \ - aclk/aclk-schemas/proto/chart/v1/dimension.pb.h \ - aclk/aclk-schemas/proto/chart/v1/config.pb.cc \ - aclk/aclk-schemas/proto/chart/v1/config.pb.h \ aclk/aclk-schemas/proto/aclk/v1/lib.pb.cc \ aclk/aclk-schemas/proto/aclk/v1/lib.pb.h \ aclk/aclk-schemas/proto/agent/v1/disconnect.pb.cc \ @@ -812,22 +792,6 @@ aclk/aclk-schemas/proto/nodeinstance/create/v1/creation.pb.cc \ aclk/aclk-schemas/proto/nodeinstance/create/v1/creation.pb.h: aclk/aclk-schemas/proto/nodeinstance/create/v1/creation.proto $(PROTOC) -I=aclk/aclk-schemas --cpp_out=$(builddir)/aclk/aclk-schemas $^ -aclk/aclk-schemas/proto/chart/v1/stream.pb.cc \ -aclk/aclk-schemas/proto/chart/v1/stream.pb.h: aclk/aclk-schemas/proto/chart/v1/stream.proto - $(PROTOC) -I=aclk/aclk-schemas --cpp_out=$(builddir)/aclk/aclk-schemas $^ - -aclk/aclk-schemas/proto/chart/v1/instance.pb.cc \ -aclk/aclk-schemas/proto/chart/v1/instance.pb.h: aclk/aclk-schemas/proto/chart/v1/instance.proto - $(PROTOC) -I=aclk/aclk-schemas --cpp_out=$(builddir)/aclk/aclk-schemas $^ - -aclk/aclk-schemas/proto/chart/v1/dimension.pb.cc \ -aclk/aclk-schemas/proto/chart/v1/dimension.pb.h: aclk/aclk-schemas/proto/chart/v1/dimension.proto - $(PROTOC) -I=aclk/aclk-schemas --cpp_out=$(builddir)/aclk/aclk-schemas $^ - -aclk/aclk-schemas/proto/chart/v1/config.pb.cc \ -aclk/aclk-schemas/proto/chart/v1/config.pb.h: aclk/aclk-schemas/proto/chart/v1/config.proto - $(PROTOC) -I=aclk/aclk-schemas --cpp_out=$(builddir)/aclk/aclk-schemas $^ - aclk/aclk-schemas/proto/aclk/v1/lib.pb.cc \ aclk/aclk-schemas/proto/aclk/v1/lib.pb.h: aclk/aclk-schemas/proto/aclk/v1/lib.proto $(PROTOC) -I=aclk/aclk-schemas --cpp_out=$(builddir)/aclk/aclk-schemas $^ @@ -860,10 +824,10 @@ endif #ENABLE_ACLK ACLK_ALWAYS_BUILD_FILES = \ aclk/aclk_rrdhost_state.h \ - aclk/aclk_api.c \ - aclk/aclk_api.h \ aclk/aclk_proxy.c \ aclk/aclk_proxy.h \ + aclk/aclk.c \ + aclk/aclk.h \ $(NULL) SPAWN_PLUGIN_FILES = \ @@ -950,7 +914,6 @@ NETDATA_FILES = \ $(LIBNETDATA_FILES) \ $(API_PLUGIN_FILES) \ $(EXPORTING_ENGINE_FILES) \ - $(CHECKS_PLUGIN_FILES) \ $(HEALTH_PLUGIN_FILES) \ $(ML_FILES) \ $(ML_TESTS_FILES) \ @@ -1002,15 +965,15 @@ NETDATA_COMMON_LIBS = \ $(OPTIONAL_MQTT_LIBS) \ $(OPTIONAL_UV_LIBS) \ $(OPTIONAL_LZ4_LIBS) \ - $(OPTIONAL_JUDY_LIBS) \ + libjudy.a \ $(OPTIONAL_SSL_LIBS) \ $(OPTIONAL_JSONC_LIBS) \ $(OPTIONAL_ATOMIC_LIBS) \ + $(OPTIONAL_DL_LIBS) \ $(NULL) -if ENABLE_DBENGINE - NETDATA_COMMON_LIBS += libjudy.a \ - $(NULL) +if ENABLE_ACLK + NETDATA_COMMON_LIBS += libmqttwebsockets.a endif if LINK_STATIC_JSONC @@ -1254,6 +1217,7 @@ if ENABLE_UNITTESTS $(EXPORTING_ENGINE_FILES) \ $(LIBNETDATA_FILES) \ database/rrdlabels.c \ + database/rrdvar.c \ $(NULL) exporting_tests_exporting_engine_testdriver_CFLAGS = \ $(AM_CFLAGS) \ diff --git a/aclk/README.md b/aclk/README.md index 6f541c38e..af0f5fdde 100644 --- a/aclk/README.md +++ b/aclk/README.md @@ -19,7 +19,7 @@ The Cloud App lives at app.netdata.cloud which currently resolves to the followi :::caution -This list of IPs can change without notice, we strongly advise you to whitelist the domain `app.netdata.cloud`, if +This list of IPs can change without notice, we strongly advise you to whitelist following domains `api.netdata.cloud`, `mqtt.netdata.cloud`, if this is not an option in your case always verify the current domain resolution (e.g via the `host` command). ::: @@ -49,7 +49,7 @@ configuration uses two settings: ```conf [global] enabled = yes - cloud base url = https://app.netdata.cloud + cloud base url = https://api.netdata.cloud ``` If your Agent needs to use a proxy to access the internet, you must [set up a proxy for @@ -60,12 +60,10 @@ You can configure following keys in the `netdata.conf` section `[cloud]`: [cloud] statistics = yes query thread count = 2 - mqtt5 = yes ``` - `statistics` enables/disables ACLK related statistics and their charts. You can disable this to save some space in the database and slightly reduce memory usage of Netdata Agent. - `query thread count` specifies the number of threads to process cloud queries. Increasing this setting is useful for nodes with many children (streaming), which can expect to handle more queries (and/or more complicated queries). -- `mqtt5` allows disabling the new MQTT5 implementation which is used now by default in case of issues. This option will be removed in future stable release. ## Disable the ACLK @@ -112,7 +110,7 @@ must contain only `EOF`. ```bash [global] enabled = no - cloud base url = https://app.netdata.cloud + cloud base url = https://api.netdata.cloud EOF ``` diff --git a/aclk/aclk.c b/aclk/aclk.c index 7b3641b1e..3b035b849 100644 --- a/aclk/aclk.c +++ b/aclk/aclk.c @@ -2,6 +2,7 @@ #include "aclk.h" +#ifdef ENABLE_ACLK #include "aclk_stats.h" #include "mqtt_wss_client.h" #include "aclk_otp.h" @@ -12,6 +13,7 @@ #include "aclk_rx_msgs.h" #include "https_client.h" #include "schema-wrappers/schema_wrappers.h" +#include "aclk_capas.h" #include "aclk_proxy.h" @@ -23,21 +25,31 @@ #define ACLK_STABLE_TIMEOUT 3 // Minimum delay to mark AGENT as stable +#endif /* ENABLE_ACLK */ + int aclk_pubacks_per_conn = 0; // How many PubAcks we got since MQTT conn est. int aclk_rcvd_cloud_msgs = 0; int aclk_connection_counter = 0; int disconnect_req = 0; +int aclk_connected = 0; +int aclk_ctx_based = 0; +int aclk_disable_runtime = 0; +int aclk_stats_enabled; +int aclk_kill_link = 0; + +usec_t aclk_session_us = 0; +time_t aclk_session_sec = 0; + time_t last_conn_time_mqtt = 0; time_t last_conn_time_appl = 0; time_t last_disconnect_time = 0; time_t next_connection_attempt = 0; float last_backoff_value = 0; -int aclk_alert_reloaded = 0; //1 on health log exchange, and again on health_reload - time_t aclk_block_until = 0; +#ifdef ENABLE_ACLK mqtt_wss_client mqttwss_client; netdata_mutex_t aclk_shared_state_mutex = NETDATA_MUTEX_INITIALIZER; @@ -447,9 +459,9 @@ static int aclk_block_till_recon_allowed() { */ static int aclk_get_transport_idx(aclk_env_t *env) { for (size_t i = 0; i < env->transport_count; i++) { - // currently we support only MQTT 3 + // currently we support only MQTT 5 // therefore select first transport that matches - if (env->transports[i]->type == ACLK_TRP_MQTT_3_1_1) { + if (env->transports[i]->type == ACLK_TRP_MQTT_5) { return i; } } @@ -483,7 +495,7 @@ static int aclk_attempt_to_connect(mqtt_wss_client client) while (!netdata_exit) { char *cloud_base_url = appconfig_get(&cloud_config, CONFIG_SECTION_GLOBAL, "cloud base url", NULL); if (cloud_base_url == NULL) { - error("Do not move the cloud base url out of post_conf_load!!"); + error_report("Do not move the cloud base url out of post_conf_load!!"); return -1; } @@ -493,13 +505,13 @@ static int aclk_attempt_to_connect(mqtt_wss_client client) info("Attempting connection now"); memset(&base_url, 0, sizeof(url_t)); if (url_parse(cloud_base_url, &base_url)) { - error("ACLK base URL configuration key could not be parsed. Will retry in %d seconds.", CLOUD_BASE_URL_READ_RETRY); + error_report("ACLK base URL configuration key could not be parsed. Will retry in %d seconds.", CLOUD_BASE_URL_READ_RETRY); sleep(CLOUD_BASE_URL_READ_RETRY); url_t_destroy(&base_url); continue; } - struct mqtt_wss_proxy proxy_conf = { .host = NULL, .port = 0, .type = MQTT_WSS_DIRECT }; + struct mqtt_wss_proxy proxy_conf = { .host = NULL, .port = 0, .username = NULL, .password = NULL, .type = MQTT_WSS_DIRECT }; aclk_set_proxy((char**)&proxy_conf.host, &proxy_conf.port, &proxy_conf.type); struct mqtt_connect_params mqtt_conn_params = { @@ -523,7 +535,7 @@ static int aclk_attempt_to_connect(mqtt_wss_client client) ret = aclk_get_env(aclk_env, base_url.host, base_url.port); url_t_destroy(&base_url); if (ret) { - error("Failed to Get ACLK environment"); + error_report("Failed to Get ACLK environment"); // delay handled by aclk_block_till_recon_allowed continue; } @@ -537,14 +549,14 @@ static int aclk_attempt_to_connect(mqtt_wss_client client) } if (!aclk_env_has_capa("proto")) { - error ("Can't use encoding=proto without at least \"proto\" capability."); + error_report("Can't use encoding=proto without at least \"proto\" capability."); continue; } info("New ACLK protobuf protocol negotiated successfully (/env response)."); memset(&auth_url, 0, sizeof(url_t)); if (url_parse(aclk_env->auth_endpoint, &auth_url)) { - error("Parsing URL returned by env endpoint for authentication failed. \"%s\"", aclk_env->auth_endpoint); + error_report("Parsing URL returned by env endpoint for authentication failed. \"%s\"", aclk_env->auth_endpoint); url_t_destroy(&auth_url); continue; } @@ -552,7 +564,7 @@ static int aclk_attempt_to_connect(mqtt_wss_client client) ret = aclk_get_mqtt_otp(aclk_private_key, (char **)&mqtt_conn_params.clientid, (char **)&mqtt_conn_params.username, (char **)&mqtt_conn_params.password, &auth_url); url_t_destroy(&auth_url); if (ret) { - error("Error passing Challenge/Response to get OTP"); + error_report("Error passing Challenge/Response to get OTP"); continue; } @@ -561,20 +573,20 @@ static int aclk_attempt_to_connect(mqtt_wss_client client) mqtt_conn_params.will_topic = aclk_get_topic(ACLK_TOPICID_AGENT_CONN); if (!mqtt_conn_params.will_topic) { - error("Couldn't get LWT topic. Will not send LWT."); + error_report("Couldn't get LWT topic. Will not send LWT."); continue; } // Do the MQTT connection ret = aclk_get_transport_idx(aclk_env); if (ret < 0) { - error("Cloud /env endpoint didn't return any transport usable by this Agent."); + error_report("Cloud /env endpoint didn't return any transport usable by this Agent."); continue; } memset(&mqtt_url, 0, sizeof(url_t)); if (url_parse(aclk_env->transports[ret]->endpoint, &mqtt_url)){ - error("Failed to parse target URL for /env trp idx %d \"%s\"", ret, aclk_env->transports[ret]->endpoint); + error_report("Failed to parse target URL for /env trp idx %d \"%s\"", ret, aclk_env->transports[ret]->endpoint); url_t_destroy(&mqtt_url); continue; } @@ -660,9 +672,7 @@ void *aclk_main(void *ptr) if (wait_till_agent_claim_ready()) goto exit; - use_mqtt_5 = config_get_boolean(CONFIG_SECTION_CLOUD, "mqtt5", CONFIG_BOOLEAN_YES); - - if (!(mqttwss_client = mqtt_wss_new("mqtt_wss", aclk_mqtt_wss_log_cb, msg_callback, puback_callback, use_mqtt_5))) { + if (!(mqttwss_client = mqtt_wss_new("mqtt_wss", aclk_mqtt_wss_log_cb, msg_callback, puback_callback, 1))) { error("Couldn't initialize MQTT_WSS network library"); goto exit; } @@ -672,7 +682,7 @@ void *aclk_main(void *ptr) // that send JSON payloads of 10 MB as single messages mqtt_wss_set_max_buf_size(mqttwss_client, 25*1024*1024); - aclk_stats_enabled = config_get_boolean(CONFIG_SECTION_CLOUD, "statistics", CONFIG_BOOLEAN_YES); + aclk_stats_enabled = config_get_boolean(CONFIG_SECTION_CLOUD, "statistics", global_statistics_enabled); if (aclk_stats_enabled) { stats_thread = callocz(1, sizeof(struct aclk_stats_thread)); stats_thread->thread = mallocz(sizeof(netdata_thread_t)); @@ -748,7 +758,7 @@ void aclk_host_state_update(RRDHOST *host, int cmd) node_instance_creation_t node_instance_creation = { .claim_id = localhost->aclk_state.claimed_id, .hops = host->system_info->hops, - .hostname = host->hostname, + .hostname = rrdhost_hostname(host), .machine_guid = host->machine_guid }; create_query->data.bin_payload.payload = generate_node_instance_creation(&create_query->data.bin_payload.size, &node_instance_creation); @@ -770,14 +780,7 @@ void aclk_host_state_update(RRDHOST *host, int cmd) node_state_update.node_id = mallocz(UUID_STR_LEN); uuid_unparse_lower(node_id, (char*)node_state_update.node_id); - struct capability caps[] = { - { .name = "proto", .version = 1, .enabled = 1 }, - { .name = "ml", .version = ml_capable(localhost), .enabled = ml_enabled(host) }, - { .name = "mc", .version = enable_metric_correlations ? metric_correlations_version : 0, .enabled = enable_metric_correlations }, - { .name = "ctx", .version = 1, .enabled = rrdcontext_enabled }, - { .name = NULL, .version = 0, .enabled = 0 } - }; - node_state_update.capabilities = caps; + node_state_update.capabilities = aclk_get_agent_capas(); rrdhost_aclk_state_lock(localhost); node_state_update.claim_id = localhost->aclk_state.claimed_id; @@ -815,15 +818,8 @@ void aclk_send_node_instances() char host_id[UUID_STR_LEN]; uuid_unparse_lower(list->host_id, host_id); - RRDHOST *host = rrdhost_find_by_guid(host_id, 0); - struct capability caps[] = { - { .name = "proto", .version = 1, .enabled = 1 }, - { .name = "ml", .version = ml_capable(localhost), .enabled = host ? ml_enabled(host) : 0 }, - { .name = "mc", .version = enable_metric_correlations ? metric_correlations_version : 0, .enabled = enable_metric_correlations }, - { .name = "ctx", .version = 1, .enabled = rrdcontext_enabled }, - { .name = NULL, .version = 0, .enabled = 0 } - }; - node_state_update.capabilities = caps; + RRDHOST *host = rrdhost_find_by_guid(host_id); + node_state_update.capabilities = aclk_get_node_instance_capas(host); rrdhost_aclk_state_lock(localhost); node_state_update.claim_id = localhost->aclk_state.claimed_id; @@ -832,6 +828,8 @@ void aclk_send_node_instances() info("Queuing status update for node=%s, live=%d, hops=%d",(char*)node_state_update.node_id, list->live, list->hops); + + freez((void*)node_state_update.capabilities); freez((void*)node_state_update.node_id); query->data.bin_payload.msg_name = "UpdateNodeInstanceConnection"; query->data.bin_payload.topic = ACLK_TOPICID_NODE_CONN; @@ -853,7 +851,7 @@ void aclk_send_node_instances() rrdhost_aclk_state_unlock(localhost); info("Queuing registration for host=%s, hops=%d",(char*)node_instance_creation.machine_guid, list->hops); - freez(node_instance_creation.machine_guid); + freez((void *)node_instance_creation.machine_guid); aclk_queue_query(create_query); } freez(list->hostname); @@ -891,41 +889,13 @@ static void fill_alert_status_for_host(BUFFER *wb, RRDHOST *host) status.last_submitted_sequence_id ); } +#endif /* ENABLE_ACLK */ -static void fill_chart_status_for_host(BUFFER *wb, RRDHOST *host) -{ - struct aclk_chart_sync_stats *stats = aclk_get_chart_sync_stats(host); - if (!stats) { - buffer_strcat(wb, "\n\t\tFailed to get alert streaming status for this host"); - return; - } - buffer_sprintf(wb, - "\n\t\tUpdates: %d" - "\n\t\tBatch ID: %"PRIu64 - "\n\t\tMin Seq ID: %"PRIu64 - "\n\t\tMax Seq ID: %"PRIu64 - "\n\t\tPending Min Seq ID: %"PRIu64 - "\n\t\tPending Max Seq ID: %"PRIu64 - "\n\t\tSent Min Seq ID: %"PRIu64 - "\n\t\tSent Max Seq ID: %"PRIu64 - "\n\t\tAcked Min Seq ID: %"PRIu64 - "\n\t\tAcked Max Seq ID: %"PRIu64, - stats->updates, - stats->batch_id, - stats->min_seqid, - stats->max_seqid, - stats->min_seqid_pend, - stats->max_seqid_pend, - stats->min_seqid_sent, - stats->max_seqid_sent, - stats->min_seqid_ack, - stats->max_seqid_ack - ); - freez(stats); -} - -char *ng_aclk_state(void) +char *aclk_state(void) { +#ifndef ENABLE_ACLK + return strdupz("ACLK Available: No"); +#else BUFFER *wb = buffer_create(1024); struct tm *tmptr, tmbuf; char *ret; @@ -935,7 +905,7 @@ char *ng_aclk_state(void) "ACLK Version: 2\n" "Protocols Supported: Protobuf\n" ); - buffer_sprintf(wb, "Protocol Used: Protobuf\nMQTT Version: %d\nClaimed: ", use_mqtt_5 ? 5 : 3); + buffer_sprintf(wb, "Protocol Used: Protobuf\nMQTT Version: %d\nClaimed: ", 5); char *agent_id = get_agent_claimid(); if (agent_id == NULL) @@ -974,7 +944,7 @@ char *ng_aclk_state(void) RRDHOST *host; rrd_rdlock(); rrdhost_foreach_read(host) { - buffer_sprintf(wb, "\n\n> Node Instance for mGUID: \"%s\" hostname \"%s\"\n", host->machine_guid, host->hostname); + buffer_sprintf(wb, "\n\n> Node Instance for mGUID: \"%s\" hostname \"%s\"\n", host->machine_guid, rrdhost_hostname(host)); buffer_strcat(wb, "\tClaimed ID: "); rrdhost_aclk_state_lock(host); @@ -1000,9 +970,6 @@ char *ng_aclk_state(void) buffer_strcat(wb, "\n\tAlert Streaming Status:"); fill_alert_status_for_host(wb, host); - - buffer_strcat(wb, "\n\tChart Streaming Status:"); - fill_chart_status_for_host(wb, host); } rrd_unlock(); } @@ -1010,8 +977,10 @@ char *ng_aclk_state(void) ret = strdupz(buffer_tostring(wb)); buffer_free(wb); return ret; +#endif /* ENABLE_ACLK */ } +#ifdef ENABLE_ACLK static void fill_alert_status_for_host_json(json_object *obj, RRDHOST *host) { struct proto_alert_status status; @@ -1038,45 +1007,6 @@ static void fill_alert_status_for_host_json(json_object *obj, RRDHOST *host) json_object_object_add(obj, "last-submitted-seq-id", tmp); } -static void fill_chart_status_for_host_json(json_object *obj, RRDHOST *host) -{ - struct aclk_chart_sync_stats *stats = aclk_get_chart_sync_stats(host); - if (!stats) - return; - - json_object *tmp = json_object_new_int(stats->updates); - json_object_object_add(obj, "updates", tmp); - - tmp = json_object_new_int(stats->batch_id); - json_object_object_add(obj, "batch-id", tmp); - - tmp = json_object_new_int(stats->min_seqid); - json_object_object_add(obj, "min-seq-id", tmp); - - tmp = json_object_new_int(stats->max_seqid); - json_object_object_add(obj, "max-seq-id", tmp); - - tmp = json_object_new_int(stats->min_seqid_pend); - json_object_object_add(obj, "pending-min-seq-id", tmp); - - tmp = json_object_new_int(stats->max_seqid_pend); - json_object_object_add(obj, "pending-max-seq-id", tmp); - - tmp = json_object_new_int(stats->min_seqid_sent); - json_object_object_add(obj, "sent-min-seq-id", tmp); - - tmp = json_object_new_int(stats->max_seqid_sent); - json_object_object_add(obj, "sent-max-seq-id", tmp); - - tmp = json_object_new_int(stats->min_seqid_ack); - json_object_object_add(obj, "acked-min-seq-id", tmp); - - tmp = json_object_new_int(stats->max_seqid_ack); - json_object_object_add(obj, "acked-max-seq-id", tmp); - - freez(stats); -} - static json_object *timestamp_to_json(const time_t *t) { struct tm *tmptr, tmbuf; @@ -1087,9 +1017,13 @@ static json_object *timestamp_to_json(const time_t *t) } return NULL; } +#endif /* ENABLE_ACLK */ -char *ng_aclk_state_json(void) +char *aclk_state_json(void) { +#ifndef ENABLE_ACLK + return strdupz("{\"aclk-available\":false}"); +#else json_object *tmp, *grp, *msg = json_object_new_object(); tmp = json_object_new_boolean(1); @@ -1124,7 +1058,7 @@ char *ng_aclk_state_json(void) tmp = json_object_new_string("Protobuf"); json_object_object_add(msg, "used-cloud-protocol", tmp); - tmp = json_object_new_int(use_mqtt_5 ? 5 : 3); + tmp = json_object_new_int(5); json_object_object_add(msg, "mqtt-version", tmp); tmp = json_object_new_int(aclk_rcvd_cloud_msgs); @@ -1155,7 +1089,7 @@ char *ng_aclk_state_json(void) rrdhost_foreach_read(host) { json_object *nodeinstance = json_object_new_object(); - tmp = json_object_new_string(host->hostname); + tmp = json_object_new_string(rrdhost_hostname(host)); json_object_object_add(nodeinstance, "hostname", tmp); tmp = json_object_new_string(host->machine_guid); @@ -1191,10 +1125,6 @@ char *ng_aclk_state_json(void) fill_alert_status_for_host_json(tmp, host); json_object_object_add(nodeinstance, "alert-sync-status", tmp); - tmp = json_object_new_object(); - fill_chart_status_for_host_json(tmp, host); - json_object_object_add(nodeinstance, "chart-sync-status", tmp); - json_object_array_add(grp, nodeinstance); } rrd_unlock(); @@ -1203,4 +1133,41 @@ char *ng_aclk_state_json(void) char *str = strdupz(json_object_to_json_string_ext(msg, JSON_C_TO_STRING_PLAIN)); json_object_put(msg); return str; +#endif /* ENABLE_ACLK */ +} + +void add_aclk_host_labels(void) { + DICTIONARY *labels = localhost->rrdlabels; + +#ifdef ENABLE_ACLK + rrdlabels_add(labels, "_aclk_available", "true", RRDLABEL_SRC_AUTO|RRDLABEL_SRC_ACLK); + ACLK_PROXY_TYPE aclk_proxy; + char *proxy_str; + aclk_get_proxy(&aclk_proxy); + + switch(aclk_proxy) { + case PROXY_TYPE_SOCKS5: + proxy_str = "SOCKS5"; + break; + case PROXY_TYPE_HTTP: + proxy_str = "HTTP"; + break; + default: + proxy_str = "none"; + break; + } + + rrdlabels_add(labels, "_mqtt_version", "5", RRDLABEL_SRC_AUTO); + rrdlabels_add(labels, "_aclk_proxy", proxy_str, RRDLABEL_SRC_AUTO); + rrdlabels_add(labels, "_aclk_ng_new_cloud_protocol", "true", RRDLABEL_SRC_AUTO|RRDLABEL_SRC_ACLK); +#else + rrdlabels_add(labels, "_aclk_available", "false", RRDLABEL_SRC_AUTO|RRDLABEL_SRC_ACLK); +#endif +} + +void aclk_queue_node_info(RRDHOST *host) { + struct aclk_database_worker_config *wc = (struct aclk_database_worker_config *) host->dbsync_worker; + if (likely(wc)) { + wc->node_info_send = 1; + } } diff --git a/aclk/aclk.h b/aclk/aclk.h index 5065ac2bf..6aed548b7 100644 --- a/aclk/aclk.h +++ b/aclk/aclk.h @@ -3,17 +3,30 @@ #define ACLK_H #include "daemon/common.h" + +#ifdef ENABLE_ACLK #include "aclk_util.h" #include "aclk_rrdhost_state.h" // How many MQTT PUBACKs we need to get to consider connection // stable for the purposes of TBEB (truncated binary exponential backoff) #define ACLK_PUBACKS_CONN_STABLE 3 +#endif /* ENABLE_ACLK */ + +extern int aclk_connected; +extern int aclk_ctx_based; +extern int aclk_disable_runtime; +extern int aclk_stats_enabled; +extern int aclk_kill_link; + +extern usec_t aclk_session_us; +extern time_t aclk_session_sec; extern time_t aclk_block_until; extern int disconnect_req; +#ifdef ENABLE_ACLK void *aclk_main(void *ptr); extern netdata_mutex_t aclk_shared_state_mutex; @@ -34,7 +47,11 @@ void aclk_send_node_instances(void); void aclk_send_bin_msg(char *msg, size_t msg_len, enum aclk_topics subtopic, const char *msgname); -char *ng_aclk_state(void); -char *ng_aclk_state_json(void); +#endif /* ENABLE_ACLK */ + +char *aclk_state(void); +char *aclk_state_json(void); +void add_aclk_host_labels(void); +void aclk_queue_node_info(RRDHOST *host); #endif /* ACLK_H */ diff --git a/aclk/aclk_alarm_api.c b/aclk/aclk_alarm_api.c index a181eb291..7df51a7b5 100644 --- a/aclk/aclk_alarm_api.c +++ b/aclk/aclk_alarm_api.c @@ -23,9 +23,6 @@ void aclk_send_alarm_log_entry(struct alarm_log_entry *log_entry) char *payload = generate_alarm_log_entry(&payload_size, log_entry); aclk_send_bin_msg(payload, payload_size, ACLK_TOPICID_ALARM_LOG, "AlarmLogEntry"); - - if (!use_mqtt_5) - freez(payload); } void aclk_send_provide_alarm_cfg(struct provide_alarm_configuration *cfg) diff --git a/aclk/aclk_api.c b/aclk/aclk_api.c deleted file mode 100644 index 141d267af..000000000 --- a/aclk/aclk_api.c +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#include "libnetdata/libnetdata.h" -#include "database/rrd.h" - -#ifdef ENABLE_ACLK -#include "aclk.h" -#endif - -int aclk_connected = 0; -int aclk_kill_link = 0; - -usec_t aclk_session_us = 0; -time_t aclk_session_sec = 0; - -int aclk_disable_runtime = 0; - -int aclk_stats_enabled; -int use_mqtt_5 = 0; -int aclk_ctx_based = 0; - -#define ACLK_IMPL_KEY_NAME "aclk implementation" - -#ifdef ENABLE_ACLK -void *aclk_starter(void *ptr) { - char *aclk_impl_req = config_get(CONFIG_SECTION_CLOUD, ACLK_IMPL_KEY_NAME, "ng"); - - if (!strcasecmp(aclk_impl_req, "ng")) { - return aclk_main(ptr); - } else if (!strcasecmp(aclk_impl_req, "legacy")) { - error("Legacy ACLK is not supported anymore key \"" ACLK_IMPL_KEY_NAME "\" in section \"" CONFIG_SECTION_CLOUD "\" ignored. Using ACLK-NG."); - } else { - error("Unknown value \"%s\" of key \"" ACLK_IMPL_KEY_NAME "\" in section \"" CONFIG_SECTION_CLOUD "\". Using ACLK-NG. This config key will be deprecated.", aclk_impl_req); - } - return aclk_main(ptr); -} -#endif /* ENABLE_ACLK */ - -void add_aclk_host_labels(void) { - DICTIONARY *labels = localhost->host_labels; - -#ifdef ENABLE_ACLK - rrdlabels_add(labels, "_aclk_ng_available", "true", RRDLABEL_SRC_AUTO|RRDLABEL_SRC_ACLK); -#else - rrdlabels_add(labels, "_aclk_ng_available", "false", RRDLABEL_SRC_AUTO|RRDLABEL_SRC_ACLK); -#endif - rrdlabels_add(labels, "_aclk_legacy_available", "false", RRDLABEL_SRC_AUTO|RRDLABEL_SRC_ACLK); -#ifdef ENABLE_ACLK - ACLK_PROXY_TYPE aclk_proxy; - char *proxy_str; - aclk_get_proxy(&aclk_proxy); - - switch(aclk_proxy) { - case PROXY_TYPE_SOCKS5: - proxy_str = "SOCKS5"; - break; - case PROXY_TYPE_HTTP: - proxy_str = "HTTP"; - break; - default: - proxy_str = "none"; - break; - } - - - int mqtt5 = config_get_boolean(CONFIG_SECTION_CLOUD, "mqtt5", CONFIG_BOOLEAN_YES); - - rrdlabels_add(labels, "_mqtt_version", mqtt5 ? "5" : "3", RRDLABEL_SRC_AUTO); - rrdlabels_add(labels, "_aclk_impl", "Next Generation", RRDLABEL_SRC_AUTO); - rrdlabels_add(labels, "_aclk_proxy", proxy_str, RRDLABEL_SRC_AUTO); - rrdlabels_add(labels, "_aclk_ng_new_cloud_protocol", "true", RRDLABEL_SRC_AUTO|RRDLABEL_SRC_ACLK); -#endif -} - -char *aclk_state(void) { -#ifndef ENABLE_ACLK - return strdupz("ACLK Available: No"); -#else - return ng_aclk_state(); -#endif -} - -char *aclk_state_json(void) { -#ifndef ENABLE_ACLK - return strdupz("{\"aclk-available\":false}"); -#else - return ng_aclk_state_json(); -#endif -} diff --git a/aclk/aclk_api.h b/aclk/aclk_api.h deleted file mode 100644 index 36a6d603f..000000000 --- a/aclk/aclk_api.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#ifndef ACLK_API_H -#define ACLK_API_H - -#include "libnetdata/libnetdata.h" - -#include "aclk_proxy.h" - -// TODO get rid global vars as soon as -// ACLK Legacy is removed -extern int aclk_connected; -extern int aclk_kill_link; - -extern usec_t aclk_session_us; -extern time_t aclk_session_sec; - -extern int aclk_disable_runtime; - -extern int aclk_stats_enabled; -extern int aclk_alert_reloaded; - -extern int use_mqtt_5; -extern int aclk_ctx_based; - -#ifdef ENABLE_ACLK -void *aclk_starter(void *ptr); - -void aclk_host_state_update(RRDHOST *host, int connect); - -#define NETDATA_ACLK_HOOK \ - { .name = "ACLK_Main", \ - .config_section = NULL, \ - .config_name = NULL, \ - .enabled = 1, \ - .thread = NULL, \ - .init_routine = NULL, \ - .start_routine = aclk_starter }, - -#endif - -void add_aclk_host_labels(void); -char *aclk_state(void); -char *aclk_state_json(void); - -#endif /* ACLK_API_H */ diff --git a/aclk/aclk_capas.c b/aclk/aclk_capas.c new file mode 100644 index 000000000..df9d18f63 --- /dev/null +++ b/aclk/aclk_capas.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "aclk_capas.h" + +#include "ml/ml.h" + +const struct capability *aclk_get_agent_capas() +{ + static struct capability agent_capabilities[] = { + { .name = "json", .version = 2, .enabled = 0 }, + { .name = "proto", .version = 1, .enabled = 1 }, + { .name = "ml", .version = 0, .enabled = 0 }, + { .name = "mc", .version = 0, .enabled = 0 }, + { .name = "ctx", .version = 1, .enabled = 1 }, + { .name = "funcs", .version = 1, .enabled = 1 }, + { .name = NULL, .version = 0, .enabled = 0 } + }; + agent_capabilities[2].version = ml_capable() ? 1 : 0; + agent_capabilities[2].enabled = ml_enabled(localhost); + + agent_capabilities[3].version = enable_metric_correlations ? metric_correlations_version : 0; + agent_capabilities[3].enabled = enable_metric_correlations; + + return agent_capabilities; +} + +struct capability *aclk_get_node_instance_capas(RRDHOST *host) +{ + struct capability ni_caps[] = { + { .name = "proto", .version = 1, .enabled = 1 }, + { .name = "ml", .version = ml_capable(), .enabled = ml_enabled(host) }, + { .name = "mc", + .version = enable_metric_correlations ? metric_correlations_version : 0, + .enabled = enable_metric_correlations }, + { .name = "ctx", .version = 1, .enabled = 1 }, + { .name = "funcs", .version = 0, .enabled = 0 }, + { .name = NULL, .version = 0, .enabled = 0 } + }; + if (host->receiver && stream_has_capability(host->receiver, STREAM_CAP_FUNCTIONS)) { + ni_caps[4].version = 1; + ni_caps[4].enabled = 1; + } + + struct capability *ret = mallocz(sizeof(ni_caps)); + memcpy(ret, ni_caps, sizeof(ni_caps)); + return ret; +} diff --git a/aclk/aclk_capas.h b/aclk/aclk_capas.h new file mode 100644 index 000000000..c39a197b8 --- /dev/null +++ b/aclk/aclk_capas.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef ACLK_CAPAS_H +#define ACLK_CAPAS_H + +#include "daemon/common.h" +#include "libnetdata/libnetdata.h" + +#include "schema-wrappers/capability.h" + +const struct capability *aclk_get_agent_capas(); +struct capability *aclk_get_node_instance_capas(RRDHOST *host); + +#endif /* ACLK_CAPAS_H */ diff --git a/aclk/aclk_charts_api.c b/aclk/aclk_charts_api.c deleted file mode 100644 index 51d8dad58..000000000 --- a/aclk/aclk_charts_api.c +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#include "aclk_charts_api.h" - -#include "aclk_query_queue.h" - -#define CHART_DIM_UPDATE_NAME "ChartsAndDimensionsUpdated" - -void aclk_chart_inst_update(char **payloads, size_t *payload_sizes, struct aclk_message_position *new_positions) -{ - aclk_query_t query = aclk_query_new(CHART_DIMS_UPDATE); - query->data.bin_payload.payload = generate_charts_updated(&query->data.bin_payload.size, payloads, payload_sizes, new_positions); - query->data.bin_payload.msg_name = CHART_DIM_UPDATE_NAME; - QUEUE_IF_PAYLOAD_PRESENT(query); -} - -void aclk_chart_dim_update(char **payloads, size_t *payload_sizes, struct aclk_message_position *new_positions) -{ - aclk_query_t query = aclk_query_new(CHART_DIMS_UPDATE); - query->data.bin_payload.topic = ACLK_TOPICID_CHART_DIMS; - query->data.bin_payload.payload = generate_chart_dimensions_updated(&query->data.bin_payload.size, payloads, payload_sizes, new_positions); - query->data.bin_payload.msg_name = CHART_DIM_UPDATE_NAME; - QUEUE_IF_PAYLOAD_PRESENT(query); -} - -void aclk_chart_inst_and_dim_update(char **payloads, size_t *payload_sizes, int *is_dim, struct aclk_message_position *new_positions, uint64_t batch_id) -{ - aclk_query_t query = aclk_query_new(CHART_DIMS_UPDATE); - query->data.bin_payload.topic = ACLK_TOPICID_CHART_DIMS; - query->data.bin_payload.payload = generate_charts_and_dimensions_updated(&query->data.bin_payload.size, payloads, payload_sizes, is_dim, new_positions, batch_id); - query->data.bin_payload.msg_name = CHART_DIM_UPDATE_NAME; - QUEUE_IF_PAYLOAD_PRESENT(query); -} - -void aclk_chart_config_updated(struct chart_config_updated *config_list, int list_size) -{ - aclk_query_t query = aclk_query_new(CHART_CONFIG_UPDATED); - query->data.bin_payload.topic = ACLK_TOPICID_CHART_CONFIGS_UPDATED; - query->data.bin_payload.payload = generate_chart_configs_updated(&query->data.bin_payload.size, config_list, list_size); - query->data.bin_payload.msg_name = "ChartConfigsUpdated"; - QUEUE_IF_PAYLOAD_PRESENT(query); -} - -void aclk_chart_reset(chart_reset_t reset) -{ - aclk_query_t query = aclk_query_new(CHART_RESET); - query->data.bin_payload.topic = ACLK_TOPICID_CHART_RESET; - query->data.bin_payload.payload = generate_reset_chart_messages(&query->data.bin_payload.size, reset); - query->data.bin_payload.msg_name = "ResetChartMessages"; - QUEUE_IF_PAYLOAD_PRESENT(query); -} - -void aclk_retention_updated(struct retention_updated *data) -{ - aclk_query_t query = aclk_query_new(RETENTION_UPDATED); - query->data.bin_payload.topic = ACLK_TOPICID_RETENTION_UPDATED; - query->data.bin_payload.payload = generate_retention_updated(&query->data.bin_payload.size, data); - query->data.bin_payload.msg_name = "RetentionUpdated"; - QUEUE_IF_PAYLOAD_PRESENT(query); -} - -void aclk_update_node_info(struct update_node_info *info) -{ - aclk_query_t query = aclk_query_new(UPDATE_NODE_INFO); - query->data.bin_payload.topic = ACLK_TOPICID_NODE_INFO; - query->data.bin_payload.payload = generate_update_node_info_message(&query->data.bin_payload.size, info); - query->data.bin_payload.msg_name = "UpdateNodeInfo"; - QUEUE_IF_PAYLOAD_PRESENT(query); -} - -void aclk_update_node_collectors(struct update_node_collectors *collectors) -{ - aclk_query_t query = aclk_query_new(UPDATE_NODE_COLLECTORS); - query->data.bin_payload.topic = ACLK_TOPICID_NODE_COLLECTORS; - query->data.bin_payload.payload = generate_update_node_collectors_message(&query->data.bin_payload.size, collectors); - query->data.bin_payload.msg_name = "UpdateNodeCollectors"; - QUEUE_IF_PAYLOAD_PRESENT(query); -} diff --git a/aclk/aclk_charts_api.h b/aclk/aclk_charts_api.h deleted file mode 100644 index 71f07dd33..000000000 --- a/aclk/aclk_charts_api.h +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#ifndef ACLK_CHARTS_H -#define ACLK_CHARTS_H - -#include "../daemon/common.h" -#include "schema-wrappers/schema_wrappers.h" - -void aclk_chart_inst_update(char **payloads, size_t *payload_sizes, struct aclk_message_position *new_positions); -void aclk_chart_dim_update(char **payloads, size_t *payload_sizes, struct aclk_message_position *new_positions); -void aclk_chart_inst_and_dim_update(char **payloads, size_t *payload_sizes, int *is_dim, struct aclk_message_position *new_positions, uint64_t batch_id); - -void aclk_chart_config_updated(struct chart_config_updated *config_list, int list_size); - -void aclk_chart_reset(chart_reset_t reset); - -void aclk_retention_updated(struct retention_updated *data); - -void aclk_update_node_info(struct update_node_info *info); - -void aclk_update_node_collectors(struct update_node_collectors *collectors); - -#endif /* ACLK_CHARTS_H */ diff --git a/aclk/aclk_contexts_api.c b/aclk/aclk_contexts_api.c index f17d3cabd..f3344935e 100644 --- a/aclk/aclk_contexts_api.c +++ b/aclk/aclk_contexts_api.c @@ -21,3 +21,21 @@ void aclk_send_contexts_updated(contexts_updated_t data) query->data.bin_payload.msg_name = "ContextsUpdated"; QUEUE_IF_PAYLOAD_PRESENT(query); } + +void aclk_update_node_collectors(struct update_node_collectors *collectors) +{ + aclk_query_t query = aclk_query_new(UPDATE_NODE_COLLECTORS); + query->data.bin_payload.topic = ACLK_TOPICID_NODE_COLLECTORS; + query->data.bin_payload.payload = generate_update_node_collectors_message(&query->data.bin_payload.size, collectors); + query->data.bin_payload.msg_name = "UpdateNodeCollectors"; + QUEUE_IF_PAYLOAD_PRESENT(query); +} + +void aclk_update_node_info(struct update_node_info *info) +{ + aclk_query_t query = aclk_query_new(UPDATE_NODE_INFO); + query->data.bin_payload.topic = ACLK_TOPICID_NODE_INFO; + query->data.bin_payload.payload = generate_update_node_info_message(&query->data.bin_payload.size, info); + query->data.bin_payload.msg_name = "UpdateNodeInfo"; + QUEUE_IF_PAYLOAD_PRESENT(query); +} diff --git a/aclk/aclk_contexts_api.h b/aclk/aclk_contexts_api.h index 46b916d22..f0b5ec77e 100644 --- a/aclk/aclk_contexts_api.h +++ b/aclk/aclk_contexts_api.h @@ -7,6 +7,8 @@ void aclk_send_contexts_snapshot(contexts_snapshot_t data); void aclk_send_contexts_updated(contexts_updated_t data); +void aclk_update_node_collectors(struct update_node_collectors *collectors); +void aclk_update_node_info(struct update_node_info *info); #endif /* ACLK_CONTEXTS_API_H */ diff --git a/aclk/aclk_otp.c b/aclk/aclk_otp.c index b7bf173c4..2bdbb70fb 100644 --- a/aclk/aclk_otp.c +++ b/aclk/aclk_otp.c @@ -13,7 +13,7 @@ static int aclk_https_request(https_req_t *request, https_req_response_t *respon int rc; // wrapper for ACLK only which loads ACLK specific proxy settings // then only calls https_request - struct mqtt_wss_proxy proxy_conf = { .host = NULL, .port = 0, .type = MQTT_WSS_DIRECT }; + struct mqtt_wss_proxy proxy_conf = { .host = NULL, .port = 0, .username = NULL, .password = NULL, .type = MQTT_WSS_DIRECT }; aclk_set_proxy((char**)&proxy_conf.host, &proxy_conf.port, &proxy_conf.type); if (proxy_conf.type == MQTT_WSS_PROXY_HTTP) { @@ -380,7 +380,7 @@ int aclk_get_otp_challenge(url_t *target, const char *agent_id, unsigned char ** base64_decode_helper(*challenge, challenge_bytes, (const unsigned char*)challenge_base64, strlen(challenge_base64)); if (*challenge_bytes != CHALLENGE_LEN) { error("Unexpected challenge length of %d instead of %d", *challenge_bytes, CHALLENGE_LEN); - freez(challenge); + freez(*challenge); *challenge = NULL; goto cleanup_json; } @@ -490,7 +490,7 @@ int aclk_get_mqtt_otp(EVP_PKEY *p_key, char **mqtt_id, char **mqtt_usr, char **m int aclk_get_mqtt_otp(RSA *p_key, char **mqtt_id, char **mqtt_usr, char **mqtt_pass, url_t *target) #endif { - unsigned char *challenge; + unsigned char *challenge = NULL; int challenge_bytes; char *agent_id = get_agent_claimid(); @@ -844,10 +844,7 @@ int aclk_get_env(aclk_env_t *env, const char* aclk_hostname, int aclk_port) { return 1; } - if (rrdcontext_enabled) - buffer_sprintf(buf, "/api/v1/env?v=%s&cap=proto,ctx&claim_id=%s", &(VERSION[1]) /* skip 'v' at beginning */, agent_id); - else - buffer_sprintf(buf, "/api/v1/env?v=%s&cap=proto&claim_id=%s", &(VERSION[1]) /* skip 'v' at beginning */, agent_id); + buffer_sprintf(buf, "/api/v1/env?v=%s&cap=proto,ctx&claim_id=%s", &(VERSION[1]) /* skip 'v' at beginning */, agent_id); freez(agent_id); diff --git a/aclk/aclk_query.c b/aclk/aclk_query.c index 981c01965..5301c281f 100644 --- a/aclk/aclk_query.c +++ b/aclk/aclk_query.c @@ -38,9 +38,7 @@ static RRDHOST *node_id_2_rrdhost(const char *node_id) int res; uuid_t node_id_bin, host_id_bin; - rrd_rdlock(); - RRDHOST *host = find_host_by_node_id((char *) node_id); - rrd_unlock(); + RRDHOST *host = find_host_by_node_id((char *)node_id); if (host) return host; @@ -54,7 +52,7 @@ static RRDHOST *node_id_2_rrdhost(const char *node_id) return NULL; } uuid_unparse_lower(host_id_bin, host_id); - return rrdhost_find_by_guid(host_id, 0); + return rrdhost_find_by_guid(host_id); } #define NODE_ID_QUERY "/node/" @@ -82,7 +80,7 @@ static int http_api_v2(struct aclk_query_thread *query_thr, aclk_query_t query) strcpy(w->origin, "*"); // Simulate web_client_create_on_fd() w->cookie1[0] = 0; // Simulate web_client_create_on_fd() w->cookie2[0] = 0; // Simulate web_client_create_on_fd() - w->acl = 0x1f; + w->acl = WEB_CLIENT_ACL_ACLK; buffer_strcat(log_buffer, query->data.http_api_v2.query); size_t size = 0; @@ -101,7 +99,6 @@ static int http_api_v2(struct aclk_query_thread *query_thr, aclk_query_t query) } } - RRDHOST *temp_host = NULL; if (!strncmp(query->data.http_api_v2.query, NODE_ID_QUERY, strlen(NODE_ID_QUERY))) { char *node_uuid = query->data.http_api_v2.query + strlen(NODE_ID_QUERY); char nodeid[UUID_STR_LEN]; @@ -116,14 +113,11 @@ static int http_api_v2(struct aclk_query_thread *query_thr, aclk_query_t query) query_host = node_id_2_rrdhost(nodeid); if (!query_host) { - temp_host = sql_create_host_by_uuid(nodeid); - if (!temp_host) { - error_report("Host with node_id \"%s\" not found! Returning 404 to Cloud!", nodeid); - retval = 1; - w->response.code = 404; - aclk_http_msg_v2_err(query_thr->client, query->callback_topic, query->msg_id, w->response.code, CLOUD_EC_NODE_NOT_FOUND, CLOUD_EMSG_NODE_NOT_FOUND, NULL, 0); - goto cleanup; - } + error_report("Host with node_id \"%s\" not found! Returning 404 to Cloud!", nodeid); + retval = 1; + w->response.code = 404; + aclk_http_msg_v2_err(query_thr->client, query->callback_topic, query->msg_id, w->response.code, CLOUD_EC_NODE_NOT_FOUND, CLOUD_EMSG_NODE_NOT_FOUND, NULL, 0); + goto cleanup; } } @@ -144,8 +138,7 @@ static int http_api_v2(struct aclk_query_thread *query_thr, aclk_query_t query) } // execute the query - t = aclk_web_api_v1_request(query_host ? query_host : temp_host, w, mysep ? mysep + 1 : "noop"); - free_temporary_host(temp_host); + t = aclk_web_api_v1_request(query_host, w, mysep ? mysep + 1 : "noop"); size = (w->mode == WEB_CLIENT_MODE_FILECOPY) ? w->response.rlen : w->response.data->len; sent = size; @@ -263,7 +256,7 @@ static int send_bin_msg(struct aclk_query_thread *query_thr, aclk_query_t query) return 0; } -const char *aclk_query_get_name(aclk_query_type_t qt) +const char *aclk_query_get_name(aclk_query_type_t qt, int unknown_ok) { switch (qt) { case HTTP_API_V2: return "http_api_request_v2"; @@ -280,7 +273,8 @@ const char *aclk_query_get_name(aclk_query_type_t qt) case UPDATE_NODE_COLLECTORS: return "update_node_collectors"; case PROTO_BIN_MESSAGE: return "generic_binary_proto_message"; default: - error_report("Unknown query type used %d", (int) qt); + if (!unknown_ok) + error_report("Unknown query type used %d", (int) qt); return "unknown"; } } @@ -329,7 +323,7 @@ int aclk_query_process_msgs(struct aclk_query_thread *query_thr) static void worker_aclk_register(void) { worker_register("ACLKQUERY"); for (int i = 1; i < ACLK_QUERY_TYPE_COUNT; i++) { - worker_register_job_name(i, aclk_query_get_name(i)); + worker_register_job_name(i, aclk_query_get_name(i, 0)); } } diff --git a/aclk/aclk_query.h b/aclk/aclk_query.h index f86754a2a..c006b0138 100644 --- a/aclk/aclk_query.h +++ b/aclk/aclk_query.h @@ -31,6 +31,6 @@ struct aclk_query_threads { void aclk_query_threads_start(struct aclk_query_threads *query_threads, mqtt_wss_client client); void aclk_query_threads_cleanup(struct aclk_query_threads *query_threads); -const char *aclk_query_get_name(aclk_query_type_t qt); +const char *aclk_query_get_name(aclk_query_type_t qt, int unknown_ok); #endif //NETDATA_AGENT_CLOUD_LINK_H diff --git a/aclk/aclk_query_queue.c b/aclk/aclk_query_queue.c index 01b20d23f..9a450571e 100644 --- a/aclk/aclk_query_queue.c +++ b/aclk/aclk_query_queue.c @@ -111,22 +111,6 @@ void aclk_query_free(aclk_query_t query) freez(query->data.http_api_v2.query); break; - case NODE_STATE_UPDATE: - case REGISTER_NODE: - case CHART_DIMS_UPDATE: - case CHART_CONFIG_UPDATED: - case CHART_RESET: - case RETENTION_UPDATED: - case UPDATE_NODE_INFO: - case ALARM_LOG_HEALTH: - case ALARM_PROVIDE_CFG: - case ALARM_SNAPSHOT: - case UPDATE_NODE_COLLECTORS: - case PROTO_BIN_MESSAGE: - if (!use_mqtt_5) - freez(query->data.bin_payload.payload); - break; - default: break; } diff --git a/aclk/aclk_rx_msgs.c b/aclk/aclk_rx_msgs.c index e6ed332cc..83bc5508b 100644 --- a/aclk/aclk_rx_msgs.c +++ b/aclk/aclk_rx_msgs.c @@ -5,6 +5,7 @@ #include "aclk_stats.h" #include "aclk_query_queue.h" #include "aclk.h" +#include "aclk_capas.h" #include "schema-wrappers/proto_2_json.h" @@ -274,7 +275,7 @@ int create_node_instance_result(const char *msg, size_t msg_len) .node_id = res.node_id }; - RRDHOST *host = rrdhost_find_by_guid(res.machine_guid, 0); + RRDHOST *host = rrdhost_find_by_guid(res.machine_guid); if (host) { // not all host must have RRDHOST struct created for them // if they never connected during runtime of agent @@ -289,20 +290,15 @@ int create_node_instance_result(const char *msg, size_t msg_len) } } - struct capability caps[] = { - { .name = "proto", .version = 1, .enabled = 1 }, - { .name = "ml", .version = ml_capable(localhost), .enabled = host ? ml_enabled(host) : 0 }, - { .name = "mc", .version = enable_metric_correlations ? metric_correlations_version : 0, .enabled = enable_metric_correlations }, - { .name = "ctx", .version = 1, .enabled = rrdcontext_enabled }, - { .name = NULL, .version = 0, .enabled = 0 } - }; - node_state_update.capabilities = caps; + node_state_update.capabilities = aclk_get_node_instance_capas(host); rrdhost_aclk_state_lock(localhost); node_state_update.claim_id = localhost->aclk_state.claimed_id; query->data.bin_payload.payload = generate_node_instance_connection(&query->data.bin_payload.size, &node_state_update); rrdhost_aclk_state_unlock(localhost); + freez((void *)node_state_update.capabilities); + query->data.bin_payload.msg_name = "UpdateNodeInstanceConnection"; query->data.bin_payload.topic = ACLK_TOPICID_NODE_CONN; @@ -322,44 +318,25 @@ int send_node_instances(const char *msg, size_t msg_len) int stream_charts_and_dimensions(const char *msg, size_t msg_len) { - aclk_ctx_based = 0; - stream_charts_and_dims_t res = parse_stream_charts_and_dims(msg, msg_len); - if (!res.claim_id || !res.node_id) { - error("Error parsing StreamChartsAndDimensions msg"); - freez(res.claim_id); - freez(res.node_id); - return 1; - } - chart_batch_id = res.batch_id; - aclk_start_streaming(res.node_id, res.seq_id, res.seq_id_created_at.tv_sec, res.batch_id); - freez(res.claim_id); - freez(res.node_id); + UNUSED(msg); + UNUSED(msg_len); + error_report("Received obsolete StreamChartsAndDimensions msg"); return 0; } int charts_and_dimensions_ack(const char *msg, size_t msg_len) { - chart_and_dim_ack_t res = parse_chart_and_dimensions_ack(msg, msg_len); - if (!res.claim_id || !res.node_id) { - error("Error parsing StreamChartsAndDimensions msg"); - freez(res.claim_id); - freez(res.node_id); - return 1; - } - aclk_ack_chart_sequence_id(res.node_id, res.last_seq_id); - freez(res.claim_id); - freez(res.node_id); + UNUSED(msg); + UNUSED(msg_len); + error_report("Received obsolete StreamChartsAndDimensionsAck msg"); return 0; } int update_chart_configs(const char *msg, size_t msg_len) { - struct update_chart_config res = parse_update_chart_config(msg, msg_len); - if (!res.claim_id || !res.node_id || !res.hashes) - error("Error parsing UpdateChartConfigs msg"); - else - aclk_get_chart_config(res.hashes); - destroy_update_chart_config(&res); + UNUSED(msg); + UNUSED(msg_len); + error_report("Received obsolete UpdateChartConfigs msg"); return 0; } @@ -527,7 +504,7 @@ unsigned int aclk_init_rx_msg_handlers(void) return i; } -void aclk_handle_new_cloud_msg(const char *message_type, const char *msg, size_t msg_len, const char *topic) +void aclk_handle_new_cloud_msg(const char *message_type, const char *msg, size_t msg_len, const char *topic __maybe_unused) { if (aclk_stats_enabled) { ACLK_STATS_LOCK; @@ -546,15 +523,16 @@ void aclk_handle_new_cloud_msg(const char *message_type, const char *msg, size_t return; } -#ifdef NETDATA_INTERNAL_CHECKS - if (!strncmp(message_type, "cmd", strlen("cmd"))) { - log_aclk_message_bin(msg, msg_len, 0, topic, msg_descriptor->name); - } else { - char *json = protomsg_to_json(msg, msg_len, msg_descriptor->name); - log_aclk_message_bin(json, strlen(json), 0, topic, msg_descriptor->name); - freez(json); + + if (aclklog_enabled) { + if (!strncmp(message_type, "cmd", strlen("cmd"))) { + log_aclk_message_bin(msg, msg_len, 0, topic, msg_descriptor->name); + } else { + char *json = protomsg_to_json(msg, msg_len, msg_descriptor->name); + log_aclk_message_bin(json, strlen(json), 0, topic, msg_descriptor->name); + freez(json); + } } -#endif if (aclk_stats_enabled) { ACLK_STATS_LOCK; diff --git a/aclk/aclk_stats.c b/aclk/aclk_stats.c index 241e9b724..215313ff9 100644 --- a/aclk/aclk_stats.c +++ b/aclk/aclk_stats.c @@ -39,8 +39,7 @@ static void aclk_stats_collect(struct aclk_metrics_per_sample *per_sample, struc "connected", "netdata", "stats", 200000, localhost->rrd_update_every, RRDSET_TYPE_LINE); rd_online_status = rrddim_add(st_aclkstats, "online", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_aclkstats); + } rrddim_set_by_pointer(st_aclkstats, rd_online_status, per_sample->offline_during_sample ? 0 : permanent->online); @@ -60,8 +59,7 @@ static void aclk_stats_query_queue(struct aclk_metrics_per_sample *per_sample) rd_queued = rrddim_add(st_query_thread, "added", NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); rd_dispatched = rrddim_add(st_query_thread, "dispatched", NULL, -1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_query_thread); + } rrddim_set_by_pointer(st_query_thread, rd_queued, per_sample->queries_queued); rrddim_set_by_pointer(st_query_thread, rd_dispatched, per_sample->queries_dispatched); @@ -83,8 +81,8 @@ static void aclk_stats_latency(struct aclk_metrics_per_sample *per_sample) rd_avg = rrddim_add(st, "avg", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_max = rrddim_add(st, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st); + } + if(per_sample->latency_count) rrddim_set_by_pointer(st, rd_avg, roundf((float)per_sample->latency_total / per_sample->latency_count)); else @@ -109,8 +107,7 @@ static void aclk_stats_cloud_req(struct aclk_metrics_per_sample *per_sample) rd_rq_rcvd = rrddim_add(st, "received", NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); rd_rq_err = rrddim_add(st, "malformed", NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_rq_rcvd, per_sample->cloud_req_recvd - per_sample->cloud_req_err); rrddim_set_by_pointer(st, rd_rq_err, per_sample->cloud_req_err); @@ -129,10 +126,9 @@ static void aclk_stats_cloud_req_type(struct aclk_metrics_per_sample *per_sample "netdata", "stats", 200006, localhost->rrd_update_every, RRDSET_TYPE_STACKED); for (int i = 0; i < ACLK_QUERY_TYPE_COUNT; i++) - dims[i] = rrddim_add(st, aclk_query_get_name(i), NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); + dims[i] = rrddim_add(st, aclk_query_get_name(i, 1), NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st); + } for (int i = 0; i < ACLK_QUERY_TYPE_COUNT; i++) rrddim_set_by_pointer(st, dims[i], per_sample->queries_per_type[i]); @@ -171,8 +167,7 @@ static void aclk_stats_cloud_req_http_type(struct aclk_metrics_per_sample *per_s for (int i = 0; i < ACLK_STATS_CLOUD_HTTP_REQ_TYPE_CNT; i++) rd_rq_types[i] = rrddim_add(st, cloud_req_http_type_names[i], NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st); + } for (int i = 0; i < ACLK_STATS_CLOUD_HTTP_REQ_TYPE_CNT; i++) rrddim_set_by_pointer(st, rd_rq_types[i], per_sample->cloud_req_http_by_type[i]); @@ -197,8 +192,7 @@ static void aclk_stats_query_threads(uint32_t *queries_per_thread) error("snprintf encoding error"); aclk_qt_data[i].dim = rrddim_add(st, dim_name, NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); } - } else - rrdset_next(st); + } for (int i = 0; i < aclk_stats_cfg.query_thread_count; i++) { rrddim_set_by_pointer(st, aclk_qt_data[i].dim, queries_per_thread[i]); @@ -222,8 +216,7 @@ static void aclk_stats_query_time(struct aclk_metrics_per_sample *per_sample) rd_rq_avg = rrddim_add(st, "avg", NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); rd_rq_max = rrddim_add(st, "max", NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); rd_rq_total = rrddim_add(st, "total", NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st); + } if(per_sample->cloud_q_process_count) rrddim_set_by_pointer(st, rd_rq_avg, roundf((float)per_sample->cloud_q_process_total / per_sample->cloud_q_process_count)); @@ -248,8 +241,7 @@ static void aclk_stats_newproto_rx(uint32_t *rx_msgs_sample) for (unsigned int i = 0; i < aclk_stats_cfg.proto_hdl_cnt; i++) { aclk_stats_cfg.rx_msg_dims[i] = rrddim_add(st, rx_handler_get_name(i), NULL, 1, localhost->rrd_update_every, RRD_ALGORITHM_ABSOLUTE); } - } else - rrdset_next(st); + } for (unsigned int i = 0; i < aclk_stats_cfg.proto_hdl_cnt; i++) rrddim_set_by_pointer(st, aclk_stats_cfg.rx_msg_dims[i], rx_msgs_sample[i]); @@ -275,8 +267,7 @@ static void aclk_stats_mqtt_wss(struct mqtt_wss_stats *stats) rd_sent = rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_recvd = rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_sent, sent); rrddim_set_by_pointer(st, rd_recvd, recvd); diff --git a/aclk/aclk_tx_msgs.c b/aclk/aclk_tx_msgs.c index 822a90fa2..532b964ad 100644 --- a/aclk/aclk_tx_msgs.c +++ b/aclk/aclk_tx_msgs.c @@ -5,6 +5,7 @@ #include "aclk_util.h" #include "aclk_stats.h" #include "aclk.h" +#include "aclk_capas.h" #include "schema-wrappers/proto_2_json.h" @@ -15,6 +16,13 @@ // version for aclk legacy (old cloud arch) #define ACLK_VERSION 2 +static void freez_aclk_publish5a(void *ptr) { + freez(ptr); +} +static void freez_aclk_publish5b(void *ptr) { + freez(ptr); +} + uint16_t aclk_send_bin_message_subtopic_pid(mqtt_wss_client client, char *msg, size_t msg_len, enum aclk_topics subtopic, const char *msgname) { #ifndef ACLK_LOG_CONVERSATION_DIR @@ -28,43 +36,27 @@ uint16_t aclk_send_bin_message_subtopic_pid(mqtt_wss_client client, char *msg, s return 0; } - if (use_mqtt_5) - mqtt_wss_publish5(client, (char*)topic, NULL, msg, &freez, msg_len, MQTT_WSS_PUB_QOS1, &packet_id); - else - mqtt_wss_publish_pid(client, topic, msg, msg_len, MQTT_WSS_PUB_QOS1, &packet_id); + mqtt_wss_publish5(client, (char*)topic, NULL, msg, &freez_aclk_publish5a, msg_len, MQTT_WSS_PUB_QOS1, &packet_id); #ifdef NETDATA_INTERNAL_CHECKS aclk_stats_msg_published(packet_id); - char *json = protomsg_to_json(msg, msg_len, msgname); - log_aclk_message_bin(json, strlen(json), 1, topic, msgname); - freez(json); #endif + if (aclklog_enabled) { + char *json = protomsg_to_json(msg, msg_len, msgname); + log_aclk_message_bin(json, strlen(json), 1, topic, msgname); + freez(json); + } + return packet_id; } -/* UNUSED now but can be used soon MVP1? -static void aclk_send_message_topic(mqtt_wss_client client, json_object *msg, const char *topic) +// json_object_put returns int unfortunately :D +// we need void(*fnc)(void *); +static void json_object_put_wrapper(void *jsonobj) { - if (unlikely(!topic || topic[0] != '/')) { - error ("Full topic required!"); - return; - } - - const char *str = json_object_to_json_string_ext(msg, JSON_C_TO_STRING_PLAIN); - - mqtt_wss_publish(client, topic, str, strlen(str), MQTT_WSS_PUB_QOS1); -#ifdef NETDATA_INTERNAL_CHECKS - aclk_stats_msg_published(); -#endif -#ifdef ACLK_LOG_CONVERSATION_DIR -#define FN_MAX_LEN 1024 - char filename[FN_MAX_LEN]; - snprintf(filename, FN_MAX_LEN, ACLK_LOG_CONVERSATION_DIR "/%010d-tx.json", ACLK_GET_CONV_LOG_NEXT()); - json_object_to_file_ext(filename, msg, JSON_C_TO_STRING_PRETTY); -#endif + json_object_put(jsonobj); } -*/ #define TOPIC_MAX_LEN 512 #define V2_BIN_PAYLOAD_SEPARATOR "\x0D\x0A\x0D\x0A" @@ -73,10 +65,11 @@ static int aclk_send_message_with_bin_payload(mqtt_wss_client client, json_objec uint16_t packet_id; const char *str; char *full_msg = NULL; - int len, rc; + int len; if (unlikely(!topic || topic[0] != '/')) { error ("Full topic required!"); + json_object_put(msg); return HTTP_RESP_INTERNAL_SERVER_ERROR; } @@ -87,40 +80,20 @@ static int aclk_send_message_with_bin_payload(mqtt_wss_client client, json_objec full_msg = mallocz(len + strlen(V2_BIN_PAYLOAD_SEPARATOR) + payload_len); memcpy(full_msg, str, len); + json_object_put(msg); + msg = NULL; memcpy(&full_msg[len], V2_BIN_PAYLOAD_SEPARATOR, strlen(V2_BIN_PAYLOAD_SEPARATOR)); len += strlen(V2_BIN_PAYLOAD_SEPARATOR); memcpy(&full_msg[len], payload, payload_len); len += payload_len; } -/* TODO -#ifdef ACLK_LOG_CONVERSATION_DIR -#define FN_MAX_LEN 1024 - char filename[FN_MAX_LEN]; - snprintf(filename, FN_MAX_LEN, ACLK_LOG_CONVERSATION_DIR "/%010d-tx.json", ACLK_GET_CONV_LOG_NEXT()); - json_object_to_file_ext(filename, msg, JSON_C_TO_STRING_PRETTY); -#endif */ - - if (use_mqtt_5) - mqtt_wss_publish5(client, (char*)topic, NULL, (char*)(payload_len ? full_msg : str), NULL, len, MQTT_WSS_PUB_QOS1, &packet_id); - else { - rc = mqtt_wss_publish_pid_block(client, topic, payload_len ? full_msg : str, len, MQTT_WSS_PUB_QOS1, &packet_id, 5000); - if (rc == MQTT_WSS_ERR_BLOCK_TIMEOUT) { - error("Timeout sending binpacked message"); - freez(full_msg); - return HTTP_RESP_BACKEND_FETCH_FAILED; - } - if (rc == MQTT_WSS_ERR_TX_BUF_TOO_SMALL) { - error("Message is bigger than allowed maximum"); - freez(full_msg); - return HTTP_RESP_FORBIDDEN; - } - } + mqtt_wss_publish5(client, (char*)topic, NULL, (char*)(payload_len ? full_msg : str), (payload_len ? &freez_aclk_publish5b : &json_object_put_wrapper), len, MQTT_WSS_PUB_QOS1, &packet_id); #ifdef NETDATA_INTERNAL_CHECKS aclk_stats_msg_published(packet_id); #endif - freez(full_msg); + return 0; } @@ -203,7 +176,6 @@ void aclk_http_msg_v2_err(mqtt_wss_client client, const char *topic, const char if (aclk_send_message_with_bin_payload(client, msg, topic, payload, payload_len)) { error("Failed to send cancelation message for http reply"); } - json_object_put(msg); } void aclk_http_msg_v2(mqtt_wss_client client, const char *topic, const char *msg_id, usec_t t_exec, usec_t created, int http_code, const char *payload, size_t payload_len) @@ -222,7 +194,6 @@ void aclk_http_msg_v2(mqtt_wss_client client, const char *topic, const char *msg json_object_object_add(msg, "http-code", tmp); int rc = aclk_send_message_with_bin_payload(client, msg, topic, payload, payload_len); - json_object_put(msg); switch (rc) { case HTTP_RESP_FORBIDDEN: @@ -241,22 +212,11 @@ uint16_t aclk_send_agent_connection_update(mqtt_wss_client client, int reachable size_t len; uint16_t pid; - struct capability agent_capabilities[] = { - { .name = "json", .version = 2, .enabled = 0 }, - { .name = "proto", .version = 1, .enabled = 1 }, -#ifdef ENABLE_ML - { .name = "ml", .version = 1, .enabled = ml_enabled(localhost) }, -#endif - { .name = "mc", .version = enable_metric_correlations ? metric_correlations_version : 0, .enabled = enable_metric_correlations }, - { .name = "ctx", .version = 1, .enabled = rrdcontext_enabled }, - { .name = NULL, .version = 0, .enabled = 0 } - }; - update_agent_connection_t conn = { .reachable = (reachable ? 1 : 0), .lwt = 0, .session_id = aclk_session_newarch, - .capabilities = agent_capabilities + .capabilities = aclk_get_agent_capas() }; rrdhost_aclk_state_lock(localhost); @@ -279,8 +239,6 @@ uint16_t aclk_send_agent_connection_update(mqtt_wss_client client, int reachable } pid = aclk_send_bin_message_subtopic_pid(client, msg, len, ACLK_TOPICID_AGENT_CONN, "UpdateAgentConnection"); - if (!use_mqtt_5) - freez(msg); if (localhost->aclk_state.prev_claimed_id) { freez(localhost->aclk_state.prev_claimed_id); localhost->aclk_state.prev_claimed_id = NULL; diff --git a/aclk/aclk_util.c b/aclk/aclk_util.c index ec021aec5..01eaedc8e 100644 --- a/aclk/aclk_util.c +++ b/aclk/aclk_util.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "aclk_util.h" +#include "aclk_proxy.h" #include "daemon/common.h" diff --git a/aclk/helpers/mqtt_wss_pal.h b/aclk/helpers/mqtt_wss_pal.h new file mode 100644 index 000000000..5c89f8bb7 --- /dev/null +++ b/aclk/helpers/mqtt_wss_pal.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef MQTT_WSS_PAL_H +#define MQTT_WSS_PAL_H + +#include "libnetdata/libnetdata.h" + +#undef OPENSSL_VERSION_095 +#undef OPENSSL_VERSION_097 +#undef OPENSSL_VERSION_110 +#undef OPENSSL_VERSION_111 + +#define mw_malloc(...) mallocz(__VA_ARGS__) +#define mw_calloc(...) callocz(__VA_ARGS__) +#define mw_free(...) freez(__VA_ARGS__) +#define mw_strdup(...) strdupz(__VA_ARGS__) +#define mw_realloc(...) reallocz(__VA_ARGS__) + +#endif /* MQTT_WSS_PAL_H */ diff --git a/aclk/helpers/ringbuffer_pal.h b/aclk/helpers/ringbuffer_pal.h new file mode 100644 index 000000000..2f7e1cb93 --- /dev/null +++ b/aclk/helpers/ringbuffer_pal.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RINGBUFFER_PAL_H +#define RINGBUFFER_PAL_H + +#include "libnetdata/libnetdata.h" + +#define crbuf_malloc(...) mallocz(__VA_ARGS__) +#define crbuf_free(...) freez(__VA_ARGS__) + +#endif /* RINGBUFFER_PAL_H */ diff --git a/aclk/schema-wrappers/capability.cc b/aclk/schema-wrappers/capability.cc index 769806f90..af45740a9 100644 --- a/aclk/schema-wrappers/capability.cc +++ b/aclk/schema-wrappers/capability.cc @@ -4,7 +4,7 @@ #include "capability.h" -void capability_set(aclk_lib::v1::Capability *proto_capa, struct capability *c_capa) { +void capability_set(aclk_lib::v1::Capability *proto_capa, const struct capability *c_capa) { proto_capa->set_name(c_capa->name); proto_capa->set_enabled(c_capa->enabled); proto_capa->set_version(c_capa->version); diff --git a/aclk/schema-wrappers/capability.h b/aclk/schema-wrappers/capability.h index 9517a8716..c6085a44b 100644 --- a/aclk/schema-wrappers/capability.h +++ b/aclk/schema-wrappers/capability.h @@ -18,7 +18,7 @@ struct capability { #include "proto/aclk/v1/lib.pb.h" -void capability_set(aclk_lib::v1::Capability *proto_capa, struct capability *c_capa); +void capability_set(aclk_lib::v1::Capability *proto_capa, const struct capability *c_capa); #endif #endif /* ACLK_SCHEMA_CAPABILITY_H */ diff --git a/aclk/schema-wrappers/chart_config.cc b/aclk/schema-wrappers/chart_config.cc deleted file mode 100644 index 87e34e0df..000000000 --- a/aclk/schema-wrappers/chart_config.cc +++ /dev/null @@ -1,105 +0,0 @@ -#include "chart_config.h" - -#include "proto/chart/v1/config.pb.h" - -#include "libnetdata/libnetdata.h" - -#include "schema_wrapper_utils.h" - -void destroy_update_chart_config(struct update_chart_config *cfg) -{ - freez(cfg->claim_id); - freez(cfg->node_id); - freez(cfg->hashes); -} - -void destroy_chart_config_updated(struct chart_config_updated *cfg) -{ - freez(cfg->type); - freez(cfg->family); - freez(cfg->context); - freez(cfg->title); - freez(cfg->plugin); - freez(cfg->module); - freez(cfg->units); - freez(cfg->config_hash); -} - -struct update_chart_config parse_update_chart_config(const char *data, size_t len) -{ - chart::v1::UpdateChartConfigs cfgs; - update_chart_config res; - memset(&res, 0, sizeof(res)); - - if (!cfgs.ParseFromArray(data, len)) - return res; - - res.claim_id = strdupz(cfgs.claim_id().c_str()); - res.node_id = strdupz(cfgs.node_id().c_str()); - - // to not do bazillion tiny allocations for individual strings - // we calculate how much memory we will need for all of them - // and allocate at once - int hash_count = cfgs.config_hashes_size(); - size_t total_strlen = 0; - for (int i = 0; i < hash_count; i++) - total_strlen += cfgs.config_hashes(i).length(); - total_strlen += hash_count; //null bytes - - res.hashes = (char**)callocz( 1, - (hash_count+1) * sizeof(char*) + //char * array incl. terminating NULL at the end - total_strlen //strings themselves incl. 1 null byte each - ); - - char* dest = ((char*)res.hashes) + (hash_count + 1 /* NULL ptr */) * sizeof(char *); - // now copy them strings - // null bytes handled by callocz - for (int i = 0; i < hash_count; i++) { - strcpy(dest, cfgs.config_hashes(i).c_str()); - res.hashes[i] = dest; - dest += strlen(dest) + 1 /* end string null */; - } - - return res; -} - -char *generate_chart_configs_updated(size_t *len, const struct chart_config_updated *config_list, int list_size) -{ - chart::v1::ChartConfigsUpdated configs; - for (int i = 0; i < list_size; i++) { - chart::v1::ChartConfigUpdated *config = configs.add_configs(); - config->set_type(config_list[i].type); - if (config_list[i].family) - config->set_family(config_list[i].family); - config->set_context(config_list[i].context); - config->set_title(config_list[i].title); - config->set_priority(config_list[i].priority); - config->set_plugin(config_list[i].plugin); - - if (config_list[i].module) - config->set_module(config_list[i].module); - - switch (config_list[i].chart_type) { - case RRDSET_TYPE_LINE: - config->set_chart_type(chart::v1::LINE); - break; - case RRDSET_TYPE_AREA: - config->set_chart_type(chart::v1::AREA); - break; - case RRDSET_TYPE_STACKED: - config->set_chart_type(chart::v1::STACKED); - break; - default: - return NULL; - } - - config->set_units(config_list[i].units); - config->set_config_hash(config_list[i].config_hash); - } - - *len = PROTO_COMPAT_MSG_SIZE(configs); - char *bin = (char*)mallocz(*len); - configs.SerializeToArray(bin, *len); - - return bin; -} diff --git a/aclk/schema-wrappers/chart_config.h b/aclk/schema-wrappers/chart_config.h deleted file mode 100644 index f08f76b61..000000000 --- a/aclk/schema-wrappers/chart_config.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef ACLK_SCHEMA_WRAPPER_CHART_CONFIG_H -#define ACLK_SCHEMA_WRAPPER_CHART_CONFIG_H - -#include - -#include "database/rrd.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct update_chart_config { - char *claim_id; - char *node_id; - char **hashes; -}; - -enum chart_config_chart_type { - LINE, - AREA, - STACKED -}; - -struct chart_config_updated { - char *type; - char *family; - char *context; - char *title; - uint64_t priority; - char *plugin; - char *module; - RRDSET_TYPE chart_type; - char *units; - char *config_hash; -}; - -void destroy_update_chart_config(struct update_chart_config *cfg); -void destroy_chart_config_updated(struct chart_config_updated *cfg); - -struct update_chart_config parse_update_chart_config(const char *data, size_t len); - -char *generate_chart_configs_updated(size_t *len, const struct chart_config_updated *config_list, int list_size); - -#ifdef __cplusplus -} -#endif - -#endif /* ACLK_SCHEMA_WRAPPER_CHART_CONFIG_H */ diff --git a/aclk/schema-wrappers/chart_stream.cc b/aclk/schema-wrappers/chart_stream.cc deleted file mode 100644 index 54c940758..000000000 --- a/aclk/schema-wrappers/chart_stream.cc +++ /dev/null @@ -1,337 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "aclk/aclk_util.h" - -#include "proto/chart/v1/stream.pb.h" -#include "chart_stream.h" - -#include "schema_wrapper_utils.h" - -#include -#include - -stream_charts_and_dims_t parse_stream_charts_and_dims(const char *data, size_t len) -{ - chart::v1::StreamChartsAndDimensions msg; - stream_charts_and_dims_t res; - memset(&res, 0, sizeof(res)); - - if (!msg.ParseFromArray(data, len)) - return res; - - res.node_id = strdup(msg.node_id().c_str()); - res.claim_id = strdup(msg.claim_id().c_str()); - res.seq_id = msg.sequence_id(); - res.batch_id = msg.batch_id(); - set_timeval_from_google_timestamp(msg.seq_id_created_at(), &res.seq_id_created_at); - - return res; -} - -chart_and_dim_ack_t parse_chart_and_dimensions_ack(const char *data, size_t len) -{ - chart::v1::ChartsAndDimensionsAck msg; - chart_and_dim_ack_t res = { .claim_id = NULL, .node_id = NULL, .last_seq_id = 0 }; - - if (!msg.ParseFromArray(data, len)) - return res; - - res.node_id = strdup(msg.node_id().c_str()); - res.claim_id = strdup(msg.claim_id().c_str()); - res.last_seq_id = msg.last_sequence_id(); - - return res; -} - -char *generate_reset_chart_messages(size_t *len, chart_reset_t reset) -{ - chart::v1::ResetChartMessages msg; - - msg.set_claim_id(reset.claim_id); - msg.set_node_id(reset.node_id); - switch (reset.reason) { - case DB_EMPTY: - msg.set_reason(chart::v1::ResetReason::DB_EMPTY); - break; - case SEQ_ID_NOT_EXISTS: - msg.set_reason(chart::v1::ResetReason::SEQ_ID_NOT_EXISTS); - break; - case TIMESTAMP_MISMATCH: - msg.set_reason(chart::v1::ResetReason::TIMESTAMP_MISMATCH); - break; - default: - return NULL; - } - - *len = PROTO_COMPAT_MSG_SIZE(msg); - char *bin = (char*)malloc(*len); - if (bin) - msg.SerializeToArray(bin, *len); - - return bin; -} - -void chart_instance_updated_destroy(struct chart_instance_updated *instance) -{ - freez((char*)instance->id); - freez((char*)instance->claim_id); - - rrdlabels_destroy(instance->chart_labels); - - freez((char*)instance->config_hash); -} - -static int set_chart_instance_updated(chart::v1::ChartInstanceUpdated *chart, const struct chart_instance_updated *update) -{ - google::protobuf::Map *map; - aclk_lib::v1::ACLKMessagePosition *pos; - - chart->set_id(update->id); - chart->set_claim_id(update->claim_id); - chart->set_node_id(update->node_id); - chart->set_name(update->name); - - map = chart->mutable_chart_labels(); - rrdlabels_walkthrough_read(update->chart_labels, label_add_to_map_callback, map); - - switch (update->memory_mode) { - case RRD_MEMORY_MODE_NONE: - chart->set_memory_mode(chart::v1::NONE); - break; - case RRD_MEMORY_MODE_RAM: - chart->set_memory_mode(chart::v1::RAM); - break; - case RRD_MEMORY_MODE_MAP: - chart->set_memory_mode(chart::v1::MAP); - break; - case RRD_MEMORY_MODE_SAVE: - chart->set_memory_mode(chart::v1::SAVE); - break; - case RRD_MEMORY_MODE_ALLOC: - chart->set_memory_mode(chart::v1::ALLOC); - break; - case RRD_MEMORY_MODE_DBENGINE: - chart->set_memory_mode(chart::v1::DB_ENGINE); - break; - default: - return 1; - break; - } - - chart->set_update_every_interval(update->update_every); - chart->set_config_hash(update->config_hash); - - pos = chart->mutable_position(); - pos->set_sequence_id(update->position.sequence_id); - pos->set_previous_sequence_id(update->position.previous_sequence_id); - set_google_timestamp_from_timeval(update->position.seq_id_creation_time, pos->mutable_seq_id_created_at()); - - return 0; -} - -static int set_chart_dim_updated(chart::v1::ChartDimensionUpdated *dim, const struct chart_dimension_updated *c_dim) -{ - aclk_lib::v1::ACLKMessagePosition *pos; - - dim->set_id(c_dim->id); - dim->set_chart_id(c_dim->chart_id); - dim->set_node_id(c_dim->node_id); - dim->set_claim_id(c_dim->claim_id); - dim->set_name(c_dim->name); - - set_google_timestamp_from_timeval(c_dim->created_at, dim->mutable_created_at()); - set_google_timestamp_from_timeval(c_dim->last_timestamp, dim->mutable_last_timestamp()); - - pos = dim->mutable_position(); - pos->set_sequence_id(c_dim->position.sequence_id); - pos->set_previous_sequence_id(c_dim->position.previous_sequence_id); - set_google_timestamp_from_timeval(c_dim->position.seq_id_creation_time, pos->mutable_seq_id_created_at()); - - return 0; -} - -char *generate_charts_and_dimensions_updated(size_t *len, char **payloads, size_t *payload_sizes, int *is_dim, struct aclk_message_position *new_positions, uint64_t batch_id) -{ - chart::v1::ChartsAndDimensionsUpdated msg; - chart::v1::ChartInstanceUpdated db_chart; - chart::v1::ChartDimensionUpdated db_dim; - aclk_lib::v1::ACLKMessagePosition *pos; - - msg.set_batch_id(batch_id); - - for (int i = 0; payloads[i]; i++) { - if (is_dim[i]) { - if (!db_dim.ParseFromArray(payloads[i], payload_sizes[i])) { - error("[ACLK] Could not parse chart::v1::chart_dimension_updated"); - return NULL; - } - - pos = db_dim.mutable_position(); - pos->set_sequence_id(new_positions[i].sequence_id); - pos->set_previous_sequence_id(new_positions[i].previous_sequence_id); - set_google_timestamp_from_timeval(new_positions[i].seq_id_creation_time, pos->mutable_seq_id_created_at()); - - chart::v1::ChartDimensionUpdated *dim = msg.add_dimensions(); - *dim = db_dim; - } else { - if (!db_chart.ParseFromArray(payloads[i], payload_sizes[i])) { - error("[ACLK] Could not parse chart::v1::ChartInstanceUpdated"); - return NULL; - } - - pos = db_chart.mutable_position(); - pos->set_sequence_id(new_positions[i].sequence_id); - pos->set_previous_sequence_id(new_positions[i].previous_sequence_id); - set_google_timestamp_from_timeval(new_positions[i].seq_id_creation_time, pos->mutable_seq_id_created_at()); - - chart::v1::ChartInstanceUpdated *chart = msg.add_charts(); - *chart = db_chart; - } - } - - *len = PROTO_COMPAT_MSG_SIZE(msg); - char *bin = (char*)mallocz(*len); - msg.SerializeToArray(bin, *len); - - return bin; -} - -char *generate_charts_updated(size_t *len, char **payloads, size_t *payload_sizes, struct aclk_message_position *new_positions) -{ - chart::v1::ChartsAndDimensionsUpdated msg; - - msg.set_batch_id(chart_batch_id); - - for (int i = 0; payloads[i]; i++) { - chart::v1::ChartInstanceUpdated db_msg; - chart::v1::ChartInstanceUpdated *chart; - aclk_lib::v1::ACLKMessagePosition *pos; - - if (!db_msg.ParseFromArray(payloads[i], payload_sizes[i])) { - error("[ACLK] Could not parse chart::v1::ChartInstanceUpdated"); - return NULL; - } - - pos = db_msg.mutable_position(); - pos->set_sequence_id(new_positions[i].sequence_id); - pos->set_previous_sequence_id(new_positions[i].previous_sequence_id); - set_google_timestamp_from_timeval(new_positions[i].seq_id_creation_time, pos->mutable_seq_id_created_at()); - - chart = msg.add_charts(); - *chart = db_msg; - } - - *len = PROTO_COMPAT_MSG_SIZE(msg); - char *bin = (char*)mallocz(*len); - msg.SerializeToArray(bin, *len); - - return bin; -} - -char *generate_chart_dimensions_updated(size_t *len, char **payloads, size_t *payload_sizes, struct aclk_message_position *new_positions) -{ - chart::v1::ChartsAndDimensionsUpdated msg; - - msg.set_batch_id(chart_batch_id); - - for (int i = 0; payloads[i]; i++) { - chart::v1::ChartDimensionUpdated db_msg; - chart::v1::ChartDimensionUpdated *dim; - aclk_lib::v1::ACLKMessagePosition *pos; - - if (!db_msg.ParseFromArray(payloads[i], payload_sizes[i])) { - error("[ACLK] Could not parse chart::v1::chart_dimension_updated"); - return NULL; - } - - pos = db_msg.mutable_position(); - pos->set_sequence_id(new_positions[i].sequence_id); - pos->set_previous_sequence_id(new_positions[i].previous_sequence_id); - set_google_timestamp_from_timeval(new_positions[i].seq_id_creation_time, pos->mutable_seq_id_created_at()); - - dim = msg.add_dimensions(); - *dim = db_msg; - } - - *len = PROTO_COMPAT_MSG_SIZE(msg); - char *bin = (char*)mallocz(*len); - msg.SerializeToArray(bin, *len); - - return bin; -} - -char *generate_chart_instance_updated(size_t *len, const struct chart_instance_updated *update) -{ - chart::v1::ChartInstanceUpdated *chart = new chart::v1::ChartInstanceUpdated(); - - if (set_chart_instance_updated(chart, update)) - return NULL; - - *len = PROTO_COMPAT_MSG_SIZE_PTR(chart); - char *bin = (char*)mallocz(*len); - chart->SerializeToArray(bin, *len); - - delete chart; - return bin; -} - -char *generate_chart_dimension_updated(size_t *len, const struct chart_dimension_updated *dim) -{ - chart::v1::ChartDimensionUpdated *proto_dim = new chart::v1::ChartDimensionUpdated(); - - if (set_chart_dim_updated(proto_dim, dim)) - return NULL; - - *len = PROTO_COMPAT_MSG_SIZE_PTR(proto_dim); - char *bin = (char*)mallocz(*len); - proto_dim->SerializeToArray(bin, *len); - - delete proto_dim; - return bin; -} - -using namespace google::protobuf; - -char *generate_retention_updated(size_t *len, struct retention_updated *data) -{ - chart::v1::RetentionUpdated msg; - - msg.set_claim_id(data->claim_id); - msg.set_node_id(data->node_id); - - switch (data->memory_mode) { - case RRD_MEMORY_MODE_NONE: - msg.set_memory_mode(chart::v1::NONE); - break; - case RRD_MEMORY_MODE_RAM: - msg.set_memory_mode(chart::v1::RAM); - break; - case RRD_MEMORY_MODE_MAP: - msg.set_memory_mode(chart::v1::MAP); - break; - case RRD_MEMORY_MODE_SAVE: - msg.set_memory_mode(chart::v1::SAVE); - break; - case RRD_MEMORY_MODE_ALLOC: - msg.set_memory_mode(chart::v1::ALLOC); - break; - case RRD_MEMORY_MODE_DBENGINE: - msg.set_memory_mode(chart::v1::DB_ENGINE); - break; - default: - return NULL; - } - - for (int i = 0; i < data->interval_duration_count; i++) { - Map *map = msg.mutable_interval_durations(); - map->insert({data->interval_durations[i].update_every, data->interval_durations[i].retention}); - } - - set_google_timestamp_from_timeval(data->rotation_timestamp, msg.mutable_rotation_timestamp()); - - *len = PROTO_COMPAT_MSG_SIZE(msg); - char *bin = (char*)mallocz(*len); - msg.SerializeToArray(bin, *len); - - return bin; -} diff --git a/aclk/schema-wrappers/chart_stream.h b/aclk/schema-wrappers/chart_stream.h deleted file mode 100644 index 904866868..000000000 --- a/aclk/schema-wrappers/chart_stream.h +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef ACLK_SCHEMA_WRAPPER_CHART_STREAM_H -#define ACLK_SCHEMA_WRAPPER_CHART_STREAM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "database/rrd.h" - -typedef struct { - char* claim_id; - char* node_id; - - uint64_t seq_id; - uint64_t batch_id; - - struct timeval seq_id_created_at; -} stream_charts_and_dims_t; - -stream_charts_and_dims_t parse_stream_charts_and_dims(const char *data, size_t len); - -typedef struct { - char* claim_id; - char* node_id; - - uint64_t last_seq_id; -} chart_and_dim_ack_t; - -chart_and_dim_ack_t parse_chart_and_dimensions_ack(const char *data, size_t len); - -enum chart_reset_reason { - DB_EMPTY, - SEQ_ID_NOT_EXISTS, - TIMESTAMP_MISMATCH -}; - -typedef struct { - char *claim_id; - char *node_id; - - enum chart_reset_reason reason; -} chart_reset_t; - -char *generate_reset_chart_messages(size_t *len, const chart_reset_t reset); - -struct aclk_message_position { - uint64_t sequence_id; - struct timeval seq_id_creation_time; - uint64_t previous_sequence_id; -}; - -struct chart_instance_updated { - const char *id; - const char *claim_id; - const char *node_id; - const char *name; - - DICTIONARY *chart_labels; - - RRD_MEMORY_MODE memory_mode; - - uint32_t update_every; - const char * config_hash; - - struct aclk_message_position position; -}; - -void chart_instance_updated_destroy(struct chart_instance_updated *instance); - -struct chart_dimension_updated { - const char *id; - const char *chart_id; - const char *node_id; - const char *claim_id; - const char *name; - struct timeval created_at; - struct timeval last_timestamp; - struct aclk_message_position position; -}; - -typedef struct { - struct chart_instance_updated *charts; - uint16_t chart_count; - - struct chart_dimension_updated *dims; - uint16_t dim_count; - - uint64_t batch_id; -} charts_and_dims_updated_t; - -struct interval_duration { - uint32_t update_every; - uint32_t retention; -}; - -struct retention_updated { - char *claim_id; - char *node_id; - - RRD_MEMORY_MODE memory_mode; - - struct interval_duration *interval_durations; - int interval_duration_count; - - struct timeval rotation_timestamp; -}; - -char *generate_charts_and_dimensions_updated(size_t *len, char **payloads, size_t *payload_sizes, int *is_dim, struct aclk_message_position *new_positions, uint64_t batch_id); -char *generate_charts_updated(size_t *len, char **payloads, size_t *payload_sizes, struct aclk_message_position *new_positions); -char *generate_chart_instance_updated(size_t *len, const struct chart_instance_updated *update); -char *generate_chart_dimensions_updated(size_t *len, char **payloads, size_t *payload_sizes, struct aclk_message_position *new_positions); -char *generate_chart_dimension_updated(size_t *len, const struct chart_dimension_updated *dim); -char *generate_retention_updated(size_t *len, struct retention_updated *data); - -#ifdef __cplusplus -} -#endif - -#endif /* ACLK_SCHEMA_WRAPPER_CHART_STREAM_H */ diff --git a/aclk/schema-wrappers/connection.cc b/aclk/schema-wrappers/connection.cc index 7520a4600..20b40ece2 100644 --- a/aclk/schema-wrappers/connection.cc +++ b/aclk/schema-wrappers/connection.cc @@ -29,7 +29,7 @@ char *generate_update_agent_connection(size_t *len, const update_agent_connectio timestamp->set_nanos(tv.tv_usec * 1000); if (data->capabilities) { - struct capability *capa = data->capabilities; + const struct capability *capa = data->capabilities; while (capa->name) { aclk_lib::v1::Capability *proto_capa = connupd.add_capabilities(); capability_set(proto_capa, capa); @@ -38,7 +38,7 @@ char *generate_update_agent_connection(size_t *len, const update_agent_connectio } *len = PROTO_COMPAT_MSG_SIZE(connupd); - char *msg = (char*)malloc(*len); + char *msg = (char*)mallocz(*len); if (msg) connupd.SerializeToArray(msg, *len); @@ -52,7 +52,7 @@ struct disconnect_cmd *parse_disconnect_cmd(const char *data, size_t len) { if (!req.ParseFromArray(data, len)) return NULL; - res = (struct disconnect_cmd *)calloc(1, sizeof(struct disconnect_cmd)); + res = (struct disconnect_cmd *)callocz(1, sizeof(struct disconnect_cmd)); if (!res) return NULL; @@ -61,9 +61,9 @@ struct disconnect_cmd *parse_disconnect_cmd(const char *data, size_t len) { res->permaban = req.permaban(); res->error_code = req.error_code(); if (req.error_description().c_str()) { - res->error_description = strdup(req.error_description().c_str()); + res->error_description = strdupz(req.error_description().c_str()); if (!res->error_description) { - free(res); + freez(res); return NULL; } } diff --git a/aclk/schema-wrappers/connection.h b/aclk/schema-wrappers/connection.h index fcbe6bd59..0356c7d78 100644 --- a/aclk/schema-wrappers/connection.h +++ b/aclk/schema-wrappers/connection.h @@ -17,7 +17,7 @@ typedef struct { unsigned int lwt:1; - struct capability *capabilities; + const struct capability *capabilities; // TODO in future optional fields // > 15 optional fields: diff --git a/aclk/schema-wrappers/node_connection.cc b/aclk/schema-wrappers/node_connection.cc index a6ca8ef98..db1fa6449 100644 --- a/aclk/schema-wrappers/node_connection.cc +++ b/aclk/schema-wrappers/node_connection.cc @@ -29,7 +29,7 @@ char *generate_node_instance_connection(size_t *len, const node_instance_connect timestamp->set_nanos(tv.tv_usec * 1000); if (data->capabilities) { - struct capability *capa = data->capabilities; + const struct capability *capa = data->capabilities; while (capa->name) { aclk_lib::v1::Capability *proto_capa = msg.add_capabilities(); capability_set(proto_capa, capa); @@ -38,7 +38,7 @@ char *generate_node_instance_connection(size_t *len, const node_instance_connect } *len = PROTO_COMPAT_MSG_SIZE(msg); - char *bin = (char*)malloc(*len); + char *bin = (char*)mallocz(*len); if (bin) msg.SerializeToArray(bin, *len); diff --git a/aclk/schema-wrappers/node_connection.h b/aclk/schema-wrappers/node_connection.h index c27729d15..dac0d8fe0 100644 --- a/aclk/schema-wrappers/node_connection.h +++ b/aclk/schema-wrappers/node_connection.h @@ -19,7 +19,7 @@ typedef struct { int64_t session_id; int32_t hops; - struct capability *capabilities; + const struct capability *capabilities; } node_instance_connection_t; char *generate_node_instance_connection(size_t *len, const node_instance_connection_t *data); diff --git a/aclk/schema-wrappers/node_creation.cc b/aclk/schema-wrappers/node_creation.cc index c696bb27b..5ad25b7e5 100644 --- a/aclk/schema-wrappers/node_creation.cc +++ b/aclk/schema-wrappers/node_creation.cc @@ -18,7 +18,7 @@ char *generate_node_instance_creation(size_t *len, const node_instance_creation_ msg.set_hops(data->hops); *len = PROTO_COMPAT_MSG_SIZE(msg); - char *bin = (char*)malloc(*len); + char *bin = (char*)mallocz(*len); if (bin) msg.SerializeToArray(bin, *len); @@ -33,7 +33,7 @@ node_instance_creation_result_t parse_create_node_instance_result(const char *da if (!msg.ParseFromArray(data, len)) return res; - res.node_id = strdup(msg.node_id().c_str()); - res.machine_guid = strdup(msg.machine_guid().c_str()); + res.node_id = strdupz(msg.node_id().c_str()); + res.machine_guid = strdupz(msg.machine_guid().c_str()); return res; } diff --git a/aclk/schema-wrappers/node_creation.h b/aclk/schema-wrappers/node_creation.h index 190ccb4d6..7a8c7f7c7 100644 --- a/aclk/schema-wrappers/node_creation.h +++ b/aclk/schema-wrappers/node_creation.h @@ -8,9 +8,9 @@ extern "C" { #endif typedef struct { - char* claim_id; - char* machine_guid; - char* hostname; + const char *claim_id; + const char *machine_guid; + const char *hostname; int32_t hops; } node_instance_creation_t; diff --git a/aclk/schema-wrappers/node_info.cc b/aclk/schema-wrappers/node_info.cc index 2a05ddaba..5e321f688 100644 --- a/aclk/schema-wrappers/node_info.cc +++ b/aclk/schema-wrappers/node_info.cc @@ -104,7 +104,7 @@ char *generate_update_node_info_message(size_t *len, struct update_node_info *in } *len = PROTO_COMPAT_MSG_SIZE(msg); - char *bin = (char*)malloc(*len); + char *bin = (char*)mallocz(*len); if (bin) msg.SerializeToArray(bin, *len); @@ -128,7 +128,7 @@ char *generate_update_node_collectors_message(size_t *len, struct update_node_co dfe_done(colls); *len = PROTO_COMPAT_MSG_SIZE(msg); - char *bin = (char*)malloc(*len); + char *bin = (char*)mallocz(*len); if (bin) msg.SerializeToArray(bin, *len); diff --git a/aclk/schema-wrappers/node_info.h b/aclk/schema-wrappers/node_info.h index e8ac2d7c6..de4ade78a 100644 --- a/aclk/schema-wrappers/node_info.h +++ b/aclk/schema-wrappers/node_info.h @@ -19,41 +19,27 @@ struct machine_learning_info { }; struct aclk_node_info { - char *name; - - char *os; - char *os_name; - char *os_version; - - char *kernel_name; - char *kernel_version; - - char *architecture; - + const char *name; + + const char *os; + const char *os_name; + const char *os_version; + const char *kernel_name; + const char *kernel_version; + const char *architecture; uint32_t cpus; - - char *cpu_frequency; - - char *memory; - - char *disk_space; - - char *version; - - char *release_channel; - - char *timezone; - - char *virtualization_type; - - char *container_type; - - char *custom_info; - - char *machine_guid; + const char *cpu_frequency; + const char *memory; + const char *disk_space; + const char *version; + const char *release_channel; + const char *timezone; + const char *virtualization_type; + const char *container_type; + const char *custom_info; + const char *machine_guid; DICTIONARY *host_labels_ptr; - struct machine_learning_info ml_info; }; @@ -72,8 +58,8 @@ struct update_node_info { }; struct collector_info { - char *module; - char *plugin; + const char *module; + const char *plugin; }; struct update_node_collectors { diff --git a/aclk/schema-wrappers/proto_2_json.cc b/aclk/schema-wrappers/proto_2_json.cc index 0e473eb6c..8853b2e08 100644 --- a/aclk/schema-wrappers/proto_2_json.cc +++ b/aclk/schema-wrappers/proto_2_json.cc @@ -4,8 +4,6 @@ #include "proto/alarm/v1/config.pb.h" #include "proto/alarm/v1/stream.pb.h" #include "proto/aclk/v1/lib.pb.h" -#include "proto/chart/v1/config.pb.h" -#include "proto/chart/v1/stream.pb.h" #include "proto/agent/v1/connection.pb.h" #include "proto/agent/v1/disconnect.pb.h" #include "proto/nodeinstance/connection/v1/connection.pb.h" @@ -29,14 +27,6 @@ static google::protobuf::Message *msg_name_to_protomsg(const char *msgname) return new nodeinstance::v1::UpdateNodeInstanceConnection; if (!strcmp(msgname, "CreateNodeInstance")) return new nodeinstance::create::v1::CreateNodeInstance; - if (!strcmp(msgname, "ChartsAndDimensionsUpdated")) - return new chart::v1::ChartsAndDimensionsUpdated; - if (!strcmp(msgname, "ChartConfigsUpdated")) - return new chart::v1::ChartConfigsUpdated; - if (!strcmp(msgname, "ResetChartMessages")) - return new chart::v1::ResetChartMessages; - if (!strcmp(msgname, "RetentionUpdated")) - return new chart::v1::RetentionUpdated; if (!strcmp(msgname, "UpdateNodeInfo")) return new nodeinstance::info::v1::UpdateNodeInfo; if (!strcmp(msgname, "AlarmLogHealth")) @@ -59,12 +49,6 @@ static google::protobuf::Message *msg_name_to_protomsg(const char *msgname) return new nodeinstance::create::v1::CreateNodeInstanceResult; if (!strcmp(msgname, "SendNodeInstances")) return new agent::v1::SendNodeInstances; - if (!strcmp(msgname, "StreamChartsAndDimensions")) - return new chart::v1::StreamChartsAndDimensions; - if (!strcmp(msgname, "ChartsAndDimensionsAck")) - return new chart::v1::ChartsAndDimensionsAck; - if (!strcmp(msgname, "UpdateChartConfigs")) - return new chart::v1::UpdateChartConfigs; if (!strcmp(msgname, "StartAlarmStreaming")) return new alarms::v1::StartAlarmStreaming; if (!strcmp(msgname, "SendAlarmLogHealth")) diff --git a/aclk/schema-wrappers/schema_wrappers.h b/aclk/schema-wrappers/schema_wrappers.h index 26412cacc..a96f7ea7a 100644 --- a/aclk/schema-wrappers/schema_wrappers.h +++ b/aclk/schema-wrappers/schema_wrappers.h @@ -8,8 +8,6 @@ #include "connection.h" #include "node_connection.h" #include "node_creation.h" -#include "chart_config.h" -#include "chart_stream.h" #include "alarm_config.h" #include "alarm_stream.h" #include "node_info.h" diff --git a/build/subst.inc b/build/subst.inc index cc8825e24..3c972114b 100644 --- a/build/subst.inc +++ b/build/subst.inc @@ -10,8 +10,10 @@ -e 's#[@]registrydir_POST@#$(registrydir)#g' \ -e 's#[@]varlibdir_POST@#$(varlibdir)#g' \ -e 's#[@]webdir_POST@#$(webdir)#g' \ + -e 's#[@]libsysdir_POST@#$(libsysdir)#g' \ -e 's#[@]enable_aclk_POST@#$(enable_aclk)#g' \ -e 's#[@]enable_cloud_POST@#$(enable_cloud)#g' \ + -e 's#[@]netdata_user_POST@#$(netdata_user)#g' \ $< > $@.tmp; then \ mv "$@.tmp" "$@"; \ else \ diff --git a/claim/README.md b/claim/README.md index 9202645ed..3731d2004 100644 --- a/claim/README.md +++ b/claim/README.md @@ -73,7 +73,7 @@ When coming from [Nodes view page](https://learn.netdata.cloud/docs/cloud/visua If you want to connect a node that is running on a Linux environment, the script that will be provided to you by Netdata Cloud is the [kickstart](/packaging/installer/README.md#automatic-one-line-installation-script) which will install the Netdata Agent on your node, if it isn't already installed, and connect the node to Netdata Cloud. It should be similar to: ``` -wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && sh /tmp/netdata-kickstart.sh --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://app.netdata.cloud +wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && sh /tmp/netdata-kickstart.sh --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://api.netdata.cloud ``` The script should return `Agent was successfully claimed.`. If the connecting to Netdata Cloud process returns errors, or if you don't see the node in your Space after 60 seconds, see the [troubleshooting information](#troubleshooting). @@ -101,7 +101,7 @@ The default user is `netdata`. Yours may be different, so pay attention to the o and run the script. ```bash -wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && sh /tmp/netdata-kickstart.sh --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://app.netdata.cloud +wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && sh /tmp/netdata-kickstart.sh --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://api.netdata.cloud ``` ### Connect an agent running in Docker @@ -212,7 +212,7 @@ docker run -d --name=netdata \ --cap-add SYS_PTRACE \ --security-opt apparmor=unconfined \ -e NETDATA_CLAIM_TOKEN=TOKEN \ - -e NETDATA_CLAIM_URL="https://app.netdata.cloud" \ + -e NETDATA_CLAIM_URL="https://api.netdata.cloud" \ -e NETDATA_CLAIM_ROOMS=ROOM1,ROOM2 \ -e NETDATA_CLAIM_PROXY=PROXY \ netdata/netdata @@ -256,7 +256,7 @@ services: - /etc/os-release:/host/etc/os-release:ro environment: - NETDATA_CLAIM_TOKEN=TOKEN - - NETDATA_CLAIM_URL="https://app.netdata.cloud" + - NETDATA_CLAIM_URL="https://api.netdata.cloud" - NETDATA_CLAIM_ROOMS=ROOM1,ROOM2 volumes: @@ -276,7 +276,7 @@ Connect a _running Netdata Agent container_, where you don't want to recreate th `netdata` with the name of your running container: ```bash -docker exec -it netdata netdata-claim.sh -token=TOKEN -rooms=ROOM1,ROOM2 -url=https://app.netdata.cloud +docker exec -it netdata netdata-claim.sh -token=TOKEN -rooms=ROOM1,ROOM2 -url=https://api.netdata.cloud ``` The values for `ROOM1,ROOM2` can be found by by going to Netdata Cloud, clicking the **Nodes** tab, clicking **Connect Nodes**, selecting **Docker**, and copying the `rooms=` value in the command provided. @@ -288,7 +288,7 @@ you don't see the node in your Space after 60 seconds, see the [troubleshooting To connect a node that is running on a macOS environment the script that will be provided to you by Netdata Cloud is the [kickstart](/packaging/installer/methods/macos.md#install-netdata-with-our-automatic-one-line-installation-script) which will install the Netdata Agent on your node, if it isn't already installed, and connect the node to Netdata Cloud. It should be similar to: ```bash -curl https://my-netdata.io/kickstart.sh > /tmp/netdata-kickstart.sh && sh /tmp/netdata-kickstart.sh --install /usr/local/ --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://app.netdata.cloud +curl https://my-netdata.io/kickstart.sh > /tmp/netdata-kickstart.sh && sh /tmp/netdata-kickstart.sh --install /usr/local/ --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://api.netdata.cloud ``` The script should return `Agent was successfully claimed.`. If the connecting to Netdata Cloud process returns errors, or if you don't see the node in your Space after 60 seconds, see the [troubleshooting information](#troubleshooting). @@ -328,7 +328,7 @@ You can now move on to connecting. When you connect with the [kickstart](/packag append the same proxy setting you added to `netdata.conf`. ```bash -wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && sh /tmp/netdata-kickstart.sh --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://app.netdata.cloud --claim-proxy http://[user:pass@]host:ip +wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && sh /tmp/netdata-kickstart.sh --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://api.netdata.cloud --claim-proxy http://[user:pass@]host:ip ``` Hit **Enter**. The script should return `Agent was successfully claimed.`. If the connecting to Netdata Cloud process returns errors, or if @@ -511,7 +511,7 @@ using the [ACLK](/aclk/README.md). | setting | default | info | |:-------------- |:------------------------- |:-------------------------------------------------------------------------------------------------------------------------------------- | -| cloud base url | https://app.netdata.cloud | The URL for the Netdata Cloud web application. You should not change this. If you want to disable Cloud, change the `enabled` setting. | +| cloud base url | https://api.netdata.cloud | The URL for the Netdata Cloud web application. You should not change this. If you want to disable Cloud, change the `enabled` setting. | | enabled | yes | The runtime option to disable the [Agent-Cloud link](/aclk/README.md) and prevent your Agent from connecting to Netdata Cloud. | ### kickstart script @@ -551,7 +551,7 @@ using `sudo`, or as the user running the Agent (typically `netdata`), and passin -rooms=ROOM1,ROOM2,... where ROOMX is the War Room this node should be added to. This list is optional. -url=URL_BASE - where URL_BASE is the Netdata Cloud endpoint base URL. By default, this is https://app.netdata.cloud. + where URL_BASE is the Netdata Cloud endpoint base URL. By default, this is https://api.netdata.cloud. -id=AGENT_ID where AGENT_ID is the unique identifier of the Agent. This is the Agent's MACHINE_GUID by default. -hostname=HOSTNAME diff --git a/claim/claim.c b/claim/claim.c index 59c21aa35..d997fc84e 100644 --- a/claim/claim.c +++ b/claim/claim.c @@ -2,7 +2,8 @@ #include "claim.h" #include "registry/registry_internals.h" -#include "aclk/aclk_api.h" +#include "aclk/aclk.h" +#include "aclk/aclk_proxy.h" char *claiming_pending_arguments = NULL; @@ -57,7 +58,7 @@ void claim_agent(char *claiming_arguments) int exit_code; pid_t command_pid; char command_buffer[CLAIMING_COMMAND_LENGTH + 1]; - FILE *fp; + FILE *fp_child_output, *fp_child_input; // This is guaranteed to be set early in main via post_conf_load() char *cloud_base_url = appconfig_get(&cloud_config, CONFIG_SECTION_GLOBAL, "cloud base url", NULL); @@ -83,14 +84,14 @@ void claim_agent(char *claiming_arguments) claiming_arguments); info("Executing agent claiming command 'netdata-claim.sh'"); - fp = mypopen(command_buffer, &command_pid); - if(!fp) { + fp_child_output = netdata_popen(command_buffer, &command_pid, &fp_child_input); + if(!fp_child_output) { error("Cannot popen(\"%s\").", command_buffer); return; } info("Waiting for claiming command to finish."); - while (fgets(command_buffer, CLAIMING_COMMAND_LENGTH, fp) != NULL) {;} - exit_code = mypclose(fp, command_pid); + while (fgets(command_buffer, CLAIMING_COMMAND_LENGTH, fp_child_output) != NULL) {;} + exit_code = netdata_pclose(fp_child_input, fp_child_output, command_pid); info("Agent claiming command returned with code %d", exit_code); if (0 == exit_code) { load_claiming_state(); @@ -169,7 +170,7 @@ void load_claiming_state(void) } invalidate_node_instances(&localhost->host_uuid, claimed_id ? &uuid : NULL); - store_claim_id(&localhost->host_uuid, claimed_id ? &uuid : NULL); + metaqueue_store_claim_id(&localhost->host_uuid, claimed_id ? &uuid : NULL); rrdhost_aclk_state_unlock(localhost); if (!claimed_id) { diff --git a/claim/netdata-claim.sh.in b/claim/netdata-claim.sh.in index 73f016623..f87fbc273 100755 --- a/claim/netdata-claim.sh.in +++ b/claim/netdata-claim.sh.in @@ -136,7 +136,7 @@ MACHINE_GUID_FILE="@registrydir_POST@/netdata.public.unique.id" CLAIMING_DIR="${NETDATA_VARLIB_DIR}/cloud.d" TOKEN="unknown" URL_BASE=$(get_config_value cloud global "cloud base url") -[ -z "$URL_BASE" ] && URL_BASE="https://app.netdata.cloud" # Cover post-install with --dont-start +[ -z "$URL_BASE" ] && URL_BASE="https://api.netdata.cloud" # Cover post-install with --dont-start ID="unknown" ROOMS="" [ -z "$HOSTNAME" ] && HOSTNAME=$(hostname) diff --git a/cli/cli.c b/cli/cli.c index 229ad2952..c14653f37 100644 --- a/cli/cli.c +++ b/cli/cli.c @@ -78,7 +78,7 @@ static void pipe_read_cb(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf } else if (nread) { size_t to_copy; - to_copy = MIN(nread, MAX_COMMAND_LENGTH - 1 - response_string_size); + to_copy = MIN((unsigned int) nread, MAX_COMMAND_LENGTH - 1 - response_string_size); memcpy(response_string + response_string_size, buf->base, to_copy); response_string_size += to_copy; response_string[response_string_size] = '\0'; diff --git a/collectors/COLLECTORS.md b/collectors/COLLECTORS.md index 1be50fd14..7f66076ff 100644 --- a/collectors/COLLECTORS.md +++ b/collectors/COLLECTORS.md @@ -74,6 +74,10 @@ configure any of these collectors according to your setup and infrastructure. - [Prometheus endpoints](https://learn.netdata.cloud/docs/agent/collectors/go.d.plugin/modules/prometheus): Gathers metrics from any number of Prometheus endpoints, with support to autodetect more than 600 services and applications. +- [Pandas](https://learn.netdata.cloud/docs/agent/collectors/python.d.plugin/pandas): A Python collector that gathers + metrics from a [pandas](https://pandas.pydata.org/) dataframe. Pandas is a high level data processing library in + Python that can read various formats of data from local files or web endpoints. Custom processing and transformation + logic can also be expressed as part of the collector configuration. ### APM (application performance monitoring) @@ -510,6 +514,5 @@ default. To use a third-party collector, visit their GitHub/documentation page a ## Etc -- [checks.plugin](checks.plugin/README.md): A debugging collector, disabled by default. - [charts.d example](charts.d.plugin/example/README.md): An example `charts.d` collector. - [python.d example](python.d.plugin/example/README.md): An example `python.d` collector. diff --git a/collectors/Makefile.am b/collectors/Makefile.am index a0a972e8f..9f8bf5280 100644 --- a/collectors/Makefile.am +++ b/collectors/Makefile.am @@ -7,7 +7,6 @@ SUBDIRS = \ apps.plugin \ cgroups.plugin \ charts.d.plugin \ - checks.plugin \ cups.plugin \ diskspace.plugin \ timex.plugin \ diff --git a/collectors/REFERENCE.md b/collectors/REFERENCE.md index 949858f60..939b189ee 100644 --- a/collectors/REFERENCE.md +++ b/collectors/REFERENCE.md @@ -148,11 +148,6 @@ collect_data() { // attach a metric to it rd = rrddim_add(st, "id", "name", multiplier, divider, algorithm); } - else { - // this chart is already created - // let Netdata know we start a new iteration on it - rrdset_next(st); - } // give the collected value(s) to the chart rrddim_set_by_pointer(st, rd, collected_value); diff --git a/collectors/all.h b/collectors/all.h index 3d7304dd5..85a7ac8b2 100644 --- a/collectors/all.h +++ b/collectors/all.h @@ -195,11 +195,11 @@ #define NETDATA_CHART_PRIO_ZFS_IO 2700 #define NETDATA_CHART_PRIO_ZFS_HITS 2520 #define NETDATA_CHART_PRIO_ZFS_DHITS 2530 -#define NETDATA_CHART_PRIO_ZFS_DEMAND_DATA_HITS 2531 -#define NETDATA_CHART_PRIO_ZFS_PREFETCH_DATA_HITS 2532 -#define NETDATA_CHART_PRIO_ZFS_PHITS 2540 -#define NETDATA_CHART_PRIO_ZFS_MHITS 2550 -#define NETDATA_CHART_PRIO_ZFS_L2HITS 2560 +#define NETDATA_CHART_PRIO_ZFS_DEMAND_DATA_HITS 2540 +#define NETDATA_CHART_PRIO_ZFS_PREFETCH_DATA_HITS 2550 +#define NETDATA_CHART_PRIO_ZFS_PHITS 2560 +#define NETDATA_CHART_PRIO_ZFS_MHITS 2570 +#define NETDATA_CHART_PRIO_ZFS_L2HITS 2580 #define NETDATA_CHART_PRIO_ZFS_LIST_HITS 2600 #define NETDATA_CHART_PRIO_ZFS_HASH_ELEMENTS 2800 #define NETDATA_CHART_PRIO_ZFS_HASH_CHAINS 2810 diff --git a/collectors/apps.plugin/apps_groups.conf b/collectors/apps.plugin/apps_groups.conf index 89db6c4e8..dd45c5a5a 100644 --- a/collectors/apps.plugin/apps_groups.conf +++ b/collectors/apps.plugin/apps_groups.conf @@ -96,13 +96,13 @@ agent_sd: agent_sd # ----------------------------------------------------------------------------- # authentication/authorization related servers -auth: radius* openldap* ldap* slapd authelia +auth: radius* openldap* ldap* slapd authelia sssd saslauthd polkitd gssproxy fail2ban: fail2ban* # ----------------------------------------------------------------------------- # web/ftp servers -httpd: apache* httpd nginx* lighttpd hiawatha caddy +httpd: apache* httpd nginx* lighttpd hiawatha caddy h2o proxy: squid* c-icap squidGuard varnish* php: php* lsphp* ftpd: proftpd in.tftpd vsftpd @@ -121,7 +121,8 @@ columndb: clickhouse-server* # ----------------------------------------------------------------------------- # email servers -email: dovecot imapd pop3d amavis* zmstat-* zmdiaglog zmmailboxdmgr saslauthd opendkim postfwd2 smtp* lmtp* sendmail postfix master pickup qmgr showq tlsmgr postscreen oqmgr +mta: amavis* zmstat-* zmdiaglog zmmailboxdmgr opendkim postfwd2 smtp* lmtp* sendmail postfix master pickup qmgr showq tlsmgr postscreen oqmgr msmtp* nullmailer* +mda: dovecot *imapd *pop3d *popd # ----------------------------------------------------------------------------- # network, routing, VPN @@ -131,15 +132,17 @@ vpn: openvpn pptp* cjdroute gvpe tincd wireguard tailscaled wifi: hostapd wpa_supplicant routing: ospfd* ospf6d* bgpd bfdd fabricd isisd eigrpd sharpd staticd ripd ripngd pimd pbrd nhrpd ldpd zebra vrrpd vtysh bird* modem: ModemManager -netmanager: NetworkManager nm* systemd-networkd networkctl netplan +netmanager: NetworkManager nm* systemd-networkd networkctl netplan connmand wicked* avahi-autoipd networkd-dispatcher +firewall: firewalld ufw nft tor: tor +bluetooth: bluetooth bluez bluedevil obexd # ----------------------------------------------------------------------------- # high availability and balancers camo: *camo* balancer: ipvs_* haproxy -ha: corosync hs_logd ha_logd stonithd pacemakerd lrmd crmd keepalived +ha: corosync hs_logd ha_logd stonithd pacemakerd lrmd crmd keepalived ucarp* # ----------------------------------------------------------------------------- # telephony @@ -155,10 +158,18 @@ chat: irssi *vines* *prosody* murmurd # ----------------------------------------------------------------------------- # monitoring -logs: ulogd* syslog* rsyslog* logrotate systemd-journald rotatelogs -nms: snmpd vnstatd smokeping zabbix* monit munin* mon openhpid watchdog tailon nrpe +logs: ulogd* syslog* rsyslog* logrotate systemd-journald rotatelogs sysklogd metalog +nms: snmpd vnstatd smokeping zabbix* munin* mon openhpid tailon nrpe +monit: monit splunk: splunkd azure: mdsd *waagent* *omiserver* *omiagent* hv_kvp_daemon hv_vss_daemon *auoms* *omsagent* +datadog: *datadog* +edgedelta: edgedelta +newrelic: newrelic* +google-agent: *google_guest_agent* *google_osconfig_agent* +nvidia-smi: nvidia-smi +htop: htop +watchdog: watchdog # ----------------------------------------------------------------------------- # storage, file systems and file servers @@ -170,6 +181,7 @@ zfs: spl_* z_* txg_* zil_* arc_* l2arc* btrfs: btrfs* iscsi: iscsid iscsi_eh afp: netatalk afpd cnid_dbd cnid_metad +ntfs-3g: ntfs-3g # ----------------------------------------------------------------------------- # kubernetes @@ -189,18 +201,21 @@ aws: aws # ----------------------------------------------------------------------------- # virtualization platform -proxmox-ve: pve* +proxmox-ve: pve* spiceproxy # ----------------------------------------------------------------------------- # containers & virtual machines containers: lxc* docker* balena* -VMs: vbox* VBox* qemu* kvm +VMs: vbox* VBox* qemu* kvm* +libvirt: virtlogd virtqemud virtstoraged virtnetworkd virtlockd virtinterfaced +libvirt: virtnodedevd virtproxyd virtsecretd libvirtd +guest-agent: qemu-ga spice-vdagent cloud-init* # ----------------------------------------------------------------------------- # ssh servers and clients -ssh: ssh* scp dropbear +ssh: ssh* scp sftp* dropbear # ----------------------------------------------------------------------------- # print servers and clients @@ -210,24 +225,30 @@ print: cups* lpd lpq # ----------------------------------------------------------------------------- # time servers and clients -time: ntp* systemd-timesyn* chronyd +time: ntp* systemd-timesyn* chronyd ptp* # ----------------------------------------------------------------------------- # dhcp servers and clients -dhcp: *dhcp* +dhcp: *dhcp* dhclient # ----------------------------------------------------------------------------- # name servers and clients -dns: named unbound nsd pdns_server knotd gdnsd yadifad dnsmasq systemd-resolve* pihole* +dns: named unbound nsd pdns_server knotd gdnsd yadifad dnsmasq systemd-resolve* pihole* avahi-daemon avahi-dnsconfd dnsdist: dnsdist # ----------------------------------------------------------------------------- # installation / compilation / debugging build: cc1 cc1plus as gcc* cppcheck ld make cmake automake autoconf autoreconf -build: cargo rustc bazel buck git gdb valgrind* +build: cargo rustc bazel buck git gdb valgrind* rpmbuild dpkg-buildpackage + +# ----------------------------------------------------------------------------- +# package management + +packagemanager: apt* dpkg* dselect dnf yum rpm zypp* yast* pacman xbps* swupd* emerge* +packagemanager: packagekitd pkgin pkg apk snapd slackpkg slapt-get # ----------------------------------------------------------------------------- # antivirus @@ -258,7 +279,10 @@ ups: upsmon upsd */nut/* apcupsd # media players, servers, clients media: mplayer vlc xine mediatomb omxplayer* kodi* xbmc* mediacenter eventlircd -media: mpd minidlnad mt-daapd avahi* Plex* jellyfin squeeze* jackett Ombi +media: mpd minidlnad mt-daapd Plex* jellyfin squeeze* jackett Ombi +media: strawberry* clementine* + +audio: pulse* pipewire wireplumber jack* # ----------------------------------------------------------------------------- # java applications @@ -290,25 +314,56 @@ solr: *solr.install.dir* airflow: *airflow* # ----------------------------------------------------------------------------- -# X - -X: X Xorg xinit lightdm xdm pulseaudio gkrellm xfwm4 xfdesktop xfce* Thunar -X: xfsettingsd xfconfd gnome-* gdm gconf* dconf* xfconf* *gvfs gvfs* slim -X: kdeinit* kdm plasmashell -X: evolution-* firefox chromium opera vivaldi-bin epiphany WebKit* -X: '*systemd --user*' chrome *chrome-sandbox* *google-chrome* *chromium* *firefox* +# GUI + +X: X Xorg xinit xdm Xwayland xsettingsd +wayland: swaylock swayidle waypipe wayvnc +kde: *kdeinit* kdm sddm plasmashell startplasma-* kwin* kwallet* krunner kactivitymanager* +gnome: gnome-* gdm gconf* mutter +mate: mate-* msd-* marco* +cinnamon: cinnamon* muffin +xfce: xfwm4 xfdesktop xfce* Thunar xfsettingsd xfconf* +lxde: lxde* startlxde lxdm lxappearance* lxlauncher* lxpanel* lxsession* lxsettings* +lxqt: lxqt* startlxqt +enlightenment: entrance enlightenment* +i3: i3* +awesome: awesome awesome-client +dwm: dwm.* +sway: sway +weston: weston +cage: cage +wayfire: wayfire +gui: lightdm colord seatd greetd gkrellm slim qingy dconf* *gvfs gvfs* +gui: '*systemd --user*' xdg-* at-spi-* + +webbrowser: *chrome-sandbox* *google-chrome* *chromium* *firefox* vivaldi* opera* epiphany chrome* +webbrowser: lynx elinks w3m w3mmee links +mua: evolution-* thunderbird* mutt neomutt pine mailx alpine # ----------------------------------------------------------------------------- # Kernel / System ksmd: ksmd +khugepaged: khugepaged +kdamond: kdamond +kswapd: kswapd +zswap: zswap +kcompactd: kcompactd + +system: systemd-* udisks* udevd* *udevd ipv6_addrconf dbus-* rtkit* +system: mdadm acpid uuidd upowerd elogind* eudev mdev lvmpolld dmeventd +system: accounts-daemon rngd haveged rasdaemon irqbalance start-stop-daemon +system: supervise-daemon openrc* init runit runsvdir runsv auditd lsmd +system: abrt* nscd rtkit-daemon gpg-agent usbguard* + +kernel: kworker kthreadd kauditd lockd khelper kdevtmpfs khungtaskd rpciod +kernel: fsnotify_mark kthrotld deferwq scsi_* kdmflush oom_reaper kdevtempfs +kernel: ksoftirqd -system: systemd-* udisks* udevd* *udevd connmand ipv6_addrconf dbus-* rtkit* -system: inetd xinetd mdadm polkitd acpid uuidd packagekitd upowerd colord -system: accounts-daemon rngd haveged +# ----------------------------------------------------------------------------- +# inetd -kernel: kthreadd kauditd lockd khelper kdevtmpfs khungtaskd rpciod -kernel: fsnotify_mark kthrotld deferwq scsi_* +inetd: inetd xinetd # ----------------------------------------------------------------------------- # other application servers @@ -355,3 +410,13 @@ gremlin: gremlin* # load testing tools locust: locust + +# ----------------------------------------------------------------------------- +# data science and machine learning tools + +jupyter: jupyter* + +# ----------------------------------------------------------------------------- +# File synchronization tools + +filesync: dropbox syncthing diff --git a/collectors/apps.plugin/apps_plugin.c b/collectors/apps.plugin/apps_plugin.c index 8521e078e..89b83332b 100644 --- a/collectors/apps.plugin/apps_plugin.c +++ b/collectors/apps.plugin/apps_plugin.c @@ -10,6 +10,11 @@ #include "libnetdata/libnetdata.h" #include "libnetdata/required_dummies.h" +#define APPS_PLUGIN_FUNCTIONS() do { \ + fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " \"processes\" 10 \"Detailed information on the currently running processes on this node\"\n"); \ + } while(0) + + // ---------------------------------------------------------------------------- // debugging @@ -191,6 +196,18 @@ struct pid_on_target { struct pid_on_target *next; }; +struct openfds { + kernel_uint_t files; + kernel_uint_t pipes; + kernel_uint_t sockets; + kernel_uint_t inotifies; + kernel_uint_t eventfds; + kernel_uint_t timerfds; + kernel_uint_t signalfds; + kernel_uint_t eventpolls; + kernel_uint_t other; +}; + // ---------------------------------------------------------------------------- // target // @@ -235,24 +252,16 @@ struct target { kernel_uint_t io_logical_bytes_read; kernel_uint_t io_logical_bytes_written; - // kernel_uint_t io_read_calls; - // kernel_uint_t io_write_calls; + kernel_uint_t io_read_calls; + kernel_uint_t io_write_calls; kernel_uint_t io_storage_bytes_read; kernel_uint_t io_storage_bytes_written; - // kernel_uint_t io_cancelled_write_bytes; + kernel_uint_t io_cancelled_write_bytes; int *target_fds; int target_fds_size; - kernel_uint_t openfiles; - kernel_uint_t openpipes; - kernel_uint_t opensockets; - kernel_uint_t openinotifies; - kernel_uint_t openeventfds; - kernel_uint_t opentimerfds; - kernel_uint_t opensignalfds; - kernel_uint_t openeventpolls; - kernel_uint_t openother; + struct openfds openfds; kernel_uint_t starttime; kernel_uint_t collected_starttime; @@ -382,22 +391,24 @@ struct pid_stat { kernel_uint_t io_logical_bytes_read_raw; kernel_uint_t io_logical_bytes_written_raw; - // kernel_uint_t io_read_calls_raw; - // kernel_uint_t io_write_calls_raw; + kernel_uint_t io_read_calls_raw; + kernel_uint_t io_write_calls_raw; kernel_uint_t io_storage_bytes_read_raw; kernel_uint_t io_storage_bytes_written_raw; - // kernel_uint_t io_cancelled_write_bytes_raw; + kernel_uint_t io_cancelled_write_bytes_raw; kernel_uint_t io_logical_bytes_read; kernel_uint_t io_logical_bytes_written; - // kernel_uint_t io_read_calls; - // kernel_uint_t io_write_calls; + kernel_uint_t io_read_calls; + kernel_uint_t io_write_calls; kernel_uint_t io_storage_bytes_read; kernel_uint_t io_storage_bytes_written; - // kernel_uint_t io_cancelled_write_bytes; + kernel_uint_t io_cancelled_write_bytes; struct pid_fd *fds; // array of fds it uses - size_t fds_size; // the size of the fds array + size_t fds_size; // the size of the fds array + + struct openfds openfds; int children_count; // number of processes directly referencing this unsigned char keep:1; // 1 when we need to keep this process in memory even after it exited @@ -447,8 +458,8 @@ kernel_uint_t global_uptime; static struct pid_stat *root_of_pids = NULL, // global list of all processes running - **all_pids = NULL; // to avoid allocations, we pre-allocate the - // the entire pid space. + **all_pids = NULL; // to avoid allocations, we pre-allocate + // a pointer for each pid in the entire pid space. static size_t all_pids_count = 0; // the number of processes running @@ -961,15 +972,10 @@ static inline struct pid_stat *get_pid_entry(pid_t pid) { p->fds = mallocz(sizeof(struct pid_fd) * MAX_SPARE_FDS); p->fds_size = MAX_SPARE_FDS; init_pid_fds(p, 0, p->fds_size); - - if(likely(root_of_pids)) - root_of_pids->prev = p; - - p->next = root_of_pids; - root_of_pids = p; - p->pid = pid; + DOUBLE_LINKED_LIST_APPEND_UNSAFE(root_of_pids, p, prev, next); + all_pids[pid] = p; all_pids_count++; @@ -986,11 +992,7 @@ static inline void del_pid_entry(pid_t pid) { debug_log("process %d %s exited, deleting it.", pid, p->comm); - if(root_of_pids == p) - root_of_pids = p->next; - - if(p->next) p->next->prev = p->prev; - if(p->prev) p->prev->next = p->next; + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(root_of_pids, p, prev, next); // free the filename #ifndef __FreeBSD__ @@ -1554,21 +1556,21 @@ static inline int read_proc_pid_io(struct pid_stat *p, void *ptr) { #else pid_incremental_rate(io, p->io_logical_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 0, 1))); pid_incremental_rate(io, p->io_logical_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 1, 1))); - // pid_incremental_rate(io, p->io_read_calls, str2kernel_uint_t(procfile_lineword(ff, 2, 1))); - // pid_incremental_rate(io, p->io_write_calls, str2kernel_uint_t(procfile_lineword(ff, 3, 1))); + pid_incremental_rate(io, p->io_read_calls, str2kernel_uint_t(procfile_lineword(ff, 2, 1))); + pid_incremental_rate(io, p->io_write_calls, str2kernel_uint_t(procfile_lineword(ff, 3, 1))); pid_incremental_rate(io, p->io_storage_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 4, 1))); pid_incremental_rate(io, p->io_storage_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 5, 1))); - // pid_incremental_rate(io, p->io_cancelled_write_bytes, str2kernel_uint_t(procfile_lineword(ff, 6, 1))); + pid_incremental_rate(io, p->io_cancelled_write_bytes, str2kernel_uint_t(procfile_lineword(ff, 6, 1))); #endif if(unlikely(global_iterations_counter == 1)) { p->io_logical_bytes_read = 0; p->io_logical_bytes_written = 0; - // p->io_read_calls = 0; - // p->io_write_calls = 0; + p->io_read_calls = 0; + p->io_write_calls = 0; p->io_storage_bytes_read = 0; p->io_storage_bytes_written = 0; - // p->io_cancelled_write_bytes = 0; + p->io_cancelled_write_bytes = 0; } return 1; @@ -1577,11 +1579,11 @@ static inline int read_proc_pid_io(struct pid_stat *p, void *ptr) { cleanup: p->io_logical_bytes_read = 0; p->io_logical_bytes_written = 0; - // p->io_read_calls = 0; - // p->io_write_calls = 0; + p->io_read_calls = 0; + p->io_write_calls = 0; p->io_storage_bytes_read = 0; p->io_storage_bytes_written = 0; - // p->io_cancelled_write_bytes = 0; + p->io_cancelled_write_bytes = 0; return 0; #endif } @@ -1888,7 +1890,7 @@ static inline int file_descriptor_find_or_add(const char *name, uint32_t hash) { else if(likely(strncmp(name, "anon_inode:", 11) == 0)) { const char *t = &name[11]; - if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY; + if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY; else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD; else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL; else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD; @@ -1943,7 +1945,6 @@ static inline void cleanup_negative_pid_fds(struct pid_stat *p) { static inline void init_pid_fds(struct pid_stat *p, size_t first, size_t size) { struct pid_fd *pfd = &p->fds[first], *pfdend = &p->fds[first + size]; - size_t i = first; while(pfd < pfdend) { #ifndef __FreeBSD__ @@ -1951,7 +1952,6 @@ static inline void init_pid_fds(struct pid_stat *p, size_t first, size_t size) { #endif clear_pid_fd(pfd); pfd++; - i++; } } @@ -2904,24 +2904,24 @@ static size_t zero_all_targets(struct target *root) { w->io_logical_bytes_read = 0; w->io_logical_bytes_written = 0; - // w->io_read_calls = 0; - // w->io_write_calls = 0; + w->io_read_calls = 0; + w->io_write_calls = 0; w->io_storage_bytes_read = 0; w->io_storage_bytes_written = 0; - // w->io_cancelled_write_bytes = 0; + w->io_cancelled_write_bytes = 0; // zero file counters if(w->target_fds) { memset(w->target_fds, 0, sizeof(int) * w->target_fds_size); - w->openfiles = 0; - w->openpipes = 0; - w->opensockets = 0; - w->openinotifies = 0; - w->openeventfds = 0; - w->opentimerfds = 0; - w->opensignalfds = 0; - w->openeventpolls = 0; - w->openother = 0; + w->openfds.files = 0; + w->openfds.pipes = 0; + w->openfds.sockets = 0; + w->openfds.inotifies = 0; + w->openfds.eventfds = 0; + w->openfds.timerfds = 0; + w->openfds.signalfds = 0; + w->openfds.eventpolls = 0; + w->openfds.other = 0; } w->collected_starttime = 0; @@ -2935,7 +2935,7 @@ static size_t zero_all_targets(struct target *root) { while(pid_on_target) { pid_on_target_to_free = pid_on_target; pid_on_target = pid_on_target->next; - free(pid_on_target_to_free); + freez(pid_on_target_to_free); } w->root_pid = NULL; @@ -2956,60 +2956,64 @@ static inline void reallocate_target_fds(struct target *w) { } } -static inline void aggregate_fd_on_target(int fd, struct target *w) { - if(unlikely(!w)) - return; - - if(unlikely(w->target_fds[fd])) { - // it is already aggregated - // just increase its usage counter - w->target_fds[fd]++; - return; - } - - // increase its usage counter - // so that we will not add it again - w->target_fds[fd]++; - - switch(all_files[fd].type) { +static void aggregage_fd_type_on_openfds(FD_FILETYPE type, struct openfds *openfds) { + switch(type) { case FILETYPE_FILE: - w->openfiles++; + openfds->files++; break; case FILETYPE_PIPE: - w->openpipes++; + openfds->pipes++; break; case FILETYPE_SOCKET: - w->opensockets++; + openfds->sockets++; break; case FILETYPE_INOTIFY: - w->openinotifies++; + openfds->inotifies++; break; case FILETYPE_EVENTFD: - w->openeventfds++; + openfds->eventfds++; break; case FILETYPE_TIMERFD: - w->opentimerfds++; + openfds->timerfds++; break; case FILETYPE_SIGNALFD: - w->opensignalfds++; + openfds->signalfds++; break; case FILETYPE_EVENTPOLL: - w->openeventpolls++; + openfds->eventpolls++; break; case FILETYPE_OTHER: - w->openother++; + openfds->other++; break; } } +static inline void aggregate_fd_on_target(int fd, struct target *w) { + if(unlikely(!w)) + return; + + if(unlikely(w->target_fds[fd])) { + // it is already aggregated + // just increase its usage counter + w->target_fds[fd]++; + return; + } + + // increase its usage counter + // so that we will not add it again + w->target_fds[fd]++; + + aggregage_fd_type_on_openfds(all_files[fd].type, &w->openfds); +} + static inline void aggregate_pid_fds_on_targets(struct pid_stat *p) { if(unlikely(!p->updated)) { @@ -3023,6 +3027,16 @@ static inline void aggregate_pid_fds_on_targets(struct pid_stat *p) { reallocate_target_fds(u); reallocate_target_fds(g); + p->openfds.files = 0; + p->openfds.pipes = 0; + p->openfds.sockets = 0; + p->openfds.inotifies = 0; + p->openfds.eventfds = 0; + p->openfds.timerfds = 0; + p->openfds.signalfds = 0; + p->openfds.eventpolls = 0; + p->openfds.other = 0; + long currentfds = 0; size_t c, size = p->fds_size; struct pid_fd *fds = p->fds; @@ -3033,6 +3047,7 @@ static inline void aggregate_pid_fds_on_targets(struct pid_stat *p) { continue; currentfds++; + aggregage_fd_type_on_openfds(all_files[fd].type, &p->openfds); aggregate_fd_on_target(fd, w); aggregate_fd_on_target(fd, u); @@ -3079,11 +3094,11 @@ static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p, w->io_logical_bytes_read += p->io_logical_bytes_read; w->io_logical_bytes_written += p->io_logical_bytes_written; - // w->io_read_calls += p->io_read_calls; - // w->io_write_calls += p->io_write_calls; + w->io_read_calls += p->io_read_calls; + w->io_write_calls += p->io_write_calls; w->io_storage_bytes_read += p->io_storage_bytes_read; w->io_storage_bytes_written += p->io_storage_bytes_written; - // w->io_cancelled_write_bytes += p->io_cancelled_write_bytes; + w->io_cancelled_write_bytes += p->io_cancelled_write_bytes; w->processes++; w->num_threads += p->num_threads; @@ -3638,7 +3653,7 @@ static void send_collected_data_to_netdata(struct target *root, const char *type send_BEGIN(type, "files", dt); for (w = root; w; w = w->next) { if (unlikely(w->exposed && w->processes)) - send_SET(w->name, w->openfiles); + send_SET(w->name, w->openfds.files); } if (!strcmp("apps", type)){ kernel_uint_t usedfdpercentage = (kernel_uint_t) ((currentmaxfds * 100) / sysconf(_SC_OPEN_MAX)); @@ -3649,14 +3664,14 @@ static void send_collected_data_to_netdata(struct target *root, const char *type send_BEGIN(type, "sockets", dt); for (w = root; w; w = w->next) { if (unlikely(w->exposed && w->processes)) - send_SET(w->name, w->opensockets); + send_SET(w->name, w->openfds.sockets); } send_END(); send_BEGIN(type, "pipes", dt); for (w = root; w; w = w->next) { if (unlikely(w->exposed && w->processes)) - send_SET(w->name, w->openpipes); + send_SET(w->name, w->openfds.pipes); } send_END(); } @@ -3704,30 +3719,36 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu %s\n", w->name, time_factor * RATES_DETAIL / 100, w->hidden ? "hidden" : ""); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.mem '' '%s Real Memory (w/o shared)' 'MiB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, 1L, 1024L); } + APPS_PLUGIN_FUNCTIONS(); + fprintf(stdout, "CHART %s.vmem '' '%s Virtual Memory Size' 'MiB' mem %s.vmem stacked 20005 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, 1L, 1024L); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20006 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20007 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } + APPS_PLUGIN_FUNCTIONS(); #ifndef __FreeBSD__ fprintf(stdout, "CHART %s.uptime '' '%s Carried Over Uptime' 'seconds' processes %s.uptime line 20008 %d\n", type, title, type, update_every); @@ -3735,6 +3756,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } + APPS_PLUGIN_FUNCTIONS(); if (enable_detailed_uptime_charts) { fprintf(stdout, "CHART %s.uptime_min '' '%s Minimum Uptime' 'seconds' processes %s.uptime_min line 20009 %d\n", type, title, type, update_every); @@ -3742,18 +3764,21 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.uptime_avg '' '%s Average Uptime' 'seconds' processes %s.uptime_avg line 20010 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.uptime_max '' '%s Maximum Uptime' 'seconds' processes %s.uptime_max line 20011 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } + APPS_PLUGIN_FUNCTIONS(); } #endif @@ -3762,12 +3787,14 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, time_factor * RATES_DETAIL / 100LLU); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.cpu_system '' '%s CPU System Time (100%% = 1 core)' 'percentage' cpu %s.cpu_system stacked 20021 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, time_factor * RATES_DETAIL / 100LLU); } + APPS_PLUGIN_FUNCTIONS(); if(show_guest_time) { fprintf(stdout, "CHART %s.cpu_guest '' '%s CPU Guest Time (100%% = 1 core)' 'percentage' cpu %s.cpu_guest stacked 20022 %d\n", type, title, type, update_every); @@ -3775,6 +3802,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, time_factor * RATES_DETAIL / 100LLU); } + APPS_PLUGIN_FUNCTIONS(); } #ifndef __FreeBSD__ @@ -3783,6 +3811,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, 1L, 1024L); } + APPS_PLUGIN_FUNCTIONS(); #endif fprintf(stdout, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20012 %d\n", type, title, type, update_every); @@ -3790,12 +3819,14 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); } + APPS_PLUGIN_FUNCTIONS(); #ifdef __FreeBSD__ fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'blocks/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every); @@ -3803,36 +3834,42 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'blocks/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); } + APPS_PLUGIN_FUNCTIONS(); #else fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'KiB/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'KiB/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.lreads '' '%s Disk Logical Reads' 'KiB/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.lwrites '' '%s I/O Logical Writes' 'KiB/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } + APPS_PLUGIN_FUNCTIONS(); #endif if(enable_file_charts) { @@ -3842,6 +3879,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if (unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n", type, title, type, update_every); @@ -3849,6 +3887,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if (unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } + APPS_PLUGIN_FUNCTIONS(); fprintf(stdout, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type, title, type, update_every); @@ -3856,6 +3895,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if (unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name); } + APPS_PLUGIN_FUNCTIONS(); } } @@ -4134,6 +4174,664 @@ static int check_capabilities() { } #endif +netdata_mutex_t mutex = NETDATA_MUTEX_INITIALIZER; + +#define PROCESS_FILTER_CATEGORY "category:" +#define PROCESS_FILTER_USER "user:" +#define PROCESS_FILTER_GROUP "group:" +#define PROCESS_FILTER_PROCESS "process:" +#define PROCESS_FILTER_PID "pid:" +#define PROCESS_FILTER_UID "uid:" +#define PROCESS_FILTER_GID "gid:" + +static struct target *find_target_by_name(struct target *base, const char *name) { + struct target *t; + for(t = base; t ; t = t->next) { + if (strcmp(t->name, name) == 0) + return t; + } + + return NULL; +} + +static kernel_uint_t MemTotal = 0; + +static void get_MemTotal(void) { +#ifdef __FreeBSD__ + // TODO - fix this for FreeBSD + return; +#else + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s/proc/meminfo", netdata_configured_host_prefix); + + procfile *ff = procfile_open(filename, ": \t", PROCFILE_FLAG_DEFAULT); + if(!ff) + return; + + ff = procfile_readall(ff); + if(!ff) + return; + + size_t line, lines = procfile_lines(ff); + + for(line = 0; line < lines ;line++) { + size_t words = procfile_linewords(ff, line); + if(words == 3 && strcmp(procfile_lineword(ff, line, 0), "MemTotal") == 0 && strcmp(procfile_lineword(ff, line, 2), "kB") == 0) { + kernel_uint_t n = str2ull(procfile_lineword(ff, line, 1)); + if(n) MemTotal = n; + break; + } + } + + procfile_close(ff); +#endif +} + +static void apps_plugin_function_error(const char *transaction, int code, const char *msg) { + char buffer[PLUGINSD_LINE_MAX + 1]; + json_escape_string(buffer, msg, PLUGINSD_LINE_MAX); + + pluginsd_function_result_begin_to_stdout(transaction, code, "application/json", now_realtime_sec()); + fprintf(stdout, "{\"status\":%d,\"error_message\":\"%s\"}", code, buffer); + pluginsd_function_result_end_to_stdout(); +} + +static void apps_plugin_function_processes_help(const char *transaction) { + pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "text/plain", now_realtime_sec() + 3600); + fprintf(stdout, "%s", + "apps.plugin / processes\n" + "\n" + "Function `processes` presents all the currently running processes of the system.\n" + "\n" + "The following filters are supported:\n" + "\n" + " category:NAME\n" + " Shows only processes that are assigned the category `NAME` in apps_groups.conf\n" + "\n" + " user:NAME\n" + " Shows only processes that are running as user name `NAME`.\n" + "\n" + " group:NAME\n" + " Shows only processes that are running as group name `NAME`.\n" + "\n" + " process:NAME\n" + " Shows only processes that their Command is `NAME` or their parent's Command is `NAME`.\n" + "\n" + " pid:NUMBER\n" + " Shows only processes that their PID is `NUMBER` or their parent's PID is `NUMBER`\n" + "\n" + " uid:NUMBER\n" + " Shows only processes that their UID is `NUMBER`\n" + "\n" + " gid:NUMBER\n" + " Shows only processes that their GID is `NUMBER`\n" + "\n" + "Filters can be combined. Each filter can be given only one time.\n" + ); + pluginsd_function_result_end_to_stdout(); +} + +#define add_table_field(wb, key, name, visible, type, units, max, sort, sortable, sticky, unique_key, pointer_to, summary) do { \ + if(fields_added) buffer_strcat(wb, ","); \ + buffer_sprintf(wb, "\n \"%s\": {", key); \ + buffer_sprintf(wb, "\n \"index\":%d,", fields_added); \ + buffer_sprintf(wb, "\n \"unique_key\":%s,", (unique_key)?"true":"false"); \ + buffer_sprintf(wb, "\n \"name\":\"%s\",", name); \ + buffer_sprintf(wb, "\n \"visible\":%s,", (visible)?"true":"false"); \ + buffer_sprintf(wb, "\n \"type\":\"%s\",", type); \ + if(units) \ + buffer_sprintf(wb, "\n \"units\":\"%s\",", (char*)(units)); \ + if(!isnan((NETDATA_DOUBLE)(max))) \ + buffer_sprintf(wb, "\n \"max\":%f,", (NETDATA_DOUBLE)(max)); \ + if(pointer_to) \ + buffer_sprintf(wb, "\n \"pointer_to\":\"%s\",", (char *)(pointer_to)); \ + buffer_sprintf(wb, "\n \"sort\":\"%s\",", sort); \ + buffer_sprintf(wb, "\n \"sortable\":%s,", (sortable)?"true":"false"); \ + buffer_sprintf(wb, "\n \"sticky\":%s,", (sticky)?"true":"false"); \ + buffer_sprintf(wb, "\n \"summary\":\"%s\"", summary); \ + buffer_sprintf(wb, "\n }"); \ + fields_added++; \ +} while(0) + +#define add_value_field_llu_with_max(wb, key, value) do { \ + unsigned long long _tmp = (value); \ + key ## _max = (rows == 0) ? (_tmp) : MAX(key ## _max, _tmp); \ + buffer_fast_strcat(wb, ",", 1); \ + buffer_print_llu(wb, _tmp); \ +} while(0) + +#define add_value_field_ndd_with_max(wb, key, value) do { \ + NETDATA_DOUBLE _tmp = (value); \ + key ## _max = (rows == 0) ? (_tmp) : MAX(key ## _max, _tmp); \ + buffer_fast_strcat(wb, ",", 1); \ + buffer_rrd_value(wb, _tmp); \ +} while(0) + +static void apps_plugin_function_processes(const char *transaction, char *function __maybe_unused, char *line_buffer __maybe_unused, int line_max __maybe_unused, int timeout __maybe_unused) { + struct pid_stat *p; + + char *words[PLUGINSD_MAX_WORDS] = { NULL }; + size_t num_words = pluginsd_split_words(function, words, PLUGINSD_MAX_WORDS, NULL, NULL, 0); + + struct target *category = NULL, *user = NULL, *group = NULL; + const char *process_name = NULL; + pid_t pid = 0; + uid_t uid = 0; + gid_t gid = 0; + + bool filter_pid = false, filter_uid = false, filter_gid = false; + + for(int i = 1; i < PLUGINSD_MAX_WORDS ;i++) { + const char *keyword = get_word(words, num_words, i); + if(!keyword) break; + + if(!category && strncmp(keyword, PROCESS_FILTER_CATEGORY, strlen(PROCESS_FILTER_CATEGORY)) == 0) { + category = find_target_by_name(apps_groups_root_target, &keyword[strlen(PROCESS_FILTER_CATEGORY)]); + if(!category) { + apps_plugin_function_error(transaction, HTTP_RESP_BAD_REQUEST, "No category with that name found."); + return; + } + } + else if(!user && strncmp(keyword, PROCESS_FILTER_USER, strlen(PROCESS_FILTER_USER)) == 0) { + user = find_target_by_name(users_root_target, &keyword[strlen(PROCESS_FILTER_USER)]); + if(!user) { + apps_plugin_function_error(transaction, HTTP_RESP_BAD_REQUEST, "No user with that name found."); + return; + } + } + else if(strncmp(keyword, PROCESS_FILTER_GROUP, strlen(PROCESS_FILTER_GROUP)) == 0) { + group = find_target_by_name(groups_root_target, &keyword[strlen(PROCESS_FILTER_GROUP)]); + if(!group) { + apps_plugin_function_error(transaction, HTTP_RESP_BAD_REQUEST, "No group with that name found."); + return; + } + } + else if(!process_name && strncmp(keyword, PROCESS_FILTER_PROCESS, strlen(PROCESS_FILTER_PROCESS)) == 0) { + process_name = &keyword[strlen(PROCESS_FILTER_PROCESS)]; + } + else if(!pid && strncmp(keyword, PROCESS_FILTER_PID, strlen(PROCESS_FILTER_PID)) == 0) { + pid = str2i(&keyword[strlen(PROCESS_FILTER_PID)]); + filter_pid = true; + } + else if(!uid && strncmp(keyword, PROCESS_FILTER_UID, strlen(PROCESS_FILTER_UID)) == 0) { + uid = str2i(&keyword[strlen(PROCESS_FILTER_UID)]); + filter_uid = true; + } + else if(!gid && strncmp(keyword, PROCESS_FILTER_GID, strlen(PROCESS_FILTER_GID)) == 0) { + gid = str2i(&keyword[strlen(PROCESS_FILTER_GID)]); + filter_gid = true; + } + else if(strcmp(keyword, "help") == 0) { + apps_plugin_function_processes_help(transaction); + return; + } + else { + char msg[PLUGINSD_LINE_MAX]; + snprintfz(msg, PLUGINSD_LINE_MAX, "Invalid parameter '%s'", keyword); + apps_plugin_function_error(transaction, HTTP_RESP_BAD_REQUEST, msg); + return; + } + } + + time_t expires = now_realtime_sec() + update_every; + pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "application/json", expires); + + unsigned int cpu_divisor = time_factor * RATES_DETAIL / 100; + unsigned int memory_divisor = 1024; + unsigned int io_divisor = 1024 * RATES_DETAIL; + + BUFFER *wb = buffer_create(PLUGINSD_LINE_MAX); + buffer_sprintf(wb, + "{" + "\n \"status\":%d" + ",\n \"type\":\"table\"" + ",\n \"update_every\":%d" + ",\n \"data\":[" + "\n" + , HTTP_RESP_OK + , update_every + ); + + NETDATA_DOUBLE + UserCPU_max = 0.0 + , SysCPU_max = 0.0 + , GuestCPU_max = 0.0 + , CUserCPU_max = 0.0 + , CSysCPU_max = 0.0 + , CGuestCPU_max = 0.0 + , CPU_max = 0.0 + , VMSize_max = 0.0 + , RSS_max = 0.0 + , Shared_max = 0.0 + , Swap_max = 0.0 + , MemPcnt_max = 0.0 + ; + + unsigned long long + Processes_max = 0 + , Threads_max = 0 + , Uptime_max = 0 + , MinFlt_max = 0 + , CMinFlt_max = 0 + , TMinFlt_max = 0 + , MajFlt_max = 0 + , CMajFlt_max = 0 + , TMajFlt_max = 0 + , PReads_max = 0 + , PWrites_max = 0 + , RCalls_max = 0 + , WCalls_max = 0 + , Files_max = 0 + , Pipes_max = 0 + , Sockets_max = 0 + , iNotiFDs_max = 0 + , EventFDs_max = 0 + , TimerFDs_max = 0 + , SigFDs_max = 0 + , EvPollFDs_max = 0 + , OtherFDs_max = 0 + , FDs_max = 0 + ; + +#ifndef __FreeBSD__ + unsigned long long + LReads_max = 0 + , LWrites_max = 0 + ; +#endif + + int rows= 0; + for(p = root_of_pids; p ; p = p->next) { + if(!p->updated) + continue; + + if(category && p->target != category) + continue; + + if(user && p->user_target != user) + continue; + + if(group && p->group_target != group) + continue; + + if(process_name && ((strcmp(p->comm, process_name) != 0 && !p->parent) || (p->parent && strcmp(p->comm, process_name) != 0 && strcmp(p->parent->comm, process_name) != 0))) + continue; + + if(filter_pid && p->pid != pid && p->ppid != pid) + continue; + + if(filter_uid && p->uid != uid) + continue; + + if(filter_gid && p->gid != gid) + continue; + + if(rows) buffer_fast_strcat(wb, ",\n", 2); + rows++; + + buffer_strcat(wb, " ["); + + // IMPORTANT! + // THE ORDER SHOULD BE THE SAME WITH THE FIELDS! + + // pid + buffer_print_llu(wb, p->pid); + + // cmd + buffer_fast_strcat(wb, ",\"", 2); + buffer_strcat_jsonescape(wb, p->comm); + buffer_fast_strcat(wb, "\"", 1); + +#ifdef NETDATA_DEV_MODE + // cmdline + buffer_fast_strcat(wb, ",\"", 2); + buffer_strcat_jsonescape(wb, (p->cmdline && *p->cmdline) ? p->cmdline : p->comm); + buffer_fast_strcat(wb, "\"", 1); +#endif + + // ppid + buffer_fast_strcat(wb, ",", 1); buffer_print_llu(wb, p->ppid); + + // category + buffer_fast_strcat(wb, ",\"", 2); + buffer_strcat_jsonescape(wb, p->target ? p->target->name : "-"); + buffer_fast_strcat(wb, "\"", 1); + + // user + buffer_fast_strcat(wb, ",\"", 2); + buffer_strcat_jsonescape(wb, p->user_target ? p->user_target->name : "-"); + buffer_fast_strcat(wb, "\"", 1); + + // uid + buffer_fast_strcat(wb, ",", 1); buffer_print_llu(wb, p->uid); + + // group + buffer_fast_strcat(wb, ",\"", 2); + buffer_strcat_jsonescape(wb, p->group_target ? p->group_target->name : "-"); + buffer_fast_strcat(wb, "\"", 1); + + // gid + buffer_fast_strcat(wb, ",", 1); buffer_print_llu(wb, p->gid); + + // procs + add_value_field_llu_with_max(wb, Processes, p->children_count); + + // threads + add_value_field_llu_with_max(wb, Threads, p->num_threads); + + // uptime + add_value_field_llu_with_max(wb, Uptime, p->uptime); + + // minor page faults + add_value_field_llu_with_max(wb, MinFlt, p->minflt / RATES_DETAIL); + add_value_field_llu_with_max(wb, CMinFlt, p->cminflt / RATES_DETAIL); + add_value_field_llu_with_max(wb, TMinFlt, (p->minflt + p->cminflt) / RATES_DETAIL); + + // major page faults + add_value_field_llu_with_max(wb, MajFlt, p->majflt / RATES_DETAIL); + add_value_field_llu_with_max(wb, CMajFlt, p->cmajflt / RATES_DETAIL); + add_value_field_llu_with_max(wb, TMajFlt, (p->majflt + p->cmajflt) / RATES_DETAIL); + + // CPU utilization % + add_value_field_ndd_with_max(wb, UserCPU, (NETDATA_DOUBLE)(p->utime) / cpu_divisor); + add_value_field_ndd_with_max(wb, SysCPU, (NETDATA_DOUBLE)(p->stime) / cpu_divisor); + add_value_field_ndd_with_max(wb, GuestCPU, (NETDATA_DOUBLE)(p->gtime) / cpu_divisor); + add_value_field_ndd_with_max(wb, CUserCPU, (NETDATA_DOUBLE)(p->cutime) / cpu_divisor); + add_value_field_ndd_with_max(wb, CSysCPU, (NETDATA_DOUBLE)(p->cstime) / cpu_divisor); + add_value_field_ndd_with_max(wb, CGuestCPU, (NETDATA_DOUBLE)(p->cgtime) / cpu_divisor); + add_value_field_ndd_with_max(wb, CPU, (NETDATA_DOUBLE)(p->utime + p->stime + p->gtime + p->cutime + p->cstime + p->cgtime) / cpu_divisor); + + // memory MiB + add_value_field_ndd_with_max(wb, VMSize, (NETDATA_DOUBLE)p->status_vmsize / memory_divisor); + add_value_field_ndd_with_max(wb, RSS, (NETDATA_DOUBLE)p->status_vmrss / memory_divisor); + add_value_field_ndd_with_max(wb, Shared, (NETDATA_DOUBLE)p->status_vmshared / memory_divisor); + add_value_field_ndd_with_max(wb, Swap, (NETDATA_DOUBLE)p->status_vmswap / memory_divisor); + + if(MemTotal) + add_value_field_ndd_with_max(wb, MemPcnt, (NETDATA_DOUBLE)p->status_vmrss * 100.0 / (NETDATA_DOUBLE)MemTotal); + + // Logical I/O +#ifndef __FreeBSD__ + add_value_field_llu_with_max(wb, LReads, p->io_logical_bytes_read / io_divisor); + add_value_field_llu_with_max(wb, LWrites, p->io_logical_bytes_written / io_divisor); +#endif + + // Physical I/O + add_value_field_llu_with_max(wb, PReads, p->io_storage_bytes_read / io_divisor); + add_value_field_llu_with_max(wb, PWrites, p->io_storage_bytes_written / io_divisor); + + // I/O calls + add_value_field_llu_with_max(wb, RCalls, p->io_read_calls / RATES_DETAIL); + add_value_field_llu_with_max(wb, WCalls, p->io_write_calls / RATES_DETAIL); + + // open file descriptors + add_value_field_llu_with_max(wb, Files, p->openfds.files); + add_value_field_llu_with_max(wb, Pipes, p->openfds.pipes); + add_value_field_llu_with_max(wb, Sockets, p->openfds.sockets); + add_value_field_llu_with_max(wb, iNotiFDs, p->openfds.inotifies); + add_value_field_llu_with_max(wb, EventFDs, p->openfds.eventfds); + add_value_field_llu_with_max(wb, TimerFDs, p->openfds.timerfds); + add_value_field_llu_with_max(wb, SigFDs, p->openfds.signalfds); + add_value_field_llu_with_max(wb, EvPollFDs, p->openfds.eventpolls); + add_value_field_llu_with_max(wb, OtherFDs, p->openfds.other); + add_value_field_llu_with_max(wb, FDs, p->openfds.files + p->openfds.pipes + p->openfds.sockets + p->openfds.inotifies + p->openfds.eventfds + p->openfds.timerfds + p->openfds.signalfds + p->openfds.eventpolls + p->openfds.other); + + buffer_fast_strcat(wb, "]", 1); + + fwrite(buffer_tostring(wb), buffer_strlen(wb), 1, stdout); + buffer_flush(wb); + } + + { + int fields_added = 0; + + buffer_flush(wb); + buffer_sprintf(wb, "\n ],\n \"columns\": {"); + + // IMPORTANT! + // THE ORDER SHOULD BE THE SAME WITH THE VALUES! + add_table_field(wb, "Pid", "Process ID", true, "integer", NULL, NAN, "ascending", true, true, true, NULL, "count_unique"); + add_table_field(wb, "Cmd", "Process Name", true, "string", NULL, NAN, "ascending", true, true, false, NULL, "count_unique"); + +#ifdef NETDATA_DEV_MODE + add_table_field(wb, "CmdLine", "Command Line", false, "detail-string:Cmd", NULL, NAN, "ascending", true, false, false, NULL, "count_unique"); +#endif + add_table_field(wb, "PPid", "Parent Process ID", false, "integer", NULL, NAN, "ascending", true, false, false, "Pid", "count_unique"); + add_table_field(wb, "Category", "Category (apps_groups.conf)", true, "string", NULL, NAN, "ascending", true, true, false, NULL, "count_unique"); + add_table_field(wb, "User", "User Owner", true, "string", NULL, NAN, "ascending", true, false, false, NULL, "count_unique"); + add_table_field(wb, "Uid", "User ID", false, "integer", NULL, NAN, "ascending", true, false, false, NULL, "count_unique"); + add_table_field(wb, "Group", "Group Owner", false, "string", NULL, NAN, "ascending", true, false, false, NULL, "count_unique"); + add_table_field(wb, "Gid", "Group ID", false, "integer", NULL, NAN, "ascending", true, false, false, NULL, "count_unique"); + add_table_field(wb, "Processes", "Processes", true, "bar-with-integer", "processes", Processes_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "Threads", "Threads", true, "bar-with-integer", "threads", Threads_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "Uptime", "Uptime in seconds", true, "duration", "seconds", Uptime_max, "descending", true, false, false, NULL, "max"); + + // minor page faults + add_table_field(wb, "MinFlt", "Minor Page Faults/s", false, "bar", "pgflts/s", MinFlt_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "CMinFlt", "Children Minor Page Faults/s", false, "bar", "pgflts/s", CMinFlt_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "TMinFlt", "Total Minor Page Faults/s", false, "bar", "pgflts/s", TMinFlt_max, "descending", true, false, false, NULL, "sum"); + + // major page faults + add_table_field(wb, "MajFlt", "Major Page Faults/s", false, "bar", "pgflts/s", MajFlt_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "CMajFlt", "Children Major Page Faults/s", false, "bar", "pgflts/s", CMajFlt_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "TMajFlt", "Total Major Page Faults/s", true, "bar", "pgflts/s", TMajFlt_max, "descending", true, false, false, NULL, "sum"); + + // CPU utilization + add_table_field(wb, "UserCPU", "User CPU time", false, "bar", "%", UserCPU_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "SysCPU", "System CPU Time", false, "bar", "%", SysCPU_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "GuestCPU", "Guest CPU Time", false, "bar", "%", GuestCPU_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "CUserCPU", "Children User CPU Time", false, "bar", "%", CUserCPU_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "CSysCPU", "Children System CPU Time", false, "bar", "%", CSysCPU_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "CGuestCPU", "Children Guest CPU Time", false, "bar", "%", CGuestCPU_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "CPU", "Total CPU Time", true, "bar", "%", CPU_max, "descending", true, false, false, NULL, "sum"); + + // memory + add_table_field(wb, "VMSize", "Virtual Memory Size", false, "bar", "MiB", VMSize_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "RSS", "Resident Set Size", MemTotal ? false : true, "bar", "MiB", RSS_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "Shared", "Shared Pages", false, "bar", "MiB", Shared_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "Swap", "Swap Memory", false, "bar", "MiB", Swap_max, "descending", true, false, false, NULL, "sum"); + + if(MemTotal) + add_table_field(wb, "MemPcnt", "Memory Percentage", true, "bar", "%", 100.0, "descending", true, false, false, NULL, "sum"); + + // Logical I/O +#ifndef __FreeBSD__ + add_table_field(wb, "LReads", "Logical I/O Reads", false, "bar", "KiB/s", LReads_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "LWrites", "Logical I/O Writes", false, "bar", "KiB/s", LWrites_max, "descending", true, false, false, NULL, "sum"); +#endif + + // Physical I/O + add_table_field(wb, "PReads", "Physical I/O Reads", true, "bar", "KiB/s", PReads_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "PWrites", "Physical I/O Writes", true, "bar", "KiB/s", PWrites_max, "descending", true, false, false, NULL, "sum"); + + // I/O calls + add_table_field(wb, "RCalls", "I/O Read Calls", false, "bar", "calls/s", RCalls_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "WCalls", "I/O Write Calls", false, "bar", "calls/s", WCalls_max, "descending", true, false, false, NULL, "sum"); + + // open file descriptors + add_table_field(wb, "Files", "Open Files", false, "bar", "fds", Files_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "Pipes", "Open Pipes", false, "bar", "fds", Pipes_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "Sockets", "Open Sockets", false, "bar", "fds", Sockets_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "iNotiFDs", "Open iNotify Descriptors", false, "bar", "fds", iNotiFDs_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "EventFDs", "Open Event Descriptors", false, "bar", "fds", EventFDs_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "TimerFDs", "Open Timer Descriptors", false, "bar", "fds", TimerFDs_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "SigFDs", "Open Signal Descriptors", false, "bar", "fds", SigFDs_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "EvPollFDs", "Open Event Poll Descriptors", false, "bar", "fds", EvPollFDs_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "OtherFDs", "Other Open Descriptors", false, "bar", "fds", OtherFDs_max, "descending", true, false, false, NULL, "sum"); + add_table_field(wb, "FDs", "All Open File Descriptors", true, "bar", "fds", FDs_max, "descending", true, false, false, NULL, "sum"); + + buffer_strcat( + wb, + "" + "\n }," + "\n \"default_sort_column\": \"CPU\"," + "\n \"charts\": {" + "\n \"CPU\": {" + "\n \"name\":\"CPU Utilization\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"UserCPU\", \"SysCPU\", \"GuestCPU\", \"CUserCPU\", \"CSysCPU\", \"CGuestCPU\" ]" + "\n }," + "\n \"Memory\": {" + "\n \"name\":\"Memory\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"VMSize\", \"RSS\", \"Shared\", \"Swap\" ]" + "\n }," + ); + + if(MemTotal) + buffer_strcat( + wb, + "" + "\n \"MemoryPercent\": {" + "\n \"name\":\"Memory Percentage\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"MemPcnt\" ]" + "\n }," + ); + + buffer_strcat( + wb, "" + #ifndef __FreeBSD__ + "\n \"Reads\": {" + "\n \"name\":\"I/O Reads\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"LReads\", \"PReads\" ]" + "\n }," + "\n \"Writes\": {" + "\n \"name\":\"I/O Writes\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"LWrites\", \"PWrites\" ]" + "\n }," + "\n \"LogicalIO\": {" + "\n \"name\":\"Logical I/O\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"LReads\", \"LWrites\" ]" + "\n }," + #endif + "\n \"PhysicalIO\": {" + "\n \"name\":\"Physical I/O\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"PReads\", \"PWrites\" ]" + "\n }," + "\n \"IOCalls\": {" + "\n \"name\":\"I/O Calls\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"RCalls\", \"WCalls\" ]" + "\n }," + "\n \"MinFlt\": {" + "\n \"name\":\"Minor Page Faults\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"MinFlt\", \"CMinFlt\" ]" + "\n }," + "\n \"MajFlt\": {" + "\n \"name\":\"Major Page Faults\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"MajFlt\", \"CMajFlt\" ]" + "\n }," + "\n \"Threads\": {" + "\n \"name\":\"Threads\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"Threads\" ]" + "\n }," + "\n \"Processes\": {" + "\n \"name\":\"Processes\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"Processes\" ]" + "\n }," + "\n \"FDs\": {" + "\n \"name\":\"File Descriptors\"," + "\n \"type\":\"stacked-bar\"," + "\n \"columns\": [ \"Files\", \"Pipes\", \"Sockets\", \"iNotiFDs\", \"EventFDs\", \"TimerFDs\", \"SigFDs\", \"EvPollFDs\", \"OtherFDs\" ]" + "\n }" + "\n }," + "\n \"group_by\": {" + "\n \"pid\": {" + "\n \"name\":\"Process Tree by PID\"," + "\n \"columns\":[ \"PPid\" ]" + "\n }," + "\n \"category\": {" + "\n \"name\":\"Process Tree by Category\"," + "\n \"columns\":[ \"Category\", \"PPid\" ]" + "\n }," + "\n \"user\": {" + "\n \"name\":\"Process Tree by User\"," + "\n \"columns\":[ \"User\", \"PPid\" ]" + "\n }," + "\n \"group\": {" + "\n \"name\":\"Process Tree by Group\"," + "\n \"columns\":[ \"Group\", \"PPid\" ]" + "\n }" + "\n }" + ); + + fwrite(buffer_tostring(wb), buffer_strlen(wb), 1, stdout); + } + + buffer_free(wb); + + fprintf(stdout, ",\n \"expires\":%lld", (long long)expires); + fprintf(stdout, "\n}"); + + pluginsd_function_result_end_to_stdout(); +} + +bool apps_plugin_exit = false; + +void *reader_main(void *arg __maybe_unused) { + char buffer[PLUGINSD_LINE_MAX + 1]; + + char *s = NULL; + while(!apps_plugin_exit && (s = fgets(buffer, PLUGINSD_LINE_MAX, stdin))) { + + char *words[PLUGINSD_MAX_WORDS] = { NULL }; + size_t num_words = pluginsd_split_words(buffer, words, PLUGINSD_MAX_WORDS, NULL, NULL, 0); + + const char *keyword = get_word(words, num_words, 0); + + if(keyword && strcmp(keyword, PLUGINSD_KEYWORD_FUNCTION) == 0) { + char *transaction = get_word(words, num_words, 1); + char *timeout_s = get_word(words, num_words, 2); + char *function = get_word(words, num_words, 3); + + if(!transaction || !*transaction || !timeout_s || !*timeout_s || !function || !*function) { + error("Received incomplete %s (transaction = '%s', timeout = '%s', function = '%s'). Ignoring it.", + keyword, + transaction?transaction:"(unset)", + timeout_s?timeout_s:"(unset)", + function?function:"(unset)"); + } + else { + int timeout = str2i(timeout_s); + if(timeout <= 0) timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT; + +// internal_error(true, "Received function '%s', transaction '%s', timeout %d", function, transaction, timeout); + + netdata_mutex_lock(&mutex); + + if(strncmp(function, "processes", strlen("processes")) == 0) + apps_plugin_function_processes(transaction, function, buffer, PLUGINSD_LINE_MAX + 1, timeout); + else + apps_plugin_function_error(transaction, HTTP_RESP_NOT_FOUND, "No function with this name found in apps.plugin."); + + fflush(stdout); + netdata_mutex_unlock(&mutex); + +// internal_error(true, "Done with function '%s', transaction '%s', timeout %d", function, transaction, timeout); + } + } + else + error("Received unknown command: %s", keyword?keyword:"(unset)"); + } + + if(!s || feof(stdin) || ferror(stdin)) { + apps_plugin_exit = true; + error("Received error on stdin."); + } + + exit(1); + return NULL; +} + int main(int argc, char **argv) { // debug_flags = D_PROCFILE; @@ -4151,6 +4849,13 @@ int main(int argc, char **argv) { error_log_errors_per_period = 100; error_log_throttle_period = 3600; + bool send_resource_usage = true; + { + const char *s = getenv("NETDATA_INTERNALS_MONITORING"); + if(s && *s && strcmp(s, "NO") == 0) + send_resource_usage = false; + } + // since apps.plugin runs as root, prevent it from opening symbolic links procfile_open_flags = O_RDONLY|O_NOFOLLOW; @@ -4224,16 +4929,21 @@ int main(int argc, char **argv) { debug_log("group file: '%s'", all_group_ids.filename); #if (ALL_PIDS_ARE_READ_INSTANTLY == 0) - all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max); + all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max + 1); #endif - all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max); + all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max + 1); + + netdata_thread_t reader_thread; + netdata_thread_create(&reader_thread, "APPS_READER", NETDATA_THREAD_OPTION_DONT_LOG, reader_main, NULL); + netdata_mutex_lock(&mutex); usec_t step = update_every * USEC_PER_SEC; global_iterations_counter = 1; heartbeat_t hb; heartbeat_init(&hb); - for(;1; global_iterations_counter++) { + for(; !apps_plugin_exit ; global_iterations_counter++) { + netdata_mutex_unlock(&mutex); #ifdef NETDATA_PROFILING #warning "compiling for profiling" @@ -4244,16 +4954,28 @@ int main(int argc, char **argv) { #else usec_t dt = heartbeat_next(&hb, step); #endif + netdata_mutex_lock(&mutex); struct pollfd pollfd = { .fd = fileno(stdout), .events = POLLERR }; - if (unlikely(poll(&pollfd, 1, 0) < 0)) + if (unlikely(poll(&pollfd, 1, 0) < 0)) { + netdata_mutex_unlock(&mutex); + netdata_thread_cancel(reader_thread); fatal("Cannot check if a pipe is available"); - if (unlikely(pollfd.revents & POLLERR)) - fatal("Cannot write to a pipe"); + } + if (unlikely(pollfd.revents & POLLERR)) { + netdata_mutex_unlock(&mutex); + netdata_thread_cancel(reader_thread); + fatal("Received error on read pipe."); + } + + if(global_iterations_counter % 10 == 0) + get_MemTotal(); if(!collect_data_for_all_processes()) { error("Cannot collect /proc data for running processes. Disabling apps.plugin..."); printf("DISABLE\n"); + netdata_mutex_unlock(&mutex); + netdata_thread_cancel(reader_thread); exit(1); } @@ -4261,7 +4983,8 @@ int main(int argc, char **argv) { calculate_netdata_statistics(); normalize_utilization(apps_groups_root_target); - send_resource_usage_to_netdata(dt); + if(send_resource_usage) + send_resource_usage_to_netdata(dt); #ifndef __FreeBSD__ send_proc_states_count(dt); diff --git a/collectors/cgroups.plugin/cgroup-name.sh b/collectors/cgroups.plugin/cgroup-name.sh index d1277b745..55b02ac72 100755 --- a/collectors/cgroups.plugin/cgroup-name.sh +++ b/collectors/cgroups.plugin/cgroup-name.sh @@ -450,7 +450,10 @@ function k8s_get_name() { function docker_get_name() { local id="${1}" - if hash docker 2> /dev/null; then + # See https://github.com/netdata/netdata/pull/13523 for details + if command -v snap >/dev/null 2>&1 && snap list docker >/dev/null 2>&1; then + docker_like_get_name_api DOCKER_HOST "${id}" + elif hash docker 2> /dev/null; then docker_like_get_name_command docker "${id}" else docker_like_get_name_api DOCKER_HOST "${id}" || docker_like_get_name_command podman "${id}" diff --git a/collectors/cgroups.plugin/cgroup-network-helper.sh b/collectors/cgroups.plugin/cgroup-network-helper.sh index 07318d774..783332f73 100755 --- a/collectors/cgroups.plugin/cgroup-network-helper.sh +++ b/collectors/cgroups.plugin/cgroup-network-helper.sh @@ -150,6 +150,7 @@ virsh_cgroup_to_domain_name() { # extract for the cgroup path sed -n -e "s|.*/machine-qemu\\\\x2d[0-9]\+\\\\x2d\(.*\)\.scope$|\1|p" \ + -e "s|.*/machine/qemu-[0-9]\+-\(.*\)\.libvirt-qemu$|\1|p" \ -e "s|.*/machine/\(.*\)\.libvirt-qemu$|\1|p" \ <some.share_time.value10 = strtod(procfile_lineword(ff, 0, 2), NULL); res->some.share_time.value60 = strtod(procfile_lineword(ff, 0, 4), NULL); res->some.share_time.value300 = strtod(procfile_lineword(ff, 0, 6), NULL); @@ -1663,16 +1666,16 @@ static inline void read_cgroup_network_interfaces(struct cgroup *cg) { } debug(D_CGROUP, "executing cgroup_identifier %s --cgroup '%s' for cgroup '%s'", cgroups_network_interface_script, cgroup_identifier, cg->id); - FILE *fp; - (void)mypopen_raw_default_flags_and_environment(&cgroup_pid, &fp, cgroups_network_interface_script, "--cgroup", cgroup_identifier); - if(!fp) { + FILE *fp_child_input, *fp_child_output; + (void)netdata_popen_raw_default_flags_and_environment(&cgroup_pid, &fp_child_input, &fp_child_output, cgroups_network_interface_script, "--cgroup", cgroup_identifier); + if(!fp_child_output) { error("CGROUP: cannot popen(%s --cgroup \"%s\", \"r\").", cgroups_network_interface_script, cgroup_identifier); return; } char *s; char buffer[CGROUP_NETWORK_INTERFACE_MAX_LINE + 1]; - while((s = fgets(buffer, CGROUP_NETWORK_INTERFACE_MAX_LINE, fp))) { + while((s = fgets(buffer, CGROUP_NETWORK_INTERFACE_MAX_LINE, fp_child_output))) { trim(s); if(*s && *s != '\n') { @@ -1707,7 +1710,7 @@ static inline void read_cgroup_network_interfaces(struct cgroup *cg) { } } - mypclose(fp, cgroup_pid); + netdata_pclose(fp_child_input, fp_child_output, cgroup_pid); // debug(D_CGROUP, "closed cgroup_identifier for cgroup '%s'", cg->id); } @@ -1869,9 +1872,9 @@ static inline void discovery_rename_cgroup(struct cgroup *cg) { debug(D_CGROUP, "executing command %s \"%s\" for cgroup '%s'", cgroups_rename_script, cg->intermediate_id, cg->chart_id); pid_t cgroup_pid; - FILE *fp; - (void)mypopen_raw_default_flags_and_environment(&cgroup_pid, &fp, cgroups_rename_script, cg->id, cg->intermediate_id); - if (!fp) { + FILE *fp_child_input, *fp_child_output; + (void)netdata_popen_raw_default_flags_and_environment(&cgroup_pid, &fp_child_input, &fp_child_output, cgroups_rename_script, cg->id, cg->intermediate_id); + if (!fp_child_output) { error("CGROUP: cannot popen(%s \"%s\", \"r\").", cgroups_rename_script, cg->intermediate_id); cg->pending_renames = 0; cg->processed = 1; @@ -1879,8 +1882,8 @@ static inline void discovery_rename_cgroup(struct cgroup *cg) { } char buffer[CGROUP_CHARTID_LINE_MAX + 1]; - char *new_name = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp); - int exit_code = mypclose(fp, cgroup_pid); + char *new_name = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp_child_output); + int exit_code = netdata_pclose(fp_child_input, fp_child_output, cgroup_pid); switch (exit_code) { case 0: @@ -2682,7 +2685,7 @@ static inline void discovery_process_cgroup(struct cgroup *cg) { cg->processed = 1; - if (strlen(cg->chart_id) >= RRD_ID_LENGTH_MAX) { + if ((strlen(cg->chart_id) + strlen(cgroup_chart_id_prefix)) >= RRD_ID_LENGTH_MAX) { info("cgroup '%s' (chart id '%s') disabled because chart_id exceeds the limit (RRD_ID_LENGTH_MAX)", cg->id, cg->chart_id); return; } @@ -2843,57 +2846,45 @@ void update_systemd_services_charts( // create the charts - if(likely(do_cpu)) { - if(unlikely(!st_cpu)) { - char title[CHART_TITLE_MAX + 1]; - snprintfz(title, CHART_TITLE_MAX, "Systemd Services CPU utilization (100%% = 1 core)"); - - st_cpu = rrdset_create_localhost( - "services" - , "cpu" - , NULL - , "cpu" - , "services.cpu" - , title - , "percentage" - , PLUGIN_CGROUPS_NAME - , PLUGIN_CGROUPS_MODULE_SYSTEMD_NAME - , NETDATA_CHART_PRIO_CGROUPS_SYSTEMD - , update_every - , RRDSET_TYPE_STACKED - ); - - } - else - rrdset_next(st_cpu); + if (unlikely(do_cpu && !st_cpu)) { + char title[CHART_TITLE_MAX + 1]; + snprintfz(title, CHART_TITLE_MAX, "Systemd Services CPU utilization (100%% = 1 core)"); + + st_cpu = rrdset_create_localhost( + "services" + , "cpu" + , NULL + , "cpu" + , "services.cpu" + , title + , "percentage" + , PLUGIN_CGROUPS_NAME + , PLUGIN_CGROUPS_MODULE_SYSTEMD_NAME + , NETDATA_CHART_PRIO_CGROUPS_SYSTEMD + , update_every + , RRDSET_TYPE_STACKED + ); } - if(likely(do_mem_usage)) { - if(unlikely(!st_mem_usage)) { - - st_mem_usage = rrdset_create_localhost( - "services" - , "mem_usage" - , NULL - , "mem" - , "services.mem_usage" - , "Systemd Services Used Memory" - , "MiB" - , PLUGIN_CGROUPS_NAME - , PLUGIN_CGROUPS_MODULE_SYSTEMD_NAME - , NETDATA_CHART_PRIO_CGROUPS_SYSTEMD + 10 - , update_every - , RRDSET_TYPE_STACKED - ); - - } - else - rrdset_next(st_mem_usage); + if (unlikely(do_mem_usage && !st_mem_usage)) { + st_mem_usage = rrdset_create_localhost( + "services" + , "mem_usage" + , NULL + , "mem" + , "services.mem_usage" + , "Systemd Services Used Memory" + , "MiB" + , PLUGIN_CGROUPS_NAME + , PLUGIN_CGROUPS_MODULE_SYSTEMD_NAME + , NETDATA_CHART_PRIO_CGROUPS_SYSTEMD + 10 + , update_every + , RRDSET_TYPE_STACKED + ); } if(likely(do_mem_detailed)) { if(unlikely(!st_mem_detailed_rss)) { - st_mem_detailed_rss = rrdset_create_localhost( "services" , "mem_rss" @@ -2908,13 +2899,9 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_mem_detailed_rss); if(unlikely(!st_mem_detailed_mapped)) { - st_mem_detailed_mapped = rrdset_create_localhost( "services" , "mem_mapped" @@ -2929,13 +2916,9 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_mem_detailed_mapped); if(unlikely(!st_mem_detailed_cache)) { - st_mem_detailed_cache = rrdset_create_localhost( "services" , "mem_cache" @@ -2950,13 +2933,9 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_mem_detailed_cache); if(unlikely(!st_mem_detailed_writeback)) { - st_mem_detailed_writeback = rrdset_create_localhost( "services" , "mem_writeback" @@ -2973,11 +2952,8 @@ void update_systemd_services_charts( ); } - else - rrdset_next(st_mem_detailed_writeback); if(unlikely(!st_mem_detailed_pgfault)) { - st_mem_detailed_pgfault = rrdset_create_localhost( "services" , "mem_pgfault" @@ -2993,11 +2969,8 @@ void update_systemd_services_charts( , RRDSET_TYPE_STACKED ); } - else - rrdset_next(st_mem_detailed_pgfault); if(unlikely(!st_mem_detailed_pgmajfault)) { - st_mem_detailed_pgmajfault = rrdset_create_localhost( "services" , "mem_pgmajfault" @@ -3012,13 +2985,9 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_mem_detailed_pgmajfault); if(unlikely(!st_mem_detailed_pgpgin)) { - st_mem_detailed_pgpgin = rrdset_create_localhost( "services" , "mem_pgpgin" @@ -3035,11 +3004,8 @@ void update_systemd_services_charts( ); } - else - rrdset_next(st_mem_detailed_pgpgin); if(unlikely(!st_mem_detailed_pgpgout)) { - st_mem_detailed_pgpgout = rrdset_create_localhost( "services" , "mem_pgpgout" @@ -3054,61 +3020,45 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_mem_detailed_pgpgout); } - if(likely(do_mem_failcnt)) { - if(unlikely(!st_mem_failcnt)) { - - st_mem_failcnt = rrdset_create_localhost( - "services" - , "mem_failcnt" - , NULL - , "mem" - , "services.mem_failcnt" - , "Systemd Services Memory Limit Failures" - , "failures" - , PLUGIN_CGROUPS_NAME - , PLUGIN_CGROUPS_MODULE_SYSTEMD_NAME - , NETDATA_CHART_PRIO_CGROUPS_SYSTEMD + 110 - , update_every - , RRDSET_TYPE_STACKED - ); - - } - else - rrdset_next(st_mem_failcnt); + if(unlikely(do_mem_failcnt && !st_mem_failcnt)) { + st_mem_failcnt = rrdset_create_localhost( + "services" + , "mem_failcnt" + , NULL + , "mem" + , "services.mem_failcnt" + , "Systemd Services Memory Limit Failures" + , "failures" + , PLUGIN_CGROUPS_NAME + , PLUGIN_CGROUPS_MODULE_SYSTEMD_NAME + , NETDATA_CHART_PRIO_CGROUPS_SYSTEMD + 110 + , update_every + , RRDSET_TYPE_STACKED + ); } - if(likely(do_swap_usage)) { - if(unlikely(!st_swap_usage)) { - - st_swap_usage = rrdset_create_localhost( - "services" - , "swap_usage" - , NULL - , "swap" - , "services.swap_usage" - , "Systemd Services Swap Memory Used" - , "MiB" - , PLUGIN_CGROUPS_NAME - , PLUGIN_CGROUPS_MODULE_SYSTEMD_NAME - , NETDATA_CHART_PRIO_CGROUPS_SYSTEMD + 100 - , update_every - , RRDSET_TYPE_STACKED - ); - - } - else - rrdset_next(st_swap_usage); + if (do_swap_usage && !st_swap_usage) { + st_swap_usage = rrdset_create_localhost( + "services" + , "swap_usage" + , NULL + , "swap" + , "services.swap_usage" + , "Systemd Services Swap Memory Used" + , "MiB" + , PLUGIN_CGROUPS_NAME + , PLUGIN_CGROUPS_MODULE_SYSTEMD_NAME + , NETDATA_CHART_PRIO_CGROUPS_SYSTEMD + 100 + , update_every + , RRDSET_TYPE_STACKED + ); } if(likely(do_io)) { if(unlikely(!st_io_read)) { - st_io_read = rrdset_create_localhost( "services" , "io_read" @@ -3123,13 +3073,9 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_io_read); if(unlikely(!st_io_write)) { - st_io_write = rrdset_create_localhost( "services" , "io_write" @@ -3144,15 +3090,11 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_io_write); } if(likely(do_io_ops)) { if(unlikely(!st_io_serviced_read)) { - st_io_serviced_read = rrdset_create_localhost( "services" , "io_ops_read" @@ -3167,13 +3109,9 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_io_serviced_read); if(unlikely(!st_io_serviced_write)) { - st_io_serviced_write = rrdset_create_localhost( "services" , "io_ops_write" @@ -3188,10 +3126,7 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_io_serviced_write); } if(likely(do_throttle_io)) { @@ -3213,11 +3148,8 @@ void update_systemd_services_charts( ); } - else - rrdset_next(st_throttle_io_read); if(unlikely(!st_throttle_io_write)) { - st_throttle_io_write = rrdset_create_localhost( "services" , "throttle_io_write" @@ -3232,15 +3164,11 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_throttle_io_write); } if(likely(do_throttle_ops)) { if(unlikely(!st_throttle_ops_read)) { - st_throttle_ops_read = rrdset_create_localhost( "services" , "throttle_io_ops_read" @@ -3255,13 +3183,9 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_throttle_ops_read); if(unlikely(!st_throttle_ops_write)) { - st_throttle_ops_write = rrdset_create_localhost( "services" , "throttle_io_ops_write" @@ -3276,15 +3200,11 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_throttle_ops_write); } if(likely(do_queued_ops)) { if(unlikely(!st_queued_ops_read)) { - st_queued_ops_read = rrdset_create_localhost( "services" , "queued_io_ops_read" @@ -3299,10 +3219,7 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_queued_ops_read); if(unlikely(!st_queued_ops_write)) { @@ -3320,15 +3237,11 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_queued_ops_write); } if(likely(do_merged_ops)) { if(unlikely(!st_merged_ops_read)) { - st_merged_ops_read = rrdset_create_localhost( "services" , "merged_io_ops_read" @@ -3343,13 +3256,9 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_merged_ops_read); if(unlikely(!st_merged_ops_write)) { - st_merged_ops_write = rrdset_create_localhost( "services" , "merged_io_ops_write" @@ -3364,10 +3273,7 @@ void update_systemd_services_charts( , update_every , RRDSET_TYPE_STACKED ); - } - else - rrdset_next(st_merged_ops_write); } // update the values @@ -3595,7 +3501,7 @@ static inline char *cgroup_chart_type(char *buffer, const char *id, size_t len) if(id[0] == '\0' || (id[0] == '/' && id[1] == '\0')) strncpy(buffer, "cgroup_root", len); else - snprintfz(buffer, len, "cgroup_%s", id); + snprintfz(buffer, len, "%s%s", cgroup_chart_id_prefix, id); netdata_fix_chart_id(buffer); return buffer; @@ -3706,10 +3612,10 @@ cpu_limits2_err: } } -static inline int update_memory_limits(char **filename, RRDSETVAR **chart_var, unsigned long long *value, const char *chart_var_name, struct cgroup *cg) { +static inline int update_memory_limits(char **filename, const RRDSETVAR_ACQUIRED **chart_var, unsigned long long *value, const char *chart_var_name, struct cgroup *cg) { if(*filename) { if(unlikely(!*chart_var)) { - *chart_var = rrdsetvar_custom_chart_variable_create(cg->st_mem_usage, chart_var_name); + *chart_var = rrdsetvar_custom_chart_variable_add_and_acquire(cg->st_mem_usage, chart_var_name); if(!*chart_var) { error("Cannot create cgroup %s chart variable '%s'. Will not update its limit anymore.", cg->id, chart_var_name); freez(*filename); @@ -3725,7 +3631,7 @@ static inline int update_memory_limits(char **filename, RRDSETVAR **chart_var, u *filename = NULL; } else { - rrdsetvar_custom_chart_variable_set(*chart_var, (NETDATA_DOUBLE)(*value / (1024 * 1024))); + rrdsetvar_custom_chart_variable_set(cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value / (1024 * 1024))); return 1; } } else { @@ -3740,11 +3646,11 @@ static inline int update_memory_limits(char **filename, RRDSETVAR **chart_var, u char *s = "max\n\0"; if(strcmp(s, buffer) == 0){ *value = UINT64_MAX; - rrdsetvar_custom_chart_variable_set(*chart_var, (NETDATA_DOUBLE)(*value / (1024 * 1024))); + rrdsetvar_custom_chart_variable_set(cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value / (1024 * 1024))); return 1; } *value = str2ull(buffer); - rrdsetvar_custom_chart_variable_set(*chart_var, (NETDATA_DOUBLE)(*value / (1024 * 1024))); + rrdsetvar_custom_chart_variable_set(cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value / (1024 * 1024))); return 1; } } @@ -3824,8 +3730,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_cpu, "system", NULL, 100, 1000000, RRD_ALGORITHM_INCREMENTAL); } } - else - rrdset_next(cg->st_cpu); rrddim_set(cg->st_cpu, "user", cg->cpuacct_stat.user); rrddim_set(cg->st_cpu, "system", cg->cpuacct_stat.system); @@ -3841,7 +3745,7 @@ void update_cgroup_charts(int update_every) { } if(unlikely(!cg->chart_var_cpu_limit)) { - cg->chart_var_cpu_limit = rrdsetvar_custom_chart_variable_create(cg->st_cpu, "cpu_limit"); + cg->chart_var_cpu_limit = rrdsetvar_custom_chart_variable_add_and_acquire(cg->st_cpu, "cpu_limit"); if(!cg->chart_var_cpu_limit) { error("Cannot create cgroup %s chart variable 'cpu_limit'. Will not update its limit anymore.", cg->id); if(cg->filename_cpuset_cpus) freez(cg->filename_cpuset_cpus); @@ -3866,8 +3770,6 @@ void update_cgroup_charts(int update_every) { value = (NETDATA_DOUBLE)cg->cpuset_cpus * 100; } if(likely(value)) { - rrdsetvar_custom_chart_variable_set(cg->chart_var_cpu_limit, value); - if(unlikely(!cg->st_cpu_limit)) { snprintfz(title, CHART_TITLE_MAX, "CPU Usage within the limits"); @@ -3894,8 +3796,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_cpu_limit, "used", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE); cg->prev_cpu_usage = (NETDATA_DOUBLE)(cg->cpuacct_stat.user + cg->cpuacct_stat.system) * 100; } - else - rrdset_next(cg->st_cpu_limit); NETDATA_DOUBLE cpu_usage = 0; cpu_usage = (NETDATA_DOUBLE)(cg->cpuacct_stat.user + cg->cpuacct_stat.system) * 100; @@ -3907,14 +3807,15 @@ void update_cgroup_charts(int update_every) { cg->prev_cpu_usage = cpu_usage; + rrdsetvar_custom_chart_variable_set(cg->st_cpu, cg->chart_var_cpu_limit, value); rrdset_done(cg->st_cpu_limit); } else { - rrdsetvar_custom_chart_variable_set(cg->chart_var_cpu_limit, NAN); if(unlikely(cg->st_cpu_limit)) { rrdset_is_obsolete(cg->st_cpu_limit); cg->st_cpu_limit = NULL; } + rrdsetvar_custom_chart_variable_set(cg->st_cpu, cg->chart_var_cpu_limit, NAN); } } } @@ -3942,7 +3843,6 @@ void update_cgroup_charts(int update_every) { rrdset_update_rrdlabels(cg->st_cpu_nr_throttled, cg->chart_labels); rrddim_add(cg->st_cpu_nr_throttled, "throttled", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else { - rrdset_next(cg->st_cpu_nr_throttled); rrddim_set(cg->st_cpu_nr_throttled, "throttled", cg->cpuacct_cpu_throttling.nr_throttled_perc); rrdset_done(cg->st_cpu_nr_throttled); } @@ -3968,7 +3868,6 @@ void update_cgroup_charts(int update_every) { rrdset_update_rrdlabels(cg->st_cpu_throttled_time, cg->chart_labels); rrddim_add(cg->st_cpu_throttled_time, "duration", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL); } else { - rrdset_next(cg->st_cpu_throttled_time); rrddim_set(cg->st_cpu_throttled_time, "duration", cg->cpuacct_cpu_throttling.throttled_time); rrdset_done(cg->st_cpu_throttled_time); } @@ -3996,7 +3895,6 @@ void update_cgroup_charts(int update_every) { rrdset_update_rrdlabels(cg->st_cpu_shares, cg->chart_labels); rrddim_add(cg->st_cpu_shares, "shares", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else { - rrdset_next(cg->st_cpu_shares); rrddim_set(cg->st_cpu_shares, "shares", cg->cpuacct_cpu_shares.shares); rrdset_done(cg->st_cpu_shares); } @@ -4031,8 +3929,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRD_ALGORITHM_INCREMENTAL); } } - else - rrdset_next(cg->st_cpu_per_core); for(i = 0; i < cg->cpuacct_usage.cpus ;i++) { snprintfz(id, RRD_ID_LENGTH_MAX, "cpu%u", i); @@ -4080,8 +3976,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_mem, "file", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } } - else - rrdset_next(cg->st_mem); if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) { rrddim_set(cg->st_mem, "cache", cg->memory.total_cache); @@ -4127,8 +4021,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(cg->st_writeback); if(cg->memory.detailed_has_dirty) rrddim_set(cg->st_writeback, "dirty", cg->memory.total_dirty); @@ -4160,8 +4052,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_mem_activity, "pgpgin", "in", system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_add(cg->st_mem_activity, "pgpgout", "out", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(cg->st_mem_activity); rrddim_set(cg->st_mem_activity, "pgpgin", cg->memory.total_pgpgin); rrddim_set(cg->st_mem_activity, "pgpgout", cg->memory.total_pgpgout); @@ -4191,8 +4081,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_pgfaults, "pgfault", NULL, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(cg->st_pgfaults); rrddim_set(cg->st_pgfaults, "pgfault", cg->memory.total_pgfault); rrddim_set(cg->st_pgfaults, "pgmajfault", cg->memory.total_pgmajfault); @@ -4223,8 +4111,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(cg->st_mem_usage); rrddim_set(cg->st_mem_usage, "ram", cg->memory.usage_in_bytes); if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) { @@ -4290,8 +4176,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_mem_usage_limit, "available", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); rrddim_add(cg->st_mem_usage_limit, "used", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(cg->st_mem_usage_limit); rrdset_isnot_obsolete(cg->st_mem_usage_limit); @@ -4320,8 +4204,7 @@ void update_cgroup_charts(int update_every) { rrdset_update_rrdlabels(cg->st_mem_utilization, cg->chart_labels); rrddim_add(cg->st_mem_utilization, "utilization", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(cg->st_mem_utilization); + } if (memory_limit) { rrdset_isnot_obsolete(cg->st_mem_utilization); @@ -4370,8 +4253,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(cg->st_mem_failcnt); rrddim_set(cg->st_mem_failcnt, "failures", cg->memory.failcnt); rrdset_done(cg->st_mem_failcnt); @@ -4401,8 +4282,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(cg->st_io); rrddim_set(cg->st_io, "read", cg->io_service_bytes.Read); rrddim_set(cg->st_io, "write", cg->io_service_bytes.Write); @@ -4433,8 +4312,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(cg->st_serviced_ops); rrddim_set(cg->st_serviced_ops, "read", cg->io_serviced.Read); rrddim_set(cg->st_serviced_ops, "write", cg->io_serviced.Write); @@ -4465,8 +4342,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(cg->st_throttle_io); rrddim_set(cg->st_throttle_io, "read", cg->throttle_io_service_bytes.Read); rrddim_set(cg->st_throttle_io, "write", cg->throttle_io_service_bytes.Write); @@ -4497,8 +4372,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(cg->st_throttle_serviced_ops); rrddim_set(cg->st_throttle_serviced_ops, "read", cg->throttle_io_serviced.Read); rrddim_set(cg->st_throttle_serviced_ops, "write", cg->throttle_io_serviced.Write); @@ -4529,8 +4402,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(cg->st_queued_ops); rrddim_set(cg->st_queued_ops, "read", cg->io_queued.Read); rrddim_set(cg->st_queued_ops, "write", cg->io_queued.Write); @@ -4561,8 +4432,6 @@ void update_cgroup_charts(int update_every) { rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(cg->st_merged_ops); rrddim_set(cg->st_merged_ops, "read", cg->io_merged.Read); rrddim_set(cg->st_merged_ops, "write", cg->io_merged.Write); @@ -4597,9 +4466,8 @@ void update_cgroup_charts(int update_every) { pcs->share_time.rd10 = rrddim_add(chart, "some 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd60 = rrddim_add(chart, "some 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd300 = rrddim_add(chart, "some 300", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); - } else { - rrdset_next(pcs->share_time.st); } + if (unlikely(!pcs->total_time.st)) { RRDSET *chart; snprintfz(title, CHART_TITLE_MAX, "CPU some pressure stall time"); @@ -4619,9 +4487,8 @@ void update_cgroup_charts(int update_every) { ); rrdset_update_rrdlabels(chart = pcs->total_time.st, cg->chart_labels); pcs->total_time.rdtotal = rrddim_add(chart, "time", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else { - rrdset_next(pcs->total_time.st); } + update_pressure_charts(pcs); } if (likely(res->updated && res->full.enabled)) { @@ -4649,9 +4516,8 @@ void update_cgroup_charts(int update_every) { pcs->share_time.rd10 = rrddim_add(chart, "full 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd60 = rrddim_add(chart, "full 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd300 = rrddim_add(chart, "full 300", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); - } else { - rrdset_next(pcs->share_time.st); } + if (unlikely(!pcs->total_time.st)) { RRDSET *chart; snprintfz(title, CHART_TITLE_MAX, "CPU full pressure stall time"); @@ -4671,9 +4537,8 @@ void update_cgroup_charts(int update_every) { ); rrdset_update_rrdlabels(chart = pcs->total_time.st, cg->chart_labels); pcs->total_time.rdtotal = rrddim_add(chart, "time", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else { - rrdset_next(pcs->total_time.st); } + update_pressure_charts(pcs); } @@ -4704,9 +4569,8 @@ void update_cgroup_charts(int update_every) { pcs->share_time.rd10 = rrddim_add(chart, "some 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd60 = rrddim_add(chart, "some 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd300 = rrddim_add(chart, "some 300", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); - } else { - rrdset_next(pcs->share_time.st); } + if (unlikely(!pcs->total_time.st)) { RRDSET *chart; snprintfz(title, CHART_TITLE_MAX, "Memory some pressure stall time"); @@ -4726,9 +4590,8 @@ void update_cgroup_charts(int update_every) { ); rrdset_update_rrdlabels(chart = pcs->total_time.st, cg->chart_labels); pcs->total_time.rdtotal = rrddim_add(chart, "time", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else { - rrdset_next(pcs->total_time.st); } + update_pressure_charts(pcs); } @@ -4759,9 +4622,8 @@ void update_cgroup_charts(int update_every) { pcs->share_time.rd10 = rrddim_add(chart, "full 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd60 = rrddim_add(chart, "full 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd300 = rrddim_add(chart, "full 300", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); - } else { - rrdset_next(pcs->share_time.st); } + if (unlikely(!pcs->total_time.st)) { RRDSET *chart; snprintfz(title, CHART_TITLE_MAX, "Memory full pressure stall time"); @@ -4781,9 +4643,8 @@ void update_cgroup_charts(int update_every) { ); rrdset_update_rrdlabels(chart = pcs->total_time.st, cg->chart_labels); pcs->total_time.rdtotal = rrddim_add(chart, "time", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else { - rrdset_next(pcs->total_time.st); } + update_pressure_charts(pcs); } @@ -4814,9 +4675,8 @@ void update_cgroup_charts(int update_every) { pcs->share_time.rd10 = rrddim_add(chart, "some 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd60 = rrddim_add(chart, "some 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd300 = rrddim_add(chart, "some 300", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); - } else { - rrdset_next(pcs->share_time.st); } + if (unlikely(!pcs->total_time.st)) { RRDSET *chart; snprintfz(title, CHART_TITLE_MAX, "I/O some pressure stall time"); @@ -4836,9 +4696,8 @@ void update_cgroup_charts(int update_every) { ); rrdset_update_rrdlabels(chart = pcs->total_time.st, cg->chart_labels); pcs->total_time.rdtotal = rrddim_add(chart, "time", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else { - rrdset_next(pcs->total_time.st); } + update_pressure_charts(pcs); } @@ -4867,9 +4726,8 @@ void update_cgroup_charts(int update_every) { pcs->share_time.rd10 = rrddim_add(chart, "full 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd60 = rrddim_add(chart, "full 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd300 = rrddim_add(chart, "full 300", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); - } else { - rrdset_next(pcs->share_time.st); } + if (unlikely(!pcs->total_time.st)) { RRDSET *chart; snprintfz(title, CHART_TITLE_MAX, "I/O full pressure stall time"); @@ -4889,9 +4747,8 @@ void update_cgroup_charts(int update_every) { ); rrdset_update_rrdlabels(chart = pcs->total_time.st, cg->chart_labels); pcs->total_time.rdtotal = rrddim_add(chart, "time", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else { - rrdset_next(pcs->total_time.st); } + update_pressure_charts(pcs); } } diff --git a/collectors/cgroups.plugin/tests/test_doubles.c b/collectors/cgroups.plugin/tests/test_doubles.c index 6203d444c..498f649f5 100644 --- a/collectors/cgroups.plugin/tests/test_doubles.c +++ b/collectors/cgroups.plugin/tests/test_doubles.c @@ -101,7 +101,7 @@ collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number return 0; } -RRDSETVAR *rrdsetvar_custom_chart_variable_create(RRDSET *st, const char *name) +const RRDSETVAR_ACQUIRED *rrdsetvar_custom_chart_variable_add_and_acquire(RRDSET *st, const char *name) { UNUSED(st); UNUSED(name); @@ -109,9 +109,10 @@ RRDSETVAR *rrdsetvar_custom_chart_variable_create(RRDSET *st, const char *name) return NULL; } -void rrdsetvar_custom_chart_variable_set(RRDSETVAR *rs, NETDATA_DOUBLE value) +void rrdsetvar_custom_chart_variable_set(RRDSET *st, const RRDSETVAR_ACQUIRED *rsa, NETDATA_DOUBLE value) { - UNUSED(rs); + UNUSED(st); + UNUSED(rsa); UNUSED(value); } @@ -146,10 +147,11 @@ void netdev_rename_device_del(const char *host_device) UNUSED(host_device); } -void sql_store_chart_label(uuid_t *chart_uuid, int source_type, char *label, char *value) +void rrdcalc_update_rrdlabels(RRDSET *st) { + (void)st; +} + +void db_execute(const char *cmd) { - UNUSED(chart_uuid); - UNUSED(source_type); - UNUSED(label); - UNUSED(value); + UNUSED(cmd); } diff --git a/collectors/charts.d.plugin/sensors/README.md b/collectors/charts.d.plugin/sensors/README.md index ab7a8b660..1b98b1a70 100644 --- a/collectors/charts.d.plugin/sensors/README.md +++ b/collectors/charts.d.plugin/sensors/README.md @@ -5,17 +5,12 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/collectors/chart # Linux machine sensors monitoring with Netdata -> THIS MODULE IS OBSOLETE. -> USE [THE PYTHON ONE](/collectors/python.d.plugin/sensors) - IT SUPPORTS MULTIPLE JOBS AND IT IS MORE EFFICIENT -> -> Unlike the python one, this module can collect temperature on RPi. +Use this collector when `lm-sensors` doesn't work on your device (e.g. for RPi temperatures). +For all other cases use the [Python collector](/collectors/python.d.plugin/sensors), which supports multiple +jobs, is more efficient and performs calculations on top of the kernel provided values. -The plugin will provide charts for all configured system sensors - -> This plugin is reading sensors directly from the kernel. -> The `lm-sensors` package is able to perform calculations on the -> kernel provided values, this plugin will not perform. -> So, the values graphed, are the raw hardware values of the sensors. +This plugin will provide charts for all configured system sensors, by reading sensors directly from the kernel. +The values graphed are the raw hardware values of the sensors. The plugin will create Netdata charts for: diff --git a/collectors/checks.plugin/Makefile.am b/collectors/checks.plugin/Makefile.am deleted file mode 100644 index 161784b8f..000000000 --- a/collectors/checks.plugin/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in - -dist_noinst_DATA = \ - README.md \ - $(NULL) diff --git a/collectors/checks.plugin/README.md b/collectors/checks.plugin/README.md deleted file mode 100644 index 6e2cf5dea..000000000 --- a/collectors/checks.plugin/README.md +++ /dev/null @@ -1,10 +0,0 @@ - - -# checks.plugin - -A debugging plugin (by default it is disabled) - - diff --git a/collectors/checks.plugin/plugin_checks.c b/collectors/checks.plugin/plugin_checks.c deleted file mode 100644 index 312515115..000000000 --- a/collectors/checks.plugin/plugin_checks.c +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "daemon/common.h" - -static void checks_main_cleanup(void *ptr) { - struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; - static_thread->enabled = NETDATA_MAIN_THREAD_EXITING; - - info("cleaning up..."); - - static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; -} - -void *checks_main(void *ptr) { - netdata_thread_cleanup_push(checks_main_cleanup, ptr); - - usec_t usec = 0, susec = localhost->rrd_update_every * USEC_PER_SEC, loop_usec = 0, total_susec = 0; - struct timeval now, last, loop; - - RRDSET *check1, *check2, *check3, *apps_cpu = NULL; - - check1 = rrdset_create_localhost( - "netdata" - , "check1" - , NULL - , "netdata" - , NULL - , "Caller gives microseconds" - , "a million !" - , "checks.plugin" - , "" - , NETDATA_CHART_PRIO_CHECKS - , localhost->rrd_update_every - , RRDSET_TYPE_LINE - ); - - rrddim_add(check1, "absolute", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); - rrddim_add(check1, "incremental", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - - check2 = rrdset_create_localhost( - "netdata" - , "check2" - , NULL - , "netdata" - , NULL - , "Netdata calcs microseconds" - , "a million !" - , "checks.plugin" - , "" - , NETDATA_CHART_PRIO_CHECKS - , localhost->rrd_update_every - , RRDSET_TYPE_LINE - ); - rrddim_add(check2, "absolute", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); - rrddim_add(check2, "incremental", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - - check3 = rrdset_create_localhost( - "netdata" - , "checkdt" - , NULL - , "netdata" - , NULL - , "Clock difference" - , "microseconds diff" - , "checks.plugin" - , "" - , NETDATA_CHART_PRIO_CHECKS - , localhost->rrd_update_every - , RRDSET_TYPE_LINE - ); - rrddim_add(check3, "caller", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - rrddim_add(check3, "netdata", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - rrddim_add(check3, "apps.plugin", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - - now_realtime_timeval(&last); - while(!netdata_exit) { - usleep(susec); - - // find the time to sleep in order to wait exactly update_every seconds - now_realtime_timeval(&now); - loop_usec = dt_usec(&now, &last); - usec = loop_usec - susec; - debug(D_PROCNETDEV_LOOP, "CHECK: last loop took %llu usec (worked for %llu, slept for %llu).", loop_usec, usec, susec); - - if(usec < (localhost->rrd_update_every * USEC_PER_SEC / 2ULL)) susec = (localhost->rrd_update_every * USEC_PER_SEC) - usec; - else susec = localhost->rrd_update_every * USEC_PER_SEC / 2ULL; - - // -------------------------------------------------------------------- - // Calculate loop time - - last.tv_sec = now.tv_sec; - last.tv_usec = now.tv_usec; - total_susec += loop_usec; - - // -------------------------------------------------------------------- - // check chart 1 - - if(check1->counter_done) rrdset_next_usec(check1, loop_usec); - rrddim_set(check1, "absolute", 1000000); - rrddim_set(check1, "incremental", total_susec); - rrdset_done(check1); - - // -------------------------------------------------------------------- - // check chart 2 - - if(check2->counter_done) rrdset_next(check2); - rrddim_set(check2, "absolute", 1000000); - rrddim_set(check2, "incremental", total_susec); - rrdset_done(check2); - - // -------------------------------------------------------------------- - // check chart 3 - - if(!apps_cpu) apps_cpu = rrdset_find_localhost("apps.cpu"); - if(check3->counter_done) rrdset_next_usec(check3, loop_usec); - now_realtime_timeval(&loop); - rrddim_set(check3, "caller", (long long) dt_usec(&loop, &check1->last_collected_time)); - rrddim_set(check3, "netdata", (long long) dt_usec(&loop, &check2->last_collected_time)); - if(apps_cpu) rrddim_set(check3, "apps.plugin", (long long) dt_usec(&loop, &apps_cpu->last_collected_time)); - rrdset_done(check3); - } - - netdata_thread_cleanup_pop(1); - return NULL; -} diff --git a/collectors/cups.plugin/README.md b/collectors/cups.plugin/README.md index e3d2e1661..f3b2a28d1 100644 --- a/collectors/cups.plugin/README.md +++ b/collectors/cups.plugin/README.md @@ -11,9 +11,11 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/collectors/cups. This plugin needs a running local CUPS daemon (`cupsd`). This plugin does not need any configuration. Supports cups since version 1.7. +If you installed Netdata using our native packages, you will have to additionaly install `netdata-plugin-cups` to use this plugin for data collection. It is not installed by default due to the large number of dependencies it requires. + ## Charts -`cups.plugin` provides one common section `destinations` and one section per destination. +`cups.plugin` provides one common section `destinations` and one section per destination. > Destinations in CUPS represent individual printers or classes (collections or pools) of printers () diff --git a/collectors/cups.plugin/cups_plugin.c b/collectors/cups.plugin/cups_plugin.c index 77bd3e0ed..9a200c31d 100644 --- a/collectors/cups.plugin/cups_plugin.c +++ b/collectors/cups.plugin/cups_plugin.c @@ -137,10 +137,7 @@ getIntegerOption( return ((int)intvalue); } -static int reset_job_metrics(const char *name, void *entry, void *data) { - (void)name; - (void)data; - +static int reset_job_metrics(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data __maybe_unused) { struct job_metrics *jm = (struct job_metrics *)entry; jm->is_collected = 0; @@ -175,8 +172,8 @@ struct job_metrics *get_job_metrics(char *dest) { return jm; } -int collect_job_metrics(const char *name, void *entry, void *data) { - (void)data; +int collect_job_metrics(const DICTIONARY_ITEM *item, void *entry, void *data __maybe_unused) { + const char *name = dictionary_acquired_item_name(item); struct job_metrics *jm = (struct job_metrics *)entry; @@ -205,7 +202,7 @@ int collect_job_metrics(const char *name, void *entry, void *data) { printf("DIMENSION pending '' absolute 1 1\n"); printf("DIMENSION held '' absolute 1 1\n"); printf("DIMENSION processing '' absolute 1 1\n"); - dictionary_del_having_write_lock(dict_dest_job_metrics, name); + dictionary_del(dict_dest_job_metrics, name); } return 0; @@ -243,7 +240,7 @@ int main(int argc, char **argv) { errno = 0; - dict_dest_job_metrics = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + dict_dest_job_metrics = dictionary_create(DICT_OPTION_SINGLE_THREADED); // ------------------------------------------------------------------------ // the main loop diff --git a/collectors/diskspace.plugin/plugin_diskspace.c b/collectors/diskspace.plugin/plugin_diskspace.c index 5bdf8bc61..5f610983b 100644 --- a/collectors/diskspace.plugin/plugin_diskspace.c +++ b/collectors/diskspace.plugin/plugin_diskspace.c @@ -95,8 +95,8 @@ int mount_point_cleanup(const char *name, void *entry, int slow) { return 0; } -int mount_point_cleanup_cb(const char *name, void *entry, void *data) { - UNUSED(data); +int mount_point_cleanup_cb(const DICTIONARY_ITEM *item, void *entry, void *data __maybe_unused) { + const char *name = dictionary_acquired_item_name(item); return mount_point_cleanup(name, (struct mount_point_metadata *)entry, 0); } @@ -204,8 +204,6 @@ static void calculate_values_and_show_charts( error("DISKSPACE: disk inode statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mi->mount_point, disk, (unsigned long long)ftotal, (unsigned long long)favail, (unsigned long long)freserved_root, (unsigned long long)fused); #endif - // -------------------------------------------------------------------------- - int rendered = 0; if(m->do_space == CONFIG_BOOLEAN_YES || (m->do_space == CONFIG_BOOLEAN_AUTO && @@ -239,8 +237,6 @@ static void calculate_values_and_show_charts( m->rd_space_used = rrddim_add(m->st_space, "used", NULL, (collected_number)bsize, 1024 * 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root", (collected_number)bsize, 1024 * 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(m->st_space); rrddim_set_by_pointer(m->st_space, m->rd_space_avail, (collected_number)bavail); rrddim_set_by_pointer(m->st_space, m->rd_space_used, (collected_number)bused); @@ -250,8 +246,6 @@ static void calculate_values_and_show_charts( rendered++; } - // -------------------------------------------------------------------------- - if(m->do_inodes == CONFIG_BOOLEAN_YES || (m->do_inodes == CONFIG_BOOLEAN_AUTO && (favail || freserved_root || fused || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -283,8 +277,6 @@ static void calculate_values_and_show_charts( m->rd_inodes_used = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); m->rd_inodes_reserved = rrddim_add(m->st_inodes, "reserved_for_root", "reserved for root", 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(m->st_inodes); rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_avail, (collected_number)favail); rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_used, (collected_number)fused); @@ -294,8 +286,6 @@ static void calculate_values_and_show_charts( rendered++; } - // -------------------------------------------------------------------------- - if(likely(rendered)) m->collected++; } @@ -330,7 +320,7 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { , SIMPLE_PATTERN_EXACT ); - dict_mountpoints = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + dict_mountpoints = dictionary_create(DICT_OPTION_SINGLE_THREADED); } struct mount_point_metadata *m = dictionary_get(dict_mountpoints, mi->mount_point); @@ -521,12 +511,19 @@ void *diskspace_slow_worker(void *ptr) netdata_thread_cleanup_push(diskspace_slow_worker_cleanup, data->slow_thread); usec_t step = slow_update_every * USEC_PER_SEC; + usec_t real_step = USEC_PER_SEC; heartbeat_t hb; heartbeat_init(&hb); while(!netdata_exit) { worker_is_idle(); - heartbeat_next(&hb, step); + heartbeat_next(&hb, USEC_PER_SEC); + + if (real_step < step) { + real_step += USEC_PER_SEC; + continue; + } + real_step = USEC_PER_SEC; usec_t start_time = now_monotonic_high_precision_usec(); @@ -581,6 +578,7 @@ void *diskspace_slow_worker(void *ptr) } static void diskspace_main_cleanup(void *ptr) { + rrd_collector_finished(); worker_unregister(); struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; @@ -612,6 +610,8 @@ void *diskspace_main(void *ptr) { worker_register_job_name(WORKER_JOB_MOUNTPOINT, "mountpoint"); worker_register_job_name(WORKER_JOB_CLEANUP, "cleanup"); + rrd_collector_started(); + netdata_thread_cleanup_push(diskspace_main_cleanup, ptr); cleanup_mount_points = config_get_boolean(CONFIG_SECTION_DISKSPACE, "remove charts of unmounted disks" , cleanup_mount_points); diff --git a/collectors/ebpf.plugin/README.md b/collectors/ebpf.plugin/README.md index 550982ad2..7762ed34f 100644 --- a/collectors/ebpf.plugin/README.md +++ b/collectors/ebpf.plugin/README.md @@ -137,6 +137,25 @@ If you do not need to monitor specific metrics for your `cgroups`, you can enabl `ebpf.d.conf`, and then disable the plugin for a specific `thread` by following the steps in the [Configuration](#configuring-ebpfplugin) section. +#### Collect PID + +When one of the previous integrations is enabled, `ebpf.plugin` will use Process Identifier (`PID`) to identify the +process group for which it needs to plot data. + +There are different ways to collect PID, and you can select the way `ebpf.plugin` collects data with the following +values: + +- `real parent`: This is the default mode. Collection will aggregate data for the real parent, the thread that creates + child threads. +- `parent`: Parent and real parent are the same when a process starts, but this value can be changed during run time. +- `all`: This option will store all PIDs that run on the host. Note, this method can be expensive for the host, + because more memory needs to be allocated and parsed. + +The threads that have integration with other collectors have an internal clean up wherein they attach either a +`trampoline` or a `kprobe` to `release_task` internal function. To avoid `overload` on this function, `ebpf.plugin` +will only enable these threads integrated with other collectors when the kernel is compiled with +`CONFIG_DEBUG_INFO_BTF`, unless you enable them manually. + #### Integration Dashboard Elements When an integration is enabled, your dashboard will also show the following cgroups and apps charts using low-level diff --git a/collectors/ebpf.plugin/ebpf.c b/collectors/ebpf.plugin/ebpf.c index 65c96f672..00b53a57d 100644 --- a/collectors/ebpf.plugin/ebpf.c +++ b/collectors/ebpf.plugin/ebpf.c @@ -27,177 +27,317 @@ struct config collector_config = { .first_section = NULL, int running_on_kernel = 0; int ebpf_nprocs; int isrh = 0; +int main_thread_id = 0; pthread_mutex_t lock; +pthread_mutex_t ebpf_exit_cleanup; pthread_mutex_t collect_data_mutex; pthread_cond_t collect_data_cond_var; ebpf_module_t ebpf_modules[] = { { .thread_name = "process", .config_name = "process", .enabled = 0, .start_routine = ebpf_process_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_level = NETDATA_APPS_LEVEL_REAL_PARENT, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = ebpf_process_create_apps_charts, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &process_config, .config_file = NETDATA_PROCESS_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_10, - .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_10 | + NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "socket", .config_name = "socket", .enabled = 0, .start_routine = ebpf_socket_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_level = NETDATA_APPS_LEVEL_REAL_PARENT, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = ebpf_socket_create_apps_charts, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &socket_config, .config_file = NETDATA_NETWORK_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = socket_targets, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = socket_targets, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "cachestat", .config_name = "cachestat", .enabled = 0, .start_routine = ebpf_cachestat_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, - .apps_routine = ebpf_cachestat_create_apps_charts, .maps = NULL, + .apps_level = NETDATA_APPS_LEVEL_REAL_PARENT, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_routine = ebpf_cachestat_create_apps_charts, .maps = cachestat_maps, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &cachestat_config, .config_file = NETDATA_CACHESTAT_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18| NETDATA_V5_4 | NETDATA_V5_15 | - NETDATA_V5_16, - .load = EBPF_LOAD_LEGACY, .targets = cachestat_targets, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18| + NETDATA_V5_4 | NETDATA_V5_14 | NETDATA_V5_15 | NETDATA_V5_16, + .load = EBPF_LOAD_LEGACY, .targets = cachestat_targets, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "sync", .config_name = "sync", .enabled = 0, .start_routine = ebpf_sync_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = NULL, .maps = NULL, - .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &sync_config, + .apps_level = NETDATA_APPS_NOT_SET, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_routine = NULL, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &sync_config, .config_file = NETDATA_SYNC_CONFIG_FILE, // All syscalls have the same kernels - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = sync_targets, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = sync_targets, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "dc", .config_name = "dc", .enabled = 0, .start_routine = ebpf_dcstat_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, - .apps_routine = ebpf_dcstat_create_apps_charts, .maps = NULL, + .apps_level = NETDATA_APPS_LEVEL_REAL_PARENT, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_routine = ebpf_dcstat_create_apps_charts, .maps = dcstat_maps, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &dcstat_config, .config_file = NETDATA_DIRECTORY_DCSTAT_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = dc_targets, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = dc_targets, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "swap", .config_name = "swap", .enabled = 0, .start_routine = ebpf_swap_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_level = NETDATA_APPS_LEVEL_REAL_PARENT, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = ebpf_swap_create_apps_charts, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &swap_config, .config_file = NETDATA_DIRECTORY_SWAP_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = swap_targets, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = swap_targets, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "vfs", .config_name = "vfs", .enabled = 0, .start_routine = ebpf_vfs_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_level = NETDATA_APPS_LEVEL_REAL_PARENT, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = ebpf_vfs_create_apps_charts, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &vfs_config, .config_file = NETDATA_DIRECTORY_VFS_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = vfs_targets, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "filesystem", .config_name = "filesystem", .enabled = 0, .start_routine = ebpf_filesystem_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = NULL, .maps = NULL, - .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &fs_config, + .apps_level = NETDATA_APPS_NOT_SET, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_routine = NULL, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &fs_config, .config_file = NETDATA_FILESYSTEM_CONFIG_FILE, //We are setting kernels as zero, because we load eBPF programs according the kernel running. - .kernels = 0, .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL }, + .kernels = 0, .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL, .thread = NULL }, { .thread_name = "disk", .config_name = "disk", .enabled = 0, .start_routine = ebpf_disk_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = NULL, .maps = NULL, - .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &disk_config, + .apps_level = NETDATA_APPS_NOT_SET, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_routine = NULL, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &disk_config, .config_file = NETDATA_DISK_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "mount", .config_name = "mount", .enabled = 0, .start_routine = ebpf_mount_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = NULL, .maps = NULL, - .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &mount_config, + .apps_level = NETDATA_APPS_NOT_SET, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_routine = NULL, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &mount_config, .config_file = NETDATA_MOUNT_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = mount_targets, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = mount_targets, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "fd", .config_name = "fd", .enabled = 0, .start_routine = ebpf_fd_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_level = NETDATA_APPS_LEVEL_REAL_PARENT, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = ebpf_fd_create_apps_charts, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &fd_config, .config_file = NETDATA_FD_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_11, - .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_11 | + NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = fd_targets, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "hardirq", .config_name = "hardirq", .enabled = 0, .start_routine = ebpf_hardirq_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = NULL, .maps = NULL, - .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &hardirq_config, + .apps_level = NETDATA_APPS_NOT_SET, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_routine = NULL, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &hardirq_config, .config_file = NETDATA_HARDIRQ_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "softirq", .config_name = "softirq", .enabled = 0, .start_routine = ebpf_softirq_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = NULL, .maps = NULL, - .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &softirq_config, + .apps_level = NETDATA_APPS_NOT_SET, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_routine = NULL, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &softirq_config, .config_file = NETDATA_SOFTIRQ_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "oomkill", .config_name = "oomkill", .enabled = 0, .start_routine = ebpf_oomkill_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_level = NETDATA_APPS_LEVEL_REAL_PARENT, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = ebpf_oomkill_create_apps_charts, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &oomkill_config, .config_file = NETDATA_OOMKILL_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "shm", .config_name = "shm", .enabled = 0, .start_routine = ebpf_shm_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_level = NETDATA_APPS_LEVEL_REAL_PARENT, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = ebpf_shm_create_apps_charts, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &shm_config, .config_file = NETDATA_DIRECTORY_SHM_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = shm_targets, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = shm_targets, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = "mdflush", .config_name = "mdflush", .enabled = 0, .start_routine = ebpf_mdflush_thread, .update_every = EBPF_DEFAULT_UPDATE_EVERY, .global_charts = 1, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, - .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = NULL, .maps = NULL, - .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &mdflush_config, + .apps_level = NETDATA_APPS_NOT_SET, .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, + .apps_routine = NULL, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL, .cfg = &mdflush_config, .config_file = NETDATA_DIRECTORY_MDFLUSH_CONFIG_FILE, - .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4, - .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL}, + .kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_14, + .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, .objects = NULL, .thread = NULL}, { .thread_name = NULL, .enabled = 0, .start_routine = NULL, .update_every = EBPF_DEFAULT_UPDATE_EVERY, - .global_charts = 0, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, .cgroup_charts = CONFIG_BOOLEAN_NO, - .mode = MODE_ENTRY, .optional = 0, .apps_routine = NULL, .maps = NULL, .pid_map_size = 0, .names = NULL, - .cfg = NULL, .config_name = NULL, .kernels = 0, .load = EBPF_LOAD_LEGACY, .targets = NULL, .probe_links = NULL, - .objects = NULL}, + .global_charts = 0, .apps_charts = NETDATA_EBPF_APPS_FLAG_NO, .apps_level = NETDATA_APPS_NOT_SET, + .cgroup_charts = CONFIG_BOOLEAN_NO, .mode = MODE_ENTRY, .optional = 0, .apps_routine = NULL, .maps = NULL, + .pid_map_size = 0, .names = NULL, .cfg = NULL, .config_name = NULL, .kernels = 0, .load = EBPF_LOAD_LEGACY, + .targets = NULL, .probe_links = NULL, .objects = NULL, .thread = NULL}, }; struct netdata_static_thread ebpf_threads[] = { - {"EBPF PROCESS", NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF SOCKET" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF CACHESTAT" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF SYNC" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF DCSTAT" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF SWAP" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF VFS" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF FILESYSTEM" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF DISK" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF MOUNT" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF FD" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF HARDIRQ" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF SOFTIRQ" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF OOMKILL" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF SHM" , NULL, NULL, 1, - NULL, NULL, NULL}, - {"EBPF MDFLUSH" , NULL, NULL, 1, - NULL, NULL, NULL}, - {NULL , NULL, NULL, 0, - NULL, NULL, NULL} + { + .name = "EBPF PROCESS", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF SOCKET", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF CACHESTAT", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF SYNC", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF DCSTAT", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF SWAP", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF VFS", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF FILESYSTEM", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF DISK", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF MOUNT", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF FD", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF HARDIRQ", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF SOFTIRQ", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF OOMKILL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF SHM", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = "EBPF MDFLUSH", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, + { + .name = NULL, + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 0, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + }, }; ebpf_filesystem_partitions_t localfs[] = @@ -294,6 +434,7 @@ ebpf_sync_syscalls_t local_syscalls[] = { } }; + // Link with apps.plugin ebpf_process_stat_t *global_process_stat = NULL; @@ -312,6 +453,8 @@ ebpf_plugin_stats_t plugin_statistics = {.core = 0, .legacy = 0, .running = 0, . #ifdef LIBBPF_MAJOR_VERSION struct btf *default_btf = NULL; +#else +void *default_btf = NULL; #endif char *btf_path = NULL; @@ -323,16 +466,16 @@ char *btf_path = NULL; /** * Close the collector gracefully - * - * @param sig is the signal number used to close the collector */ -static void ebpf_exit(int sig) +static void ebpf_exit() { #ifdef LIBBPF_MAJOR_VERSION + pthread_mutex_lock(&ebpf_exit_cleanup); if (default_btf) { btf__free(default_btf); default_btf = NULL; } + pthread_mutex_unlock(&ebpf_exit_cleanup); #endif char filename[FILENAME_MAX + 1]; @@ -340,7 +483,7 @@ static void ebpf_exit(int sig) if (unlink(filename)) error("Cannot remove PID file %s", filename); - exit(sig); + exit(0); } /** @@ -373,44 +516,66 @@ int ebpf_exit_plugin = 0; */ static void ebpf_stop_threads(int sig) { - ebpf_exit_plugin = 1; + UNUSED(sig); + static int only_one = 0; + int i; - for (i = 0; ebpf_threads[i].name != NULL; i++); + // Child thread should be closed by itself. + pthread_mutex_lock(&ebpf_exit_cleanup); + if (main_thread_id != gettid() || only_one) { + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + only_one = 1; + for (i = 0; ebpf_threads[i].name != NULL; i++) { + if (ebpf_threads[i].enabled != NETDATA_THREAD_EBPF_STOPPED) + netdata_thread_cancel(*ebpf_threads[i].thread); + } + pthread_mutex_unlock(&ebpf_exit_cleanup); - usec_t max = 2 * USEC_PER_SEC, step = 100000; + ebpf_exit_plugin = 1; + usec_t max = 3 * USEC_PER_SEC, step = 100000; while (i && max) { max -= step; sleep_usec(step); i = 0; int j; + pthread_mutex_lock(&ebpf_exit_cleanup); for (j = 0; ebpf_threads[j].name != NULL; j++) { - if (ebpf_threads[j].enabled != NETDATA_MAIN_THREAD_EXITED) + if (ebpf_threads[j].enabled != NETDATA_THREAD_EBPF_STOPPED) i++; } + pthread_mutex_unlock(&ebpf_exit_cleanup); } //Unload threads(except sync and filesystem) + pthread_mutex_lock(&ebpf_exit_cleanup); for (i = 0; ebpf_threads[i].name != NULL; i++) { - if (ebpf_threads[i].enabled == NETDATA_MAIN_THREAD_EXITED && i != EBPF_MODULE_FILESYSTEM_IDX && + if (ebpf_threads[i].enabled == NETDATA_THREAD_EBPF_STOPPED && i != EBPF_MODULE_FILESYSTEM_IDX && i != EBPF_MODULE_SYNC_IDX) ebpf_unload_legacy_code(ebpf_modules[i].objects, ebpf_modules[i].probe_links); } + pthread_mutex_unlock(&ebpf_exit_cleanup); //Unload filesystem - if (ebpf_threads[EBPF_MODULE_FILESYSTEM_IDX].enabled == NETDATA_MAIN_THREAD_EXITED) { + pthread_mutex_lock(&ebpf_exit_cleanup); + if (ebpf_threads[EBPF_MODULE_FILESYSTEM_IDX].enabled == NETDATA_THREAD_EBPF_STOPPED) { for (i = 0; localfs[i].filesystem != NULL; i++) { ebpf_unload_legacy_code(localfs[i].objects, localfs[i].probe_links); } } + pthread_mutex_unlock(&ebpf_exit_cleanup); //Unload Sync - if (ebpf_threads[EBPF_MODULE_SYNC_IDX].enabled == NETDATA_MAIN_THREAD_EXITED) { + pthread_mutex_lock(&ebpf_exit_cleanup); + if (ebpf_threads[EBPF_MODULE_SYNC_IDX].enabled == NETDATA_THREAD_EBPF_STOPPED) { for (i = 0; local_syscalls[i].syscall != NULL; i++) { ebpf_unload_legacy_code(local_syscalls[i].objects, local_syscalls[i].probe_links); } } + pthread_mutex_unlock(&ebpf_exit_cleanup); - ebpf_exit(sig); + ebpf_exit(); } /***************************************************************** @@ -1170,6 +1335,7 @@ static void read_local_addresses() int ebpf_start_pthread_variables() { pthread_mutex_init(&lock, NULL); + pthread_mutex_init(&ebpf_exit_cleanup, NULL); pthread_mutex_init(&collect_data_mutex, NULL); if (pthread_cond_init(&collect_data_cond_var, NULL)) { @@ -1261,35 +1427,28 @@ static void ebpf_update_table_size() /** * Set Load mode * - * @param load default load mode. + * @param origin specify the configuration file loaded */ -static inline void ebpf_set_load_mode(netdata_ebpf_load_mode_t load) +static inline void ebpf_set_load_mode(netdata_ebpf_load_mode_t load, netdata_ebpf_load_mode_t origin) { -#ifdef LIBBPF_MAJOR_VERSION - if (load == EBPF_LOAD_CORE || load == EBPF_LOAD_PLAY_DICE) { - load = (!default_btf) ? EBPF_LOAD_LEGACY : EBPF_LOAD_CORE; - } -#else - load = EBPF_LOAD_LEGACY; -#endif - int i; for (i = 0; ebpf_modules[i].thread_name; i++) { - // TO DO: Use `load` variable after we change all threads. - ebpf_modules[i].load = EBPF_LOAD_LEGACY; // load ; + ebpf_modules[i].load &= ~NETDATA_EBPF_LOAD_METHODS; + ebpf_modules[i].load |= load | origin ; } } /** * Update mode * - * @param str value read from configuration file. + * @param str value read from configuration file. + * @param origin specify the configuration file loaded */ -static inline void epbf_update_load_mode(char *str) +static inline void epbf_update_load_mode(char *str, netdata_ebpf_load_mode_t origin) { netdata_ebpf_load_mode_t load = epbf_convert_string_to_load_mode(str); - ebpf_set_load_mode(load); + ebpf_set_load_mode(load, origin); } /** @@ -1297,9 +1456,11 @@ static inline void epbf_update_load_mode(char *str) * * @param disable_apps variable to store information related to apps. * @param disable_cgroups variable to store information related to cgroups. - * @param update_every value to overwrite the update frequency set by the server. + * @param update_every value to overwrite the update frequency set by the server. + * @param origin specify the configuration file loaded */ -static void read_collector_values(int *disable_apps, int *disable_cgroups, int update_every) +static void read_collector_values(int *disable_apps, int *disable_cgroups, + int update_every, netdata_ebpf_load_mode_t origin) { // Read global section char *value; @@ -1321,7 +1482,7 @@ static void read_collector_values(int *disable_apps, int *disable_cgroups, int u value = appconfig_get(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_TYPE_FORMAT, EBPF_CFG_DEFAULT_PROGRAM); - epbf_update_load_mode(value); + epbf_update_load_mode(value, origin); ebpf_update_interval(update_every); @@ -1499,6 +1660,7 @@ static void read_collector_values(int *disable_apps, int *disable_cgroups, int u static int load_collector_config(char *path, int *disable_apps, int *disable_cgroups, int update_every) { char lpath[4096]; + netdata_ebpf_load_mode_t origin; snprintf(lpath, 4095, "%s/%s", path, NETDATA_EBPF_CONFIG_FILE); if (!appconfig_load(&collector_config, lpath, 0, NULL)) { @@ -1506,9 +1668,11 @@ static int load_collector_config(char *path, int *disable_apps, int *disable_cgr if (!appconfig_load(&collector_config, lpath, 0, NULL)) { return -1; } - } + origin = EBPF_LOADED_FROM_STOCK; + } else + origin = EBPF_LOADED_FROM_USER; - read_collector_values(disable_apps, disable_cgroups, update_every); + read_collector_values(disable_apps, disable_cgroups, update_every, origin); return 0; } @@ -1552,7 +1716,7 @@ static inline void ebpf_load_thread_config() { int i; for (i = 0; ebpf_modules[i].thread_name; i++) { - ebpf_update_module(&ebpf_modules[i]); + ebpf_update_module(&ebpf_modules[i], default_btf, running_on_kernel, isrh); } } @@ -1771,14 +1935,14 @@ static void ebpf_parse_args(int argc, char **argv) break; } case EBPF_OPTION_LEGACY: { - ebpf_set_load_mode(EBPF_LOAD_LEGACY); + ebpf_set_load_mode(EBPF_LOAD_LEGACY, EBPF_LOADED_FROM_USER); #ifdef NETDATA_INTERNAL_CHECKS info("EBPF running with \"LEGACY\" code, because it was started with the option \"[-]-legacy\"."); #endif break; } case EBPF_OPTION_CORE: { - ebpf_set_load_mode(EBPF_LOAD_CORE); + ebpf_set_load_mode(EBPF_LOAD_CORE, EBPF_LOADED_FROM_USER); #ifdef NETDATA_INTERNAL_CHECKS info("EBPF running with \"CO-RE\" code, because it was started with the option \"[-]-core\"."); #endif @@ -1816,7 +1980,7 @@ static void ebpf_parse_args(int argc, char **argv) &apps_groups_default_target, &apps_groups_root_target, ebpf_stock_config_dir, "groups")) { error("Cannot read process groups '%s/apps_groups.conf'. There are no internal defaults. Failing.", ebpf_stock_config_dir); - ebpf_exit(1); + ebpf_exit(); } } else info("Loaded config file '%s/apps_groups.conf'", ebpf_user_config_dir); @@ -1995,6 +2159,7 @@ static void ebpf_manage_pid(pid_t pid) int main(int argc, char **argv) { clocks_init(); + main_thread_id = gettid(); set_global_variables(); ebpf_parse_args(argc, argv); @@ -2035,7 +2200,7 @@ int main(int argc, char **argv) if (ebpf_start_pthread_variables()) { error("Cannot start mutex to control overall charts."); - ebpf_exit(5); + ebpf_exit(); } netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX"); @@ -2058,18 +2223,25 @@ int main(int argc, char **argv) int i; for (i = 0; ebpf_threads[i].name != NULL; i++) { struct netdata_static_thread *st = &ebpf_threads[i]; - st->thread = mallocz(sizeof(netdata_thread_t)); ebpf_module_t *em = &ebpf_modules[i]; - em->thread_id = i; - netdata_thread_create(st->thread, st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, em); + em->thread = st; + // We always initialize process, because it is responsible to take care of apps integration + if (em->enabled || !i) { + st->thread = mallocz(sizeof(netdata_thread_t)); + em->thread_id = i; + st->enabled = NETDATA_THREAD_EBPF_RUNNING; + netdata_thread_create(st->thread, st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, em); + } else { + st->enabled = NETDATA_THREAD_EBPF_STOPPED; + } } - usec_t step = 60 * USEC_PER_SEC; + usec_t step = EBPF_DEFAULT_UPDATE_EVERY * USEC_PER_SEC; heartbeat_t hb; heartbeat_init(&hb); //Plugin will be killed when it receives a signal - for (;;) { + while (!ebpf_exit_plugin) { (void)heartbeat_next(&hb, step); } diff --git a/collectors/ebpf.plugin/ebpf.d.conf b/collectors/ebpf.plugin/ebpf.d.conf index aeba473ed..cf5c740fc 100644 --- a/collectors/ebpf.plugin/ebpf.d.conf +++ b/collectors/ebpf.plugin/ebpf.d.conf @@ -26,16 +26,16 @@ # # eBPF Programs # -# The eBPF collector enables and runs the following eBPF programs by default: +# The eBPF collector has the following eBPF programs: # # `cachestat` : Make charts for kernel functions related to page cache. # `dcstat` : Make charts for kernel functions related to directory cache. # `disk` : Monitor I/O latencies for disks # `fd` : This eBPF program creates charts that show information about file manipulation. -# `mdflush` : Monitors flush counts for multi-devices. -# `mount` : Monitor calls for syscalls mount and umount # `filesystem`: Monitor calls for functions used to manipulate specific filesystems # `hardirq` : Monitor latency of serving hardware interrupt requests (hard IRQs). +# `mdflush` : Monitors flush counts for multi-devices. +# `mount` : Monitor calls for syscalls mount and umount # `oomkill` : This eBPF program creates a chart that shows which process got OOM killed and when. # `process` : This eBPF program creates charts that show information about process life. # `shm` : Monitor calls for syscalls shmget, shmat, shmdt and shmctl. @@ -46,6 +46,9 @@ # `swap` : Monitor calls for internal swap functions. # `vfs` : This eBPF program creates charts that show information about process VFS IO, VFS file manipulation and # files removed. +# +# When plugin detects that system has support to BTF, it enables integration with apps.plugin. +# [ebpf programs] cachestat = no dcstat = no @@ -57,7 +60,7 @@ mount = yes oomkill = yes process = yes - shm = yes + shm = no socket = yes softirq = yes sync = yes diff --git a/collectors/ebpf.plugin/ebpf.d/cachestat.conf b/collectors/ebpf.plugin/ebpf.d/cachestat.conf index e2418394e..52466be51 100644 --- a/collectors/ebpf.plugin/ebpf.d/cachestat.conf +++ b/collectors/ebpf.plugin/ebpf.d/cachestat.conf @@ -19,6 +19,11 @@ # `trampoline`: This is the default mode used by the eBPF collector, due the small overhead added to host. # `probe` : This is the same as legacy code. # +# The `collect pid` option defines the PID stored inside hash tables and accepts the following options: +# `real parent`: Only stores real parent inside PID +# `parent` : Only stores parent PID. +# `all` : Stores all PIDs used by software. This is the most expensive option. +# # Uncomment lines to define specific options for thread. [global] # ebpf load mode = entry @@ -28,3 +33,4 @@ # pid table size = 32768 ebpf type format = auto ebpf co-re tracing = trampoline + collect pid = real parent diff --git a/collectors/ebpf.plugin/ebpf.d/dcstat.conf b/collectors/ebpf.plugin/ebpf.d/dcstat.conf index 3986ae4f8..8aed8f783 100644 --- a/collectors/ebpf.plugin/ebpf.d/dcstat.conf +++ b/collectors/ebpf.plugin/ebpf.d/dcstat.conf @@ -17,6 +17,11 @@ # `trampoline`: This is the default mode used by the eBPF collector, due the small overhead added to host. # `probe` : This is the same as legacy code. # +# The `collect pid` option defines the PID stored inside hash tables and accepts the following options: +# `real parent`: Only stores real parent inside PID +# `parent` : Only stores parent PID. +# `all` : Stores all PIDs used by software. This is the most expensive option. +# # Uncomment lines to define specific options for thread. [global] # ebpf load mode = entry @@ -26,3 +31,4 @@ # pid table size = 32768 ebpf type format = auto ebpf co-re tracing = trampoline + collect pid = real parent diff --git a/collectors/ebpf.plugin/ebpf.d/fd.conf b/collectors/ebpf.plugin/ebpf.d/fd.conf index f6edd3d93..8333520fc 100644 --- a/collectors/ebpf.plugin/ebpf.d/fd.conf +++ b/collectors/ebpf.plugin/ebpf.d/fd.conf @@ -11,9 +11,11 @@ # The `pid table size` defines the maximum number of PIDs stored inside the hash table. # # Uncomment lines to define specific options for thread. -#[global] +[global] # ebpf load mode = entry # apps = yes # cgroups = no # update every = 10 # pid table size = 32768 + ebpf type format = auto + ebpf co-re tracing = trampoline diff --git a/collectors/ebpf.plugin/ebpf.d/process.conf b/collectors/ebpf.plugin/ebpf.d/process.conf index f6edd3d93..1da5f84d3 100644 --- a/collectors/ebpf.plugin/ebpf.d/process.conf +++ b/collectors/ebpf.plugin/ebpf.d/process.conf @@ -9,6 +9,11 @@ # the setting `apps` and `cgroups` to 'no'. # # The `pid table size` defines the maximum number of PIDs stored inside the hash table. +# +# The `collect pid` option defines the PID stored inside hash tables and accepts the following options: +# `real parent`: Only stores real parent inside PID +# `parent` : Only stores parent PID. +# `all` : Stores all PIDs used by software. This is the most expensive option. # # Uncomment lines to define specific options for thread. #[global] @@ -17,3 +22,4 @@ # cgroups = no # update every = 10 # pid table size = 32768 +# collect pid = real parent diff --git a/collectors/ebpf.plugin/ebpf.d/vfs.conf b/collectors/ebpf.plugin/ebpf.d/vfs.conf index a65e0acbc..fa5d5b4e9 100644 --- a/collectors/ebpf.plugin/ebpf.d/vfs.conf +++ b/collectors/ebpf.plugin/ebpf.d/vfs.conf @@ -9,9 +9,11 @@ # the setting `apps` and `cgroups` to 'no'. # # Uncomment lines to define specific options for thread. -#[global] +[global] # ebpf load mode = entry # apps = yes # cgroups = no # update every = 10 # pid table size = 32768 + ebpf type format = auto + ebpf co-re tracing = trampoline diff --git a/collectors/ebpf.plugin/ebpf.h b/collectors/ebpf.plugin/ebpf.h index c23ca332d..28b04ce48 100644 --- a/collectors/ebpf.plugin/ebpf.h +++ b/collectors/ebpf.plugin/ebpf.h @@ -163,11 +163,12 @@ enum ebpf_algorithms_list { }; // Threads -extern void *ebpf_process_thread(void *ptr); -extern void *ebpf_socket_thread(void *ptr); +void *ebpf_process_thread(void *ptr); +void *ebpf_socket_thread(void *ptr); // Common variables extern pthread_mutex_t lock; +extern pthread_mutex_t ebpf_exit_cleanup; extern int ebpf_nprocs; extern int running_on_kernel; extern int isrh; @@ -177,14 +178,14 @@ extern pthread_mutex_t collect_data_mutex; extern pthread_cond_t collect_data_cond_var; // Common functions -extern void ebpf_global_labels(netdata_syscall_stat_t *is, +void ebpf_global_labels(netdata_syscall_stat_t *is, netdata_publish_syscall_t *pio, char **dim, char **name, int *algorithm, int end); -extern void ebpf_write_chart_cmd(char *type, +void ebpf_write_chart_cmd(char *type, char *id, char *title, char *units, @@ -195,11 +196,11 @@ extern void ebpf_write_chart_cmd(char *type, int update_every, char *module); -extern void ebpf_write_global_dimension(char *name, char *id, char *algorithm); +void ebpf_write_global_dimension(char *name, char *id, char *algorithm); -extern void ebpf_create_global_dimension(void *ptr, int end); +void ebpf_create_global_dimension(void *ptr, int end); -extern void ebpf_create_chart(char *type, +void ebpf_create_chart(char *type, char *id, char *title, char *units, @@ -213,18 +214,18 @@ extern void ebpf_create_chart(char *type, int update_every, char *module); -extern void write_begin_chart(char *family, char *name); +void write_begin_chart(char *family, char *name); -extern void write_chart_dimension(char *dim, long long value); +void write_chart_dimension(char *dim, long long value); -extern void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, uint32_t end); +void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, uint32_t end); -extern void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end); +void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end); -extern void write_io_chart(char *chart, char *family, char *dwrite, long long vwrite, +void write_io_chart(char *chart, char *family, char *dwrite, long long vwrite, char *dread, long long vread); -extern void ebpf_create_charts_on_apps(char *name, +void ebpf_create_charts_on_apps(char *name, char *title, char *units, char *family, @@ -235,15 +236,15 @@ extern void ebpf_create_charts_on_apps(char *name, int update_every, char *module); -extern void write_end_chart(); +void write_end_chart(); -extern void ebpf_cleanup_publish_syscall(netdata_publish_syscall_t *nps); +void ebpf_cleanup_publish_syscall(netdata_publish_syscall_t *nps); -extern int ebpf_enable_tracepoint(ebpf_tracepoint_t *tp); -extern int ebpf_disable_tracepoint(ebpf_tracepoint_t *tp); -extern uint32_t ebpf_enable_tracepoints(ebpf_tracepoint_t *tps); +int ebpf_enable_tracepoint(ebpf_tracepoint_t *tp); +int ebpf_disable_tracepoint(ebpf_tracepoint_t *tp); +uint32_t ebpf_enable_tracepoints(ebpf_tracepoint_t *tps); -extern void ebpf_pid_file(char *filename, size_t length); +void ebpf_pid_file(char *filename, size_t length); #define EBPF_PROGRAMS_SECTION "ebpf programs" @@ -271,19 +272,23 @@ extern sem_t *shm_sem_ebpf_cgroup; extern pthread_mutex_t mutex_cgroup_shm; extern size_t all_pids_count; extern ebpf_plugin_stats_t plugin_statistics; +#ifdef LIBBPF_MAJOR_VERSION extern struct btf *default_btf; +#else +extern void *default_btf; +#endif // Socket functions and variables // Common functions -extern void ebpf_process_create_apps_charts(struct ebpf_module *em, void *ptr); -extern void ebpf_socket_create_apps_charts(struct ebpf_module *em, void *ptr); -extern void ebpf_cachestat_create_apps_charts(struct ebpf_module *em, void *root); -extern void ebpf_one_dimension_write_charts(char *family, char *chart, char *dim, long long v1); -extern collected_number get_value_from_structure(char *basis, size_t offset); -extern void ebpf_update_pid_table(ebpf_local_maps_t *pid, ebpf_module_t *em); -extern void ebpf_write_chart_obsolete(char *type, char *id, char *title, char *units, char *family, +void ebpf_process_create_apps_charts(struct ebpf_module *em, void *ptr); +void ebpf_socket_create_apps_charts(struct ebpf_module *em, void *ptr); +void ebpf_cachestat_create_apps_charts(struct ebpf_module *em, void *root); +void ebpf_one_dimension_write_charts(char *family, char *chart, char *dim, long long v1); +collected_number get_value_from_structure(char *basis, size_t offset); +void ebpf_update_pid_table(ebpf_local_maps_t *pid, ebpf_module_t *em); +void ebpf_write_chart_obsolete(char *type, char *id, char *title, char *units, char *family, char *charttype, char *context, int order, int update_every); -extern void write_histogram_chart(char *family, char *name, const netdata_idx_t *hist, char **dimensions, uint32_t end); +void write_histogram_chart(char *family, char *name, const netdata_idx_t *hist, char **dimensions, uint32_t end); void ebpf_update_disabled_plugin_stats(ebpf_module_t *em); extern ebpf_filesystem_partitions_t localfs[]; extern ebpf_sync_syscalls_t local_syscalls[]; diff --git a/collectors/ebpf.plugin/ebpf_apps.h b/collectors/ebpf.plugin/ebpf_apps.h index f65a137b5..0bea9122f 100644 --- a/collectors/ebpf.plugin/ebpf_apps.h +++ b/collectors/ebpf.plugin/ebpf_apps.h @@ -408,30 +408,30 @@ static inline void debug_log_int(const char *fmt, ...) // extern struct pid_stat **all_pids; -extern int ebpf_read_apps_groups_conf(struct target **apps_groups_default_target, +int ebpf_read_apps_groups_conf(struct target **apps_groups_default_target, struct target **apps_groups_root_target, const char *path, const char *file); -extern void clean_apps_groups_target(struct target *apps_groups_root_target); +void clean_apps_groups_target(struct target *apps_groups_root_target); -extern size_t zero_all_targets(struct target *root); +size_t zero_all_targets(struct target *root); -extern int am_i_running_as_root(); +int am_i_running_as_root(); -extern void cleanup_exited_pids(); +void cleanup_exited_pids(); -extern int ebpf_read_hash_table(void *ep, int fd, uint32_t pid); +int ebpf_read_hash_table(void *ep, int fd, uint32_t pid); -extern int get_pid_comm(pid_t pid, size_t n, char *dest); +int get_pid_comm(pid_t pid, size_t n, char *dest); -extern size_t read_processes_statistic_using_pid_on_target(ebpf_process_stat_t **ep, +size_t read_processes_statistic_using_pid_on_target(ebpf_process_stat_t **ep, int fd, struct pid_on_target *pids); -extern size_t read_bandwidth_statistic_using_pid_on_target(ebpf_bandwidth_t **ep, int fd, struct pid_on_target *pids); +size_t read_bandwidth_statistic_using_pid_on_target(ebpf_bandwidth_t **ep, int fd, struct pid_on_target *pids); -extern void collect_data_for_all_processes(int tbl_pid_stats_fd); +void collect_data_for_all_processes(int tbl_pid_stats_fd); extern ebpf_process_stat_t **global_process_stats; extern ebpf_process_publish_apps_t **current_apps_data; diff --git a/collectors/ebpf.plugin/ebpf_cachestat.c b/collectors/ebpf.plugin/ebpf_cachestat.c index 14669bf68..4c410647d 100644 --- a/collectors/ebpf.plugin/ebpf_cachestat.c +++ b/collectors/ebpf.plugin/ebpf_cachestat.c @@ -15,11 +15,16 @@ netdata_cachestat_pid_t *cachestat_vector = NULL; static netdata_idx_t cachestat_hash_values[NETDATA_CACHESTAT_END]; static netdata_idx_t *cachestat_values = NULL; -struct netdata_static_thread cachestat_threads = {"CACHESTAT KERNEL", - NULL, NULL, 1, NULL, - NULL, NULL}; - -static ebpf_local_maps_t cachestat_maps[] = {{.name = "cstat_global", .internal_input = NETDATA_CACHESTAT_END, +struct netdata_static_thread cachestat_threads = {.name = "CACHESTAT KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL}; + +ebpf_local_maps_t cachestat_maps[] = {{.name = "cstat_global", .internal_input = NETDATA_CACHESTAT_END, .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC, .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED}, {.name = "cstat_pid", .internal_input = ND_EBPF_DEFAULT_PID_SIZE, @@ -39,7 +44,6 @@ struct config cachestat_config = { .first_section = NULL, .mutex = NETDATA_MUTEX_INITIALIZER, .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare }, .rwlock = AVL_LOCK_INITIALIZER } }; -static enum ebpf_threads_status ebpf_cachestat_exited = NETDATA_THREAD_EBPF_RUNNING; netdata_ebpf_targets_t cachestat_targets[] = { {.name = "add_to_page_cache_lru", .mode = EBPF_LOAD_TRAMPOLINE}, {.name = "mark_page_accessed", .mode = EBPF_LOAD_TRAMPOLINE}, @@ -67,6 +71,7 @@ static void ebpf_cachestat_disable_probe(struct cachestat_bpf *obj) bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_kprobe, false); bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_kprobe, false); bpf_program__set_autoload(obj->progs.netdata_mark_buffer_dirty_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_release_task_kprobe, false); } /* @@ -105,6 +110,7 @@ static void ebpf_cachestat_disable_trampoline(struct cachestat_bpf *obj) bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_fentry, false); bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_fentry, false); bpf_program__set_autoload(obj->progs.netdata_mark_buffer_dirty_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_release_task_fentry, false); } /* @@ -156,6 +162,9 @@ static inline void netdata_set_trampoline_target(struct cachestat_bpf *obj) bpf_program__set_attach_target(obj->progs.netdata_mark_buffer_dirty_fentry, 0, cachestat_targets[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY].name); + + bpf_program__set_attach_target(obj->progs.netdata_release_task_fentry, 0, + EBPF_COMMON_FNCT_CLEAN_UP); } /** @@ -210,6 +219,13 @@ static int ebpf_cachestat_attach_probe(struct cachestat_bpf *obj) if (ret) return -1; + obj->links.netdata_release_task_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_release_task_kprobe, + false, + EBPF_COMMON_FNCT_CLEAN_UP); + ret = libbpf_get_error(obj->links.netdata_release_task_kprobe); + if (ret) + return -1; + return 0; } @@ -241,6 +257,19 @@ static void ebpf_cachestat_set_hash_tables(struct cachestat_bpf *obj) cachestat_maps[NETDATA_CACHESTAT_CTRL].map_fd = bpf_map__fd(obj->maps.cstat_ctrl); } +/** + * Disable Release Task + * + * Disable release task when apps is not enabled. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_cachestat_disable_release_task(struct cachestat_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_release_task_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_release_task_fentry, false); +} + /** * Load and attach * @@ -266,13 +295,16 @@ static inline int ebpf_cachestat_load_and_attach(struct cachestat_bpf *obj, ebpf ebpf_cachestat_disable_specific_probe(obj); } + ebpf_cachestat_adjust_map_size(obj, em); + + if (!em->apps_charts && !em->cgroup_charts) + ebpf_cachestat_disable_release_task(obj); + int ret = cachestat_bpf__load(obj); if (ret) { return ret; } - ebpf_cachestat_adjust_map_size(obj, em); - ret = (test == EBPF_LOAD_TRAMPOLINE) ? cachestat_bpf__attach(obj) : ebpf_cachestat_attach_probe(obj); if (!ret) { ebpf_cachestat_set_hash_tables(obj); @@ -289,6 +321,38 @@ static inline int ebpf_cachestat_load_and_attach(struct cachestat_bpf *obj, ebpf * *****************************************************************/ +/** + * Cachestat Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_cachestat_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + ebpf_cleanup_publish_syscall(cachestat_counter_publish_aggregated); + + freez(cachestat_vector); + freez(cachestat_values); + freez(cachestat_threads.thread); + +#ifdef LIBBPF_MAJOR_VERSION + if (bpf_obj) + cachestat_bpf__destroy(bpf_obj); +#endif + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} + /** * Cachestat exit. * @@ -299,12 +363,8 @@ static inline int ebpf_cachestat_load_and_attach(struct cachestat_bpf *obj, ebpf static void ebpf_cachestat_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_cachestat_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*cachestat_threads.thread); + ebpf_cachestat_free(em); } /** @@ -317,21 +377,7 @@ static void ebpf_cachestat_exit(void *ptr) static void ebpf_cachestat_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_cachestat_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - ebpf_cleanup_publish_syscall(cachestat_counter_publish_aggregated); - - freez(cachestat_vector); - freez(cachestat_values); - freez(cachestat_threads.thread); - -#ifdef LIBBPF_MAJOR_VERSION - if (bpf_obj) - cachestat_bpf__destroy(bpf_obj); -#endif - cachestat_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_cachestat_free(em); } /***************************************************************** @@ -649,17 +695,12 @@ void *ebpf_cachestat_read_hash(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; usec_t step = NETDATA_LATENCY_CACHESTAT_SLEEP_MS * em->update_every; - while (ebpf_cachestat_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_cachestat_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); read_global_table(); } - ebpf_cachestat_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -872,21 +913,16 @@ static void ebpf_create_systemd_cachestat_charts(int update_every) * Send Cache Stat charts * * Send collected data to Netdata. - * - * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned - * otherwise function returns 1 to avoid chart recreation */ -static int ebpf_send_systemd_cachestat_charts() +static void ebpf_send_systemd_cachestat_charts() { - int ret = 1; ebpf_cgroup_target_t *ect; write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_CACHESTAT_HIT_RATIO_CHART); for (ect = ebpf_cgroup_pids; ect; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, (long long)ect->publish_cachestat.ratio); - } else if (unlikely(ect->systemd)) - ret = 0; + } } write_end_chart(); @@ -913,8 +949,6 @@ static int ebpf_send_systemd_cachestat_charts() } } write_end_chart(); - - return ret; } /** @@ -1038,13 +1072,11 @@ void ebpf_cachestat_send_cgroup_data(int update_every) int has_systemd = shm_ebpf_cgroup.header->systemd_enabled; if (has_systemd) { - static int systemd_charts = 0; - if (!systemd_charts) { + if (send_cgroup_chart) { ebpf_create_systemd_cachestat_charts(update_every); - systemd_charts = 1; } - systemd_charts = ebpf_send_systemd_cachestat_charts(); + ebpf_send_systemd_cachestat_charts(); } for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { @@ -1227,7 +1259,8 @@ static void ebpf_cachestat_set_internal_value() static int ebpf_cachestat_load_bpf(ebpf_module_t *em) { int ret = 0; - if (em->load == EBPF_LOAD_LEGACY) { + ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU].mode); + if (em->load & EBPF_LOAD_LEGACY) { em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { ret = -1; @@ -1267,16 +1300,13 @@ void *ebpf_cachestat_thread(void *ptr) ebpf_update_pid_table(&cachestat_maps[NETDATA_CACHESTAT_PID_STATS], em); - if (!em->enabled) - goto endcachestat; - ebpf_cachestat_set_internal_value(); #ifdef LIBBPF_MAJOR_VERSION ebpf_adjust_thread_load(em, default_btf); #endif if (ebpf_cachestat_load_bpf(em)) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endcachestat; } @@ -1298,8 +1328,7 @@ void *ebpf_cachestat_thread(void *ptr) cachestat_collector(em); endcachestat: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_cachestat.h b/collectors/ebpf.plugin/ebpf_cachestat.h index fdd88464a..07f0745d4 100644 --- a/collectors/ebpf.plugin/ebpf_cachestat.h +++ b/collectors/ebpf.plugin/ebpf_cachestat.h @@ -81,9 +81,10 @@ typedef struct netdata_publish_cachestat { netdata_cachestat_pid_t prev; } netdata_publish_cachestat_t; -extern void *ebpf_cachestat_thread(void *ptr); +void *ebpf_cachestat_thread(void *ptr); extern struct config cachestat_config; extern netdata_ebpf_targets_t cachestat_targets[]; +extern ebpf_local_maps_t cachestat_maps[]; #endif // NETDATA_EBPF_CACHESTAT_H diff --git a/collectors/ebpf.plugin/ebpf_cgroup.c b/collectors/ebpf.plugin/ebpf_cgroup.c index 24469c642..42c045368 100644 --- a/collectors/ebpf.plugin/ebpf_cgroup.c +++ b/collectors/ebpf.plugin/ebpf_cgroup.c @@ -6,6 +6,7 @@ #include "ebpf_cgroup.h" ebpf_cgroup_target_t *ebpf_cgroup_pids = NULL; +int send_cgroup_chart = 0; // -------------------------------------------------------------------------------------------------------------------- // Map shared memory @@ -98,24 +99,6 @@ void ebpf_map_cgroup_shared_memory() // -------------------------------------------------------------------------------------------------------------------- // Close and Cleanup -/** - * Close shared memory - */ -void ebpf_close_cgroup_shm() -{ - if (shm_sem_ebpf_cgroup != SEM_FAILED) { - sem_close(shm_sem_ebpf_cgroup); - sem_unlink(NETDATA_NAMED_SEMAPHORE_EBPF_CGROUP_NAME); - shm_sem_ebpf_cgroup = SEM_FAILED; - } - - if (shm_fd_ebpf_cgroup > 0) { - close(shm_fd_ebpf_cgroup); - shm_unlink(NETDATA_SHARED_MEMORY_EBPF_CGROUP_NAME); - shm_fd_ebpf_cgroup = -1; - } -} - /** * Clean Specific cgroup pid * @@ -259,7 +242,7 @@ static void ebpf_update_pid_link_list(ebpf_cgroup_target_t *ect, char *path) * * Set variable remove. If this variable is not reset, the structure will be removed from link list. */ - void ebpf_reset_updated_var() +void ebpf_reset_updated_var() { ebpf_cgroup_target_t *ect; for (ect = ebpf_cgroup_pids; ect; ect = ect->next) { @@ -274,6 +257,7 @@ static void ebpf_update_pid_link_list(ebpf_cgroup_target_t *ect, char *path) */ void ebpf_parse_cgroup_shm_data() { + static int previous = 0; if (shm_ebpf_cgroup.header) { sem_wait(shm_sem_ebpf_cgroup); int i, end = shm_ebpf_cgroup.header->cgroup_root_count; @@ -291,6 +275,11 @@ void ebpf_parse_cgroup_shm_data() ebpf_update_pid_link_list(ect, ptr->path); } } + send_cgroup_chart = previous != shm_ebpf_cgroup.header->cgroup_root_count; + previous = shm_ebpf_cgroup.header->cgroup_root_count; +#ifdef NETDATA_DEV_MODE + error("Updating cgroup %d (Previous: %d, Current: %d)", send_cgroup_chart, previous, shm_ebpf_cgroup.header->cgroup_root_count); +#endif pthread_mutex_unlock(&mutex_cgroup_shm); sem_post(shm_sem_ebpf_cgroup); diff --git a/collectors/ebpf.plugin/ebpf_cgroup.h b/collectors/ebpf.plugin/ebpf_cgroup.h index cca9a950d..19da7fca9 100644 --- a/collectors/ebpf.plugin/ebpf_cgroup.h +++ b/collectors/ebpf.plugin/ebpf_cgroup.h @@ -60,10 +60,10 @@ typedef struct ebpf_cgroup_target { struct ebpf_cgroup_target *next; } ebpf_cgroup_target_t; -extern void ebpf_map_cgroup_shared_memory(); -extern void ebpf_parse_cgroup_shm_data(); -extern void ebpf_close_cgroup_shm(); -extern void ebpf_create_charts_on_systemd(char *id, char *title, char *units, char *family, char *charttype, int order, +void ebpf_map_cgroup_shared_memory(); +void ebpf_parse_cgroup_shm_data(); +void ebpf_create_charts_on_systemd(char *id, char *title, char *units, char *family, char *charttype, int order, char *algorithm, char *context, char *module, int update_every); +extern int send_cgroup_chart; #endif /* NETDATA_EBPF_CGROUP_H */ diff --git a/collectors/ebpf.plugin/ebpf_dcstat.c b/collectors/ebpf.plugin/ebpf_dcstat.c index 8cf063ca1..71169e153 100644 --- a/collectors/ebpf.plugin/ebpf_dcstat.c +++ b/collectors/ebpf.plugin/ebpf_dcstat.c @@ -20,11 +20,15 @@ struct config dcstat_config = { .first_section = NULL, .rwlock = AVL_LOCK_INITIALIZER } }; struct netdata_static_thread dcstat_threads = {"DCSTAT KERNEL", - NULL, NULL, 1, NULL, - NULL, NULL}; -static enum ebpf_threads_status ebpf_dcstat_exited = NETDATA_THREAD_EBPF_RUNNING; - -static ebpf_local_maps_t dcstat_maps[] = {{.name = "dcstat_global", .internal_input = NETDATA_DIRECTORY_CACHE_END, + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL}; + +ebpf_local_maps_t dcstat_maps[] = {{.name = "dcstat_global", .internal_input = NETDATA_DIRECTORY_CACHE_END, .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC, .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED}, {.name = "dcstat_pid", .internal_input = ND_EBPF_DEFAULT_PID_SIZE, @@ -65,6 +69,7 @@ static inline void ebpf_dc_disable_probes(struct dc_bpf *obj) { bpf_program__set_autoload(obj->progs.netdata_lookup_fast_kprobe, false); bpf_program__set_autoload(obj->progs.netdata_d_lookup_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_dcstat_release_task_kprobe, false); } /* @@ -78,6 +83,7 @@ static inline void ebpf_dc_disable_trampoline(struct dc_bpf *obj) { bpf_program__set_autoload(obj->progs.netdata_lookup_fast_fentry, false); bpf_program__set_autoload(obj->progs.netdata_d_lookup_fexit, false); + bpf_program__set_autoload(obj->progs.netdata_dcstat_release_task_fentry, false); } /** @@ -94,6 +100,9 @@ static void ebpf_dc_set_trampoline_target(struct dc_bpf *obj) bpf_program__set_attach_target(obj->progs.netdata_d_lookup_fexit, 0, dc_targets[NETDATA_DC_TARGET_D_LOOKUP].name); + + bpf_program__set_attach_target(obj->progs.netdata_dcstat_release_task_fentry, 0, + EBPF_COMMON_FNCT_CLEAN_UP); } /** @@ -125,6 +134,13 @@ static int ebpf_dc_attach_probes(struct dc_bpf *obj) if (ret) return -1; + obj->links.netdata_dcstat_release_task_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_dcstat_release_task_kprobe, + false, + EBPF_COMMON_FNCT_CLEAN_UP); + ret = libbpf_get_error(obj->links.netdata_dcstat_release_task_kprobe); + if (ret) + return -1; + return 0; } @@ -179,6 +195,19 @@ netdata_ebpf_program_loaded_t ebpf_dc_update_load(ebpf_module_t *em) return EBPF_LOAD_RETPROBE; } +/** + * Disable Release Task + * + * Disable release task when apps is not enabled. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_dc_disable_release_task(struct dc_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_dcstat_release_task_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_dcstat_release_task_fentry, false); +} + /** * Load and attach * @@ -200,13 +229,16 @@ static inline int ebpf_dc_load_and_attach(struct dc_bpf *obj, ebpf_module_t *em) ebpf_dc_disable_trampoline(obj); } + ebpf_dc_adjust_map_size(obj, em); + + if (!em->apps_charts && !em->cgroup_charts) + ebpf_dc_disable_release_task(obj); + int ret = dc_bpf__load(obj); if (ret) { return ret; } - ebpf_dc_adjust_map_size(obj, em); - ret = (test == EBPF_LOAD_TRAMPOLINE) ? dc_bpf__attach(obj) : ebpf_dc_attach_probes(obj); if (!ret) { ebpf_dc_set_hash_tables(obj); @@ -262,33 +294,21 @@ void ebpf_dcstat_clean_names() } /** - * DCstat exit + * DCstat Free * - * Cancel child and exit. + * Cleanup variables after child threads to stop * * @param ptr thread data. */ -static void ebpf_dcstat_exit(void *ptr) +static void ebpf_dcstat_free(ebpf_module_t *em ) { - ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); return; } - - ebpf_dcstat_exited = NETDATA_THREAD_EBPF_STOPPING; -} - -/** - * Clean up the main thread. - * - * @param ptr thread data. - */ -static void ebpf_dcstat_cleanup(void *ptr) -{ - ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_dcstat_exited != NETDATA_THREAD_EBPF_STOPPED) - return; + pthread_mutex_unlock(&ebpf_exit_cleanup); freez(dcstat_vector); freez(dcstat_values); @@ -303,8 +323,34 @@ static void ebpf_dcstat_cleanup(void *ptr) dc_bpf__destroy(bpf_obj); #endif - dcstat_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} + +/** + * DCstat exit + * + * Cancel child and exit. + * + * @param ptr thread data. + */ +static void ebpf_dcstat_exit(void *ptr) +{ + ebpf_module_t *em = (ebpf_module_t *)ptr; + netdata_thread_cancel(*dcstat_threads.thread); + ebpf_dcstat_free(em); +} + +/** + * Clean up the main thread. + * + * @param ptr thread data. + */ +static void ebpf_dcstat_cleanup(void *ptr) +{ + ebpf_module_t *em = (ebpf_module_t *)ptr; + ebpf_dcstat_free(em); } /***************************************************************** @@ -531,17 +577,12 @@ void *ebpf_dcstat_read_hash(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; usec_t step = NETDATA_LATENCY_DCSTAT_SLEEP_MS * em->update_every; - while (ebpf_dcstat_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_dcstat_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); read_global_table(); } - ebpf_dcstat_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -860,21 +901,16 @@ static void ebpf_create_systemd_dc_charts(int update_every) * Send Directory Cache charts * * Send collected data to Netdata. - * - * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned - * otherwise function returns 1 to avoid chart recreation */ -static int ebpf_send_systemd_dc_charts() +static void ebpf_send_systemd_dc_charts() { - int ret = 1; collected_number value; ebpf_cgroup_target_t *ect; write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_DC_HIT_CHART); for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, (long long) ect->publish_dc.ratio); - } else if (unlikely(ect->systemd)) - ret = 0; + } } write_end_chart(); @@ -910,8 +946,6 @@ static int ebpf_send_systemd_dc_charts() } } write_end_chart(); - - return ret; } /** @@ -966,13 +1000,11 @@ void ebpf_dc_send_cgroup_data(int update_every) int has_systemd = shm_ebpf_cgroup.header->systemd_enabled; if (has_systemd) { - static int systemd_charts = 0; - if (!systemd_charts) { + if (send_cgroup_chart) { ebpf_create_systemd_dc_charts(update_every); - systemd_charts = 1; } - systemd_charts = ebpf_send_systemd_dc_charts(); + ebpf_send_systemd_dc_charts(); } for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { @@ -1116,7 +1148,8 @@ static void ebpf_dcstat_allocate_global_vectors(int apps) static int ebpf_dcstat_load_bpf(ebpf_module_t *em) { int ret = 0; - if (em->load == EBPF_LOAD_LEGACY) { + ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_DC_TARGET_LOOKUP_FAST].mode); + if (em->load & EBPF_LOAD_LEGACY) { em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { ret = -1; @@ -1158,14 +1191,11 @@ void *ebpf_dcstat_thread(void *ptr) ebpf_update_names(dc_optional_name, em); - if (!em->enabled) - goto enddcstat; - #ifdef LIBBPF_MAJOR_VERSION ebpf_adjust_thread_load(em, default_btf); #endif if (ebpf_dcstat_load_bpf(em)) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto enddcstat; } @@ -1188,8 +1218,7 @@ void *ebpf_dcstat_thread(void *ptr) dcstat_collector(em); enddcstat: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_dcstat.h b/collectors/ebpf.plugin/ebpf_dcstat.h index 5c4a80cd7..d8687f968 100644 --- a/collectors/ebpf.plugin/ebpf_dcstat.h +++ b/collectors/ebpf.plugin/ebpf_dcstat.h @@ -75,9 +75,10 @@ typedef struct netdata_publish_dcstat { netdata_dcstat_pid_t prev; } netdata_publish_dcstat_t; -extern void *ebpf_dcstat_thread(void *ptr); -extern void ebpf_dcstat_create_apps_charts(struct ebpf_module *em, void *ptr); +void *ebpf_dcstat_thread(void *ptr); +void ebpf_dcstat_create_apps_charts(struct ebpf_module *em, void *ptr); extern struct config dcstat_config; extern netdata_ebpf_targets_t dc_targets[]; +extern ebpf_local_maps_t dcstat_maps[]; #endif // NETDATA_EBPF_DCSTAT_H diff --git a/collectors/ebpf.plugin/ebpf_disk.c b/collectors/ebpf.plugin/ebpf_disk.c index 96b1705ce..a27bd81e3 100644 --- a/collectors/ebpf.plugin/ebpf_disk.c +++ b/collectors/ebpf.plugin/ebpf_disk.c @@ -33,10 +33,16 @@ static netdata_syscall_stat_t disk_aggregated_data[NETDATA_EBPF_HIST_MAX_BINS]; static netdata_publish_syscall_t disk_publish_aggregated[NETDATA_EBPF_HIST_MAX_BINS]; static netdata_idx_t *disk_hash_values = NULL; -static struct netdata_static_thread disk_threads = {"DISK KERNEL", - NULL, NULL, 1, NULL, - NULL, NULL }; -static enum ebpf_threads_status ebpf_disk_exited = NETDATA_THREAD_EBPF_RUNNING; +static struct netdata_static_thread disk_threads = { + .name = "DISK KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; ebpf_publish_disk_t *plot_disks = NULL; pthread_mutex_t plot_mutex; @@ -423,6 +429,40 @@ static void ebpf_cleanup_disk_list() } } +/** + * DISK Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_disk_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + ebpf_disk_disable_tracepoints(); + + if (dimensions) + ebpf_histogram_dimension_cleanup(dimensions, NETDATA_EBPF_HIST_MAX_BINS); + + freez(disk_hash_values); + freez(disk_threads.thread); + pthread_mutex_destroy(&plot_mutex); + + ebpf_cleanup_plot_disks(); + ebpf_cleanup_disk_list(); + + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} + /** * Disk exit. * @@ -433,12 +473,8 @@ static void ebpf_cleanup_disk_list() static void ebpf_disk_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_disk_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*disk_threads.thread); + ebpf_disk_free(em); } /** @@ -451,23 +487,7 @@ static void ebpf_disk_exit(void *ptr) static void ebpf_disk_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_disk_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - ebpf_disk_disable_tracepoints(); - - if (dimensions) - ebpf_histogram_dimension_cleanup(dimensions, NETDATA_EBPF_HIST_MAX_BINS); - - freez(disk_hash_values); - freez(disk_threads.thread); - pthread_mutex_destroy(&plot_mutex); - - ebpf_cleanup_plot_disks(); - ebpf_cleanup_disk_list(); - - disk_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_disk_free(em); } /***************************************************************** @@ -590,17 +610,12 @@ void *ebpf_disk_read_hash(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; usec_t step = NETDATA_LATENCY_DISK_SLEEP_MS * em->update_every; - while (ebpf_disk_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_disk_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); read_hard_disk_tables(disk_maps[NETDATA_DISK_READ].map_fd); } - ebpf_disk_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -805,29 +820,26 @@ void *ebpf_disk_thread(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; em->maps = disk_maps; - if (!em->enabled) - goto enddisk; - if (ebpf_disk_enable_tracepoints()) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto enddisk; } avl_init_lock(&disk_tree, ebpf_compare_disks); if (read_local_disks()) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto enddisk; } if (pthread_mutex_init(&plot_mutex, NULL)) { - em->enabled = 0; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; error("Cannot initialize local mutex"); goto enddisk; } em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { - em->enabled = 0; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto enddisk; } @@ -845,8 +857,7 @@ void *ebpf_disk_thread(void *ptr) disk_collector(em); enddisk: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); diff --git a/collectors/ebpf.plugin/ebpf_disk.h b/collectors/ebpf.plugin/ebpf_disk.h index 8e58174b9..c14b887f8 100644 --- a/collectors/ebpf.plugin/ebpf_disk.h +++ b/collectors/ebpf.plugin/ebpf_disk.h @@ -72,7 +72,7 @@ typedef struct netdata_ebpf_publish_disk { extern struct config disk_config; -extern void *ebpf_disk_thread(void *ptr); +void *ebpf_disk_thread(void *ptr); #endif /* NETDATA_EBPF_DISK_H */ diff --git a/collectors/ebpf.plugin/ebpf_fd.c b/collectors/ebpf.plugin/ebpf_fd.c index b4e577dad..30b7f22ce 100644 --- a/collectors/ebpf.plugin/ebpf_fd.c +++ b/collectors/ebpf.plugin/ebpf_fd.c @@ -29,21 +29,339 @@ struct config fd_config = { .first_section = NULL, .last_section = NULL, .mutex .index = {.avl_tree = { .root = NULL, .compar = appconfig_section_compare }, .rwlock = AVL_LOCK_INITIALIZER } }; -struct netdata_static_thread fd_thread = {"FD KERNEL", NULL, NULL, 1, NULL, - NULL, NULL}; -static enum ebpf_threads_status ebpf_fd_exited = NETDATA_THREAD_EBPF_RUNNING; +struct netdata_static_thread fd_thread = {"FD KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL}; + static netdata_idx_t fd_hash_values[NETDATA_FD_COUNTER]; static netdata_idx_t *fd_values = NULL; netdata_fd_stat_t *fd_vector = NULL; netdata_fd_stat_t **fd_pid = NULL; +netdata_ebpf_targets_t fd_targets[] = { {.name = "open", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = "close", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}}; + +#ifdef LIBBPF_MAJOR_VERSION +#include "includes/fd.skel.h" // BTF code + +static struct fd_bpf *bpf_obj = NULL; + +/** + * Disable probe + * + * Disable all probes to use exclusively another method. + * + * @param obj is the main structure for bpf objects +*/ +static inline void ebpf_fd_disable_probes(struct fd_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_sys_open_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_sys_open_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_release_task_fd_kprobe, false); + if (running_on_kernel >= NETDATA_EBPF_KERNEL_5_11) { + bpf_program__set_autoload(obj->progs.netdata___close_fd_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata___close_fd_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_close_fd_kprobe, false); + } else { + bpf_program__set_autoload(obj->progs.netdata___close_fd_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_close_fd_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_close_fd_kprobe, false); + } +} + +/* + * Disable specific probe + * + * Disable probes according the kernel version + * + * @param obj is the main structure for bpf objects + */ +static inline void ebpf_disable_specific_probes(struct fd_bpf *obj) +{ + if (running_on_kernel >= NETDATA_EBPF_KERNEL_5_11) { + bpf_program__set_autoload(obj->progs.netdata___close_fd_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata___close_fd_kprobe, false); + } else { + bpf_program__set_autoload(obj->progs.netdata_close_fd_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_close_fd_kprobe, false); + } +} + +/* + * Disable trampoline + * + * Disable all trampoline to use exclusively another method. + * + * @param obj is the main structure for bpf objects. + */ +static inline void ebpf_disable_trampoline(struct fd_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_sys_open_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_sys_open_fexit, false); + bpf_program__set_autoload(obj->progs.netdata_close_fd_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_close_fd_fexit, false); + bpf_program__set_autoload(obj->progs.netdata___close_fd_fentry, false); + bpf_program__set_autoload(obj->progs.netdata___close_fd_fexit, false); + bpf_program__set_autoload(obj->progs.netdata_release_task_fd_fentry, false); +} + +/* + * Disable specific trampoline + * + * Disable trampoline according to kernel version. + * + * @param obj is the main structure for bpf objects. + */ +static inline void ebpf_disable_specific_trampoline(struct fd_bpf *obj) +{ + if (running_on_kernel >= NETDATA_EBPF_KERNEL_5_11) { + bpf_program__set_autoload(obj->progs.netdata___close_fd_fentry, false); + bpf_program__set_autoload(obj->progs.netdata___close_fd_fexit, false); + } else { + bpf_program__set_autoload(obj->progs.netdata_close_fd_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_close_fd_fexit, false); + } +} + +/** + * Set trampoline target + * + * Set the targets we will monitor. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_set_trampoline_target(struct fd_bpf *obj) +{ + bpf_program__set_attach_target(obj->progs.netdata_sys_open_fentry, 0, fd_targets[NETDATA_FD_SYSCALL_OPEN].name); + bpf_program__set_attach_target(obj->progs.netdata_sys_open_fexit, 0, fd_targets[NETDATA_FD_SYSCALL_OPEN].name); + bpf_program__set_attach_target(obj->progs.netdata_release_task_fd_fentry, 0, EBPF_COMMON_FNCT_CLEAN_UP); + + if (running_on_kernel >= NETDATA_EBPF_KERNEL_5_11) { + bpf_program__set_attach_target( + obj->progs.netdata_close_fd_fentry, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name); + bpf_program__set_attach_target(obj->progs.netdata_close_fd_fexit, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name); + } else { + bpf_program__set_attach_target( + obj->progs.netdata___close_fd_fentry, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name); + bpf_program__set_attach_target( + obj->progs.netdata___close_fd_fexit, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name); + } +} + +/** + * Mount Attach Probe + * + * Attach probes to target + * + * @param obj is the main structure for bpf objects. + * + * @return It returns 0 on success and -1 otherwise. + */ +static int ebpf_fd_attach_probe(struct fd_bpf *obj) +{ + obj->links.netdata_sys_open_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_sys_open_kprobe, false, + fd_targets[NETDATA_FD_SYSCALL_OPEN].name); + int ret = libbpf_get_error(obj->links.netdata_sys_open_kprobe); + if (ret) + return -1; + + obj->links.netdata_sys_open_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_sys_open_kretprobe, true, + fd_targets[NETDATA_FD_SYSCALL_OPEN].name); + ret = libbpf_get_error(obj->links.netdata_sys_open_kretprobe); + if (ret) + return -1; + + obj->links.netdata_release_task_fd_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_release_task_fd_kprobe, + false, + EBPF_COMMON_FNCT_CLEAN_UP); + ret = libbpf_get_error(obj->links.netdata_release_task_fd_kprobe); + if (ret) + return -1; + + if (running_on_kernel >= NETDATA_EBPF_KERNEL_5_11) { + obj->links.netdata_close_fd_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_close_fd_kretprobe, true, + fd_targets[NETDATA_FD_SYSCALL_CLOSE].name); + ret = libbpf_get_error(obj->links.netdata_close_fd_kretprobe); + if (ret) + return -1; + + obj->links.netdata_close_fd_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_close_fd_kprobe, false, + fd_targets[NETDATA_FD_SYSCALL_CLOSE].name); + ret = libbpf_get_error(obj->links.netdata_close_fd_kprobe); + if (ret) + return -1; + } else { + obj->links.netdata___close_fd_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata___close_fd_kretprobe, + true, + fd_targets[NETDATA_FD_SYSCALL_CLOSE].name); + ret = libbpf_get_error(obj->links.netdata___close_fd_kretprobe); + if (ret) + return -1; + + obj->links.netdata___close_fd_kprobe = bpf_program__attach_kprobe(obj->progs.netdata___close_fd_kprobe, + false, + fd_targets[NETDATA_FD_SYSCALL_CLOSE].name); + ret = libbpf_get_error(obj->links.netdata___close_fd_kprobe); + if (ret) + return -1; + } + + return 0; +} + +/** + * Set target values + * + * Set pointers used to laod data. + */ +static void ebpf_fd_set_target_values() +{ + static char *close_targets[] = {"close_fd", "__close_fd"}; + static char *open_targets[] = {"do_sys_openat2", "do_sys_open"}; + if (running_on_kernel >= NETDATA_EBPF_KERNEL_5_11) { + fd_targets[NETDATA_FD_SYSCALL_OPEN].name = open_targets[0]; + fd_targets[NETDATA_FD_SYSCALL_CLOSE].name = close_targets[0]; + } else { + fd_targets[NETDATA_FD_SYSCALL_OPEN].name = open_targets[1]; + fd_targets[NETDATA_FD_SYSCALL_CLOSE].name = close_targets[1]; + } +} + +/** + * Set hash tables + * + * Set the values for maps according the value given by kernel. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_fd_set_hash_tables(struct fd_bpf *obj) +{ + fd_maps[NETDATA_FD_GLOBAL_STATS].map_fd = bpf_map__fd(obj->maps.tbl_fd_global); + fd_maps[NETDATA_FD_PID_STATS].map_fd = bpf_map__fd(obj->maps.tbl_fd_pid); + fd_maps[NETDATA_FD_CONTROLLER].map_fd = bpf_map__fd(obj->maps.fd_ctrl); +} + +/** + * Adjust Map Size + * + * Resize maps according input from users. + * + * @param obj is the main structure for bpf objects. + * @param em structure with configuration + */ +static void ebpf_fd_adjust_map_size(struct fd_bpf *obj, ebpf_module_t *em) +{ + ebpf_update_map_size(obj->maps.tbl_fd_pid, &fd_maps[NETDATA_FD_PID_STATS], + em, bpf_map__name(obj->maps.tbl_fd_pid)); +} + +/** + * Disable Release Task + * + * Disable release task when apps is not enabled. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_fd_disable_release_task(struct fd_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_release_task_fd_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_release_task_fd_fentry, false); +} + +/** + * Load and attach + * + * Load and attach the eBPF code in kernel. + * + * @param obj is the main structure for bpf objects. + * @param em structure with configuration + * + * @return it returns 0 on succes and -1 otherwise + */ +static inline int ebpf_fd_load_and_attach(struct fd_bpf *obj, ebpf_module_t *em) +{ + netdata_ebpf_targets_t *mt = em->targets; + netdata_ebpf_program_loaded_t test = mt[NETDATA_FD_SYSCALL_OPEN].mode; + + ebpf_fd_set_target_values(); + if (test == EBPF_LOAD_TRAMPOLINE) { + ebpf_fd_disable_probes(obj); + ebpf_disable_specific_trampoline(obj); + + ebpf_set_trampoline_target(obj); + // TODO: Remove this in next PR, because this specific trampoline has an error. + bpf_program__set_autoload(obj->progs.netdata_release_task_fd_fentry, false); + } else { + ebpf_disable_trampoline(obj); + ebpf_disable_specific_probes(obj); + } + + ebpf_fd_adjust_map_size(obj, em); + + if (!em->apps_charts && !em->cgroup_charts) + ebpf_fd_disable_release_task(obj); + + int ret = fd_bpf__load(obj); + if (ret) { + return ret; + } + + ret = (test == EBPF_LOAD_TRAMPOLINE) ? fd_bpf__attach(obj) : ebpf_fd_attach_probe(obj); + if (!ret) { + ebpf_fd_set_hash_tables(obj); + + ebpf_update_controller(fd_maps[NETDATA_CACHESTAT_CTRL].map_fd, em); + } + + return ret; +} +#endif + /***************************************************************** * * FUNCTIONS TO CLOSE THE THREAD * *****************************************************************/ +/** + * FD Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_fd_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + ebpf_cleanup_publish_syscall(fd_publish_aggregated); + freez(fd_thread.thread); + freez(fd_values); + freez(fd_vector); + +#ifdef LIBBPF_MAJOR_VERSION + if (bpf_obj) + fd_bpf__destroy(bpf_obj); +#endif + + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} + /** * FD Exit * @@ -54,12 +372,8 @@ netdata_fd_stat_t **fd_pid = NULL; static void ebpf_fd_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_fd_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*fd_thread.thread); + ebpf_fd_free(em); } /** @@ -70,16 +384,7 @@ static void ebpf_fd_exit(void *ptr) static void ebpf_fd_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_fd_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - ebpf_cleanup_publish_syscall(fd_publish_aggregated); - freez(fd_thread.thread); - freez(fd_values); - freez(fd_vector); - - fd_thread.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_fd_free(em); } /***************************************************************** @@ -153,17 +458,12 @@ void *ebpf_fd_read_hash(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; usec_t step = NETDATA_FD_SLEEP_MS * em->update_every; - while (ebpf_fd_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_fd_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); read_global_table(); } - ebpf_fd_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -549,20 +849,15 @@ static void ebpf_create_systemd_fd_charts(ebpf_module_t *em) * Send collected data to Netdata. * * @param em the main collector structure - * - * @return It returns the status for chart creation, if it is necessary to remove a specific dimension zero is returned - * otherwise function returns 1 to avoid chart recreation */ -static int ebpf_send_systemd_fd_charts(ebpf_module_t *em) +static void ebpf_send_systemd_fd_charts(ebpf_module_t *em) { - int ret = 1; ebpf_cgroup_target_t *ect; write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_SYSCALL_APPS_FILE_OPEN); for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, ect->publish_systemd_fd.open_call); - } else if (unlikely(ect->systemd)) - ret = 0; + } } write_end_chart(); @@ -593,8 +888,6 @@ static int ebpf_send_systemd_fd_charts(ebpf_module_t *em) } write_end_chart(); } - - return ret; } /** @@ -615,13 +908,11 @@ static void ebpf_fd_send_cgroup_data(ebpf_module_t *em) int has_systemd = shm_ebpf_cgroup.header->systemd_enabled; if (has_systemd) { - static int systemd_charts = 0; - if (!systemd_charts) { + if (send_cgroup_chart) { ebpf_create_systemd_fd_charts(em); - systemd_charts = 1; } - systemd_charts = ebpf_send_systemd_fd_charts(em); + ebpf_send_systemd_fd_charts(em); } for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { @@ -810,6 +1101,40 @@ static void ebpf_fd_allocate_global_vectors(int apps) fd_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t)); } +/* + * Load BPF + * + * Load BPF files. + * + * @param em the structure with configuration + */ +static int ebpf_fd_load_bpf(ebpf_module_t *em) +{ + int ret = 0; + ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_FD_SYSCALL_OPEN].mode); + if (em->load & EBPF_LOAD_LEGACY) { + em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); + if (!em->probe_links) { + em->enabled = CONFIG_BOOLEAN_NO; + ret = -1; + } + } +#ifdef LIBBPF_MAJOR_VERSION + else { + bpf_obj = fd_bpf__open(); + if (!bpf_obj) + ret = -1; + else + ret = ebpf_fd_load_and_attach(bpf_obj, em); + } +#endif + + if (ret) + error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->thread_name); + + return ret; +} + /** * Directory Cache thread * @@ -826,17 +1151,16 @@ void *ebpf_fd_thread(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; em->maps = fd_maps; - if (!em->enabled) +#ifdef LIBBPF_MAJOR_VERSION + ebpf_adjust_thread_load(em, default_btf); +#endif + if (ebpf_fd_load_bpf(em)) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endfd; + } ebpf_fd_allocate_global_vectors(em->apps_charts); - em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); - if (!em->probe_links) { - em->enabled = CONFIG_BOOLEAN_NO; - goto endfd; - } - int algorithms[NETDATA_FD_SYSCALL_END] = { NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_INCREMENTAL_IDX }; @@ -852,8 +1176,7 @@ void *ebpf_fd_thread(void *ptr) fd_collector(em); endfd: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_fd.h b/collectors/ebpf.plugin/ebpf_fd.h index 8742558df..914a34b98 100644 --- a/collectors/ebpf.plugin/ebpf_fd.h +++ b/collectors/ebpf.plugin/ebpf_fd.h @@ -75,10 +75,11 @@ enum fd_syscalls { }; -extern void *ebpf_fd_thread(void *ptr); -extern void ebpf_fd_create_apps_charts(struct ebpf_module *em, void *ptr); +void *ebpf_fd_thread(void *ptr); +void ebpf_fd_create_apps_charts(struct ebpf_module *em, void *ptr); extern struct config fd_config; extern netdata_fd_stat_t **fd_pid; +extern netdata_ebpf_targets_t fd_targets[]; #endif /* NETDATA_EBPF_FD_H */ diff --git a/collectors/ebpf.plugin/ebpf_filesystem.c b/collectors/ebpf.plugin/ebpf_filesystem.c index bc767fbc9..7dbec7410 100644 --- a/collectors/ebpf.plugin/ebpf_filesystem.c +++ b/collectors/ebpf.plugin/ebpf_filesystem.c @@ -30,10 +30,16 @@ static ebpf_local_maps_t fs_maps[] = {{.name = "tbl_ext4", .internal_input = NET .type = NETDATA_EBPF_MAP_CONTROLLER, .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED}}; -struct netdata_static_thread filesystem_threads = {"EBPF FS READ", - NULL, NULL, 1, NULL, - NULL, NULL }; -static enum ebpf_threads_status ebpf_fs_exited = NETDATA_THREAD_EBPF_RUNNING; +struct netdata_static_thread filesystem_threads = { + .name = "EBPF FS READ", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; static netdata_syscall_stat_t filesystem_aggregated_data[NETDATA_EBPF_HIST_MAX_BINS]; static netdata_publish_syscall_t filesystem_publish_aggregated[NETDATA_EBPF_HIST_MAX_BINS]; @@ -317,21 +323,40 @@ void ebpf_filesystem_cleanup_ebpf_data() freez(efp->hadditional.name); freez(efp->hadditional.title); - - struct bpf_link **probe_links = efp->probe_links; - size_t j = 0 ; - struct bpf_program *prog; - bpf_object__for_each_program(prog, efp->objects) { - bpf_link__destroy(probe_links[j]); - j++; - } - freez(probe_links); - if (efp->objects) - bpf_object__close(efp->objects); } } } +/** + * Filesystem Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_filesystem_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + freez(filesystem_threads.thread); + ebpf_cleanup_publish_syscall(filesystem_publish_aggregated); + + ebpf_filesystem_cleanup_ebpf_data(); + if (dimensions) + ebpf_histogram_dimension_cleanup(dimensions, NETDATA_EBPF_HIST_MAX_BINS); + freez(filesystem_hash_values); + + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} + /** * Filesystem exit * @@ -342,12 +367,8 @@ void ebpf_filesystem_cleanup_ebpf_data() static void ebpf_filesystem_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_fs_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*filesystem_threads.thread); + ebpf_filesystem_free(em); } /** @@ -360,19 +381,7 @@ static void ebpf_filesystem_exit(void *ptr) static void ebpf_filesystem_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_fs_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - freez(filesystem_threads.thread); - ebpf_cleanup_publish_syscall(filesystem_publish_aggregated); - - ebpf_filesystem_cleanup_ebpf_data(); - if (dimensions) - ebpf_histogram_dimension_cleanup(dimensions, NETDATA_EBPF_HIST_MAX_BINS); - freez(filesystem_hash_values); - - filesystem_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_filesystem_free(em); } /***************************************************************** @@ -483,11 +492,8 @@ void *ebpf_filesystem_read_hash(void *ptr) heartbeat_init(&hb); usec_t step = NETDATA_FILESYSTEM_READ_SLEEP_MS * em->update_every; int update_every = em->update_every; - while (ebpf_fs_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_fs_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); (void) ebpf_update_partitions(em); ebpf_obsolete_fs_charts(update_every); @@ -499,8 +505,6 @@ void *ebpf_filesystem_read_hash(void *ptr) read_filesystem_tables(); } - ebpf_fs_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -603,9 +607,6 @@ void *ebpf_filesystem_thread(void *ptr) em->maps = fs_maps; ebpf_update_filesystem(); - if (!em->enabled) - goto endfilesystem; - // Initialize optional as zero, to identify when there are not partitions to monitor em->optional = 0; @@ -613,7 +614,7 @@ void *ebpf_filesystem_thread(void *ptr) if (em->optional) info("Netdata cannot monitor the filesystems used on this host."); - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endfilesystem; } @@ -630,8 +631,7 @@ void *ebpf_filesystem_thread(void *ptr) filesystem_collector(em); endfilesystem: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_filesystem.h b/collectors/ebpf.plugin/ebpf_filesystem.h index f6a10c874..0d558df7d 100644 --- a/collectors/ebpf.plugin/ebpf_filesystem.h +++ b/collectors/ebpf.plugin/ebpf_filesystem.h @@ -43,7 +43,7 @@ enum netdata_filesystem_table { NETDATA_ADDR_FS_TABLE }; -extern void *ebpf_filesystem_thread(void *ptr); +void *ebpf_filesystem_thread(void *ptr); extern struct config fs_config; #endif /* NETDATA_EBPF_FILESYSTEM_H */ diff --git a/collectors/ebpf.plugin/ebpf_hardirq.c b/collectors/ebpf.plugin/ebpf_hardirq.c index 41a881647..b07dd24ca 100644 --- a/collectors/ebpf.plugin/ebpf_hardirq.c +++ b/collectors/ebpf.plugin/ebpf_hardirq.c @@ -135,10 +135,43 @@ static hardirq_ebpf_val_t *hardirq_ebpf_vals = NULL; // tmp store for static hard IRQ values we get from a per-CPU eBPF map. static hardirq_ebpf_static_val_t *hardirq_ebpf_static_vals = NULL; -static struct netdata_static_thread hardirq_threads = {"HARDIRQ KERNEL", - NULL, NULL, 1, NULL, - NULL, NULL }; -static enum ebpf_threads_status ebpf_hardirq_exited = NETDATA_THREAD_EBPF_RUNNING; +static struct netdata_static_thread hardirq_threads = { + .name = "HARDIRQ KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; + +/** + * Hardirq Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_hardirq_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + freez(hardirq_threads.thread); + for (int i = 0; hardirq_tracepoints[i].class != NULL; i++) { + ebpf_disable_tracepoint(&hardirq_tracepoints[i]); + } + freez(hardirq_ebpf_vals); + freez(hardirq_ebpf_static_vals); + + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; +} /** * Hardirq Exit @@ -150,12 +183,8 @@ static enum ebpf_threads_status ebpf_hardirq_exited = NETDATA_THREAD_EBPF_RUNNIN static void hardirq_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_hardirq_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*hardirq_threads.thread); + ebpf_hardirq_free(em); } /** @@ -168,19 +197,7 @@ static void hardirq_exit(void *ptr) static void hardirq_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - - if (ebpf_hardirq_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - freez(hardirq_threads.thread); - for (int i = 0; hardirq_tracepoints[i].class != NULL; i++) { - ebpf_disable_tracepoint(&hardirq_tracepoints[i]); - } - freez(hardirq_ebpf_vals); - freez(hardirq_ebpf_static_vals); - - hardirq_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_hardirq_free(em); } /***************************************************************** @@ -323,16 +340,12 @@ static void *hardirq_reader(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; usec_t step = NETDATA_HARDIRQ_SLEEP_MS * em->update_every; - while (ebpf_hardirq_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - UNUSED(dt); - if (ebpf_hardirq_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); hardirq_read_latency_map(hardirq_maps[HARDIRQ_MAP_LATENCY].map_fd); hardirq_read_latency_static_map(hardirq_maps[HARDIRQ_MAP_LATENCY_STATIC].map_fd); } - ebpf_hardirq_exited = NETDATA_THREAD_EBPF_STOPPED; netdata_thread_cleanup_pop(1); return NULL; @@ -472,26 +485,21 @@ void *ebpf_hardirq_thread(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; em->maps = hardirq_maps; - if (!em->enabled) { - goto endhardirq; - } - if (ebpf_enable_tracepoints(hardirq_tracepoints) == 0) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endhardirq; } em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endhardirq; } hardirq_collector(em); endhardirq: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); diff --git a/collectors/ebpf.plugin/ebpf_hardirq.h b/collectors/ebpf.plugin/ebpf_hardirq.h index 4c8a7a098..381da57d8 100644 --- a/collectors/ebpf.plugin/ebpf_hardirq.h +++ b/collectors/ebpf.plugin/ebpf_hardirq.h @@ -68,6 +68,6 @@ typedef struct hardirq_static_val { } hardirq_static_val_t; extern struct config hardirq_config; -extern void *ebpf_hardirq_thread(void *ptr); +void *ebpf_hardirq_thread(void *ptr); #endif /* NETDATA_EBPF_HARDIRQ_H */ diff --git a/collectors/ebpf.plugin/ebpf_mdflush.c b/collectors/ebpf.plugin/ebpf_mdflush.c index 4dca04505..dc805da23 100644 --- a/collectors/ebpf.plugin/ebpf_mdflush.c +++ b/collectors/ebpf.plugin/ebpf_mdflush.c @@ -35,10 +35,39 @@ static avl_tree_lock mdflush_pub; // tmp store for mdflush values we get from a per-CPU eBPF map. static mdflush_ebpf_val_t *mdflush_ebpf_vals = NULL; -static struct netdata_static_thread mdflush_threads = {"MDFLUSH KERNEL", - NULL, NULL, 1, NULL, - NULL, NULL }; -static enum ebpf_threads_status ebpf_mdflush_exited = NETDATA_THREAD_EBPF_RUNNING; +static struct netdata_static_thread mdflush_threads = { + .name = "MDFLUSH KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; + +/** + * MDflush Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_mdflush_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + freez(mdflush_ebpf_vals); + freez(mdflush_threads.thread); + + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; +} /** * MDflush exit @@ -50,12 +79,7 @@ static enum ebpf_threads_status ebpf_mdflush_exited = NETDATA_THREAD_EBPF_RUNNIN static void mdflush_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_mdflush_exited = NETDATA_THREAD_EBPF_STOPPING; + ebpf_mdflush_free(em); } /** @@ -68,14 +92,8 @@ static void mdflush_exit(void *ptr) static void mdflush_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_mdflush_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - freez(mdflush_ebpf_vals); - freez(mdflush_threads.thread); - - mdflush_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + netdata_thread_cancel(*mdflush_threads.thread); + ebpf_mdflush_free(em); } /** @@ -182,17 +200,12 @@ static void *mdflush_reader(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; usec_t step = NETDATA_MDFLUSH_SLEEP_MS * em->update_every; - while (ebpf_mdflush_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - UNUSED(dt); - if (ebpf_mdflush_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); mdflush_read_count_map(); } - ebpf_mdflush_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -295,26 +308,25 @@ void *ebpf_mdflush_thread(void *ptr) char *md_flush_request = ebpf_find_symbol("md_flush_request"); if (!md_flush_request) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; error("Cannot monitor MD devices, because md is not loaded."); } freez(md_flush_request); - if (!em->enabled) { + if (em->thread->enabled == NETDATA_THREAD_EBPF_STOPPED) { goto endmdflush; } em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { - em->enabled = CONFIG_BOOLEAN_NO; + em->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endmdflush; } mdflush_collector(em); endmdflush: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); diff --git a/collectors/ebpf.plugin/ebpf_mdflush.h b/collectors/ebpf.plugin/ebpf_mdflush.h index 59856ad67..b04eefd28 100644 --- a/collectors/ebpf.plugin/ebpf_mdflush.h +++ b/collectors/ebpf.plugin/ebpf_mdflush.h @@ -35,7 +35,7 @@ typedef struct netdata_mdflush { uint64_t cnt; } netdata_mdflush_t; -extern void *ebpf_mdflush_thread(void *ptr); +void *ebpf_mdflush_thread(void *ptr); extern struct config mdflush_config; diff --git a/collectors/ebpf.plugin/ebpf_mount.c b/collectors/ebpf.plugin/ebpf_mount.c index bca467bce..ec1f07a65 100644 --- a/collectors/ebpf.plugin/ebpf_mount.c +++ b/collectors/ebpf.plugin/ebpf_mount.c @@ -22,14 +22,20 @@ static netdata_idx_t *mount_values = NULL; static netdata_idx_t mount_hash_values[NETDATA_MOUNT_END]; -struct netdata_static_thread mount_thread = {"MOUNT KERNEL", - NULL, NULL, 1, NULL, - NULL, NULL}; +struct netdata_static_thread mount_thread = { + .name = "MOUNT KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; netdata_ebpf_targets_t mount_targets[] = { {.name = "mount", .mode = EBPF_LOAD_TRAMPOLINE}, {.name = "umount", .mode = EBPF_LOAD_TRAMPOLINE}, {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}}; -static enum ebpf_threads_status ebpf_mount_exited = NETDATA_THREAD_EBPF_RUNNING; #ifdef LIBBPF_MAJOR_VERSION #include "includes/mount.skel.h" // BTF code @@ -223,6 +229,36 @@ static inline int ebpf_mount_load_and_attach(struct mount_bpf *obj, ebpf_module_ * *****************************************************************/ +/** + * Mount Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_mount_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + freez(mount_thread.thread); + freez(mount_values); + +#ifdef LIBBPF_MAJOR_VERSION + if (bpf_obj) + mount_bpf__destroy(bpf_obj); +#endif + + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} + /** * Mount Exit * @@ -233,12 +269,8 @@ static inline int ebpf_mount_load_and_attach(struct mount_bpf *obj, ebpf_module_ static void ebpf_mount_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_mount_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*mount_thread.thread); + ebpf_mount_free(em); } /** @@ -251,19 +283,7 @@ static void ebpf_mount_exit(void *ptr) static void ebpf_mount_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_mount_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - freez(mount_thread.thread); - freez(mount_values); - -#ifdef LIBBPF_MAJOR_VERSION - if (bpf_obj) - mount_bpf__destroy(bpf_obj); -#endif - - mount_thread.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_mount_free(em); } /***************************************************************** @@ -317,17 +337,12 @@ void *ebpf_mount_read_hash(void *ptr) usec_t step = NETDATA_LATENCY_MOUNT_SLEEP_MS * em->update_every; //This will be cancelled by its parent - while (ebpf_mount_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_mount_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); read_global_table(); } - ebpf_mount_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -435,7 +450,7 @@ static void ebpf_create_mount_charts(int update_every) static int ebpf_mount_load_bpf(ebpf_module_t *em) { int ret = 0; - if (em->load == EBPF_LOAD_LEGACY) { + if (em->load & EBPF_LOAD_LEGACY) { em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { em->enabled = CONFIG_BOOLEAN_NO; @@ -474,14 +489,11 @@ void *ebpf_mount_thread(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; em->maps = mount_maps; - if (!em->enabled) - goto endmount; - #ifdef LIBBPF_MAJOR_VERSION ebpf_adjust_thread_load(em, default_btf); #endif if (ebpf_mount_load_bpf(em)) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endmount; } @@ -498,8 +510,7 @@ void *ebpf_mount_thread(void *ptr) mount_collector(em); endmount: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_mount.h b/collectors/ebpf.plugin/ebpf_mount.h index d4f48efd1..5a8d11a59 100644 --- a/collectors/ebpf.plugin/ebpf_mount.h +++ b/collectors/ebpf.plugin/ebpf_mount.h @@ -38,7 +38,7 @@ enum netdata_mount_syscalls { }; extern struct config mount_config; -extern void *ebpf_mount_thread(void *ptr); +void *ebpf_mount_thread(void *ptr); extern netdata_ebpf_targets_t mount_targets[]; #endif /* NETDATA_EBPF_MOUNT_H */ diff --git a/collectors/ebpf.plugin/ebpf_oomkill.c b/collectors/ebpf.plugin/ebpf_oomkill.c index 33f505b0e..d93e4159e 100644 --- a/collectors/ebpf.plugin/ebpf_oomkill.c +++ b/collectors/ebpf.plugin/ebpf_oomkill.c @@ -46,8 +46,7 @@ static netdata_publish_syscall_t oomkill_publish_aggregated = {.name = "oomkill" static void oomkill_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - - em->enabled = NETDATA_MAIN_THREAD_EXITED; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; } static void oomkill_write_data(int32_t *keys, uint32_t total) @@ -132,25 +131,18 @@ static void ebpf_create_systemd_oomkill_charts(int update_every) * Send Systemd charts * * Send collected data to Netdata. - * - * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned - * otherwise function returns 1 to avoid chart recreation */ -static int ebpf_send_systemd_oomkill_charts() +static void ebpf_send_systemd_oomkill_charts() { - int ret = 1; ebpf_cgroup_target_t *ect; write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_OOMKILL_CHART); for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, (long long) ect->oomkill); ect->oomkill = 0; - } else if (unlikely(ect->systemd)) - ret = 0; + } } write_end_chart(); - - return ret; } /* @@ -199,12 +191,10 @@ void ebpf_oomkill_send_cgroup_data(int update_every) int has_systemd = shm_ebpf_cgroup.header->systemd_enabled; if (has_systemd) { - static int systemd_charts = 0; - if (!systemd_charts) { + if (send_cgroup_chart) { ebpf_create_systemd_oomkill_charts(update_every); - systemd_charts = 1; } - systemd_charts = ebpf_send_systemd_oomkill_charts(); + ebpf_send_systemd_oomkill_charts(); } for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { @@ -367,27 +357,33 @@ void *ebpf_oomkill_thread(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; em->maps = oomkill_maps; +#define NETDATA_DEFAULT_OOM_DISABLED_MSG "Disabling OOMKILL thread, because" if (unlikely(!all_pids || !em->apps_charts)) { // When we are not running integration with apps, we won't fill necessary variables for this thread to run, so // we need to disable it. - if (em->enabled) - info("Disabling OOMKILL thread, because apps integration is completely disabled."); + if (em->thread->enabled) + info("%s apps integration is completely disabled.", NETDATA_DEFAULT_OOM_DISABLED_MSG); + + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + } else if (running_on_kernel < NETDATA_EBPF_KERNEL_4_14) { + if (em->thread->enabled) + info("%s kernel does not have necessary tracepoints.", NETDATA_DEFAULT_OOM_DISABLED_MSG); - em->enabled = 0; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; } - if (!em->enabled) { + if (em->thread->enabled == NETDATA_THREAD_EBPF_STOPPED) { goto endoomkill; } if (ebpf_enable_tracepoints(oomkill_tracepoints) == 0) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endoomkill; } em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endoomkill; } @@ -398,8 +394,7 @@ void *ebpf_oomkill_thread(void *ptr) oomkill_collector(em); endoomkill: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); diff --git a/collectors/ebpf.plugin/ebpf_oomkill.h b/collectors/ebpf.plugin/ebpf_oomkill.h index b5f04c74c..786086384 100644 --- a/collectors/ebpf.plugin/ebpf_oomkill.h +++ b/collectors/ebpf.plugin/ebpf_oomkill.h @@ -26,7 +26,7 @@ typedef uint8_t oomkill_ebpf_val_t; #define NETDATA_CGROUP_OOMKILLS_CONTEXT "cgroup.oomkills" extern struct config oomkill_config; -extern void *ebpf_oomkill_thread(void *ptr); -extern void ebpf_oomkill_create_apps_charts(struct ebpf_module *em, void *ptr); +void *ebpf_oomkill_thread(void *ptr); +void ebpf_oomkill_create_apps_charts(struct ebpf_module *em, void *ptr); #endif /* NETDATA_EBPF_OOMKILL_H */ diff --git a/collectors/ebpf.plugin/ebpf_process.c b/collectors/ebpf.plugin/ebpf_process.c index f6b379a51..682577da7 100644 --- a/collectors/ebpf.plugin/ebpf_process.c +++ b/collectors/ebpf.plugin/ebpf_process.c @@ -46,6 +46,7 @@ ebpf_process_stat_t **global_process_stats = NULL; ebpf_process_publish_apps_t **current_apps_data = NULL; int process_enabled = 0; +bool publish_internal_metrics = true; struct config process_config = { .first_section = NULL, .last_section = NULL, @@ -53,13 +54,20 @@ struct config process_config = { .first_section = NULL, .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare }, .rwlock = AVL_LOCK_INITIALIZER } }; -static struct netdata_static_thread cgroup_thread = {"EBPF CGROUP", NULL, NULL, - 1, NULL, NULL, NULL}; -static enum ebpf_threads_status ebpf_process_exited = NETDATA_THREAD_EBPF_RUNNING; - static char *threads_stat[NETDATA_EBPF_THREAD_STAT_END] = {"total", "running"}; static char *load_event_stat[NETDATA_EBPF_LOAD_STAT_END] = {"legacy", "co-re"}; +static struct netdata_static_thread cgroup_thread = { + .name = "EBPF CGROUP", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; + /***************************************************************** * * PROCESS DATA AND SEND TO NETDATA @@ -318,6 +326,55 @@ static void ebpf_process_update_apps_data() } } +/** + * Cgroup Exit + * + * Function used with netdata_thread_clean_push + * + * @param ptr unused argument + */ +static void ebpf_cgroup_exit(void *ptr) +{ + UNUSED(ptr); +} + +/** + * Cgroup update shm + * + * This is the thread callback. + * This thread is necessary, because we cannot freeze the whole plugin to read the data from shared memory. + * + * @param ptr It is a NULL value for this thread. + * + * @return It always returns NULL. + */ +void *ebpf_cgroup_update_shm(void *ptr) +{ + netdata_thread_cleanup_push(ebpf_cgroup_exit, ptr); + heartbeat_t hb; + heartbeat_init(&hb); + + usec_t step = 3 * USEC_PER_SEC; + int counter = NETDATA_EBPF_CGROUP_UPDATE - 1; + //This will be cancelled by its parent + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); + + // We are using a small heartbeat time to wake up thread, + // but we should not update so frequently the shared memory data + if (++counter >= NETDATA_EBPF_CGROUP_UPDATE) { + counter = 0; + if (!shm_ebpf_cgroup.header) + ebpf_map_cgroup_shared_memory(); + + ebpf_parse_cgroup_shm_data(); + } + } + + netdata_thread_cleanup_pop(1); + return NULL; +} + /** * Update cgroup * @@ -492,6 +549,21 @@ static inline void ebpf_create_statistic_load_chart(ebpf_module_t *em) ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]); } +/** + * Update Internal Metric variable + * + * By default eBPF.plugin sends internal metrics for netdata, but user can + * disable this. + * + * The function updates the variable used to send charts. + */ +static void update_internal_metric_variable() +{ + const char *s = getenv("NETDATA_INTERNALS_MONITORING"); + if (s && *s && strcmp(s, "NO") == 0) + publish_internal_metrics = false; +} + /** * Create Statistics Charts * @@ -501,6 +573,10 @@ static inline void ebpf_create_statistic_load_chart(ebpf_module_t *em) */ static void ebpf_create_statistic_charts(ebpf_module_t *em) { + update_internal_metric_variable(); + if (!publish_internal_metrics) + return; + ebpf_create_statistic_thread_chart(em); ebpf_create_statistic_load_chart(em); @@ -658,32 +734,18 @@ static void ebpf_process_disable_tracepoints() * @param ptr thread data. */ static void ebpf_process_exit(void *ptr) -{ - (void)ptr; - ebpf_process_exited = NETDATA_THREAD_EBPF_STOPPING; -} - -/** - * Process cleanup - * - * Cleanup allocated memory. - * - * @param ptr thread data. - */ -static void ebpf_process_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_process_exited != NETDATA_THREAD_EBPF_STOPPED) - return; ebpf_cleanup_publish_syscall(process_publish_aggregated); freez(process_hash_values); - freez(cgroup_thread.thread); ebpf_process_disable_tracepoints(); - cgroup_thread.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); + pthread_cancel(*cgroup_thread.thread); } /***************************************************************** @@ -692,47 +754,6 @@ static void ebpf_process_cleanup(void *ptr) * *****************************************************************/ -/** - * Cgroup update shm - * - * This is the thread callback. - * This thread is necessary, because we cannot freeze the whole plugin to read the data from shared memory. - * - * @param ptr It is a NULL value for this thread. - * - * @return It always returns NULL. - */ -void *ebpf_cgroup_update_shm(void *ptr) -{ - netdata_thread_cleanup_push(ebpf_process_cleanup, ptr); - heartbeat_t hb; - heartbeat_init(&hb); - - usec_t step = 3 * USEC_PER_SEC; - int counter = NETDATA_EBPF_CGROUP_UPDATE - 1; - //This will be cancelled by its parent - while (ebpf_process_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_process_exited == NETDATA_THREAD_EBPF_STOPPING) - break; - - // We are using a small heartbeat time to wake up thread, - // but we should not update so frequently the shared memory data - if (++counter >= NETDATA_EBPF_CGROUP_UPDATE) { - counter = 0; - if (!shm_ebpf_cgroup.header) - ebpf_map_cgroup_shared_memory(); - - ebpf_parse_cgroup_shm_data(); - } - } - - ebpf_process_exited = NETDATA_THREAD_EBPF_STOPPED; - - netdata_thread_cleanup_pop(1); - return NULL; -} /** * Sum PIDs @@ -945,20 +966,15 @@ static void ebpf_create_systemd_process_charts(ebpf_module_t *em) * Send collected data to Netdata. * * @param em the structure with thread information - * - * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned - * otherwise function returns 1 to avoid chart recreation */ -static int ebpf_send_systemd_process_charts(ebpf_module_t *em) +static void ebpf_send_systemd_process_charts(ebpf_module_t *em) { - int ret = 1; ebpf_cgroup_target_t *ect; write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_SYSCALL_APPS_TASK_PROCESS); for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, ect->publish_systemd_ps.create_process); - } else if (unlikely(ect->systemd)) - ret = 0; + } } write_end_chart(); @@ -995,8 +1011,6 @@ static int ebpf_send_systemd_process_charts(ebpf_module_t *em) } write_end_chart(); } - - return ret; } /** @@ -1018,13 +1032,11 @@ static void ebpf_process_send_cgroup_data(ebpf_module_t *em) int has_systemd = shm_ebpf_cgroup.header->systemd_enabled; if (has_systemd) { - static int systemd_chart = 0; - if (!systemd_chart) { + if (send_cgroup_chart) { ebpf_create_systemd_process_charts(em); - systemd_chart = 1; } - systemd_chart = ebpf_send_systemd_process_charts(em); + ebpf_send_systemd_process_charts(em); } for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { @@ -1071,6 +1083,9 @@ void ebpf_process_update_cgroup_algorithm() */ void ebpf_send_statistic_data() { + if (!publish_internal_metrics) + return; + write_begin_chart(NETDATA_MONITORING_FAMILY, NETDATA_EBPF_THREADS); write_chart_dimension(threads_stat[NETDATA_EBPF_THREAD_STAT_TOTAL], (long long)plugin_statistics.threads); write_chart_dimension(threads_stat[NETDATA_EBPF_THREAD_STAT_RUNNING], (long long)plugin_statistics.running); @@ -1089,11 +1104,12 @@ void ebpf_send_statistic_data() */ static void process_collector(ebpf_module_t *em) { + // Start cgroup integration before other threads cgroup_thread.thread = mallocz(sizeof(netdata_thread_t)); cgroup_thread.start_routine = ebpf_cgroup_update_shm; netdata_thread_create(cgroup_thread.thread, cgroup_thread.name, NETDATA_THREAD_OPTION_DEFAULT, - ebpf_cgroup_update_shm, em); + ebpf_cgroup_update_shm, NULL); heartbeat_t hb; heartbeat_init(&hb); @@ -1119,8 +1135,6 @@ static void process_collector(ebpf_module_t *em) update_apps_list = 0; cleanup_exited_pids(); collect_data_for_all_processes(pid_fd); - - ebpf_create_apps_charts(apps_groups_root_target); } pthread_mutex_unlock(&collect_data_mutex); @@ -1131,6 +1145,8 @@ static void process_collector(ebpf_module_t *em) netdata_apps_integration_flags_t apps_enabled = em->apps_charts; pthread_mutex_lock(&collect_data_mutex); + + ebpf_create_apps_charts(apps_groups_root_target); if (all_pids_count > 0) { if (apps_enabled) { ebpf_process_update_apps_data(); @@ -1210,34 +1226,6 @@ static void set_local_pointers() * *****************************************************************/ -/** - * - */ -static void wait_for_all_threads_die() -{ - ebpf_modules[EBPF_MODULE_PROCESS_IDX].enabled = 0; - - heartbeat_t hb; - heartbeat_init(&hb); - - int max = 10; - int i; - for (i = 0; i < max; i++) { - heartbeat_next(&hb, 200000); - - size_t j, counter = 0, compare = 0; - for (j = 0; ebpf_modules[j].thread_name; j++) { - if (!ebpf_modules[j].enabled) - counter++; - - compare++; - } - - if (counter == compare) - break; - } -} - /** * Enable tracepoints * @@ -1334,7 +1322,6 @@ endprocess: if (!em->enabled) ebpf_update_disabled_plugin_stats(em); - wait_for_all_threads_die(); netdata_thread_cleanup_pop(1); return NULL; } diff --git a/collectors/ebpf.plugin/ebpf_shm.c b/collectors/ebpf.plugin/ebpf_shm.c index bd928cbdc..f81287d82 100644 --- a/collectors/ebpf.plugin/ebpf_shm.c +++ b/collectors/ebpf.plugin/ebpf_shm.c @@ -34,9 +34,16 @@ static ebpf_local_maps_t shm_maps[] = {{.name = "tbl_pid_shm", .internal_input = .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED}, {.name = NULL, .internal_input = 0, .user_input = 0}}; -struct netdata_static_thread shm_threads = {"SHM KERNEL", NULL, NULL, 1, - NULL, NULL, NULL}; -static enum ebpf_threads_status ebpf_shm_exited = NETDATA_THREAD_EBPF_RUNNING; +struct netdata_static_thread shm_threads = { + .name = "SHM KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; netdata_ebpf_targets_t shm_targets[] = { {.name = "shmget", .mode = EBPF_LOAD_TRAMPOLINE}, {.name = "shmat", .mode = EBPF_LOAD_TRAMPOLINE}, @@ -83,6 +90,7 @@ static void ebpf_disable_probe(struct shm_bpf *obj) bpf_program__set_autoload(obj->progs.netdata_shmat_probe, false); bpf_program__set_autoload(obj->progs.netdata_shmdt_probe, false); bpf_program__set_autoload(obj->progs.netdata_shmctl_probe, false); + bpf_program__set_autoload(obj->progs.netdata_shm_release_task_probe, false); } /* @@ -98,6 +106,7 @@ static void ebpf_disable_trampoline(struct shm_bpf *obj) bpf_program__set_autoload(obj->progs.netdata_shmat_fentry, false); bpf_program__set_autoload(obj->progs.netdata_shmdt_fentry, false); bpf_program__set_autoload(obj->progs.netdata_shmctl_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_shm_release_task_fentry, false); } /** @@ -130,6 +139,9 @@ static void ebpf_set_trampoline_target(struct shm_bpf *obj) shm_targets[NETDATA_KEY_SHMCTL_CALL].name, running_on_kernel); bpf_program__set_attach_target(obj->progs.netdata_shmctl_fentry, 0, syscall); + + bpf_program__set_attach_target(obj->progs.netdata_shm_release_task_fentry, 0, + EBPF_COMMON_FNCT_CLEAN_UP); } /** @@ -177,6 +189,13 @@ static int ebpf_shm_attach_probe(struct shm_bpf *obj) if (ret) return -1; + obj->links.netdata_shm_release_task_probe = bpf_program__attach_kprobe(obj->progs.netdata_shm_release_task_probe, + false, EBPF_COMMON_FNCT_CLEAN_UP); + ret = (int)libbpf_get_error(obj->links.netdata_shm_release_task_probe); + if (ret) + return -1; + + return 0; } @@ -192,6 +211,33 @@ static void ebpf_shm_set_hash_tables(struct shm_bpf *obj) shm_maps[NETDATA_SHM_GLOBAL_TABLE].map_fd = bpf_map__fd(obj->maps.tbl_shm); } +/** + * Disable Release Task + * + * Disable release task when apps is not enabled. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_shm_disable_release_task(struct shm_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_shm_release_task_probe, false); + bpf_program__set_autoload(obj->progs.netdata_shm_release_task_fentry, false); +} + +/** + * Adjust Map Size + * + * Resize maps according input from users. + * + * @param obj is the main structure for bpf objects. + * @param em structure with configuration + */ +static void ebpf_shm_adjust_map_size(struct shm_bpf *obj, ebpf_module_t *em) +{ + ebpf_update_map_size(obj->maps.tbl_pid_shm, &shm_maps[NETDATA_PID_SHM_TABLE], + em, bpf_map__name(obj->maps.tbl_pid_shm)); +} + /** * Load and attach * @@ -221,6 +267,10 @@ static inline int ebpf_shm_load_and_attach(struct shm_bpf *obj, ebpf_module_t *e ebpf_disable_trampoline(obj); } + ebpf_shm_adjust_map_size(obj, em); + if (!em->apps_charts && !em->cgroup_charts) + ebpf_shm_disable_release_task(obj); + int ret = shm_bpf__load(obj); if (!ret) { if (test != EBPF_LOAD_PROBE && test != EBPF_LOAD_RETPROBE) @@ -239,6 +289,36 @@ static inline int ebpf_shm_load_and_attach(struct shm_bpf *obj, ebpf_module_t *e * FUNCTIONS TO CLOSE THE THREAD *****************************************************************/ +/** + * SHM Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_shm_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + ebpf_cleanup_publish_syscall(shm_publish_aggregated); + + freez(shm_vector); + freez(shm_values); + +#ifdef LIBBPF_MAJOR_VERSION + if (bpf_obj) + shm_bpf__destroy(bpf_obj); +#endif + + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; +} + /** * SHM Exit * @@ -249,12 +329,8 @@ static inline int ebpf_shm_load_and_attach(struct shm_bpf *obj, ebpf_module_t *e static void ebpf_shm_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_shm_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*shm_threads.thread); + ebpf_shm_free(em); } /** @@ -267,21 +343,7 @@ static void ebpf_shm_exit(void *ptr) static void ebpf_shm_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_shm_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - ebpf_cleanup_publish_syscall(shm_publish_aggregated); - - freez(shm_vector); - freez(shm_values); - -#ifdef LIBBPF_MAJOR_VERSION - if (bpf_obj) - shm_bpf__destroy(bpf_obj); -#endif - - shm_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_shm_free(em); } /***************************************************************** @@ -463,17 +525,12 @@ void *ebpf_shm_read_hash(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; usec_t step = NETDATA_SHM_SLEEP_MS * em->update_every; - while (ebpf_shm_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_shm_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); read_global_table(); } - ebpf_shm_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -721,20 +778,15 @@ static void ebpf_create_systemd_shm_charts(int update_every) * Send Systemd charts * * Send collected data to Netdata. - * - * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned - * otherwise function returns 1 to avoid chart recreation */ -static int ebpf_send_systemd_shm_charts() +static void ebpf_send_systemd_shm_charts() { - int ret = 1; ebpf_cgroup_target_t *ect; write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_SHMGET_CHART); for (ect = ebpf_cgroup_pids; ect; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, (long long)ect->publish_shm.get); - } else if (unlikely(ect->systemd)) - ret = 0; + } } write_end_chart(); @@ -761,8 +813,6 @@ static int ebpf_send_systemd_shm_charts() } } write_end_chart(); - - return ret; } /* @@ -810,13 +860,11 @@ void ebpf_shm_send_cgroup_data(int update_every) int has_systemd = shm_ebpf_cgroup.header->systemd_enabled; if (has_systemd) { - static int systemd_charts = 0; - if (!systemd_charts) { + if (send_cgroup_chart) { ebpf_create_systemd_shm_charts(update_every); - systemd_charts = 1; } - systemd_charts = ebpf_send_systemd_shm_charts(); + ebpf_send_systemd_shm_charts(); } for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { @@ -1008,7 +1056,9 @@ static void ebpf_create_shm_charts(int update_every) static int ebpf_shm_load_bpf(ebpf_module_t *em) { int ret = 0; - if (em->load == EBPF_LOAD_LEGACY) { + + ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_KEY_SHMGET_CALL].mode); + if (em->load & EBPF_LOAD_LEGACY) { em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { em->enabled = CONFIG_BOOLEAN_NO; @@ -1047,15 +1097,11 @@ void *ebpf_shm_thread(void *ptr) ebpf_update_pid_table(&shm_maps[NETDATA_PID_SHM_TABLE], em); - if (!em->enabled) { - goto endshm; - } - #ifdef LIBBPF_MAJOR_VERSION ebpf_adjust_thread_load(em, default_btf); #endif if (ebpf_shm_load_bpf(em)) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endshm; } @@ -1084,8 +1130,7 @@ void *ebpf_shm_thread(void *ptr) shm_collector(em); endshm: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_shm.h b/collectors/ebpf.plugin/ebpf_shm.h index 8e118a6f5..4e068819b 100644 --- a/collectors/ebpf.plugin/ebpf_shm.h +++ b/collectors/ebpf.plugin/ebpf_shm.h @@ -54,8 +54,8 @@ enum shm_counters { extern netdata_publish_shm_t **shm_pid; -extern void *ebpf_shm_thread(void *ptr); -extern void ebpf_shm_create_apps_charts(struct ebpf_module *em, void *ptr); +void *ebpf_shm_thread(void *ptr); +void ebpf_shm_create_apps_charts(struct ebpf_module *em, void *ptr); extern netdata_ebpf_targets_t shm_targets[]; extern struct config shm_config; diff --git a/collectors/ebpf.plugin/ebpf_socket.c b/collectors/ebpf.plugin/ebpf_socket.c index ba63934d5..3a023e4a4 100644 --- a/collectors/ebpf.plugin/ebpf_socket.c +++ b/collectors/ebpf.plugin/ebpf_socket.c @@ -23,7 +23,7 @@ static char *socket_id_names[NETDATA_MAX_SOCKET_VECTOR] = { "tcp_cleanup_rbuf", static ebpf_local_maps_t socket_maps[] = {{.name = "tbl_bandwidth", .internal_input = NETDATA_COMPILED_CONNECTIONS_ALLOWED, .user_input = NETDATA_MAXIMUM_CONNECTIONS_ALLOWED, - .type = NETDATA_EBPF_MAP_STATIC, + .type = NETDATA_EBPF_MAP_RESIZABLE | NETDATA_EBPF_MAP_PID, .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED}, {.name = "tbl_global_sock", .internal_input = NETDATA_SOCKET_COUNTER, @@ -87,10 +87,16 @@ netdata_ebpf_targets_t socket_targets[] = { {.name = "inet_csk_accept", .mode = {.name = "tcp_v6_connect", .mode = EBPF_LOAD_TRAMPOLINE}, {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}}; -struct netdata_static_thread socket_threads = {"EBPF SOCKET READ", - NULL, NULL, 1, NULL, - NULL, NULL }; -static enum ebpf_threads_status ebpf_socket_exited = NETDATA_THREAD_EBPF_RUNNING; +struct netdata_static_thread socket_threads = { + .name = "EBPF SOCKET READ", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; #ifdef LIBBPF_MAJOR_VERSION #include "includes/socket.skel.h" // BTF code @@ -581,35 +587,21 @@ static void clean_ip_structure(ebpf_network_viewer_ip_list_t **clean) } /** - * Socket exit + * Socket Free * - * Clean up the main thread. + * Cleanup variables after child threads to stop * * @param ptr thread data. */ -static void ebpf_socket_exit(void *ptr) +static void ebpf_socket_free(ebpf_module_t *em ) { - ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); return; } - - ebpf_socket_exited = NETDATA_THREAD_EBPF_STOPPING; -} - -/** - * Socket cleanup - * - * Clean up allocated addresses. - * - * @param ptr thread data. - */ -void ebpf_socket_cleanup(void *ptr) -{ - ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_socket_exited != NETDATA_THREAD_EBPF_STOPPED) - return; + pthread_mutex_unlock(&ebpf_exit_cleanup); ebpf_cleanup_publish_syscall(socket_publish_aggregated); freez(socket_hash_values); @@ -639,8 +631,37 @@ void ebpf_socket_cleanup(void *ptr) if (bpf_obj) socket_bpf__destroy(bpf_obj); #endif - socket_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} + +/** + * Socket exit + * + * Clean up the main thread. + * + * @param ptr thread data. + */ +static void ebpf_socket_exit(void *ptr) +{ + ebpf_module_t *em = (ebpf_module_t *)ptr; + netdata_thread_cancel(*socket_threads.thread); + ebpf_socket_free(em); +} + +/** + * Socket cleanup + * + * Clean up allocated addresses. + * + * @param ptr thread data. + */ +void ebpf_socket_cleanup(void *ptr) +{ + ebpf_module_t *em = (ebpf_module_t *)ptr; + ebpf_socket_free(em); } /***************************************************************** @@ -2146,11 +2167,8 @@ void *ebpf_socket_read_hash(void *ptr) int fd_ipv4 = socket_maps[NETDATA_SOCKET_TABLE_IPV4].map_fd; int fd_ipv6 = socket_maps[NETDATA_SOCKET_TABLE_IPV6].map_fd; int network_connection = em->optional; - while (ebpf_socket_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_socket_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); pthread_mutex_lock(&nv_mutex); read_listen_table(); @@ -2160,8 +2178,6 @@ void *ebpf_socket_read_hash(void *ptr) pthread_mutex_unlock(&nv_mutex); } - ebpf_socket_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -2702,20 +2718,15 @@ static void ebpf_create_systemd_socket_charts(int update_every) * Send Systemd charts * * Send collected data to Netdata. - * - * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned - * otherwise function returns 1 to avoid chart recreation */ -static int ebpf_send_systemd_socket_charts() +static void ebpf_send_systemd_socket_charts() { - int ret = 1; ebpf_cgroup_target_t *ect; write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_NET_APPS_CONNECTION_TCP_V4); for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, (long long)ect->publish_socket.call_tcp_v4_connection); - } else if (unlikely(ect->systemd)) - ret = 0; + } } write_end_chart(); @@ -2723,8 +2734,7 @@ static int ebpf_send_systemd_socket_charts() for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, (long long)ect->publish_socket.call_tcp_v6_connection); - } else - ret = 0; + } } write_end_chart(); @@ -2732,8 +2742,7 @@ static int ebpf_send_systemd_socket_charts() for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, (long long)ect->publish_socket.bytes_sent); - } else - ret = 0; + } } write_end_chart(); @@ -2784,8 +2793,6 @@ static int ebpf_send_systemd_socket_charts() } } write_end_chart(); - - return ret; } /** @@ -2821,12 +2828,10 @@ static void ebpf_socket_send_cgroup_data(int update_every) int has_systemd = shm_ebpf_cgroup.header->systemd_enabled; if (has_systemd) { - static int systemd_charts = 0; - if (!systemd_charts) { + if (send_cgroup_chart) { ebpf_create_systemd_socket_charts(update_every); - systemd_charts = 1; } - systemd_charts = ebpf_send_systemd_socket_charts(); + ebpf_send_systemd_socket_charts(); } for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { @@ -3875,7 +3880,7 @@ static int ebpf_socket_load_bpf(ebpf_module_t *em) { int ret = 0; - if (em->load == EBPF_LOAD_LEGACY) { + if (em->load & EBPF_LOAD_LEGACY) { em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { ret = -1; @@ -3923,11 +3928,8 @@ void *ebpf_socket_thread(void *ptr) parse_service_name_section(&socket_config); parse_table_size_options(&socket_config); - if (!em->enabled) - goto endsocket; - if (pthread_mutex_init(&nv_mutex, NULL)) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; error("Cannot initialize local mutex"); goto endsocket; } @@ -3967,8 +3969,7 @@ void *ebpf_socket_thread(void *ptr) socket_collector((usec_t)(em->update_every * USEC_PER_SEC), em); endsocket: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_socket.h b/collectors/ebpf.plugin/ebpf_socket.h index 711225acf..ca6b193f0 100644 --- a/collectors/ebpf.plugin/ebpf_socket.h +++ b/collectors/ebpf.plugin/ebpf_socket.h @@ -356,12 +356,12 @@ typedef struct netdata_vector_plot { } netdata_vector_plot_t; -extern void clean_port_structure(ebpf_network_viewer_port_list_t **clean); +void clean_port_structure(ebpf_network_viewer_port_list_t **clean); extern ebpf_network_viewer_port_list_t *listen_ports; -extern void update_listen_table(uint16_t value, uint16_t proto, netdata_passive_connection_t *values); -extern void parse_network_viewer_section(struct config *cfg); -extern void fill_ip_list(ebpf_network_viewer_ip_list_t **out, ebpf_network_viewer_ip_list_t *in, char *table); -extern void parse_service_name_section(struct config *cfg); +void update_listen_table(uint16_t value, uint16_t proto, netdata_passive_connection_t *values); +void parse_network_viewer_section(struct config *cfg); +void fill_ip_list(ebpf_network_viewer_ip_list_t **out, ebpf_network_viewer_ip_list_t *in, char *table); +void parse_service_name_section(struct config *cfg); extern ebpf_socket_publish_apps_t **socket_bandwidth_curr; extern struct config socket_config; diff --git a/collectors/ebpf.plugin/ebpf_softirq.c b/collectors/ebpf.plugin/ebpf_softirq.c index ed13f027f..3b5d15921 100644 --- a/collectors/ebpf.plugin/ebpf_softirq.c +++ b/collectors/ebpf.plugin/ebpf_softirq.c @@ -54,10 +54,45 @@ static softirq_val_t softirq_vals[] = { // tmp store for soft IRQ values we get from a per-CPU eBPF map. static softirq_ebpf_val_t *softirq_ebpf_vals = NULL; -static struct netdata_static_thread softirq_threads = {"SOFTIRQ KERNEL", - NULL, NULL, 1, NULL, - NULL, NULL }; -static enum ebpf_threads_status ebpf_softirq_exited = NETDATA_THREAD_EBPF_RUNNING; +static struct netdata_static_thread softirq_threads = { + .name = "SOFTIRQ KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; + +/** + * Cachestat Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_softirq_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + freez(softirq_threads.thread); + + for (int i = 0; softirq_tracepoints[i].class != NULL; i++) { + ebpf_disable_tracepoint(&softirq_tracepoints[i]); + } + freez(softirq_ebpf_vals); + + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_MAIN_THREAD_EXITED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} /** * Exit @@ -69,12 +104,8 @@ static enum ebpf_threads_status ebpf_softirq_exited = NETDATA_THREAD_EBPF_RUNNIN static void softirq_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_softirq_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*softirq_threads.thread); + ebpf_softirq_free(em); } /** @@ -87,18 +118,7 @@ static void softirq_exit(void *ptr) static void softirq_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_softirq_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - freez(softirq_threads.thread); - - for (int i = 0; softirq_tracepoints[i].class != NULL; i++) { - ebpf_disable_tracepoint(&softirq_tracepoints[i]); - } - freez(softirq_ebpf_vals); - - softirq_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_softirq_free(em); } /***************************************************************** @@ -138,15 +158,11 @@ static void *softirq_reader(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; usec_t step = NETDATA_SOFTIRQ_SLEEP_MS * em->update_every; - while (ebpf_softirq_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - UNUSED(dt); - if (ebpf_softirq_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); softirq_read_latency_map(); } - ebpf_softirq_exited = NETDATA_THREAD_EBPF_STOPPED; netdata_thread_cleanup_pop(1); return NULL; @@ -252,26 +268,21 @@ void *ebpf_softirq_thread(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; em->maps = softirq_maps; - if (!em->enabled) { - goto endsoftirq; - } - if (ebpf_enable_tracepoints(softirq_tracepoints) == 0) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endsoftirq; } em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endsoftirq; } softirq_collector(em); endsoftirq: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); diff --git a/collectors/ebpf.plugin/ebpf_softirq.h b/collectors/ebpf.plugin/ebpf_softirq.h index a22751895..7dcddbb49 100644 --- a/collectors/ebpf.plugin/ebpf_softirq.h +++ b/collectors/ebpf.plugin/ebpf_softirq.h @@ -29,6 +29,6 @@ typedef struct sofirq_val { } softirq_val_t; extern struct config softirq_config; -extern void *ebpf_softirq_thread(void *ptr); +void *ebpf_softirq_thread(void *ptr); #endif /* NETDATA_EBPF_SOFTIRQ_H */ diff --git a/collectors/ebpf.plugin/ebpf_swap.c b/collectors/ebpf.plugin/ebpf_swap.c index 71d0c402b..8199573a9 100644 --- a/collectors/ebpf.plugin/ebpf_swap.c +++ b/collectors/ebpf.plugin/ebpf_swap.c @@ -34,9 +34,16 @@ static ebpf_local_maps_t swap_maps[] = {{.name = "tbl_pid_swap", .internal_input .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED}, {.name = NULL, .internal_input = 0, .user_input = 0}}; -struct netdata_static_thread swap_threads = {"SWAP KERNEL", NULL, NULL, 1, - NULL, NULL, NULL}; -static enum ebpf_threads_status ebpf_swap_exited = NETDATA_THREAD_EBPF_RUNNING; +struct netdata_static_thread swap_threads = { + .name = "SWAP KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; netdata_ebpf_targets_t swap_targets[] = { {.name = "swap_readpage", .mode = EBPF_LOAD_TRAMPOLINE}, {.name = "swap_writepage", .mode = EBPF_LOAD_TRAMPOLINE}, @@ -71,6 +78,7 @@ static void ebpf_swap_disable_trampoline(struct swap_bpf *obj) { bpf_program__set_autoload(obj->progs.netdata_swap_readpage_fentry, false); bpf_program__set_autoload(obj->progs.netdata_swap_writepage_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_release_task_fentry, false); } /** @@ -87,6 +95,9 @@ static void ebpf_swap_set_trampoline_target(struct swap_bpf *obj) bpf_program__set_attach_target(obj->progs.netdata_swap_writepage_fentry, 0, swap_targets[NETDATA_KEY_SWAP_WRITEPAGE_CALL].name); + + bpf_program__set_attach_target(obj->progs.netdata_release_task_fentry, 0, + EBPF_COMMON_FNCT_CLEAN_UP); } /** @@ -114,6 +125,13 @@ static int ebpf_swap_attach_kprobe(struct swap_bpf *obj) if (ret) return -1; + obj->links.netdata_release_task_probe = bpf_program__attach_kprobe(obj->progs.netdata_release_task_probe, + false, + EBPF_COMMON_FNCT_CLEAN_UP); + ret = libbpf_get_error(obj->links.netdata_swap_writepage_probe); + if (ret) + return -1; + return 0; } @@ -145,6 +163,19 @@ static void ebpf_swap_adjust_map_size(struct swap_bpf *obj, ebpf_module_t *em) em, bpf_map__name(obj->maps.tbl_pid_swap)); } +/** + * Disable Release Task + * + * Disable release task when apps is not enabled. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_swap_disable_release_task(struct swap_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_release_task_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_release_task_probe, false); +} + /** * Load and attach * @@ -168,13 +199,16 @@ static inline int ebpf_swap_load_and_attach(struct swap_bpf *obj, ebpf_module_t ebpf_swap_disable_trampoline(obj); } + ebpf_swap_adjust_map_size(obj, em); + + if (!em->apps_charts && !em->cgroup_charts) + ebpf_swap_disable_release_task(obj); + int ret = swap_bpf__load(obj); if (ret) { return ret; } - ebpf_swap_adjust_map_size(obj, em); - ret = (test == EBPF_LOAD_TRAMPOLINE) ? swap_bpf__attach(obj) : ebpf_swap_attach_kprobe(obj); if (!ret) { ebpf_swap_set_hash_tables(obj); @@ -192,6 +226,38 @@ static inline int ebpf_swap_load_and_attach(struct swap_bpf *obj, ebpf_module_t * *****************************************************************/ +/** + * Cachestat Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_swap_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + ebpf_cleanup_publish_syscall(swap_publish_aggregated); + + freez(swap_vector); + freez(swap_values); + freez(swap_threads.thread); + +#ifdef LIBBPF_MAJOR_VERSION + if (bpf_obj) + swap_bpf__destroy(bpf_obj); +#endif + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} + /** * Swap exit * @@ -202,12 +268,8 @@ static inline int ebpf_swap_load_and_attach(struct swap_bpf *obj, ebpf_module_t static void ebpf_swap_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_swap_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*swap_threads.thread); + ebpf_swap_free(em); } /** @@ -220,21 +282,7 @@ static void ebpf_swap_exit(void *ptr) static void ebpf_swap_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_swap_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - ebpf_cleanup_publish_syscall(swap_publish_aggregated); - - freez(swap_vector); - freez(swap_values); - freez(swap_threads.thread); - -#ifdef LIBBPF_MAJOR_VERSION - if (bpf_obj) - swap_bpf__destroy(bpf_obj); -#endif - swap_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_swap_free(em); } /***************************************************************** @@ -401,17 +449,12 @@ void *ebpf_swap_read_hash(void *ptr) ebpf_module_t *em = (ebpf_module_t *)ptr; usec_t step = NETDATA_SWAP_SLEEP_MS * em->update_every; - while (ebpf_swap_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_swap_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); read_global_table(); } - ebpf_swap_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -505,20 +548,15 @@ static void ebpf_swap_sum_cgroup_pids(netdata_publish_swap_t *swap, struct pid_o * Send Systemd charts * * Send collected data to Netdata. - * - * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned - * otherwise function returns 1 to avoid chart recreation */ -static int ebpf_send_systemd_swap_charts() +static void ebpf_send_systemd_swap_charts() { - int ret = 1; ebpf_cgroup_target_t *ect; write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_MEM_SWAP_READ_CHART); for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, (long long) ect->publish_systemd_swap.read); - } else if (unlikely(ect->systemd)) - ret = 0; + } } write_end_chart(); @@ -529,8 +567,6 @@ static int ebpf_send_systemd_swap_charts() } } write_end_chart(); - - return ret; } /** @@ -644,14 +680,11 @@ void ebpf_swap_send_cgroup_data(int update_every) int has_systemd = shm_ebpf_cgroup.header->systemd_enabled; if (has_systemd) { - static int systemd_charts = 0; - if (!systemd_charts) { + if (send_cgroup_chart) { ebpf_create_systemd_swap_charts(update_every); - systemd_charts = 1; fflush(stdout); } - - systemd_charts = ebpf_send_systemd_swap_charts(); + ebpf_send_systemd_swap_charts(); } for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { @@ -812,7 +845,8 @@ static void ebpf_create_swap_charts(int update_every) static int ebpf_swap_load_bpf(ebpf_module_t *em) { int ret = 0; - if (em->load == EBPF_LOAD_LEGACY) { + ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_KEY_SWAP_READPAGE_CALL].mode); + if (em->load & EBPF_LOAD_LEGACY) { em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { ret = -1; @@ -852,14 +886,11 @@ void *ebpf_swap_thread(void *ptr) ebpf_update_pid_table(&swap_maps[NETDATA_PID_SWAP_TABLE], em); - if (!em->enabled) - goto endswap; - #ifdef LIBBPF_MAJOR_VERSION ebpf_adjust_thread_load(em, default_btf); #endif if (ebpf_swap_load_bpf(em)) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endswap; } @@ -877,8 +908,7 @@ void *ebpf_swap_thread(void *ptr) swap_collector(em); endswap: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_swap.h b/collectors/ebpf.plugin/ebpf_swap.h index 80c2c8e94..79182e52e 100644 --- a/collectors/ebpf.plugin/ebpf_swap.h +++ b/collectors/ebpf.plugin/ebpf_swap.h @@ -44,8 +44,8 @@ enum swap_counters { extern netdata_publish_swap_t **swap_pid; -extern void *ebpf_swap_thread(void *ptr); -extern void ebpf_swap_create_apps_charts(struct ebpf_module *em, void *ptr); +void *ebpf_swap_thread(void *ptr); +void ebpf_swap_create_apps_charts(struct ebpf_module *em, void *ptr); extern struct config swap_config; extern netdata_ebpf_targets_t swap_targets[]; diff --git a/collectors/ebpf.plugin/ebpf_sync.c b/collectors/ebpf.plugin/ebpf_sync.c index 0e56f541d..840497533 100644 --- a/collectors/ebpf.plugin/ebpf_sync.c +++ b/collectors/ebpf.plugin/ebpf_sync.c @@ -10,8 +10,16 @@ static netdata_publish_syscall_t sync_counter_publish_aggregated[NETDATA_SYNC_ID static netdata_idx_t sync_hash_values[NETDATA_SYNC_IDX_END]; -struct netdata_static_thread sync_threads = {"SYNC KERNEL", NULL, NULL, 1, - NULL, NULL, NULL}; +struct netdata_static_thread sync_threads = { + .name = "SYNC KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; static ebpf_local_maps_t sync_maps[] = {{.name = "tbl_sync", .internal_input = NETDATA_SYNC_END, .user_input = 0, .type = NETDATA_EBPF_MAP_STATIC, @@ -48,8 +56,6 @@ netdata_ebpf_targets_t sync_targets[] = { {.name = NETDATA_SYSCALLS_SYNC, .mode {.name = NETDATA_SYSCALLS_FDATASYNC, .mode = EBPF_LOAD_TRAMPOLINE}, {.name = NETDATA_SYSCALLS_SYNC_FILE_RANGE, .mode = EBPF_LOAD_TRAMPOLINE}, {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}}; -static enum ebpf_threads_status ebpf_sync_exited = NETDATA_THREAD_EBPF_RUNNING; - #ifdef LIBBPF_MAJOR_VERSION /***************************************************************** @@ -183,6 +189,7 @@ static inline int ebpf_sync_load_and_attach(struct sync_bpf *obj, ebpf_module_t * *****************************************************************/ +#ifdef LIBBPF_MAJOR_VERSION /** * Cleanup Objects * @@ -193,22 +200,37 @@ void ebpf_sync_cleanup_objects() int i; for (i = 0; local_syscalls[i].syscall; i++) { ebpf_sync_syscalls_t *w = &local_syscalls[i]; - if (w->probe_links) { - struct bpf_program *prog; - size_t j = 0 ; - bpf_object__for_each_program(prog, w->objects) { - bpf_link__destroy(w->probe_links[j]); - j++; - } - freez(w->probe_links); - if (w->objects) - bpf_object__close(w->objects); - } -#ifdef LIBBPF_MAJOR_VERSION - else if (w->sync_obj) + if (w->sync_obj) sync_bpf__destroy(w->sync_obj); + } +} #endif + +/** + * Sync Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_sync_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; } + pthread_mutex_unlock(&ebpf_exit_cleanup); + +#ifdef LIBBPF_MAJOR_VERSION + ebpf_sync_cleanup_objects(); +#endif + freez(sync_threads.thread); + + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); } /** @@ -221,12 +243,8 @@ void ebpf_sync_cleanup_objects() static void ebpf_sync_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_sync_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*sync_threads.thread); + ebpf_sync_free(em); } /** @@ -237,14 +255,7 @@ static void ebpf_sync_exit(void *ptr) static void ebpf_sync_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_sync_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - ebpf_sync_cleanup_objects(); - freez(sync_threads.thread); - - sync_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_sync_free(em); } /***************************************************************** @@ -291,7 +302,7 @@ static int ebpf_sync_initialize_syscall(ebpf_module_t *em) for (i = 0; local_syscalls[i].syscall; i++) { ebpf_sync_syscalls_t *w = &local_syscalls[i]; if (w->enabled) { - if (em->load == EBPF_LOAD_LEGACY) { + if (em->load & EBPF_LOAD_LEGACY) { if (ebpf_sync_load_legacy(w, em)) errors++; @@ -372,17 +383,12 @@ void *ebpf_sync_read_hash(void *ptr) heartbeat_init(&hb); usec_t step = NETDATA_EBPF_SYNC_SLEEP_MS * em->update_every; - while (ebpf_sync_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_sync_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); read_global_table(); } - ebpf_sync_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -572,14 +578,11 @@ void *ebpf_sync_thread(void *ptr) ebpf_sync_parse_syscalls(); - if (!em->enabled) - goto endsync; - #ifdef LIBBPF_MAJOR_VERSION ebpf_adjust_thread_load(em, default_btf); #endif if (ebpf_sync_initialize_syscall(em)) { - em->enabled = CONFIG_BOOLEAN_NO; + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endsync; } @@ -598,8 +601,7 @@ void *ebpf_sync_thread(void *ptr) sync_collector(em); endsync: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_sync.h b/collectors/ebpf.plugin/ebpf_sync.h index a52434c17..cace2a1cf 100644 --- a/collectors/ebpf.plugin/ebpf_sync.h +++ b/collectors/ebpf.plugin/ebpf_sync.h @@ -52,7 +52,7 @@ enum netdata_sync_table { NETDATA_SYNC_GLOBAL_TABLE }; -extern void *ebpf_sync_thread(void *ptr); +void *ebpf_sync_thread(void *ptr); extern struct config sync_config; extern netdata_ebpf_targets_t sync_targets[]; diff --git a/collectors/ebpf.plugin/ebpf_vfs.c b/collectors/ebpf.plugin/ebpf_vfs.c index 6746cc624..ad6de4a07 100644 --- a/collectors/ebpf.plugin/ebpf_vfs.c +++ b/collectors/ebpf.plugin/ebpf_vfs.c @@ -34,16 +34,402 @@ struct config vfs_config = { .first_section = NULL, .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare }, .rwlock = AVL_LOCK_INITIALIZER } }; -struct netdata_static_thread vfs_threads = {"VFS KERNEL", - NULL, NULL, 1, NULL, - NULL, NULL}; -static enum ebpf_threads_status ebpf_vfs_exited = NETDATA_THREAD_EBPF_RUNNING; +struct netdata_static_thread vfs_threads = { + .name = "VFS KERNEL", + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL +}; + +netdata_ebpf_targets_t vfs_targets[] = { {.name = "vfs_write", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = "vfs_writev", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = "vfs_read", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = "vfs_readv", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = "vfs_unlink", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = "vfs_fsync", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = "vfs_open", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = "vfs_create", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = "release_task", .mode = EBPF_LOAD_TRAMPOLINE}, + {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}}; + +#ifdef LIBBPF_MAJOR_VERSION +#include "includes/vfs.skel.h" // BTF code + +static struct vfs_bpf *bpf_obj = NULL; + +/** + * Disable probe + * + * Disable all probes to use exclusively another method. + * + * @param obj is the main structure for bpf objects + */ +static void ebpf_vfs_disable_probes(struct vfs_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_vfs_write_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_write_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_writev_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_writev_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_read_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_read_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_readv_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_readv_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_unlink_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_unlink_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_fsync_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_fsync_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_open_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_open_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_create_kprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_create_kretprobe, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_release_task_kprobe, false); +} + +/* + * Disable trampoline + * + * Disable all trampoline to use exclusively another method. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_vfs_disable_trampoline(struct vfs_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_vfs_write_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_write_fexit, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_writev_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_writev_fexit, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_read_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_read_fexit, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_readv_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_readv_fexit, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_unlink_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_fsync_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_fsync_fexit, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_open_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_open_fexit, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_create_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_release_task_fentry, false); +} + +/** + * Set trampoline target + * + * Set the targets we will monitor. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_vfs_set_trampoline_target(struct vfs_bpf *obj) +{ + bpf_program__set_attach_target(obj->progs.netdata_vfs_write_fentry, 0, vfs_targets[NETDATA_EBPF_VFS_WRITE].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_write_fexit, 0, vfs_targets[NETDATA_EBPF_VFS_WRITE].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_writev_fentry, 0, vfs_targets[NETDATA_EBPF_VFS_WRITEV].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_writev_fexit, 0, vfs_targets[NETDATA_EBPF_VFS_WRITEV].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_read_fentry, 0, vfs_targets[NETDATA_EBPF_VFS_READ].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_read_fexit, 0, vfs_targets[NETDATA_EBPF_VFS_READ].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_readv_fentry, 0, vfs_targets[NETDATA_EBPF_VFS_READV].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_readv_fexit, 0, vfs_targets[NETDATA_EBPF_VFS_READV].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_unlink_fentry, 0, vfs_targets[NETDATA_EBPF_VFS_UNLINK].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_fsync_fentry, 0, vfs_targets[NETDATA_EBPF_VFS_FSYNC].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_fsync_fexit, 0, vfs_targets[NETDATA_EBPF_VFS_FSYNC].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_open_fentry, 0, vfs_targets[NETDATA_EBPF_VFS_OPEN].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_open_fexit, 0, vfs_targets[NETDATA_EBPF_VFS_OPEN].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_create_fentry, 0, vfs_targets[NETDATA_EBPF_VFS_CREATE].name); + + bpf_program__set_attach_target(obj->progs.netdata_vfs_release_task_fentry, 0, EBPF_COMMON_FNCT_CLEAN_UP); +} + +/** + * Attach Probe + * + * Attach probes to target + * + * @param obj is the main structure for bpf objects. + * + * @return It returns 0 on success and -1 otherwise. + */ +static int ebpf_vfs_attach_probe(struct vfs_bpf *obj) +{ + obj->links.netdata_vfs_write_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_write_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_WRITE].name); + int ret = libbpf_get_error(obj->links.netdata_vfs_write_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_write_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_write_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_WRITE].name); + ret = libbpf_get_error(obj->links.netdata_vfs_write_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_writev_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_writev_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_WRITEV].name); + ret = libbpf_get_error(obj->links.netdata_vfs_writev_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_writev_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_writev_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_WRITEV].name); + ret = libbpf_get_error(obj->links.netdata_vfs_writev_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_read_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_read_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_READ].name); + ret = libbpf_get_error(obj->links.netdata_vfs_read_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_read_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_read_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_READ].name); + ret = libbpf_get_error(obj->links.netdata_vfs_read_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_readv_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_readv_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_READV].name); + ret = libbpf_get_error(obj->links.netdata_vfs_readv_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_readv_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_readv_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_READV].name); + ret = libbpf_get_error(obj->links.netdata_vfs_readv_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_unlink_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_unlink_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_UNLINK].name); + ret = libbpf_get_error(obj->links.netdata_vfs_unlink_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_unlink_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_unlink_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_UNLINK].name); + ret = libbpf_get_error(obj->links.netdata_vfs_unlink_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_fsync_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_fsync_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_FSYNC].name); + ret = libbpf_get_error(obj->links.netdata_vfs_fsync_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_fsync_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_fsync_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_FSYNC].name); + ret = libbpf_get_error(obj->links.netdata_vfs_fsync_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_open_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_open_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_OPEN].name); + ret = libbpf_get_error(obj->links.netdata_vfs_open_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_open_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_open_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_OPEN].name); + ret = libbpf_get_error(obj->links.netdata_vfs_open_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_create_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_create_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_CREATE].name); + ret = libbpf_get_error(obj->links.netdata_vfs_create_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_create_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_create_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_CREATE].name); + ret = libbpf_get_error(obj->links.netdata_vfs_create_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_fsync_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_fsync_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_FSYNC].name); + ret = libbpf_get_error(obj->links.netdata_vfs_fsync_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_fsync_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_fsync_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_FSYNC].name); + ret = libbpf_get_error(obj->links.netdata_vfs_fsync_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_open_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_open_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_OPEN].name); + ret = libbpf_get_error(obj->links.netdata_vfs_open_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_open_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_open_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_OPEN].name); + ret = libbpf_get_error(obj->links.netdata_vfs_open_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_create_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_create_kprobe, false, + vfs_targets[NETDATA_EBPF_VFS_CREATE].name); + ret = libbpf_get_error(obj->links.netdata_vfs_create_kprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_create_kretprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_create_kretprobe, true, + vfs_targets[NETDATA_EBPF_VFS_CREATE].name); + ret = libbpf_get_error(obj->links.netdata_vfs_create_kretprobe); + if (ret) + return -1; + + obj->links.netdata_vfs_release_task_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_vfs_release_task_fentry, + true, + EBPF_COMMON_FNCT_CLEAN_UP); + ret = libbpf_get_error(obj->links.netdata_vfs_release_task_kprobe); + if (ret) + return -1; + + return 0; +} + +/** + * Adjust Map Size + * + * Resize maps according input from users. + * + * @param obj is the main structure for bpf objects. + * @param em structure with configuration + */ +static void ebpf_vfs_adjust_map_size(struct vfs_bpf *obj, ebpf_module_t *em) +{ + ebpf_update_map_size(obj->maps.tbl_vfs_pid, &vfs_maps[NETDATA_VFS_PID], + em, bpf_map__name(obj->maps.tbl_vfs_pid)); +} + +/** + * Set hash tables + * + * Set the values for maps according the value given by kernel. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_vfs_set_hash_tables(struct vfs_bpf *obj) +{ + vfs_maps[NETDATA_VFS_ALL].map_fd = bpf_map__fd(obj->maps.tbl_vfs_stats); + vfs_maps[NETDATA_VFS_PID].map_fd = bpf_map__fd(obj->maps.tbl_vfs_pid); + vfs_maps[NETDATA_VFS_CTRL].map_fd = bpf_map__fd(obj->maps.vfs_ctrl); +} + +/** + * Disable Release Task + * + * Disable release task when apps is not enabled. + * + * @param obj is the main structure for bpf objects. + */ +static void ebpf_vfs_disable_release_task(struct vfs_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.netdata_vfs_release_task_fentry, false); + bpf_program__set_autoload(obj->progs.netdata_vfs_release_task_kprobe, false); +} + +/** + * Load and attach + * + * Load and attach the eBPF code in kernel. + * + * @param obj is the main structure for bpf objects. + * @param em structure with configuration + * + * @return it returns 0 on succes and -1 otherwise + */ +static inline int ebpf_vfs_load_and_attach(struct vfs_bpf *obj, ebpf_module_t *em) +{ + netdata_ebpf_targets_t *mt = em->targets; + netdata_ebpf_program_loaded_t test = mt[NETDATA_EBPF_VFS_WRITE].mode; + + if (test == EBPF_LOAD_TRAMPOLINE) { + ebpf_vfs_disable_probes(obj); + + ebpf_vfs_set_trampoline_target(obj); + } else { + ebpf_vfs_disable_trampoline(obj); + } + + ebpf_vfs_adjust_map_size(obj, em); + + if (!em->apps_charts && !em->cgroup_charts) + ebpf_vfs_disable_release_task(obj); + + int ret = vfs_bpf__load(obj); + if (ret) { + return ret; + } + + ret = (test == EBPF_LOAD_TRAMPOLINE) ? vfs_bpf__attach(obj) : ebpf_vfs_attach_probe(obj); + if (!ret) { + ebpf_vfs_set_hash_tables(obj); + + ebpf_update_controller(vfs_maps[NETDATA_VFS_CTRL].map_fd, em); + } + + return ret; +} +#endif /***************************************************************** * * FUNCTIONS TO CLOSE THE THREAD * *****************************************************************/ + +/** + * Cachestat Free + * + * Cleanup variables after child threads to stop + * + * @param ptr thread data. + */ +static void ebpf_vfs_free(ebpf_module_t *em) +{ + pthread_mutex_lock(&ebpf_exit_cleanup); + if (em->thread->enabled == NETDATA_THREAD_EBPF_RUNNING) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPING; + pthread_mutex_unlock(&ebpf_exit_cleanup); + return; + } + pthread_mutex_unlock(&ebpf_exit_cleanup); + + freez(vfs_hash_values); + freez(vfs_vector); + freez(vfs_threads.thread); + +#ifdef LIBBPF_MAJOR_VERSION + if (bpf_obj) + vfs_bpf__destroy(bpf_obj); +#endif + + pthread_mutex_lock(&ebpf_exit_cleanup); + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; + pthread_mutex_unlock(&ebpf_exit_cleanup); +} + /** * Exit * @@ -54,12 +440,8 @@ static enum ebpf_threads_status ebpf_vfs_exited = NETDATA_THREAD_EBPF_RUNNING; static void ebpf_vfs_exit(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (!em->enabled) { - em->enabled = NETDATA_MAIN_THREAD_EXITED; - return; - } - - ebpf_vfs_exited = NETDATA_THREAD_EBPF_STOPPING; + netdata_thread_cancel(*vfs_threads.thread); + ebpf_vfs_free(em); } /** @@ -70,15 +452,7 @@ static void ebpf_vfs_exit(void *ptr) static void ebpf_vfs_cleanup(void *ptr) { ebpf_module_t *em = (ebpf_module_t *)ptr; - if (ebpf_vfs_exited != NETDATA_THREAD_EBPF_STOPPED) - return; - - freez(vfs_hash_values); - freez(vfs_vector); - freez(vfs_threads.thread); - - vfs_threads.enabled = NETDATA_MAIN_THREAD_EXITED; - em->enabled = NETDATA_MAIN_THREAD_EXITED; + ebpf_vfs_free(em); } /***************************************************************** @@ -519,17 +893,12 @@ void *ebpf_vfs_read_hash(void *ptr) usec_t step = NETDATA_LATENCY_VFS_SLEEP_MS * em->update_every; //This will be cancelled by its parent - while (ebpf_vfs_exited == NETDATA_THREAD_EBPF_RUNNING) { - usec_t dt = heartbeat_next(&hb, step); - (void)dt; - if (ebpf_vfs_exited == NETDATA_THREAD_EBPF_STOPPING) - break; + while (!ebpf_exit_plugin) { + (void)heartbeat_next(&hb, step); read_global_table(); } - ebpf_vfs_exited = NETDATA_THREAD_EBPF_STOPPED; - netdata_thread_cleanup_pop(1); return NULL; } @@ -976,20 +1345,15 @@ static void ebpf_create_systemd_vfs_charts(ebpf_module_t *em) * Send collected data to Netdata. * * @param em the main collector structure - * - * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned - * otherwise function returns 1 to avoid chart recreation */ -static int ebpf_send_systemd_vfs_charts(ebpf_module_t *em) +static void ebpf_send_systemd_vfs_charts(ebpf_module_t *em) { - int ret = 1; ebpf_cgroup_target_t *ect; write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_SYSCALL_APPS_FILE_DELETED); for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { if (unlikely(ect->systemd) && unlikely(ect->updated)) { write_chart_dimension(ect->name, ect->publish_systemd_vfs.unlink_call); - } else if (unlikely(ect->systemd)) - ret = 0; + } } write_end_chart(); @@ -1105,8 +1469,6 @@ static int ebpf_send_systemd_vfs_charts(ebpf_module_t *em) } write_end_chart(); } - - return ret; } /** @@ -1127,13 +1489,10 @@ static void ebpf_vfs_send_cgroup_data(ebpf_module_t *em) int has_systemd = shm_ebpf_cgroup.header->systemd_enabled; if (has_systemd) { - static int systemd_charts = 0; - if (!systemd_charts) { + if (send_cgroup_chart) { ebpf_create_systemd_vfs_charts(em); - systemd_charts = 1; } - - systemd_charts = ebpf_send_systemd_vfs_charts(em); + ebpf_send_systemd_vfs_charts(em); } for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) { @@ -1543,6 +1902,36 @@ static void ebpf_vfs_allocate_global_vectors(int apps) * *****************************************************************/ +/* + * Load BPF + * + * Load BPF files. + * + * @param em the structure with configuration + */ +static int ebpf_vfs_load_bpf(ebpf_module_t *em) +{ + int ret = 0; + ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_EBPF_VFS_WRITE].mode); + if (em->load & EBPF_LOAD_LEGACY) { + em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); + if (!em->probe_links) { + ret = -1; + } + } +#ifdef LIBBPF_MAJOR_VERSION + else { + bpf_obj = vfs_bpf__open(); + if (!bpf_obj) + ret = -1; + else + ret = ebpf_vfs_load_and_attach(bpf_obj, em); + } +#endif + + return ret; +} + /** * Process thread * @@ -1563,12 +1952,11 @@ void *ebpf_vfs_thread(void *ptr) ebpf_vfs_allocate_global_vectors(em->apps_charts); - if (!em->enabled) - goto endvfs; - - em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); - if (!em->probe_links) { - em->enabled = CONFIG_BOOLEAN_NO; +#ifdef LIBBPF_MAJOR_VERSION + ebpf_adjust_thread_load(em, default_btf); +#endif + if (ebpf_vfs_load_bpf(em)) { + em->thread->enabled = NETDATA_THREAD_EBPF_STOPPED; goto endvfs; } @@ -1588,8 +1976,7 @@ void *ebpf_vfs_thread(void *ptr) vfs_collector(em); endvfs: - if (!em->enabled) - ebpf_update_disabled_plugin_stats(em); + ebpf_update_disabled_plugin_stats(em); netdata_thread_cleanup_pop(1); return NULL; diff --git a/collectors/ebpf.plugin/ebpf_vfs.h b/collectors/ebpf.plugin/ebpf_vfs.h index 87a21e39c..2e3c7cc29 100644 --- a/collectors/ebpf.plugin/ebpf_vfs.h +++ b/collectors/ebpf.plugin/ebpf_vfs.h @@ -149,13 +149,28 @@ enum vfs_counters { enum netdata_vfs_tables { NETDATA_VFS_PID, - NETDATA_VFS_ALL + NETDATA_VFS_ALL, + NETDATA_VFS_CTRL +}; + +enum netdata_vfs_calls_name { + NETDATA_EBPF_VFS_WRITE, + NETDATA_EBPF_VFS_WRITEV, + NETDATA_EBPF_VFS_READ, + NETDATA_EBPF_VFS_READV, + NETDATA_EBPF_VFS_UNLINK, + NETDATA_EBPF_VFS_FSYNC, + NETDATA_EBPF_VFS_OPEN, + NETDATA_EBPF_VFS_CREATE, + + NETDATA_VFS_END_LIST }; extern netdata_publish_vfs_t **vfs_pid; -extern void *ebpf_vfs_thread(void *ptr); -extern void ebpf_vfs_create_apps_charts(struct ebpf_module *em, void *ptr); +void *ebpf_vfs_thread(void *ptr); +void ebpf_vfs_create_apps_charts(struct ebpf_module *em, void *ptr); +extern netdata_ebpf_targets_t vfs_targets[]; extern struct config vfs_config; diff --git a/collectors/fping.plugin/README.md b/collectors/fping.plugin/README.md index 626edf5d0..e32d3911b 100644 --- a/collectors/fping.plugin/README.md +++ b/collectors/fping.plugin/README.md @@ -8,22 +8,30 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/collectors/fping The fping plugin supports monitoring latency, packet loss and uptime of any number of network end points, by pinging them with `fping`. -A recent version of `fping` is required (one that supports option `-N`). -The supplied plugin can install it, by running: +This plugin requires version 5.1 or newer of `fping` (earlier versions may or may not work). Our static builds and +Docker images come bundled with a known working version of `fping`. Native packages and local builds will need to +have a working version installed before the plugin is usable. + +## Installing fping locally + +If your distribution’s repositories do not include a working version of `fping`, the supplied plugin can install +it, by running: ```sh /usr/libexec/netdata/plugins.d/fping.plugin install ``` -The above will download, build and install the right version as `/usr/local/bin/fping`. +The above will download, build and install the right version as `/usr/local/bin/fping`. This requires a working C +compiler, GNU autotools (at least autoconf and automake), and GNU make. On Debian or Ubuntu, you can pull in most +of the required tools by installing the `build-essential` package (this should include everything except automake +and autoconf). + +## Configuration Then you need to edit `/etc/netdata/fping.conf` (to edit it on your system run `/etc/netdata/edit-config fping.conf`) like this: ```sh -# uncomment the following line - it should already be there -fping="/usr/local/bin/fping" - # set here all the hosts you need to ping # I suggest to use hostnames and put their IPs in /etc/hosts hosts="host1 host2 host3" diff --git a/collectors/freebsd.plugin/freebsd_devstat.c b/collectors/freebsd.plugin/freebsd_devstat.c index 66a1e61d2..0f037741a 100644 --- a/collectors/freebsd.plugin/freebsd_devstat.c +++ b/collectors/freebsd.plugin/freebsd_devstat.c @@ -347,8 +347,6 @@ int do_kern_devstat(int update_every, usec_t dt) { cur_dstat.busy_time_ms = dstat[i].busy_time.sec * 1000 + dstat[i].busy_time.frac * BINTIME_SCALE; - // -------------------------------------------------------------------- - if(dm->do_io == CONFIG_BOOLEAN_YES || (dm->do_io == CONFIG_BOOLEAN_AUTO && (dstat[i].bytes[DEVSTAT_READ] || dstat[i].bytes[DEVSTAT_WRITE] || @@ -375,8 +373,7 @@ int do_kern_devstat(int update_every, usec_t dt) { RRD_ALGORITHM_INCREMENTAL); dm->rd_io_free = rrddim_add(dm->st_io, "frees", NULL, -1, KILO_FACTOR, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(dm->st_io); + } rrddim_set_by_pointer(dm->st_io, dm->rd_io_in, dstat[i].bytes[DEVSTAT_READ]); rrddim_set_by_pointer(dm->st_io, dm->rd_io_out, dstat[i].bytes[DEVSTAT_WRITE]); @@ -384,8 +381,6 @@ int do_kern_devstat(int update_every, usec_t dt) { rrdset_done(dm->st_io); } - // -------------------------------------------------------------------- - if(dm->do_ops == CONFIG_BOOLEAN_YES || (dm->do_ops == CONFIG_BOOLEAN_AUTO && (dstat[i].operations[DEVSTAT_READ] || dstat[i].operations[DEVSTAT_WRITE] || @@ -417,8 +412,7 @@ int do_kern_devstat(int update_every, usec_t dt) { RRD_ALGORITHM_INCREMENTAL); dm->rd_ops_free = rrddim_add(dm->st_ops, "frees", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(dm->st_ops); + } rrddim_set_by_pointer(dm->st_ops, dm->rd_ops_in, dstat[i].operations[DEVSTAT_READ]); rrddim_set_by_pointer(dm->st_ops, dm->rd_ops_out, dstat[i].operations[DEVSTAT_WRITE]); @@ -427,8 +421,6 @@ int do_kern_devstat(int update_every, usec_t dt) { rrdset_done(dm->st_ops); } - // -------------------------------------------------------------------- - if(dm->do_qops == CONFIG_BOOLEAN_YES || (dm->do_qops == CONFIG_BOOLEAN_AUTO && (dstat[i].start_count || dstat[i].end_count || @@ -451,15 +443,12 @@ int do_kern_devstat(int update_every, usec_t dt) { rrdset_flag_set(dm->st_qops, RRDSET_FLAG_DETAIL); dm->rd_qops = rrddim_add(dm->st_qops, "operations", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(dm->st_qops); + } rrddim_set_by_pointer(dm->st_qops, dm->rd_qops, dstat[i].start_count - dstat[i].end_count); rrdset_done(dm->st_qops); } - // -------------------------------------------------------------------- - if(dm->do_util == CONFIG_BOOLEAN_YES || (dm->do_util == CONFIG_BOOLEAN_AUTO && (cur_dstat.busy_time_ms || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -482,15 +471,12 @@ int do_kern_devstat(int update_every, usec_t dt) { dm->rd_util = rrddim_add(dm->st_util, "utilization", NULL, 1, 10, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(dm->st_util); + } rrddim_set_by_pointer(dm->st_util, dm->rd_util, cur_dstat.busy_time_ms); rrdset_done(dm->st_util); } - // -------------------------------------------------------------------- - if(dm->do_iotime == CONFIG_BOOLEAN_YES || (dm->do_iotime == CONFIG_BOOLEAN_AUTO && (cur_dstat.duration_read_ms || cur_dstat.duration_write_ms || @@ -522,8 +508,7 @@ int do_kern_devstat(int update_every, usec_t dt) { RRD_ALGORITHM_INCREMENTAL); dm->rd_iotime_free = rrddim_add(dm->st_iotime, "frees", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(dm->st_iotime); + } rrddim_set_by_pointer(dm->st_iotime, dm->rd_iotime_in, cur_dstat.duration_read_ms); rrddim_set_by_pointer(dm->st_iotime, dm->rd_iotime_out, cur_dstat.duration_write_ms); @@ -532,14 +517,10 @@ int do_kern_devstat(int update_every, usec_t dt) { rrdset_done(dm->st_iotime); } - // -------------------------------------------------------------------- // calculate differential charts // only if this is not the first time we run if (likely(dt)) { - - // -------------------------------------------------------------------- - if(dm->do_await == CONFIG_BOOLEAN_YES || (dm->do_await == CONFIG_BOOLEAN_AUTO && (dstat[i].operations[DEVSTAT_READ] || dstat[i].operations[DEVSTAT_WRITE] || @@ -571,8 +552,7 @@ int do_kern_devstat(int update_every, usec_t dt) { RRD_ALGORITHM_ABSOLUTE); dm->rd_await_free = rrddim_add(dm->st_await, "frees", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(dm->st_await); + } rrddim_set_by_pointer(dm->st_await, dm->rd_await_in, (dstat[i].operations[DEVSTAT_READ] - @@ -605,8 +585,6 @@ int do_kern_devstat(int update_every, usec_t dt) { rrdset_done(dm->st_await); } - // -------------------------------------------------------------------- - if(dm->do_avagsz == CONFIG_BOOLEAN_YES || (dm->do_avagsz == CONFIG_BOOLEAN_AUTO && (dstat[i].operations[DEVSTAT_READ] || dstat[i].operations[DEVSTAT_WRITE] || @@ -635,8 +613,7 @@ int do_kern_devstat(int update_every, usec_t dt) { RRD_ALGORITHM_ABSOLUTE); dm->rd_avagsz_free = rrddim_add(dm->st_avagsz, "frees", NULL, -1, KILO_FACTOR, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(dm->st_avagsz); + } rrddim_set_by_pointer(dm->st_avagsz, dm->rd_avagsz_in, (dstat[i].operations[DEVSTAT_READ] - @@ -662,8 +639,6 @@ int do_kern_devstat(int update_every, usec_t dt) { rrdset_done(dm->st_avagsz); } - // -------------------------------------------------------------------- - if(dm->do_svctm == CONFIG_BOOLEAN_YES || (dm->do_svctm == CONFIG_BOOLEAN_AUTO && (dstat[i].operations[DEVSTAT_READ] || dstat[i].operations[DEVSTAT_WRITE] || @@ -689,8 +664,7 @@ int do_kern_devstat(int update_every, usec_t dt) { dm->rd_svctm = rrddim_add(dm->st_svctm, "svctm", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(dm->st_svctm); + } rrddim_set_by_pointer(dm->st_svctm, dm->rd_svctm, ((dstat[i].operations[DEVSTAT_READ] - dm->prev_dstat.operations_read) + @@ -706,8 +680,6 @@ int do_kern_devstat(int update_every, usec_t dt) { rrdset_done(dm->st_svctm); } - // -------------------------------------------------------------------- - dm->prev_dstat.bytes_read = dstat[i].bytes[DEVSTAT_READ]; dm->prev_dstat.bytes_write = dstat[i].bytes[DEVSTAT_WRITE]; dm->prev_dstat.bytes_free = dstat[i].bytes[DEVSTAT_FREE]; @@ -724,8 +696,6 @@ int do_kern_devstat(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - if (likely(do_system_io)) { static RRDSET *st = NULL; static RRDDIM *rd_in = NULL, *rd_out = NULL; @@ -747,8 +717,7 @@ int do_kern_devstat(int update_every, usec_t dt) { rd_in = rrddim_add(st, "in", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st, "out", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in, total_disk_kbytes_read); rrddim_set_by_pointer(st, rd_out, total_disk_kbytes_write); @@ -756,6 +725,7 @@ int do_kern_devstat(int update_every, usec_t dt) { } } } + if (unlikely(common_error)) { do_system_io = 0; error("DISABLED: system.io chart"); diff --git a/collectors/freebsd.plugin/freebsd_getifaddrs.c b/collectors/freebsd.plugin/freebsd_getifaddrs.c index 0c0c1e7ab..1e870c0db 100644 --- a/collectors/freebsd.plugin/freebsd_getifaddrs.c +++ b/collectors/freebsd.plugin/freebsd_getifaddrs.c @@ -226,8 +226,6 @@ int do_getifaddrs(int update_every, usec_t dt) { u_long ift_omcasts; } iftot = {0, 0, 0, 0, 0, 0}; - // -------------------------------------------------------------------- - if (likely(do_bandwidth_net)) { iftot.ift_ibytes = iftot.ift_obytes = 0; @@ -260,19 +258,16 @@ int do_getifaddrs(int update_every, usec_t dt) { rd_in = rrddim_add(st, "InOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st, "OutOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in, iftot.ift_ibytes); rrddim_set_by_pointer(st, rd_out, iftot.ift_obytes); rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_packets_net)) { - iftot.ift_ipackets = iftot.ift_opackets = iftot.ift_imcasts = iftot.ift_omcasts = 0; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; @@ -308,8 +303,7 @@ int do_getifaddrs(int update_every, usec_t dt) { rd_packets_out = rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_packets_m_in = rrddim_add(st, "multicast_received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_packets_m_out = rrddim_add(st, "multicast_sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_packets_in, iftot.ift_ipackets); rrddim_set_by_pointer(st, rd_packets_out, iftot.ift_opackets); @@ -318,8 +312,6 @@ int do_getifaddrs(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_bandwidth_ipv4)) { iftot.ift_ibytes = iftot.ift_obytes = 0; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { @@ -349,16 +341,13 @@ int do_getifaddrs(int update_every, usec_t dt) { rd_in = rrddim_add(st, "InOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st, "OutOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in, iftot.ift_ibytes); rrddim_set_by_pointer(st, rd_out, iftot.ift_obytes); rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_bandwidth_ipv6)) { iftot.ift_ibytes = iftot.ift_obytes = 0; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { @@ -388,16 +377,13 @@ int do_getifaddrs(int update_every, usec_t dt) { rd_in = rrddim_add(st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in, iftot.ift_ibytes); rrddim_set_by_pointer(st, rd_out, iftot.ift_obytes); rrdset_done(st); } - // -------------------------------------------------------------------- - network_interfaces_found = 0; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { @@ -437,8 +423,6 @@ int do_getifaddrs(int update_every, usec_t dt) { if (unlikely(!ifm->enabled)) continue; - // -------------------------------------------------------------------- - if (ifm->do_bandwidth == CONFIG_BOOLEAN_YES || (ifm->do_bandwidth == CONFIG_BOOLEAN_AUTO && (IFA_DATA(ibytes) || IFA_DATA(obytes) || @@ -460,16 +444,13 @@ int do_getifaddrs(int update_every, usec_t dt) { ifm->rd_bandwidth_in = rrddim_add(ifm->st_bandwidth, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); ifm->rd_bandwidth_out = rrddim_add(ifm->st_bandwidth, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(ifm->st_bandwidth); + } rrddim_set_by_pointer(ifm->st_bandwidth, ifm->rd_bandwidth_in, IFA_DATA(ibytes)); rrddim_set_by_pointer(ifm->st_bandwidth, ifm->rd_bandwidth_out, IFA_DATA(obytes)); rrdset_done(ifm->st_bandwidth); } - // -------------------------------------------------------------------- - if (ifm->do_packets == CONFIG_BOOLEAN_YES || (ifm->do_packets == CONFIG_BOOLEAN_AUTO && (IFA_DATA(ipackets) || IFA_DATA(opackets) || @@ -501,8 +482,7 @@ int do_getifaddrs(int update_every, usec_t dt) { RRD_ALGORITHM_INCREMENTAL); ifm->rd_packets_m_out = rrddim_add(ifm->st_packets, "multicast_sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(ifm->st_packets); + } rrddim_set_by_pointer(ifm->st_packets, ifm->rd_packets_in, IFA_DATA(ipackets)); rrddim_set_by_pointer(ifm->st_packets, ifm->rd_packets_out, IFA_DATA(opackets)); @@ -511,8 +491,6 @@ int do_getifaddrs(int update_every, usec_t dt) { rrdset_done(ifm->st_packets); } - // -------------------------------------------------------------------- - if (ifm->do_errors == CONFIG_BOOLEAN_YES || (ifm->do_errors == CONFIG_BOOLEAN_AUTO && (IFA_DATA(ierrors) || IFA_DATA(oerrors) || @@ -536,14 +514,12 @@ int do_getifaddrs(int update_every, usec_t dt) { ifm->rd_errors_in = rrddim_add(ifm->st_errors, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); ifm->rd_errors_out = rrddim_add(ifm->st_errors, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(ifm->st_errors); + } rrddim_set_by_pointer(ifm->st_errors, ifm->rd_errors_in, IFA_DATA(ierrors)); rrddim_set_by_pointer(ifm->st_errors, ifm->rd_errors_out, IFA_DATA(oerrors)); rrdset_done(ifm->st_errors); } - // -------------------------------------------------------------------- if (ifm->do_drops == CONFIG_BOOLEAN_YES || (ifm->do_drops == CONFIG_BOOLEAN_AUTO && (IFA_DATA(iqdrops) || @@ -572,8 +548,7 @@ int do_getifaddrs(int update_every, usec_t dt) { #if __FreeBSD__ >= 11 ifm->rd_drops_out = rrddim_add(ifm->st_drops, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); #endif - } else - rrdset_next(ifm->st_drops); + } rrddim_set_by_pointer(ifm->st_drops, ifm->rd_drops_in, IFA_DATA(iqdrops)); #if __FreeBSD__ >= 11 @@ -582,8 +557,6 @@ int do_getifaddrs(int update_every, usec_t dt) { rrdset_done(ifm->st_drops); } - // -------------------------------------------------------------------- - if (ifm->do_events == CONFIG_BOOLEAN_YES || (ifm->do_events == CONFIG_BOOLEAN_AUTO && (IFA_DATA(collisions) || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -606,8 +579,7 @@ int do_getifaddrs(int update_every, usec_t dt) { ifm->rd_events_coll = rrddim_add(ifm->st_events, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(ifm->st_events); + } rrddim_set_by_pointer(ifm->st_events, ifm->rd_events_coll, IFA_DATA(collisions)); rrdset_done(ifm->st_events); diff --git a/collectors/freebsd.plugin/freebsd_getmntinfo.c b/collectors/freebsd.plugin/freebsd_getmntinfo.c index f83a4a0db..e8feefc2b 100644 --- a/collectors/freebsd.plugin/freebsd_getmntinfo.c +++ b/collectors/freebsd.plugin/freebsd_getmntinfo.c @@ -212,8 +212,6 @@ int do_getmntinfo(int update_every, usec_t dt) { if (unlikely(mntbuf[i].f_flags & MNT_RDONLY && !m->collected)) continue; - // -------------------------------------------------------------------------- - int rendered = 0; if (m->do_space == CONFIG_BOOLEAN_YES || (m->do_space == CONFIG_BOOLEAN_AUTO && @@ -242,8 +240,7 @@ int do_getmntinfo(int update_every, usec_t dt) { mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(m->st_space); + } rrddim_set_by_pointer(m->st_space, m->rd_space_avail, (collected_number) mntbuf[i].f_bavail); rrddim_set_by_pointer(m->st_space, m->rd_space_used, (collected_number) (mntbuf[i].f_blocks - @@ -255,8 +252,6 @@ int do_getmntinfo(int update_every, usec_t dt) { rendered++; } - // -------------------------------------------------------------------------- - if (m->do_inodes == CONFIG_BOOLEAN_YES || (m->do_inodes == CONFIG_BOOLEAN_AUTO && (mntbuf[i].f_files > 1 || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -279,8 +274,7 @@ int do_getmntinfo(int update_every, usec_t dt) { m->rd_inodes_avail = rrddim_add(m->st_inodes, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); m->rd_inodes_used = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(m->st_inodes); + } rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_avail, (collected_number) mntbuf[i].f_ffree); rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_used, (collected_number) (mntbuf[i].f_files - diff --git a/collectors/freebsd.plugin/freebsd_ipfw.c b/collectors/freebsd.plugin/freebsd_ipfw.c index 16e9fd332..178eaa36c 100644 --- a/collectors/freebsd.plugin/freebsd_ipfw.c +++ b/collectors/freebsd.plugin/freebsd_ipfw.c @@ -149,8 +149,6 @@ int do_ipfw(int update_every, usec_t dt) { dynsz = 0; } - // -------------------------------------------------------------------- - if (likely(do_mem)) { static RRDSET *st_mem = NULL; static RRDDIM *rd_dyn_mem = NULL; @@ -174,22 +172,19 @@ int do_ipfw(int update_every, usec_t dt) { rd_dyn_mem = rrddim_add(st_mem, "dynamic", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_stat_mem = rrddim_add(st_mem, "static", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_mem); + } rrddim_set_by_pointer(st_mem, rd_dyn_mem, dynsz); rrddim_set_by_pointer(st_mem, rd_stat_mem, *optlen - dynsz); rrdset_done(st_mem); } - // -------------------------------------------------------------------- - static RRDSET *st_packets = NULL, *st_bytes = NULL; RRDDIM *rd_packets = NULL, *rd_bytes = NULL; if (likely(do_static || do_dynamic)) { if (likely(do_static)) { - if (unlikely(!st_packets)) + if (unlikely(!st_packets)) { st_packets = rrdset_create_localhost("ipfw", "packets", NULL, @@ -203,10 +198,9 @@ int do_ipfw(int update_every, usec_t dt) { update_every, RRDSET_TYPE_STACKED ); - else - rrdset_next(st_packets); + } - if (unlikely(!st_bytes)) + if (unlikely(!st_bytes)) { st_bytes = rrdset_create_localhost("ipfw", "bytes", NULL, @@ -220,8 +214,7 @@ int do_ipfw(int update_every, usec_t dt) { update_every, RRDSET_TYPE_STACKED ); - else - rrdset_next(st_bytes); + } } for (n = seen = 0; n < rcnt; n++, rbase = (ipfw_obj_tlv *) ((caddr_t) rbase + rbase->length)) { @@ -256,8 +249,6 @@ int do_ipfw(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - // go through dynamic rules configuration structures if (likely(do_dynamic && (dynsz > 0))) { @@ -305,12 +296,10 @@ int do_ipfw(int update_every, usec_t dt) { dynbase += tlv->length; } - // -------------------------------------------------------------------- - static RRDSET *st_active = NULL, *st_expired = NULL; RRDDIM *rd_active = NULL, *rd_expired = NULL; - if (unlikely(!st_active)) + if (unlikely(!st_active)) { st_active = rrdset_create_localhost("ipfw", "active", NULL, @@ -324,10 +313,9 @@ int do_ipfw(int update_every, usec_t dt) { update_every, RRDSET_TYPE_STACKED ); - else - rrdset_next(st_active); + } - if (unlikely(!st_expired)) + if (unlikely(!st_expired)) { st_expired = rrdset_create_localhost("ipfw", "expired", NULL, @@ -341,8 +329,7 @@ int do_ipfw(int update_every, usec_t dt) { update_every, RRDSET_TYPE_STACKED ); - else - rrdset_next(st_expired); + } for (srn = 0; (srn < (static_rules_num - 1)) && (dyn_rules_num[srn].rule_num != IPFW_DEFAULT_RULE); srn++) { sprintf(rule_num_str, "%d", dyn_rules_num[srn].rule_num); diff --git a/collectors/freebsd.plugin/freebsd_kstat_zfs.c b/collectors/freebsd.plugin/freebsd_kstat_zfs.c index 142fdb974..046a1e693 100644 --- a/collectors/freebsd.plugin/freebsd_kstat_zfs.c +++ b/collectors/freebsd.plugin/freebsd_kstat_zfs.c @@ -7,7 +7,6 @@ extern struct arcstats arcstats; unsigned long long zfs_arcstats_shrinkable_cache_size_bytes = 0; -// -------------------------------------------------------------------------------------------------------------------- // kstat.zfs.misc.arcstats int do_kstat_zfs_misc_arcstats(int update_every, usec_t dt) { @@ -227,7 +226,6 @@ int do_kstat_zfs_misc_arcstats(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // kstat.zfs.misc.zio_trim int do_kstat_zfs_misc_zio_trim(int update_every, usec_t dt) { @@ -246,8 +244,6 @@ int do_kstat_zfs_misc_zio_trim(int update_every, usec_t dt) { return 1; } else { - // -------------------------------------------------------------------- - static RRDSET *st_bytes = NULL; static RRDDIM *rd_bytes = NULL; @@ -269,13 +265,10 @@ int do_kstat_zfs_misc_zio_trim(int update_every, usec_t dt) { rd_bytes = rrddim_add(st_bytes, "TRIMmed", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_bytes); rrddim_set_by_pointer(st_bytes, rd_bytes, bytes); rrdset_done(st_bytes); - // -------------------------------------------------------------------- - static RRDSET *st_requests = NULL; static RRDDIM *rd_successful = NULL, *rd_failed = NULL, *rd_unsupported = NULL; @@ -299,7 +292,6 @@ int do_kstat_zfs_misc_zio_trim(int update_every, usec_t dt) { rd_failed = rrddim_add(st_requests, "failed", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_unsupported = rrddim_add(st_requests, "unsupported", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_requests); rrddim_set_by_pointer(st_requests, rd_successful, success); rrddim_set_by_pointer(st_requests, rd_failed, failed); diff --git a/collectors/freebsd.plugin/freebsd_sysctl.c b/collectors/freebsd.plugin/freebsd_sysctl.c index 016a71e37..dd94a1615 100644 --- a/collectors/freebsd.plugin/freebsd_sysctl.c +++ b/collectors/freebsd.plugin/freebsd_sysctl.c @@ -90,7 +90,6 @@ typedef struct vmmeter vmmeter_t; #define NETDATA_COLLECT_LAUNDRY 1 #endif -// -------------------------------------------------------------------------------------------------------------------- // FreeBSD plugin initialization int freebsd_plugin_init() @@ -114,7 +113,6 @@ int freebsd_plugin_init() return 0; } -// -------------------------------------------------------------------------------------------------------------------- // vm.loadavg // FreeBSD calculates load averages once every 5 seconds @@ -132,9 +130,6 @@ int do_vm_loadavg(int update_every, usec_t dt){ error("DISABLED: vm.loadavg module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd_load1 = NULL, *rd_load2 = NULL, *rd_load3 = NULL; @@ -156,8 +151,7 @@ int do_vm_loadavg(int update_every, usec_t dt){ rd_load1 = rrddim_add(st, "load1", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); rd_load2 = rrddim_add(st, "load5", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); rd_load3 = rrddim_add(st, "load15", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_load1, (collected_number) ((double) sysload.ldavg[0] / sysload.fscale * 1000)); rrddim_set_by_pointer(st, rd_load2, (collected_number) ((double) sysload.ldavg[1] / sysload.fscale * 1000)); @@ -173,7 +167,6 @@ int do_vm_loadavg(int update_every, usec_t dt){ return 0; } -// -------------------------------------------------------------------------------------------------------------------- // vm.vmtotal int do_vm_vmtotal(int update_every, usec_t dt) { @@ -200,9 +193,6 @@ int do_vm_vmtotal(int update_every, usec_t dt) { error("DISABLED: vm.vmtotal module"); return 1; } else { - - // -------------------------------------------------------------------- - if (likely(do_all_processes)) { static RRDSET *st = NULL; static RRDDIM *rd = NULL; @@ -224,14 +214,11 @@ int do_vm_vmtotal(int update_every, usec_t dt) { ); rd = rrddim_add(st, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd, (vmtotal_data.t_rq + vmtotal_data.t_dw + vmtotal_data.t_pw + vmtotal_data.t_sl + vmtotal_data.t_sw)); rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_processes)) { static RRDSET *st = NULL; static RRDDIM *rd_running = NULL, *rd_blocked = NULL; @@ -255,15 +242,12 @@ int do_vm_vmtotal(int update_every, usec_t dt) { rd_running = rrddim_add(st, "running", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_blocked = rrddim_add(st, "blocked", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_running, vmtotal_data.t_rq); rrddim_set_by_pointer(st, rd_blocked, (vmtotal_data.t_dw + vmtotal_data.t_pw)); rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_mem_real)) { static RRDSET *st = NULL; static RRDDIM *rd = NULL; @@ -287,7 +271,6 @@ int do_vm_vmtotal(int update_every, usec_t dt) { rd = rrddim_add(st, "used", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd, vmtotal_data.t_rm); rrdset_done(st); @@ -301,7 +284,6 @@ int do_vm_vmtotal(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // kern.cp_time int do_kern_cp_time(int update_every, usec_t dt) { @@ -321,9 +303,6 @@ int do_kern_cp_time(int update_every, usec_t dt) { error("DISABLED: kern.cp_time module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd_nice = NULL, *rd_system = NULL, *rd_user = NULL, *rd_interrupt = NULL, *rd_idle = NULL; @@ -350,7 +329,6 @@ int do_kern_cp_time(int update_every, usec_t dt) { rd_idle = rrddim_add(st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rrddim_hide(st, "idle"); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_nice, cp_time[1]); rrddim_set_by_pointer(st, rd_system, cp_time[2]); @@ -364,7 +342,6 @@ int do_kern_cp_time(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // kern.cp_times int do_kern_cp_times(int update_every, usec_t dt) { @@ -388,9 +365,6 @@ int do_kern_cp_times(int update_every, usec_t dt) { error("DISABLED: kern.cp_times module"); return 1; } else { - - // -------------------------------------------------------------------- - int i; static struct cpu_chart { char cpuid[MAX_INT_DIGITS + 4]; @@ -436,7 +410,7 @@ int do_kern_cp_times(int update_every, usec_t dt) { all_cpu_charts[i].rd_idle = rrddim_add(all_cpu_charts[i].st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rrddim_hide(all_cpu_charts[i].st, "idle"); - } else rrdset_next(all_cpu_charts[i].st); + } rrddim_set_by_pointer(all_cpu_charts[i].st, all_cpu_charts[i].rd_nice, pcpu_cp_time[i * 5 + 1]); rrddim_set_by_pointer(all_cpu_charts[i].st, all_cpu_charts[i].rd_system, pcpu_cp_time[i * 5 + 2]); @@ -453,7 +427,6 @@ int do_kern_cp_times(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // dev.cpu.temperature int do_dev_cpu_temperature(int update_every, usec_t dt) { @@ -482,8 +455,6 @@ int do_dev_cpu_temperature(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - static RRDSET *st; static RRDDIM **rd_pcpu_temperature; @@ -509,7 +480,6 @@ int do_dev_cpu_temperature(int update_every, usec_t dt) { RRDSET_TYPE_LINE ); } - else rrdset_next(st); for (i = 0; i < number_of_cpus; i++) { if (unlikely(!rd_pcpu_temperature[i])) { @@ -527,7 +497,6 @@ int do_dev_cpu_temperature(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // dev.cpu.0.freq int do_dev_cpu_0_freq(int update_every, usec_t dt) { @@ -540,9 +509,6 @@ int do_dev_cpu_0_freq(int update_every, usec_t dt) { error("DISABLED: dev.cpu.0.freq module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd = NULL; @@ -564,7 +530,6 @@ int do_dev_cpu_0_freq(int update_every, usec_t dt) { rd = rrddim_add(st, "frequency", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd, cpufreq); rrdset_done(st); @@ -606,8 +571,6 @@ int do_hw_intcnt(int update_every, usec_t dt) { for (i = 0; i < nintr; i++) totalintr += intrcnt[i]; - // -------------------------------------------------------------------- - static RRDSET *st_intr = NULL; static RRDDIM *rd_intr = NULL; @@ -629,14 +592,11 @@ int do_hw_intcnt(int update_every, usec_t dt) { rrdset_flag_set(st_intr, RRDSET_FLAG_DETAIL); rd_intr = rrddim_add(st_intr, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st_intr); + } rrddim_set_by_pointer(st_intr, rd_intr, totalintr); rrdset_done(st_intr); - // -------------------------------------------------------------------- - size_t size; static int mib_hw_intrnames[2] = {0, 0}; static char *intrnames = NULL; @@ -655,12 +615,9 @@ int do_hw_intcnt(int update_every, usec_t dt) { error("DISABLED: hw.intrcnt module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st_interrupts = NULL; - if (unlikely(!st_interrupts)) + if (unlikely(!st_interrupts)) { st_interrupts = rrdset_create_localhost( "system", "interrupts", @@ -675,8 +632,7 @@ int do_hw_intcnt(int update_every, usec_t dt) { update_every, RRDSET_TYPE_STACKED ); - else - rrdset_next(st_interrupts); + } for (i = 0; i < nintr; i++) { void *p; @@ -702,7 +658,6 @@ int do_hw_intcnt(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // vm.stats.sys.v_intr int do_vm_stats_sys_v_intr(int update_every, usec_t dt) { @@ -715,9 +670,6 @@ int do_vm_stats_sys_v_intr(int update_every, usec_t dt) { error("DISABLED: vm.stats.sys.v_intr module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd = NULL; @@ -739,7 +691,6 @@ int do_vm_stats_sys_v_intr(int update_every, usec_t dt) { rd = rrddim_add(st, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd, int_number); rrdset_done(st); @@ -748,7 +699,6 @@ int do_vm_stats_sys_v_intr(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // vm.stats.sys.v_soft int do_vm_stats_sys_v_soft(int update_every, usec_t dt) { @@ -761,9 +711,6 @@ int do_vm_stats_sys_v_soft(int update_every, usec_t dt) { error("DISABLED: vm.stats.sys.v_soft module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd = NULL; @@ -785,7 +732,6 @@ int do_vm_stats_sys_v_soft(int update_every, usec_t dt) { rd = rrddim_add(st, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd, soft_intr_number); rrdset_done(st); @@ -794,7 +740,6 @@ int do_vm_stats_sys_v_soft(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // vm.stats.sys.v_swtch int do_vm_stats_sys_v_swtch(int update_every, usec_t dt) { @@ -807,9 +752,6 @@ int do_vm_stats_sys_v_swtch(int update_every, usec_t dt) { error("DISABLED: vm.stats.sys.v_swtch module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd = NULL; @@ -831,7 +773,6 @@ int do_vm_stats_sys_v_swtch(int update_every, usec_t dt) { rd = rrddim_add(st, "switches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd, ctxt_number); rrdset_done(st); @@ -840,7 +781,6 @@ int do_vm_stats_sys_v_swtch(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // vm.stats.vm.v_forks int do_vm_stats_sys_v_forks(int update_every, usec_t dt) { @@ -879,7 +819,6 @@ int do_vm_stats_sys_v_forks(int update_every, usec_t dt) { rd = rrddim_add(st, "started", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd, forks_number); rrdset_done(st); @@ -888,7 +827,6 @@ int do_vm_stats_sys_v_forks(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // vm.swap_info int do_vm_swap_info(int update_every, usec_t dt) { @@ -931,8 +869,6 @@ int do_vm_swap_info(int update_every, usec_t dt) { total_xsw.bytes_total += xsw.xsw_nblks; } - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd_free = NULL, *rd_used = NULL; @@ -957,7 +893,6 @@ int do_vm_swap_info(int update_every, usec_t dt) { rd_free = rrddim_add(st, "free", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); rd_used = rrddim_add(st, "used", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_free, total_xsw.bytes_total - total_xsw.bytes_used); rrddim_set_by_pointer(st, rd_used, total_xsw.bytes_used); @@ -967,7 +902,6 @@ int do_vm_swap_info(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // system.ram int do_system_ram(int update_every, usec_t dt) { @@ -1002,9 +936,6 @@ int do_system_ram(int update_every, usec_t dt) { error("DISABLED: system.ram module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL, *st_mem_available = NULL; static RRDDIM *rd_free = NULL, *rd_active = NULL, *rd_inactive = NULL, *rd_wired = NULL, *rd_cache = NULL, *rd_buffers = NULL, *rd_avail = NULL; @@ -1039,7 +970,6 @@ int do_system_ram(int update_every, usec_t dt) { #endif rd_buffers = rrddim_add(st, "buffers", NULL, 1, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_free, vmmeter_data.v_free_count); rrddim_set_by_pointer(st, rd_active, vmmeter_data.v_active_count); @@ -1074,7 +1004,6 @@ int do_system_ram(int update_every, usec_t dt) { rd_avail = rrddim_add(st_mem_available, "MemAvailable", "avail", system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_mem_available); #if __FreeBSD_version < 1200016 rrddim_set_by_pointer(st_mem_available, rd_avail, vmmeter_data.v_inactive_count + vmmeter_data.v_free_count + vmmeter_data.v_cache_count + zfs_arcstats_shrinkable_cache_size_bytes / system_pagesize); @@ -1088,7 +1017,6 @@ int do_system_ram(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // vm.stats.vm.v_swappgs int do_vm_stats_sys_v_swappgs(int update_every, usec_t dt) { @@ -1102,9 +1030,6 @@ int do_vm_stats_sys_v_swappgs(int update_every, usec_t dt) { error("DISABLED: vm.stats.vm.v_swappgs module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd_in = NULL, *rd_out = NULL; @@ -1127,7 +1052,6 @@ int do_vm_stats_sys_v_swappgs(int update_every, usec_t dt) { rd_in = rrddim_add(st, "in", NULL, system_pagesize, KILO_FACTOR, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st, "out", NULL, -system_pagesize, KILO_FACTOR, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_in, vmmeter_data.v_swappgsin); rrddim_set_by_pointer(st, rd_out, vmmeter_data.v_swappgsout); @@ -1137,7 +1061,6 @@ int do_vm_stats_sys_v_swappgs(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // vm.stats.vm.v_pgfaults int do_vm_stats_sys_v_pgfaults(int update_every, usec_t dt) { @@ -1155,9 +1078,6 @@ int do_vm_stats_sys_v_pgfaults(int update_every, usec_t dt) { error("DISABLED: vm.stats.vm.v_pgfaults module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd_memory = NULL, *rd_io_requiring = NULL, *rd_cow = NULL, *rd_cow_optimized = NULL, *rd_in_transit = NULL; @@ -1186,7 +1106,6 @@ int do_vm_stats_sys_v_pgfaults(int update_every, usec_t dt) { rd_cow_optimized = rrddim_add(st, "cow_optimized", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_transit = rrddim_add(st, "in_transit", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_memory, vmmeter_data.v_vm_faults); rrddim_set_by_pointer(st, rd_io_requiring, vmmeter_data.v_io_faults); @@ -1240,8 +1159,6 @@ int do_kern_ipc_sem(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - static RRDSET *st_semaphores = NULL, *st_semaphore_arrays = NULL; static RRDDIM *rd_semaphores = NULL, *rd_semaphore_arrays = NULL; @@ -1263,13 +1180,10 @@ int do_kern_ipc_sem(int update_every, usec_t dt) { rd_semaphores = rrddim_add(st_semaphores, "semaphores", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_semaphores); rrddim_set_by_pointer(st_semaphores, rd_semaphores, ipc_sem.semaphores); rrdset_done(st_semaphores); - // -------------------------------------------------------------------- - if (unlikely(!st_semaphore_arrays)) { st_semaphore_arrays = rrdset_create_localhost( "system", @@ -1288,7 +1202,6 @@ int do_kern_ipc_sem(int update_every, usec_t dt) { rd_semaphore_arrays = rrddim_add(st_semaphore_arrays, "arrays", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_semaphore_arrays); rrddim_set_by_pointer(st_semaphore_arrays, rd_semaphore_arrays, ipc_sem.sets); rrdset_done(st_semaphore_arrays); @@ -1298,7 +1211,6 @@ int do_kern_ipc_sem(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // kern.ipc.shm int do_kern_ipc_shm(int update_every, usec_t dt) { @@ -1340,8 +1252,6 @@ int do_kern_ipc_shm(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - static RRDSET *st_segs = NULL, *st_size = NULL; static RRDDIM *rd_segments = NULL, *rd_allocated = NULL; @@ -1363,13 +1273,10 @@ int do_kern_ipc_shm(int update_every, usec_t dt) { rd_segments = rrddim_add(st_segs, "segments", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_segs); rrddim_set_by_pointer(st_segs, rd_segments, ipc_shm.segs); rrdset_done(st_segs); - // -------------------------------------------------------------------- - if (unlikely(!st_size)) { st_size = rrdset_create_localhost( "system", @@ -1388,7 +1295,6 @@ int do_kern_ipc_shm(int update_every, usec_t dt) { rd_allocated = rrddim_add(st_size, "allocated", NULL, 1, KILO_FACTOR, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_size); rrddim_set_by_pointer(st_size, rd_allocated, ipc_shm.segsize); rrdset_done(st_size); @@ -1398,7 +1304,6 @@ int do_kern_ipc_shm(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // kern.ipc.msq int do_kern_ipc_msq(int update_every, usec_t dt) { @@ -1446,8 +1351,6 @@ int do_kern_ipc_msq(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - static RRDSET *st_queues = NULL, *st_messages = NULL, *st_size = NULL; static RRDDIM *rd_queues = NULL, *rd_messages = NULL, *rd_allocated = NULL, *rd_used = NULL; @@ -1469,13 +1372,10 @@ int do_kern_ipc_msq(int update_every, usec_t dt) { rd_queues = rrddim_add(st_queues, "queues", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_queues); rrddim_set_by_pointer(st_queues, rd_queues, ipc_msq.queues); rrdset_done(st_queues); - // -------------------------------------------------------------------- - if (unlikely(!st_messages)) { st_messages = rrdset_create_localhost( "system", @@ -1494,13 +1394,10 @@ int do_kern_ipc_msq(int update_every, usec_t dt) { rd_messages = rrddim_add(st_messages, "messages", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_messages); rrddim_set_by_pointer(st_messages, rd_messages, ipc_msq.messages); rrdset_done(st_messages); - // -------------------------------------------------------------------- - if (unlikely(!st_size)) { st_size = rrdset_create_localhost( "system", @@ -1520,7 +1417,6 @@ int do_kern_ipc_msq(int update_every, usec_t dt) { rd_allocated = rrddim_add(st_size, "allocated", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_used = rrddim_add(st_size, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_size); rrddim_set_by_pointer(st_size, rd_allocated, ipc_msq.allocsize); rrddim_set_by_pointer(st_size, rd_used, ipc_msq.usedsize); @@ -1531,7 +1427,6 @@ int do_kern_ipc_msq(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // uptime int do_uptime(int update_every, usec_t dt) { @@ -1540,8 +1435,6 @@ int do_uptime(int update_every, usec_t dt) { clock_gettime(CLOCK_UPTIME, &up_time); - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd = NULL; @@ -1563,15 +1456,12 @@ int do_uptime(int update_every, usec_t dt) { rd = rrddim_add(st, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd, up_time.tv_sec); rrdset_done(st); - return 0; } -// -------------------------------------------------------------------------------------------------------------------- // net.isr int do_net_isr(int update_every, usec_t dt) { @@ -1668,8 +1558,6 @@ int do_net_isr(int update_every, usec_t dt) { return 1; } - // -------------------------------------------------------------------- - if (likely(do_netisr)) { static RRDSET *st = NULL; static RRDDIM *rd_dispatched = NULL, *rd_hybrid_dispatched = NULL, *rd_qdrops = NULL, *rd_queued = NULL; @@ -1695,7 +1583,6 @@ int do_net_isr(int update_every, usec_t dt) { rd_qdrops = rrddim_add(st, "qdrops", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_queued = rrddim_add(st, "queued", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_dispatched, netisr_stats[number_of_cpus].dispatched); rrddim_set_by_pointer(st, rd_hybrid_dispatched, netisr_stats[number_of_cpus].hybrid_dispatched); @@ -1704,8 +1591,6 @@ int do_net_isr(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_netisr_per_core)) { static struct softnet_chart { char netisr_cpuid[MAX_INT_DIGITS + 17]; @@ -1752,7 +1637,6 @@ int do_net_isr(int update_every, usec_t dt) { all_softnet_charts[i].rd_queued = rrddim_add(all_softnet_charts[i].st, "queued", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(all_softnet_charts[i].st); rrddim_set_by_pointer(all_softnet_charts[i].st, all_softnet_charts[i].rd_dispatched, netisr_stats[i].dispatched); @@ -1769,7 +1653,6 @@ int do_net_isr(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // net.inet.tcp.states int do_net_inet_tcp_states(int update_every, usec_t dt) { @@ -1783,9 +1666,6 @@ int do_net_inet_tcp_states(int update_every, usec_t dt) { error("DISABLED: net.inet.tcp.states module"); return 1; } else { - - // -------------------------------------------------------------------- - static RRDSET *st = NULL; static RRDDIM *rd = NULL; @@ -1806,8 +1686,7 @@ int do_net_inet_tcp_states(int update_every, usec_t dt) { ); rd = rrddim_add(st, "CurrEstab", "connections", 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd, tcps_states[TCPS_ESTABLISHED]); rrdset_done(st); @@ -1816,7 +1695,6 @@ int do_net_inet_tcp_states(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // net.inet.tcp.stats int do_net_inet_tcp_stats(int update_every, usec_t dt) { @@ -1866,9 +1744,6 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { error("DISABLED: net.inet.tcp.stats module"); return 1; } else { - - // -------------------------------------------------------------------- - if (likely(do_tcp_packets)) { static RRDSET *st = NULL; static RRDDIM *rd_in_segs = NULL, *rd_out_segs = NULL; @@ -1891,16 +1766,13 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rd_in_segs = rrddim_add(st, "InSegs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out_segs = rrddim_add(st, "OutSegs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_segs, tcpstat.tcps_rcvtotal); rrddim_set_by_pointer(st, rd_out_segs, tcpstat.tcps_sndtotal); rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_tcp_errors)) { static RRDSET *st = NULL; static RRDDIM *rd_in_errs = NULL, *rd_in_csum_errs = NULL, *rd_retrans_segs = NULL; @@ -1926,8 +1798,7 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rd_in_errs = rrddim_add(st, "InErrs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_csum_errs = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_retrans_segs = rrddim_add(st, "RetransSegs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } #if __FreeBSD__ >= 11 rrddim_set_by_pointer(st, rd_in_errs, tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvreassfull + @@ -1940,8 +1811,6 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_tcp_handshake)) { static RRDSET *st = NULL; static RRDDIM *rd_estab_resets = NULL, *rd_active_opens = NULL, *rd_passive_opens = NULL, @@ -1969,8 +1838,7 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rd_active_opens = rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_passive_opens = rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_attempt_fails = rrddim_add(st, "AttemptFails", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_estab_resets, tcpstat.tcps_drops); rrddim_set_by_pointer(st, rd_active_opens, tcpstat.tcps_connattempt); @@ -1979,8 +1847,6 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || @@ -2016,7 +1882,6 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rd_on_timeout = rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_on_linger = rrddim_add(st, "TCPAbortOnLinger", "linger", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_on_data, tcpstat.tcps_rcvpackafterwin); rrddim_set_by_pointer(st, rd_on_close, tcpstat.tcps_rcvafterclose); @@ -2026,8 +1891,6 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_rcvoopack || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -2054,14 +1917,11 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rd_ofo_queue = rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_ofo_queue, tcpstat.tcps_rcvoopack); rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_tcpext_syncookies == CONFIG_BOOLEAN_YES || (do_tcpext_syncookies == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || @@ -2092,7 +1952,6 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rd_send = rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_failed = rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_recv, tcpstat.tcps_sc_recvcookie); rrddim_set_by_pointer(st, rd_send, tcpstat.tcps_sc_sendcookie); @@ -2100,8 +1959,6 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_tcpext_listen == CONFIG_BOOLEAN_YES || (do_tcpext_listen == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_listendrop || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -2129,16 +1986,11 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rd_overflows = rrddim_add(st_listen, "ListenOverflows", "overflows", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_listen); rrddim_set_by_pointer(st_listen, rd_overflows, tcpstat.tcps_listendrop); - rrdset_done(st_listen); } - // -------------------------------------------------------------------- - if (do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_ecn_ce || tcpstat.tcps_ecn_ect0 || @@ -2172,7 +2024,6 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { rd_ect0 = rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_ect1 = rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_ce, tcpstat.tcps_ecn_ce); rrddim_set_by_pointer(st, rd_no_ect, tcpstat.tcps_ecn_ce - (tcpstat.tcps_ecn_ect0 + @@ -2191,7 +2042,6 @@ int do_net_inet_tcp_stats(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // net.inet.udp.stats int do_net_inet_udp_stats(int update_every, usec_t dt) { @@ -2216,9 +2066,6 @@ int do_net_inet_udp_stats(int update_every, usec_t dt) { error("DISABLED: net.inet.udp.stats module"); return 1; } else { - - // -------------------------------------------------------------------- - if (likely(do_udp_packets)) { static RRDSET *st = NULL; static RRDDIM *rd_in = NULL, *rd_out = NULL; @@ -2241,16 +2088,13 @@ int do_net_inet_udp_stats(int update_every, usec_t dt) { rd_in = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in, udpstat.udps_ipackets); rrddim_set_by_pointer(st, rd_out, udpstat.udps_opackets); rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_udp_errors)) { static RRDSET *st = NULL; static RRDDIM *rd_in_errors = NULL, *rd_no_ports = NULL, *rd_recv_buf_errors = NULL, @@ -2279,8 +2123,7 @@ int do_net_inet_udp_stats(int update_every, usec_t dt) { rd_recv_buf_errors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_csum_errors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_ignored_multi = rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_errors, udpstat.udps_hdrops + udpstat.udps_badlen); rrddim_set_by_pointer(st, rd_no_ports, udpstat.udps_noport); @@ -2298,7 +2141,6 @@ int do_net_inet_udp_stats(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // net.inet.icmp.stats int do_net_inet_icmp_stats(int update_every, usec_t dt) { @@ -2337,8 +2179,6 @@ int do_net_inet_icmp_stats(int update_every, usec_t dt) { } icmp_total.msgs_in += icmpstat.icps_badcode + icmpstat.icps_badlen + icmpstat.icps_checksum + icmpstat.icps_tooshort; - // -------------------------------------------------------------------- - if (likely(do_icmp_packets)) { static RRDSET *st = NULL; static RRDDIM *rd_in = NULL, *rd_out = NULL; @@ -2361,17 +2201,13 @@ int do_net_inet_icmp_stats(int update_every, usec_t dt) { rd_in = rrddim_add(st, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in, icmp_total.msgs_in); rrddim_set_by_pointer(st, rd_out, icmp_total.msgs_out); - rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_icmp_errors)) { static RRDSET *st = NULL; static RRDDIM *rd_in = NULL, *rd_out = NULL, *rd_in_csum = NULL; @@ -2395,8 +2231,7 @@ int do_net_inet_icmp_stats(int update_every, usec_t dt) { rd_in = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_csum = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in, icmpstat.icps_badcode + icmpstat.icps_badlen + icmpstat.icps_checksum + icmpstat.icps_tooshort); @@ -2406,8 +2241,6 @@ int do_net_inet_icmp_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_icmpmsg)) { static RRDSET *st = NULL; static RRDDIM *rd_in_reps = NULL, *rd_out_reps = NULL, *rd_in = NULL, *rd_out = NULL; @@ -2432,14 +2265,12 @@ int do_net_inet_icmp_stats(int update_every, usec_t dt) { rd_out_reps = rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in = rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_reps, icmpstat.icps_inhist[ICMP_ECHOREPLY]); rrddim_set_by_pointer(st, rd_out_reps, icmpstat.icps_outhist[ICMP_ECHOREPLY]); rrddim_set_by_pointer(st, rd_in, icmpstat.icps_inhist[ICMP_ECHO]); rrddim_set_by_pointer(st, rd_out, icmpstat.icps_outhist[ICMP_ECHO]); - rrdset_done(st); } } @@ -2451,7 +2282,6 @@ int do_net_inet_icmp_stats(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // net.inet.ip.stats int do_net_inet_ip_stats(int update_every, usec_t dt) { @@ -2482,9 +2312,6 @@ int do_net_inet_ip_stats(int update_every, usec_t dt) { error("DISABLED: net.inet.ip.stats module"); return 1; } else { - - // -------------------------------------------------------------------- - if (likely(do_ip_packets)) { static RRDSET *st = NULL; static RRDDIM *rd_in_receives = NULL, *rd_out_requests = NULL, *rd_forward_datagrams = NULL, @@ -2510,8 +2337,7 @@ int do_net_inet_ip_stats(int update_every, usec_t dt) { rd_out_requests = rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_forward_datagrams = rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_delivers = rrddim_add(st, "InDelivers", "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_receives, ipstat.ips_total); rrddim_set_by_pointer(st, rd_out_requests, ipstat.ips_localout); @@ -2520,8 +2346,6 @@ int do_net_inet_ip_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_ip_fragsout)) { static RRDSET *st = NULL; static RRDDIM *rd_ok = NULL, *rd_fails = NULL, *rd_created = NULL; @@ -2547,8 +2371,7 @@ int do_net_inet_ip_stats(int update_every, usec_t dt) { rd_ok = rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_fails = rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_created = rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_ok, ipstat.ips_fragmented); rrddim_set_by_pointer(st, rd_fails, ipstat.ips_cantfrag); @@ -2556,8 +2379,6 @@ int do_net_inet_ip_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_ip_fragsin)) { static RRDSET *st = NULL; static RRDDIM *rd_ok = NULL, *rd_failed = NULL, *rd_all = NULL; @@ -2583,8 +2404,7 @@ int do_net_inet_ip_stats(int update_every, usec_t dt) { rd_ok = rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_failed = rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_all = rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_ok, ipstat.ips_fragments); rrddim_set_by_pointer(st, rd_failed, ipstat.ips_fragdropped); @@ -2592,8 +2412,6 @@ int do_net_inet_ip_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_ip_errors)) { static RRDSET *st = NULL; static RRDDIM *rd_in_discards = NULL, *rd_out_discards = NULL, @@ -2624,8 +2442,7 @@ int do_net_inet_ip_stats(int update_every, usec_t dt) { rd_out_no_routes = rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_addr_errors = rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_unknown_protos = rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_discards, ipstat.ips_badsum + ipstat.ips_tooshort + ipstat.ips_toosmall + ipstat.ips_toolong); @@ -2646,7 +2463,6 @@ int do_net_inet_ip_stats(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // net.inet6.ip6.stats int do_net_inet6_ip6_stats(int update_every, usec_t dt) { @@ -2680,9 +2496,6 @@ int do_net_inet6_ip6_stats(int update_every, usec_t dt) { error("DISABLED: net.inet6.ip6.stats module"); return 1; } else { - - // -------------------------------------------------------------------- - if (do_ip6_packets == CONFIG_BOOLEAN_YES || (do_ip6_packets == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_localout || ip6stat.ip6s_total || @@ -2714,8 +2527,7 @@ int do_net_inet6_ip6_stats(int update_every, usec_t dt) { rd_sent = rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_forwarded = rrddim_add(st, "forwarded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_delivers = rrddim_add(st, "delivers", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_sent, ip6stat.ip6s_localout); rrddim_set_by_pointer(st, rd_received, ip6stat.ip6s_total); @@ -2724,8 +2536,6 @@ int do_net_inet6_ip6_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_ip6_fragsout == CONFIG_BOOLEAN_YES || (do_ip6_fragsout == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_fragmented || ip6stat.ip6s_cantfrag || @@ -2757,8 +2567,7 @@ int do_net_inet6_ip6_stats(int update_every, usec_t dt) { rd_ok = rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_failed = rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_all = rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_ok, ip6stat.ip6s_fragmented); rrddim_set_by_pointer(st, rd_failed, ip6stat.ip6s_cantfrag); @@ -2766,8 +2575,6 @@ int do_net_inet6_ip6_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_ip6_fragsin == CONFIG_BOOLEAN_YES || (do_ip6_fragsin == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_reassembled || ip6stat.ip6s_fragdropped || @@ -2801,8 +2608,7 @@ int do_net_inet6_ip6_stats(int update_every, usec_t dt) { rd_failed = rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_timeout = rrddim_add(st, "timeout", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_all = rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_ok, ip6stat.ip6s_reassembled); rrddim_set_by_pointer(st, rd_failed, ip6stat.ip6s_fragdropped); @@ -2811,8 +2617,6 @@ int do_net_inet6_ip6_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_ip6_errors == CONFIG_BOOLEAN_YES || (do_ip6_errors == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_toosmall || ip6stat.ip6s_odropped || @@ -2856,8 +2660,7 @@ int do_net_inet6_ip6_stats(int update_every, usec_t dt) { rd_in_truncated_pkts = rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_no_routes = rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out_no_routes = rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_discards, ip6stat.ip6s_toosmall); rrddim_set_by_pointer(st, rd_out_discards, ip6stat.ip6s_odropped); @@ -2878,7 +2681,6 @@ int do_net_inet6_ip6_stats(int update_every, usec_t dt) { return 0; } -// -------------------------------------------------------------------------------------------------------------------- // net.inet6.icmp6.stats int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { @@ -2966,17 +2768,13 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rd_received = rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_sent = rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_received, icmp6_total.msgs_out); rrddim_set_by_pointer(st, rd_sent, icmp6_total.msgs_in); - rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_redir == CONFIG_BOOLEAN_YES || (do_icmp6_redir == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT] || @@ -3004,16 +2802,13 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rd_received = rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_sent = rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_received, icmp6stat.icp6s_outhist[ND_REDIRECT]); rrddim_set_by_pointer(st, rd_sent, icmp6stat.icp6s_inhist[ND_REDIRECT]); rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_errors == CONFIG_BOOLEAN_YES || (do_icmp6_errors == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_badcode || icmp6stat.icp6s_badlen || @@ -3060,8 +2855,7 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rd_out_dest_unreachs = rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out_time_excds = rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out_parm_problems = rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_errors, icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort); @@ -3077,8 +2871,6 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_echos == CONFIG_BOOLEAN_YES || (do_icmp6_echos == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST] || icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST] || @@ -3110,8 +2902,7 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rd_out = rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_replies = rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out_replies = rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in, icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST]); rrddim_set_by_pointer(st, rd_out, icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST]); @@ -3120,8 +2911,6 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_router == CONFIG_BOOLEAN_YES || (do_icmp6_router == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT] || icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT] || @@ -3154,8 +2943,7 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rd_out_solicits = rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_advertisements = rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out_advertisements = rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_solicits, icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT]); rrddim_set_by_pointer(st, rd_out_solicits, icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT]); @@ -3164,8 +2952,6 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_neighbor == CONFIG_BOOLEAN_YES || (do_icmp6_neighbor == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT] || icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT] || @@ -3198,8 +2984,7 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rd_out_solicits = rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_in_advertisements = rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out_advertisements = rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_solicits, icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT]); rrddim_set_by_pointer(st, rd_out_solicits, icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]); @@ -3208,8 +2993,6 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_types == CONFIG_BOOLEAN_YES || (do_icmp6_types == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[1] || icmp6stat.icp6s_inhist[128] || @@ -3255,8 +3038,7 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { rd_out_133 = rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out_135 = rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out_143 = rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set_by_pointer(st, rd_in_1, icmp6stat.icp6s_inhist[1]); rrddim_set_by_pointer(st, rd_in_128, icmp6stat.icp6s_inhist[128]); diff --git a/collectors/freebsd.plugin/plugin_freebsd.h b/collectors/freebsd.plugin/plugin_freebsd.h index 3a4ec13a7..af7d0822e 100644 --- a/collectors/freebsd.plugin/plugin_freebsd.h +++ b/collectors/freebsd.plugin/plugin_freebsd.h @@ -13,41 +13,41 @@ #define MAX_INT_DIGITS 10 // maximum number of digits for int -extern int freebsd_plugin_init(); - -extern int do_vm_loadavg(int update_every, usec_t dt); -extern int do_vm_vmtotal(int update_every, usec_t dt); -extern int do_kern_cp_time(int update_every, usec_t dt); -extern int do_kern_cp_times(int update_every, usec_t dt); -extern int do_dev_cpu_temperature(int update_every, usec_t dt); -extern int do_dev_cpu_0_freq(int update_every, usec_t dt); -extern int do_hw_intcnt(int update_every, usec_t dt); -extern int do_vm_stats_sys_v_intr(int update_every, usec_t dt); -extern int do_vm_stats_sys_v_soft(int update_every, usec_t dt); -extern int do_vm_stats_sys_v_swtch(int update_every, usec_t dt); -extern int do_vm_stats_sys_v_forks(int update_every, usec_t dt); -extern int do_vm_swap_info(int update_every, usec_t dt); -extern int do_system_ram(int update_every, usec_t dt); -extern int do_vm_stats_sys_v_swappgs(int update_every, usec_t dt); -extern int do_vm_stats_sys_v_pgfaults(int update_every, usec_t dt); -extern int do_kern_ipc_sem(int update_every, usec_t dt); -extern int do_kern_ipc_shm(int update_every, usec_t dt); -extern int do_kern_ipc_msq(int update_every, usec_t dt); -extern int do_uptime(int update_every, usec_t dt); -extern int do_net_isr(int update_every, usec_t dt); -extern int do_net_inet_tcp_states(int update_every, usec_t dt); -extern int do_net_inet_tcp_stats(int update_every, usec_t dt); -extern int do_net_inet_udp_stats(int update_every, usec_t dt); -extern int do_net_inet_icmp_stats(int update_every, usec_t dt); -extern int do_net_inet_ip_stats(int update_every, usec_t dt); -extern int do_net_inet6_ip6_stats(int update_every, usec_t dt); -extern int do_net_inet6_icmp6_stats(int update_every, usec_t dt); -extern int do_getifaddrs(int update_every, usec_t dt); -extern int do_getmntinfo(int update_every, usec_t dt); -extern int do_kern_devstat(int update_every, usec_t dt); -extern int do_kstat_zfs_misc_arcstats(int update_every, usec_t dt); -extern int do_kstat_zfs_misc_zio_trim(int update_every, usec_t dt); -extern int do_ipfw(int update_every, usec_t dt); +int freebsd_plugin_init(); + +int do_vm_loadavg(int update_every, usec_t dt); +int do_vm_vmtotal(int update_every, usec_t dt); +int do_kern_cp_time(int update_every, usec_t dt); +int do_kern_cp_times(int update_every, usec_t dt); +int do_dev_cpu_temperature(int update_every, usec_t dt); +int do_dev_cpu_0_freq(int update_every, usec_t dt); +int do_hw_intcnt(int update_every, usec_t dt); +int do_vm_stats_sys_v_intr(int update_every, usec_t dt); +int do_vm_stats_sys_v_soft(int update_every, usec_t dt); +int do_vm_stats_sys_v_swtch(int update_every, usec_t dt); +int do_vm_stats_sys_v_forks(int update_every, usec_t dt); +int do_vm_swap_info(int update_every, usec_t dt); +int do_system_ram(int update_every, usec_t dt); +int do_vm_stats_sys_v_swappgs(int update_every, usec_t dt); +int do_vm_stats_sys_v_pgfaults(int update_every, usec_t dt); +int do_kern_ipc_sem(int update_every, usec_t dt); +int do_kern_ipc_shm(int update_every, usec_t dt); +int do_kern_ipc_msq(int update_every, usec_t dt); +int do_uptime(int update_every, usec_t dt); +int do_net_isr(int update_every, usec_t dt); +int do_net_inet_tcp_states(int update_every, usec_t dt); +int do_net_inet_tcp_stats(int update_every, usec_t dt); +int do_net_inet_udp_stats(int update_every, usec_t dt); +int do_net_inet_icmp_stats(int update_every, usec_t dt); +int do_net_inet_ip_stats(int update_every, usec_t dt); +int do_net_inet6_ip6_stats(int update_every, usec_t dt); +int do_net_inet6_icmp6_stats(int update_every, usec_t dt); +int do_getifaddrs(int update_every, usec_t dt); +int do_getmntinfo(int update_every, usec_t dt); +int do_kern_devstat(int update_every, usec_t dt); +int do_kstat_zfs_misc_arcstats(int update_every, usec_t dt); +int do_kstat_zfs_misc_zio_trim(int update_every, usec_t dt); +int do_ipfw(int update_every, usec_t dt); // metrics that need to be shared among data collectors extern unsigned long long zfs_arcstats_shrinkable_cache_size_bytes; diff --git a/collectors/freeipmi.plugin/README.md b/collectors/freeipmi.plugin/README.md index 13958784e..ff13717d9 100644 --- a/collectors/freeipmi.plugin/README.md +++ b/collectors/freeipmi.plugin/README.md @@ -9,20 +9,30 @@ Netdata has a [freeipmi](https://www.gnu.org/software/freeipmi/) plugin. > FreeIPMI provides in-band and out-of-band IPMI software based on the IPMI v1.5/2.0 specification. The IPMI specification defines a set of interfaces for platform management and is implemented by a number vendors for system management. The features of IPMI that most users will be interested in are sensor monitoring, system event monitoring, power control, and serial-over-LAN (SOL). -## Compile `freeipmi.plugin` +## Installing the FreeIPMI plugin -1. install `libipmimonitoring-dev` or `libipmimonitoring-devel` (`freeipmi-devel` on RHEL based OS) using the package manager of your system. +When using our official DEB/RPM packages, the FreeIPMI plugin is included in a separate package named +`netdata-plugin-freeipmi` which needs to be manually installed using your system package manager. It is not +installed automatically due to the large number of dependencies it requires. -2. re-install Netdata from source. The installer will detect that the required libraries are now available and will also build `freeipmi.plugin`. +When using a static build of Netdata, the FreeIPMI plugin will be included and installed automatically, though +you will still need to have FreeIPMI installed on your system to be able to use the plugin. -> ❗ In some distributions `libipmimonitoring.pc` is located in an unregistered directory. -> In that case you should find the file and link it to the standard pkg-config directory. Usually, running -> `sudo ln -s /usr/lib/x86_64-linux-gnu/pkgconfig/libipmimonitoring.pc/libipmimonitoring.pc /usr/lib/pkgconfig/libipmimonitoring.pc` -> resolves the issue. +When using a local build of Netdata, you need to ensure that the FreeIPMI development packages (typically called `libipmimonitoring-dev`, `libipmimonitoring-devel`, or `freeipmi-devel`) are installed when building Netdata. -Keep in mind IPMI requires root access, so the plugin is setuid to root. +### Special Considerations -If you just installed the required IPMI tools, please run at least once the command `ipmimonitoring` and verify it returns sensors information. This command initialises IPMI configuration, so that the Netdata plugin will be able to work. +Accessing IPMI requires root access, so the FreeIPMI plugin is automatically installed setuid root. + +FreeIPMI does not work correctly on IBM POWER systems, thus Netdata’s FreeIPMI plugin is not usable on such systems. + +If you have not previously used IPMI on your system, you will probably need to run the `ipmimonitoring` command as root to initiailze IPMI settings so that the Netdata plugin works correctly. It should return information about available seensors on the system. + +In some distributions `libipmimonitoring.pc` is located in a non-standard directory, which +can cause building the plugin to fail when building Netdata from source. In that case you +should find the file and link it to the standard pkg-config directory. Usually, running `sudo ln -s +/usr/lib/$(uname -m)-linux-gnu/pkgconfig/libipmimonitoring.pc/libipmimonitoring.pc /usr/lib/pkgconfig/libipmimonitoring.pc` +resolves this issue. ## Netdata use @@ -190,5 +200,3 @@ If you need to disable IPMI for Netdata, edit `/etc/netdata/netdata.conf` and se [plugins] freeipmi = no ``` - - diff --git a/collectors/idlejitter.plugin/plugin_idlejitter.c b/collectors/idlejitter.plugin/plugin_idlejitter.c index 535819c69..b6339cc0f 100644 --- a/collectors/idlejitter.plugin/plugin_idlejitter.c +++ b/collectors/idlejitter.plugin/plugin_idlejitter.c @@ -47,18 +47,15 @@ void *cpuidlejitter_main(void *ptr) { usec_t update_every_ut = localhost->rrd_update_every * USEC_PER_SEC; struct timeval before, after; - unsigned long long counter; - for(counter = 0; 1 ;counter++) { + while (!netdata_exit) { int iterations = 0; usec_t error_total = 0, error_min = 0, error_max = 0, elapsed = 0; - if(netdata_exit) break; - - while(elapsed < update_every_ut) { + while (elapsed < update_every_ut) { now_monotonic_high_precision_timeval(&before); worker_is_idle(); sleep_usec(sleep_ut); @@ -82,10 +79,7 @@ void *cpuidlejitter_main(void *ptr) { iterations++; } - if(netdata_exit) break; - if(iterations) { - if (likely(counter)) rrdset_next(st); rrddim_set_by_pointer(st, rd_min, error_min); rrddim_set_by_pointer(st, rd_max, error_max); rrddim_set_by_pointer(st, rd_avg, error_total / iterations); diff --git a/collectors/macos.plugin/macos_fw.c b/collectors/macos.plugin/macos_fw.c index 08d1ddadd..07f7d773d 100644 --- a/collectors/macos.plugin/macos_fw.c +++ b/collectors/macos.plugin/macos_fw.c @@ -168,14 +168,11 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); prev_diskstat.bytes_read = rrddim_set(st, "reads", diskstat.bytes_read); prev_diskstat.bytes_write = rrddim_set(st, "writes", diskstat.bytes_write); rrdset_done(st); - // -------------------------------------------------------------------- - /* Get number of reads. */ if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) { CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.reads); @@ -207,14 +204,11 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); prev_diskstat.operations_read = rrddim_set(st, "reads", diskstat.reads); prev_diskstat.operations_write = rrddim_set(st, "writes", diskstat.writes); rrdset_done(st); - // -------------------------------------------------------------------- - /* Get reads time. */ if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) { CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_read); @@ -245,14 +239,11 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "utilization", NULL, 1, 10000000, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); cur_diskstat.busy_time_ns = (diskstat.time_read + diskstat.time_write); prev_diskstat.busy_time_ns = rrddim_set(st, "utilization", cur_diskstat.busy_time_ns); rrdset_done(st); - // -------------------------------------------------------------------- - /* Get reads latency. */ if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) { CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_read); @@ -284,7 +275,6 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); cur_diskstat.duration_read_ns = diskstat.time_read + diskstat.latency_read; cur_diskstat.duration_write_ns = diskstat.time_write + diskstat.latency_write; @@ -292,14 +282,10 @@ int do_macos_iokit(int update_every, usec_t dt) { prev_diskstat.duration_write_ns = rrddim_set(st, "writes", cur_diskstat.duration_write_ns); rrdset_done(st); - // -------------------------------------------------------------------- // calculate differential charts // only if this is not the first time we run if (likely(dt)) { - - // -------------------------------------------------------------------- - st = rrdset_find_active_bytype_localhost("disk_await", diskstat.name); if (unlikely(!st)) { st = rrdset_create_localhost( @@ -321,7 +307,6 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ? (cur_diskstat.duration_read_ns - prev_diskstat.duration_read_ns) / (diskstat.reads - prev_diskstat.operations_read) : 0); @@ -329,8 +314,6 @@ int do_macos_iokit(int update_every, usec_t dt) { (cur_diskstat.duration_write_ns - prev_diskstat.duration_write_ns) / (diskstat.writes - prev_diskstat.operations_write) : 0); rrdset_done(st); - // -------------------------------------------------------------------- - st = rrdset_find_active_bytype_localhost("disk_avgsz", diskstat.name); if (unlikely(!st)) { st = rrdset_create_localhost( @@ -352,7 +335,6 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ? (diskstat.bytes_read - prev_diskstat.bytes_read) / (diskstat.reads - prev_diskstat.operations_read) : 0); @@ -360,8 +342,6 @@ int do_macos_iokit(int update_every, usec_t dt) { (diskstat.bytes_write - prev_diskstat.bytes_write) / (diskstat.writes - prev_diskstat.operations_write) : 0); rrdset_done(st); - // -------------------------------------------------------------------- - st = rrdset_find_active_bytype_localhost("disk_svctm", diskstat.name); if (unlikely(!st)) { st = rrdset_create_localhost( @@ -382,7 +362,6 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "svctm", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set(st, "svctm", ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) ? (cur_diskstat.busy_time_ns - prev_diskstat.busy_time_ns) / ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) : 0); @@ -423,7 +402,6 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "in", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "out", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "in", total_disk_reads); rrddim_set(st, "out", total_disk_writes); @@ -431,7 +409,6 @@ int do_macos_iokit(int update_every, usec_t dt) { } // Can be merged with FreeBSD plugin - // -------------------------------------------------------------------------- if (likely(do_space || do_inodes)) { // there is no mount info in sysctl MIBs @@ -477,8 +454,7 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st); + } rrddim_set(st, "avail", (collected_number) mntbuf[i].f_bavail); rrddim_set(st, "used", (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree)); @@ -510,8 +486,7 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st); + } rrddim_set(st, "avail", (collected_number) mntbuf[i].f_ffree); rrddim_set(st, "used", (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree)); @@ -522,7 +497,6 @@ int do_macos_iokit(int update_every, usec_t dt) { } // Can be merged with FreeBSD plugin - // -------------------------------------------------------------------- if (likely(do_bandwidth)) { if (unlikely(getifaddrs(&ifap))) { @@ -534,8 +508,6 @@ int do_macos_iokit(int update_every, usec_t dt) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; - // -------------------------------------------------------------------- - st = rrdset_find_active_bytype_localhost("net", ifa->ifa_name); if (unlikely(!st)) { st = rrdset_create_localhost( @@ -556,14 +528,11 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "received", IFA_DATA(ibytes)); rrddim_set(st, "sent", IFA_DATA(obytes)); rrdset_done(st); - // -------------------------------------------------------------------- - st = rrdset_find_active_bytype_localhost("net_packets", ifa->ifa_name); if (unlikely(!st)) { st = rrdset_create_localhost( @@ -587,7 +556,6 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "multicast_received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "multicast_sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "received", IFA_DATA(ipackets)); rrddim_set(st, "sent", IFA_DATA(opackets)); @@ -595,8 +563,6 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_set(st, "multicast_sent", IFA_DATA(omcasts)); rrdset_done(st); - // -------------------------------------------------------------------- - st = rrdset_find_active_bytype_localhost("net_errors", ifa->ifa_name); if (unlikely(!st)) { st = rrdset_create_localhost( @@ -618,14 +584,11 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "inbound", IFA_DATA(ierrors)); rrddim_set(st, "outbound", IFA_DATA(oerrors)); rrdset_done(st); - // -------------------------------------------------------------------- - st = rrdset_find_active_bytype_localhost("net_drops", ifa->ifa_name); if (unlikely(!st)) { st = rrdset_create_localhost( @@ -646,13 +609,10 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "inbound", IFA_DATA(iqdrops)); rrdset_done(st); - // -------------------------------------------------------------------- - st = rrdset_find_active_bytype_localhost("net_events", ifa->ifa_name); if (unlikely(!st)) { st = rrdset_create_localhost( @@ -675,7 +635,6 @@ int do_macos_iokit(int update_every, usec_t dt) { rrddim_add(st, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "collisions", IFA_DATA(collisions)); rrdset_done(st); @@ -685,6 +644,5 @@ int do_macos_iokit(int update_every, usec_t dt) { } } - return 0; } diff --git a/collectors/macos.plugin/macos_mach_smi.c b/collectors/macos.plugin/macos_mach_smi.c index f2c4623c9..53b2607b4 100644 --- a/collectors/macos.plugin/macos_mach_smi.c +++ b/collectors/macos.plugin/macos_mach_smi.c @@ -39,8 +39,6 @@ int do_macos_mach_smi(int update_every, usec_t dt) { if (unlikely(kr != KERN_SUCCESS)) return -1; - // -------------------------------------------------------------------- - if (likely(do_cpu)) { if (unlikely(HOST_CPU_LOAD_INFO_COUNT != 4)) { error("MACOS: There are %d CPU states (4 was expected)", HOST_CPU_LOAD_INFO_COUNT); @@ -78,7 +76,6 @@ int do_macos_mach_smi(int update_every, usec_t dt) { rrddim_add(st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rrddim_hide(st, "idle"); } - else rrdset_next(st); rrddim_set(st, "user", cp_time[CPU_STATE_USER]); rrddim_set(st, "nice", cp_time[CPU_STATE_NICE]); @@ -87,9 +84,7 @@ int do_macos_mach_smi(int update_every, usec_t dt) { rrdset_done(st); } } - } - - // -------------------------------------------------------------------- + } if (likely(do_ram || do_swapio || do_pgfaults)) { #if (defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) @@ -137,7 +132,6 @@ int do_macos_mach_smi(int update_every, usec_t dt) { rrddim_add(st, "speculative", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "free", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set(st, "active", vm_statistics.active_count); rrddim_set(st, "wired", vm_statistics.wire_count); @@ -153,8 +147,6 @@ int do_macos_mach_smi(int update_every, usec_t dt) { } #if (defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) - // -------------------------------------------------------------------- - if (likely(do_swapio)) { st = rrdset_find_active_localhost("system.swapio"); if (unlikely(!st)) { @@ -176,7 +168,6 @@ int do_macos_mach_smi(int update_every, usec_t dt) { rrddim_add(st, "in", NULL, system_pagesize, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "in", vm_statistics.swapins); rrddim_set(st, "out", vm_statistics.swapouts); @@ -184,8 +175,6 @@ int do_macos_mach_smi(int update_every, usec_t dt) { } #endif - // -------------------------------------------------------------------- - if (likely(do_pgfaults)) { st = rrdset_find_active_localhost("mem.pgfaults"); if (unlikely(!st)) { @@ -217,7 +206,6 @@ int do_macos_mach_smi(int update_every, usec_t dt) { rrddim_add(st, "reactivate", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "purge", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "memory", vm_statistics.faults); rrddim_set(st, "cow", vm_statistics.cow_faults); @@ -235,7 +223,5 @@ int do_macos_mach_smi(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - return 0; } diff --git a/collectors/macos.plugin/macos_sysctl.c b/collectors/macos.plugin/macos_sysctl.c index 34d3f0b3e..1f04f6e41 100644 --- a/collectors/macos.plugin/macos_sysctl.c +++ b/collectors/macos.plugin/macos_sysctl.c @@ -218,8 +218,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { // NEEDED BY: do_uptime struct timespec boot_time, cur_time; - // -------------------------------------------------------------------- - if (next_loadavg_dt <= dt) { if (likely(do_loadavg)) { if (unlikely(GETSYSCTL_BY_NAME("vm.loadavg", sysload))) { @@ -247,7 +245,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "load5", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "load15", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set(st, "load1", (collected_number) ((double)sysload.ldavg[0] / sysload.fscale * 1000)); rrddim_set(st, "load5", (collected_number) ((double)sysload.ldavg[1] / sysload.fscale * 1000)); @@ -260,8 +257,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { } else next_loadavg_dt -= dt; - // -------------------------------------------------------------------- - if (likely(do_swap)) { if (unlikely(GETSYSCTL_BY_NAME("vm.swapusage", swap_usage))) { do_swap = 0; @@ -288,7 +283,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "free", NULL, 1, 1048576, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "used", NULL, 1, 1048576, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set(st, "free", swap_usage.xsu_avail); rrddim_set(st, "used", swap_usage.xsu_used); @@ -296,8 +290,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - if (likely(do_bandwidth)) { mib[0] = CTL_NET; mib[1] = PF_ROUTE; @@ -349,7 +341,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "InOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "InOctets", iftot.ift_ibytes); rrddim_set(st, "OutOctets", iftot.ift_obytes); @@ -358,8 +349,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html if (likely(do_tcp_packets || do_tcp_errors || do_tcp_handshake || do_tcpext_connaborts || do_tcpext_ofo || do_tcpext_syscookies || do_ecn)) { if (unlikely(GETSYSCTL_BY_NAME("net.inet.tcp.stats", tcpstat))){ @@ -398,16 +387,13 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "InSegs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutSegs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InSegs", tcpstat.tcps_rcvtotal); rrddim_set(st, "OutSegs", tcpstat.tcps_sndtotal); rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_tcp_errors)) { st = rrdset_find_active_localhost("ipv4.tcperrors"); if (unlikely(!st)) { @@ -430,8 +416,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "InErrs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "RetransSegs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InErrs", tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvshort); rrddim_set(st, "InCsumErrors", tcpstat.tcps_rcvbadsum); @@ -439,8 +424,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_tcp_handshake)) { st = rrdset_find_active_localhost("ipv4.tcphandshake"); if (unlikely(!st)) { @@ -464,8 +447,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "AttemptFails", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "EstabResets", tcpstat.tcps_drops); rrddim_set(st, "ActiveOpens", tcpstat.tcps_connattempt); @@ -474,8 +456,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || @@ -505,7 +485,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "TCPAbortOnData", tcpstat.tcps_rcvpackafterwin); rrddim_set(st, "TCPAbortOnClose", tcpstat.tcps_rcvafterclose); @@ -514,8 +493,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_rcvoopack || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -539,14 +516,11 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "TCPOFOQueue", tcpstat.tcps_rcvoopack); rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_tcpext_syscookies == CONFIG_BOOLEAN_YES || (do_tcpext_syscookies == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || @@ -575,7 +549,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "SyncookiesRecv", tcpstat.tcps_sc_recvcookie); rrddim_set(st, "SyncookiesSent", tcpstat.tcps_sc_sendcookie); @@ -583,9 +556,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - - #if (defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) if (do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_ecn_recv_ce || @@ -613,7 +583,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "InCEPkts", tcpstat.tcps_ecn_recv_ce); rrddim_set(st, "InNoECTPkts", tcpstat.tcps_ecn_not_supported); @@ -624,8 +593,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - // see http://net-snmp.sourceforge.net/docs/mibs/udp.html if (likely(do_udp_packets || do_udp_errors)) { if (unlikely(GETSYSCTL_BY_NAME("net.inet.udp.stats", udpstat))) { @@ -654,16 +621,13 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InDatagrams", udpstat.udps_ipackets); rrddim_set(st, "OutDatagrams", udpstat.udps_opackets); rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_udp_errors)) { st = rrdset_find_active_localhost("ipv4.udperrors"); if (unlikely(!st)) { @@ -690,8 +654,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { #if (defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); #endif - } else - rrdset_next(st); + } rrddim_set(st, "InErrors", udpstat.udps_hdrops + udpstat.udps_badlen); rrddim_set(st, "NoPorts", udpstat.udps_noport); @@ -707,8 +670,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - if (likely(do_icmp_packets || do_icmpmsg)) { if (unlikely(GETSYSCTL_BY_NAME("net.inet.icmp.stats", icmpstat))) { do_icmp_packets = 0; @@ -745,16 +706,12 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InMsgs", icmp_total.msgs_in); rrddim_set(st, "OutMsgs", icmp_total.msgs_out); - rrdset_done(st); - // -------------------------------------------------------------------- - st = rrdset_find_active_localhost("ipv4.icmp_errors"); if (unlikely(!st)) { st = rrdset_create_localhost( @@ -775,18 +732,14 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InErrors", icmpstat.icps_badcode + icmpstat.icps_badlen + icmpstat.icps_checksum + icmpstat.icps_tooshort); rrddim_set(st, "OutErrors", icmpstat.icps_error); rrddim_set(st, "InCsumErrors", icmpstat.icps_checksum); - rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_icmpmsg)) { st = rrdset_find_active_localhost("ipv4.icmpmsg"); if (unlikely(!st)) { @@ -809,21 +762,17 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); - - rrddim_set(st, "InEchoReps", icmpstat.icps_inhist[ICMP_ECHOREPLY]); - rrddim_set(st, "OutEchoReps", icmpstat.icps_outhist[ICMP_ECHOREPLY]); - rrddim_set(st, "InEchos", icmpstat.icps_inhist[ICMP_ECHO]); - rrddim_set(st, "OutEchos", icmpstat.icps_outhist[ICMP_ECHO]); + } + rrddim_set(st, "InEchoReps", icmpstat.icps_inhist[ICMP_ECHOREPLY]); + rrddim_set(st, "OutEchoReps", icmpstat.icps_outhist[ICMP_ECHOREPLY]); + rrddim_set(st, "InEchos", icmpstat.icps_inhist[ICMP_ECHO]); + rrddim_set(st, "OutEchos", icmpstat.icps_outhist[ICMP_ECHO]); rrdset_done(st); } } } - // -------------------------------------------------------------------- - // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html if (likely(do_ip_packets || do_ip_fragsout || do_ip_fragsin || do_ip_errors)) { if (unlikely(GETSYSCTL_BY_NAME("net.inet.ip.stats", ipstat))) { @@ -858,8 +807,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "InDelivers", "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "OutRequests", ipstat.ips_localout); rrddim_set(st, "InReceives", ipstat.ips_total); @@ -868,8 +816,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_ip_fragsout)) { st = rrdset_find_active_localhost("ipv4.fragsout"); if (unlikely(!st)) { @@ -892,8 +838,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "FragOKs", ipstat.ips_fragmented); rrddim_set(st, "FragFails", ipstat.ips_cantfrag); @@ -901,8 +846,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_ip_fragsin)) { st = rrdset_find_active_localhost("ipv4.fragsin"); if (unlikely(!st)) { @@ -925,8 +868,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "ReasmOKs", ipstat.ips_fragments); rrddim_set(st, "ReasmFails", ipstat.ips_fragdropped); @@ -934,8 +876,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (likely(do_ip_errors)) { st = rrdset_find_active_localhost("ipv4.errors"); if (unlikely(!st)) { @@ -963,8 +903,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InDiscards", ipstat.ips_badsum + ipstat.ips_tooshort + ipstat.ips_toosmall + ipstat.ips_toolong); rrddim_set(st, "OutDiscards", ipstat.ips_odropped); @@ -977,8 +916,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - if (likely(do_ip6_packets || do_ip6_fragsout || do_ip6_fragsin || do_ip6_errors)) { if (unlikely(GETSYSCTL_BY_NAME("net.inet6.ip6.stats", ip6stat))) { do_ip6_packets = 0; @@ -1018,8 +955,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "forwarded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "delivers", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "sent", ip6stat.ip6s_localout); rrddim_set(st, "received", ip6stat.ip6s_total); @@ -1028,8 +964,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_ip6_fragsout == CONFIG_BOOLEAN_YES || (do_ip6_fragsout == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_fragmented || ip6stat.ip6s_cantfrag || @@ -1057,8 +991,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "ok", ip6stat.ip6s_fragmented); rrddim_set(st, "failed", ip6stat.ip6s_cantfrag); @@ -1066,8 +999,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_ip6_fragsin == CONFIG_BOOLEAN_YES || (do_ip6_fragsin == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_reassembled || ip6stat.ip6s_fragdropped || @@ -1097,8 +1028,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "timeout", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "ok", ip6stat.ip6s_reassembled); rrddim_set(st, "failed", ip6stat.ip6s_fragdropped); @@ -1107,8 +1037,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_ip6_errors == CONFIG_BOOLEAN_YES || (do_ip6_errors == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_toosmall || ip6stat.ip6s_odropped || @@ -1148,8 +1076,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InDiscards", ip6stat.ip6s_toosmall); rrddim_set(st, "OutDiscards", ip6stat.ip6s_odropped); @@ -1166,8 +1093,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - if (likely(do_icmp6 || do_icmp6_redir || do_icmp6_errors || do_icmp6_echos || do_icmp6_router || do_icmp6_neighbor || do_icmp6_types)) { if (unlikely(GETSYSCTL_BY_NAME("net.inet6.icmp6.stats", icmp6stat))) { do_icmp6 = 0; @@ -1202,16 +1127,13 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "sent", icmp6_total.msgs_in); rrddim_set(st, "received", icmp6_total.msgs_out); rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_redir == CONFIG_BOOLEAN_YES || (do_icmp6_redir == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT] || @@ -1236,16 +1158,13 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "sent", icmp6stat.icp6s_inhist[ND_REDIRECT]); rrddim_set(st, "received", icmp6stat.icp6s_outhist[ND_REDIRECT]); rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_errors == CONFIG_BOOLEAN_YES || (do_icmp6_errors == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_badcode || icmp6stat.icp6s_badlen || @@ -1288,8 +1207,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InErrors", icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort); rrddim_set(st, "OutErrors", icmp6stat.icp6s_error); @@ -1304,8 +1222,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_echos == CONFIG_BOOLEAN_YES || (do_icmp6_echos == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST] || icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST] || @@ -1334,8 +1250,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InEchos", icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST]); rrddim_set(st, "OutEchos", icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST]); @@ -1344,8 +1259,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_router == CONFIG_BOOLEAN_YES || (do_icmp6_router == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT] || icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT] || @@ -1374,8 +1287,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InSolicits", icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT]); rrddim_set(st, "OutSolicits", icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT]); @@ -1384,8 +1296,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_neighbor == CONFIG_BOOLEAN_YES || (do_icmp6_neighbor == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT] || icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT] || @@ -1414,18 +1324,14 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InSolicits", icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT]); rrddim_set(st, "OutSolicits", icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]); rrddim_set(st, "InAdvertisements", icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT]); rrddim_set(st, "OutAdvertisements", icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]); - rrdset_done(st); } - // -------------------------------------------------------------------- - if (do_icmp6_types == CONFIG_BOOLEAN_YES || (do_icmp6_types == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[1] || icmp6stat.icp6s_inhist[128] || @@ -1466,8 +1372,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st); + } rrddim_set(st, "InType1", icmp6stat.icp6s_inhist[1]); rrddim_set(st, "InType128", icmp6stat.icp6s_inhist[128]); @@ -1484,8 +1389,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - if (likely(do_uptime)) { if (unlikely(GETSYSCTL_BY_NAME("kern.boottime", boot_time))) { do_uptime = 0; @@ -1511,7 +1414,6 @@ int do_macos_sysctl(int update_every, usec_t dt) { ); rrddim_add(st, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set(st, "uptime", cur_time.tv_sec - boot_time.tv_sec); rrdset_done(st); @@ -1520,4 +1422,3 @@ int do_macos_sysctl(int update_every, usec_t dt) { return 0; } - diff --git a/collectors/macos.plugin/plugin_macos.h b/collectors/macos.plugin/plugin_macos.h index b4c2cf542..2c673a224 100644 --- a/collectors/macos.plugin/plugin_macos.h +++ b/collectors/macos.plugin/plugin_macos.h @@ -5,8 +5,8 @@ #include "daemon/common.h" -extern int do_macos_sysctl(int update_every, usec_t dt); -extern int do_macos_mach_smi(int update_every, usec_t dt); -extern int do_macos_iokit(int update_every, usec_t dt); +int do_macos_sysctl(int update_every, usec_t dt); +int do_macos_mach_smi(int update_every, usec_t dt); +int do_macos_iokit(int update_every, usec_t dt); #endif /* NETDATA_PLUGIN_MACOS_H */ diff --git a/collectors/plugins.d/README.md b/collectors/plugins.d/README.md index 0741636b9..2ecf233f7 100644 --- a/collectors/plugins.d/README.md +++ b/collectors/plugins.d/README.md @@ -116,17 +116,19 @@ For example, if your plugin wants to monitor `squid`, you can search for it on p Any program that can print a few values to its standard output can become a Netdata external plugin. -Netdata parses 9 lines starting with: +Netdata parses lines starting with: - `CHART` - create or update a chart - `DIMENSION` - add or update a dimension to the chart just created +- `VARIABLE` - define a variable (to be used in health calculations) +- `CLABEL` - add a label to a chart +- `CLABEL_COMMIT` - commit added labels to the chart +- `FUNCTION` - define a function that can be called later to execute it - `BEGIN` - initialize data collection for a chart - `SET` - set the value of a dimension for the initialized chart - `END` - complete data collection for the initialized chart - `FLUSH` - ignore the last collected values - `DISABLE` - disable this plugin -- `CLABEL` - add a label to a chart -- `CLABEL_COMMIT` - commit added labels to the chart. a single program can produce any number of charts with any number of dimensions each. @@ -362,6 +364,80 @@ The `source` is an integer field that can have the following values: `CLABEL_COMMIT` indicates that all labels were defined and the chart can be updated. +#### FUNCTION + +> FUNCTION [GLOBAL] "name and parameters of the function" timeout "help string for users" + +A function can be used by users to ask for more information from the collector. Netdata maintains a registry of functions in 2 levels: + +- per node +- per chart + +Both node and chart functions are exactly the same, but chart functions allow Netdata to relate functions with charts and therefore present a context sensitive menu of functions related to the chart the user is using. + +A function is identified by a string. The allowed characters in the function definition are: + +| Character | Symbol | In Functions | +|-------------------|:------:|:------------:| +| UTF-8 character | UTF-8 | keep | +| Lower case letter | [a-z] | keep | +| Upper case letter | [A-Z] | keep | +| Digit | [0-9] | keep | +| Underscore | _ | keep | +| Comma | , | keep | +| Minus | - | keep | +| Period | . | keep | +| Colon | : | keep | +| Slash | / | keep | +| Space | ' ' | keep | +| Semicolon | ; | : | +| Equal | = | : | +| Backslash | \ | / | +| Anything else | | _ | + +Uses can get a list of all the registered functions using the `/api/v1/functions` end point of Netdata. + +Users can call functions using the `/api/v1/function` end point of Netdata. +Once a function is called, the plugin will receive at its standard input a command that looks like this: + +> FUNCTION transaction_id timeout "name and parameters of the function" + +The plugin is expected to parse and validate `name and parameters of the function`. Netdata allows users to edit this string, append more parameters or even change the ones the plugin originally exposed. To minimize the security risk, Netdata guarantees that only the characters shown above are accepted in function definitions, but still the plugin should carefully inspect the `name and parameters of the function` to ensure that it is valid and not harmful. + +If the plugin rejects the request, it should respond with this: + +``` +FUNCTION_RESULT_BEGIN transaction_id 400 application/json +{ + "status": 400, + "error_message": "description of the rejection reasons" +} +FUNCTION_RESULT_END +``` + +If the plugin prepares a response, it should send (via its standard output, together with the collected data, but not interleaved with them): + +> FUNCTION_RESULT_BEGIN transaction_id http_error_code content_type expiration + +Where: + + - `transaction_id` is the transaction id that Netdata sent for this function execution + - `http_error` is the http error code Netdata should respond with, 200 is the "ok" response + - `content_type` is the content type of the response + - `expiration` is the absolute timestamp (number, unix epoch) this response expires + +Immediately after this, all text is assumed to be the response content. +The content is text and line oriented. The maximum line length accepted is 15kb. Longer lines will be truncated. +The type of the context itself depends on the plugin and the UI. + +To terminate the message, Netdata seeks a line with just this: + +> FUNCTION_RESULT_END + +This defines the end of the message. `FUNCTION_RESULT_END` should appear in a line alone, without any other text, so it is wise to add `\n` before and after it. + +After this line, Netdata resumes processing collected metrics from the plugin. + ## Data collection data collection is defined as a series of `BEGIN` -> `SET` -> `END` lines @@ -463,7 +539,7 @@ There are a few rules for writing plugins properly: readConfiguration(); if(!verifyWeCanCollectValues()) { - print "DISABLE"; + print("DISABLE"); exit(1); } @@ -475,7 +551,7 @@ There are a few rules for writing plugins properly: var dt_since_last_run = 0; var now = 0; - FOREVER { + while(true) { /* find the current time in milliseconds */ now = currentTimeStampInMilliseconds(); diff --git a/collectors/plugins.d/plugins_d.c b/collectors/plugins.d/plugins_d.c index 377ec1401..79abc7070 100644 --- a/collectors/plugins.d/plugins_d.c +++ b/collectors/plugins.d/plugins_d.c @@ -6,118 +6,7 @@ char *plugin_directories[PLUGINSD_MAX_DIRECTORIES] = { NULL }; struct plugind *pluginsd_root = NULL; -inline int pluginsd_space(char c) { - switch(c) { - case ' ': - case '\t': - case '\r': - case '\n': - case '=': - return 1; - - default: - return 0; - } -} - -inline int config_isspace(char c) -{ - switch (c) { - case ' ': - case '\t': - case '\r': - case '\n': - case ',': - return 1; - - default: - return 0; - } -} - -// split a text into words, respecting quotes -inline int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover) -{ - char *s = str, quote = 0; - int i = 0, rec = 0; - char *recover = recover_input; - - // skip all white space - while (unlikely(custom_isspace(*s))) - s++; - - // check for quote - if (unlikely(*s == '\'' || *s == '"')) { - quote = *s; // remember the quote - s++; // skip the quote - } - - // store the first word - words[i++] = s; - - // while we have something - while (likely(*s)) { - // if it is escape - if (unlikely(*s == '\\' && s[1])) { - s += 2; - continue; - } - - // if it is quote - else if (unlikely(*s == quote)) { - quote = 0; - if (recover && rec < max_recover) { - recover_location[rec++] = s; - *recover++ = *s; - } - *s = ' '; - continue; - } - - // if it is a space - else if (unlikely(quote == 0 && custom_isspace(*s))) { - // terminate the word - if (recover && rec < max_recover) { - if (!rec || (rec && recover_location[rec-1] != s)) { - recover_location[rec++] = s; - *recover++ = *s; - } - } - *s++ = '\0'; - - // skip all white space - while (likely(custom_isspace(*s))) - s++; - - // check for quote - if (unlikely(*s == '\'' || *s == '"')) { - quote = *s; // remember the quote - s++; // skip the quote - } - - // if we reached the end, stop - if (unlikely(!*s)) - break; - - // store the next word - if (likely(i < max_words)) - words[i++] = s; - else - break; - } - - // anything else - else - s++; - } - - // terminate the words - memset(&words[i], 0, (max_words - i) * sizeof (char *)); - - return i; -} - -inline int pluginsd_initialize_plugin_directories() +inline size_t pluginsd_initialize_plugin_directories() { char plugins_dirs[(FILENAME_MAX * 2) + 1]; static char *plugins_dir_list = NULL; @@ -132,12 +21,6 @@ inline int pluginsd_initialize_plugin_directories() return quoted_strings_splitter(plugins_dir_list, plugin_directories, PLUGINSD_MAX_DIRECTORIES, config_isspace, NULL, NULL, 0); } -inline int pluginsd_split_words(char *str, char **words, int max_words, char *recover_input, char **recover_location, int max_recover) -{ - return quoted_strings_splitter(str, words, max_words, pluginsd_space, recover_input, recover_location, max_recover); -} - - static void pluginsd_worker_thread_cleanup(void *arg) { struct plugind *cd = (struct plugind *)arg; @@ -238,18 +121,19 @@ void *pluginsd_worker_thread(void *arg) size_t count = 0; while (!netdata_exit) { - FILE *fp = mypopen(cd->cmd, &cd->pid); - if (unlikely(!fp)) { + FILE *fp_child_input = NULL; + FILE *fp_child_output = netdata_popen(cd->cmd, &cd->pid, &fp_child_input); + if (unlikely(!fp_child_input || !fp_child_output)) { error("Cannot popen(\"%s\", \"r\").", cd->cmd); break; } info("connected to '%s' running on pid %d", cd->fullfilename, cd->pid); - count = pluginsd_process(localhost, cd, fp, 0); + count = pluginsd_process(localhost, cd, fp_child_input, fp_child_output, 0); error("'%s' (pid %d) disconnected after %zu successful data collections (ENDs).", cd->fullfilename, cd->pid, count); killpid(cd->pid); - int worker_ret_code = mypclose(fp, cd->pid); + int worker_ret_code = netdata_pclose(fp_child_input, fp_child_output, cd->pid); if (likely(worker_ret_code == 0)) pluginsd_worker_thread_handle_success(cd); diff --git a/collectors/plugins.d/plugins_d.h b/collectors/plugins.d/plugins_d.h index e0b8ac570..a8acf038a 100644 --- a/collectors/plugins.d/plugins_d.h +++ b/collectors/plugins.d/plugins_d.h @@ -10,23 +10,34 @@ #define PLUGINSD_CMD_MAX (FILENAME_MAX*2) #define PLUGINSD_STOCK_PLUGINS_DIRECTORY_PATH 0 -#define PLUGINSD_KEYWORD_CHART "CHART" -#define PLUGINSD_KEYWORD_DIMENSION "DIMENSION" -#define PLUGINSD_KEYWORD_BEGIN "BEGIN" -#define PLUGINSD_KEYWORD_END "END" -#define PLUGINSD_KEYWORD_FLUSH "FLUSH" -#define PLUGINSD_KEYWORD_DISABLE "DISABLE" -#define PLUGINSD_KEYWORD_VARIABLE "VARIABLE" -#define PLUGINSD_KEYWORD_LABEL "LABEL" -#define PLUGINSD_KEYWORD_OVERWRITE "OVERWRITE" -#define PLUGINSD_KEYWORD_GUID "GUID" -#define PLUGINSD_KEYWORD_CONTEXT "CONTEXT" -#define PLUGINSD_KEYWORD_TOMBSTONE "TOMBSTONE" -#define PLUGINSD_KEYWORD_HOST "HOST" - - -#define PLUGINSD_LINE_MAX 1024 +#define PLUGINSD_KEYWORD_CHART "CHART" +#define PLUGINSD_KEYWORD_CHART_DEFINITION_END "CHART_DEFINITION_END" +#define PLUGINSD_KEYWORD_DIMENSION "DIMENSION" +#define PLUGINSD_KEYWORD_BEGIN "BEGIN" +#define PLUGINSD_KEYWORD_SET "SET" +#define PLUGINSD_KEYWORD_END "END" +#define PLUGINSD_KEYWORD_FLUSH "FLUSH" +#define PLUGINSD_KEYWORD_DISABLE "DISABLE" +#define PLUGINSD_KEYWORD_VARIABLE "VARIABLE" +#define PLUGINSD_KEYWORD_LABEL "LABEL" +#define PLUGINSD_KEYWORD_OVERWRITE "OVERWRITE" +#define PLUGINSD_KEYWORD_CLABEL "CLABEL" +#define PLUGINSD_KEYWORD_CLABEL_COMMIT "CLABEL_COMMIT" +#define PLUGINSD_KEYWORD_FUNCTION "FUNCTION" +#define PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN "FUNCTION_RESULT_BEGIN" +#define PLUGINSD_KEYWORD_FUNCTION_RESULT_END "FUNCTION_RESULT_END" + +#define PLUGINSD_KEYWORD_REPLAY_CHART "REPLAY_CHART" +#define PLUGINSD_KEYWORD_REPLAY_BEGIN "RBEGIN" +#define PLUGINSD_KEYWORD_REPLAY_SET "RSET" +#define PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE "RDSTATE" +#define PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE "RSSTATE" +#define PLUGINSD_KEYWORD_REPLAY_END "REND" + +#define PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT 10 // seconds + #define PLUGINSD_LINE_MAX_SSL_READ 512 + #define PLUGINSD_MAX_WORDS 20 #define PLUGINSD_MAX_DIRECTORIES 20 @@ -53,19 +64,40 @@ struct plugind { volatile sig_atomic_t enabled; // if this is enabled or not time_t started_t; - uint32_t version; + uint32_t capabilities; // follows the same principles as streaming capabilities struct plugind *next; }; extern struct plugind *pluginsd_root; -extern size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations); -extern int pluginsd_split_words(char *str, char **words, int max_words, char *recover_string, char **recover_location, int max_recover); +size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp_plugin_input, FILE *fp_plugin_output, int trust_durations); + +size_t pluginsd_initialize_plugin_directories(); + + + +#define pluginsd_function_result_begin_to_buffer(wb, transaction, code, content_type, expires) \ + buffer_sprintf(wb \ + , PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN " \"%s\" %d \"%s\" %ld\n" \ + , (transaction) ? (transaction) : "" \ + , (int)(code) \ + , (content_type) ? (content_type) : "" \ + , (long int)(expires) \ + ) + +#define pluginsd_function_result_end_to_buffer(wb) \ + buffer_strcat(wb, "\n" PLUGINSD_KEYWORD_FUNCTION_RESULT_END "\n") -extern int pluginsd_initialize_plugin_directories(); +#define pluginsd_function_result_begin_to_stdout(transaction, code, content_type, expires) \ + fprintf(stdout \ + , PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN " \"%s\" %d \"%s\" %ld\n" \ + , (transaction) ? (transaction) : "" \ + , (int)(code) \ + , (content_type) ? (content_type) : "" \ + , (long int)(expires) \ + ) -extern int config_isspace(char c); -extern int pluginsd_space(char c); -int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover); +#define pluginsd_function_result_end_to_stdout() \ + fprintf(stdout, "\n" PLUGINSD_KEYWORD_FUNCTION_RESULT_END "\n") #endif /* NETDATA_PLUGINS_D_H */ diff --git a/collectors/plugins.d/pluginsd_parser.c b/collectors/plugins.d/pluginsd_parser.c index 88e07fab7..5501c12fa 100644 --- a/collectors/plugins.d/pluginsd_parser.c +++ b/collectors/plugins.d/pluginsd_parser.c @@ -2,336 +2,223 @@ #include "pluginsd_parser.h" -/* - * This is the action defined for the FLUSH command - */ -PARSER_RC pluginsd_set_action(void *user, RRDSET *st, RRDDIM *rd, long long int value) -{ - UNUSED(user); - - rrddim_set_by_pointer(st, rd, value); - return PARSER_RC_OK; -} +#define LOG_FUNCTIONS false -PARSER_RC pluginsd_flush_action(void *user, RRDSET *st) -{ - UNUSED(user); - UNUSED(st); - return PARSER_RC_OK; -} +static int send_to_plugin(const char *txt, void *data) { + PARSER *parser = data; -PARSER_RC pluginsd_begin_action(void *user, RRDSET *st, usec_t microseconds, int trust_durations) -{ - UNUSED(user); - if (likely(st->counter_done)) { - if (likely(microseconds)) { - if (trust_durations) - rrdset_next_usec_unfiltered(st, microseconds); - else - rrdset_next_usec(st, microseconds); - } else - rrdset_next(st); - } - return PARSER_RC_OK; -} - - -PARSER_RC pluginsd_end_action(void *user, RRDSET *st) -{ - UNUSED(user); - - rrdset_done(st); - return PARSER_RC_OK; -} + if(!txt || !*txt) + return 0; -PARSER_RC pluginsd_chart_action(void *user, char *type, char *id, char *name, char *family, char *context, char *title, char *units, char *plugin, - char *module, int priority, int update_every, RRDSET_TYPE chart_type, char *options) -{ - RRDSET *st = NULL; - RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; +#ifdef ENABLE_HTTPS + struct netdata_ssl *ssl = parser->ssl_output; + if(ssl) { + if(ssl->conn && ssl->flags == NETDATA_SSL_HANDSHAKE_COMPLETE) + return (int)netdata_ssl_write(ssl->conn, (void *)txt, strlen(txt)); - st = rrdset_create( - host, type, id, name, family, context, title, units, - plugin, module, priority, update_every, - chart_type); + error("PLUGINSD: cannot send command (SSL)"); + return -1; + } +#endif - if (options && *options) { - if (strstr(options, "obsolete")) - rrdset_is_obsolete(st); - else - rrdset_isnot_obsolete(st); + if(parser->fp_output) { + int bytes = fprintf(parser->fp_output, "%s", txt); + if(bytes <= 0) { + error("PLUGINSD: cannot send command (FILE)"); + return -2; + } + fflush(parser->fp_output); + return bytes; + } - if (strstr(options, "detail")) - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - else - rrdset_flag_clear(st, RRDSET_FLAG_DETAIL); + if(parser->fd != -1) { + size_t bytes = 0; + size_t total = strlen(txt); + ssize_t sent; - if (strstr(options, "hidden")) - rrdset_flag_set(st, RRDSET_FLAG_HIDDEN); - else - rrdset_flag_clear(st, RRDSET_FLAG_HIDDEN); + do { + sent = write(parser->fd, &txt[bytes], total - bytes); + if(sent <= 0) { + error("PLUGINSD: cannot send command (fd)"); + return -3; + } + bytes += sent; + } + while(bytes < total); - if (strstr(options, "store_first")) - rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST); - else - rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST); - } else { - rrdset_isnot_obsolete(st); - rrdset_flag_clear(st, RRDSET_FLAG_DETAIL); - rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST); + return (int)bytes; } - ((PARSER_USER_OBJECT *)user)->st = st; - return PARSER_RC_OK; + error("PLUGINSD: cannot send command (no output socket/pipe/file given to plugins.d parser)"); + return -4; } +static inline RRDHOST *pluginsd_require_host_from_parent(void *user, const char *cmd) { + RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; -PARSER_RC pluginsd_disable_action(void *user) -{ - UNUSED(user); + if(unlikely(!host)) + error("PLUGINSD: command %s requires a host, but is not set.", cmd); - info("called DISABLE. Disabling it."); - ((PARSER_USER_OBJECT *) user)->enabled = 0; - return PARSER_RC_ERROR; + return host; } +static inline RRDSET *pluginsd_require_chart_from_parent(void *user, const char *cmd, const char *parent_cmd) { + RRDSET *st = ((PARSER_USER_OBJECT *) user)->st; -PARSER_RC pluginsd_variable_action(void *user, RRDHOST *host, RRDSET *st, char *name, int global, NETDATA_DOUBLE value) -{ - UNUSED(user); + if(unlikely(!st)) + error("PLUGINSD: command %s requires a chart defined via command %s, but is not set.", cmd, parent_cmd); - if (global) { - RRDVAR *rv = rrdvar_custom_host_variable_create(host, name); - if (rv) - rrdvar_custom_host_variable_set(host, rv, value); - else - error("cannot find/create HOST VARIABLE '%s' on host '%s'", name, host->hostname); - } else { - RRDSETVAR *rs = rrdsetvar_custom_chart_variable_create(st, name); - if (rs) - rrdsetvar_custom_chart_variable_set(rs, value); - else - error("cannot find/create CHART VARIABLE '%s' on host '%s', chart '%s'", name, host->hostname, st->id); - } - return PARSER_RC_OK; + return st; } - - -PARSER_RC pluginsd_dimension_action(void *user, RRDSET *st, char *id, char *name, char *algorithm, long multiplier, long divisor, char *options, - RRD_ALGORITHM algorithm_type) -{ - UNUSED(user); - UNUSED(algorithm); - - RRDDIM *rd = rrddim_add(st, id, name, multiplier, divisor, algorithm_type); - int unhide_dimension = 1; - - rrddim_flag_clear(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS); - if (options && *options) { - if (strstr(options, "obsolete") != NULL) - rrddim_is_obsolete(st, rd); - else - rrddim_isnot_obsolete(st, rd); - - unhide_dimension = !strstr(options, "hidden"); - - if (strstr(options, "noreset") != NULL) - rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS); - if (strstr(options, "nooverflow") != NULL) - rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS); - } else - rrddim_isnot_obsolete(st, rd); - - if (likely(unhide_dimension)) { - rrddim_flag_clear(rd, RRDDIM_FLAG_HIDDEN); - if (rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) { - (void)sql_set_dimension_option(&rd->metric_uuid, NULL); - rrddim_flag_clear(rd, RRDDIM_FLAG_META_HIDDEN); - } - } else { - rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN); - if (!rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) { - (void)sql_set_dimension_option(&rd->metric_uuid, "hidden"); - rrddim_flag_set(rd, RRDDIM_FLAG_META_HIDDEN); - } +static inline RRDDIM_ACQUIRED *pluginsd_acquire_dimension(RRDHOST *host, RRDSET *st, const char *dimension, const char *cmd) { + if (unlikely(!dimension || !*dimension)) { + error("PLUGINSD: 'host:%s/chart:%s' got a %s, without a dimension.", + rrdhost_hostname(host), rrdset_id(st), cmd); + return NULL; } - return PARSER_RC_OK; -} - -PARSER_RC pluginsd_label_action(void *user, char *key, char *value, RRDLABEL_SRC source) -{ - - if(unlikely(!((PARSER_USER_OBJECT *) user)->new_host_labels)) - ((PARSER_USER_OBJECT *) user)->new_host_labels = rrdlabels_create(); - rrdlabels_add(((PARSER_USER_OBJECT *)user)->new_host_labels, key, value, source); + RRDDIM_ACQUIRED *rda = rrddim_find_and_acquire(st, dimension); - return PARSER_RC_OK; -} + if (unlikely(!rda)) + error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a %s but dimension does not exist.", + rrdhost_hostname(host), rrdset_id(st), dimension, cmd); -PARSER_RC pluginsd_clabel_action(void *user, char *key, char *value, RRDLABEL_SRC source) -{ - if(unlikely(!((PARSER_USER_OBJECT *) user)->new_chart_labels)) - ((PARSER_USER_OBJECT *) user)->new_chart_labels = rrdlabels_create(); - - rrdlabels_add(((PARSER_USER_OBJECT *)user)->new_chart_labels, key, value, source); - - return PARSER_RC_OK; + return rda; } -PARSER_RC pluginsd_clabel_commit_action(void *user, RRDHOST *host, DICTIONARY *new_chart_labels) -{ - RRDSET *st = ((PARSER_USER_OBJECT *)user)->st; - if (unlikely(!st)) { - error("requested CLABEL_COMMIT on host '%s', without a BEGIN, ignoring it.", host->hostname); - return PARSER_RC_OK; +static inline RRDSET *pluginsd_find_chart(RRDHOST *host, const char *chart, const char *cmd) { + if (unlikely(!chart || !*chart)) { + error("PLUGINSD: 'host:%s' got a %s without a chart id.", + rrdhost_hostname(host), cmd); + return NULL; } - rrdset_update_rrdlabels(st, new_chart_labels); + RRDSET *st = rrdset_find(host, chart); + if (unlikely(!st)) + error("PLUGINSD: 'host:%s/chart:%s' got a %s but chart does not exist.", + rrdhost_hostname(host), chart, cmd); - return PARSER_RC_OK; + return st; } -PARSER_RC pluginsd_overwrite_action(void *user, RRDHOST *host, DICTIONARY *new_host_labels) -{ - UNUSED(user); - - if(!host->host_labels) - host->host_labels = rrdlabels_create(); - - rrdlabels_migrate_to_these(host->host_labels, new_host_labels); - sql_store_host_labels(host); - - return PARSER_RC_OK; +static inline PARSER_RC PLUGINSD_DISABLE_PLUGIN(void *user) { + ((PARSER_USER_OBJECT *) user)->enabled = 0; + return PARSER_RC_ERROR; } -PARSER_RC pluginsd_set(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_set(char **words, size_t num_words, void *user) { - char *dimension = words[1]; - char *value = words[2]; + char *dimension = get_word(words, num_words, 1); + char *value = get_word(words, num_words, 2); - RRDSET *st = ((PARSER_USER_OBJECT *) user)->st; - RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_SET); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); - if (unlikely(!dimension || !*dimension)) { - error("requested a SET on chart '%s' of host '%s', without a dimension. Disabling it.", st->id, host->hostname); - goto disable; - } + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_SET, PLUGINSD_KEYWORD_CHART); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); - if (unlikely(!value || !*value)) - value = NULL; + RRDDIM_ACQUIRED *rda = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_SET); + if(!rda) return PLUGINSD_DISABLE_PLUGIN(user); - if (unlikely(!st)) { - error( - "requested a SET on dimension %s with value %s on host '%s', without a BEGIN. Disabling it.", dimension, - value ? value : "", host->hostname); - goto disable; - } + RRDDIM *rd = rrddim_acquired_to_rrddim(rda); if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) - debug(D_PLUGINSD, "is setting dimension %s/%s to %s", st->id, dimension, value ? value : ""); - - if (value) { - RRDDIM *rd = rrddim_find(st, dimension); - if (unlikely(!rd)) { - error( - "requested a SET to dimension with id '%s' on stats '%s' (%s) on host '%s', which does not exist. Disabling it.", - dimension, st->name, st->id, st->rrdhost->hostname); - goto disable; - } else { - if (plugins_action->set_action) { - return plugins_action->set_action( - user, st, rd, strtoll(value, NULL, 0)); - } - } - } - return PARSER_RC_OK; + debug(D_PLUGINSD, "PLUGINSD: 'host:%s/chart:%s/dim:%s' SET is setting value to '%s'", + rrdhost_hostname(host), rrdset_id(st), dimension, value && *value ? value : "UNSET"); -disable: - ((PARSER_USER_OBJECT *) user)->enabled = 0; - return PARSER_RC_ERROR; + if (value && *value) + rrddim_set_by_pointer(st, rd, strtoll(value, NULL, 0)); + + rrddim_acquired_release(rda); + return PARSER_RC_OK; } -PARSER_RC pluginsd_begin(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_begin(char **words, size_t num_words, void *user) { - char *id = words[1]; - char *microseconds_txt = words[2]; + char *id = get_word(words, num_words, 1); + char *microseconds_txt = get_word(words, num_words, 2); - RRDSET *st = NULL; - RRDHOST *host = ((PARSER_USER_OBJECT *)user)->host; + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_BEGIN); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); - if (unlikely(!id)) { - error("requested a BEGIN without a chart id for host '%s'. Disabling it.", host->hostname); - goto disable; - } + RRDSET *st = pluginsd_find_chart(host, id, PLUGINSD_KEYWORD_BEGIN); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); - st = rrdset_find(host, id); - if (unlikely(!st)) { - error("requested a BEGIN on chart '%s', which does not exist on host '%s'. Disabling it.", id, host->hostname); - goto disable; - } ((PARSER_USER_OBJECT *)user)->st = st; usec_t microseconds = 0; if (microseconds_txt && *microseconds_txt) microseconds = str2ull(microseconds_txt); - if (plugins_action->begin_action) { - return plugins_action->begin_action(user, st, microseconds, - ((PARSER_USER_OBJECT *)user)->trust_durations); +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + if(st->replay.log_next_data_collection) { + st->replay.log_next_data_collection = false; + + internal_error(true, + "REPLAY: 'host:%s/chart:%s' first BEGIN after replication, last collected %llu, last updated %llu, microseconds %llu", + rrdhost_hostname(host), rrdset_id(st), + st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec, + st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec, + microseconds + ); + } +#endif + + if (likely(st->counter_done)) { + if (likely(microseconds)) { + if (((PARSER_USER_OBJECT *)user)->trust_durations) + rrdset_next_usec_unfiltered(st, microseconds); + else + rrdset_next_usec(st, microseconds); + } + else + rrdset_next(st); } return PARSER_RC_OK; -disable: - ((PARSER_USER_OBJECT *)user)->enabled = 0; - return PARSER_RC_ERROR; } -PARSER_RC pluginsd_end(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_end(char **words, size_t num_words, void *user) { UNUSED(words); - RRDSET *st = ((PARSER_USER_OBJECT *) user)->st; - RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; + UNUSED(num_words); - if (unlikely(!st)) { - error("requested an END, without a BEGIN on host '%s'. Disabling it.", host->hostname); - ((PARSER_USER_OBJECT *) user)->enabled = 0; - return PARSER_RC_ERROR; - } + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_END); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_END, PLUGINSD_KEYWORD_BEGIN); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) - debug(D_PLUGINSD, "requested an END on chart %s", st->id); + debug(D_PLUGINSD, "requested an END on chart '%s'", rrdset_id(st)); ((PARSER_USER_OBJECT *) user)->st = NULL; ((PARSER_USER_OBJECT *) user)->count++; - if (plugins_action->end_action) { - return plugins_action->end_action(user, st); - } + + struct timeval now; + now_realtime_timeval(&now); + rrdset_timed_done(st, now, /* pending_rrdset_next = */ false); + return PARSER_RC_OK; } -PARSER_RC pluginsd_chart(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_chart(char **words, size_t num_words, void *user) { - RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; - if (unlikely(!host && !((PARSER_USER_OBJECT *) user)->host_exists)) { - debug(D_PLUGINSD, "Ignoring chart belonging to missing or ignored host."); - return PARSER_RC_OK; - } - - char *type = words[1]; - char *name = words[2]; - char *title = words[3]; - char *units = words[4]; - char *family = words[5]; - char *context = words[6]; - char *chart = words[7]; - char *priority_s = words[8]; - char *update_every_s = words[9]; - char *options = words[10]; - char *plugin = words[11]; - char *module = words[12]; - - int have_action = ((plugins_action->chart_action) != NULL); + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CHART); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + char *type = get_word(words, num_words, 1); + char *name = get_word(words, num_words, 2); + char *title = get_word(words, num_words, 3); + char *units = get_word(words, num_words, 4); + char *family = get_word(words, num_words, 5); + char *context = get_word(words, num_words, 6); + char *chart = get_word(words, num_words, 7); + char *priority_s = get_word(words, num_words, 8); + char *update_every_s = get_word(words, num_words, 9); + char *options = get_word(words, num_words, 10); + char *plugin = get_word(words, num_words, 11); + char *module = get_word(words, num_words, 12); // parse the id from type char *id = NULL; @@ -342,10 +229,9 @@ PARSER_RC pluginsd_chart(char **words, void *user, PLUGINSD_ACTION *plugins_act // make sure we have the required variables if (unlikely((!type || !*type || !id || !*id))) { - if (likely(host)) - error("requested a CHART, without a type.id, on host '%s'. Disabling it.", host->hostname); - else - error("requested a CHART, without a type.id. Disabling it."); + error("PLUGINSD: 'host:%s' requested a CHART, without a type.id. Disabling it.", + rrdhost_hostname(host)); + ((PARSER_USER_OBJECT *) user)->enabled = 0; return PARSER_RC_ERROR; } @@ -396,42 +282,120 @@ PARSER_RC pluginsd_chart(char **words, void *user, PLUGINSD_ACTION *plugins_act type, id, name ? name : "", family ? family : "", context ? context : "", rrdset_type_name(chart_type), priority, update_every); - if (have_action) { - return plugins_action->chart_action( - user, type, id, name, family, context, title, units, - (plugin && *plugin) ? plugin : ((PARSER_USER_OBJECT *)user)->cd->filename, module, priority, update_every, - chart_type, options); + RRDSET *st = NULL; + + st = rrdset_create( + host, type, id, name, family, context, title, units, + (plugin && *plugin) ? plugin : ((PARSER_USER_OBJECT *)user)->cd->filename, + module, priority, update_every, + chart_type); + + if (likely(st)) { + if (options && *options) { + if (strstr(options, "obsolete")) + rrdset_is_obsolete(st); + else + rrdset_isnot_obsolete(st); + + if (strstr(options, "detail")) + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + else + rrdset_flag_clear(st, RRDSET_FLAG_DETAIL); + + if (strstr(options, "hidden")) + rrdset_flag_set(st, RRDSET_FLAG_HIDDEN); + else + rrdset_flag_clear(st, RRDSET_FLAG_HIDDEN); + + if (strstr(options, "store_first")) + rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST); + else + rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST); + } else { + rrdset_isnot_obsolete(st); + rrdset_flag_clear(st, RRDSET_FLAG_DETAIL); + rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST); + } } + ((PARSER_USER_OBJECT *)user)->st = st; return PARSER_RC_OK; } -PARSER_RC pluginsd_dimension(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_words, void *user) { - char *id = words[1]; - char *name = words[2]; - char *algorithm = words[3]; - char *multiplier_s = words[4]; - char *divisor_s = words[5]; - char *options = words[6]; - - RRDSET *st = ((PARSER_USER_OBJECT *) user)->st; - RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; - if (unlikely(!host && !((PARSER_USER_OBJECT *) user)->host_exists)) { - debug(D_PLUGINSD, "Ignoring dimension belonging to missing or ignored host."); - return PARSER_RC_OK; + const char *first_entry_txt = get_word(words, num_words, 1); + const char *last_entry_txt = get_word(words, num_words, 2); + const char *world_time_txt = get_word(words, num_words, 3); + + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CHART_DEFINITION_END); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_CHART_DEFINITION_END, PLUGINSD_KEYWORD_CHART); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); + + time_t first_entry_child = (first_entry_txt && *first_entry_txt) ? (time_t)str2ul(first_entry_txt) : 0; + time_t last_entry_child = (last_entry_txt && *last_entry_txt) ? (time_t)str2ul(last_entry_txt) : 0; + time_t child_world_time = (world_time_txt && *world_time_txt) ? (time_t)str2ul(world_time_txt) : now_realtime_sec(); + + if((first_entry_child != 0 || last_entry_child != 0) && (first_entry_child == 0 || last_entry_child == 0)) + error("PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_CHART_DEFINITION_END " with malformed timings (first time %ld, last time %ld, world time %ld).", + rrdhost_hostname(host), rrdset_id(st), + first_entry_child, last_entry_child, child_world_time); + + bool ok = true; + if(!rrdset_flag_check(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS)) { + +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + st->replay.start_streaming = false; + st->replay.after = 0; + st->replay.before = 0; +#endif + + rrdset_flag_set(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS); + rrdset_flag_clear(st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED); + rrdhost_receiver_replicating_charts_plus_one(st->rrdhost); + + PARSER *parser = ((PARSER_USER_OBJECT *)user)->parser; + ok = replicate_chart_request(send_to_plugin, parser, host, st, + first_entry_child, last_entry_child, child_world_time, + 0, 0); + } +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + else { + internal_error(true, "REPLAY: 'host:%s/chart:%s' not sending duplicate replication request", + rrdhost_hostname(st->rrdhost), rrdset_id(st)); } +#endif + + return ok ? PARSER_RC_OK : PARSER_RC_ERROR; +} + +PARSER_RC pluginsd_dimension(char **words, size_t num_words, void *user) +{ + char *id = get_word(words, num_words, 1); + char *name = get_word(words, num_words, 2); + char *algorithm = get_word(words, num_words, 3); + char *multiplier_s = get_word(words, num_words, 4); + char *divisor_s = get_word(words, num_words, 5); + char *options = get_word(words, num_words, 6); + + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_DIMENSION); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_DIMENSION, PLUGINSD_KEYWORD_CHART); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); if (unlikely(!id)) { - error( - "requested a DIMENSION, without an id, host '%s' and chart '%s'. Disabling it.", host->hostname, - st ? st->id : "UNSET"); - goto disable; + error("PLUGINSD: 'host:%s/chart:%s' got a DIMENSION, without an id. Disabling it.", + rrdhost_hostname(host), st ? rrdset_id(st) : "UNSET"); + return PLUGINSD_DISABLE_PLUGIN(user); } if (unlikely(!st && !((PARSER_USER_OBJECT *) user)->st_exists)) { - error("requested a DIMENSION, without a CHART, on host '%s'. Disabling it.", host->hostname); - goto disable; + error("PLUGINSD: 'host:%s' got a DIMENSION, without a CHART. Disabling it.", + rrdhost_hostname(host)); + return PLUGINSD_DISABLE_PLUGIN(user); } long multiplier = 1; @@ -455,316 +419,906 @@ PARSER_RC pluginsd_dimension(char **words, void *user, PLUGINSD_ACTION *plugins debug( D_PLUGINSD, "creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'", - st->id, id, name ? name : "", rrd_algorithm_name(rrd_algorithm_id(algorithm)), multiplier, divisor, + rrdset_id(st), id, name ? name : "", rrd_algorithm_name(rrd_algorithm_id(algorithm)), multiplier, divisor, options ? options : ""); - if (plugins_action->dimension_action) { - return plugins_action->dimension_action( - user, st, id, name, algorithm, - multiplier, divisor, (options && *options)?options:NULL, rrd_algorithm_id(algorithm)); + RRDDIM *rd = rrddim_add(st, id, name, multiplier, divisor, rrd_algorithm_id(algorithm)); + int unhide_dimension = 1; + + rrddim_option_clear(rd, RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS); + if (options && *options) { + if (strstr(options, "obsolete") != NULL) + rrddim_is_obsolete(st, rd); + else + rrddim_isnot_obsolete(st, rd); + + unhide_dimension = !strstr(options, "hidden"); + + if (strstr(options, "noreset") != NULL) + rrddim_option_set(rd, RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS); + if (strstr(options, "nooverflow") != NULL) + rrddim_option_set(rd, RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS); + } else + rrddim_isnot_obsolete(st, rd); + + if (likely(unhide_dimension)) { + rrddim_option_clear(rd, RRDDIM_OPTION_HIDDEN); + if (rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) { + rrddim_flag_clear(rd, RRDDIM_FLAG_META_HIDDEN); + metaqueue_dimension_update_flags(rd); + } + } + else { + rrddim_option_set(rd, RRDDIM_OPTION_HIDDEN); + if (!rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) { + rrddim_flag_set(rd, RRDDIM_FLAG_META_HIDDEN); + metaqueue_dimension_update_flags(rd); + } + } + + return PARSER_RC_OK; +} + +// ---------------------------------------------------------------------------- +// execution of functions + +struct inflight_function { + int code; + int timeout; + BUFFER *destination_wb; + STRING *function; + void (*callback)(BUFFER *wb, int code, void *callback_data); + void *callback_data; + usec_t timeout_ut; + usec_t started_ut; + usec_t sent_ut; +}; + +static void inflight_functions_insert_callback(const DICTIONARY_ITEM *item, void *func, void *parser_ptr) { + struct inflight_function *pf = func; + + PARSER *parser = parser_ptr; + + // leave this code as default, so that when the dictionary is destroyed this will be sent back to the caller + pf->code = HTTP_RESP_GATEWAY_TIMEOUT; + + char buffer[2048 + 1]; + snprintfz(buffer, 2048, "FUNCTION %s %d \"%s\"\n", + dictionary_acquired_item_name(item), + pf->timeout, + string2str(pf->function)); + + // send the command to the plugin + int ret = send_to_plugin(buffer, parser); + + pf->sent_ut = now_realtime_usec(); + + if(ret < 0) { + error("FUNCTION: failed to send function to plugin, error %d", ret); + rrd_call_function_error(pf->destination_wb, "Failed to communicate with collector", HTTP_RESP_BACKEND_FETCH_FAILED); + } + else { + internal_error(LOG_FUNCTIONS, + "FUNCTION '%s' with transaction '%s' sent to collector (%d bytes, in %llu usec)", + string2str(pf->function), dictionary_acquired_item_name(item), ret, + pf->sent_ut - pf->started_ut); + } +} + +static bool inflight_functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func __maybe_unused, void *new_func, void *parser_ptr __maybe_unused) { + struct inflight_function *pf = new_func; + + error("PLUGINSD_PARSER: duplicate UUID on pending function '%s' detected. Ignoring the second one.", string2str(pf->function)); + pf->code = rrd_call_function_error(pf->destination_wb, "This request is already in progress", HTTP_RESP_BAD_REQUEST); + pf->callback(pf->destination_wb, pf->code, pf->callback_data); + string_freez(pf->function); + + return false; +} + +static void inflight_functions_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func, void *parser_ptr __maybe_unused) { + struct inflight_function *pf = func; + + internal_error(LOG_FUNCTIONS, + "FUNCTION '%s' result of transaction '%s' received from collector (%zu bytes, request %llu usec, response %llu usec)", + string2str(pf->function), dictionary_acquired_item_name(item), + buffer_strlen(pf->destination_wb), pf->sent_ut - pf->started_ut, now_realtime_usec() - pf->sent_ut); + + pf->callback(pf->destination_wb, pf->code, pf->callback_data); + string_freez(pf->function); +} + +void inflight_functions_init(PARSER *parser) { + parser->inflight.functions = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + dictionary_register_insert_callback(parser->inflight.functions, inflight_functions_insert_callback, parser); + dictionary_register_delete_callback(parser->inflight.functions, inflight_functions_delete_callback, parser); + dictionary_register_conflict_callback(parser->inflight.functions, inflight_functions_conflict_callback, parser); +} + +static void inflight_functions_garbage_collect(PARSER *parser, usec_t now) { + parser->inflight.smaller_timeout = 0; + struct inflight_function *pf; + dfe_start_write(parser->inflight.functions, pf) { + if (pf->timeout_ut < now) { + internal_error(true, + "FUNCTION '%s' removing expired transaction '%s', after %llu usec.", + string2str(pf->function), pf_dfe.name, now - pf->started_ut); + + if(!buffer_strlen(pf->destination_wb) || pf->code == HTTP_RESP_OK) + pf->code = rrd_call_function_error(pf->destination_wb, + "Timeout waiting for collector response.", + HTTP_RESP_GATEWAY_TIMEOUT); + + dictionary_del(parser->inflight.functions, pf_dfe.name); + } + + else if(!parser->inflight.smaller_timeout || pf->timeout_ut < parser->inflight.smaller_timeout) + parser->inflight.smaller_timeout = pf->timeout_ut; + } + dfe_done(pf); +} + +// this is the function that is called from +// rrd_call_function_and_wait() and rrd_call_function_async() +static int pluginsd_execute_function_callback(BUFFER *destination_wb, int timeout, const char *function, void *collector_data, void (*callback)(BUFFER *wb, int code, void *callback_data), void *callback_data) { + PARSER *parser = collector_data; + + usec_t now = now_realtime_usec(); + + struct inflight_function tmp = { + .started_ut = now, + .timeout_ut = now + timeout * USEC_PER_SEC, + .destination_wb = destination_wb, + .timeout = timeout, + .function = string_strdupz(function), + .callback = callback, + .callback_data = callback_data, + }; + + uuid_t uuid; + uuid_generate_time(uuid); + + char key[UUID_STR_LEN]; + uuid_unparse_lower(uuid, key); + + dictionary_write_lock(parser->inflight.functions); + + // if there is any error, our dictionary callbacks will call the caller callback to notify + // the caller about the error - no need for error handling here. + dictionary_set(parser->inflight.functions, key, &tmp, sizeof(struct inflight_function)); + + if(!parser->inflight.smaller_timeout || tmp.timeout_ut < parser->inflight.smaller_timeout) + parser->inflight.smaller_timeout = tmp.timeout_ut; + + // garbage collect stale inflight functions + if(parser->inflight.smaller_timeout < now) + inflight_functions_garbage_collect(parser, now); + + dictionary_write_unlock(parser->inflight.functions); + + return HTTP_RESP_OK; +} + +PARSER_RC pluginsd_function(char **words, size_t num_words, void *user) +{ + bool global = false; + size_t i = 1; + if(num_words >= 2 && strcmp(get_word(words, num_words, 1), "GLOBAL") == 0) { + i++; + global = true; + } + + char *name = get_word(words, num_words, i++); + char *timeout_s = get_word(words, num_words, i++); + char *help = get_word(words, num_words, i++); + + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_FUNCTION); + if(!host) return PARSER_RC_ERROR; + + RRDSET *st = (global)?NULL:pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_FUNCTION, PLUGINSD_KEYWORD_CHART); + if(!st) global = true; + + if (unlikely(!timeout_s || !name || !help || (!global && !st))) { + error("PLUGINSD: 'host:%s/chart:%s' got a FUNCTION, without providing the required data (global = '%s', name = '%s', timeout = '%s', help = '%s'). Ignoring it.", + rrdhost_hostname(host), + st?rrdset_id(st):"(unset)", + global?"yes":"no", + name?name:"(unset)", + timeout_s?timeout_s:"(unset)", + help?help:"(unset)" + ); + return PARSER_RC_ERROR; + } + + int timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT; + if (timeout_s && *timeout_s) { + timeout = str2i(timeout_s); + if (unlikely(timeout <= 0)) + timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT; } + PARSER *parser = ((PARSER_USER_OBJECT *) user)->parser; + rrd_collector_add_function(host, st, name, timeout, help, false, pluginsd_execute_function_callback, parser); + + return PARSER_RC_OK; +} + +static void pluginsd_function_result_end(struct parser *parser, void *action_data) { + STRING *key = action_data; + if(key) + dictionary_del(parser->inflight.functions, string2str(key)); + string_freez(key); +} + +PARSER_RC pluginsd_function_result_begin(char **words, size_t num_words, void *user) +{ + char *key = get_word(words, num_words, 1); + char *status = get_word(words, num_words, 2); + char *format = get_word(words, num_words, 3); + char *expires = get_word(words, num_words, 4); + + if (unlikely(!key || !*key || !status || !*status || !format || !*format || !expires || !*expires)) { + error("got a " PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN " without providing the required data (key = '%s', status = '%s', format = '%s', expires = '%s')." + , key ? key : "(unset)" + , status ? status : "(unset)" + , format ? format : "(unset)" + , expires ? expires : "(unset)" + ); + } + + int code = (status && *status) ? str2i(status) : 0; + if (code <= 0) + code = HTTP_RESP_BACKEND_RESPONSE_INVALID; + + time_t expiration = (expires && *expires) ? str2l(expires) : 0; + + PARSER *parser = ((PARSER_USER_OBJECT *) user)->parser; + + struct inflight_function *pf = NULL; + + if(key && *key) + pf = (struct inflight_function *)dictionary_get(parser->inflight.functions, key); + + if(!pf) { + error("got a " PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN " for transaction '%s', but the transaction is not found.", key?key:"(unset)"); + } + else { + if(format && *format) + pf->destination_wb->contenttype = functions_format_to_content_type(format); + + pf->code = code; + + pf->destination_wb->expires = expiration; + if(expiration <= now_realtime_sec()) + buffer_no_cacheable(pf->destination_wb); + else + buffer_cacheable(pf->destination_wb); + } + + parser->defer.response = (pf) ? pf->destination_wb : NULL; + parser->defer.end_keyword = PLUGINSD_KEYWORD_FUNCTION_RESULT_END; + parser->defer.action = pluginsd_function_result_end; + parser->defer.action_data = string_strdupz(key); // it is ok is key is NULL + parser->flags |= PARSER_DEFER_UNTIL_KEYWORD; + return PARSER_RC_OK; -disable: - ((PARSER_USER_OBJECT *)user)->enabled = 0; - return PARSER_RC_ERROR; } -PARSER_RC pluginsd_variable(char **words, void *user, PLUGINSD_ACTION *plugins_action) +// ---------------------------------------------------------------------------- + +PARSER_RC pluginsd_variable(char **words, size_t num_words, void *user) { - char *name = words[1]; - char *value = words[2]; + char *name = get_word(words, num_words, 1); + char *value = get_word(words, num_words, 2); NETDATA_DOUBLE v; + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_VARIABLE); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + RRDSET *st = ((PARSER_USER_OBJECT *) user)->st; - RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; int global = (st) ? 0 : 1; if (name && *name) { if ((strcmp(name, "GLOBAL") == 0 || strcmp(name, "HOST") == 0)) { global = 1; - name = words[2]; - value = words[3]; + name = get_word(words, num_words, 2); + value = get_word(words, num_words, 3); } else if ((strcmp(name, "LOCAL") == 0 || strcmp(name, "CHART") == 0)) { global = 0; - name = words[2]; - value = words[3]; + name = get_word(words, num_words, 2); + value = get_word(words, num_words, 3); } } if (unlikely(!name || !*name)) { - error("requested a VARIABLE on host '%s', without a variable name. Disabling it.", host->hostname); + error("PLUGINSD: 'host:%s/chart:%s' got a VARIABLE without a variable name. Disabling it.", + rrdhost_hostname(host), st ? rrdset_id(st):"UNSET"); + ((PARSER_USER_OBJECT *)user)->enabled = 0; - return PARSER_RC_ERROR; + return PLUGINSD_DISABLE_PLUGIN(user); } if (unlikely(!value || !*value)) value = NULL; if (unlikely(!value)) { - error("cannot set %s VARIABLE '%s' on host '%s' to an empty value", (global) ? "HOST" : "CHART", name, - host->hostname); + error("PLUGINSD: 'host:%s/chart:%s' cannot set %s VARIABLE '%s' to an empty value", + rrdhost_hostname(host), + st ? rrdset_id(st):"UNSET", + (global) ? "HOST" : "CHART", + name); return PARSER_RC_OK; } if (!global && !st) { - error("cannot find/create CHART VARIABLE '%s' on host '%s' without a chart", name, host->hostname); - return PARSER_RC_OK; + error("PLUGINSD: 'host:%s/chart:%s' cannot update CHART VARIABLE '%s' without a chart", + rrdhost_hostname(host), + st ? rrdset_id(st):"UNSET", + name + ); + return PLUGINSD_DISABLE_PLUGIN(user); } char *endptr = NULL; v = (NETDATA_DOUBLE)str2ndd(value, &endptr); if (unlikely(endptr && *endptr)) { if (endptr == value) - error( - "the value '%s' of VARIABLE '%s' on host '%s' cannot be parsed as a number", value, name, - host->hostname); + error("PLUGINSD: 'host:%s/chart:%s' the value '%s' of VARIABLE '%s' cannot be parsed as a number", + rrdhost_hostname(host), + st ? rrdset_id(st):"UNSET", + value, + name); else - error( - "the value '%s' of VARIABLE '%s' on host '%s' has leftovers: '%s'", value, name, host->hostname, - endptr); + error("PLUGINSD: 'host:%s/chart:%s' the value '%s' of VARIABLE '%s' has leftovers: '%s'", + rrdhost_hostname(host), + st ? rrdset_id(st):"UNSET", + value, + name, + endptr); } - if (plugins_action->variable_action) { - return plugins_action->variable_action(user, host, st, name, global, v); + if (global) { + const RRDVAR_ACQUIRED *rva = rrdvar_custom_host_variable_add_and_acquire(host, name); + if (rva) { + rrdvar_custom_host_variable_set(host, rva, v); + rrdvar_custom_host_variable_release(host, rva); + } + else + error("PLUGINSD: 'host:%s' cannot find/create HOST VARIABLE '%s'", + rrdhost_hostname(host), + name); + } else { + const RRDSETVAR_ACQUIRED *rsa = rrdsetvar_custom_chart_variable_add_and_acquire(st, name); + if (rsa) { + rrdsetvar_custom_chart_variable_set(st, rsa, v); + rrdsetvar_custom_chart_variable_release(st, rsa); + } + else + error("PLUGINSD: 'host:%s/chart:%s' cannot find/create CHART VARIABLE '%s'", + rrdhost_hostname(host), rrdset_id(st), name); } return PARSER_RC_OK; } -PARSER_RC pluginsd_flush(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_flush(char **words __maybe_unused, size_t num_words __maybe_unused, void *user) { - UNUSED(words); debug(D_PLUGINSD, "requested a FLUSH"); - RRDSET *st = ((PARSER_USER_OBJECT *) user)->st; ((PARSER_USER_OBJECT *) user)->st = NULL; - if (plugins_action->flush_action) { - return plugins_action->flush_action(user, st); - } + ((PARSER_USER_OBJECT *) user)->replay.start_time = 0; + ((PARSER_USER_OBJECT *) user)->replay.end_time = 0; + ((PARSER_USER_OBJECT *) user)->replay.start_time_ut = 0; + ((PARSER_USER_OBJECT *) user)->replay.end_time_ut = 0; return PARSER_RC_OK; } -PARSER_RC pluginsd_disable(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_disable(char **words __maybe_unused, size_t num_words __maybe_unused, void *user __maybe_unused) { - UNUSED(user); - UNUSED(words); - - if (plugins_action->disable_action) { - return plugins_action->disable_action(user); - } + info("PLUGINSD: plugin called DISABLE. Disabling it."); + ((PARSER_USER_OBJECT *) user)->enabled = 0; return PARSER_RC_ERROR; } -PARSER_RC pluginsd_label(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_label(char **words, size_t num_words, void *user) { - char *store; + const char *name = get_word(words, num_words, 1); + const char *label_source = get_word(words, num_words, 2); + const char *value = get_word(words, num_words, 3); - if (!words[1] || !words[2] || !words[3]) { - error("Ignoring malformed or empty LABEL command."); - return PARSER_RC_OK; + if (!name || !label_source || !value) { + error("PLUGINSD: ignoring malformed or empty LABEL command."); + return PLUGINSD_DISABLE_PLUGIN(user); } - if (!words[4]) - store = words[3]; - else { - store = callocz(PLUGINSD_LINE_MAX + 1, sizeof(char)); + + char *store = (char *)value; + bool allocated_store = false; + + if(unlikely(num_words > 4)) { + allocated_store = true; + store = mallocz(PLUGINSD_LINE_MAX + 1); size_t remaining = PLUGINSD_LINE_MAX; char *move = store; - int i = 3; - while (i < PLUGINSD_MAX_WORDS) { - size_t length = strlen(words[i]); - if ((length + 1) >= remaining) - break; - - remaining -= (length + 1); - memcpy(move, words[i], length); - move += length; - *move++ = ' '; + char *word; + for(size_t i = 3; i < num_words && remaining > 2 && (word = get_word(words, num_words, i)) ;i++) { + if(i > 3) { + *move++ = ' '; + *move = '\0'; + remaining--; + } + + size_t length = strlen(word); + if (length > remaining) + length = remaining; - i++; - if (!words[i]) - break; + remaining -= length; + memcpy(move, word, length); + move += length; + *move = '\0'; } } - if (plugins_action->label_action) { - PARSER_RC rc = plugins_action->label_action(user, words[1], store, strtol(words[2], NULL, 10)); - if (store != words[3]) - freez(store); - return rc; - } + if(unlikely(!((PARSER_USER_OBJECT *) user)->new_host_labels)) + ((PARSER_USER_OBJECT *) user)->new_host_labels = rrdlabels_create(); + + rrdlabels_add(((PARSER_USER_OBJECT *)user)->new_host_labels, + name, + store, + str2l(label_source)); - if (store != words[3]) + if (allocated_store) freez(store); + + return PARSER_RC_OK; +} + +PARSER_RC pluginsd_overwrite(char **words __maybe_unused, size_t num_words __maybe_unused, void *user) +{ + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_OVERWRITE); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + debug(D_PLUGINSD, "requested to OVERWRITE host labels"); + + if(unlikely(!host->rrdlabels)) + host->rrdlabels = rrdlabels_create(); + + rrdlabels_migrate_to_these(host->rrdlabels, (DICTIONARY *) (((PARSER_USER_OBJECT *)user)->new_host_labels)); + metaqueue_store_host_labels(host->machine_guid); + + rrdlabels_destroy(((PARSER_USER_OBJECT *)user)->new_host_labels); + ((PARSER_USER_OBJECT *)user)->new_host_labels = NULL; return PARSER_RC_OK; } -PARSER_RC pluginsd_clabel(char **words, void *user, PLUGINSD_ACTION *plugins_action) + +PARSER_RC pluginsd_clabel(char **words, size_t num_words, void *user) { - if (!words[1] || !words[2] || !words[3]) { + const char *name = get_word(words, num_words, 1); + const char *value = get_word(words, num_words, 2); + const char *label_source = get_word(words, num_words, 3); + + if (!name || !value || !*label_source) { error("Ignoring malformed or empty CHART LABEL command."); - return PARSER_RC_OK; + return PLUGINSD_DISABLE_PLUGIN(user); } - if (plugins_action->clabel_action) { - PARSER_RC rc = plugins_action->clabel_action(user, words[1], words[2], strtol(words[3], NULL, 10)); - return rc; + if(unlikely(!((PARSER_USER_OBJECT *) user)->chart_rrdlabels_linked_temporarily)) { + ((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily = ((PARSER_USER_OBJECT *)user)->st->rrdlabels; + rrdlabels_unmark_all(((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily); } + rrdlabels_add(((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily, + name, value, str2l(label_source)); + return PARSER_RC_OK; } -PARSER_RC pluginsd_clabel_commit(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_clabel_commit(char **words __maybe_unused, size_t num_words __maybe_unused, void *user) { - UNUSED(words); + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CLABEL_COMMIT); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_CLABEL_COMMIT, PLUGINSD_KEYWORD_BEGIN); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); - RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; debug(D_PLUGINSD, "requested to commit chart labels"); - PARSER_RC rc = PARSER_RC_OK; + if(!((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily) { + error("PLUGINSD: 'host:%s' got CLABEL_COMMIT, without a CHART or BEGIN. Ignoring it.", + rrdhost_hostname(host)); + return PLUGINSD_DISABLE_PLUGIN(user); + } - if (plugins_action->clabel_commit_action) - rc = plugins_action->clabel_commit_action(user, host, ((PARSER_USER_OBJECT *)user)->new_chart_labels); + rrdlabels_remove_all_unmarked(((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily); - rrdlabels_destroy(((PARSER_USER_OBJECT *)user)->new_chart_labels); - ((PARSER_USER_OBJECT *)user)->new_chart_labels = NULL; + rrdset_flag_set(st, RRDSET_FLAG_METADATA_UPDATE); + rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_METADATA_UPDATE); - return rc; + ((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily = NULL; + return PARSER_RC_OK; } -PARSER_RC pluginsd_overwrite(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_replay_rrdset_begin(char **words, size_t num_words, void *user) { - UNUSED(words); + char *id = get_word(words, num_words, 1); + char *start_time_str = get_word(words, num_words, 2); + char *end_time_str = get_word(words, num_words, 3); + char *child_now_str = get_word(words, num_words, 4); - RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; - debug(D_PLUGINSD, "requested to OVERWRITE host labels"); + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_BEGIN); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); - PARSER_RC rc = PARSER_RC_OK; + RRDSET *st; + if (likely(!id || !*id)) + st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_BEGIN, PLUGINSD_KEYWORD_REPLAY_BEGIN); + else + st = pluginsd_find_chart(host, id, PLUGINSD_KEYWORD_REPLAY_BEGIN); - if (plugins_action->overwrite_action) - rc = plugins_action->overwrite_action(user, host, ((PARSER_USER_OBJECT *)user)->new_host_labels); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); + ((PARSER_USER_OBJECT *) user)->st = st; - rrdlabels_destroy(((PARSER_USER_OBJECT *)user)->new_host_labels); - ((PARSER_USER_OBJECT *)user)->new_host_labels = NULL; + if(start_time_str && end_time_str) { + time_t start_time = (time_t)str2ul(start_time_str); + time_t end_time = (time_t)str2ul(end_time_str); - return rc; -} + time_t wall_clock_time = 0, tolerance; + bool wall_clock_comes_from_child; (void)wall_clock_comes_from_child; + if(child_now_str) { + wall_clock_time = (time_t)str2ul(child_now_str); + tolerance = st->update_every + 1; + wall_clock_comes_from_child = true; + } -PARSER_RC pluginsd_guid(char **words, void *user, PLUGINSD_ACTION *plugins_action) -{ - char *uuid_str = words[1]; - uuid_t uuid; + if(wall_clock_time <= 0) { + wall_clock_time = now_realtime_sec(); + tolerance = st->update_every + 5; + wall_clock_comes_from_child = false; + } - if (unlikely(!uuid_str)) { - error("requested a GUID, without a uuid."); - return PARSER_RC_ERROR; - } - if (unlikely(strlen(uuid_str) != GUID_LEN || uuid_parse(uuid_str, uuid) == -1)) { - error("requested a GUID, without a valid uuid string."); - return PARSER_RC_ERROR; - } +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + internal_error( + (!st->replay.start_streaming && (end_time < st->replay.after || start_time > st->replay.before)), + "REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN " from %ld to %ld, which does not match our request (%ld to %ld).", + rrdhost_hostname(st->rrdhost), rrdset_id(st), start_time, end_time, st->replay.after, st->replay.before); + + internal_error( + true, + "REPLAY: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN " from %ld to %ld, child wall clock is %ld (%s), had requested %ld to %ld", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + start_time, end_time, wall_clock_time, wall_clock_comes_from_child ? "from child" : "parent time", + st->replay.after, st->replay.before); +#endif + + if(start_time && end_time && start_time < wall_clock_time + tolerance && end_time < wall_clock_time + tolerance && start_time < end_time) { + if (unlikely(end_time - start_time != st->update_every)) + rrdset_set_update_every(st, end_time - start_time); + + st->last_collected_time.tv_sec = end_time; + st->last_collected_time.tv_usec = 0; + + st->last_updated.tv_sec = end_time; + st->last_updated.tv_usec = 0; + + st->counter++; + st->counter_done++; + + // these are only needed for db mode RAM, SAVE, MAP, ALLOC + st->current_entry++; + if(st->current_entry >= st->entries) + st->current_entry -= st->entries; + + ((PARSER_USER_OBJECT *) user)->replay.start_time = start_time; + ((PARSER_USER_OBJECT *) user)->replay.end_time = end_time; + ((PARSER_USER_OBJECT *) user)->replay.start_time_ut = (usec_t) start_time * USEC_PER_SEC; + ((PARSER_USER_OBJECT *) user)->replay.end_time_ut = (usec_t) end_time * USEC_PER_SEC; + ((PARSER_USER_OBJECT *) user)->replay.wall_clock_time = wall_clock_time; + ((PARSER_USER_OBJECT *) user)->replay.rset_enabled = true; + + return PARSER_RC_OK; + } - debug(D_PLUGINSD, "Parsed uuid=%s", uuid_str); - if (plugins_action->guid_action) { - return plugins_action->guid_action(user, &uuid); + error("PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN " from %ld to %ld, but timestamps are invalid (now is %ld [%s], tolerance %ld). Ignoring " PLUGINSD_KEYWORD_REPLAY_SET, + rrdhost_hostname(st->rrdhost), rrdset_id(st), start_time, end_time, + wall_clock_time, wall_clock_comes_from_child ? "child wall clock" : "parent wall clock", tolerance); } + // the child sends an RBEGIN without any parameters initially + // setting rset_enabled to false, means the RSET should not store any metrics + // to store metrics, the RBEGIN needs to have timestamps + ((PARSER_USER_OBJECT *) user)->replay.start_time = 0; + ((PARSER_USER_OBJECT *) user)->replay.end_time = 0; + ((PARSER_USER_OBJECT *) user)->replay.start_time_ut = 0; + ((PARSER_USER_OBJECT *) user)->replay.end_time_ut = 0; + ((PARSER_USER_OBJECT *) user)->replay.wall_clock_time = 0; + ((PARSER_USER_OBJECT *) user)->replay.rset_enabled = false; return PARSER_RC_OK; } -PARSER_RC pluginsd_context(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_replay_set(char **words, size_t num_words, void *user) { - char *uuid_str = words[1]; - uuid_t uuid; + char *dimension = get_word(words, num_words, 1); + char *value_str = get_word(words, num_words, 2); + char *flags_str = get_word(words, num_words, 3); - if (unlikely(!uuid_str)) { - error("requested a CONTEXT, without a uuid."); - return PARSER_RC_ERROR; + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_SET); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_SET, PLUGINSD_KEYWORD_REPLAY_BEGIN); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); + + if(!((PARSER_USER_OBJECT *) user)->replay.rset_enabled) { + error_limit_static_thread_var(erl, 1, 0); + error_limit(&erl, "PLUGINSD: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_SET " but it is disabled by " PLUGINSD_KEYWORD_REPLAY_BEGIN " errors", + rrdhost_hostname(host), rrdset_id(st)); + + // we have to return OK here + return PARSER_RC_OK; } - if (unlikely(strlen(uuid_str) != GUID_LEN || uuid_parse(uuid_str, uuid) == -1)) { - error("requested a CONTEXT, without a valid uuid string."); - return PARSER_RC_ERROR; + + RRDDIM_ACQUIRED *rda = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_REPLAY_SET); + if(!rda) return PLUGINSD_DISABLE_PLUGIN(user); + + if (unlikely(!((PARSER_USER_OBJECT *) user)->replay.start_time || !((PARSER_USER_OBJECT *) user)->replay.end_time)) { + error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a " PLUGINSD_KEYWORD_REPLAY_SET " with invalid timestamps %ld to %ld from a " PLUGINSD_KEYWORD_REPLAY_BEGIN ". Disabling it.", + rrdhost_hostname(host), + rrdset_id(st), + dimension, + ((PARSER_USER_OBJECT *) user)->replay.start_time, + ((PARSER_USER_OBJECT *) user)->replay.end_time); + return PLUGINSD_DISABLE_PLUGIN(user); + } + + if (unlikely(!value_str || !*value_str)) + value_str = "NAN"; + + if(unlikely(!flags_str)) + flags_str = ""; + + if (likely(value_str)) { + RRDDIM *rd = rrddim_acquired_to_rrddim(rda); + + RRDDIM_FLAGS rd_flags = rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE | RRDDIM_FLAG_ARCHIVED); + + if(!(rd_flags & RRDDIM_FLAG_ARCHIVED)) { + NETDATA_DOUBLE value = strtondd(value_str, NULL); + SN_FLAGS flags = SN_FLAG_NONE; + + char c; + while ((c = *flags_str++)) { + switch (c) { + case 'R': + flags |= SN_FLAG_RESET; + break; + + case 'E': + flags |= SN_EMPTY_SLOT; + value = NAN; + break; + + default: + error("unknown flag '%c'", c); + break; + } + } + + if (!netdata_double_isnumber(value)) { + value = NAN; + flags = SN_EMPTY_SLOT; + } + + rrddim_store_metric(rd, ((PARSER_USER_OBJECT *) user)->replay.end_time_ut, value, flags); + rd->last_collected_time.tv_sec = ((PARSER_USER_OBJECT *) user)->replay.end_time; + rd->last_collected_time.tv_usec = 0; + rd->collections_counter++; + } + else { + error_limit_static_global_var(erl, 1, 0); + error_limit(&erl, "PLUGINSD: 'host:%s/chart:%s/dim:%s' has the ARCHIVED flag set, but it is replicated. Ignoring data.", + rrdhost_hostname(st->rrdhost), rrdset_id(st), rrddim_name(rd)); + } } - debug(D_PLUGINSD, "Parsed uuid=%s", uuid_str); - if (plugins_action->context_action) { - return plugins_action->context_action(user, &uuid); + rrddim_acquired_release(rda); + return PARSER_RC_OK; +} + +PARSER_RC pluginsd_replay_rrddim_collection_state(char **words, size_t num_words, void *user) +{ + char *dimension = get_word(words, num_words, 1); + char *last_collected_ut_str = get_word(words, num_words, 2); + char *last_collected_value_str = get_word(words, num_words, 3); + char *last_calculated_value_str = get_word(words, num_words, 4); + char *last_stored_value_str = get_word(words, num_words, 5); + + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE, PLUGINSD_KEYWORD_REPLAY_BEGIN); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDDIM_ACQUIRED *rda = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE); + if(!rda) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDDIM *rd = rrddim_acquired_to_rrddim(rda); + usec_t dim_last_collected_ut = (usec_t)rd->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)rd->last_collected_time.tv_usec; + usec_t last_collected_ut = last_collected_ut_str ? str2ull(last_collected_ut_str) : 0; + if(last_collected_ut > dim_last_collected_ut) { + rd->last_collected_time.tv_sec = last_collected_ut / USEC_PER_SEC; + rd->last_collected_time.tv_usec = last_collected_ut % USEC_PER_SEC; } + rd->last_collected_value = last_collected_value_str ? str2ll(last_collected_value_str, NULL) : 0; + rd->last_calculated_value = last_calculated_value_str ? str2ndd(last_calculated_value_str, NULL) : 0; + rd->last_stored_value = last_stored_value_str ? str2ndd(last_stored_value_str, NULL) : 0.0; + rrddim_acquired_release(rda); return PARSER_RC_OK; } -PARSER_RC pluginsd_tombstone(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words, void *user) { - char *uuid_str = words[1]; - uuid_t uuid; + char *last_collected_ut_str = get_word(words, num_words, 1); + char *last_updated_ut_str = get_word(words, num_words, 2); - if (unlikely(!uuid_str)) { - error("requested a TOMBSTONE, without a uuid."); - return PARSER_RC_ERROR; - } - if (unlikely(strlen(uuid_str) != GUID_LEN || uuid_parse(uuid_str, uuid) == -1)) { - error("requested a TOMBSTONE, without a valid uuid string."); - return PARSER_RC_ERROR; + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE, PLUGINSD_KEYWORD_REPLAY_BEGIN); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); + + usec_t chart_last_collected_ut = (usec_t)st->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)st->last_collected_time.tv_usec; + usec_t last_collected_ut = last_collected_ut_str ? str2ull(last_collected_ut_str) : 0; + if(last_collected_ut > chart_last_collected_ut) { + st->last_collected_time.tv_sec = last_collected_ut / USEC_PER_SEC; + st->last_collected_time.tv_usec = last_collected_ut % USEC_PER_SEC; } - debug(D_PLUGINSD, "Parsed uuid=%s", uuid_str); - if (plugins_action->tombstone_action) { - return plugins_action->tombstone_action(user, &uuid); + usec_t chart_last_updated_ut = (usec_t)st->last_updated.tv_sec * USEC_PER_SEC + (usec_t)st->last_updated.tv_usec; + usec_t last_updated_ut = last_updated_ut_str ? str2ull(last_updated_ut_str) : 0; + if(last_updated_ut > chart_last_updated_ut) { + st->last_updated.tv_sec = last_updated_ut / USEC_PER_SEC; + st->last_updated.tv_usec = last_updated_ut % USEC_PER_SEC; } + st->counter++; + st->counter_done++; + return PARSER_RC_OK; } -PARSER_RC metalog_pluginsd_host(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user) { - char *machine_guid = words[1]; - char *hostname = words[2]; - char *registry_hostname = words[3]; - char *update_every_s = words[4]; - char *os = words[5]; - char *timezone = words[6]; - char *tags = words[7]; - - int update_every = 1; - if (likely(update_every_s && *update_every_s)) - update_every = str2i(update_every_s); - if (unlikely(!update_every)) - update_every = 1; + if (num_words < 7) { // accepts 7, but the 7th is optional + error("REPLAY: malformed " PLUGINSD_KEYWORD_REPLAY_END " command"); + return PARSER_RC_ERROR; + } - debug(D_PLUGINSD, "HOST PARSED: guid=%s, hostname=%s, reg_host=%s, update=%d, os=%s, timezone=%s, tags=%s", - machine_guid, hostname, registry_hostname, update_every, os, timezone, tags); + const char *update_every_child_txt = get_word(words, num_words, 1); + const char *first_entry_child_txt = get_word(words, num_words, 2); + const char *last_entry_child_txt = get_word(words, num_words, 3); + const char *start_streaming_txt = get_word(words, num_words, 4); + const char *first_entry_requested_txt = get_word(words, num_words, 5); + const char *last_entry_requested_txt = get_word(words, num_words, 6); + const char *child_world_time_txt = get_word(words, num_words, 7); // optional - if (plugins_action->host_action) { - return plugins_action->host_action( - user, machine_guid, hostname, registry_hostname, update_every, os, timezone, tags); + time_t update_every_child = (time_t)str2ul(update_every_child_txt); + time_t first_entry_child = (time_t)str2ul(first_entry_child_txt); + time_t last_entry_child = (time_t)str2ul(last_entry_child_txt); + + bool start_streaming = (strcmp(start_streaming_txt, "true") == 0); + time_t first_entry_requested = (time_t)str2ul(first_entry_requested_txt); + time_t last_entry_requested = (time_t)str2ul(last_entry_requested_txt); + + // the optional child world time + time_t child_world_time = (child_world_time_txt && *child_world_time_txt) ? (time_t)str2ul(child_world_time_txt) : now_realtime_sec(); + + PARSER_USER_OBJECT *user_object = user; + + RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_END); + if(!host) return PLUGINSD_DISABLE_PLUGIN(user); + + RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_END, PLUGINSD_KEYWORD_REPLAY_BEGIN); + if(!st) return PLUGINSD_DISABLE_PLUGIN(user); + +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + internal_error(true, + "PLUGINSD REPLAY: 'host:%s/chart:%s': got a " PLUGINSD_KEYWORD_REPLAY_END " child db from %llu to %llu, start_streaming %s, had requested from %llu to %llu, wall clock %llu", + rrdhost_hostname(host), rrdset_id(st), + (unsigned long long)first_entry_child, (unsigned long long)last_entry_child, + start_streaming?"true":"false", + (unsigned long long)first_entry_requested, (unsigned long long)last_entry_requested, + (unsigned long long)child_world_time + ); +#endif + + ((PARSER_USER_OBJECT *) user)->st = NULL; + ((PARSER_USER_OBJECT *) user)->count++; + + if(((PARSER_USER_OBJECT *) user)->replay.rset_enabled && st->rrdhost->receiver) { + time_t now = now_realtime_sec(); + time_t started = st->rrdhost->receiver->replication_first_time_t; + time_t current = ((PARSER_USER_OBJECT *) user)->replay.end_time; + + worker_set_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION, + (NETDATA_DOUBLE)(current - started) * 100.0 / (NETDATA_DOUBLE)(now - started)); } - return PARSER_RC_OK; + ((PARSER_USER_OBJECT *) user)->replay.start_time = 0; + ((PARSER_USER_OBJECT *) user)->replay.end_time = 0; + ((PARSER_USER_OBJECT *) user)->replay.start_time_ut = 0; + ((PARSER_USER_OBJECT *) user)->replay.end_time_ut = 0; + ((PARSER_USER_OBJECT *) user)->replay.wall_clock_time = 0; + ((PARSER_USER_OBJECT *) user)->replay.rset_enabled = false; + + st->counter++; + st->counter_done++; + +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + st->replay.start_streaming = false; + st->replay.after = 0; + st->replay.before = 0; + if(start_streaming) + st->replay.log_next_data_collection = true; +#endif + + if (start_streaming) { + if (st->update_every != update_every_child) + rrdset_set_update_every(st, update_every_child); + + if(rrdset_flag_check(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS)) { + rrdset_flag_set(st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED); + rrdset_flag_clear(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS); + rrdset_flag_clear(st, RRDSET_FLAG_SYNC_CLOCK); + rrdhost_receiver_replicating_charts_minus_one(st->rrdhost); + } +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + else + internal_error(true, "REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_END " with enable_streaming = true, but there is no replication in progress for this chart.", + rrdhost_hostname(host), rrdset_id(st)); +#endif + worker_set_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION, 100.0); + + return PARSER_RC_OK; + } + + rrdcontext_updated_retention_rrdset(st); + + bool ok = replicate_chart_request(send_to_plugin, user_object->parser, host, st, + first_entry_child, last_entry_child, child_world_time, + first_entry_requested, last_entry_requested); + return ok ? PARSER_RC_OK : PARSER_RC_ERROR; } static void pluginsd_process_thread_cleanup(void *ptr) { PARSER *parser = (PARSER *)ptr; + rrd_collector_finished(); parser_destroy(parser); } // New plugins.d parser -inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations) +inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp_plugin_input, FILE *fp_plugin_output, int trust_durations) { int enabled = cd->enabled; - if (!fp || !enabled) { + if (!fp_plugin_input || !fp_plugin_output || !enabled) { cd->enabled = 0; return 0; } - if (unlikely(fileno(fp) == -1)) { - error("file descriptor given is not a valid stream"); + if (unlikely(fileno(fp_plugin_input) == -1)) { + error("input file descriptor given is not a valid stream"); cd->serial_failures++; return 0; } - clearerr(fp); + + if (unlikely(fileno(fp_plugin_output) == -1)) { + error("output file descriptor given is not a valid stream"); + cd->serial_failures++; + return 0; + } + + clearerr(fp_plugin_input); + clearerr(fp_plugin_output); PARSER_USER_OBJECT user = { .enabled = cd->enabled, @@ -773,25 +1327,15 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int .trust_durations = trust_durations }; - PARSER *parser = parser_init(host, &user, fp, PARSER_INPUT_SPLIT); + // fp_plugin_output = our input; fp_plugin_input = our output + PARSER *parser = parser_init(host, &user, fp_plugin_output, fp_plugin_input, -1, PARSER_INPUT_SPLIT, NULL); + + rrd_collector_started(); // this keeps the parser with its current value // so, parser needs to be allocated before pushing it netdata_thread_cleanup_push(pluginsd_process_thread_cleanup, parser); - parser->plugins_action->begin_action = &pluginsd_begin_action; - parser->plugins_action->flush_action = &pluginsd_flush_action; - parser->plugins_action->end_action = &pluginsd_end_action; - parser->plugins_action->disable_action = &pluginsd_disable_action; - parser->plugins_action->variable_action = &pluginsd_variable_action; - parser->plugins_action->dimension_action = &pluginsd_dimension_action; - parser->plugins_action->label_action = &pluginsd_label_action; - parser->plugins_action->overwrite_action = &pluginsd_overwrite_action; - parser->plugins_action->chart_action = &pluginsd_chart_action; - parser->plugins_action->set_action = &pluginsd_set_action; - parser->plugins_action->clabel_commit_action = &pluginsd_clabel_commit_action; - parser->plugins_action->clabel_action = &pluginsd_clabel_action; - user.parser = parser; while (likely(!parser_next(parser))) { diff --git a/collectors/plugins.d/pluginsd_parser.h b/collectors/plugins.d/pluginsd_parser.h index 924d48b7b..e18b43e58 100644 --- a/collectors/plugins.d/pluginsd_parser.h +++ b/collectors/plugins.d/pluginsd_parser.h @@ -5,7 +5,6 @@ #include "parser/parser.h" - typedef struct parser_user_object { PARSER *parser; RRDSET *st; @@ -14,29 +13,27 @@ typedef struct parser_user_object { struct plugind *cd; int trust_durations; DICTIONARY *new_host_labels; - DICTIONARY *new_chart_labels; + DICTIONARY *chart_rrdlabels_linked_temporarily; size_t count; int enabled; uint8_t st_exists; uint8_t host_exists; void *private; // the user can set this for private use -} PARSER_USER_OBJECT; -extern PARSER_RC pluginsd_set_action(void *user, RRDSET *st, RRDDIM *rd, long long int value); -extern PARSER_RC pluginsd_flush_action(void *user, RRDSET *st); -extern PARSER_RC pluginsd_begin_action(void *user, RRDSET *st, usec_t microseconds, int trust_durations); -extern PARSER_RC pluginsd_end_action(void *user, RRDSET *st); -extern PARSER_RC pluginsd_chart_action(void *user, char *type, char *id, char *name, char *family, char *context, - char *title, char *units, char *plugin, char *module, int priority, - int update_every, RRDSET_TYPE chart_type, char *options); -extern PARSER_RC pluginsd_disable_action(void *user); -extern PARSER_RC pluginsd_variable_action(void *user, RRDHOST *host, RRDSET *st, char *name, int global, NETDATA_DOUBLE value); -extern PARSER_RC pluginsd_dimension_action(void *user, RRDSET *st, char *id, char *name, char *algorithm, - long multiplier, long divisor, char *options, RRD_ALGORITHM algorithm_type); -extern PARSER_RC pluginsd_label_action(void *user, char *key, char *value, RRDLABEL_SRC source); -extern PARSER_RC pluginsd_overwrite_action(void *user, RRDHOST *host, DICTIONARY *new_host_labels); -extern PARSER_RC pluginsd_clabel_commit_action(void *user, RRDHOST *host, DICTIONARY *new_chart_labels); -extern PARSER_RC pluginsd_clabel_action(void *user, char *key, char *value, RRDLABEL_SRC source); + struct { + time_t start_time; + time_t end_time; + + usec_t start_time_ut; + usec_t end_time_ut; + time_t wall_clock_time; + + bool rset_enabled; + } replay; +} PARSER_USER_OBJECT; +PARSER_RC pluginsd_function(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_function_result_begin(char **words, size_t num_words, void *user); +void inflight_functions_init(PARSER *parser); #endif //NETDATA_PLUGINSD_PARSER_H diff --git a/collectors/proc.plugin/ipc.c b/collectors/proc.plugin/ipc.c index b5c9ae5e1..9185894eb 100644 --- a/collectors/proc.plugin/ipc.c +++ b/collectors/proc.plugin/ipc.c @@ -281,7 +281,7 @@ int do_ipc(int update_every, usec_t dt) { static int read_limits_next = -1; static struct ipc_limits limits; static struct ipc_status status; - static RRDVAR *arrays_max = NULL, *semaphores_max = NULL; + static const RRDVAR_ACQUIRED *arrays_max = NULL, *semaphores_max = NULL; static RRDSET *st_semaphores = NULL, *st_arrays = NULL; static RRDDIM *rd_semaphores = NULL, *rd_arrays = NULL; static char *msg_filename = NULL; @@ -352,8 +352,8 @@ int do_ipc(int update_every, usec_t dt) { } // variables - semaphores_max = rrdvar_custom_host_variable_create(localhost, "ipc_semaphores_max"); - arrays_max = rrdvar_custom_host_variable_create(localhost, "ipc_semaphores_arrays_max"); + semaphores_max = rrdvar_custom_host_variable_add_and_acquire(localhost, "ipc_semaphores_max"); + arrays_max = rrdvar_custom_host_variable_add_and_acquire(localhost, "ipc_semaphores_arrays_max"); } struct stat stbuf; @@ -390,17 +390,13 @@ int do_ipc(int update_every, usec_t dt) { return 0; } - if(st_semaphores->counter_done) rrdset_next(st_semaphores); rrddim_set_by_pointer(st_semaphores, rd_semaphores, status.semaem); rrdset_done(st_semaphores); - if(st_arrays->counter_done) rrdset_next(st_arrays); rrddim_set_by_pointer(st_arrays, rd_arrays, status.semusz); rrdset_done(st_arrays); } - // -------------------------------------------------------------------- - if(likely(do_msg != CONFIG_BOOLEAN_NO)) { static RRDSET *st_msq_messages = NULL, *st_msq_bytes = NULL; @@ -422,8 +418,6 @@ int do_ipc(int update_every, usec_t dt) { , update_every , RRDSET_TYPE_STACKED ); - else - rrdset_next(st_msq_messages); if(unlikely(!st_msq_bytes)) st_msq_bytes = rrdset_create_localhost( @@ -440,8 +434,6 @@ int do_ipc(int update_every, usec_t dt) { , update_every , RRDSET_TYPE_STACKED ); - else - rrdset_next(st_msq_bytes); struct message_queue *msq = message_queue_root, *msq_prev = NULL; while(likely(msq)){ @@ -483,11 +475,7 @@ int do_ipc(int update_every, usec_t dt) { rrdset_done(st_msq_messages); rrdset_done(st_msq_bytes); - long long dimensions_num = 0; - RRDDIM *rd; - rrdset_rdlock(st_msq_messages); - rrddim_foreach_read(rd, st_msq_messages) dimensions_num++; - rrdset_unlock(st_msq_messages); + long long dimensions_num = rrdset_number_of_dimensions(st_msq_messages); if(unlikely(dimensions_num > dimensions_limit)) { info("Message queue statistics has been disabled"); @@ -499,19 +487,17 @@ int do_ipc(int update_every, usec_t dt) { do_msg = CONFIG_BOOLEAN_NO; } else if(unlikely(!message_queue_root)) { - info("Making chart %s (%s) obsolete since it does not have any dimensions", st_msq_messages->name, st_msq_messages->id); + info("Making chart %s (%s) obsolete since it does not have any dimensions", rrdset_name(st_msq_messages), rrdset_id(st_msq_messages)); rrdset_is_obsolete(st_msq_messages); st_msq_messages = NULL; - info("Making chart %s (%s) obsolete since it does not have any dimensions", st_msq_bytes->name, st_msq_bytes->id); + info("Making chart %s (%s) obsolete since it does not have any dimensions", rrdset_name(st_msq_bytes), rrdset_id(st_msq_bytes)); rrdset_is_obsolete(st_msq_bytes); st_msq_bytes = NULL; } } } - // -------------------------------------------------------------------- - if(likely(do_shm != CONFIG_BOOLEAN_NO)) { static RRDSET *st_shm_segments = NULL, *st_shm_bytes = NULL; static RRDDIM *rd_shm_segments = NULL, *rd_shm_bytes = NULL; @@ -536,15 +522,10 @@ int do_ipc(int update_every, usec_t dt) { rd_shm_segments = rrddim_add(st_shm_segments, "segments", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_shm_segments); rrddim_set_by_pointer(st_shm_segments, rd_shm_segments, shm.segments); - rrdset_done(st_shm_segments); - // -------------------------------------------------------------------- - if(unlikely(!st_shm_bytes)) { st_shm_bytes = rrdset_create_localhost( "system" @@ -563,11 +544,8 @@ int do_ipc(int update_every, usec_t dt) { rd_shm_bytes = rrddim_add(st_shm_bytes, "bytes", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_shm_bytes); rrddim_set_by_pointer(st_shm_bytes, rd_shm_bytes, shm.bytes); - rrdset_done(st_shm_bytes); } } diff --git a/collectors/proc.plugin/plugin_proc.c b/collectors/proc.plugin/plugin_proc.c index 5033aa5e2..1b24df45f 100644 --- a/collectors/proc.plugin/plugin_proc.c +++ b/collectors/proc.plugin/plugin_proc.c @@ -40,11 +40,7 @@ static struct proc_module { {.name = "/proc/net/wireless", .dim = "netwireless", .func = do_proc_net_wireless}, {.name = "/proc/net/sockstat", .dim = "sockstat", .func = do_proc_net_sockstat}, {.name = "/proc/net/sockstat6", .dim = "sockstat6", .func = do_proc_net_sockstat6}, - {.name = "/proc/net/netstat", - .dim = "netstat", - .func = do_proc_net_netstat}, // this has to be before /proc/net/snmp, because there is a shared metric - {.name = "/proc/net/snmp", .dim = "snmp", .func = do_proc_net_snmp}, - {.name = "/proc/net/snmp6", .dim = "snmp6", .func = do_proc_net_snmp6}, + {.name = "/proc/net/netstat", .dim = "netstat", .func = do_proc_net_netstat}, {.name = "/proc/net/sctp/snmp", .dim = "sctp", .func = do_proc_net_sctp_snmp}, {.name = "/proc/net/softnet_stat", .dim = "softnet", .func = do_proc_net_softnet_stat}, {.name = "/proc/net/ip_vs/stats", .dim = "ipvs", .func = do_proc_net_ip_vs_stats}, diff --git a/collectors/proc.plugin/plugin_proc.h b/collectors/proc.plugin/plugin_proc.h index 8cb5431e5..d67ccd6e5 100644 --- a/collectors/proc.plugin/plugin_proc.h +++ b/collectors/proc.plugin/plugin_proc.h @@ -9,58 +9,55 @@ #define PLUGIN_PROC_NAME PLUGIN_PROC_CONFIG_NAME ".plugin" #define THREAD_NETDEV_NAME "PLUGIN[proc netdev]" -extern void *netdev_main(void *ptr); +void *netdev_main(void *ptr); -extern int do_proc_net_wireless(int update_every, usec_t dt); -extern int do_proc_diskstats(int update_every, usec_t dt); -extern int do_proc_mdstat(int update_every, usec_t dt); -extern int do_proc_net_snmp(int update_every, usec_t dt); -extern int do_proc_net_snmp6(int update_every, usec_t dt); -extern int do_proc_net_netstat(int update_every, usec_t dt); -extern int do_proc_net_stat_conntrack(int update_every, usec_t dt); -extern int do_proc_net_ip_vs_stats(int update_every, usec_t dt); -extern int do_proc_stat(int update_every, usec_t dt); -extern int do_proc_meminfo(int update_every, usec_t dt); -extern int do_proc_vmstat(int update_every, usec_t dt); -extern int do_proc_net_rpc_nfs(int update_every, usec_t dt); -extern int do_proc_net_rpc_nfsd(int update_every, usec_t dt); -extern int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt); -extern int do_proc_interrupts(int update_every, usec_t dt); -extern int do_proc_softirqs(int update_every, usec_t dt); -extern int do_proc_pressure(int update_every, usec_t dt); -extern int do_sys_kernel_mm_ksm(int update_every, usec_t dt); -extern int do_sys_block_zram(int update_every, usec_t dt); -extern int do_proc_loadavg(int update_every, usec_t dt); -extern int do_proc_net_stat_synproxy(int update_every, usec_t dt); -extern int do_proc_net_softnet_stat(int update_every, usec_t dt); -extern int do_proc_uptime(int update_every, usec_t dt); -extern int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt); -extern int do_proc_sys_devices_system_node(int update_every, usec_t dt); -extern int do_proc_spl_kstat_zfs_arcstats(int update_every, usec_t dt); -extern int do_proc_spl_kstat_zfs_pool_state(int update_every, usec_t dt); -extern int do_sys_fs_btrfs(int update_every, usec_t dt); -extern int do_proc_net_sockstat(int update_every, usec_t dt); -extern int do_proc_net_sockstat6(int update_every, usec_t dt); -extern int do_proc_net_sctp_snmp(int update_every, usec_t dt); -extern int do_ipc(int update_every, usec_t dt); -extern int do_sys_class_power_supply(int update_every, usec_t dt); -extern int do_proc_pagetypeinfo(int update_every, usec_t dt); -extern int do_sys_class_infiniband(int update_every, usec_t dt); -extern int get_numa_node_count(void); +int do_proc_net_wireless(int update_every, usec_t dt); +int do_proc_diskstats(int update_every, usec_t dt); +int do_proc_mdstat(int update_every, usec_t dt); +int do_proc_net_netstat(int update_every, usec_t dt); +int do_proc_net_stat_conntrack(int update_every, usec_t dt); +int do_proc_net_ip_vs_stats(int update_every, usec_t dt); +int do_proc_stat(int update_every, usec_t dt); +int do_proc_meminfo(int update_every, usec_t dt); +int do_proc_vmstat(int update_every, usec_t dt); +int do_proc_net_rpc_nfs(int update_every, usec_t dt); +int do_proc_net_rpc_nfsd(int update_every, usec_t dt); +int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt); +int do_proc_interrupts(int update_every, usec_t dt); +int do_proc_softirqs(int update_every, usec_t dt); +int do_proc_pressure(int update_every, usec_t dt); +int do_sys_kernel_mm_ksm(int update_every, usec_t dt); +int do_sys_block_zram(int update_every, usec_t dt); +int do_proc_loadavg(int update_every, usec_t dt); +int do_proc_net_stat_synproxy(int update_every, usec_t dt); +int do_proc_net_softnet_stat(int update_every, usec_t dt); +int do_proc_uptime(int update_every, usec_t dt); +int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt); +int do_proc_sys_devices_system_node(int update_every, usec_t dt); +int do_proc_spl_kstat_zfs_arcstats(int update_every, usec_t dt); +int do_proc_spl_kstat_zfs_pool_state(int update_every, usec_t dt); +int do_sys_fs_btrfs(int update_every, usec_t dt); +int do_proc_net_sockstat(int update_every, usec_t dt); +int do_proc_net_sockstat6(int update_every, usec_t dt); +int do_proc_net_sctp_snmp(int update_every, usec_t dt); +int do_ipc(int update_every, usec_t dt); +int do_sys_class_power_supply(int update_every, usec_t dt); +int do_proc_pagetypeinfo(int update_every, usec_t dt); +int do_sys_class_infiniband(int update_every, usec_t dt); +int get_numa_node_count(void); // metrics that need to be shared among data collectors -extern unsigned long long tcpext_TCPSynRetrans; extern unsigned long long zfs_arcstats_shrinkable_cache_size_bytes; // netdev renames -extern void netdev_rename_device_add( +void netdev_rename_device_add( const char *host_device, const char *container_device, const char *container_name, DICTIONARY *labels, const char *ctx_prefix); -extern void netdev_rename_device_del(const char *host_device); +void netdev_rename_device_del(const char *host_device); #include "proc_self_mountinfo.h" #include "proc_pressure.h" diff --git a/collectors/proc.plugin/proc_diskstats.c b/collectors/proc.plugin/proc_diskstats.c index be4a481cd..28d0e7584 100644 --- a/collectors/proc.plugin/proc_diskstats.c +++ b/collectors/proc.plugin/proc_diskstats.c @@ -25,6 +25,8 @@ static struct disk { char *mount_point; + char *chart_id; + // disk options caching int do_io; int do_ops; @@ -283,7 +285,7 @@ void bcache_read_priority_stats(struct disk *d, const char *family, int update_e if(unlikely(!d->st_bcache_cache_allocations)) { d->st_bcache_cache_allocations = rrdset_create_localhost( "disk_bcache_cache_alloc" - , d->device + , d->chart_id , d->disk , family , "disk.bcache_cache_alloc" @@ -304,7 +306,6 @@ void bcache_read_priority_stats(struct disk *d, const char *family, int update_e d->bcache_priority_stats_update_every_usec = update_every * USEC_PER_SEC; } - else rrdset_next(d->st_bcache_cache_allocations); rrddim_set_by_pointer(d->st_bcache_cache_allocations, d->rd_bcache_cache_allocations_unused, unused); rrddim_set_by_pointer(d->st_bcache_cache_allocations, d->rd_bcache_cache_allocations_dirty, dirty); @@ -609,6 +610,26 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis last->next = d; } + d->chart_id = strdupz(d->device); + + // read device uuid if it is an LVM volume + if (!strncmp(d->device, "dm-", 3)) { + char uuid_filename[FILENAME_MAX + 1]; + snprintfz(uuid_filename, FILENAME_MAX, path_to_sys_devices_virtual_block_device, disk); + strncat(uuid_filename, "/dm/uuid", FILENAME_MAX); + + char device_uuid[RRD_ID_LENGTH_MAX + 1]; + if (!read_file(uuid_filename, device_uuid, RRD_ID_LENGTH_MAX) && !strncmp(device_uuid, "LVM-", 4)) { + trim(device_uuid); + + char chart_id[RRD_ID_LENGTH_MAX + 1]; + snprintf(chart_id, RRD_ID_LENGTH_MAX, "%s-%s", d->device, device_uuid + 4); + + freez(d->chart_id); + d->chart_id = strdupz(chart_id); + } + } + char buffer[FILENAME_MAX + 1]; // find if it is a physical disk @@ -831,25 +852,25 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis } static void add_labels_to_disk(struct disk *d, RRDSET *st) { - rrdlabels_add(st->state->chart_labels, "device", d->disk, RRDLABEL_SRC_AUTO); - rrdlabels_add(st->state->chart_labels, "mount_point", d->mount_point, RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device", d->disk, RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "mount_point", d->mount_point, RRDLABEL_SRC_AUTO); switch (d->type) { default: case DISK_TYPE_UNKNOWN: - rrdlabels_add(st->state->chart_labels, "device_type", "unknown", RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device_type", "unknown", RRDLABEL_SRC_AUTO); break; case DISK_TYPE_PHYSICAL: - rrdlabels_add(st->state->chart_labels, "device_type", "physical", RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device_type", "physical", RRDLABEL_SRC_AUTO); break; case DISK_TYPE_PARTITION: - rrdlabels_add(st->state->chart_labels, "device_type", "partition", RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device_type", "partition", RRDLABEL_SRC_AUTO); break; case DISK_TYPE_VIRTUAL: - rrdlabels_add(st->state->chart_labels, "device_type", "virtual", RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device_type", "virtual", RRDLABEL_SRC_AUTO); break; } } @@ -1076,7 +1097,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_io)) { d->st_io = rrdset_create_localhost( RRD_TYPE_DISK - , d->device + , d->chart_id , d->disk , family , "disk.io" @@ -1094,20 +1115,17 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_io); } - else rrdset_next(d->st_io); last_readsectors = rrddim_set_by_pointer(d->st_io, d->rd_io_reads, readsectors); last_writesectors = rrddim_set_by_pointer(d->st_io, d->rd_io_writes, writesectors); rrdset_done(d->st_io); } - // -------------------------------------------------------------------- - if (do_dc_stats && d->do_io == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) { if (unlikely(!d->st_ext_io)) { d->st_ext_io = rrdset_create_localhost( "disk_ext" - , d->device + , d->chart_id , d->disk , family , "disk_ext.io" @@ -1123,15 +1141,12 @@ int do_proc_diskstats(int update_every, usec_t dt) { d->rd_io_discards = rrddim_add(d->st_ext_io, "discards", NULL, d->sector_size, 1024, RRD_ALGORITHM_INCREMENTAL); add_labels_to_disk(d, d->st_ext_io); - } else - rrdset_next(d->st_ext_io); + } last_discardsectors = rrddim_set_by_pointer(d->st_ext_io, d->rd_io_discards, discardsectors); rrdset_done(d->st_ext_io); } - // -------------------------------------------------------------------- - if(d->do_ops == CONFIG_BOOLEAN_YES || (d->do_ops == CONFIG_BOOLEAN_AUTO && (reads || writes || discards || flushes || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -1140,7 +1155,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_ops)) { d->st_ops = rrdset_create_localhost( "disk_ops" - , d->device + , d->chart_id , d->disk , family , "disk.ops" @@ -1160,20 +1175,17 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_ops); } - else rrdset_next(d->st_ops); last_reads = rrddim_set_by_pointer(d->st_ops, d->rd_ops_reads, reads); last_writes = rrddim_set_by_pointer(d->st_ops, d->rd_ops_writes, writes); rrdset_done(d->st_ops); } - // -------------------------------------------------------------------- - if (do_dc_stats && d->do_ops == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) { if (unlikely(!d->st_ext_ops)) { d->st_ext_ops = rrdset_create_localhost( "disk_ext_ops" - , d->device + , d->chart_id , d->disk , family , "disk_ext.ops" @@ -1194,8 +1206,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_ext_ops); } - else - rrdset_next(d->st_ext_ops); last_discards = rrddim_set_by_pointer(d->st_ext_ops, d->rd_ops_discards, discards); if (do_fl_stats) @@ -1203,8 +1213,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { rrdset_done(d->st_ext_ops); } - // -------------------------------------------------------------------- - if(d->do_qops == CONFIG_BOOLEAN_YES || (d->do_qops == CONFIG_BOOLEAN_AUTO && (queued_ios || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { d->do_qops = CONFIG_BOOLEAN_YES; @@ -1212,7 +1220,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_qops)) { d->st_qops = rrdset_create_localhost( "disk_qops" - , d->device + , d->chart_id , d->disk , family , "disk.qops" @@ -1231,14 +1239,11 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_qops); } - else rrdset_next(d->st_qops); rrddim_set_by_pointer(d->st_qops, d->rd_qops_operations, queued_ios); rrdset_done(d->st_qops); } - // -------------------------------------------------------------------- - if(d->do_backlog == CONFIG_BOOLEAN_YES || (d->do_backlog == CONFIG_BOOLEAN_AUTO && (backlog_ms || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { d->do_backlog = CONFIG_BOOLEAN_YES; @@ -1246,7 +1251,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_backlog)) { d->st_backlog = rrdset_create_localhost( "disk_backlog" - , d->device + , d->chart_id , d->disk , family , "disk.backlog" @@ -1265,14 +1270,11 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_backlog); } - else rrdset_next(d->st_backlog); rrddim_set_by_pointer(d->st_backlog, d->rd_backlog_backlog, backlog_ms); rrdset_done(d->st_backlog); } - // -------------------------------------------------------------------- - if(d->do_util == CONFIG_BOOLEAN_YES || (d->do_util == CONFIG_BOOLEAN_AUTO && (busy_ms || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { d->do_util = CONFIG_BOOLEAN_YES; @@ -1280,7 +1282,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_busy)) { d->st_busy = rrdset_create_localhost( "disk_busy" - , d->device + , d->chart_id , d->disk , family , "disk.busy" @@ -1299,17 +1301,14 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_busy); } - else rrdset_next(d->st_busy); last_busy_ms = rrddim_set_by_pointer(d->st_busy, d->rd_busy_busy, busy_ms); rrdset_done(d->st_busy); - // -------------------------------------------------------------------- - if(unlikely(!d->st_util)) { d->st_util = rrdset_create_localhost( "disk_util" - , d->device + , d->chart_id , d->disk , family , "disk.util" @@ -1328,7 +1327,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_util); } - else rrdset_next(d->st_util); collected_number disk_utilization = (busy_ms - last_busy_ms) / (10 * update_every); if (disk_utilization > 100) @@ -1338,8 +1336,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { rrdset_done(d->st_util); } - // -------------------------------------------------------------------- - if(d->do_mops == CONFIG_BOOLEAN_YES || (d->do_mops == CONFIG_BOOLEAN_AUTO && (mreads || mwrites || mdiscards || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -1348,7 +1344,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_mops)) { d->st_mops = rrdset_create_localhost( "disk_mops" - , d->device + , d->chart_id , d->disk , family , "disk.mops" @@ -1368,22 +1364,19 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_mops); } - else rrdset_next(d->st_mops); rrddim_set_by_pointer(d->st_mops, d->rd_mops_reads, mreads); rrddim_set_by_pointer(d->st_mops, d->rd_mops_writes, mwrites); rrdset_done(d->st_mops); } - // -------------------------------------------------------------------- - if(do_dc_stats && d->do_mops == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) { d->do_mops = CONFIG_BOOLEAN_YES; if(unlikely(!d->st_ext_mops)) { d->st_ext_mops = rrdset_create_localhost( "disk_ext_mops" - , d->device + , d->chart_id , d->disk , family , "disk_ext.mops" @@ -1402,15 +1395,11 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_ext_mops); } - else - rrdset_next(d->st_ext_mops); rrddim_set_by_pointer(d->st_ext_mops, d->rd_mops_discards, mdiscards); rrdset_done(d->st_ext_mops); } - // -------------------------------------------------------------------- - if(d->do_iotime == CONFIG_BOOLEAN_YES || (d->do_iotime == CONFIG_BOOLEAN_AUTO && (readms || writems || discardms || flushms || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { d->do_iotime = CONFIG_BOOLEAN_YES; @@ -1418,7 +1407,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_iotime)) { d->st_iotime = rrdset_create_localhost( "disk_iotime" - , d->device + , d->chart_id , d->disk , family , "disk.iotime" @@ -1438,20 +1427,17 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_iotime); } - else rrdset_next(d->st_iotime); last_readms = rrddim_set_by_pointer(d->st_iotime, d->rd_iotime_reads, readms); last_writems = rrddim_set_by_pointer(d->st_iotime, d->rd_iotime_writes, writems); rrdset_done(d->st_iotime); } - // -------------------------------------------------------------------- - if(do_dc_stats && d->do_iotime == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) { if(unlikely(!d->st_ext_iotime)) { d->st_ext_iotime = rrdset_create_localhost( "disk_ext_iotime" - , d->device + , d->chart_id , d->disk , family , "disk_ext.iotime" @@ -1472,8 +1458,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_ext_iotime); } - else - rrdset_next(d->st_ext_iotime); last_discardms = rrddim_set_by_pointer(d->st_ext_iotime, d->rd_iotime_discards, discardms); if (do_fl_stats) @@ -1481,7 +1465,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { rrdset_done(d->st_ext_iotime); } - // -------------------------------------------------------------------- // calculate differential charts // only if this is not the first time we run @@ -1496,7 +1479,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_await)) { d->st_await = rrdset_create_localhost( "disk_await" - , d->device + , d->chart_id , d->disk , family , "disk.await" @@ -1516,7 +1499,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_await); } - else rrdset_next(d->st_await); rrddim_set_by_pointer(d->st_await, d->rd_await_reads, (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0); rrddim_set_by_pointer(d->st_await, d->rd_await_writes, (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0); @@ -1527,7 +1509,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_ext_await)) { d->st_ext_await = rrdset_create_localhost( "disk_ext_await" - , d->device + , d->chart_id , d->disk , family , "disk_ext.await" @@ -1548,8 +1530,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_ext_await); } - else - rrdset_next(d->st_ext_await); rrddim_set_by_pointer( d->st_ext_await, d->rd_await_discards, @@ -1571,7 +1551,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_avgsz)) { d->st_avgsz = rrdset_create_localhost( "disk_avgsz" - , d->device + , d->chart_id , d->disk , family , "disk.avgsz" @@ -1591,7 +1571,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_avgsz); } - else rrdset_next(d->st_avgsz); rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_reads, (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0); rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_writes, (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0); @@ -1602,7 +1581,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_ext_avgsz)) { d->st_ext_avgsz = rrdset_create_localhost( "disk_ext_avgsz" - , d->device + , d->chart_id , d->disk , family , "disk_ext.avgsz" @@ -1621,8 +1600,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_ext_avgsz); } - else - rrdset_next(d->st_ext_avgsz); rrddim_set_by_pointer( d->st_ext_avgsz, d->rd_avgsz_discards, @@ -1641,7 +1618,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_svctm)) { d->st_svctm = rrdset_create_localhost( "disk_svctm" - , d->device + , d->chart_id , d->disk , family , "disk.svctm" @@ -1660,15 +1637,12 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_svctm); } - else - rrdset_next(d->st_svctm); rrddim_set_by_pointer(d->st_svctm, d->rd_svctm_svctm, ((reads - last_reads) + (writes - last_writes)) ? (busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) : 0); rrdset_done(d->st_svctm); } } - // -------------------------------------------------------------------------- // read bcache metrics and generate the bcache charts if(d->device_is_bcache && d->do_bcache != CONFIG_BOOLEAN_NO) { @@ -1749,7 +1723,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_bcache_hit_ratio)) { d->st_bcache_hit_ratio = rrdset_create_localhost( "disk_bcache_hit_ratio" - , d->device + , d->chart_id , d->disk , family , "disk.bcache_hit_ratio" @@ -1769,8 +1743,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_bcache_hit_ratio); } - else - rrdset_next(d->st_bcache_hit_ratio); rrddim_set_by_pointer(d->st_bcache_hit_ratio, d->rd_bcache_hit_ratio_5min, stats_five_minute_cache_hit_ratio); rrddim_set_by_pointer(d->st_bcache_hit_ratio, d->rd_bcache_hit_ratio_1hour, stats_hour_cache_hit_ratio); @@ -1784,7 +1756,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_bcache_rates)) { d->st_bcache_rates = rrdset_create_localhost( "disk_bcache_rates" - , d->device + , d->chart_id , d->disk , family , "disk.bcache_rates" @@ -1802,8 +1774,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_bcache_rates); } - else - rrdset_next(d->st_bcache_rates); rrddim_set_by_pointer(d->st_bcache_rates, d->rd_bcache_rate_writeback, writeback_rate); rrddim_set_by_pointer(d->st_bcache_rates, d->rd_bcache_rate_congested, cache_congested); @@ -1814,7 +1784,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_bcache_size)) { d->st_bcache_size = rrdset_create_localhost( "disk_bcache_size" - , d->device + , d->chart_id , d->disk , family , "disk.bcache_size" @@ -1831,8 +1801,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_bcache_size); } - else - rrdset_next(d->st_bcache_size); rrddim_set_by_pointer(d->st_bcache_size, d->rd_bcache_dirty_size, dirty_data); rrdset_done(d->st_bcache_size); @@ -1842,7 +1810,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_bcache_usage)) { d->st_bcache_usage = rrdset_create_localhost( "disk_bcache_usage" - , d->device + , d->chart_id , d->disk , family , "disk.bcache_usage" @@ -1859,8 +1827,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_bcache_usage); } - else - rrdset_next(d->st_bcache_usage); rrddim_set_by_pointer(d->st_bcache_usage, d->rd_bcache_available_percent, cache_available_percent); rrdset_done(d->st_bcache_usage); @@ -1871,7 +1837,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_bcache_cache_read_races)) { d->st_bcache_cache_read_races = rrdset_create_localhost( "disk_bcache_cache_read_races" - , d->device + , d->chart_id , d->disk , family , "disk.bcache_cache_read_races" @@ -1889,8 +1855,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_bcache_cache_read_races); } - else - rrdset_next(d->st_bcache_cache_read_races); rrddim_set_by_pointer(d->st_bcache_cache_read_races, d->rd_bcache_cache_read_races, cache_read_races); rrddim_set_by_pointer(d->st_bcache_cache_read_races, d->rd_bcache_cache_io_errors, cache_io_errors); @@ -1906,7 +1870,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_bcache)) { d->st_bcache = rrdset_create_localhost( "disk_bcache" - , d->device + , d->chart_id , d->disk , family , "disk.bcache" @@ -1928,8 +1892,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_bcache); } - else - rrdset_next(d->st_bcache); rrddim_set_by_pointer(d->st_bcache, d->rd_bcache_hits, stats_total_cache_hits); rrddim_set_by_pointer(d->st_bcache, d->rd_bcache_misses, stats_total_cache_misses); @@ -1946,7 +1908,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_bcache_bypass)) { d->st_bcache_bypass = rrdset_create_localhost( "disk_bcache_bypass" - , d->device + , d->chart_id , d->disk , family , "disk.bcache_bypass" @@ -1966,7 +1928,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { add_labels_to_disk(d, d->st_bcache_bypass); } - else rrdset_next(d->st_bcache_bypass); rrddim_set_by_pointer(d->st_bcache_bypass, d->rd_bcache_bypass_hits, stats_total_cache_bypass_hits); rrddim_set_by_pointer(d->st_bcache_bypass, d->rd_bcache_bypass_misses, stats_total_cache_bypass_misses); @@ -1975,8 +1936,6 @@ int do_proc_diskstats(int update_every, usec_t dt) { } } - - // ------------------------------------------------------------------------ // update the system total I/O if(global_do_io == CONFIG_BOOLEAN_YES || (global_do_io == CONFIG_BOOLEAN_AUTO && @@ -2004,15 +1963,12 @@ int do_proc_diskstats(int update_every, usec_t dt) { rd_in = rrddim_add(st_io, "in", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st_io, "out", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_io); rrddim_set_by_pointer(st_io, rd_in, system_read_kb); rrddim_set_by_pointer(st_io, rd_out, system_write_kb); rrdset_done(st_io); } - - // ------------------------------------------------------------------------ // cleanup removed disks struct disk *d = disk_root, *last = NULL; @@ -2021,12 +1977,19 @@ int do_proc_diskstats(int update_every, usec_t dt) { struct disk *t = d; rrdset_obsolete_and_pointer_null(d->st_avgsz); + rrdset_obsolete_and_pointer_null(d->st_ext_avgsz); rrdset_obsolete_and_pointer_null(d->st_await); + rrdset_obsolete_and_pointer_null(d->st_ext_await); rrdset_obsolete_and_pointer_null(d->st_backlog); + rrdset_obsolete_and_pointer_null(d->st_busy); rrdset_obsolete_and_pointer_null(d->st_io); + rrdset_obsolete_and_pointer_null(d->st_ext_io); rrdset_obsolete_and_pointer_null(d->st_iotime); + rrdset_obsolete_and_pointer_null(d->st_ext_iotime); rrdset_obsolete_and_pointer_null(d->st_mops); + rrdset_obsolete_and_pointer_null(d->st_ext_mops); rrdset_obsolete_and_pointer_null(d->st_ops); + rrdset_obsolete_and_pointer_null(d->st_ext_ops); rrdset_obsolete_and_pointer_null(d->st_qops); rrdset_obsolete_and_pointer_null(d->st_svctm); rrdset_obsolete_and_pointer_null(d->st_util); @@ -2036,6 +1999,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { rrdset_obsolete_and_pointer_null(d->st_bcache_size); rrdset_obsolete_and_pointer_null(d->st_bcache_usage); rrdset_obsolete_and_pointer_null(d->st_bcache_hit_ratio); + rrdset_obsolete_and_pointer_null(d->st_bcache_cache_allocations); + rrdset_obsolete_and_pointer_null(d->st_bcache_cache_read_races); if(d == disk_root) { disk_root = d = d->next; @@ -2066,6 +2031,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { freez(t->disk); freez(t->device); freez(t->mount_point); + freez(t->chart_id); freez(t); } else { diff --git a/collectors/proc.plugin/proc_interrupts.c b/collectors/proc.plugin/proc_interrupts.c index 46290554b..f87684758 100644 --- a/collectors/proc.plugin/proc_interrupts.c +++ b/collectors/proc.plugin/proc_interrupts.c @@ -146,8 +146,6 @@ int do_proc_interrupts(int update_every, usec_t dt) { irr->used = 1; } - // -------------------------------------------------------------------- - static RRDSET *st_system_interrupts = NULL; if(unlikely(!st_system_interrupts)) st_system_interrupts = rrdset_create_localhost( @@ -164,8 +162,6 @@ int do_proc_interrupts(int update_every, usec_t dt) { , update_every , RRDSET_TYPE_STACKED ); - else - rrdset_next(st_system_interrupts); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); @@ -173,9 +169,9 @@ int do_proc_interrupts(int update_every, usec_t dt) { // some interrupt may have changed without changing the total number of lines // if the same number of interrupts have been added and removed between two // calls of this function. - if(unlikely(!irr->rd || strncmp(irr->rd->name, irr->name, MAX_INTERRUPT_NAME) != 0)) { + if(unlikely(!irr->rd || strncmp(rrddim_name(irr->rd), irr->name, MAX_INTERRUPT_NAME) != 0)) { irr->rd = rrddim_add(st_system_interrupts, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_set_name(st_system_interrupts, irr->rd, irr->name); + rrddim_reset_name(st_system_interrupts, irr->rd, irr->name); // also reset per cpu RRDDIMs to avoid repeating strncmp() in the per core loop if(likely(do_per_core != CONFIG_BOOLEAN_NO)) { @@ -190,8 +186,6 @@ int do_proc_interrupts(int update_every, usec_t dt) { rrdset_done(st_system_interrupts); - // -------------------------------------------------------------------- - if(likely(do_per_core != CONFIG_BOOLEAN_NO)) { static RRDSET **core_st = NULL; static int old_cpus = 0; @@ -228,16 +222,15 @@ int do_proc_interrupts(int update_every, usec_t dt) { char core[50+1]; snprintfz(core, 50, "cpu%d", c); - rrdlabels_add(core_st[c]->state->chart_labels, "cpu", core, RRDLABEL_SRC_AUTO); + rrdlabels_add(core_st[c]->rrdlabels, "cpu", core, RRDLABEL_SRC_AUTO); } - else rrdset_next(core_st[c]); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); if(irr->used && (do_per_core == CONFIG_BOOLEAN_YES || irr->cpu[c].value)) { if(unlikely(!irr->cpu[c].rd)) { irr->cpu[c].rd = rrddim_add(core_st[c], irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_set_name(core_st[c], irr->cpu[c].rd, irr->name); + rrddim_reset_name(core_st[c], irr->cpu[c].rd, irr->name); } rrddim_set_by_pointer(core_st[c], irr->cpu[c].rd, irr->cpu[c].value); diff --git a/collectors/proc.plugin/proc_loadavg.c b/collectors/proc.plugin/proc_loadavg.c index 8b78ecc9e..d928c8617 100644 --- a/collectors/proc.plugin/proc_loadavg.c +++ b/collectors/proc.plugin/proc_loadavg.c @@ -52,9 +52,6 @@ int do_proc_loadavg(int update_every, usec_t dt) { // //unsigned long long next_pid = str2ull(procfile_lineword(ff, 0, 5)); - - // -------------------------------------------------------------------- - if(next_loadavg_dt <= dt) { if(likely(do_loadavg)) { static RRDSET *load_chart = NULL; @@ -80,8 +77,6 @@ int do_proc_loadavg(int update_every, usec_t dt) { rd_load5 = rrddim_add(load_chart, "load5", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); rd_load15 = rrddim_add(load_chart, "load15", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(load_chart); rrddim_set_by_pointer(load_chart, rd_load1, (collected_number) (load1 * 1000)); rrddim_set_by_pointer(load_chart, rd_load5, (collected_number) (load5 * 1000)); @@ -90,16 +85,17 @@ int do_proc_loadavg(int update_every, usec_t dt) { next_loadavg_dt = load_chart->update_every * USEC_PER_SEC; } - else next_loadavg_dt = MIN_LOADAVG_UPDATE_EVERY * USEC_PER_SEC; + else + next_loadavg_dt = MIN_LOADAVG_UPDATE_EVERY * USEC_PER_SEC; } - else next_loadavg_dt -= dt; + else + next_loadavg_dt -= dt; - // -------------------------------------------------------------------- if(likely(do_all_processes)) { static RRDSET *processes_chart = NULL; static RRDDIM *rd_active = NULL; - static RRDSETVAR *rd_pidmax; + static const RRDSETVAR_ACQUIRED *rd_pidmax; if(unlikely(!processes_chart)) { processes_chart = rrdset_create_localhost( @@ -118,12 +114,11 @@ int do_proc_loadavg(int update_every, usec_t dt) { ); rd_active = rrddim_add(processes_chart, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - rd_pidmax = rrdsetvar_custom_chart_variable_create(processes_chart, "pidmax"); + rd_pidmax = rrdsetvar_custom_chart_variable_add_and_acquire(processes_chart, "pidmax"); } - else rrdset_next(processes_chart); rrddim_set_by_pointer(processes_chart, rd_active, active_processes); - rrdsetvar_custom_chart_variable_set(rd_pidmax, max_processes); + rrdsetvar_custom_chart_variable_set(processes_chart, rd_pidmax, max_processes); rrdset_done(processes_chart); } diff --git a/collectors/proc.plugin/proc_mdstat.c b/collectors/proc.plugin/proc_mdstat.c index c8015827e..63e0c68eb 100644 --- a/collectors/proc.plugin/proc_mdstat.c +++ b/collectors/proc.plugin/proc_mdstat.c @@ -78,8 +78,8 @@ static inline void make_chart_obsolete(char *name, const char *id_modifier) } static void add_labels_to_mdstat(struct raid *raid, RRDSET *st) { - rrdlabels_add(st->state->chart_labels, "device", raid->name, RRDLABEL_SRC_AUTO); - rrdlabels_add(st->state->chart_labels, "raid_level", raid->level, RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device", raid->name, RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "raid_level", raid->level, RRDLABEL_SRC_AUTO); } int do_proc_mdstat(int update_every, usec_t dt) @@ -392,8 +392,6 @@ int do_proc_mdstat(int update_every, usec_t dt) } } - // -------------------------------------------------------------------- - if (likely(do_health && redundant_num)) { static RRDSET *st_mdstat_health = NULL; if (unlikely(!st_mdstat_health)) { @@ -413,8 +411,6 @@ int do_proc_mdstat(int update_every, usec_t dt) rrdset_isnot_obsolete(st_mdstat_health); } - else - rrdset_next(st_mdstat_health); if (!redundant_num) { if (likely(make_charts_obsolete)) @@ -435,8 +431,6 @@ int do_proc_mdstat(int update_every, usec_t dt) } } - // -------------------------------------------------------------------- - for (raid_idx = 0; raid_idx < raids_num; raid_idx++) { struct raid *raid = &raids[raid_idx]; char id[50 + 1]; @@ -467,8 +461,6 @@ int do_proc_mdstat(int update_every, usec_t dt) add_labels_to_mdstat(raid, raid->st_disks); } - else - rrdset_next(raid->st_disks); if (unlikely(!raid->rd_inuse && !(raid->rd_inuse = rrddim_find_active(raid->st_disks, "inuse")))) raid->rd_inuse = rrddim_add(raid->st_disks, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); @@ -477,12 +469,9 @@ int do_proc_mdstat(int update_every, usec_t dt) rrddim_set_by_pointer(raid->st_disks, raid->rd_inuse, raid->inuse_disks); rrddim_set_by_pointer(raid->st_disks, raid->rd_down, raid->failed_disks); - rrdset_done(raid->st_disks); } - // -------------------------------------------------------------------- - if (likely(do_mismatch)) { snprintfz(id, 50, "%s_mismatch", raid->name); @@ -507,19 +496,14 @@ int do_proc_mdstat(int update_every, usec_t dt) add_labels_to_mdstat(raid, raid->st_mismatch_cnt); } - else - rrdset_next(raid->st_mismatch_cnt); if (unlikely(!raid->rd_mismatch_cnt && !(raid->rd_mismatch_cnt = rrddim_find_active(raid->st_mismatch_cnt, "count")))) raid->rd_mismatch_cnt = rrddim_add(raid->st_mismatch_cnt, "count", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_set_by_pointer(raid->st_mismatch_cnt, raid->rd_mismatch_cnt, raid->mismatch_cnt); - rrdset_done(raid->st_mismatch_cnt); } - // -------------------------------------------------------------------- - if (likely(do_operations)) { snprintfz(id, 50, "%s_operation", raid->name); @@ -544,8 +528,6 @@ int do_proc_mdstat(int update_every, usec_t dt) add_labels_to_mdstat(raid, raid->st_operation); } - else - rrdset_next(raid->st_operation); if(unlikely(!raid->rd_check && !(raid->rd_check = rrddim_find_active(raid->st_operation, "check")))) raid->rd_check = rrddim_add(raid->st_operation, "check", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); @@ -560,13 +542,9 @@ int do_proc_mdstat(int update_every, usec_t dt) rrddim_set_by_pointer(raid->st_operation, raid->rd_resync, raid->resync); rrddim_set_by_pointer(raid->st_operation, raid->rd_recovery, raid->recovery); rrddim_set_by_pointer(raid->st_operation, raid->rd_reshape, raid->reshape); - rrdset_done(raid->st_operation); - // -------------------------------------------------------------------- - snprintfz(id, 50, "%s_finish", raid->name); - if (unlikely(!raid->st_finish && !(raid->st_finish = rrdset_find_active_byname_localhost(id)))) { snprintfz(family, 50, "%s (%s)", raid->name, raid->level); @@ -587,20 +565,14 @@ int do_proc_mdstat(int update_every, usec_t dt) add_labels_to_mdstat(raid, raid->st_finish); } - else - rrdset_next(raid->st_finish); if(unlikely(!raid->rd_finish_in && !(raid->rd_finish_in = rrddim_find_active(raid->st_finish, "finish_in")))) raid->rd_finish_in = rrddim_add(raid->st_finish, "finish_in", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_set_by_pointer(raid->st_finish, raid->rd_finish_in, raid->finish_in); - rrdset_done(raid->st_finish); - // -------------------------------------------------------------------- - snprintfz(id, 50, "%s_speed", raid->name); - if (unlikely(!raid->st_speed && !(raid->st_speed = rrdset_find_active_byname_localhost(id)))) { snprintfz(family, 50, "%s (%s)", raid->name, raid->level); @@ -622,19 +594,14 @@ int do_proc_mdstat(int update_every, usec_t dt) add_labels_to_mdstat(raid, raid->st_speed); } - else - rrdset_next(raid->st_speed); if (unlikely(!raid->rd_speed && !(raid->rd_speed = rrddim_find_active(raid->st_speed, "speed")))) raid->rd_speed = rrddim_add(raid->st_speed, "speed", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_set_by_pointer(raid->st_speed, raid->rd_speed, raid->speed); - rrdset_done(raid->st_speed); } } else { - // -------------------------------------------------------------------- - if (likely(do_nonredundant)) { snprintfz(id, 50, "%s_availability", raid->name); @@ -659,14 +626,11 @@ int do_proc_mdstat(int update_every, usec_t dt) add_labels_to_mdstat(raid, raid->st_nonredundant); } - else - rrdset_next(raid->st_nonredundant); if (unlikely(!raid->rd_nonredundant && !(raid->rd_nonredundant = rrddim_find_active(raid->st_nonredundant, "available")))) raid->rd_nonredundant = rrddim_add(raid->st_nonredundant, "available", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_set_by_pointer(raid->st_nonredundant, raid->rd_nonredundant, 1); - rrdset_done(raid->st_nonredundant); } } diff --git a/collectors/proc.plugin/proc_meminfo.c b/collectors/proc.plugin/proc_meminfo.c index f89ddd8d4..2f390c653 100644 --- a/collectors/proc.plugin/proc_meminfo.c +++ b/collectors/proc.plugin/proc_meminfo.c @@ -154,8 +154,6 @@ int do_proc_meminfo(int update_every, usec_t dt) { if (first_ff_read) first_ff_read = 0; - // -------------------------------------------------------------------- - // http://calimeroteknik.free.fr/blag/?article20/really-used-memory-on-gnu-linux unsigned long long MemCached = Cached + SReclaimable - Shmem; unsigned long long MemUsed = MemTotal - MemFree - MemCached - Buffers; @@ -190,13 +188,11 @@ int do_proc_meminfo(int update_every, usec_t dt) { rd_cached = rrddim_add(st_system_ram, "cached", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rd_buffers = rrddim_add(st_system_ram, "buffers", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_system_ram); rrddim_set_by_pointer(st_system_ram, rd_free, MemFree); rrddim_set_by_pointer(st_system_ram, rd_used, MemUsed); rrddim_set_by_pointer(st_system_ram, rd_cached, MemCached); rrddim_set_by_pointer(st_system_ram, rd_buffers, Buffers); - rrdset_done(st_system_ram); } @@ -222,16 +218,12 @@ int do_proc_meminfo(int update_every, usec_t dt) { rd_avail = rrddim_add(st_mem_available, "MemAvailable", "avail", 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_mem_available); rrddim_set_by_pointer(st_mem_available, rd_avail, MemAvailable); - rrdset_done(st_mem_available); } } - // -------------------------------------------------------------------- - unsigned long long SwapUsed = SwapTotal - SwapFree; if(do_swap == CONFIG_BOOLEAN_YES || (do_swap == CONFIG_BOOLEAN_AUTO && @@ -263,16 +255,12 @@ int do_proc_meminfo(int update_every, usec_t dt) { rd_free = rrddim_add(st_system_swap, "free", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rd_used = rrddim_add(st_system_swap, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_system_swap); rrddim_set_by_pointer(st_system_swap, rd_used, SwapUsed); rrddim_set_by_pointer(st_system_swap, rd_free, SwapFree); - rrdset_done(st_system_swap); } - // -------------------------------------------------------------------- - if(arl_hwcorrupted->flags & ARL_ENTRY_FLAG_FOUND && (do_hwcorrupt == CONFIG_BOOLEAN_YES || (do_hwcorrupt == CONFIG_BOOLEAN_AUTO && (HardwareCorrupted > 0 || @@ -302,15 +290,11 @@ int do_proc_meminfo(int update_every, usec_t dt) { rd_corrupted = rrddim_add(st_mem_hwcorrupt, "HardwareCorrupted", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_mem_hwcorrupt); rrddim_set_by_pointer(st_mem_hwcorrupt, rd_corrupted, HardwareCorrupted); - rrdset_done(st_mem_hwcorrupt); } - // -------------------------------------------------------------------- - if(do_committed) { static RRDSET *st_mem_committed = NULL; static RRDDIM *rd_committed = NULL; @@ -335,15 +319,11 @@ int do_proc_meminfo(int update_every, usec_t dt) { rd_committed = rrddim_add(st_mem_committed, "Committed_AS", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_mem_committed); rrddim_set_by_pointer(st_mem_committed, rd_committed, Committed_AS); - rrdset_done(st_mem_committed); } - // -------------------------------------------------------------------- - if(do_writeback) { static RRDSET *st_mem_writeback = NULL; static RRDDIM *rd_dirty = NULL, *rd_writeback = NULL, *rd_fusewriteback = NULL, *rd_nfs_writeback = NULL, *rd_bounce = NULL; @@ -371,14 +351,12 @@ int do_proc_meminfo(int update_every, usec_t dt) { rd_nfs_writeback = rrddim_add(st_mem_writeback, "NfsWriteback", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rd_bounce = rrddim_add(st_mem_writeback, "Bounce", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_mem_writeback); rrddim_set_by_pointer(st_mem_writeback, rd_dirty, Dirty); rrddim_set_by_pointer(st_mem_writeback, rd_writeback, Writeback); rrddim_set_by_pointer(st_mem_writeback, rd_fusewriteback, WritebackTmp); rrddim_set_by_pointer(st_mem_writeback, rd_nfs_writeback, NFS_Unstable); rrddim_set_by_pointer(st_mem_writeback, rd_bounce, Bounce); - rrdset_done(st_mem_writeback); } @@ -414,7 +392,6 @@ int do_proc_meminfo(int update_every, usec_t dt) { if (do_percpu) rd_percpu = rrddim_add(st_mem_kernel, "Percpu", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_mem_kernel); rrddim_set_by_pointer(st_mem_kernel, rd_slab, Slab); rrddim_set_by_pointer(st_mem_kernel, rd_kernelstack, KernelStack); @@ -426,8 +403,6 @@ int do_proc_meminfo(int update_every, usec_t dt) { rrdset_done(st_mem_kernel); } - // -------------------------------------------------------------------- - if(do_slab) { static RRDSET *st_mem_slab = NULL; static RRDDIM *rd_reclaimable = NULL, *rd_unreclaimable = NULL; @@ -453,16 +428,12 @@ int do_proc_meminfo(int update_every, usec_t dt) { rd_reclaimable = rrddim_add(st_mem_slab, "reclaimable", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rd_unreclaimable = rrddim_add(st_mem_slab, "unreclaimable", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_mem_slab); rrddim_set_by_pointer(st_mem_slab, rd_reclaimable, SReclaimable); rrddim_set_by_pointer(st_mem_slab, rd_unreclaimable, SUnreclaim); - rrdset_done(st_mem_slab); } - // -------------------------------------------------------------------- - if(do_hugepages == CONFIG_BOOLEAN_YES || (do_hugepages == CONFIG_BOOLEAN_AUTO && ((Hugepagesize && HugePages_Total) || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { @@ -494,18 +465,14 @@ int do_proc_meminfo(int update_every, usec_t dt) { rd_surp = rrddim_add(st_mem_hugepages, "surplus", NULL, Hugepagesize, 1024, RRD_ALGORITHM_ABSOLUTE); rd_rsvd = rrddim_add(st_mem_hugepages, "reserved", NULL, Hugepagesize, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_mem_hugepages); rrddim_set_by_pointer(st_mem_hugepages, rd_used, HugePages_Total - HugePages_Free - HugePages_Rsvd); rrddim_set_by_pointer(st_mem_hugepages, rd_free, HugePages_Free); rrddim_set_by_pointer(st_mem_hugepages, rd_rsvd, HugePages_Rsvd); rrddim_set_by_pointer(st_mem_hugepages, rd_surp, HugePages_Surp); - rrdset_done(st_mem_hugepages); } - // -------------------------------------------------------------------- - if(do_transparent_hugepages == CONFIG_BOOLEAN_YES || (do_transparent_hugepages == CONFIG_BOOLEAN_AUTO && (AnonHugePages || ShmemHugePages || @@ -536,11 +503,9 @@ int do_proc_meminfo(int update_every, usec_t dt) { rd_anonymous = rrddim_add(st_mem_transparent_hugepages, "anonymous", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rd_shared = rrddim_add(st_mem_transparent_hugepages, "shmem", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_mem_transparent_hugepages); rrddim_set_by_pointer(st_mem_transparent_hugepages, rd_anonymous, AnonHugePages); rrddim_set_by_pointer(st_mem_transparent_hugepages, rd_shared, ShmemHugePages); - rrdset_done(st_mem_transparent_hugepages); } diff --git a/collectors/proc.plugin/proc_net_dev.c b/collectors/proc.plugin/proc_net_dev.c index 79572f442..e124f631f 100644 --- a/collectors/proc.plugin/proc_net_dev.c +++ b/collectors/proc.plugin/proc_net_dev.c @@ -7,6 +7,8 @@ #define STATE_LENGTH_MAX 32 +#define READ_RETRY_PERIOD 60 // seconds + enum { NETDEV_DUPLEX_UNKNOWN, NETDEV_DUPLEX_HALF, @@ -55,6 +57,15 @@ static struct netdev { int configured; int enabled; int updated; + + int carrier_file_exists; + time_t carrier_file_lost_time; + + int duplex_file_exists; + time_t duplex_file_lost_time; + + int speed_file_exists; + time_t speed_file_lost_time; int do_bandwidth; int do_packets; @@ -188,7 +199,7 @@ static struct netdev { RRDDIM *rd_mtu; char *filename_speed; - RRDSETVAR *chart_var_speed; + const RRDSETVAR_ACQUIRED *chart_var_speed; char *filename_duplex; char *filename_operstate; @@ -859,21 +870,37 @@ int do_proc_net_dev(int update_every, usec_t dt) { if ((d->do_carrier != CONFIG_BOOLEAN_NO || d->do_duplex != CONFIG_BOOLEAN_NO || d->do_speed != CONFIG_BOOLEAN_NO) && - d->filename_carrier) { + d->filename_carrier && + (d->carrier_file_exists || + now_monotonic_sec() - d->carrier_file_lost_time > READ_RETRY_PERIOD)) { if (read_single_number_file(d->filename_carrier, &d->carrier)) { - error("Cannot refresh interface %s carrier state by reading '%s'. Stop updating it.", d->name, d->filename_carrier); - freez(d->filename_carrier); - d->filename_carrier = NULL; + if (d->carrier_file_exists) + error( + "Cannot refresh interface %s carrier state by reading '%s'. Next update is in %d seconds.", + d->name, + d->filename_carrier, + READ_RETRY_PERIOD); + d->carrier_file_exists = 0; + d->carrier_file_lost_time = now_monotonic_sec(); + } else { + d->carrier_file_exists = 1; + d->carrier_file_lost_time = 0; } } - if (d->do_duplex != CONFIG_BOOLEAN_NO && d->filename_duplex && (d->carrier || !d->filename_carrier)) { + if (d->do_duplex != CONFIG_BOOLEAN_NO && + d->filename_duplex && + (d->carrier || d->carrier_file_exists) && + (d->duplex_file_exists || + now_monotonic_sec() - d->duplex_file_lost_time > READ_RETRY_PERIOD)) { char buffer[STATE_LENGTH_MAX + 1]; if (read_file(d->filename_duplex, buffer, STATE_LENGTH_MAX)) { - error("Cannot refresh interface %s duplex state by reading '%s'. I will stop updating it.", d->name, d->filename_duplex); - freez(d->filename_duplex); - d->filename_duplex = NULL; + if (d->duplex_file_exists) + error("Cannot refresh interface %s duplex state by reading '%s'.", d->name, d->filename_duplex); + d->duplex_file_exists = 0; + d->duplex_file_lost_time = now_monotonic_sec(); + d->duplex = NETDEV_DUPLEX_UNKNOWN; } else { // values can be unknown, half or full -- just check the first letter for speed if (buffer[0] == 'f') @@ -882,9 +909,11 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->duplex = NETDEV_DUPLEX_HALF; else d->duplex = NETDEV_DUPLEX_UNKNOWN; + d->duplex_file_exists = 1; + d->duplex_file_lost_time = 0; } } else { - d->duplex = 0; + d->duplex = NETDEV_DUPLEX_UNKNOWN; } if(d->do_operstate != CONFIG_BOOLEAN_NO && d->filename_operstate) { @@ -904,7 +933,8 @@ int do_proc_net_dev(int update_every, usec_t dt) { if (d->do_mtu != CONFIG_BOOLEAN_NO && d->filename_mtu) { if (read_single_number_file(d->filename_mtu, &d->mtu)) { - error("Cannot refresh mtu for interface %s by reading '%s'. Stop updating it.", d->name, d->filename_mtu); + error( + "Cannot refresh mtu for interface %s by reading '%s'. Stop updating it.", d->name, d->filename_mtu); freez(d->filename_mtu); d->filename_mtu = NULL; } @@ -921,8 +951,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { // , d->rframe, d->tcollisions, d->tcarrier // ); - // -------------------------------------------------------------------- - if(unlikely(d->do_bandwidth == CONFIG_BOOLEAN_AUTO && (d->rbytes || d->tbytes || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) d->do_bandwidth = CONFIG_BOOLEAN_YES; @@ -958,7 +986,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_tbytes = td; } } - else rrdset_next(d->st_bandwidth); rrddim_set_by_pointer(d->st_bandwidth, d->rd_rbytes, (collected_number)d->rbytes); rrddim_set_by_pointer(d->st_bandwidth, d->rd_tbytes, (collected_number)d->tbytes); @@ -967,31 +994,34 @@ int do_proc_net_dev(int update_every, usec_t dt) { // update the interface speed if(d->filename_speed) { if(unlikely(!d->chart_var_speed)) { - d->chart_var_speed = rrdsetvar_custom_chart_variable_create(d->st_bandwidth, "nic_speed_max"); + d->chart_var_speed = + rrdsetvar_custom_chart_variable_add_and_acquire(d->st_bandwidth, "nic_speed_max"); if(!d->chart_var_speed) { - error("Cannot create interface %s chart variable 'nic_speed_max'. Will not update its speed anymore.", d->name); + error( + "Cannot create interface %s chart variable 'nic_speed_max'. Will not update its speed anymore.", + d->name); freez(d->filename_speed); d->filename_speed = NULL; } } - if(d->filename_speed && d->chart_var_speed) { + if (d->filename_speed && d->chart_var_speed) { int ret = 0; - if (d->carrier || !d->filename_carrier) { + if ((d->carrier || d->carrier_file_exists) && + (d->speed_file_exists || now_monotonic_sec() - d->speed_file_lost_time > READ_RETRY_PERIOD)) { ret = read_single_number_file(d->filename_speed, (unsigned long long *) &d->speed); } else { - d->speed = 0; + d->speed = 0; // TODO: this is wrong, shouldn't use 0 value, but NULL. } if(ret) { - error("Cannot refresh interface %s speed by reading '%s'. Will not update its speed anymore.", d->name, d->filename_speed); - freez(d->filename_speed); - d->filename_speed = NULL; + if (d->speed_file_exists) + error("Cannot refresh interface %s speed by reading '%s'.", d->name, d->filename_speed); + d->speed_file_exists = 0; + d->speed_file_lost_time = now_monotonic_sec(); } else { - rrdsetvar_custom_chart_variable_set(d->chart_var_speed, (NETDATA_DOUBLE) d->speed * KILOBITS_IN_A_MEGABIT); - if(d->do_speed != CONFIG_BOOLEAN_NO) { if(unlikely(!d->st_speed)) { d->st_speed = rrdset_create_localhost( @@ -1015,18 +1045,23 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_speed = rrddim_add(d->st_speed, "speed", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(d->st_speed); rrddim_set_by_pointer(d->st_speed, d->rd_speed, (collected_number)d->speed * KILOBITS_IN_A_MEGABIT); rrdset_done(d->st_speed); } + + rrdsetvar_custom_chart_variable_set( + d->st_bandwidth, d->chart_var_speed, (NETDATA_DOUBLE)d->speed * KILOBITS_IN_A_MEGABIT); + + if (d->speed) { + d->speed_file_exists = 1; + d->speed_file_lost_time = 0; + } } } } } - // -------------------------------------------------------------------- - if(d->do_duplex != CONFIG_BOOLEAN_NO && d->filename_duplex) { if(unlikely(!d->st_duplex)) { d->st_duplex = rrdset_create_localhost( @@ -1052,7 +1087,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_duplex_half = rrddim_add(d->st_duplex, "half", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); d->rd_duplex_unknown = rrddim_add(d->st_duplex, "unknown", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(d->st_duplex); rrddim_set_by_pointer(d->st_duplex, d->rd_duplex_full, (collected_number)(d->duplex == NETDEV_DUPLEX_FULL)); rrddim_set_by_pointer(d->st_duplex, d->rd_duplex_half, (collected_number)(d->duplex == NETDEV_DUPLEX_HALF)); @@ -1060,8 +1094,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { rrdset_done(d->st_duplex); } - // -------------------------------------------------------------------- - if(d->do_operstate != CONFIG_BOOLEAN_NO && d->filename_operstate) { if(unlikely(!d->st_operstate)) { d->st_operstate = rrdset_create_localhost( @@ -1091,7 +1123,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_operstate_dormant = rrddim_add(d->st_operstate, "dormant", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); d->rd_operstate_unknown = rrddim_add(d->st_operstate, "unknown", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(d->st_operstate); rrddim_set_by_pointer(d->st_operstate, d->rd_operstate_up, (collected_number)(d->operstate == NETDEV_OPERSTATE_UP)); rrddim_set_by_pointer(d->st_operstate, d->rd_operstate_down, (collected_number)(d->operstate == NETDEV_OPERSTATE_DOWN)); @@ -1103,9 +1134,7 @@ int do_proc_net_dev(int update_every, usec_t dt) { rrdset_done(d->st_operstate); } - // -------------------------------------------------------------------- - - if(d->do_carrier != CONFIG_BOOLEAN_NO && d->filename_carrier) { + if(d->do_carrier != CONFIG_BOOLEAN_NO && d->carrier_file_exists) { if(unlikely(!d->st_carrier)) { d->st_carrier = rrdset_create_localhost( d->chart_type_net_carrier @@ -1129,15 +1158,12 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_carrier_up = rrddim_add(d->st_carrier, "up", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); d->rd_carrier_down = rrddim_add(d->st_carrier, "down", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(d->st_carrier); rrddim_set_by_pointer(d->st_carrier, d->rd_carrier_up, (collected_number)(d->carrier == 1)); rrddim_set_by_pointer(d->st_carrier, d->rd_carrier_down, (collected_number)(d->carrier != 1)); rrdset_done(d->st_carrier); } - // -------------------------------------------------------------------- - if(d->do_mtu != CONFIG_BOOLEAN_NO && d->filename_mtu) { if(unlikely(!d->st_mtu)) { d->st_mtu = rrdset_create_localhost( @@ -1161,14 +1187,11 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_mtu = rrddim_add(d->st_mtu, "mtu", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(d->st_mtu); rrddim_set_by_pointer(d->st_mtu, d->rd_mtu, (collected_number)d->mtu); rrdset_done(d->st_mtu); } - // -------------------------------------------------------------------- - if(unlikely(d->do_packets == CONFIG_BOOLEAN_AUTO && (d->rpackets || d->tpackets || d->rmulticast || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) d->do_packets = CONFIG_BOOLEAN_YES; @@ -1207,7 +1230,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_tpackets = td; } } - else rrdset_next(d->st_packets); rrddim_set_by_pointer(d->st_packets, d->rd_rpackets, (collected_number)d->rpackets); rrddim_set_by_pointer(d->st_packets, d->rd_tpackets, (collected_number)d->tpackets); @@ -1215,8 +1237,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { rrdset_done(d->st_packets); } - // -------------------------------------------------------------------- - if(unlikely(d->do_errors == CONFIG_BOOLEAN_AUTO && (d->rerrors || d->terrors || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) d->do_errors = CONFIG_BOOLEAN_YES; @@ -1254,15 +1274,12 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_terrors = td; } } - else rrdset_next(d->st_errors); rrddim_set_by_pointer(d->st_errors, d->rd_rerrors, (collected_number)d->rerrors); rrddim_set_by_pointer(d->st_errors, d->rd_terrors, (collected_number)d->terrors); rrdset_done(d->st_errors); } - // -------------------------------------------------------------------- - if(unlikely(d->do_drops == CONFIG_BOOLEAN_AUTO && (d->rdrops || d->tdrops || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) d->do_drops = CONFIG_BOOLEAN_YES; @@ -1300,15 +1317,12 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_tdrops = td; } } - else rrdset_next(d->st_drops); rrddim_set_by_pointer(d->st_drops, d->rd_rdrops, (collected_number)d->rdrops); rrddim_set_by_pointer(d->st_drops, d->rd_tdrops, (collected_number)d->tdrops); rrdset_done(d->st_drops); } - // -------------------------------------------------------------------- - if(unlikely(d->do_fifo == CONFIG_BOOLEAN_AUTO && (d->rfifo || d->tfifo || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) d->do_fifo = CONFIG_BOOLEAN_YES; @@ -1346,15 +1360,12 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_tfifo = td; } } - else rrdset_next(d->st_fifo); rrddim_set_by_pointer(d->st_fifo, d->rd_rfifo, (collected_number)d->rfifo); rrddim_set_by_pointer(d->st_fifo, d->rd_tfifo, (collected_number)d->tfifo); rrdset_done(d->st_fifo); } - // -------------------------------------------------------------------- - if(unlikely(d->do_compressed == CONFIG_BOOLEAN_AUTO && (d->rcompressed || d->tcompressed || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) d->do_compressed = CONFIG_BOOLEAN_YES; @@ -1392,15 +1403,12 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_tcompressed = td; } } - else rrdset_next(d->st_compressed); rrddim_set_by_pointer(d->st_compressed, d->rd_rcompressed, (collected_number)d->rcompressed); rrddim_set_by_pointer(d->st_compressed, d->rd_tcompressed, (collected_number)d->tcompressed); rrdset_done(d->st_compressed); } - // -------------------------------------------------------------------- - if(unlikely(d->do_events == CONFIG_BOOLEAN_AUTO && (d->rframe || d->tcollisions || d->tcarrier || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) d->do_events = CONFIG_BOOLEAN_YES; @@ -1431,7 +1439,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { d->rd_tcollisions = rrddim_add(d->st_events, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); d->rd_tcarrier = rrddim_add(d->st_events, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(d->st_events); rrddim_set_by_pointer(d->st_events, d->rd_rframe, (collected_number)d->rframe); rrddim_set_by_pointer(d->st_events, d->rd_tcollisions, (collected_number)d->tcollisions); @@ -1466,8 +1473,6 @@ int do_proc_net_dev(int update_every, usec_t dt) { rd_in = rrddim_add(st_system_net, "InOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st_system_net, "OutOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_system_net); rrddim_set_by_pointer(st_system_net, rd_in, (collected_number)system_rbytes); rrddim_set_by_pointer(st_system_net, rd_out, (collected_number)system_tbytes); diff --git a/collectors/proc.plugin/proc_net_ip_vs_stats.c b/collectors/proc.plugin/proc_net_ip_vs_stats.c index 43dcf2a88..2b9c9332e 100644 --- a/collectors/proc.plugin/proc_net_ip_vs_stats.c +++ b/collectors/proc.plugin/proc_net_ip_vs_stats.c @@ -39,9 +39,6 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { InBytes = strtoull(procfile_lineword(ff, 2, 3), NULL, 16); OutBytes = strtoull(procfile_lineword(ff, 2, 4), NULL, 16); - - // -------------------------------------------------------------------- - if(do_sockets) { static RRDSET *st = NULL; @@ -63,14 +60,11 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { rrddim_add(st, "connections", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "connections", entries); rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_packets) { static RRDSET *st = NULL; if(unlikely(!st)) { @@ -92,15 +86,12 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "received", InPackets); rrddim_set(st, "sent", OutPackets); rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_bandwidth) { static RRDSET *st = NULL; if(unlikely(!st)) { @@ -122,7 +113,6 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { rrddim_add(st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "received", InBytes); rrddim_set(st, "sent", OutBytes); diff --git a/collectors/proc.plugin/proc_net_netstat.c b/collectors/proc.plugin/proc_net_netstat.c index ab8206be3..f7635e3d0 100644 --- a/collectors/proc.plugin/proc_net_netstat.c +++ b/collectors/proc.plugin/proc_net_netstat.c @@ -3,14 +3,97 @@ #include "plugin_proc.h" #define RRD_TYPE_NET_NETSTAT "ip" +#define RRD_TYPE_NET_SNMP "ipv4" +#define RRD_TYPE_NET_SNMP6 "ipv6" #define PLUGIN_PROC_MODULE_NETSTAT_NAME "/proc/net/netstat" #define CONFIG_SECTION_PLUGIN_PROC_NETSTAT "plugin:" PLUGIN_PROC_CONFIG_NAME ":" PLUGIN_PROC_MODULE_NETSTAT_NAME -unsigned long long tcpext_TCPSynRetrans = 0; - -static void parse_line_pair(procfile *ff, ARL_BASE *base, size_t header_line, size_t values_line) { - size_t hwords = procfile_linewords(ff, header_line); - size_t vwords = procfile_linewords(ff, values_line); +static struct proc_net_snmp { + // kernel_uint_t ip_Forwarding; + kernel_uint_t ip_DefaultTTL; + kernel_uint_t ip_InReceives; + kernel_uint_t ip_InHdrErrors; + kernel_uint_t ip_InAddrErrors; + kernel_uint_t ip_ForwDatagrams; + kernel_uint_t ip_InUnknownProtos; + kernel_uint_t ip_InDiscards; + kernel_uint_t ip_InDelivers; + kernel_uint_t ip_OutRequests; + kernel_uint_t ip_OutDiscards; + kernel_uint_t ip_OutNoRoutes; + kernel_uint_t ip_ReasmTimeout; + kernel_uint_t ip_ReasmReqds; + kernel_uint_t ip_ReasmOKs; + kernel_uint_t ip_ReasmFails; + kernel_uint_t ip_FragOKs; + kernel_uint_t ip_FragFails; + kernel_uint_t ip_FragCreates; + + kernel_uint_t icmp_InMsgs; + kernel_uint_t icmp_OutMsgs; + kernel_uint_t icmp_InErrors; + kernel_uint_t icmp_OutErrors; + kernel_uint_t icmp_InCsumErrors; + + kernel_uint_t icmpmsg_InEchoReps; + kernel_uint_t icmpmsg_OutEchoReps; + kernel_uint_t icmpmsg_InDestUnreachs; + kernel_uint_t icmpmsg_OutDestUnreachs; + kernel_uint_t icmpmsg_InRedirects; + kernel_uint_t icmpmsg_OutRedirects; + kernel_uint_t icmpmsg_InEchos; + kernel_uint_t icmpmsg_OutEchos; + kernel_uint_t icmpmsg_InRouterAdvert; + kernel_uint_t icmpmsg_OutRouterAdvert; + kernel_uint_t icmpmsg_InRouterSelect; + kernel_uint_t icmpmsg_OutRouterSelect; + kernel_uint_t icmpmsg_InTimeExcds; + kernel_uint_t icmpmsg_OutTimeExcds; + kernel_uint_t icmpmsg_InParmProbs; + kernel_uint_t icmpmsg_OutParmProbs; + kernel_uint_t icmpmsg_InTimestamps; + kernel_uint_t icmpmsg_OutTimestamps; + kernel_uint_t icmpmsg_InTimestampReps; + kernel_uint_t icmpmsg_OutTimestampReps; + + //kernel_uint_t tcp_RtoAlgorithm; + //kernel_uint_t tcp_RtoMin; + //kernel_uint_t tcp_RtoMax; + ssize_t tcp_MaxConn; + kernel_uint_t tcp_ActiveOpens; + kernel_uint_t tcp_PassiveOpens; + kernel_uint_t tcp_AttemptFails; + kernel_uint_t tcp_EstabResets; + kernel_uint_t tcp_CurrEstab; + kernel_uint_t tcp_InSegs; + kernel_uint_t tcp_OutSegs; + kernel_uint_t tcp_RetransSegs; + kernel_uint_t tcp_InErrs; + kernel_uint_t tcp_OutRsts; + kernel_uint_t tcp_InCsumErrors; + + kernel_uint_t udp_InDatagrams; + kernel_uint_t udp_NoPorts; + kernel_uint_t udp_InErrors; + kernel_uint_t udp_OutDatagrams; + kernel_uint_t udp_RcvbufErrors; + kernel_uint_t udp_SndbufErrors; + kernel_uint_t udp_InCsumErrors; + kernel_uint_t udp_IgnoredMulti; + + kernel_uint_t udplite_InDatagrams; + kernel_uint_t udplite_NoPorts; + kernel_uint_t udplite_InErrors; + kernel_uint_t udplite_OutDatagrams; + kernel_uint_t udplite_RcvbufErrors; + kernel_uint_t udplite_SndbufErrors; + kernel_uint_t udplite_InCsumErrors; + kernel_uint_t udplite_IgnoredMulti; +} snmp_root = { 0 }; + +static void parse_line_pair(procfile *ff_netstat, ARL_BASE *base, size_t header_line, size_t values_line) { + size_t hwords = procfile_linewords(ff_netstat, header_line); + size_t vwords = procfile_linewords(ff_netstat, values_line); size_t w; if(unlikely(vwords > hwords)) { @@ -19,7 +102,7 @@ static void parse_line_pair(procfile *ff, ARL_BASE *base, size_t header_line, si } for(w = 1; w < vwords ;w++) { - if(unlikely(arl_check(base, procfile_lineword(ff, header_line, w), procfile_lineword(ff, values_line, w)))) + if(unlikely(arl_check(base, procfile_lineword(ff_netstat, header_line, w), procfile_lineword(ff_netstat, values_line, w)))) break; } } @@ -31,12 +114,38 @@ int do_proc_net_netstat(int update_every, usec_t dt) { do_tcpext_reorder = -1, do_tcpext_syscookies = -1, do_tcpext_ofo = -1, do_tcpext_connaborts = -1, do_tcpext_memory = -1, do_tcpext_syn_queue = -1, do_tcpext_accept_queue = -1; + static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1, + do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1, do_tcp_opens = -1, + do_udp_packets = -1, do_udp_errors = -1, do_icmp_packets = -1, do_icmpmsg = -1, do_udplite_packets = -1; + + static int do_ip6_packets = -1, do_ip6_fragsout = -1, do_ip6_fragsin = -1, do_ip6_errors = -1, + do_ip6_udplite_packets = -1, do_ip6_udplite_errors = -1, do_ip6_udp_packets = -1, do_ip6_udp_errors = -1, + do_ip6_bandwidth = -1, do_ip6_mcast = -1, do_ip6_bcast = -1, do_ip6_mcast_p = -1, do_ip6_icmp = -1, + do_ip6_icmp_redir = -1, do_ip6_icmp_errors = -1, do_ip6_icmp_echos = -1, do_ip6_icmp_groupmemb = -1, + do_ip6_icmp_router = -1, do_ip6_icmp_neighbor = -1, do_ip6_icmp_mldv2 = -1, do_ip6_icmp_types = -1, + do_ip6_ect = -1; + static uint32_t hash_ipext = 0, hash_tcpext = 0; - static procfile *ff = NULL; + static uint32_t hash_ip = 0, hash_icmp = 0, hash_tcp = 0, hash_udp = 0, hash_icmpmsg = 0, hash_udplite = 0; + + static procfile *ff_netstat = NULL; + static procfile *ff_snmp = NULL; + static procfile *ff_snmp6 = NULL; static ARL_BASE *arl_tcpext = NULL; static ARL_BASE *arl_ipext = NULL; + static ARL_BASE *arl_ip = NULL; + static ARL_BASE *arl_icmp = NULL; + static ARL_BASE *arl_icmpmsg = NULL; + static ARL_BASE *arl_tcp = NULL; + static ARL_BASE *arl_udp = NULL; + static ARL_BASE *arl_udplite = NULL; + + static ARL_BASE *arl_ipv6 = NULL; + + static const RRDVAR_ACQUIRED *tcp_max_connections_var = NULL; + // -------------------------------------------------------------------- // IP @@ -111,8 +220,103 @@ int do_proc_net_netstat(int update_every, usec_t dt) { static unsigned long long tcpext_TCPReqQFullDrop = 0; static unsigned long long tcpext_TCPReqQFullDoCookies = 0; - // shared: tcpext_TCPSynRetrans - + static unsigned long long tcpext_TCPSynRetrans = 0; + + // IPv6 + static unsigned long long Ip6InReceives = 0ULL; + static unsigned long long Ip6InHdrErrors = 0ULL; + static unsigned long long Ip6InTooBigErrors = 0ULL; + static unsigned long long Ip6InNoRoutes = 0ULL; + static unsigned long long Ip6InAddrErrors = 0ULL; + static unsigned long long Ip6InUnknownProtos = 0ULL; + static unsigned long long Ip6InTruncatedPkts = 0ULL; + static unsigned long long Ip6InDiscards = 0ULL; + static unsigned long long Ip6InDelivers = 0ULL; + static unsigned long long Ip6OutForwDatagrams = 0ULL; + static unsigned long long Ip6OutRequests = 0ULL; + static unsigned long long Ip6OutDiscards = 0ULL; + static unsigned long long Ip6OutNoRoutes = 0ULL; + static unsigned long long Ip6ReasmTimeout = 0ULL; + static unsigned long long Ip6ReasmReqds = 0ULL; + static unsigned long long Ip6ReasmOKs = 0ULL; + static unsigned long long Ip6ReasmFails = 0ULL; + static unsigned long long Ip6FragOKs = 0ULL; + static unsigned long long Ip6FragFails = 0ULL; + static unsigned long long Ip6FragCreates = 0ULL; + static unsigned long long Ip6InMcastPkts = 0ULL; + static unsigned long long Ip6OutMcastPkts = 0ULL; + static unsigned long long Ip6InOctets = 0ULL; + static unsigned long long Ip6OutOctets = 0ULL; + static unsigned long long Ip6InMcastOctets = 0ULL; + static unsigned long long Ip6OutMcastOctets = 0ULL; + static unsigned long long Ip6InBcastOctets = 0ULL; + static unsigned long long Ip6OutBcastOctets = 0ULL; + static unsigned long long Ip6InNoECTPkts = 0ULL; + static unsigned long long Ip6InECT1Pkts = 0ULL; + static unsigned long long Ip6InECT0Pkts = 0ULL; + static unsigned long long Ip6InCEPkts = 0ULL; + static unsigned long long Icmp6InMsgs = 0ULL; + static unsigned long long Icmp6InErrors = 0ULL; + static unsigned long long Icmp6OutMsgs = 0ULL; + static unsigned long long Icmp6OutErrors = 0ULL; + static unsigned long long Icmp6InCsumErrors = 0ULL; + static unsigned long long Icmp6InDestUnreachs = 0ULL; + static unsigned long long Icmp6InPktTooBigs = 0ULL; + static unsigned long long Icmp6InTimeExcds = 0ULL; + static unsigned long long Icmp6InParmProblems = 0ULL; + static unsigned long long Icmp6InEchos = 0ULL; + static unsigned long long Icmp6InEchoReplies = 0ULL; + static unsigned long long Icmp6InGroupMembQueries = 0ULL; + static unsigned long long Icmp6InGroupMembResponses = 0ULL; + static unsigned long long Icmp6InGroupMembReductions = 0ULL; + static unsigned long long Icmp6InRouterSolicits = 0ULL; + static unsigned long long Icmp6InRouterAdvertisements = 0ULL; + static unsigned long long Icmp6InNeighborSolicits = 0ULL; + static unsigned long long Icmp6InNeighborAdvertisements = 0ULL; + static unsigned long long Icmp6InRedirects = 0ULL; + static unsigned long long Icmp6InMLDv2Reports = 0ULL; + static unsigned long long Icmp6OutDestUnreachs = 0ULL; + static unsigned long long Icmp6OutPktTooBigs = 0ULL; + static unsigned long long Icmp6OutTimeExcds = 0ULL; + static unsigned long long Icmp6OutParmProblems = 0ULL; + static unsigned long long Icmp6OutEchos = 0ULL; + static unsigned long long Icmp6OutEchoReplies = 0ULL; + static unsigned long long Icmp6OutGroupMembQueries = 0ULL; + static unsigned long long Icmp6OutGroupMembResponses = 0ULL; + static unsigned long long Icmp6OutGroupMembReductions = 0ULL; + static unsigned long long Icmp6OutRouterSolicits = 0ULL; + static unsigned long long Icmp6OutRouterAdvertisements = 0ULL; + static unsigned long long Icmp6OutNeighborSolicits = 0ULL; + static unsigned long long Icmp6OutNeighborAdvertisements = 0ULL; + static unsigned long long Icmp6OutRedirects = 0ULL; + static unsigned long long Icmp6OutMLDv2Reports = 0ULL; + static unsigned long long Icmp6InType1 = 0ULL; + static unsigned long long Icmp6InType128 = 0ULL; + static unsigned long long Icmp6InType129 = 0ULL; + static unsigned long long Icmp6InType136 = 0ULL; + static unsigned long long Icmp6OutType1 = 0ULL; + static unsigned long long Icmp6OutType128 = 0ULL; + static unsigned long long Icmp6OutType129 = 0ULL; + static unsigned long long Icmp6OutType133 = 0ULL; + static unsigned long long Icmp6OutType135 = 0ULL; + static unsigned long long Icmp6OutType143 = 0ULL; + static unsigned long long Udp6InDatagrams = 0ULL; + static unsigned long long Udp6NoPorts = 0ULL; + static unsigned long long Udp6InErrors = 0ULL; + static unsigned long long Udp6OutDatagrams = 0ULL; + static unsigned long long Udp6RcvbufErrors = 0ULL; + static unsigned long long Udp6SndbufErrors = 0ULL; + static unsigned long long Udp6InCsumErrors = 0ULL; + static unsigned long long Udp6IgnoredMulti = 0ULL; + static unsigned long long UdpLite6InDatagrams = 0ULL; + static unsigned long long UdpLite6NoPorts = 0ULL; + static unsigned long long UdpLite6InErrors = 0ULL; + static unsigned long long UdpLite6OutDatagrams = 0ULL; + static unsigned long long UdpLite6RcvbufErrors = 0ULL; + static unsigned long long UdpLite6SndbufErrors = 0ULL; + static unsigned long long UdpLite6InCsumErrors = 0ULL; + + // prepare for /proc/net/netstat parsing if(unlikely(!arl_ipext)) { hash_ipext = simple_hash("IpExt"); @@ -225,645 +429,2682 @@ int do_proc_net_netstat(int update_every, usec_t dt) { arl_expect(arl_tcpext, "TCPReqQFullDoCookies", &tcpext_TCPReqQFullDoCookies); } - // shared metrics arl_expect(arl_tcpext, "TCPSynRetrans", &tcpext_TCPSynRetrans); } - if(unlikely(!ff)) { + // prepare for /proc/net/snmp parsing + + if(unlikely(!arl_ip)) { + do_ip_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 packets", CONFIG_BOOLEAN_AUTO); + do_ip_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 fragments sent", CONFIG_BOOLEAN_AUTO); + do_ip_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 fragments assembly", CONFIG_BOOLEAN_AUTO); + do_ip_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 errors", CONFIG_BOOLEAN_AUTO); + do_tcp_sockets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP connections", CONFIG_BOOLEAN_AUTO); + do_tcp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP packets", CONFIG_BOOLEAN_AUTO); + do_tcp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP errors", CONFIG_BOOLEAN_AUTO); + do_tcp_opens = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP opens", CONFIG_BOOLEAN_AUTO); + do_tcp_handshake = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP handshake issues", CONFIG_BOOLEAN_AUTO); + do_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 UDP packets", CONFIG_BOOLEAN_AUTO); + do_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 UDP errors", CONFIG_BOOLEAN_AUTO); + do_icmp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 ICMP packets", CONFIG_BOOLEAN_AUTO); + do_icmpmsg = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 ICMP messages", CONFIG_BOOLEAN_AUTO); + do_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 UDPLite packets", CONFIG_BOOLEAN_AUTO); + + hash_ip = simple_hash("Ip"); + hash_tcp = simple_hash("Tcp"); + hash_udp = simple_hash("Udp"); + hash_icmp = simple_hash("Icmp"); + hash_icmpmsg = simple_hash("IcmpMsg"); + hash_udplite = simple_hash("UdpLite"); + + arl_ip = arl_create("snmp/Ip", arl_callback_str2kernel_uint_t, 60); + // arl_expect(arl_ip, "Forwarding", &snmp_root.ip_Forwarding); + arl_expect(arl_ip, "DefaultTTL", &snmp_root.ip_DefaultTTL); + arl_expect(arl_ip, "InReceives", &snmp_root.ip_InReceives); + arl_expect(arl_ip, "InHdrErrors", &snmp_root.ip_InHdrErrors); + arl_expect(arl_ip, "InAddrErrors", &snmp_root.ip_InAddrErrors); + arl_expect(arl_ip, "ForwDatagrams", &snmp_root.ip_ForwDatagrams); + arl_expect(arl_ip, "InUnknownProtos", &snmp_root.ip_InUnknownProtos); + arl_expect(arl_ip, "InDiscards", &snmp_root.ip_InDiscards); + arl_expect(arl_ip, "InDelivers", &snmp_root.ip_InDelivers); + arl_expect(arl_ip, "OutRequests", &snmp_root.ip_OutRequests); + arl_expect(arl_ip, "OutDiscards", &snmp_root.ip_OutDiscards); + arl_expect(arl_ip, "OutNoRoutes", &snmp_root.ip_OutNoRoutes); + arl_expect(arl_ip, "ReasmTimeout", &snmp_root.ip_ReasmTimeout); + arl_expect(arl_ip, "ReasmReqds", &snmp_root.ip_ReasmReqds); + arl_expect(arl_ip, "ReasmOKs", &snmp_root.ip_ReasmOKs); + arl_expect(arl_ip, "ReasmFails", &snmp_root.ip_ReasmFails); + arl_expect(arl_ip, "FragOKs", &snmp_root.ip_FragOKs); + arl_expect(arl_ip, "FragFails", &snmp_root.ip_FragFails); + arl_expect(arl_ip, "FragCreates", &snmp_root.ip_FragCreates); + + arl_icmp = arl_create("snmp/Icmp", arl_callback_str2kernel_uint_t, 60); + arl_expect(arl_icmp, "InMsgs", &snmp_root.icmp_InMsgs); + arl_expect(arl_icmp, "OutMsgs", &snmp_root.icmp_OutMsgs); + arl_expect(arl_icmp, "InErrors", &snmp_root.icmp_InErrors); + arl_expect(arl_icmp, "OutErrors", &snmp_root.icmp_OutErrors); + arl_expect(arl_icmp, "InCsumErrors", &snmp_root.icmp_InCsumErrors); + + arl_icmpmsg = arl_create("snmp/Icmpmsg", arl_callback_str2kernel_uint_t, 60); + arl_expect(arl_icmpmsg, "InType0", &snmp_root.icmpmsg_InEchoReps); + arl_expect(arl_icmpmsg, "OutType0", &snmp_root.icmpmsg_OutEchoReps); + arl_expect(arl_icmpmsg, "InType3", &snmp_root.icmpmsg_InDestUnreachs); + arl_expect(arl_icmpmsg, "OutType3", &snmp_root.icmpmsg_OutDestUnreachs); + arl_expect(arl_icmpmsg, "InType5", &snmp_root.icmpmsg_InRedirects); + arl_expect(arl_icmpmsg, "OutType5", &snmp_root.icmpmsg_OutRedirects); + arl_expect(arl_icmpmsg, "InType8", &snmp_root.icmpmsg_InEchos); + arl_expect(arl_icmpmsg, "OutType8", &snmp_root.icmpmsg_OutEchos); + arl_expect(arl_icmpmsg, "InType9", &snmp_root.icmpmsg_InRouterAdvert); + arl_expect(arl_icmpmsg, "OutType9", &snmp_root.icmpmsg_OutRouterAdvert); + arl_expect(arl_icmpmsg, "InType10", &snmp_root.icmpmsg_InRouterSelect); + arl_expect(arl_icmpmsg, "OutType10", &snmp_root.icmpmsg_OutRouterSelect); + arl_expect(arl_icmpmsg, "InType11", &snmp_root.icmpmsg_InTimeExcds); + arl_expect(arl_icmpmsg, "OutType11", &snmp_root.icmpmsg_OutTimeExcds); + arl_expect(arl_icmpmsg, "InType12", &snmp_root.icmpmsg_InParmProbs); + arl_expect(arl_icmpmsg, "OutType12", &snmp_root.icmpmsg_OutParmProbs); + arl_expect(arl_icmpmsg, "InType13", &snmp_root.icmpmsg_InTimestamps); + arl_expect(arl_icmpmsg, "OutType13", &snmp_root.icmpmsg_OutTimestamps); + arl_expect(arl_icmpmsg, "InType14", &snmp_root.icmpmsg_InTimestampReps); + arl_expect(arl_icmpmsg, "OutType14", &snmp_root.icmpmsg_OutTimestampReps); + + arl_tcp = arl_create("snmp/Tcp", arl_callback_str2kernel_uint_t, 60); + // arl_expect(arl_tcp, "RtoAlgorithm", &snmp_root.tcp_RtoAlgorithm); + // arl_expect(arl_tcp, "RtoMin", &snmp_root.tcp_RtoMin); + // arl_expect(arl_tcp, "RtoMax", &snmp_root.tcp_RtoMax); + arl_expect_custom(arl_tcp, "MaxConn", arl_callback_ssize_t, &snmp_root.tcp_MaxConn); + arl_expect(arl_tcp, "ActiveOpens", &snmp_root.tcp_ActiveOpens); + arl_expect(arl_tcp, "PassiveOpens", &snmp_root.tcp_PassiveOpens); + arl_expect(arl_tcp, "AttemptFails", &snmp_root.tcp_AttemptFails); + arl_expect(arl_tcp, "EstabResets", &snmp_root.tcp_EstabResets); + arl_expect(arl_tcp, "CurrEstab", &snmp_root.tcp_CurrEstab); + arl_expect(arl_tcp, "InSegs", &snmp_root.tcp_InSegs); + arl_expect(arl_tcp, "OutSegs", &snmp_root.tcp_OutSegs); + arl_expect(arl_tcp, "RetransSegs", &snmp_root.tcp_RetransSegs); + arl_expect(arl_tcp, "InErrs", &snmp_root.tcp_InErrs); + arl_expect(arl_tcp, "OutRsts", &snmp_root.tcp_OutRsts); + arl_expect(arl_tcp, "InCsumErrors", &snmp_root.tcp_InCsumErrors); + + arl_udp = arl_create("snmp/Udp", arl_callback_str2kernel_uint_t, 60); + arl_expect(arl_udp, "InDatagrams", &snmp_root.udp_InDatagrams); + arl_expect(arl_udp, "NoPorts", &snmp_root.udp_NoPorts); + arl_expect(arl_udp, "InErrors", &snmp_root.udp_InErrors); + arl_expect(arl_udp, "OutDatagrams", &snmp_root.udp_OutDatagrams); + arl_expect(arl_udp, "RcvbufErrors", &snmp_root.udp_RcvbufErrors); + arl_expect(arl_udp, "SndbufErrors", &snmp_root.udp_SndbufErrors); + arl_expect(arl_udp, "InCsumErrors", &snmp_root.udp_InCsumErrors); + arl_expect(arl_udp, "IgnoredMulti", &snmp_root.udp_IgnoredMulti); + + arl_udplite = arl_create("snmp/Udplite", arl_callback_str2kernel_uint_t, 60); + arl_expect(arl_udplite, "InDatagrams", &snmp_root.udplite_InDatagrams); + arl_expect(arl_udplite, "NoPorts", &snmp_root.udplite_NoPorts); + arl_expect(arl_udplite, "InErrors", &snmp_root.udplite_InErrors); + arl_expect(arl_udplite, "OutDatagrams", &snmp_root.udplite_OutDatagrams); + arl_expect(arl_udplite, "RcvbufErrors", &snmp_root.udplite_RcvbufErrors); + arl_expect(arl_udplite, "SndbufErrors", &snmp_root.udplite_SndbufErrors); + arl_expect(arl_udplite, "InCsumErrors", &snmp_root.udplite_InCsumErrors); + arl_expect(arl_udplite, "IgnoredMulti", &snmp_root.udplite_IgnoredMulti); + + tcp_max_connections_var = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_max_connections"); + } + + // prepare for /proc/net/snmp6 parsing + + if(unlikely(!arl_ipv6)) { + do_ip6_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_BOOLEAN_AUTO); + do_ip6_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_BOOLEAN_AUTO); + do_ip6_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_BOOLEAN_AUTO); + do_ip6_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_BOOLEAN_AUTO); + do_ip6_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_BOOLEAN_AUTO); + do_ip6_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_BOOLEAN_AUTO); + do_ip6_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_BOOLEAN_AUTO); + do_ip6_udplite_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_BOOLEAN_AUTO); + do_ip6_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_BOOLEAN_AUTO); + do_ip6_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_BOOLEAN_AUTO); + do_ip6_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_BOOLEAN_AUTO); + do_ip6_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_BOOLEAN_AUTO); + do_ip6_icmp = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_BOOLEAN_AUTO); + do_ip6_icmp_redir = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_BOOLEAN_AUTO); + do_ip6_icmp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_BOOLEAN_AUTO); + do_ip6_icmp_echos = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_BOOLEAN_AUTO); + do_ip6_icmp_groupmemb = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_BOOLEAN_AUTO); + do_ip6_icmp_router = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_BOOLEAN_AUTO); + do_ip6_icmp_neighbor = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_BOOLEAN_AUTO); + do_ip6_icmp_mldv2 = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_BOOLEAN_AUTO); + do_ip6_icmp_types = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_BOOLEAN_AUTO); + do_ip6_ect = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_BOOLEAN_AUTO); + + arl_ipv6 = arl_create("snmp6", NULL, 60); + arl_expect(arl_ipv6, "Ip6InReceives", &Ip6InReceives); + arl_expect(arl_ipv6, "Ip6InHdrErrors", &Ip6InHdrErrors); + arl_expect(arl_ipv6, "Ip6InTooBigErrors", &Ip6InTooBigErrors); + arl_expect(arl_ipv6, "Ip6InNoRoutes", &Ip6InNoRoutes); + arl_expect(arl_ipv6, "Ip6InAddrErrors", &Ip6InAddrErrors); + arl_expect(arl_ipv6, "Ip6InUnknownProtos", &Ip6InUnknownProtos); + arl_expect(arl_ipv6, "Ip6InTruncatedPkts", &Ip6InTruncatedPkts); + arl_expect(arl_ipv6, "Ip6InDiscards", &Ip6InDiscards); + arl_expect(arl_ipv6, "Ip6InDelivers", &Ip6InDelivers); + arl_expect(arl_ipv6, "Ip6OutForwDatagrams", &Ip6OutForwDatagrams); + arl_expect(arl_ipv6, "Ip6OutRequests", &Ip6OutRequests); + arl_expect(arl_ipv6, "Ip6OutDiscards", &Ip6OutDiscards); + arl_expect(arl_ipv6, "Ip6OutNoRoutes", &Ip6OutNoRoutes); + arl_expect(arl_ipv6, "Ip6ReasmTimeout", &Ip6ReasmTimeout); + arl_expect(arl_ipv6, "Ip6ReasmReqds", &Ip6ReasmReqds); + arl_expect(arl_ipv6, "Ip6ReasmOKs", &Ip6ReasmOKs); + arl_expect(arl_ipv6, "Ip6ReasmFails", &Ip6ReasmFails); + arl_expect(arl_ipv6, "Ip6FragOKs", &Ip6FragOKs); + arl_expect(arl_ipv6, "Ip6FragFails", &Ip6FragFails); + arl_expect(arl_ipv6, "Ip6FragCreates", &Ip6FragCreates); + arl_expect(arl_ipv6, "Ip6InMcastPkts", &Ip6InMcastPkts); + arl_expect(arl_ipv6, "Ip6OutMcastPkts", &Ip6OutMcastPkts); + arl_expect(arl_ipv6, "Ip6InOctets", &Ip6InOctets); + arl_expect(arl_ipv6, "Ip6OutOctets", &Ip6OutOctets); + arl_expect(arl_ipv6, "Ip6InMcastOctets", &Ip6InMcastOctets); + arl_expect(arl_ipv6, "Ip6OutMcastOctets", &Ip6OutMcastOctets); + arl_expect(arl_ipv6, "Ip6InBcastOctets", &Ip6InBcastOctets); + arl_expect(arl_ipv6, "Ip6OutBcastOctets", &Ip6OutBcastOctets); + arl_expect(arl_ipv6, "Ip6InNoECTPkts", &Ip6InNoECTPkts); + arl_expect(arl_ipv6, "Ip6InECT1Pkts", &Ip6InECT1Pkts); + arl_expect(arl_ipv6, "Ip6InECT0Pkts", &Ip6InECT0Pkts); + arl_expect(arl_ipv6, "Ip6InCEPkts", &Ip6InCEPkts); + arl_expect(arl_ipv6, "Icmp6InMsgs", &Icmp6InMsgs); + arl_expect(arl_ipv6, "Icmp6InErrors", &Icmp6InErrors); + arl_expect(arl_ipv6, "Icmp6OutMsgs", &Icmp6OutMsgs); + arl_expect(arl_ipv6, "Icmp6OutErrors", &Icmp6OutErrors); + arl_expect(arl_ipv6, "Icmp6InCsumErrors", &Icmp6InCsumErrors); + arl_expect(arl_ipv6, "Icmp6InDestUnreachs", &Icmp6InDestUnreachs); + arl_expect(arl_ipv6, "Icmp6InPktTooBigs", &Icmp6InPktTooBigs); + arl_expect(arl_ipv6, "Icmp6InTimeExcds", &Icmp6InTimeExcds); + arl_expect(arl_ipv6, "Icmp6InParmProblems", &Icmp6InParmProblems); + arl_expect(arl_ipv6, "Icmp6InEchos", &Icmp6InEchos); + arl_expect(arl_ipv6, "Icmp6InEchoReplies", &Icmp6InEchoReplies); + arl_expect(arl_ipv6, "Icmp6InGroupMembQueries", &Icmp6InGroupMembQueries); + arl_expect(arl_ipv6, "Icmp6InGroupMembResponses", &Icmp6InGroupMembResponses); + arl_expect(arl_ipv6, "Icmp6InGroupMembReductions", &Icmp6InGroupMembReductions); + arl_expect(arl_ipv6, "Icmp6InRouterSolicits", &Icmp6InRouterSolicits); + arl_expect(arl_ipv6, "Icmp6InRouterAdvertisements", &Icmp6InRouterAdvertisements); + arl_expect(arl_ipv6, "Icmp6InNeighborSolicits", &Icmp6InNeighborSolicits); + arl_expect(arl_ipv6, "Icmp6InNeighborAdvertisements", &Icmp6InNeighborAdvertisements); + arl_expect(arl_ipv6, "Icmp6InRedirects", &Icmp6InRedirects); + arl_expect(arl_ipv6, "Icmp6InMLDv2Reports", &Icmp6InMLDv2Reports); + arl_expect(arl_ipv6, "Icmp6OutDestUnreachs", &Icmp6OutDestUnreachs); + arl_expect(arl_ipv6, "Icmp6OutPktTooBigs", &Icmp6OutPktTooBigs); + arl_expect(arl_ipv6, "Icmp6OutTimeExcds", &Icmp6OutTimeExcds); + arl_expect(arl_ipv6, "Icmp6OutParmProblems", &Icmp6OutParmProblems); + arl_expect(arl_ipv6, "Icmp6OutEchos", &Icmp6OutEchos); + arl_expect(arl_ipv6, "Icmp6OutEchoReplies", &Icmp6OutEchoReplies); + arl_expect(arl_ipv6, "Icmp6OutGroupMembQueries", &Icmp6OutGroupMembQueries); + arl_expect(arl_ipv6, "Icmp6OutGroupMembResponses", &Icmp6OutGroupMembResponses); + arl_expect(arl_ipv6, "Icmp6OutGroupMembReductions", &Icmp6OutGroupMembReductions); + arl_expect(arl_ipv6, "Icmp6OutRouterSolicits", &Icmp6OutRouterSolicits); + arl_expect(arl_ipv6, "Icmp6OutRouterAdvertisements", &Icmp6OutRouterAdvertisements); + arl_expect(arl_ipv6, "Icmp6OutNeighborSolicits", &Icmp6OutNeighborSolicits); + arl_expect(arl_ipv6, "Icmp6OutNeighborAdvertisements", &Icmp6OutNeighborAdvertisements); + arl_expect(arl_ipv6, "Icmp6OutRedirects", &Icmp6OutRedirects); + arl_expect(arl_ipv6, "Icmp6OutMLDv2Reports", &Icmp6OutMLDv2Reports); + arl_expect(arl_ipv6, "Icmp6InType1", &Icmp6InType1); + arl_expect(arl_ipv6, "Icmp6InType128", &Icmp6InType128); + arl_expect(arl_ipv6, "Icmp6InType129", &Icmp6InType129); + arl_expect(arl_ipv6, "Icmp6InType136", &Icmp6InType136); + arl_expect(arl_ipv6, "Icmp6OutType1", &Icmp6OutType1); + arl_expect(arl_ipv6, "Icmp6OutType128", &Icmp6OutType128); + arl_expect(arl_ipv6, "Icmp6OutType129", &Icmp6OutType129); + arl_expect(arl_ipv6, "Icmp6OutType133", &Icmp6OutType133); + arl_expect(arl_ipv6, "Icmp6OutType135", &Icmp6OutType135); + arl_expect(arl_ipv6, "Icmp6OutType143", &Icmp6OutType143); + arl_expect(arl_ipv6, "Udp6InDatagrams", &Udp6InDatagrams); + arl_expect(arl_ipv6, "Udp6NoPorts", &Udp6NoPorts); + arl_expect(arl_ipv6, "Udp6InErrors", &Udp6InErrors); + arl_expect(arl_ipv6, "Udp6OutDatagrams", &Udp6OutDatagrams); + arl_expect(arl_ipv6, "Udp6RcvbufErrors", &Udp6RcvbufErrors); + arl_expect(arl_ipv6, "Udp6SndbufErrors", &Udp6SndbufErrors); + arl_expect(arl_ipv6, "Udp6InCsumErrors", &Udp6InCsumErrors); + arl_expect(arl_ipv6, "Udp6IgnoredMulti", &Udp6IgnoredMulti); + arl_expect(arl_ipv6, "UdpLite6InDatagrams", &UdpLite6InDatagrams); + arl_expect(arl_ipv6, "UdpLite6NoPorts", &UdpLite6NoPorts); + arl_expect(arl_ipv6, "UdpLite6InErrors", &UdpLite6InErrors); + arl_expect(arl_ipv6, "UdpLite6OutDatagrams", &UdpLite6OutDatagrams); + arl_expect(arl_ipv6, "UdpLite6RcvbufErrors", &UdpLite6RcvbufErrors); + arl_expect(arl_ipv6, "UdpLite6SndbufErrors", &UdpLite6SndbufErrors); + arl_expect(arl_ipv6, "UdpLite6InCsumErrors", &UdpLite6InCsumErrors); + } + + size_t lines, l, words; + + // parse /proc/net/netstat + + if(unlikely(!ff_netstat)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/netstat"); - ff = procfile_open(config_get(CONFIG_SECTION_PLUGIN_PROC_NETSTAT, "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); - if(unlikely(!ff)) return 1; + ff_netstat = procfile_open(config_get(CONFIG_SECTION_PLUGIN_PROC_NETSTAT, "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff_netstat)) return 1; } - ff = procfile_readall(ff); - if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time + ff_netstat = procfile_readall(ff_netstat); + if(unlikely(!ff_netstat)) return 0; // we return 0, so that we will retry to open it next time - size_t lines = procfile_lines(ff), l; - size_t words; + lines = procfile_lines(ff_netstat); arl_begin(arl_ipext); arl_begin(arl_tcpext); for(l = 0; l < lines ;l++) { - char *key = procfile_lineword(ff, l, 0); + char *key = procfile_lineword(ff_netstat, l, 0); uint32_t hash = simple_hash(key); if(unlikely(hash == hash_ipext && strcmp(key, "IpExt") == 0)) { size_t h = l++; - words = procfile_linewords(ff, l); + words = procfile_linewords(ff_netstat, l); if(unlikely(words < 2)) { error("Cannot read /proc/net/netstat IpExt line. Expected 2+ params, read %zu.", words); continue; } - parse_line_pair(ff, arl_ipext, h, l); - - // -------------------------------------------------------------------- - - if(do_bandwidth == CONFIG_BOOLEAN_YES || (do_bandwidth == CONFIG_BOOLEAN_AUTO && - (ipext_InOctets || - ipext_OutOctets || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_bandwidth = CONFIG_BOOLEAN_YES; - static RRDSET *st_system_ip = NULL; - static RRDDIM *rd_in = NULL, *rd_out = NULL; - - if(unlikely(!st_system_ip)) { - st_system_ip = rrdset_create_localhost( - "system" - , RRD_TYPE_NET_NETSTAT - , NULL - , "network" - , NULL - , "IP Bandwidth" - , "kilobits/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_SYSTEM_IP - , update_every - , RRDSET_TYPE_AREA - ); - - rd_in = rrddim_add(st_system_ip, "InOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - rd_out = rrddim_add(st_system_ip, "OutOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_system_ip); - - rrddim_set_by_pointer(st_system_ip, rd_in, ipext_InOctets); - rrddim_set_by_pointer(st_system_ip, rd_out, ipext_OutOctets); - - rrdset_done(st_system_ip); + parse_line_pair(ff_netstat, arl_ipext, h, l); + + } + else if(unlikely(hash == hash_tcpext && strcmp(key, "TcpExt") == 0)) { + size_t h = l++; + + words = procfile_linewords(ff_netstat, l); + if(unlikely(words < 2)) { + error("Cannot read /proc/net/netstat TcpExt line. Expected 2+ params, read %zu.", words); + continue; + } + + parse_line_pair(ff_netstat, arl_tcpext, h, l); + } + } + + // parse /proc/net/snmp + + if(unlikely(!ff_snmp)) { + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/snmp"); + ff_snmp = procfile_open(config_get("plugin:proc:/proc/net/snmp", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff_snmp)) return 1; + } + + ff_snmp = procfile_readall(ff_snmp); + if(unlikely(!ff_snmp)) return 0; // we return 0, so that we will retry to open it next time + + lines = procfile_lines(ff_snmp); + size_t w; + + for(l = 0; l < lines ;l++) { + char *key = procfile_lineword(ff_snmp, l, 0); + uint32_t hash = simple_hash(key); + + if(unlikely(hash == hash_ip && strcmp(key, "Ip") == 0)) { + size_t h = l++; + + if(strcmp(procfile_lineword(ff_snmp, l, 0), "Ip") != 0) { + error("Cannot read Ip line from /proc/net/snmp."); + break; } - // -------------------------------------------------------------------- - - if(do_inerrors == CONFIG_BOOLEAN_YES || (do_inerrors == CONFIG_BOOLEAN_AUTO && - (ipext_InNoRoutes || - ipext_InTruncatedPkts || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_inerrors = CONFIG_BOOLEAN_YES; - static RRDSET *st_ip_inerrors = NULL; - static RRDDIM *rd_noroutes = NULL, *rd_truncated = NULL, *rd_checksum = NULL; - - if(unlikely(!st_ip_inerrors)) { - st_ip_inerrors = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "inerrors" - , NULL - , "errors" - , NULL - , "IP Input Errors" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_ERRORS - , update_every - , RRDSET_TYPE_LINE - ); - - rrdset_flag_set(st_ip_inerrors, RRDSET_FLAG_DETAIL); - - rd_noroutes = rrddim_add(st_ip_inerrors, "InNoRoutes", "noroutes", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_truncated = rrddim_add(st_ip_inerrors, "InTruncatedPkts", "truncated", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_checksum = rrddim_add(st_ip_inerrors, "InCsumErrors", "checksum", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_ip_inerrors); - - rrddim_set_by_pointer(st_ip_inerrors, rd_noroutes, ipext_InNoRoutes); - rrddim_set_by_pointer(st_ip_inerrors, rd_truncated, ipext_InTruncatedPkts); - rrddim_set_by_pointer(st_ip_inerrors, rd_checksum, ipext_InCsumErrors); - - rrdset_done(st_ip_inerrors); + words = procfile_linewords(ff_snmp, l); + if(words < 3) { + error("Cannot read /proc/net/snmp Ip line. Expected 3+ params, read %zu.", words); + continue; } - // -------------------------------------------------------------------- + arl_begin(arl_ip); + for(w = 1; w < words ; w++) { + if (unlikely(arl_check(arl_ip, procfile_lineword(ff_snmp, h, w), procfile_lineword(ff_snmp, l, w)) != 0)) + break; + } + } + else if(unlikely(hash == hash_icmp && strcmp(key, "Icmp") == 0)) { + size_t h = l++; - if(do_mcast == CONFIG_BOOLEAN_YES || (do_mcast == CONFIG_BOOLEAN_AUTO && - (ipext_InMcastOctets || - ipext_OutMcastOctets || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_mcast = CONFIG_BOOLEAN_YES; - static RRDSET *st_ip_mcast = NULL; - static RRDDIM *rd_in = NULL, *rd_out = NULL; - - if(unlikely(!st_ip_mcast)) { - st_ip_mcast = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "mcast" - , NULL - , "multicast" - , NULL - , "IP Multicast Bandwidth" - , "kilobits/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_MCAST - , update_every - , RRDSET_TYPE_AREA - ); - - rrdset_flag_set(st_ip_mcast, RRDSET_FLAG_DETAIL); - - rd_in = rrddim_add(st_ip_mcast, "InMcastOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - rd_out = rrddim_add(st_ip_mcast, "OutMcastOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_ip_mcast); - - rrddim_set_by_pointer(st_ip_mcast, rd_in, ipext_InMcastOctets); - rrddim_set_by_pointer(st_ip_mcast, rd_out, ipext_OutMcastOctets); - - rrdset_done(st_ip_mcast); + if(strcmp(procfile_lineword(ff_snmp, l, 0), "Icmp") != 0) { + error("Cannot read Icmp line from /proc/net/snmp."); + break; } - // -------------------------------------------------------------------- + words = procfile_linewords(ff_snmp, l); + if(words < 3) { + error("Cannot read /proc/net/snmp Icmp line. Expected 3+ params, read %zu.", words); + continue; + } - if(do_bcast == CONFIG_BOOLEAN_YES || (do_bcast == CONFIG_BOOLEAN_AUTO && - (ipext_InBcastOctets || - ipext_OutBcastOctets || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_bcast = CONFIG_BOOLEAN_YES; - - static RRDSET *st_ip_bcast = NULL; - static RRDDIM *rd_in = NULL, *rd_out = NULL; - - if(unlikely(!st_ip_bcast)) { - st_ip_bcast = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "bcast" - , NULL - , "broadcast" - , NULL - , "IP Broadcast Bandwidth" - , "kilobits/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_BCAST - , update_every - , RRDSET_TYPE_AREA - ); - - rrdset_flag_set(st_ip_bcast, RRDSET_FLAG_DETAIL); - - rd_in = rrddim_add(st_ip_bcast, "InBcastOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - rd_out = rrddim_add(st_ip_bcast, "OutBcastOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_ip_bcast); - - rrddim_set_by_pointer(st_ip_bcast, rd_in, ipext_InBcastOctets); - rrddim_set_by_pointer(st_ip_bcast, rd_out, ipext_OutBcastOctets); - - rrdset_done(st_ip_bcast); + arl_begin(arl_icmp); + for(w = 1; w < words ; w++) { + if (unlikely(arl_check(arl_icmp, procfile_lineword(ff_snmp, h, w), procfile_lineword(ff_snmp, l, w)) != 0)) + break; } + } + else if(unlikely(hash == hash_icmpmsg && strcmp(key, "IcmpMsg") == 0)) { + size_t h = l++; - // -------------------------------------------------------------------- + if(strcmp(procfile_lineword(ff_snmp, l, 0), "IcmpMsg") != 0) { + error("Cannot read IcmpMsg line from /proc/net/snmp."); + break; + } - if(do_mcast_p == CONFIG_BOOLEAN_YES || (do_mcast_p == CONFIG_BOOLEAN_AUTO && - (ipext_InMcastPkts || - ipext_OutMcastPkts || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_mcast_p = CONFIG_BOOLEAN_YES; - - static RRDSET *st_ip_mcastpkts = NULL; - static RRDDIM *rd_in = NULL, *rd_out = NULL; - - if(unlikely(!st_ip_mcastpkts)) { - st_ip_mcastpkts = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "mcastpkts" - , NULL - , "multicast" - , NULL - , "IP Multicast Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_MCAST_PACKETS - , update_every - , RRDSET_TYPE_LINE - ); - - rrdset_flag_set(st_ip_mcastpkts, RRDSET_FLAG_DETAIL); - - rd_in = rrddim_add(st_ip_mcastpkts, "InMcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_out = rrddim_add(st_ip_mcastpkts, "OutMcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st_ip_mcastpkts); - - rrddim_set_by_pointer(st_ip_mcastpkts, rd_in, ipext_InMcastPkts); - rrddim_set_by_pointer(st_ip_mcastpkts, rd_out, ipext_OutMcastPkts); - - rrdset_done(st_ip_mcastpkts); + words = procfile_linewords(ff_snmp, l); + if(words < 2) { + error("Cannot read /proc/net/snmp IcmpMsg line. Expected 2+ params, read %zu.", words); + continue; } - // -------------------------------------------------------------------- + arl_begin(arl_icmpmsg); + for(w = 1; w < words ; w++) { + if (unlikely(arl_check(arl_icmpmsg, procfile_lineword(ff_snmp, h, w), procfile_lineword(ff_snmp, l, w)) != 0)) + break; + } + } + else if(unlikely(hash == hash_tcp && strcmp(key, "Tcp") == 0)) { + size_t h = l++; - if(do_bcast_p == CONFIG_BOOLEAN_YES || (do_bcast_p == CONFIG_BOOLEAN_AUTO && - (ipext_InBcastPkts || - ipext_OutBcastPkts || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_bcast_p = CONFIG_BOOLEAN_YES; - - static RRDSET *st_ip_bcastpkts = NULL; - static RRDDIM *rd_in = NULL, *rd_out = NULL; - - if(unlikely(!st_ip_bcastpkts)) { - st_ip_bcastpkts = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "bcastpkts" - , NULL - , "broadcast" - , NULL - , "IP Broadcast Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_BCAST_PACKETS - , update_every - , RRDSET_TYPE_LINE - ); - - rrdset_flag_set(st_ip_bcastpkts, RRDSET_FLAG_DETAIL); - - rd_in = rrddim_add(st_ip_bcastpkts, "InBcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_out = rrddim_add(st_ip_bcastpkts, "OutBcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_ip_bcastpkts); - - rrddim_set_by_pointer(st_ip_bcastpkts, rd_in, ipext_InBcastPkts); - rrddim_set_by_pointer(st_ip_bcastpkts, rd_out, ipext_OutBcastPkts); - - rrdset_done(st_ip_bcastpkts); + if(strcmp(procfile_lineword(ff_snmp, l, 0), "Tcp") != 0) { + error("Cannot read Tcp line from /proc/net/snmp."); + break; } - // -------------------------------------------------------------------- + words = procfile_linewords(ff_snmp, l); + if(words < 3) { + error("Cannot read /proc/net/snmp Tcp line. Expected 3+ params, read %zu.", words); + continue; + } - if(do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && - (ipext_InCEPkts || - ipext_InECT0Pkts || - ipext_InECT1Pkts || - ipext_InNoECTPkts || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ecn = CONFIG_BOOLEAN_YES; - - static RRDSET *st_ecnpkts = NULL; - static RRDDIM *rd_cep = NULL, *rd_noectp = NULL, *rd_ectp0 = NULL, *rd_ectp1 = NULL; - - if(unlikely(!st_ecnpkts)) { - st_ecnpkts = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "ecnpkts" - , NULL - , "ecn" - , NULL - , "IP ECN Statistics" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_ECN - , update_every - , RRDSET_TYPE_LINE - ); - - rrdset_flag_set(st_ecnpkts, RRDSET_FLAG_DETAIL); - - rd_cep = rrddim_add(st_ecnpkts, "InCEPkts", "CEP", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_noectp = rrddim_add(st_ecnpkts, "InNoECTPkts", "NoECTP", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_ectp0 = rrddim_add(st_ecnpkts, "InECT0Pkts", "ECTP0", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_ectp1 = rrddim_add(st_ecnpkts, "InECT1Pkts", "ECTP1", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st_ecnpkts); - - rrddim_set_by_pointer(st_ecnpkts, rd_cep, ipext_InCEPkts); - rrddim_set_by_pointer(st_ecnpkts, rd_noectp, ipext_InNoECTPkts); - rrddim_set_by_pointer(st_ecnpkts, rd_ectp0, ipext_InECT0Pkts); - rrddim_set_by_pointer(st_ecnpkts, rd_ectp1, ipext_InECT1Pkts); - - rrdset_done(st_ecnpkts); + arl_begin(arl_tcp); + for(w = 1; w < words ; w++) { + if (unlikely(arl_check(arl_tcp, procfile_lineword(ff_snmp, h, w), procfile_lineword(ff_snmp, l, w)) != 0)) + break; } } - else if(unlikely(hash == hash_tcpext && strcmp(key, "TcpExt") == 0)) { + else if(unlikely(hash == hash_udp && strcmp(key, "Udp") == 0)) { size_t h = l++; - words = procfile_linewords(ff, l); - if(unlikely(words < 2)) { - error("Cannot read /proc/net/netstat TcpExt line. Expected 2+ params, read %zu.", words); + if(strcmp(procfile_lineword(ff_snmp, l, 0), "Udp") != 0) { + error("Cannot read Udp line from /proc/net/snmp."); + break; + } + + words = procfile_linewords(ff_snmp, l); + if(words < 3) { + error("Cannot read /proc/net/snmp Udp line. Expected 3+ params, read %zu.", words); continue; } - parse_line_pair(ff, arl_tcpext, h, l); - - // -------------------------------------------------------------------- - - if(do_tcpext_memory == CONFIG_BOOLEAN_YES || (do_tcpext_memory == CONFIG_BOOLEAN_AUTO && - (tcpext_TCPMemoryPressures || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcpext_memory = CONFIG_BOOLEAN_YES; - - static RRDSET *st_tcpmemorypressures = NULL; - static RRDDIM *rd_pressures = NULL; - - if(unlikely(!st_tcpmemorypressures)) { - st_tcpmemorypressures = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "tcpmemorypressures" - , NULL - , "tcp" - , NULL - , "TCP Memory Pressures" - , "events/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_TCP_MEM - , update_every - , RRDSET_TYPE_LINE - ); - - rd_pressures = rrddim_add(st_tcpmemorypressures, "TCPMemoryPressures", "pressures", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_tcpmemorypressures); - - rrddim_set_by_pointer(st_tcpmemorypressures, rd_pressures, tcpext_TCPMemoryPressures); - - rrdset_done(st_tcpmemorypressures); + arl_begin(arl_udp); + for(w = 1; w < words ; w++) { + if (unlikely(arl_check(arl_udp, procfile_lineword(ff_snmp, h, w), procfile_lineword(ff_snmp, l, w)) != 0)) + break; } + } + else if(unlikely(hash == hash_udplite && strcmp(key, "UdpLite") == 0)) { + size_t h = l++; - // -------------------------------------------------------------------- - - if(do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && - (tcpext_TCPAbortOnData || - tcpext_TCPAbortOnClose || - tcpext_TCPAbortOnMemory || - tcpext_TCPAbortOnTimeout || - tcpext_TCPAbortOnLinger || - tcpext_TCPAbortFailed || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcpext_connaborts = CONFIG_BOOLEAN_YES; - - static RRDSET *st_tcpconnaborts = NULL; - static RRDDIM *rd_baddata = NULL, *rd_userclosed = NULL, *rd_nomemory = NULL, *rd_timeout = NULL, *rd_linger = NULL, *rd_failed = NULL; - - if(unlikely(!st_tcpconnaborts)) { - st_tcpconnaborts = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "tcpconnaborts" - , NULL - , "tcp" - , NULL - , "TCP Connection Aborts" - , "connections/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_TCP_CONNABORTS - , update_every - , RRDSET_TYPE_LINE - ); - - rd_baddata = rrddim_add(st_tcpconnaborts, "TCPAbortOnData", "baddata", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_userclosed = rrddim_add(st_tcpconnaborts, "TCPAbortOnClose", "userclosed", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_nomemory = rrddim_add(st_tcpconnaborts, "TCPAbortOnMemory", "nomemory", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_timeout = rrddim_add(st_tcpconnaborts, "TCPAbortOnTimeout", "timeout", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_linger = rrddim_add(st_tcpconnaborts, "TCPAbortOnLinger", "linger", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_failed = rrddim_add(st_tcpconnaborts, "TCPAbortFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_tcpconnaborts); - - rrddim_set_by_pointer(st_tcpconnaborts, rd_baddata, tcpext_TCPAbortOnData); - rrddim_set_by_pointer(st_tcpconnaborts, rd_userclosed, tcpext_TCPAbortOnClose); - rrddim_set_by_pointer(st_tcpconnaborts, rd_nomemory, tcpext_TCPAbortOnMemory); - rrddim_set_by_pointer(st_tcpconnaborts, rd_timeout, tcpext_TCPAbortOnTimeout); - rrddim_set_by_pointer(st_tcpconnaborts, rd_linger, tcpext_TCPAbortOnLinger); - rrddim_set_by_pointer(st_tcpconnaborts, rd_failed, tcpext_TCPAbortFailed); - - rrdset_done(st_tcpconnaborts); + if(strcmp(procfile_lineword(ff_snmp, l, 0), "UdpLite") != 0) { + error("Cannot read UdpLite line from /proc/net/snmp."); + break; } - // -------------------------------------------------------------------- + words = procfile_linewords(ff_snmp, l); + if(words < 3) { + error("Cannot read /proc/net/snmp UdpLite line. Expected 3+ params, read %zu.", words); + continue; + } - if(do_tcpext_reorder == CONFIG_BOOLEAN_YES || (do_tcpext_reorder == CONFIG_BOOLEAN_AUTO && - (tcpext_TCPRenoReorder || - tcpext_TCPFACKReorder || - tcpext_TCPSACKReorder || - tcpext_TCPTSReorder || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcpext_reorder = CONFIG_BOOLEAN_YES; - - static RRDSET *st_tcpreorders = NULL; - static RRDDIM *rd_timestamp = NULL, *rd_sack = NULL, *rd_fack = NULL, *rd_reno = NULL; - - if(unlikely(!st_tcpreorders)) { - st_tcpreorders = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "tcpreorders" - , NULL - , "tcp" - , NULL - , "TCP Reordered Packets by Detection Method" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_TCP_REORDERS - , update_every - , RRDSET_TYPE_LINE - ); - - rd_timestamp = rrddim_add(st_tcpreorders, "TCPTSReorder", "timestamp", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_sack = rrddim_add(st_tcpreorders, "TCPSACKReorder", "sack", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_fack = rrddim_add(st_tcpreorders, "TCPFACKReorder", "fack", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_reno = rrddim_add(st_tcpreorders, "TCPRenoReorder", "reno", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_tcpreorders); - - rrddim_set_by_pointer(st_tcpreorders, rd_timestamp, tcpext_TCPTSReorder); - rrddim_set_by_pointer(st_tcpreorders, rd_sack, tcpext_TCPSACKReorder); - rrddim_set_by_pointer(st_tcpreorders, rd_fack, tcpext_TCPFACKReorder); - rrddim_set_by_pointer(st_tcpreorders, rd_reno, tcpext_TCPRenoReorder); - - rrdset_done(st_tcpreorders); + arl_begin(arl_udplite); + for(w = 1; w < words ; w++) { + if (unlikely(arl_check(arl_udplite, procfile_lineword(ff_snmp, h, w), procfile_lineword(ff_snmp, l, w)) != 0)) + break; } + } + } + + // parse /proc/net/snmp + + if(unlikely(!ff_snmp6)) { + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/snmp6"); + ff_snmp6 = procfile_open(config_get("plugin:proc:/proc/net/snmp6", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff_snmp6)) + return 1; + } + + ff_snmp6 = procfile_readall(ff_snmp6); + if(unlikely(!ff_snmp6)) + return 0; // we return 0, so that we will retry to open it next time - // -------------------------------------------------------------------- + lines = procfile_lines(ff_snmp6); - if(do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && - (tcpext_TCPOFOQueue || - tcpext_TCPOFODrop || - tcpext_TCPOFOMerge || + arl_begin(arl_ipv6); + + for(l = 0; l < lines ;l++) { + size_t words = procfile_linewords(ff_snmp6, l); + if(unlikely(words < 2)) { + if(unlikely(words)) error("Cannot read /proc/net/snmp6 line %zu. Expected 2 params, read %zu.", l, words); + continue; + } + + if(unlikely(arl_check(arl_ipv6, + procfile_lineword(ff_snmp6, l, 0), + procfile_lineword(ff_snmp6, l, 1)))) break; + } + + // netstat IpExt charts + + if(do_bandwidth == CONFIG_BOOLEAN_YES || (do_bandwidth == CONFIG_BOOLEAN_AUTO && + (ipext_InOctets || + ipext_OutOctets || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_bandwidth = CONFIG_BOOLEAN_YES; + static RRDSET *st_system_ip = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; + + if(unlikely(!st_system_ip)) { + st_system_ip = rrdset_create_localhost( + "system" + , RRD_TYPE_NET_NETSTAT + , NULL + , "network" + , NULL + , "IP Bandwidth" + , "kilobits/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_SYSTEM_IP + , update_every + , RRDSET_TYPE_AREA + ); + + rd_in = rrddim_add(st_system_ip, "InOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st_system_ip, "OutOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_system_ip, rd_in, ipext_InOctets); + rrddim_set_by_pointer(st_system_ip, rd_out, ipext_OutOctets); + rrdset_done(st_system_ip); + } + + if(do_inerrors == CONFIG_BOOLEAN_YES || (do_inerrors == CONFIG_BOOLEAN_AUTO && + (ipext_InNoRoutes || + ipext_InTruncatedPkts || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_inerrors = CONFIG_BOOLEAN_YES; + static RRDSET *st_ip_inerrors = NULL; + static RRDDIM *rd_noroutes = NULL, *rd_truncated = NULL, *rd_checksum = NULL; + + if(unlikely(!st_ip_inerrors)) { + st_ip_inerrors = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "inerrors" + , NULL + , "errors" + , NULL + , "IP Input Errors" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_ERRORS + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st_ip_inerrors, RRDSET_FLAG_DETAIL); + + rd_noroutes = rrddim_add(st_ip_inerrors, "InNoRoutes", "noroutes", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_truncated = rrddim_add(st_ip_inerrors, "InTruncatedPkts", "truncated", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_checksum = rrddim_add(st_ip_inerrors, "InCsumErrors", "checksum", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_ip_inerrors, rd_noroutes, ipext_InNoRoutes); + rrddim_set_by_pointer(st_ip_inerrors, rd_truncated, ipext_InTruncatedPkts); + rrddim_set_by_pointer(st_ip_inerrors, rd_checksum, ipext_InCsumErrors); + rrdset_done(st_ip_inerrors); + } + + if(do_mcast == CONFIG_BOOLEAN_YES || (do_mcast == CONFIG_BOOLEAN_AUTO && + (ipext_InMcastOctets || + ipext_OutMcastOctets || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_mcast = CONFIG_BOOLEAN_YES; + static RRDSET *st_ip_mcast = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; + + if(unlikely(!st_ip_mcast)) { + st_ip_mcast = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "mcast" + , NULL + , "multicast" + , NULL + , "IP Multicast Bandwidth" + , "kilobits/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_MCAST + , update_every + , RRDSET_TYPE_AREA + ); + + rrdset_flag_set(st_ip_mcast, RRDSET_FLAG_DETAIL); + + rd_in = rrddim_add(st_ip_mcast, "InMcastOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st_ip_mcast, "OutMcastOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_ip_mcast, rd_in, ipext_InMcastOctets); + rrddim_set_by_pointer(st_ip_mcast, rd_out, ipext_OutMcastOctets); + + rrdset_done(st_ip_mcast); + } + + // -------------------------------------------------------------------- + + if(do_bcast == CONFIG_BOOLEAN_YES || (do_bcast == CONFIG_BOOLEAN_AUTO && + (ipext_InBcastOctets || + ipext_OutBcastOctets || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_bcast = CONFIG_BOOLEAN_YES; + + static RRDSET *st_ip_bcast = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; + + if(unlikely(!st_ip_bcast)) { + st_ip_bcast = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "bcast" + , NULL + , "broadcast" + , NULL + , "IP Broadcast Bandwidth" + , "kilobits/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_BCAST + , update_every + , RRDSET_TYPE_AREA + ); + + rrdset_flag_set(st_ip_bcast, RRDSET_FLAG_DETAIL); + + rd_in = rrddim_add(st_ip_bcast, "InBcastOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st_ip_bcast, "OutBcastOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_ip_bcast, rd_in, ipext_InBcastOctets); + rrddim_set_by_pointer(st_ip_bcast, rd_out, ipext_OutBcastOctets); + + rrdset_done(st_ip_bcast); + } + + // -------------------------------------------------------------------- + + if(do_mcast_p == CONFIG_BOOLEAN_YES || (do_mcast_p == CONFIG_BOOLEAN_AUTO && + (ipext_InMcastPkts || + ipext_OutMcastPkts || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_mcast_p = CONFIG_BOOLEAN_YES; + + static RRDSET *st_ip_mcastpkts = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; + + if(unlikely(!st_ip_mcastpkts)) { + st_ip_mcastpkts = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "mcastpkts" + , NULL + , "multicast" + , NULL + , "IP Multicast Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_MCAST_PACKETS + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st_ip_mcastpkts, RRDSET_FLAG_DETAIL); + + rd_in = rrddim_add(st_ip_mcastpkts, "InMcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st_ip_mcastpkts, "OutMcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_ip_mcastpkts, rd_in, ipext_InMcastPkts); + rrddim_set_by_pointer(st_ip_mcastpkts, rd_out, ipext_OutMcastPkts); + rrdset_done(st_ip_mcastpkts); + } + + if(do_bcast_p == CONFIG_BOOLEAN_YES || (do_bcast_p == CONFIG_BOOLEAN_AUTO && + (ipext_InBcastPkts || + ipext_OutBcastPkts || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_bcast_p = CONFIG_BOOLEAN_YES; + + static RRDSET *st_ip_bcastpkts = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; + + if(unlikely(!st_ip_bcastpkts)) { + st_ip_bcastpkts = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "bcastpkts" + , NULL + , "broadcast" + , NULL + , "IP Broadcast Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_BCAST_PACKETS + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st_ip_bcastpkts, RRDSET_FLAG_DETAIL); + + rd_in = rrddim_add(st_ip_bcastpkts, "InBcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st_ip_bcastpkts, "OutBcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_ip_bcastpkts, rd_in, ipext_InBcastPkts); + rrddim_set_by_pointer(st_ip_bcastpkts, rd_out, ipext_OutBcastPkts); + rrdset_done(st_ip_bcastpkts); + } + + if(do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && + (ipext_InCEPkts || + ipext_InECT0Pkts || + ipext_InECT1Pkts || + ipext_InNoECTPkts || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ecn = CONFIG_BOOLEAN_YES; + + static RRDSET *st_ecnpkts = NULL; + static RRDDIM *rd_cep = NULL, *rd_noectp = NULL, *rd_ectp0 = NULL, *rd_ectp1 = NULL; + + if(unlikely(!st_ecnpkts)) { + st_ecnpkts = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "ecnpkts" + , NULL + , "ecn" + , NULL + , "IP ECN Statistics" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_ECN + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st_ecnpkts, RRDSET_FLAG_DETAIL); + + rd_cep = rrddim_add(st_ecnpkts, "InCEPkts", "CEP", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_noectp = rrddim_add(st_ecnpkts, "InNoECTPkts", "NoECTP", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ectp0 = rrddim_add(st_ecnpkts, "InECT0Pkts", "ECTP0", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ectp1 = rrddim_add(st_ecnpkts, "InECT1Pkts", "ECTP1", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_ecnpkts, rd_cep, ipext_InCEPkts); + rrddim_set_by_pointer(st_ecnpkts, rd_noectp, ipext_InNoECTPkts); + rrddim_set_by_pointer(st_ecnpkts, rd_ectp0, ipext_InECT0Pkts); + rrddim_set_by_pointer(st_ecnpkts, rd_ectp1, ipext_InECT1Pkts); + rrdset_done(st_ecnpkts); + } + + // netstat TcpExt charts + + if(do_tcpext_memory == CONFIG_BOOLEAN_YES || (do_tcpext_memory == CONFIG_BOOLEAN_AUTO && + (tcpext_TCPMemoryPressures || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcpext_memory = CONFIG_BOOLEAN_YES; + + static RRDSET *st_tcpmemorypressures = NULL; + static RRDDIM *rd_pressures = NULL; + + if(unlikely(!st_tcpmemorypressures)) { + st_tcpmemorypressures = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "tcpmemorypressures" + , NULL + , "tcp" + , NULL + , "TCP Memory Pressures" + , "events/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_TCP_MEM + , update_every + , RRDSET_TYPE_LINE + ); + + rd_pressures = rrddim_add(st_tcpmemorypressures, "TCPMemoryPressures", "pressures", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_tcpmemorypressures, rd_pressures, tcpext_TCPMemoryPressures); + rrdset_done(st_tcpmemorypressures); + } + + if(do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && + (tcpext_TCPAbortOnData || + tcpext_TCPAbortOnClose || + tcpext_TCPAbortOnMemory || + tcpext_TCPAbortOnTimeout || + tcpext_TCPAbortOnLinger || + tcpext_TCPAbortFailed || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcpext_ofo = CONFIG_BOOLEAN_YES; - - static RRDSET *st_ip_tcpofo = NULL; - static RRDDIM *rd_inqueue = NULL, *rd_dropped = NULL, *rd_merged = NULL, *rd_pruned = NULL; - - if(unlikely(!st_ip_tcpofo)) { - - st_ip_tcpofo = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "tcpofo" - , NULL - , "tcp" - , NULL - , "TCP Out-Of-Order Queue" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_TCP_OFO - , update_every - , RRDSET_TYPE_LINE - ); - - rd_inqueue = rrddim_add(st_ip_tcpofo, "TCPOFOQueue", "inqueue", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_dropped = rrddim_add(st_ip_tcpofo, "TCPOFODrop", "dropped", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_merged = rrddim_add(st_ip_tcpofo, "TCPOFOMerge", "merged", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_pruned = rrddim_add(st_ip_tcpofo, "OfoPruned", "pruned", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_ip_tcpofo); - - rrddim_set_by_pointer(st_ip_tcpofo, rd_inqueue, tcpext_TCPOFOQueue); - rrddim_set_by_pointer(st_ip_tcpofo, rd_dropped, tcpext_TCPOFODrop); - rrddim_set_by_pointer(st_ip_tcpofo, rd_merged, tcpext_TCPOFOMerge); - rrddim_set_by_pointer(st_ip_tcpofo, rd_pruned, tcpext_OfoPruned); - - rrdset_done(st_ip_tcpofo); + do_tcpext_connaborts = CONFIG_BOOLEAN_YES; + + static RRDSET *st_tcpconnaborts = NULL; + static RRDDIM *rd_baddata = NULL, *rd_userclosed = NULL, *rd_nomemory = NULL, *rd_timeout = NULL, *rd_linger = NULL, *rd_failed = NULL; + + if(unlikely(!st_tcpconnaborts)) { + st_tcpconnaborts = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "tcpconnaborts" + , NULL + , "tcp" + , NULL + , "TCP Connection Aborts" + , "connections/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_TCP_CONNABORTS + , update_every + , RRDSET_TYPE_LINE + ); + + rd_baddata = rrddim_add(st_tcpconnaborts, "TCPAbortOnData", "baddata", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_userclosed = rrddim_add(st_tcpconnaborts, "TCPAbortOnClose", "userclosed", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_nomemory = rrddim_add(st_tcpconnaborts, "TCPAbortOnMemory", "nomemory", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_timeout = rrddim_add(st_tcpconnaborts, "TCPAbortOnTimeout", "timeout", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_linger = rrddim_add(st_tcpconnaborts, "TCPAbortOnLinger", "linger", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_failed = rrddim_add(st_tcpconnaborts, "TCPAbortFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_tcpconnaborts, rd_baddata, tcpext_TCPAbortOnData); + rrddim_set_by_pointer(st_tcpconnaborts, rd_userclosed, tcpext_TCPAbortOnClose); + rrddim_set_by_pointer(st_tcpconnaborts, rd_nomemory, tcpext_TCPAbortOnMemory); + rrddim_set_by_pointer(st_tcpconnaborts, rd_timeout, tcpext_TCPAbortOnTimeout); + rrddim_set_by_pointer(st_tcpconnaborts, rd_linger, tcpext_TCPAbortOnLinger); + rrddim_set_by_pointer(st_tcpconnaborts, rd_failed, tcpext_TCPAbortFailed); + rrdset_done(st_tcpconnaborts); + } + + if(do_tcpext_reorder == CONFIG_BOOLEAN_YES || (do_tcpext_reorder == CONFIG_BOOLEAN_AUTO && + (tcpext_TCPRenoReorder || + tcpext_TCPFACKReorder || + tcpext_TCPSACKReorder || + tcpext_TCPTSReorder || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcpext_reorder = CONFIG_BOOLEAN_YES; + + static RRDSET *st_tcpreorders = NULL; + static RRDDIM *rd_timestamp = NULL, *rd_sack = NULL, *rd_fack = NULL, *rd_reno = NULL; + + if(unlikely(!st_tcpreorders)) { + st_tcpreorders = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "tcpreorders" + , NULL + , "tcp" + , NULL + , "TCP Reordered Packets by Detection Method" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_TCP_REORDERS + , update_every + , RRDSET_TYPE_LINE + ); + + rd_timestamp = rrddim_add(st_tcpreorders, "TCPTSReorder", "timestamp", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_sack = rrddim_add(st_tcpreorders, "TCPSACKReorder", "sack", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_fack = rrddim_add(st_tcpreorders, "TCPFACKReorder", "fack", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_reno = rrddim_add(st_tcpreorders, "TCPRenoReorder", "reno", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_tcpreorders, rd_timestamp, tcpext_TCPTSReorder); + rrddim_set_by_pointer(st_tcpreorders, rd_sack, tcpext_TCPSACKReorder); + rrddim_set_by_pointer(st_tcpreorders, rd_fack, tcpext_TCPFACKReorder); + rrddim_set_by_pointer(st_tcpreorders, rd_reno, tcpext_TCPRenoReorder); + rrdset_done(st_tcpreorders); + } + + // -------------------------------------------------------------------- + + if(do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && + (tcpext_TCPOFOQueue || + tcpext_TCPOFODrop || + tcpext_TCPOFOMerge || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcpext_ofo = CONFIG_BOOLEAN_YES; + + static RRDSET *st_ip_tcpofo = NULL; + static RRDDIM *rd_inqueue = NULL, *rd_dropped = NULL, *rd_merged = NULL, *rd_pruned = NULL; + + if(unlikely(!st_ip_tcpofo)) { + + st_ip_tcpofo = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "tcpofo" + , NULL + , "tcp" + , NULL + , "TCP Out-Of-Order Queue" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_TCP_OFO + , update_every + , RRDSET_TYPE_LINE + ); + + rd_inqueue = rrddim_add(st_ip_tcpofo, "TCPOFOQueue", "inqueue", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_dropped = rrddim_add(st_ip_tcpofo, "TCPOFODrop", "dropped", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_merged = rrddim_add(st_ip_tcpofo, "TCPOFOMerge", "merged", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_pruned = rrddim_add(st_ip_tcpofo, "OfoPruned", "pruned", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_ip_tcpofo, rd_inqueue, tcpext_TCPOFOQueue); + rrddim_set_by_pointer(st_ip_tcpofo, rd_dropped, tcpext_TCPOFODrop); + rrddim_set_by_pointer(st_ip_tcpofo, rd_merged, tcpext_TCPOFOMerge); + rrddim_set_by_pointer(st_ip_tcpofo, rd_pruned, tcpext_OfoPruned); + rrdset_done(st_ip_tcpofo); + } + + if(do_tcpext_syscookies == CONFIG_BOOLEAN_YES || (do_tcpext_syscookies == CONFIG_BOOLEAN_AUTO && + (tcpext_SyncookiesSent || + tcpext_SyncookiesRecv || + tcpext_SyncookiesFailed || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcpext_syscookies = CONFIG_BOOLEAN_YES; + + static RRDSET *st_syncookies = NULL; + static RRDDIM *rd_received = NULL, *rd_sent = NULL, *rd_failed = NULL; + + if(unlikely(!st_syncookies)) { + + st_syncookies = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "tcpsyncookies" + , NULL + , "tcp" + , NULL + , "TCP SYN Cookies" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_TCP_SYNCOOKIES + , update_every + , RRDSET_TYPE_LINE + ); + + rd_received = rrddim_add(st_syncookies, "SyncookiesRecv", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_sent = rrddim_add(st_syncookies, "SyncookiesSent", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_failed = rrddim_add(st_syncookies, "SyncookiesFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_syncookies, rd_received, tcpext_SyncookiesRecv); + rrddim_set_by_pointer(st_syncookies, rd_sent, tcpext_SyncookiesSent); + rrddim_set_by_pointer(st_syncookies, rd_failed, tcpext_SyncookiesFailed); + rrdset_done(st_syncookies); + } + + if(do_tcpext_syn_queue == CONFIG_BOOLEAN_YES || (do_tcpext_syn_queue == CONFIG_BOOLEAN_AUTO && + (tcpext_TCPReqQFullDrop || + tcpext_TCPReqQFullDoCookies || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcpext_syn_queue = CONFIG_BOOLEAN_YES; + + static RRDSET *st_syn_queue = NULL; + static RRDDIM + *rd_TCPReqQFullDrop = NULL, + *rd_TCPReqQFullDoCookies = NULL; + + if(unlikely(!st_syn_queue)) { + + st_syn_queue = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "tcp_syn_queue" + , NULL + , "tcp" + , NULL + , "TCP SYN Queue Issues" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_TCP_SYN_QUEUE + , update_every + , RRDSET_TYPE_LINE + ); + + rd_TCPReqQFullDrop = rrddim_add(st_syn_queue, "TCPReqQFullDrop", "drops", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_TCPReqQFullDoCookies = rrddim_add(st_syn_queue, "TCPReqQFullDoCookies", "cookies", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_syn_queue, rd_TCPReqQFullDrop, tcpext_TCPReqQFullDrop); + rrddim_set_by_pointer(st_syn_queue, rd_TCPReqQFullDoCookies, tcpext_TCPReqQFullDoCookies); + rrdset_done(st_syn_queue); + } + + if(do_tcpext_accept_queue == CONFIG_BOOLEAN_YES || (do_tcpext_accept_queue == CONFIG_BOOLEAN_AUTO && + (tcpext_ListenOverflows || + tcpext_ListenDrops || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcpext_accept_queue = CONFIG_BOOLEAN_YES; + + static RRDSET *st_accept_queue = NULL; + static RRDDIM *rd_overflows = NULL, + *rd_drops = NULL; + + if(unlikely(!st_accept_queue)) { + + st_accept_queue = rrdset_create_localhost( + RRD_TYPE_NET_NETSTAT + , "tcp_accept_queue" + , NULL + , "tcp" + , NULL + , "TCP Accept Queue Issues" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IP_TCP_ACCEPT_QUEUE + , update_every + , RRDSET_TYPE_LINE + ); + + rd_overflows = rrddim_add(st_accept_queue, "ListenOverflows", "overflows", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_drops = rrddim_add(st_accept_queue, "ListenDrops", "drops", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_accept_queue, rd_overflows, tcpext_ListenOverflows); + rrddim_set_by_pointer(st_accept_queue, rd_drops, tcpext_ListenDrops); + rrdset_done(st_accept_queue); + } + + // snmp Ip charts + + if(do_ip_packets == CONFIG_BOOLEAN_YES || (do_ip_packets == CONFIG_BOOLEAN_AUTO && + (snmp_root.ip_OutRequests || + snmp_root.ip_InReceives || + snmp_root.ip_ForwDatagrams || + snmp_root.ip_InDelivers || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip_packets = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_InReceives = NULL, + *rd_OutRequests = NULL, + *rd_ForwDatagrams = NULL, + *rd_InDelivers = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "packets" + , NULL + , "packets" + , NULL + , "IPv4 Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_PACKETS + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InReceives = rrddim_add(st, "InReceives", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutRequests = rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ForwDatagrams = rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InDelivers = rrddim_add(st, "InDelivers", "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_OutRequests, (collected_number)snmp_root.ip_OutRequests); + rrddim_set_by_pointer(st, rd_InReceives, (collected_number)snmp_root.ip_InReceives); + rrddim_set_by_pointer(st, rd_ForwDatagrams, (collected_number)snmp_root.ip_ForwDatagrams); + rrddim_set_by_pointer(st, rd_InDelivers, (collected_number)snmp_root.ip_InDelivers); + rrdset_done(st); + } + + if(do_ip_fragsout == CONFIG_BOOLEAN_YES || (do_ip_fragsout == CONFIG_BOOLEAN_AUTO && + (snmp_root.ip_FragOKs || + snmp_root.ip_FragFails || + snmp_root.ip_FragCreates || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip_fragsout = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_FragOKs = NULL, + *rd_FragFails = NULL, + *rd_FragCreates = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "fragsout" + , NULL + , "fragments" + , NULL + , "IPv4 Fragments Sent" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_FRAGMENTS + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_FragOKs = rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_FragFails = rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_FragCreates = rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_FragOKs, (collected_number)snmp_root.ip_FragOKs); + rrddim_set_by_pointer(st, rd_FragFails, (collected_number)snmp_root.ip_FragFails); + rrddim_set_by_pointer(st, rd_FragCreates, (collected_number)snmp_root.ip_FragCreates); + rrdset_done(st); + } + + if(do_ip_fragsin == CONFIG_BOOLEAN_YES || (do_ip_fragsin == CONFIG_BOOLEAN_AUTO && + (snmp_root.ip_ReasmOKs || + snmp_root.ip_ReasmFails || + snmp_root.ip_ReasmReqds || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip_fragsin = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_ReasmOKs = NULL, + *rd_ReasmFails = NULL, + *rd_ReasmReqds = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "fragsin" + , NULL + , "fragments" + , NULL + , "IPv4 Fragments Reassembly" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_FRAGMENTS + 1 + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_ReasmOKs = rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ReasmFails = rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ReasmReqds = rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_ReasmOKs, (collected_number)snmp_root.ip_ReasmOKs); + rrddim_set_by_pointer(st, rd_ReasmFails, (collected_number)snmp_root.ip_ReasmFails); + rrddim_set_by_pointer(st, rd_ReasmReqds, (collected_number)snmp_root.ip_ReasmReqds); + rrdset_done(st); + } + + if(do_ip_errors == CONFIG_BOOLEAN_YES || (do_ip_errors == CONFIG_BOOLEAN_AUTO && + (snmp_root.ip_InDiscards || + snmp_root.ip_OutDiscards || + snmp_root.ip_InHdrErrors || + snmp_root.ip_InAddrErrors || + snmp_root.ip_InUnknownProtos || + snmp_root.ip_OutNoRoutes || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip_errors = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_InDiscards = NULL, + *rd_OutDiscards = NULL, + *rd_InHdrErrors = NULL, + *rd_OutNoRoutes = NULL, + *rd_InAddrErrors = NULL, + *rd_InUnknownProtos = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "errors" + , NULL + , "errors" + , NULL + , "IPv4 Errors" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_ERRORS + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_InDiscards = rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutDiscards = rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rd_InHdrErrors = rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutNoRoutes = rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rd_InAddrErrors = rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InUnknownProtos = rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InDiscards, (collected_number)snmp_root.ip_InDiscards); + rrddim_set_by_pointer(st, rd_OutDiscards, (collected_number)snmp_root.ip_OutDiscards); + rrddim_set_by_pointer(st, rd_InHdrErrors, (collected_number)snmp_root.ip_InHdrErrors); + rrddim_set_by_pointer(st, rd_InAddrErrors, (collected_number)snmp_root.ip_InAddrErrors); + rrddim_set_by_pointer(st, rd_InUnknownProtos, (collected_number)snmp_root.ip_InUnknownProtos); + rrddim_set_by_pointer(st, rd_OutNoRoutes, (collected_number)snmp_root.ip_OutNoRoutes); + rrdset_done(st); + } + + // snmp Icmp charts + + if(do_icmp_packets == CONFIG_BOOLEAN_YES || (do_icmp_packets == CONFIG_BOOLEAN_AUTO && + (snmp_root.icmp_InMsgs || + snmp_root.icmp_OutMsgs || + snmp_root.icmp_InErrors || + snmp_root.icmp_OutErrors || + snmp_root.icmp_InCsumErrors || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_icmp_packets = CONFIG_BOOLEAN_YES; + + { + static RRDSET *st_packets = NULL; + static RRDDIM *rd_InMsgs = NULL, + *rd_OutMsgs = NULL; + + if(unlikely(!st_packets)) { + st_packets = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "icmp" + , NULL + , "icmp" + , NULL + , "IPv4 ICMP Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_ICMP + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InMsgs = rrddim_add(st_packets, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutMsgs = rrddim_add(st_packets, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } - // -------------------------------------------------------------------- - - if(do_tcpext_syscookies == CONFIG_BOOLEAN_YES || (do_tcpext_syscookies == CONFIG_BOOLEAN_AUTO && - (tcpext_SyncookiesSent || - tcpext_SyncookiesRecv || - tcpext_SyncookiesFailed || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcpext_syscookies = CONFIG_BOOLEAN_YES; - - static RRDSET *st_syncookies = NULL; - static RRDDIM *rd_received = NULL, *rd_sent = NULL, *rd_failed = NULL; - - if(unlikely(!st_syncookies)) { - - st_syncookies = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "tcpsyncookies" - , NULL - , "tcp" - , NULL - , "TCP SYN Cookies" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_TCP_SYNCOOKIES - , update_every - , RRDSET_TYPE_LINE - ); - - rd_received = rrddim_add(st_syncookies, "SyncookiesRecv", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_sent = rrddim_add(st_syncookies, "SyncookiesSent", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_failed = rrddim_add(st_syncookies, "SyncookiesFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_syncookies); - - rrddim_set_by_pointer(st_syncookies, rd_received, tcpext_SyncookiesRecv); - rrddim_set_by_pointer(st_syncookies, rd_sent, tcpext_SyncookiesSent); - rrddim_set_by_pointer(st_syncookies, rd_failed, tcpext_SyncookiesFailed); - - rrdset_done(st_syncookies); + rrddim_set_by_pointer(st_packets, rd_InMsgs, (collected_number)snmp_root.icmp_InMsgs); + rrddim_set_by_pointer(st_packets, rd_OutMsgs, (collected_number)snmp_root.icmp_OutMsgs); + rrdset_done(st_packets); + } + + { + static RRDSET *st_errors = NULL; + static RRDDIM *rd_InErrors = NULL, + *rd_OutErrors = NULL, + *rd_InCsumErrors = NULL; + + if(unlikely(!st_errors)) { + st_errors = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "icmp_errors" + , NULL + , "icmp" + , NULL + , "IPv4 ICMP Errors" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_ICMP + 1 + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InErrors = rrddim_add(st_errors, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutErrors = rrddim_add(st_errors, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InCsumErrors = rrddim_add(st_errors, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - // -------------------------------------------------------------------- - - if(do_tcpext_syn_queue == CONFIG_BOOLEAN_YES || (do_tcpext_syn_queue == CONFIG_BOOLEAN_AUTO && - (tcpext_TCPReqQFullDrop || - tcpext_TCPReqQFullDoCookies || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcpext_syn_queue = CONFIG_BOOLEAN_YES; - - static RRDSET *st_syn_queue = NULL; - static RRDDIM - *rd_TCPReqQFullDrop = NULL, - *rd_TCPReqQFullDoCookies = NULL; - - if(unlikely(!st_syn_queue)) { - - st_syn_queue = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "tcp_syn_queue" - , NULL - , "tcp" - , NULL - , "TCP SYN Queue Issues" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_TCP_SYN_QUEUE - , update_every - , RRDSET_TYPE_LINE - ); - - rd_TCPReqQFullDrop = rrddim_add(st_syn_queue, "TCPReqQFullDrop", "drops", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_TCPReqQFullDoCookies = rrddim_add(st_syn_queue, "TCPReqQFullDoCookies", "cookies", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_syn_queue); - - rrddim_set_by_pointer(st_syn_queue, rd_TCPReqQFullDrop, tcpext_TCPReqQFullDrop); - rrddim_set_by_pointer(st_syn_queue, rd_TCPReqQFullDoCookies, tcpext_TCPReqQFullDoCookies); - - rrdset_done(st_syn_queue); + rrddim_set_by_pointer(st_errors, rd_InErrors, (collected_number)snmp_root.icmp_InErrors); + rrddim_set_by_pointer(st_errors, rd_OutErrors, (collected_number)snmp_root.icmp_OutErrors); + rrddim_set_by_pointer(st_errors, rd_InCsumErrors, (collected_number)snmp_root.icmp_InCsumErrors); + rrdset_done(st_errors); + } + } + + // snmp IcmpMsg charts + + if(do_icmpmsg == CONFIG_BOOLEAN_YES || (do_icmpmsg == CONFIG_BOOLEAN_AUTO && + (snmp_root.icmpmsg_InEchoReps || + snmp_root.icmpmsg_OutEchoReps || + snmp_root.icmpmsg_InDestUnreachs || + snmp_root.icmpmsg_OutDestUnreachs || + snmp_root.icmpmsg_InRedirects || + snmp_root.icmpmsg_OutRedirects || + snmp_root.icmpmsg_InEchos || + snmp_root.icmpmsg_OutEchos || + snmp_root.icmpmsg_InRouterAdvert || + snmp_root.icmpmsg_OutRouterAdvert || + snmp_root.icmpmsg_InRouterSelect || + snmp_root.icmpmsg_OutRouterSelect || + snmp_root.icmpmsg_InTimeExcds || + snmp_root.icmpmsg_OutTimeExcds || + snmp_root.icmpmsg_InParmProbs || + snmp_root.icmpmsg_OutParmProbs || + snmp_root.icmpmsg_InTimestamps || + snmp_root.icmpmsg_OutTimestamps || + snmp_root.icmpmsg_InTimestampReps || + snmp_root.icmpmsg_OutTimestampReps || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_icmpmsg = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_InEchoReps = NULL, + *rd_OutEchoReps = NULL, + *rd_InDestUnreachs = NULL, + *rd_OutDestUnreachs = NULL, + *rd_InRedirects = NULL, + *rd_OutRedirects = NULL, + *rd_InEchos = NULL, + *rd_OutEchos = NULL, + *rd_InRouterAdvert = NULL, + *rd_OutRouterAdvert = NULL, + *rd_InRouterSelect = NULL, + *rd_OutRouterSelect = NULL, + *rd_InTimeExcds = NULL, + *rd_OutTimeExcds = NULL, + *rd_InParmProbs = NULL, + *rd_OutParmProbs = NULL, + *rd_InTimestamps = NULL, + *rd_OutTimestamps = NULL, + *rd_InTimestampReps = NULL, + *rd_OutTimestampReps = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "icmpmsg" + , NULL + , "icmp" + , NULL + , "IPv4 ICMP Messages" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_ICMP + 2 + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InEchoReps = rrddim_add(st, "InType0", "InEchoReps", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutEchoReps = rrddim_add(st, "OutType0", "OutEchoReps", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InDestUnreachs = rrddim_add(st, "InType3", "InDestUnreachs", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutDestUnreachs = rrddim_add(st, "OutType3", "OutDestUnreachs", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InRedirects = rrddim_add(st, "InType5", "InRedirects", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutRedirects = rrddim_add(st, "OutType5", "OutRedirects", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InEchos = rrddim_add(st, "InType8", "InEchos", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutEchos = rrddim_add(st, "OutType8", "OutEchos", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InRouterAdvert = rrddim_add(st, "InType9", "InRouterAdvert", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutRouterAdvert = rrddim_add(st, "OutType9", "OutRouterAdvert", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InRouterSelect = rrddim_add(st, "InType10", "InRouterSelect", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutRouterSelect = rrddim_add(st, "OutType10", "OutRouterSelect", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InTimeExcds = rrddim_add(st, "InType11", "InTimeExcds", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutTimeExcds = rrddim_add(st, "OutType11", "OutTimeExcds", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InParmProbs = rrddim_add(st, "InType12", "InParmProbs", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutParmProbs = rrddim_add(st, "OutType12", "OutParmProbs", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InTimestamps = rrddim_add(st, "InType13", "InTimestamps", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutTimestamps = rrddim_add(st, "OutType13", "OutTimestamps", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InTimestampReps = rrddim_add(st, "InType14", "InTimestampReps", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutTimestampReps = rrddim_add(st, "OutType14", "OutTimestampReps", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InEchoReps, (collected_number)snmp_root.icmpmsg_InEchoReps); + rrddim_set_by_pointer(st, rd_OutEchoReps, (collected_number)snmp_root.icmpmsg_OutEchoReps); + rrddim_set_by_pointer(st, rd_InDestUnreachs, (collected_number)snmp_root.icmpmsg_InDestUnreachs); + rrddim_set_by_pointer(st, rd_OutDestUnreachs, (collected_number)snmp_root.icmpmsg_OutDestUnreachs); + rrddim_set_by_pointer(st, rd_InRedirects, (collected_number)snmp_root.icmpmsg_InRedirects); + rrddim_set_by_pointer(st, rd_OutRedirects, (collected_number)snmp_root.icmpmsg_OutRedirects); + rrddim_set_by_pointer(st, rd_InEchos, (collected_number)snmp_root.icmpmsg_InEchos); + rrddim_set_by_pointer(st, rd_OutEchos, (collected_number)snmp_root.icmpmsg_OutEchos); + rrddim_set_by_pointer(st, rd_InRouterAdvert, (collected_number)snmp_root.icmpmsg_InRouterAdvert); + rrddim_set_by_pointer(st, rd_OutRouterAdvert, (collected_number)snmp_root.icmpmsg_OutRouterAdvert); + rrddim_set_by_pointer(st, rd_InRouterSelect, (collected_number)snmp_root.icmpmsg_InRouterSelect); + rrddim_set_by_pointer(st, rd_OutRouterSelect, (collected_number)snmp_root.icmpmsg_OutRouterSelect); + rrddim_set_by_pointer(st, rd_InTimeExcds, (collected_number)snmp_root.icmpmsg_InTimeExcds); + rrddim_set_by_pointer(st, rd_OutTimeExcds, (collected_number)snmp_root.icmpmsg_OutTimeExcds); + rrddim_set_by_pointer(st, rd_InParmProbs, (collected_number)snmp_root.icmpmsg_InParmProbs); + rrddim_set_by_pointer(st, rd_OutParmProbs, (collected_number)snmp_root.icmpmsg_OutParmProbs); + rrddim_set_by_pointer(st, rd_InTimestamps, (collected_number)snmp_root.icmpmsg_InTimestamps); + rrddim_set_by_pointer(st, rd_OutTimestamps, (collected_number)snmp_root.icmpmsg_OutTimestamps); + rrddim_set_by_pointer(st, rd_InTimestampReps, (collected_number)snmp_root.icmpmsg_InTimestampReps); + rrddim_set_by_pointer(st, rd_OutTimestampReps, (collected_number)snmp_root.icmpmsg_OutTimestampReps); + + rrdset_done(st); + } + + // snmp Tcp charts + + // this is smart enough to update it, only when it is changed + rrdvar_custom_host_variable_set(localhost, tcp_max_connections_var, snmp_root.tcp_MaxConn); + + // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html + if(do_tcp_sockets == CONFIG_BOOLEAN_YES || (do_tcp_sockets == CONFIG_BOOLEAN_AUTO && + (snmp_root.tcp_CurrEstab || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcp_sockets = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_CurrEstab = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "tcpsock" + , NULL + , "tcp" + , NULL + , "IPv4 TCP Connections" + , "active connections" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_TCP + , update_every + , RRDSET_TYPE_LINE + ); + + rd_CurrEstab = rrddim_add(st, "CurrEstab", "connections", 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(st, rd_CurrEstab, (collected_number)snmp_root.tcp_CurrEstab); + rrdset_done(st); + } + + if(do_tcp_packets == CONFIG_BOOLEAN_YES || (do_tcp_packets == CONFIG_BOOLEAN_AUTO && + (snmp_root.tcp_InSegs || + snmp_root.tcp_OutSegs || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcp_packets = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_InSegs = NULL, + *rd_OutSegs = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "tcppackets" + , NULL + , "tcp" + , NULL + , "IPv4 TCP Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_TCP + 4 + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InSegs = rrddim_add(st, "InSegs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutSegs = rrddim_add(st, "OutSegs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InSegs, (collected_number)snmp_root.tcp_InSegs); + rrddim_set_by_pointer(st, rd_OutSegs, (collected_number)snmp_root.tcp_OutSegs); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if(do_tcp_errors == CONFIG_BOOLEAN_YES || (do_tcp_errors == CONFIG_BOOLEAN_AUTO && + (snmp_root.tcp_InErrs || + snmp_root.tcp_InCsumErrors || + snmp_root.tcp_RetransSegs || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcp_errors = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_InErrs = NULL, + *rd_InCsumErrors = NULL, + *rd_RetransSegs = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "tcperrors" + , NULL + , "tcp" + , NULL + , "IPv4 TCP Errors" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_TCP + 20 + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_InErrs = rrddim_add(st, "InErrs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_RetransSegs = rrddim_add(st, "RetransSegs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InErrs, (collected_number)snmp_root.tcp_InErrs); + rrddim_set_by_pointer(st, rd_InCsumErrors, (collected_number)snmp_root.tcp_InCsumErrors); + rrddim_set_by_pointer(st, rd_RetransSegs, (collected_number)snmp_root.tcp_RetransSegs); + rrdset_done(st); + } + + if(do_tcp_opens == CONFIG_BOOLEAN_YES || (do_tcp_opens == CONFIG_BOOLEAN_AUTO && + (snmp_root.tcp_ActiveOpens || + snmp_root.tcp_PassiveOpens || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcp_opens = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_ActiveOpens = NULL, + *rd_PassiveOpens = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "tcpopens" + , NULL + , "tcp" + , NULL + , "IPv4 TCP Opens" + , "connections/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_TCP + 5 + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_ActiveOpens = rrddim_add(st, "ActiveOpens", "active", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_PassiveOpens = rrddim_add(st, "PassiveOpens", "passive", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_ActiveOpens, (collected_number)snmp_root.tcp_ActiveOpens); + rrddim_set_by_pointer(st, rd_PassiveOpens, (collected_number)snmp_root.tcp_PassiveOpens); + rrdset_done(st); + } + + if(do_tcp_handshake == CONFIG_BOOLEAN_YES || (do_tcp_handshake == CONFIG_BOOLEAN_AUTO && + (snmp_root.tcp_EstabResets || + snmp_root.tcp_OutRsts || + snmp_root.tcp_AttemptFails || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_tcp_handshake = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_EstabResets = NULL, + *rd_OutRsts = NULL, + *rd_AttemptFails = NULL, + *rd_TCPSynRetrans = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "tcphandshake" + , NULL + , "tcp" + , NULL + , "IPv4 TCP Handshake Issues" + , "events/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_TCP + 30 + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_EstabResets = rrddim_add(st, "EstabResets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutRsts = rrddim_add(st, "OutRsts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_AttemptFails = rrddim_add(st, "AttemptFails", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_TCPSynRetrans = rrddim_add(st, "TCPSynRetrans", "SynRetrans", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_EstabResets, (collected_number)snmp_root.tcp_EstabResets); + rrddim_set_by_pointer(st, rd_OutRsts, (collected_number)snmp_root.tcp_OutRsts); + rrddim_set_by_pointer(st, rd_AttemptFails, (collected_number)snmp_root.tcp_AttemptFails); + rrddim_set_by_pointer(st, rd_TCPSynRetrans, tcpext_TCPSynRetrans); + rrdset_done(st); + } + + // snmp Udp charts + + // see http://net-snmp.sourceforge.net/docs/mibs/udp.html + if(do_udp_packets == CONFIG_BOOLEAN_YES || (do_udp_packets == CONFIG_BOOLEAN_AUTO && + (snmp_root.udp_InDatagrams || + snmp_root.udp_OutDatagrams || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_udp_packets = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_InDatagrams = NULL, + *rd_OutDatagrams = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "udppackets" + , NULL + , "udp" + , NULL + , "IPv4 UDP Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_UDP + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InDatagrams = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutDatagrams = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InDatagrams, (collected_number)snmp_root.udp_InDatagrams); + rrddim_set_by_pointer(st, rd_OutDatagrams, (collected_number)snmp_root.udp_OutDatagrams); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + if(do_udp_errors == CONFIG_BOOLEAN_YES || (do_udp_errors == CONFIG_BOOLEAN_AUTO && + (snmp_root.udp_InErrors || + snmp_root.udp_NoPorts || + snmp_root.udp_RcvbufErrors || + snmp_root.udp_SndbufErrors || + snmp_root.udp_InCsumErrors || + snmp_root.udp_IgnoredMulti || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_udp_errors = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_RcvbufErrors = NULL, + *rd_SndbufErrors = NULL, + *rd_InErrors = NULL, + *rd_NoPorts = NULL, + *rd_InCsumErrors = NULL, + *rd_IgnoredMulti = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "udperrors" + , NULL + , "udp" + , NULL + , "IPv4 UDP Errors" + , "events/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_UDP + 10 + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_RcvbufErrors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_SndbufErrors = rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_NoPorts = rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_IgnoredMulti = rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InErrors, (collected_number)snmp_root.udp_InErrors); + rrddim_set_by_pointer(st, rd_NoPorts, (collected_number)snmp_root.udp_NoPorts); + rrddim_set_by_pointer(st, rd_RcvbufErrors, (collected_number)snmp_root.udp_RcvbufErrors); + rrddim_set_by_pointer(st, rd_SndbufErrors, (collected_number)snmp_root.udp_SndbufErrors); + rrddim_set_by_pointer(st, rd_InCsumErrors, (collected_number)snmp_root.udp_InCsumErrors); + rrddim_set_by_pointer(st, rd_IgnoredMulti, (collected_number)snmp_root.udp_IgnoredMulti); + rrdset_done(st); + } + + // snmp UdpLite charts + + if(do_udplite_packets == CONFIG_BOOLEAN_YES || (do_udplite_packets == CONFIG_BOOLEAN_AUTO && + (snmp_root.udplite_InDatagrams || + snmp_root.udplite_OutDatagrams || + snmp_root.udplite_NoPorts || + snmp_root.udplite_InErrors || + snmp_root.udplite_InCsumErrors || + snmp_root.udplite_RcvbufErrors || + snmp_root.udplite_SndbufErrors || + snmp_root.udplite_IgnoredMulti || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_udplite_packets = CONFIG_BOOLEAN_YES; + + { + static RRDSET *st = NULL; + static RRDDIM *rd_InDatagrams = NULL, + *rd_OutDatagrams = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "udplite" + , NULL + , "udplite" + , NULL + , "IPv4 UDPLite Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_UDPLITE + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InDatagrams = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutDatagrams = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } - // -------------------------------------------------------------------- - - if(do_tcpext_accept_queue == CONFIG_BOOLEAN_YES || (do_tcpext_accept_queue == CONFIG_BOOLEAN_AUTO && - (tcpext_ListenOverflows || - tcpext_ListenDrops || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcpext_accept_queue = CONFIG_BOOLEAN_YES; - - static RRDSET *st_accept_queue = NULL; - static RRDDIM *rd_overflows = NULL, - *rd_drops = NULL; - - if(unlikely(!st_accept_queue)) { - - st_accept_queue = rrdset_create_localhost( - RRD_TYPE_NET_NETSTAT - , "tcp_accept_queue" - , NULL - , "tcp" - , NULL - , "TCP Accept Queue Issues" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NETSTAT_NAME - , NETDATA_CHART_PRIO_IP_TCP_ACCEPT_QUEUE - , update_every - , RRDSET_TYPE_LINE - ); - - rd_overflows = rrddim_add(st_accept_queue, "ListenOverflows", "overflows", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_drops = rrddim_add(st_accept_queue, "ListenDrops", "drops", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else - rrdset_next(st_accept_queue); - - rrddim_set_by_pointer(st_accept_queue, rd_overflows, tcpext_ListenOverflows); - rrddim_set_by_pointer(st_accept_queue, rd_drops, tcpext_ListenDrops); - - rrdset_done(st_accept_queue); + rrddim_set_by_pointer(st, rd_InDatagrams, (collected_number)snmp_root.udplite_InDatagrams); + rrddim_set_by_pointer(st, rd_OutDatagrams, (collected_number)snmp_root.udplite_OutDatagrams); + rrdset_done(st); + } + + { + static RRDSET *st = NULL; + static RRDDIM *rd_RcvbufErrors = NULL, + *rd_SndbufErrors = NULL, + *rd_InErrors = NULL, + *rd_NoPorts = NULL, + *rd_InCsumErrors = NULL, + *rd_IgnoredMulti = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP + , "udplite_errors" + , NULL + , "udplite" + , NULL + , "IPv4 UDPLite Errors" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV4_UDPLITE + 10 + , update_every + , RRDSET_TYPE_LINE); + + rd_RcvbufErrors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_SndbufErrors = rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_NoPorts = rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_IgnoredMulti = rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } + rrddim_set_by_pointer(st, rd_NoPorts, (collected_number)snmp_root.udplite_NoPorts); + rrddim_set_by_pointer(st, rd_InErrors, (collected_number)snmp_root.udplite_InErrors); + rrddim_set_by_pointer(st, rd_InCsumErrors, (collected_number)snmp_root.udplite_InCsumErrors); + rrddim_set_by_pointer(st, rd_RcvbufErrors, (collected_number)snmp_root.udplite_RcvbufErrors); + rrddim_set_by_pointer(st, rd_SndbufErrors, (collected_number)snmp_root.udplite_SndbufErrors); + rrddim_set_by_pointer(st, rd_IgnoredMulti, (collected_number)snmp_root.udplite_IgnoredMulti); + rrdset_done(st); } } + // snmp6 charts + + if(do_ip6_bandwidth == CONFIG_BOOLEAN_YES || (do_ip6_bandwidth == CONFIG_BOOLEAN_AUTO && + (Ip6InOctets || + Ip6OutOctets || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_bandwidth = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_received = NULL, + *rd_sent = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + "system" + , "ipv6" + , NULL + , "network" + , NULL + , "IPv6 Bandwidth" + , "kilobits/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_SYSTEM_IPV6 + , update_every + , RRDSET_TYPE_AREA + ); + + rd_received = rrddim_add(st, "InOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + rd_sent = rrddim_add(st, "OutOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_received, Ip6InOctets); + rrddim_set_by_pointer(st, rd_sent, Ip6OutOctets); + rrdset_done(st); + } + + if(do_ip6_packets == CONFIG_BOOLEAN_YES || (do_ip6_packets == CONFIG_BOOLEAN_AUTO && + (Ip6InReceives || + Ip6OutRequests || + Ip6InDelivers || + Ip6OutForwDatagrams || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_packets = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_received = NULL, + *rd_sent = NULL, + *rd_forwarded = NULL, + *rd_delivers = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "packets" + , NULL + , "packets" + , NULL + , "IPv6 Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_PACKETS + , update_every + , RRDSET_TYPE_LINE + ); + + rd_received = rrddim_add(st, "InReceives", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_sent = rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_forwarded = rrddim_add(st, "OutForwDatagrams", "forwarded", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_delivers = rrddim_add(st, "InDelivers", "delivers", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_received, Ip6InReceives); + rrddim_set_by_pointer(st, rd_sent, Ip6OutRequests); + rrddim_set_by_pointer(st, rd_forwarded, Ip6OutForwDatagrams); + rrddim_set_by_pointer(st, rd_delivers, Ip6InDelivers); + rrdset_done(st); + } + + if(do_ip6_fragsout == CONFIG_BOOLEAN_YES || (do_ip6_fragsout == CONFIG_BOOLEAN_AUTO && + (Ip6FragOKs || + Ip6FragFails || + Ip6FragCreates || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_fragsout = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_ok = NULL, + *rd_failed = NULL, + *rd_all = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "fragsout" + , NULL + , "fragments6" + , NULL + , "IPv6 Fragments Sent" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_FRAGSOUT + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_ok = rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_failed = rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_all = rrddim_add(st, "FragCreates", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_ok, Ip6FragOKs); + rrddim_set_by_pointer(st, rd_failed, Ip6FragFails); + rrddim_set_by_pointer(st, rd_all, Ip6FragCreates); + rrdset_done(st); + } + + if(do_ip6_fragsin == CONFIG_BOOLEAN_YES || (do_ip6_fragsin == CONFIG_BOOLEAN_AUTO && + (Ip6ReasmOKs || + Ip6ReasmFails || + Ip6ReasmTimeout || + Ip6ReasmReqds || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_fragsin = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_ok = NULL, + *rd_failed = NULL, + *rd_timeout = NULL, + *rd_all = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "fragsin" + , NULL + , "fragments6" + , NULL + , "IPv6 Fragments Reassembly" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_FRAGSIN + , update_every + , RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_ok = rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_failed = rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_timeout = rrddim_add(st, "ReasmTimeout", "timeout", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_all = rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_ok, Ip6ReasmOKs); + rrddim_set_by_pointer(st, rd_failed, Ip6ReasmFails); + rrddim_set_by_pointer(st, rd_timeout, Ip6ReasmTimeout); + rrddim_set_by_pointer(st, rd_all, Ip6ReasmReqds); + rrdset_done(st); + } + + if(do_ip6_errors == CONFIG_BOOLEAN_YES || (do_ip6_errors == CONFIG_BOOLEAN_AUTO && + (Ip6InDiscards || + Ip6OutDiscards || + Ip6InHdrErrors || + Ip6InAddrErrors || + Ip6InUnknownProtos || + Ip6InTooBigErrors || + Ip6InTruncatedPkts || + Ip6InNoRoutes || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_errors = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_InDiscards = NULL, + *rd_OutDiscards = NULL, + *rd_InHdrErrors = NULL, + *rd_InAddrErrors = NULL, + *rd_InUnknownProtos = NULL, + *rd_InTooBigErrors = NULL, + *rd_InTruncatedPkts = NULL, + *rd_InNoRoutes = NULL, + *rd_OutNoRoutes = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "errors" + , NULL + , "errors" + , NULL + , "IPv6 Errors" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ERRORS + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_InDiscards = rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutDiscards = rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InHdrErrors = rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InAddrErrors = rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InUnknownProtos = rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InTooBigErrors = rrddim_add(st, "InTooBigErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InTruncatedPkts = rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InNoRoutes = rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutNoRoutes = rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InDiscards, Ip6InDiscards); + rrddim_set_by_pointer(st, rd_OutDiscards, Ip6OutDiscards); + rrddim_set_by_pointer(st, rd_InHdrErrors, Ip6InHdrErrors); + rrddim_set_by_pointer(st, rd_InAddrErrors, Ip6InAddrErrors); + rrddim_set_by_pointer(st, rd_InUnknownProtos, Ip6InUnknownProtos); + rrddim_set_by_pointer(st, rd_InTooBigErrors, Ip6InTooBigErrors); + rrddim_set_by_pointer(st, rd_InTruncatedPkts, Ip6InTruncatedPkts); + rrddim_set_by_pointer(st, rd_InNoRoutes, Ip6InNoRoutes); + rrddim_set_by_pointer(st, rd_OutNoRoutes, Ip6OutNoRoutes); + rrdset_done(st); + } + + if(do_ip6_udp_packets == CONFIG_BOOLEAN_YES || (do_ip6_udp_packets == CONFIG_BOOLEAN_AUTO && + (Udp6InDatagrams || + Udp6OutDatagrams || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_udp_packets = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_received = NULL, + *rd_sent = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "udppackets" + , NULL + , "udp6" + , NULL + , "IPv6 UDP Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_UDP_PACKETS + , update_every + , RRDSET_TYPE_LINE + ); + + rd_received = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_sent = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_received, Udp6InDatagrams); + rrddim_set_by_pointer(st, rd_sent, Udp6OutDatagrams); + rrdset_done(st); + } + + if(do_ip6_udp_errors == CONFIG_BOOLEAN_YES || (do_ip6_udp_errors == CONFIG_BOOLEAN_AUTO && + (Udp6InErrors || + Udp6NoPorts || + Udp6RcvbufErrors || + Udp6SndbufErrors || + Udp6InCsumErrors || + Udp6IgnoredMulti || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_udp_errors = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_RcvbufErrors = NULL, + *rd_SndbufErrors = NULL, + *rd_InErrors = NULL, + *rd_NoPorts = NULL, + *rd_InCsumErrors = NULL, + *rd_IgnoredMulti = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "udperrors" + , NULL + , "udp6" + , NULL + , "IPv6 UDP Errors" + , "events/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_UDP_ERRORS + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_RcvbufErrors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_SndbufErrors = rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_NoPorts = rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_IgnoredMulti = rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_RcvbufErrors, Udp6RcvbufErrors); + rrddim_set_by_pointer(st, rd_SndbufErrors, Udp6SndbufErrors); + rrddim_set_by_pointer(st, rd_InErrors, Udp6InErrors); + rrddim_set_by_pointer(st, rd_NoPorts, Udp6NoPorts); + rrddim_set_by_pointer(st, rd_InCsumErrors, Udp6InCsumErrors); + rrddim_set_by_pointer(st, rd_IgnoredMulti, Udp6IgnoredMulti); + rrdset_done(st); + } + + if(do_ip6_udplite_packets == CONFIG_BOOLEAN_YES || (do_ip6_udplite_packets == CONFIG_BOOLEAN_AUTO && + (UdpLite6InDatagrams || + UdpLite6OutDatagrams || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_udplite_packets = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_received = NULL, + *rd_sent = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "udplitepackets" + , NULL + , "udplite6" + , NULL + , "IPv6 UDPlite Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_UDPLITE_PACKETS + , update_every + , RRDSET_TYPE_LINE + ); + + rd_received = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_sent = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_received, UdpLite6InDatagrams); + rrddim_set_by_pointer(st, rd_sent, UdpLite6OutDatagrams); + rrdset_done(st); + } + + if(do_ip6_udplite_errors == CONFIG_BOOLEAN_YES || (do_ip6_udplite_errors == CONFIG_BOOLEAN_AUTO && + (UdpLite6InErrors || + UdpLite6NoPorts || + UdpLite6RcvbufErrors || + UdpLite6SndbufErrors || + Udp6InCsumErrors || + UdpLite6InCsumErrors || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_udplite_errors = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_RcvbufErrors = NULL, + *rd_SndbufErrors = NULL, + *rd_InErrors = NULL, + *rd_NoPorts = NULL, + *rd_InCsumErrors = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "udpliteerrors" + , NULL + , "udplite6" + , NULL + , "IPv6 UDP Lite Errors" + , "events/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_UDPLITE_ERRORS + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_RcvbufErrors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_SndbufErrors = rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_NoPorts = rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InErrors, UdpLite6InErrors); + rrddim_set_by_pointer(st, rd_NoPorts, UdpLite6NoPorts); + rrddim_set_by_pointer(st, rd_RcvbufErrors, UdpLite6RcvbufErrors); + rrddim_set_by_pointer(st, rd_SndbufErrors, UdpLite6SndbufErrors); + rrddim_set_by_pointer(st, rd_InCsumErrors, UdpLite6InCsumErrors); + rrdset_done(st); + } + + if(do_ip6_mcast == CONFIG_BOOLEAN_YES || (do_ip6_mcast == CONFIG_BOOLEAN_AUTO && + (Ip6OutMcastOctets || + Ip6InMcastOctets || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_mcast = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_Ip6InMcastOctets = NULL, + *rd_Ip6OutMcastOctets = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "mcast" + , NULL + , "multicast6" + , NULL + , "IPv6 Multicast Bandwidth" + , "kilobits/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_MCAST + , update_every + , RRDSET_TYPE_AREA + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_Ip6InMcastOctets = rrddim_add(st, "InMcastOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + rd_Ip6OutMcastOctets = rrddim_add(st, "OutMcastOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_Ip6InMcastOctets, Ip6InMcastOctets); + rrddim_set_by_pointer(st, rd_Ip6OutMcastOctets, Ip6OutMcastOctets); + rrdset_done(st); + } + + if(do_ip6_bcast == CONFIG_BOOLEAN_YES || (do_ip6_bcast == CONFIG_BOOLEAN_AUTO && + (Ip6OutBcastOctets || + Ip6InBcastOctets || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_bcast = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_Ip6InBcastOctets = NULL, + *rd_Ip6OutBcastOctets = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "bcast" + , NULL + , "broadcast6" + , NULL + , "IPv6 Broadcast Bandwidth" + , "kilobits/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_BCAST + , update_every + , RRDSET_TYPE_AREA + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_Ip6InBcastOctets = rrddim_add(st, "InBcastOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + rd_Ip6OutBcastOctets = rrddim_add(st, "OutBcastOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_Ip6InBcastOctets, Ip6InBcastOctets); + rrddim_set_by_pointer(st, rd_Ip6OutBcastOctets, Ip6OutBcastOctets); + rrdset_done(st); + } + + if(do_ip6_mcast_p == CONFIG_BOOLEAN_YES || (do_ip6_mcast_p == CONFIG_BOOLEAN_AUTO && + (Ip6OutMcastPkts || + Ip6InMcastPkts || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_mcast_p = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_Ip6InMcastPkts = NULL, + *rd_Ip6OutMcastPkts = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "mcastpkts" + , NULL + , "multicast6" + , NULL + , "IPv6 Multicast Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_MCAST_PACKETS + , update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_Ip6InMcastPkts = rrddim_add(st, "InMcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_Ip6OutMcastPkts = rrddim_add(st, "OutMcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_Ip6InMcastPkts, Ip6InMcastPkts); + rrddim_set_by_pointer(st, rd_Ip6OutMcastPkts, Ip6OutMcastPkts); + rrdset_done(st); + } + + if(do_ip6_icmp == CONFIG_BOOLEAN_YES || (do_ip6_icmp == CONFIG_BOOLEAN_AUTO && + (Icmp6InMsgs || + Icmp6OutMsgs || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_icmp = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_Icmp6InMsgs = NULL, + *rd_Icmp6OutMsgs = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "icmp" + , NULL + , "icmp6" + , NULL + , "IPv6 ICMP Messages" + , "messages/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ICMP + , update_every + , RRDSET_TYPE_LINE + ); + + rd_Icmp6InMsgs = rrddim_add(st, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_Icmp6OutMsgs = rrddim_add(st, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_Icmp6InMsgs, Icmp6InMsgs); + rrddim_set_by_pointer(st, rd_Icmp6OutMsgs, Icmp6OutMsgs); + rrdset_done(st); + } + + if(do_ip6_icmp_redir == CONFIG_BOOLEAN_YES || (do_ip6_icmp_redir == CONFIG_BOOLEAN_AUTO && + (Icmp6InRedirects || + Icmp6OutRedirects || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_icmp_redir = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_Icmp6InRedirects = NULL, + *rd_Icmp6OutRedirects = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "icmpredir" + , NULL + , "icmp6" + , NULL + , "IPv6 ICMP Redirects" + , "redirects/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ICMP_REDIR + , update_every + , RRDSET_TYPE_LINE + ); + + rd_Icmp6InRedirects = rrddim_add(st, "InRedirects", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_Icmp6OutRedirects = rrddim_add(st, "OutRedirects", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_Icmp6InRedirects, Icmp6InRedirects); + rrddim_set_by_pointer(st, rd_Icmp6OutRedirects, Icmp6OutRedirects); + rrdset_done(st); + } + + if(do_ip6_icmp_errors == CONFIG_BOOLEAN_YES || (do_ip6_icmp_errors == CONFIG_BOOLEAN_AUTO && + (Icmp6InErrors || + Icmp6OutErrors || + Icmp6InCsumErrors || + Icmp6InDestUnreachs || + Icmp6InPktTooBigs || + Icmp6InTimeExcds || + Icmp6InParmProblems || + Icmp6OutDestUnreachs || + Icmp6OutPktTooBigs || + Icmp6OutTimeExcds || + Icmp6OutParmProblems || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_icmp_errors = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_InErrors = NULL, + *rd_OutErrors = NULL, + *rd_InCsumErrors = NULL, + *rd_InDestUnreachs = NULL, + *rd_InPktTooBigs = NULL, + *rd_InTimeExcds = NULL, + *rd_InParmProblems = NULL, + *rd_OutDestUnreachs = NULL, + *rd_OutPktTooBigs = NULL, + *rd_OutTimeExcds = NULL, + *rd_OutParmProblems = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "icmperrors" + , NULL + , "icmp6" + , NULL + , "IPv6 ICMP Errors" + , "errors/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ICMP_ERRORS + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutErrors = rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InDestUnreachs = rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InPktTooBigs = rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InTimeExcds = rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InParmProblems = rrddim_add(st, "InParmProblems", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutDestUnreachs = rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutPktTooBigs = rrddim_add(st, "OutPktTooBigs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutTimeExcds = rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutParmProblems = rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InErrors, Icmp6InErrors); + rrddim_set_by_pointer(st, rd_OutErrors, Icmp6OutErrors); + rrddim_set_by_pointer(st, rd_InCsumErrors, Icmp6InCsumErrors); + rrddim_set_by_pointer(st, rd_InDestUnreachs, Icmp6InDestUnreachs); + rrddim_set_by_pointer(st, rd_InPktTooBigs, Icmp6InPktTooBigs); + rrddim_set_by_pointer(st, rd_InTimeExcds, Icmp6InTimeExcds); + rrddim_set_by_pointer(st, rd_InParmProblems, Icmp6InParmProblems); + rrddim_set_by_pointer(st, rd_OutDestUnreachs, Icmp6OutDestUnreachs); + rrddim_set_by_pointer(st, rd_OutPktTooBigs, Icmp6OutPktTooBigs); + rrddim_set_by_pointer(st, rd_OutTimeExcds, Icmp6OutTimeExcds); + rrddim_set_by_pointer(st, rd_OutParmProblems, Icmp6OutParmProblems); + rrdset_done(st); + } + + if(do_ip6_icmp_echos == CONFIG_BOOLEAN_YES || (do_ip6_icmp_echos == CONFIG_BOOLEAN_AUTO && + (Icmp6InEchos || + Icmp6OutEchos || + Icmp6InEchoReplies || + Icmp6OutEchoReplies || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_icmp_echos = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_InEchos = NULL, + *rd_OutEchos = NULL, + *rd_InEchoReplies = NULL, + *rd_OutEchoReplies = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "icmpechos" + , NULL + , "icmp6" + , NULL + , "IPv6 ICMP Echo" + , "messages/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ICMP_ECHOS + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InEchos = rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutEchos = rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InEchoReplies = rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutEchoReplies = rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InEchos, Icmp6InEchos); + rrddim_set_by_pointer(st, rd_OutEchos, Icmp6OutEchos); + rrddim_set_by_pointer(st, rd_InEchoReplies, Icmp6InEchoReplies); + rrddim_set_by_pointer(st, rd_OutEchoReplies, Icmp6OutEchoReplies); + rrdset_done(st); + } + + if(do_ip6_icmp_groupmemb == CONFIG_BOOLEAN_YES || (do_ip6_icmp_groupmemb == CONFIG_BOOLEAN_AUTO && + (Icmp6InGroupMembQueries || + Icmp6OutGroupMembQueries || + Icmp6InGroupMembResponses || + Icmp6OutGroupMembResponses || + Icmp6InGroupMembReductions || + Icmp6OutGroupMembReductions || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_icmp_groupmemb = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_InQueries = NULL, + *rd_OutQueries = NULL, + *rd_InResponses = NULL, + *rd_OutResponses = NULL, + *rd_InReductions = NULL, + *rd_OutReductions = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "groupmemb" + , NULL + , "icmp6" + , NULL + , "IPv6 ICMP Group Membership" + , "messages/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ICMP_GROUPMEMB + , update_every + , RRDSET_TYPE_LINE); + + rd_InQueries = rrddim_add(st, "InQueries", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutQueries = rrddim_add(st, "OutQueries", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InResponses = rrddim_add(st, "InResponses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutResponses = rrddim_add(st, "OutResponses", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InReductions = rrddim_add(st, "InReductions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutReductions = rrddim_add(st, "OutReductions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InQueries, Icmp6InGroupMembQueries); + rrddim_set_by_pointer(st, rd_OutQueries, Icmp6OutGroupMembQueries); + rrddim_set_by_pointer(st, rd_InResponses, Icmp6InGroupMembResponses); + rrddim_set_by_pointer(st, rd_OutResponses, Icmp6OutGroupMembResponses); + rrddim_set_by_pointer(st, rd_InReductions, Icmp6InGroupMembReductions); + rrddim_set_by_pointer(st, rd_OutReductions, Icmp6OutGroupMembReductions); + rrdset_done(st); + } + + if(do_ip6_icmp_router == CONFIG_BOOLEAN_YES || (do_ip6_icmp_router == CONFIG_BOOLEAN_AUTO && + (Icmp6InRouterSolicits || + Icmp6OutRouterSolicits || + Icmp6InRouterAdvertisements || + Icmp6OutRouterAdvertisements || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_icmp_router = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_InSolicits = NULL, + *rd_OutSolicits = NULL, + *rd_InAdvertisements = NULL, + *rd_OutAdvertisements = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "icmprouter" + , NULL + , "icmp6" + , NULL + , "IPv6 Router Messages" + , "messages/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ICMP_ROUTER + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InSolicits = rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutSolicits = rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InAdvertisements = rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutAdvertisements = rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InSolicits, Icmp6InRouterSolicits); + rrddim_set_by_pointer(st, rd_OutSolicits, Icmp6OutRouterSolicits); + rrddim_set_by_pointer(st, rd_InAdvertisements, Icmp6InRouterAdvertisements); + rrddim_set_by_pointer(st, rd_OutAdvertisements, Icmp6OutRouterAdvertisements); + rrdset_done(st); + } + + if(do_ip6_icmp_neighbor == CONFIG_BOOLEAN_YES || (do_ip6_icmp_neighbor == CONFIG_BOOLEAN_AUTO && + (Icmp6InNeighborSolicits || + Icmp6OutNeighborSolicits || + Icmp6InNeighborAdvertisements || + Icmp6OutNeighborAdvertisements || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_icmp_neighbor = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_InSolicits = NULL, + *rd_OutSolicits = NULL, + *rd_InAdvertisements = NULL, + *rd_OutAdvertisements = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "icmpneighbor" + , NULL + , "icmp6" + , NULL + , "IPv6 Neighbor Messages" + , "messages/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ICMP_NEIGHBOR + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InSolicits = rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutSolicits = rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InAdvertisements = rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutAdvertisements = rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InSolicits, Icmp6InNeighborSolicits); + rrddim_set_by_pointer(st, rd_OutSolicits, Icmp6OutNeighborSolicits); + rrddim_set_by_pointer(st, rd_InAdvertisements, Icmp6InNeighborAdvertisements); + rrddim_set_by_pointer(st, rd_OutAdvertisements, Icmp6OutNeighborAdvertisements); + rrdset_done(st); + } + + if(do_ip6_icmp_mldv2 == CONFIG_BOOLEAN_YES || (do_ip6_icmp_mldv2 == CONFIG_BOOLEAN_AUTO && + (Icmp6InMLDv2Reports || + Icmp6OutMLDv2Reports || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_icmp_mldv2 = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_InMLDv2Reports = NULL, + *rd_OutMLDv2Reports = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "icmpmldv2" + , NULL + , "icmp6" + , NULL + , "IPv6 ICMP MLDv2 Reports" + , "reports/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ICMP_LDV2 + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InMLDv2Reports = rrddim_add(st, "InMLDv2Reports", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutMLDv2Reports = rrddim_add(st, "OutMLDv2Reports", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InMLDv2Reports, Icmp6InMLDv2Reports); + rrddim_set_by_pointer(st, rd_OutMLDv2Reports, Icmp6OutMLDv2Reports); + rrdset_done(st); + } + + if(do_ip6_icmp_types == CONFIG_BOOLEAN_YES || (do_ip6_icmp_types == CONFIG_BOOLEAN_AUTO && + (Icmp6InType1 || + Icmp6InType128 || + Icmp6InType129 || + Icmp6InType136 || + Icmp6OutType1 || + Icmp6OutType128 || + Icmp6OutType129 || + Icmp6OutType133 || + Icmp6OutType135 || + Icmp6OutType143 || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_icmp_types = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_InType1 = NULL, + *rd_InType128 = NULL, + *rd_InType129 = NULL, + *rd_InType136 = NULL, + *rd_OutType1 = NULL, + *rd_OutType128 = NULL, + *rd_OutType129 = NULL, + *rd_OutType133 = NULL, + *rd_OutType135 = NULL, + *rd_OutType143 = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "icmptypes" + , NULL + , "icmp6" + , NULL + , "IPv6 ICMP Types" + , "messages/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ICMP_TYPES + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InType1 = rrddim_add(st, "InType1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InType128 = rrddim_add(st, "InType128", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InType129 = rrddim_add(st, "InType129", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InType136 = rrddim_add(st, "InType136", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutType1 = rrddim_add(st, "OutType1", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutType128 = rrddim_add(st, "OutType128", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutType129 = rrddim_add(st, "OutType129", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutType133 = rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutType135 = rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_OutType143 = rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InType1, Icmp6InType1); + rrddim_set_by_pointer(st, rd_InType128, Icmp6InType128); + rrddim_set_by_pointer(st, rd_InType129, Icmp6InType129); + rrddim_set_by_pointer(st, rd_InType136, Icmp6InType136); + rrddim_set_by_pointer(st, rd_OutType1, Icmp6OutType1); + rrddim_set_by_pointer(st, rd_OutType128, Icmp6OutType128); + rrddim_set_by_pointer(st, rd_OutType129, Icmp6OutType129); + rrddim_set_by_pointer(st, rd_OutType133, Icmp6OutType133); + rrddim_set_by_pointer(st, rd_OutType135, Icmp6OutType135); + rrddim_set_by_pointer(st, rd_OutType143, Icmp6OutType143); + rrdset_done(st); + } + + if(do_ip6_ect == CONFIG_BOOLEAN_YES || (do_ip6_ect == CONFIG_BOOLEAN_AUTO && + (Ip6InNoECTPkts || + Ip6InECT1Pkts || + Ip6InECT0Pkts || + Ip6InCEPkts || + netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + do_ip6_ect = CONFIG_BOOLEAN_YES; + static RRDSET *st = NULL; + static RRDDIM *rd_InNoECTPkts = NULL, + *rd_InECT1Pkts = NULL, + *rd_InECT0Pkts = NULL, + *rd_InCEPkts = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost( + RRD_TYPE_NET_SNMP6 + , "ect" + , NULL + , "packets" + , NULL + , "IPv6 ECT Packets" + , "packets/s" + , PLUGIN_PROC_NAME + , PLUGIN_PROC_MODULE_NETSTAT_NAME + , NETDATA_CHART_PRIO_IPV6_ECT + , update_every + , RRDSET_TYPE_LINE + ); + + rd_InNoECTPkts = rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InECT1Pkts = rrddim_add(st, "InECT1Pkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InECT0Pkts = rrddim_add(st, "InECT0Pkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_InCEPkts = rrddim_add(st, "InCEPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st, rd_InNoECTPkts, Ip6InNoECTPkts); + rrddim_set_by_pointer(st, rd_InECT1Pkts, Ip6InECT1Pkts); + rrddim_set_by_pointer(st, rd_InECT0Pkts, Ip6InECT0Pkts); + rrddim_set_by_pointer(st, rd_InCEPkts, Ip6InCEPkts); + rrdset_done(st); + } + return 0; } diff --git a/collectors/proc.plugin/proc_net_rpc_nfs.c b/collectors/proc.plugin/proc_net_rpc_nfs.c index f5702859c..b1ff4e05a 100644 --- a/collectors/proc.plugin/proc_net_rpc_nfs.c +++ b/collectors/proc.plugin/proc_net_rpc_nfs.c @@ -275,8 +275,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - if(do_net == 2) { static RRDSET *st = NULL; static RRDDIM *rd_udp = NULL, @@ -303,7 +301,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { rd_udp = rrddim_add(st, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_tcp = rrddim_add(st, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); // ignore net_count, net_tcp_connections (void)net_count; @@ -314,8 +311,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_rpc == 2) { static RRDSET *st = NULL; static RRDDIM *rd_calls = NULL, @@ -343,7 +338,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { rd_retransmits = rrddim_add(st, "retransmits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_auth_refresh = rrddim_add(st, "auth_refresh", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_calls, rpc_calls); rrddim_set_by_pointer(st, rd_retransmits, rpc_retransmits); @@ -351,8 +345,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_proc2 == 2) { static RRDSET *st = NULL; if(unlikely(!st)) { @@ -371,7 +363,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { , RRDSET_TYPE_STACKED ); } - else rrdset_next(st); size_t i; for(i = 0; nfs_proc2_values[i].present ; i++) { @@ -384,8 +375,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_proc3 == 2) { static RRDSET *st = NULL; if(unlikely(!st)) { @@ -404,7 +393,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { , RRDSET_TYPE_STACKED ); } - else rrdset_next(st); size_t i; for(i = 0; nfs_proc3_values[i].present ; i++) { @@ -417,8 +405,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_proc4 == 2) { static RRDSET *st = NULL; if(unlikely(!st)) { @@ -437,7 +423,6 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { , RRDSET_TYPE_STACKED ); } - else rrdset_next(st); size_t i; for(i = 0; nfs_proc4_values[i].present ; i++) { diff --git a/collectors/proc.plugin/proc_net_rpc_nfsd.c b/collectors/proc.plugin/proc_net_rpc_nfsd.c index 48f218e44..bd1da8889 100644 --- a/collectors/proc.plugin/proc_net_rpc_nfsd.c +++ b/collectors/proc.plugin/proc_net_rpc_nfsd.c @@ -225,8 +225,8 @@ struct nfsd_procs nfsd4_ops_values[] = { int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { (void)dt; static procfile *ff = NULL; - static int do_rc = -1, do_fh = -1, do_io = -1, do_th = -1, do_ra = -1, do_net = -1, do_rpc = -1, do_proc2 = -1, do_proc3 = -1, do_proc4 = -1, do_proc4ops = -1; - static int ra_warning = 0, proc2_warning = 0, proc3_warning = 0, proc4_warning = 0, proc4ops_warning = 0; + static int do_rc = -1, do_fh = -1, do_io = -1, do_th = -1, do_net = -1, do_rpc = -1, do_proc2 = -1, do_proc3 = -1, do_proc4 = -1, do_proc4ops = -1; + static int proc2_warning = 0, proc3_warning = 0, proc4_warning = 0, proc4ops_warning = 0; if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; @@ -243,7 +243,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { do_fh = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "file handles", 1); do_io = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "I/O", 1); do_th = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "threads", 1); - do_ra = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "read ahead", 1); do_net = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "network", 1); do_rpc = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "rpc", 1); do_proc2 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v2 procedures", 1); @@ -258,7 +257,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { if(do_fh) do_fh = 1; if(do_io) do_io = 1; if(do_th) do_th = 1; - if(do_ra) do_ra = 1; if(do_net) do_net = 1; if(do_rpc) do_rpc = 1; if(do_proc2) do_proc2 = 1; @@ -273,7 +271,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { unsigned long long fh_stale = 0; unsigned long long io_read = 0, io_write = 0; unsigned long long th_threads = 0; - unsigned long long ra_size = 0, ra_hist10 = 0, ra_hist20 = 0, ra_hist30 = 0, ra_hist40 = 0, ra_hist50 = 0, ra_hist60 = 0, ra_hist70 = 0, ra_hist80 = 0, ra_hist90 = 0, ra_hist100 = 0, ra_none = 0; unsigned long long net_count = 0, net_udp_count = 0, net_tcp_count = 0, net_tcp_connections = 0; unsigned long long rpc_calls = 0, rpc_bad_format = 0, rpc_bad_auth = 0, rpc_bad_client = 0; @@ -336,38 +333,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { do_th = 2; } - else if(do_ra == 1 && strcmp(type, "ra") == 0) { - if(unlikely(words < 13)) { - error("%s line of /proc/net/rpc/nfsd has %zu words, expected %d", type, words, 13); - continue; - } - - // readahead cache has been disabled since 2019 (kernel 5.4) - // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/fs/nfsd/vfs.c?id=501cb1849f865960501d19d54e6a5af306f9b6fd - - ra_size = str2ull(procfile_lineword(ff, l, 1)); - ra_hist10 = str2ull(procfile_lineword(ff, l, 2)); - ra_hist20 = str2ull(procfile_lineword(ff, l, 3)); - ra_hist30 = str2ull(procfile_lineword(ff, l, 4)); - ra_hist40 = str2ull(procfile_lineword(ff, l, 5)); - ra_hist50 = str2ull(procfile_lineword(ff, l, 6)); - ra_hist60 = str2ull(procfile_lineword(ff, l, 7)); - ra_hist70 = str2ull(procfile_lineword(ff, l, 8)); - ra_hist80 = str2ull(procfile_lineword(ff, l, 9)); - ra_hist90 = str2ull(procfile_lineword(ff, l, 10)); - ra_hist100 = str2ull(procfile_lineword(ff, l, 11)); - ra_none = str2ull(procfile_lineword(ff, l, 12)); - - unsigned long long sum = ra_hist10 + ra_hist20 + ra_hist30 + ra_hist40 + ra_hist50 + ra_hist60 + ra_hist70 + ra_hist80 + ra_hist90 + ra_hist100 + ra_none; - if(sum == 0ULL) { - if(!ra_warning) { - info("Disabling /proc/net/rpc/nfsd read ahead histogram. It seems unused on this machine. It will be enabled automatically when found with data in it."); - ra_warning = 1; - } - do_ra = -1; - } - else do_ra = 2; - } else if(do_net == 1 && strcmp(type, "net") == 0) { if(unlikely(words < 5)) { error("%s line of /proc/net/rpc/nfsd has %zu words, expected %d", type, words, 5); @@ -484,8 +449,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { } } - // -------------------------------------------------------------------- - if(do_rc == 2) { static RRDSET *st = NULL; static RRDDIM *rd_hits = NULL, @@ -512,7 +475,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rd_misses = rrddim_add(st, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_nocache = rrddim_add(st, "nocache", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_hits, rc_hits); rrddim_set_by_pointer(st, rd_misses, rc_misses); @@ -520,8 +482,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_fh == 2) { static RRDSET *st = NULL; static RRDDIM *rd_stale = NULL; @@ -545,14 +505,11 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rd_stale = rrddim_add(st, "stale", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_stale, fh_stale); rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_io == 2) { static RRDSET *st = NULL; static RRDDIM *rd_read = NULL, @@ -577,15 +534,12 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rd_read = rrddim_add(st, "read", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); rd_write = rrddim_add(st, "write", NULL, -1, 1000, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_read, io_read); rrddim_set_by_pointer(st, rd_write, io_write); rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_th == 2) { static RRDSET *st = NULL; static RRDDIM *rd_threads = NULL; @@ -608,78 +562,11 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rd_threads = rrddim_add(st, "threads", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_threads, th_threads); rrdset_done(st); - - } - - // -------------------------------------------------------------------- - - if(do_ra == 2) { - static RRDSET *st = NULL; - static RRDDIM *rd_ra_hist10 = NULL, - *rd_ra_hist20 = NULL, - *rd_ra_hist30 = NULL, - *rd_ra_hist40 = NULL, - *rd_ra_hist50 = NULL, - *rd_ra_hist60 = NULL, - *rd_ra_hist70 = NULL, - *rd_ra_hist80 = NULL, - *rd_ra_hist90 = NULL, - *rd_ra_hist100 = NULL, - *rd_ra_none = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - "nfsd" - , "readahead" - , NULL - , "readahead" - , NULL - , "NFS Server Read Ahead Depth" - , "percentage" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NFSD_NAME - , NETDATA_CHART_PRIO_NFSD_READAHEAD - , update_every - , RRDSET_TYPE_STACKED - ); - - rd_ra_hist10 = rrddim_add(st, "10%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_hist20 = rrddim_add(st, "20%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_hist30 = rrddim_add(st, "30%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_hist40 = rrddim_add(st, "40%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_hist50 = rrddim_add(st, "50%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_hist60 = rrddim_add(st, "60%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_hist70 = rrddim_add(st, "70%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_hist80 = rrddim_add(st, "80%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_hist90 = rrddim_add(st, "90%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_hist100 = rrddim_add(st, "100%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - rd_ra_none = rrddim_add(st, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); - } - else rrdset_next(st); - - // ignore ra_size - (void)ra_size; - - rrddim_set_by_pointer(st, rd_ra_hist10, ra_hist10); - rrddim_set_by_pointer(st, rd_ra_hist20, ra_hist20); - rrddim_set_by_pointer(st, rd_ra_hist30, ra_hist30); - rrddim_set_by_pointer(st, rd_ra_hist40, ra_hist40); - rrddim_set_by_pointer(st, rd_ra_hist50, ra_hist50); - rrddim_set_by_pointer(st, rd_ra_hist60, ra_hist60); - rrddim_set_by_pointer(st, rd_ra_hist70, ra_hist70); - rrddim_set_by_pointer(st, rd_ra_hist80, ra_hist80); - rrddim_set_by_pointer(st, rd_ra_hist90, ra_hist90); - rrddim_set_by_pointer(st, rd_ra_hist100,ra_hist100); - rrddim_set_by_pointer(st, rd_ra_none, ra_none); - rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_net == 2) { static RRDSET *st = NULL; static RRDDIM *rd_udp = NULL, @@ -705,7 +592,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rd_udp = rrddim_add(st, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_tcp = rrddim_add(st, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); // ignore net_count, net_tcp_connections (void)net_count; @@ -716,8 +602,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_rpc == 2) { static RRDSET *st = NULL; static RRDDIM *rd_calls = NULL, @@ -745,7 +629,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rd_bad_format = rrddim_add(st, "bad_format", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_bad_auth = rrddim_add(st, "bad_auth", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); // ignore rpc_bad_client (void)rpc_bad_client; @@ -756,8 +639,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_proc2 == 2) { static RRDSET *st = NULL; if(unlikely(!st)) { @@ -776,7 +657,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { , RRDSET_TYPE_STACKED ); } - else rrdset_next(st); size_t i; for(i = 0; nfsd_proc2_values[i].present ; i++) { @@ -789,8 +669,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_proc3 == 2) { static RRDSET *st = NULL; if(unlikely(!st)) { @@ -809,7 +687,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { , RRDSET_TYPE_STACKED ); } - else rrdset_next(st); size_t i; for(i = 0; nfsd_proc3_values[i].present ; i++) { @@ -822,8 +699,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_proc4 == 2) { static RRDSET *st = NULL; if(unlikely(!st)) { @@ -842,7 +717,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { , RRDSET_TYPE_STACKED ); } - else rrdset_next(st); size_t i; for(i = 0; nfsd_proc4_values[i].present ; i++) { @@ -855,8 +729,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { rrdset_done(st); } - // -------------------------------------------------------------------- - if(do_proc4ops == 2) { static RRDSET *st = NULL; if(unlikely(!st)) { @@ -875,7 +747,6 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { , RRDSET_TYPE_STACKED ); } - else rrdset_next(st); size_t i; for(i = 0; nfsd4_ops_values[i].present ; i++) { diff --git a/collectors/proc.plugin/proc_net_sctp_snmp.c b/collectors/proc.plugin/proc_net_sctp_snmp.c index 343cc5afb..292449a73 100644 --- a/collectors/proc.plugin/proc_net_sctp_snmp.c +++ b/collectors/proc.plugin/proc_net_sctp_snmp.c @@ -148,7 +148,6 @@ int do_proc_net_sctp_snmp(int update_every, usec_t dt) { rd_established = rrddim_add(st, "SctpCurrEstab", "established", 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_established, SctpCurrEstab); rrdset_done(st); @@ -190,7 +189,6 @@ int do_proc_net_sctp_snmp(int update_every, usec_t dt) { rd_aborted = rrddim_add(st, "SctpAborteds", "aborted", -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_shutdown = rrddim_add(st, "SctpShutdowns", "shutdown", -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_active, SctpActiveEstabs); rrddim_set_by_pointer(st, rd_passive, SctpPassiveEstabs); @@ -230,7 +228,6 @@ int do_proc_net_sctp_snmp(int update_every, usec_t dt) { rd_received = rrddim_add(st, "SctpInSCTPPacks", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_sent = rrddim_add(st, "SctpOutSCTPPacks", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_received, SctpInSCTPPacks); rrddim_set_by_pointer(st, rd_sent, SctpOutSCTPPacks); @@ -268,7 +265,6 @@ int do_proc_net_sctp_snmp(int update_every, usec_t dt) { rd_invalid = rrddim_add(st, "SctpOutOfBlues", "invalid", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_csum = rrddim_add(st, "SctpChecksumErrors", "checksum", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_invalid, SctpOutOfBlues); rrddim_set_by_pointer(st, rd_csum, SctpChecksumErrors); @@ -306,7 +302,6 @@ int do_proc_net_sctp_snmp(int update_every, usec_t dt) { rd_reassembled = rrddim_add(st, "SctpReasmUsrMsgs", "reassembled", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_fragmented = rrddim_add(st, "SctpFragUsrMsgs", "fragmented", -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_reassembled, SctpReasmUsrMsgs); rrddim_set_by_pointer(st, rd_fragmented, SctpFragUsrMsgs); @@ -357,7 +352,6 @@ int do_proc_net_sctp_snmp(int update_every, usec_t dt) { rd_OutOrder = rrddim_add(st, "SctpOutOrderChunks", "OutOrder", -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_OutUnorder = rrddim_add(st, "SctpOutUnorderChunks", "OutUnorder", -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_InCtrl, SctpInCtrlChunks); rrddim_set_by_pointer(st, rd_InOrder, SctpInOrderChunks); diff --git a/collectors/proc.plugin/proc_net_snmp.c b/collectors/proc.plugin/proc_net_snmp.c deleted file mode 100644 index b03a6ac74..000000000 --- a/collectors/proc.plugin/proc_net_snmp.c +++ /dev/null @@ -1,1130 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "plugin_proc.h" -#define PLUGIN_PROC_MODULE_NET_SNMP_NAME "/proc/net/snmp" - -#define RRD_TYPE_NET_SNMP "ipv4" - -static struct proc_net_snmp { - // kernel_uint_t ip_Forwarding; - kernel_uint_t ip_DefaultTTL; - kernel_uint_t ip_InReceives; - kernel_uint_t ip_InHdrErrors; - kernel_uint_t ip_InAddrErrors; - kernel_uint_t ip_ForwDatagrams; - kernel_uint_t ip_InUnknownProtos; - kernel_uint_t ip_InDiscards; - kernel_uint_t ip_InDelivers; - kernel_uint_t ip_OutRequests; - kernel_uint_t ip_OutDiscards; - kernel_uint_t ip_OutNoRoutes; - kernel_uint_t ip_ReasmTimeout; - kernel_uint_t ip_ReasmReqds; - kernel_uint_t ip_ReasmOKs; - kernel_uint_t ip_ReasmFails; - kernel_uint_t ip_FragOKs; - kernel_uint_t ip_FragFails; - kernel_uint_t ip_FragCreates; - - kernel_uint_t icmp_InMsgs; - kernel_uint_t icmp_OutMsgs; - kernel_uint_t icmp_InErrors; - kernel_uint_t icmp_OutErrors; - kernel_uint_t icmp_InCsumErrors; - - kernel_uint_t icmpmsg_InEchoReps; - kernel_uint_t icmpmsg_OutEchoReps; - kernel_uint_t icmpmsg_InDestUnreachs; - kernel_uint_t icmpmsg_OutDestUnreachs; - kernel_uint_t icmpmsg_InRedirects; - kernel_uint_t icmpmsg_OutRedirects; - kernel_uint_t icmpmsg_InEchos; - kernel_uint_t icmpmsg_OutEchos; - kernel_uint_t icmpmsg_InRouterAdvert; - kernel_uint_t icmpmsg_OutRouterAdvert; - kernel_uint_t icmpmsg_InRouterSelect; - kernel_uint_t icmpmsg_OutRouterSelect; - kernel_uint_t icmpmsg_InTimeExcds; - kernel_uint_t icmpmsg_OutTimeExcds; - kernel_uint_t icmpmsg_InParmProbs; - kernel_uint_t icmpmsg_OutParmProbs; - kernel_uint_t icmpmsg_InTimestamps; - kernel_uint_t icmpmsg_OutTimestamps; - kernel_uint_t icmpmsg_InTimestampReps; - kernel_uint_t icmpmsg_OutTimestampReps; - - //kernel_uint_t tcp_RtoAlgorithm; - //kernel_uint_t tcp_RtoMin; - //kernel_uint_t tcp_RtoMax; - ssize_t tcp_MaxConn; - kernel_uint_t tcp_ActiveOpens; - kernel_uint_t tcp_PassiveOpens; - kernel_uint_t tcp_AttemptFails; - kernel_uint_t tcp_EstabResets; - kernel_uint_t tcp_CurrEstab; - kernel_uint_t tcp_InSegs; - kernel_uint_t tcp_OutSegs; - kernel_uint_t tcp_RetransSegs; - kernel_uint_t tcp_InErrs; - kernel_uint_t tcp_OutRsts; - kernel_uint_t tcp_InCsumErrors; - - kernel_uint_t udp_InDatagrams; - kernel_uint_t udp_NoPorts; - kernel_uint_t udp_InErrors; - kernel_uint_t udp_OutDatagrams; - kernel_uint_t udp_RcvbufErrors; - kernel_uint_t udp_SndbufErrors; - kernel_uint_t udp_InCsumErrors; - kernel_uint_t udp_IgnoredMulti; - - kernel_uint_t udplite_InDatagrams; - kernel_uint_t udplite_NoPorts; - kernel_uint_t udplite_InErrors; - kernel_uint_t udplite_OutDatagrams; - kernel_uint_t udplite_RcvbufErrors; - kernel_uint_t udplite_SndbufErrors; - kernel_uint_t udplite_InCsumErrors; - kernel_uint_t udplite_IgnoredMulti; -} snmp_root = { 0 }; - -int do_proc_net_snmp(int update_every, usec_t dt) { - (void)dt; - - static procfile *ff = NULL; - static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1, - do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1, do_tcp_opens = -1, - do_udp_packets = -1, do_udp_errors = -1, do_icmp_packets = -1, do_icmpmsg = -1, do_udplite_packets = -1; - static uint32_t hash_ip = 0, hash_icmp = 0, hash_tcp = 0, hash_udp = 0, hash_icmpmsg = 0, hash_udplite = 0; - - static ARL_BASE *arl_ip = NULL, - *arl_icmp = NULL, - *arl_icmpmsg = NULL, - *arl_tcp = NULL, - *arl_udp = NULL, - *arl_udplite = NULL; - - static RRDVAR *tcp_max_connections_var = NULL; - - if(unlikely(!arl_ip)) { - do_ip_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 packets", CONFIG_BOOLEAN_AUTO); - do_ip_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 fragments sent", CONFIG_BOOLEAN_AUTO); - do_ip_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 fragments assembly", CONFIG_BOOLEAN_AUTO); - do_ip_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 errors", CONFIG_BOOLEAN_AUTO); - do_tcp_sockets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP connections", CONFIG_BOOLEAN_AUTO); - do_tcp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP packets", CONFIG_BOOLEAN_AUTO); - do_tcp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP errors", CONFIG_BOOLEAN_AUTO); - do_tcp_opens = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP opens", CONFIG_BOOLEAN_AUTO); - do_tcp_handshake = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 TCP handshake issues", CONFIG_BOOLEAN_AUTO); - do_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 UDP packets", CONFIG_BOOLEAN_AUTO); - do_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 UDP errors", CONFIG_BOOLEAN_AUTO); - do_icmp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 ICMP packets", CONFIG_BOOLEAN_AUTO); - do_icmpmsg = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 ICMP messages", CONFIG_BOOLEAN_AUTO); - do_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp", "ipv4 UDPLite packets", CONFIG_BOOLEAN_AUTO); - - hash_ip = simple_hash("Ip"); - hash_tcp = simple_hash("Tcp"); - hash_udp = simple_hash("Udp"); - hash_icmp = simple_hash("Icmp"); - hash_icmpmsg = simple_hash("IcmpMsg"); - hash_udplite = simple_hash("UdpLite"); - - arl_ip = arl_create("snmp/Ip", arl_callback_str2kernel_uint_t, 60); - // arl_expect(arl_ip, "Forwarding", &snmp_root.ip_Forwarding); - arl_expect(arl_ip, "DefaultTTL", &snmp_root.ip_DefaultTTL); - arl_expect(arl_ip, "InReceives", &snmp_root.ip_InReceives); - arl_expect(arl_ip, "InHdrErrors", &snmp_root.ip_InHdrErrors); - arl_expect(arl_ip, "InAddrErrors", &snmp_root.ip_InAddrErrors); - arl_expect(arl_ip, "ForwDatagrams", &snmp_root.ip_ForwDatagrams); - arl_expect(arl_ip, "InUnknownProtos", &snmp_root.ip_InUnknownProtos); - arl_expect(arl_ip, "InDiscards", &snmp_root.ip_InDiscards); - arl_expect(arl_ip, "InDelivers", &snmp_root.ip_InDelivers); - arl_expect(arl_ip, "OutRequests", &snmp_root.ip_OutRequests); - arl_expect(arl_ip, "OutDiscards", &snmp_root.ip_OutDiscards); - arl_expect(arl_ip, "OutNoRoutes", &snmp_root.ip_OutNoRoutes); - arl_expect(arl_ip, "ReasmTimeout", &snmp_root.ip_ReasmTimeout); - arl_expect(arl_ip, "ReasmReqds", &snmp_root.ip_ReasmReqds); - arl_expect(arl_ip, "ReasmOKs", &snmp_root.ip_ReasmOKs); - arl_expect(arl_ip, "ReasmFails", &snmp_root.ip_ReasmFails); - arl_expect(arl_ip, "FragOKs", &snmp_root.ip_FragOKs); - arl_expect(arl_ip, "FragFails", &snmp_root.ip_FragFails); - arl_expect(arl_ip, "FragCreates", &snmp_root.ip_FragCreates); - - arl_icmp = arl_create("snmp/Icmp", arl_callback_str2kernel_uint_t, 60); - arl_expect(arl_icmp, "InMsgs", &snmp_root.icmp_InMsgs); - arl_expect(arl_icmp, "OutMsgs", &snmp_root.icmp_OutMsgs); - arl_expect(arl_icmp, "InErrors", &snmp_root.icmp_InErrors); - arl_expect(arl_icmp, "OutErrors", &snmp_root.icmp_OutErrors); - arl_expect(arl_icmp, "InCsumErrors", &snmp_root.icmp_InCsumErrors); - - arl_icmpmsg = arl_create("snmp/Icmpmsg", arl_callback_str2kernel_uint_t, 60); - arl_expect(arl_icmpmsg, "InType0", &snmp_root.icmpmsg_InEchoReps); - arl_expect(arl_icmpmsg, "OutType0", &snmp_root.icmpmsg_OutEchoReps); - arl_expect(arl_icmpmsg, "InType3", &snmp_root.icmpmsg_InDestUnreachs); - arl_expect(arl_icmpmsg, "OutType3", &snmp_root.icmpmsg_OutDestUnreachs); - arl_expect(arl_icmpmsg, "InType5", &snmp_root.icmpmsg_InRedirects); - arl_expect(arl_icmpmsg, "OutType5", &snmp_root.icmpmsg_OutRedirects); - arl_expect(arl_icmpmsg, "InType8", &snmp_root.icmpmsg_InEchos); - arl_expect(arl_icmpmsg, "OutType8", &snmp_root.icmpmsg_OutEchos); - arl_expect(arl_icmpmsg, "InType9", &snmp_root.icmpmsg_InRouterAdvert); - arl_expect(arl_icmpmsg, "OutType9", &snmp_root.icmpmsg_OutRouterAdvert); - arl_expect(arl_icmpmsg, "InType10", &snmp_root.icmpmsg_InRouterSelect); - arl_expect(arl_icmpmsg, "OutType10", &snmp_root.icmpmsg_OutRouterSelect); - arl_expect(arl_icmpmsg, "InType11", &snmp_root.icmpmsg_InTimeExcds); - arl_expect(arl_icmpmsg, "OutType11", &snmp_root.icmpmsg_OutTimeExcds); - arl_expect(arl_icmpmsg, "InType12", &snmp_root.icmpmsg_InParmProbs); - arl_expect(arl_icmpmsg, "OutType12", &snmp_root.icmpmsg_OutParmProbs); - arl_expect(arl_icmpmsg, "InType13", &snmp_root.icmpmsg_InTimestamps); - arl_expect(arl_icmpmsg, "OutType13", &snmp_root.icmpmsg_OutTimestamps); - arl_expect(arl_icmpmsg, "InType14", &snmp_root.icmpmsg_InTimestampReps); - arl_expect(arl_icmpmsg, "OutType14", &snmp_root.icmpmsg_OutTimestampReps); - - arl_tcp = arl_create("snmp/Tcp", arl_callback_str2kernel_uint_t, 60); - // arl_expect(arl_tcp, "RtoAlgorithm", &snmp_root.tcp_RtoAlgorithm); - // arl_expect(arl_tcp, "RtoMin", &snmp_root.tcp_RtoMin); - // arl_expect(arl_tcp, "RtoMax", &snmp_root.tcp_RtoMax); - arl_expect_custom(arl_tcp, "MaxConn", arl_callback_ssize_t, &snmp_root.tcp_MaxConn); - arl_expect(arl_tcp, "ActiveOpens", &snmp_root.tcp_ActiveOpens); - arl_expect(arl_tcp, "PassiveOpens", &snmp_root.tcp_PassiveOpens); - arl_expect(arl_tcp, "AttemptFails", &snmp_root.tcp_AttemptFails); - arl_expect(arl_tcp, "EstabResets", &snmp_root.tcp_EstabResets); - arl_expect(arl_tcp, "CurrEstab", &snmp_root.tcp_CurrEstab); - arl_expect(arl_tcp, "InSegs", &snmp_root.tcp_InSegs); - arl_expect(arl_tcp, "OutSegs", &snmp_root.tcp_OutSegs); - arl_expect(arl_tcp, "RetransSegs", &snmp_root.tcp_RetransSegs); - arl_expect(arl_tcp, "InErrs", &snmp_root.tcp_InErrs); - arl_expect(arl_tcp, "OutRsts", &snmp_root.tcp_OutRsts); - arl_expect(arl_tcp, "InCsumErrors", &snmp_root.tcp_InCsumErrors); - - arl_udp = arl_create("snmp/Udp", arl_callback_str2kernel_uint_t, 60); - arl_expect(arl_udp, "InDatagrams", &snmp_root.udp_InDatagrams); - arl_expect(arl_udp, "NoPorts", &snmp_root.udp_NoPorts); - arl_expect(arl_udp, "InErrors", &snmp_root.udp_InErrors); - arl_expect(arl_udp, "OutDatagrams", &snmp_root.udp_OutDatagrams); - arl_expect(arl_udp, "RcvbufErrors", &snmp_root.udp_RcvbufErrors); - arl_expect(arl_udp, "SndbufErrors", &snmp_root.udp_SndbufErrors); - arl_expect(arl_udp, "InCsumErrors", &snmp_root.udp_InCsumErrors); - arl_expect(arl_udp, "IgnoredMulti", &snmp_root.udp_IgnoredMulti); - - arl_udplite = arl_create("snmp/Udplite", arl_callback_str2kernel_uint_t, 60); - arl_expect(arl_udplite, "InDatagrams", &snmp_root.udplite_InDatagrams); - arl_expect(arl_udplite, "NoPorts", &snmp_root.udplite_NoPorts); - arl_expect(arl_udplite, "InErrors", &snmp_root.udplite_InErrors); - arl_expect(arl_udplite, "OutDatagrams", &snmp_root.udplite_OutDatagrams); - arl_expect(arl_udplite, "RcvbufErrors", &snmp_root.udplite_RcvbufErrors); - arl_expect(arl_udplite, "SndbufErrors", &snmp_root.udplite_SndbufErrors); - arl_expect(arl_udplite, "InCsumErrors", &snmp_root.udplite_InCsumErrors); - arl_expect(arl_udplite, "IgnoredMulti", &snmp_root.udplite_IgnoredMulti); - - tcp_max_connections_var = rrdvar_custom_host_variable_create(localhost, "tcp_max_connections"); - } - - if(unlikely(!ff)) { - char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/snmp"); - ff = procfile_open(config_get("plugin:proc:/proc/net/snmp", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); - if(unlikely(!ff)) return 1; - } - - ff = procfile_readall(ff); - if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time - - size_t lines = procfile_lines(ff), l; - size_t words, w; - - for(l = 0; l < lines ;l++) { - char *key = procfile_lineword(ff, l, 0); - uint32_t hash = simple_hash(key); - - if(unlikely(hash == hash_ip && strcmp(key, "Ip") == 0)) { - size_t h = l++; - - if(strcmp(procfile_lineword(ff, l, 0), "Ip") != 0) { - error("Cannot read Ip line from /proc/net/snmp."); - break; - } - - words = procfile_linewords(ff, l); - if(words < 3) { - error("Cannot read /proc/net/snmp Ip line. Expected 3+ params, read %zu.", words); - continue; - } - - arl_begin(arl_ip); - for(w = 1; w < words ; w++) { - if (unlikely(arl_check(arl_ip, procfile_lineword(ff, h, w), procfile_lineword(ff, l, w)) != 0)) - break; - } - - // -------------------------------------------------------------------- - - if(do_ip_packets == CONFIG_BOOLEAN_YES || (do_ip_packets == CONFIG_BOOLEAN_AUTO && - (snmp_root.ip_OutRequests || - snmp_root.ip_InReceives || - snmp_root.ip_ForwDatagrams || - snmp_root.ip_InDelivers || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ip_packets = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_InReceives = NULL, - *rd_OutRequests = NULL, - *rd_ForwDatagrams = NULL, - *rd_InDelivers = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "packets" - , NULL - , "packets" - , NULL - , "IPv4 Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_PACKETS - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InReceives = rrddim_add(st, "InReceives", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutRequests = rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_ForwDatagrams = rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InDelivers = rrddim_add(st, "InDelivers", "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_OutRequests, (collected_number)snmp_root.ip_OutRequests); - rrddim_set_by_pointer(st, rd_InReceives, (collected_number)snmp_root.ip_InReceives); - rrddim_set_by_pointer(st, rd_ForwDatagrams, (collected_number)snmp_root.ip_ForwDatagrams); - rrddim_set_by_pointer(st, rd_InDelivers, (collected_number)snmp_root.ip_InDelivers); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_ip_fragsout == CONFIG_BOOLEAN_YES || (do_ip_fragsout == CONFIG_BOOLEAN_AUTO && - (snmp_root.ip_FragOKs || - snmp_root.ip_FragFails || - snmp_root.ip_FragCreates || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ip_fragsout = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_FragOKs = NULL, - *rd_FragFails = NULL, - *rd_FragCreates = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "fragsout" - , NULL - , "fragments" - , NULL - , "IPv4 Fragments Sent" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_FRAGMENTS - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_FragOKs = rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_FragFails = rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_FragCreates = rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_FragOKs, (collected_number)snmp_root.ip_FragOKs); - rrddim_set_by_pointer(st, rd_FragFails, (collected_number)snmp_root.ip_FragFails); - rrddim_set_by_pointer(st, rd_FragCreates, (collected_number)snmp_root.ip_FragCreates); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_ip_fragsin == CONFIG_BOOLEAN_YES || (do_ip_fragsin == CONFIG_BOOLEAN_AUTO && - (snmp_root.ip_ReasmOKs || - snmp_root.ip_ReasmFails || - snmp_root.ip_ReasmReqds || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ip_fragsin = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_ReasmOKs = NULL, - *rd_ReasmFails = NULL, - *rd_ReasmReqds = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "fragsin" - , NULL - , "fragments" - , NULL - , "IPv4 Fragments Reassembly" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_FRAGMENTS + 1 - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_ReasmOKs = rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_ReasmFails = rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_ReasmReqds = rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_ReasmOKs, (collected_number)snmp_root.ip_ReasmOKs); - rrddim_set_by_pointer(st, rd_ReasmFails, (collected_number)snmp_root.ip_ReasmFails); - rrddim_set_by_pointer(st, rd_ReasmReqds, (collected_number)snmp_root.ip_ReasmReqds); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_ip_errors == CONFIG_BOOLEAN_YES || (do_ip_errors == CONFIG_BOOLEAN_AUTO && - (snmp_root.ip_InDiscards || - snmp_root.ip_OutDiscards || - snmp_root.ip_InHdrErrors || - snmp_root.ip_InAddrErrors || - snmp_root.ip_InUnknownProtos || - snmp_root.ip_OutNoRoutes || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ip_errors = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_InDiscards = NULL, - *rd_OutDiscards = NULL, - *rd_InHdrErrors = NULL, - *rd_OutNoRoutes = NULL, - *rd_InAddrErrors = NULL, - *rd_InUnknownProtos = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "errors" - , NULL - , "errors" - , NULL - , "IPv4 Errors" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_ERRORS - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_InDiscards = rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutDiscards = rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - - rd_InHdrErrors = rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutNoRoutes = rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - - rd_InAddrErrors = rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InUnknownProtos = rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InDiscards, (collected_number)snmp_root.ip_InDiscards); - rrddim_set_by_pointer(st, rd_OutDiscards, (collected_number)snmp_root.ip_OutDiscards); - rrddim_set_by_pointer(st, rd_InHdrErrors, (collected_number)snmp_root.ip_InHdrErrors); - rrddim_set_by_pointer(st, rd_InAddrErrors, (collected_number)snmp_root.ip_InAddrErrors); - rrddim_set_by_pointer(st, rd_InUnknownProtos, (collected_number)snmp_root.ip_InUnknownProtos); - rrddim_set_by_pointer(st, rd_OutNoRoutes, (collected_number)snmp_root.ip_OutNoRoutes); - rrdset_done(st); - } - } - else if(unlikely(hash == hash_icmp && strcmp(key, "Icmp") == 0)) { - size_t h = l++; - - if(strcmp(procfile_lineword(ff, l, 0), "Icmp") != 0) { - error("Cannot read Icmp line from /proc/net/snmp."); - break; - } - - words = procfile_linewords(ff, l); - if(words < 3) { - error("Cannot read /proc/net/snmp Icmp line. Expected 3+ params, read %zu.", words); - continue; - } - - arl_begin(arl_icmp); - for(w = 1; w < words ; w++) { - if (unlikely(arl_check(arl_icmp, procfile_lineword(ff, h, w), procfile_lineword(ff, l, w)) != 0)) - break; - } - - // -------------------------------------------------------------------- - - if(do_icmp_packets == CONFIG_BOOLEAN_YES || (do_icmp_packets == CONFIG_BOOLEAN_AUTO && - (snmp_root.icmp_InMsgs || - snmp_root.icmp_OutMsgs || - snmp_root.icmp_InErrors || - snmp_root.icmp_OutErrors || - snmp_root.icmp_InCsumErrors || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp_packets = CONFIG_BOOLEAN_YES; - - { - static RRDSET *st_packets = NULL; - static RRDDIM *rd_InMsgs = NULL, - *rd_OutMsgs = NULL; - - if(unlikely(!st_packets)) { - st_packets = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "icmp" - , NULL - , "icmp" - , NULL - , "IPv4 ICMP Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_ICMP - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InMsgs = rrddim_add(st_packets, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutMsgs = rrddim_add(st_packets, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st_packets); - - rrddim_set_by_pointer(st_packets, rd_InMsgs, (collected_number)snmp_root.icmp_InMsgs); - rrddim_set_by_pointer(st_packets, rd_OutMsgs, (collected_number)snmp_root.icmp_OutMsgs); - - rrdset_done(st_packets); - } - - { - static RRDSET *st_errors = NULL; - static RRDDIM *rd_InErrors = NULL, - *rd_OutErrors = NULL, - *rd_InCsumErrors = NULL; - - if(unlikely(!st_errors)) { - st_errors = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "icmp_errors" - , NULL - , "icmp" - , NULL - , "IPv4 ICMP Errors" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_ICMP + 1 - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InErrors = rrddim_add(st_errors, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutErrors = rrddim_add(st_errors, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InCsumErrors = rrddim_add(st_errors, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st_errors); - - rrddim_set_by_pointer(st_errors, rd_InErrors, (collected_number)snmp_root.icmp_InErrors); - rrddim_set_by_pointer(st_errors, rd_OutErrors, (collected_number)snmp_root.icmp_OutErrors); - rrddim_set_by_pointer(st_errors, rd_InCsumErrors, (collected_number)snmp_root.icmp_InCsumErrors); - - rrdset_done(st_errors); - } - } - } - else if(unlikely(hash == hash_icmpmsg && strcmp(key, "IcmpMsg") == 0)) { - size_t h = l++; - - if(strcmp(procfile_lineword(ff, l, 0), "IcmpMsg") != 0) { - error("Cannot read IcmpMsg line from /proc/net/snmp."); - break; - } - - words = procfile_linewords(ff, l); - if(words < 2) { - error("Cannot read /proc/net/snmp IcmpMsg line. Expected 2+ params, read %zu.", words); - continue; - } - - arl_begin(arl_icmpmsg); - for(w = 1; w < words ; w++) { - if (unlikely(arl_check(arl_icmpmsg, procfile_lineword(ff, h, w), procfile_lineword(ff, l, w)) != 0)) - break; - } - - // -------------------------------------------------------------------- - - if(do_icmpmsg == CONFIG_BOOLEAN_YES || (do_icmpmsg == CONFIG_BOOLEAN_AUTO && - (snmp_root.icmpmsg_InEchoReps || - snmp_root.icmpmsg_OutEchoReps || - snmp_root.icmpmsg_InDestUnreachs || - snmp_root.icmpmsg_OutDestUnreachs || - snmp_root.icmpmsg_InRedirects || - snmp_root.icmpmsg_OutRedirects || - snmp_root.icmpmsg_InEchos || - snmp_root.icmpmsg_OutEchos || - snmp_root.icmpmsg_InRouterAdvert || - snmp_root.icmpmsg_OutRouterAdvert || - snmp_root.icmpmsg_InRouterSelect || - snmp_root.icmpmsg_OutRouterSelect || - snmp_root.icmpmsg_InTimeExcds || - snmp_root.icmpmsg_OutTimeExcds || - snmp_root.icmpmsg_InParmProbs || - snmp_root.icmpmsg_OutParmProbs || - snmp_root.icmpmsg_InTimestamps || - snmp_root.icmpmsg_OutTimestamps || - snmp_root.icmpmsg_InTimestampReps || - snmp_root.icmpmsg_OutTimestampReps || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmpmsg = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_InEchoReps = NULL, - *rd_OutEchoReps = NULL, - *rd_InDestUnreachs = NULL, - *rd_OutDestUnreachs = NULL, - *rd_InRedirects = NULL, - *rd_OutRedirects = NULL, - *rd_InEchos = NULL, - *rd_OutEchos = NULL, - *rd_InRouterAdvert = NULL, - *rd_OutRouterAdvert = NULL, - *rd_InRouterSelect = NULL, - *rd_OutRouterSelect = NULL, - *rd_InTimeExcds = NULL, - *rd_OutTimeExcds = NULL, - *rd_InParmProbs = NULL, - *rd_OutParmProbs = NULL, - *rd_InTimestamps = NULL, - *rd_OutTimestamps = NULL, - *rd_InTimestampReps = NULL, - *rd_OutTimestampReps = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "icmpmsg" - , NULL - , "icmp" - , NULL - , "IPv4 ICMP Messages" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_ICMP + 2 - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InEchoReps = rrddim_add(st, "InType0", "InEchoReps", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutEchoReps = rrddim_add(st, "OutType0", "OutEchoReps", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InDestUnreachs = rrddim_add(st, "InType3", "InDestUnreachs", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutDestUnreachs = rrddim_add(st, "OutType3", "OutDestUnreachs", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InRedirects = rrddim_add(st, "InType5", "InRedirects", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutRedirects = rrddim_add(st, "OutType5", "OutRedirects", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InEchos = rrddim_add(st, "InType8", "InEchos", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutEchos = rrddim_add(st, "OutType8", "OutEchos", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InRouterAdvert = rrddim_add(st, "InType9", "InRouterAdvert", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutRouterAdvert = rrddim_add(st, "OutType9", "OutRouterAdvert", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InRouterSelect = rrddim_add(st, "InType10", "InRouterSelect", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutRouterSelect = rrddim_add(st, "OutType10", "OutRouterSelect", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InTimeExcds = rrddim_add(st, "InType11", "InTimeExcds", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutTimeExcds = rrddim_add(st, "OutType11", "OutTimeExcds", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InParmProbs = rrddim_add(st, "InType12", "InParmProbs", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutParmProbs = rrddim_add(st, "OutType12", "OutParmProbs", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InTimestamps = rrddim_add(st, "InType13", "InTimestamps", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutTimestamps = rrddim_add(st, "OutType13", "OutTimestamps", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InTimestampReps = rrddim_add(st, "InType14", "InTimestampReps", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutTimestampReps = rrddim_add(st, "OutType14", "OutTimestampReps", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InEchoReps, (collected_number)snmp_root.icmpmsg_InEchoReps); - rrddim_set_by_pointer(st, rd_OutEchoReps, (collected_number)snmp_root.icmpmsg_OutEchoReps); - rrddim_set_by_pointer(st, rd_InDestUnreachs, (collected_number)snmp_root.icmpmsg_InDestUnreachs); - rrddim_set_by_pointer(st, rd_OutDestUnreachs, (collected_number)snmp_root.icmpmsg_OutDestUnreachs); - rrddim_set_by_pointer(st, rd_InRedirects, (collected_number)snmp_root.icmpmsg_InRedirects); - rrddim_set_by_pointer(st, rd_OutRedirects, (collected_number)snmp_root.icmpmsg_OutRedirects); - rrddim_set_by_pointer(st, rd_InEchos, (collected_number)snmp_root.icmpmsg_InEchos); - rrddim_set_by_pointer(st, rd_OutEchos, (collected_number)snmp_root.icmpmsg_OutEchos); - rrddim_set_by_pointer(st, rd_InRouterAdvert, (collected_number)snmp_root.icmpmsg_InRouterAdvert); - rrddim_set_by_pointer(st, rd_OutRouterAdvert, (collected_number)snmp_root.icmpmsg_OutRouterAdvert); - rrddim_set_by_pointer(st, rd_InRouterSelect, (collected_number)snmp_root.icmpmsg_InRouterSelect); - rrddim_set_by_pointer(st, rd_OutRouterSelect, (collected_number)snmp_root.icmpmsg_OutRouterSelect); - rrddim_set_by_pointer(st, rd_InTimeExcds, (collected_number)snmp_root.icmpmsg_InTimeExcds); - rrddim_set_by_pointer(st, rd_OutTimeExcds, (collected_number)snmp_root.icmpmsg_OutTimeExcds); - rrddim_set_by_pointer(st, rd_InParmProbs, (collected_number)snmp_root.icmpmsg_InParmProbs); - rrddim_set_by_pointer(st, rd_OutParmProbs, (collected_number)snmp_root.icmpmsg_OutParmProbs); - rrddim_set_by_pointer(st, rd_InTimestamps, (collected_number)snmp_root.icmpmsg_InTimestamps); - rrddim_set_by_pointer(st, rd_OutTimestamps, (collected_number)snmp_root.icmpmsg_OutTimestamps); - rrddim_set_by_pointer(st, rd_InTimestampReps, (collected_number)snmp_root.icmpmsg_InTimestampReps); - rrddim_set_by_pointer(st, rd_OutTimestampReps, (collected_number)snmp_root.icmpmsg_OutTimestampReps); - - rrdset_done(st); - } - } - else if(unlikely(hash == hash_tcp && strcmp(key, "Tcp") == 0)) { - size_t h = l++; - - if(strcmp(procfile_lineword(ff, l, 0), "Tcp") != 0) { - error("Cannot read Tcp line from /proc/net/snmp."); - break; - } - - words = procfile_linewords(ff, l); - if(words < 3) { - error("Cannot read /proc/net/snmp Tcp line. Expected 3+ params, read %zu.", words); - continue; - } - - arl_begin(arl_tcp); - for(w = 1; w < words ; w++) { - if (unlikely(arl_check(arl_tcp, procfile_lineword(ff, h, w), procfile_lineword(ff, l, w)) != 0)) - break; - } - - // -------------------------------------------------------------------- - - // this is smart enough to update it, only when it is changed - rrdvar_custom_host_variable_set(localhost, tcp_max_connections_var, snmp_root.tcp_MaxConn); - - // -------------------------------------------------------------------- - - // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html - if(do_tcp_sockets == CONFIG_BOOLEAN_YES || (do_tcp_sockets == CONFIG_BOOLEAN_AUTO && - (snmp_root.tcp_CurrEstab || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcp_sockets = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_CurrEstab = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "tcpsock" - , NULL - , "tcp" - , NULL - , "IPv4 TCP Connections" - , "active connections" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_TCP - , update_every - , RRDSET_TYPE_LINE - ); - - rd_CurrEstab = rrddim_add(st, "CurrEstab", "connections", 1, 1, RRD_ALGORITHM_ABSOLUTE); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_CurrEstab, (collected_number)snmp_root.tcp_CurrEstab); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_tcp_packets == CONFIG_BOOLEAN_YES || (do_tcp_packets == CONFIG_BOOLEAN_AUTO && - (snmp_root.tcp_InSegs || - snmp_root.tcp_OutSegs || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcp_packets = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_InSegs = NULL, - *rd_OutSegs = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "tcppackets" - , NULL - , "tcp" - , NULL - , "IPv4 TCP Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_TCP + 4 - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InSegs = rrddim_add(st, "InSegs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutSegs = rrddim_add(st, "OutSegs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InSegs, (collected_number)snmp_root.tcp_InSegs); - rrddim_set_by_pointer(st, rd_OutSegs, (collected_number)snmp_root.tcp_OutSegs); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_tcp_errors == CONFIG_BOOLEAN_YES || (do_tcp_errors == CONFIG_BOOLEAN_AUTO && - (snmp_root.tcp_InErrs || - snmp_root.tcp_InCsumErrors || - snmp_root.tcp_RetransSegs || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcp_errors = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_InErrs = NULL, - *rd_InCsumErrors = NULL, - *rd_RetransSegs = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "tcperrors" - , NULL - , "tcp" - , NULL - , "IPv4 TCP Errors" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_TCP + 20 - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_InErrs = rrddim_add(st, "InErrs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_RetransSegs = rrddim_add(st, "RetransSegs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InErrs, (collected_number)snmp_root.tcp_InErrs); - rrddim_set_by_pointer(st, rd_InCsumErrors, (collected_number)snmp_root.tcp_InCsumErrors); - rrddim_set_by_pointer(st, rd_RetransSegs, (collected_number)snmp_root.tcp_RetransSegs); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_tcp_opens == CONFIG_BOOLEAN_YES || (do_tcp_opens == CONFIG_BOOLEAN_AUTO && - (snmp_root.tcp_ActiveOpens || - snmp_root.tcp_PassiveOpens || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcp_opens = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_ActiveOpens = NULL, - *rd_PassiveOpens = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "tcpopens" - , NULL - , "tcp" - , NULL - , "IPv4 TCP Opens" - , "connections/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_TCP + 5 - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_ActiveOpens = rrddim_add(st, "ActiveOpens", "active", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_PassiveOpens = rrddim_add(st, "PassiveOpens", "passive", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_ActiveOpens, (collected_number)snmp_root.tcp_ActiveOpens); - rrddim_set_by_pointer(st, rd_PassiveOpens, (collected_number)snmp_root.tcp_PassiveOpens); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_tcp_handshake == CONFIG_BOOLEAN_YES || (do_tcp_handshake == CONFIG_BOOLEAN_AUTO && - (snmp_root.tcp_EstabResets || - snmp_root.tcp_OutRsts || - snmp_root.tcp_AttemptFails || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_tcp_handshake = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_EstabResets = NULL, - *rd_OutRsts = NULL, - *rd_AttemptFails = NULL, - *rd_TCPSynRetrans = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "tcphandshake" - , NULL - , "tcp" - , NULL - , "IPv4 TCP Handshake Issues" - , "events/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_TCP + 30 - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_EstabResets = rrddim_add(st, "EstabResets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutRsts = rrddim_add(st, "OutRsts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_AttemptFails = rrddim_add(st, "AttemptFails", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_TCPSynRetrans = rrddim_add(st, "TCPSynRetrans", "SynRetrans", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_EstabResets, (collected_number)snmp_root.tcp_EstabResets); - rrddim_set_by_pointer(st, rd_OutRsts, (collected_number)snmp_root.tcp_OutRsts); - rrddim_set_by_pointer(st, rd_AttemptFails, (collected_number)snmp_root.tcp_AttemptFails); - rrddim_set_by_pointer(st, rd_TCPSynRetrans, tcpext_TCPSynRetrans); - rrdset_done(st); - } - } - else if(unlikely(hash == hash_udp && strcmp(key, "Udp") == 0)) { - size_t h = l++; - - if(strcmp(procfile_lineword(ff, l, 0), "Udp") != 0) { - error("Cannot read Udp line from /proc/net/snmp."); - break; - } - - words = procfile_linewords(ff, l); - if(words < 3) { - error("Cannot read /proc/net/snmp Udp line. Expected 3+ params, read %zu.", words); - continue; - } - - arl_begin(arl_udp); - for(w = 1; w < words ; w++) { - if (unlikely(arl_check(arl_udp, procfile_lineword(ff, h, w), procfile_lineword(ff, l, w)) != 0)) - break; - } - - // -------------------------------------------------------------------- - - // see http://net-snmp.sourceforge.net/docs/mibs/udp.html - if(do_udp_packets == CONFIG_BOOLEAN_YES || (do_udp_packets == CONFIG_BOOLEAN_AUTO && - (snmp_root.udp_InDatagrams || - snmp_root.udp_OutDatagrams || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_udp_packets = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_InDatagrams = NULL, - *rd_OutDatagrams = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "udppackets" - , NULL - , "udp" - , NULL - , "IPv4 UDP Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_UDP - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InDatagrams = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutDatagrams = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InDatagrams, (collected_number)snmp_root.udp_InDatagrams); - rrddim_set_by_pointer(st, rd_OutDatagrams, (collected_number)snmp_root.udp_OutDatagrams); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_udp_errors == CONFIG_BOOLEAN_YES || (do_udp_errors == CONFIG_BOOLEAN_AUTO && - (snmp_root.udp_InErrors || - snmp_root.udp_NoPorts || - snmp_root.udp_RcvbufErrors || - snmp_root.udp_SndbufErrors || - snmp_root.udp_InCsumErrors || - snmp_root.udp_IgnoredMulti || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_udp_errors = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_RcvbufErrors = NULL, - *rd_SndbufErrors = NULL, - *rd_InErrors = NULL, - *rd_NoPorts = NULL, - *rd_InCsumErrors = NULL, - *rd_IgnoredMulti = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "udperrors" - , NULL - , "udp" - , NULL - , "IPv4 UDP Errors" - , "events/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_UDP + 10 - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_RcvbufErrors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_SndbufErrors = rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_NoPorts = rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_IgnoredMulti = rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InErrors, (collected_number)snmp_root.udp_InErrors); - rrddim_set_by_pointer(st, rd_NoPorts, (collected_number)snmp_root.udp_NoPorts); - rrddim_set_by_pointer(st, rd_RcvbufErrors, (collected_number)snmp_root.udp_RcvbufErrors); - rrddim_set_by_pointer(st, rd_SndbufErrors, (collected_number)snmp_root.udp_SndbufErrors); - rrddim_set_by_pointer(st, rd_InCsumErrors, (collected_number)snmp_root.udp_InCsumErrors); - rrddim_set_by_pointer(st, rd_IgnoredMulti, (collected_number)snmp_root.udp_IgnoredMulti); - rrdset_done(st); - } - } - else if(unlikely(hash == hash_udplite && strcmp(key, "UdpLite") == 0)) { - size_t h = l++; - - if(strcmp(procfile_lineword(ff, l, 0), "UdpLite") != 0) { - error("Cannot read UdpLite line from /proc/net/snmp."); - break; - } - - words = procfile_linewords(ff, l); - if(words < 3) { - error("Cannot read /proc/net/snmp UdpLite line. Expected 3+ params, read %zu.", words); - continue; - } - - arl_begin(arl_udplite); - for(w = 1; w < words ; w++) { - if (unlikely(arl_check(arl_udplite, procfile_lineword(ff, h, w), procfile_lineword(ff, l, w)) != 0)) - break; - } - - // -------------------------------------------------------------------- - - if(do_udplite_packets == CONFIG_BOOLEAN_YES || (do_udplite_packets == CONFIG_BOOLEAN_AUTO && - (snmp_root.udplite_InDatagrams || - snmp_root.udplite_OutDatagrams || - snmp_root.udplite_NoPorts || - snmp_root.udplite_InErrors || - snmp_root.udplite_InCsumErrors || - snmp_root.udplite_RcvbufErrors || - snmp_root.udplite_SndbufErrors || - snmp_root.udplite_IgnoredMulti || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_udplite_packets = CONFIG_BOOLEAN_YES; - - { - static RRDSET *st = NULL; - static RRDDIM *rd_InDatagrams = NULL, - *rd_OutDatagrams = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "udplite" - , NULL - , "udplite" - , NULL - , "IPv4 UDPLite Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_UDPLITE - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InDatagrams = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutDatagrams = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InDatagrams, (collected_number)snmp_root.udplite_InDatagrams); - rrddim_set_by_pointer(st, rd_OutDatagrams, (collected_number)snmp_root.udplite_OutDatagrams); - rrdset_done(st); - } - - { - static RRDSET *st = NULL; - static RRDDIM *rd_RcvbufErrors = NULL, - *rd_SndbufErrors = NULL, - *rd_InErrors = NULL, - *rd_NoPorts = NULL, - *rd_InCsumErrors = NULL, - *rd_IgnoredMulti = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP - , "udplite_errors" - , NULL - , "udplite" - , NULL - , "IPv4 UDPLite Errors" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP_NAME - , NETDATA_CHART_PRIO_IPV4_UDPLITE + 10 - , update_every - , RRDSET_TYPE_LINE); - - rd_RcvbufErrors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_SndbufErrors = rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_NoPorts = rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_IgnoredMulti = rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_NoPorts, (collected_number)snmp_root.udplite_NoPorts); - rrddim_set_by_pointer(st, rd_InErrors, (collected_number)snmp_root.udplite_InErrors); - rrddim_set_by_pointer(st, rd_InCsumErrors, (collected_number)snmp_root.udplite_InCsumErrors); - rrddim_set_by_pointer(st, rd_RcvbufErrors, (collected_number)snmp_root.udplite_RcvbufErrors); - rrddim_set_by_pointer(st, rd_SndbufErrors, (collected_number)snmp_root.udplite_SndbufErrors); - rrddim_set_by_pointer(st, rd_IgnoredMulti, (collected_number)snmp_root.udplite_IgnoredMulti); - rrdset_done(st); - } - } - } - } - - return 0; -} - diff --git a/collectors/proc.plugin/proc_net_snmp6.c b/collectors/proc.plugin/proc_net_snmp6.c deleted file mode 100644 index 445e0dcab..000000000 --- a/collectors/proc.plugin/proc_net_snmp6.c +++ /dev/null @@ -1,1293 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "plugin_proc.h" - -#define RRD_TYPE_NET_SNMP6 "ipv6" -#define PLUGIN_PROC_MODULE_NET_SNMP6_NAME "/proc/net/snmp6" - -int do_proc_net_snmp6(int update_every, usec_t dt) { - (void)dt; - - static procfile *ff = NULL; - - static int do_ip_packets = -1, - do_ip_fragsout = -1, - do_ip_fragsin = -1, - do_ip_errors = -1, - do_udplite_packets = -1, - do_udplite_errors = -1, - do_udp_packets = -1, - do_udp_errors = -1, - do_bandwidth = -1, - do_mcast = -1, - do_bcast = -1, - do_mcast_p = -1, - do_icmp = -1, - do_icmp_redir = -1, - do_icmp_errors = -1, - do_icmp_echos = -1, - do_icmp_groupmemb = -1, - do_icmp_router = -1, - do_icmp_neighbor = -1, - do_icmp_mldv2 = -1, - do_icmp_types = -1, - do_ect = -1; - - static ARL_BASE *arl_base = NULL; - - static unsigned long long Ip6InReceives = 0ULL; - static unsigned long long Ip6InHdrErrors = 0ULL; - static unsigned long long Ip6InTooBigErrors = 0ULL; - static unsigned long long Ip6InNoRoutes = 0ULL; - static unsigned long long Ip6InAddrErrors = 0ULL; - static unsigned long long Ip6InUnknownProtos = 0ULL; - static unsigned long long Ip6InTruncatedPkts = 0ULL; - static unsigned long long Ip6InDiscards = 0ULL; - static unsigned long long Ip6InDelivers = 0ULL; - static unsigned long long Ip6OutForwDatagrams = 0ULL; - static unsigned long long Ip6OutRequests = 0ULL; - static unsigned long long Ip6OutDiscards = 0ULL; - static unsigned long long Ip6OutNoRoutes = 0ULL; - static unsigned long long Ip6ReasmTimeout = 0ULL; - static unsigned long long Ip6ReasmReqds = 0ULL; - static unsigned long long Ip6ReasmOKs = 0ULL; - static unsigned long long Ip6ReasmFails = 0ULL; - static unsigned long long Ip6FragOKs = 0ULL; - static unsigned long long Ip6FragFails = 0ULL; - static unsigned long long Ip6FragCreates = 0ULL; - static unsigned long long Ip6InMcastPkts = 0ULL; - static unsigned long long Ip6OutMcastPkts = 0ULL; - static unsigned long long Ip6InOctets = 0ULL; - static unsigned long long Ip6OutOctets = 0ULL; - static unsigned long long Ip6InMcastOctets = 0ULL; - static unsigned long long Ip6OutMcastOctets = 0ULL; - static unsigned long long Ip6InBcastOctets = 0ULL; - static unsigned long long Ip6OutBcastOctets = 0ULL; - static unsigned long long Ip6InNoECTPkts = 0ULL; - static unsigned long long Ip6InECT1Pkts = 0ULL; - static unsigned long long Ip6InECT0Pkts = 0ULL; - static unsigned long long Ip6InCEPkts = 0ULL; - static unsigned long long Icmp6InMsgs = 0ULL; - static unsigned long long Icmp6InErrors = 0ULL; - static unsigned long long Icmp6OutMsgs = 0ULL; - static unsigned long long Icmp6OutErrors = 0ULL; - static unsigned long long Icmp6InCsumErrors = 0ULL; - static unsigned long long Icmp6InDestUnreachs = 0ULL; - static unsigned long long Icmp6InPktTooBigs = 0ULL; - static unsigned long long Icmp6InTimeExcds = 0ULL; - static unsigned long long Icmp6InParmProblems = 0ULL; - static unsigned long long Icmp6InEchos = 0ULL; - static unsigned long long Icmp6InEchoReplies = 0ULL; - static unsigned long long Icmp6InGroupMembQueries = 0ULL; - static unsigned long long Icmp6InGroupMembResponses = 0ULL; - static unsigned long long Icmp6InGroupMembReductions = 0ULL; - static unsigned long long Icmp6InRouterSolicits = 0ULL; - static unsigned long long Icmp6InRouterAdvertisements = 0ULL; - static unsigned long long Icmp6InNeighborSolicits = 0ULL; - static unsigned long long Icmp6InNeighborAdvertisements = 0ULL; - static unsigned long long Icmp6InRedirects = 0ULL; - static unsigned long long Icmp6InMLDv2Reports = 0ULL; - static unsigned long long Icmp6OutDestUnreachs = 0ULL; - static unsigned long long Icmp6OutPktTooBigs = 0ULL; - static unsigned long long Icmp6OutTimeExcds = 0ULL; - static unsigned long long Icmp6OutParmProblems = 0ULL; - static unsigned long long Icmp6OutEchos = 0ULL; - static unsigned long long Icmp6OutEchoReplies = 0ULL; - static unsigned long long Icmp6OutGroupMembQueries = 0ULL; - static unsigned long long Icmp6OutGroupMembResponses = 0ULL; - static unsigned long long Icmp6OutGroupMembReductions = 0ULL; - static unsigned long long Icmp6OutRouterSolicits = 0ULL; - static unsigned long long Icmp6OutRouterAdvertisements = 0ULL; - static unsigned long long Icmp6OutNeighborSolicits = 0ULL; - static unsigned long long Icmp6OutNeighborAdvertisements = 0ULL; - static unsigned long long Icmp6OutRedirects = 0ULL; - static unsigned long long Icmp6OutMLDv2Reports = 0ULL; - static unsigned long long Icmp6InType1 = 0ULL; - static unsigned long long Icmp6InType128 = 0ULL; - static unsigned long long Icmp6InType129 = 0ULL; - static unsigned long long Icmp6InType136 = 0ULL; - static unsigned long long Icmp6OutType1 = 0ULL; - static unsigned long long Icmp6OutType128 = 0ULL; - static unsigned long long Icmp6OutType129 = 0ULL; - static unsigned long long Icmp6OutType133 = 0ULL; - static unsigned long long Icmp6OutType135 = 0ULL; - static unsigned long long Icmp6OutType143 = 0ULL; - static unsigned long long Udp6InDatagrams = 0ULL; - static unsigned long long Udp6NoPorts = 0ULL; - static unsigned long long Udp6InErrors = 0ULL; - static unsigned long long Udp6OutDatagrams = 0ULL; - static unsigned long long Udp6RcvbufErrors = 0ULL; - static unsigned long long Udp6SndbufErrors = 0ULL; - static unsigned long long Udp6InCsumErrors = 0ULL; - static unsigned long long Udp6IgnoredMulti = 0ULL; - static unsigned long long UdpLite6InDatagrams = 0ULL; - static unsigned long long UdpLite6NoPorts = 0ULL; - static unsigned long long UdpLite6InErrors = 0ULL; - static unsigned long long UdpLite6OutDatagrams = 0ULL; - static unsigned long long UdpLite6RcvbufErrors = 0ULL; - static unsigned long long UdpLite6SndbufErrors = 0ULL; - static unsigned long long UdpLite6InCsumErrors = 0ULL; - - if(unlikely(!arl_base)) { - do_ip_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_BOOLEAN_AUTO); - do_ip_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_BOOLEAN_AUTO); - do_ip_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_BOOLEAN_AUTO); - do_ip_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_BOOLEAN_AUTO); - do_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_BOOLEAN_AUTO); - do_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_BOOLEAN_AUTO); - do_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_BOOLEAN_AUTO); - do_udplite_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_BOOLEAN_AUTO); - do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_BOOLEAN_AUTO); - do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_BOOLEAN_AUTO); - do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_BOOLEAN_AUTO); - do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_BOOLEAN_AUTO); - do_icmp = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_BOOLEAN_AUTO); - do_icmp_redir = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_BOOLEAN_AUTO); - do_icmp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_BOOLEAN_AUTO); - do_icmp_echos = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_BOOLEAN_AUTO); - do_icmp_groupmemb = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_BOOLEAN_AUTO); - do_icmp_router = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_BOOLEAN_AUTO); - do_icmp_neighbor = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_BOOLEAN_AUTO); - do_icmp_mldv2 = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_BOOLEAN_AUTO); - do_icmp_types = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_BOOLEAN_AUTO); - do_ect = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_BOOLEAN_AUTO); - - arl_base = arl_create("snmp6", NULL, 60); - arl_expect(arl_base, "Ip6InReceives", &Ip6InReceives); - arl_expect(arl_base, "Ip6InHdrErrors", &Ip6InHdrErrors); - arl_expect(arl_base, "Ip6InTooBigErrors", &Ip6InTooBigErrors); - arl_expect(arl_base, "Ip6InNoRoutes", &Ip6InNoRoutes); - arl_expect(arl_base, "Ip6InAddrErrors", &Ip6InAddrErrors); - arl_expect(arl_base, "Ip6InUnknownProtos", &Ip6InUnknownProtos); - arl_expect(arl_base, "Ip6InTruncatedPkts", &Ip6InTruncatedPkts); - arl_expect(arl_base, "Ip6InDiscards", &Ip6InDiscards); - arl_expect(arl_base, "Ip6InDelivers", &Ip6InDelivers); - arl_expect(arl_base, "Ip6OutForwDatagrams", &Ip6OutForwDatagrams); - arl_expect(arl_base, "Ip6OutRequests", &Ip6OutRequests); - arl_expect(arl_base, "Ip6OutDiscards", &Ip6OutDiscards); - arl_expect(arl_base, "Ip6OutNoRoutes", &Ip6OutNoRoutes); - arl_expect(arl_base, "Ip6ReasmTimeout", &Ip6ReasmTimeout); - arl_expect(arl_base, "Ip6ReasmReqds", &Ip6ReasmReqds); - arl_expect(arl_base, "Ip6ReasmOKs", &Ip6ReasmOKs); - arl_expect(arl_base, "Ip6ReasmFails", &Ip6ReasmFails); - arl_expect(arl_base, "Ip6FragOKs", &Ip6FragOKs); - arl_expect(arl_base, "Ip6FragFails", &Ip6FragFails); - arl_expect(arl_base, "Ip6FragCreates", &Ip6FragCreates); - arl_expect(arl_base, "Ip6InMcastPkts", &Ip6InMcastPkts); - arl_expect(arl_base, "Ip6OutMcastPkts", &Ip6OutMcastPkts); - arl_expect(arl_base, "Ip6InOctets", &Ip6InOctets); - arl_expect(arl_base, "Ip6OutOctets", &Ip6OutOctets); - arl_expect(arl_base, "Ip6InMcastOctets", &Ip6InMcastOctets); - arl_expect(arl_base, "Ip6OutMcastOctets", &Ip6OutMcastOctets); - arl_expect(arl_base, "Ip6InBcastOctets", &Ip6InBcastOctets); - arl_expect(arl_base, "Ip6OutBcastOctets", &Ip6OutBcastOctets); - arl_expect(arl_base, "Ip6InNoECTPkts", &Ip6InNoECTPkts); - arl_expect(arl_base, "Ip6InECT1Pkts", &Ip6InECT1Pkts); - arl_expect(arl_base, "Ip6InECT0Pkts", &Ip6InECT0Pkts); - arl_expect(arl_base, "Ip6InCEPkts", &Ip6InCEPkts); - arl_expect(arl_base, "Icmp6InMsgs", &Icmp6InMsgs); - arl_expect(arl_base, "Icmp6InErrors", &Icmp6InErrors); - arl_expect(arl_base, "Icmp6OutMsgs", &Icmp6OutMsgs); - arl_expect(arl_base, "Icmp6OutErrors", &Icmp6OutErrors); - arl_expect(arl_base, "Icmp6InCsumErrors", &Icmp6InCsumErrors); - arl_expect(arl_base, "Icmp6InDestUnreachs", &Icmp6InDestUnreachs); - arl_expect(arl_base, "Icmp6InPktTooBigs", &Icmp6InPktTooBigs); - arl_expect(arl_base, "Icmp6InTimeExcds", &Icmp6InTimeExcds); - arl_expect(arl_base, "Icmp6InParmProblems", &Icmp6InParmProblems); - arl_expect(arl_base, "Icmp6InEchos", &Icmp6InEchos); - arl_expect(arl_base, "Icmp6InEchoReplies", &Icmp6InEchoReplies); - arl_expect(arl_base, "Icmp6InGroupMembQueries", &Icmp6InGroupMembQueries); - arl_expect(arl_base, "Icmp6InGroupMembResponses", &Icmp6InGroupMembResponses); - arl_expect(arl_base, "Icmp6InGroupMembReductions", &Icmp6InGroupMembReductions); - arl_expect(arl_base, "Icmp6InRouterSolicits", &Icmp6InRouterSolicits); - arl_expect(arl_base, "Icmp6InRouterAdvertisements", &Icmp6InRouterAdvertisements); - arl_expect(arl_base, "Icmp6InNeighborSolicits", &Icmp6InNeighborSolicits); - arl_expect(arl_base, "Icmp6InNeighborAdvertisements", &Icmp6InNeighborAdvertisements); - arl_expect(arl_base, "Icmp6InRedirects", &Icmp6InRedirects); - arl_expect(arl_base, "Icmp6InMLDv2Reports", &Icmp6InMLDv2Reports); - arl_expect(arl_base, "Icmp6OutDestUnreachs", &Icmp6OutDestUnreachs); - arl_expect(arl_base, "Icmp6OutPktTooBigs", &Icmp6OutPktTooBigs); - arl_expect(arl_base, "Icmp6OutTimeExcds", &Icmp6OutTimeExcds); - arl_expect(arl_base, "Icmp6OutParmProblems", &Icmp6OutParmProblems); - arl_expect(arl_base, "Icmp6OutEchos", &Icmp6OutEchos); - arl_expect(arl_base, "Icmp6OutEchoReplies", &Icmp6OutEchoReplies); - arl_expect(arl_base, "Icmp6OutGroupMembQueries", &Icmp6OutGroupMembQueries); - arl_expect(arl_base, "Icmp6OutGroupMembResponses", &Icmp6OutGroupMembResponses); - arl_expect(arl_base, "Icmp6OutGroupMembReductions", &Icmp6OutGroupMembReductions); - arl_expect(arl_base, "Icmp6OutRouterSolicits", &Icmp6OutRouterSolicits); - arl_expect(arl_base, "Icmp6OutRouterAdvertisements", &Icmp6OutRouterAdvertisements); - arl_expect(arl_base, "Icmp6OutNeighborSolicits", &Icmp6OutNeighborSolicits); - arl_expect(arl_base, "Icmp6OutNeighborAdvertisements", &Icmp6OutNeighborAdvertisements); - arl_expect(arl_base, "Icmp6OutRedirects", &Icmp6OutRedirects); - arl_expect(arl_base, "Icmp6OutMLDv2Reports", &Icmp6OutMLDv2Reports); - arl_expect(arl_base, "Icmp6InType1", &Icmp6InType1); - arl_expect(arl_base, "Icmp6InType128", &Icmp6InType128); - arl_expect(arl_base, "Icmp6InType129", &Icmp6InType129); - arl_expect(arl_base, "Icmp6InType136", &Icmp6InType136); - arl_expect(arl_base, "Icmp6OutType1", &Icmp6OutType1); - arl_expect(arl_base, "Icmp6OutType128", &Icmp6OutType128); - arl_expect(arl_base, "Icmp6OutType129", &Icmp6OutType129); - arl_expect(arl_base, "Icmp6OutType133", &Icmp6OutType133); - arl_expect(arl_base, "Icmp6OutType135", &Icmp6OutType135); - arl_expect(arl_base, "Icmp6OutType143", &Icmp6OutType143); - arl_expect(arl_base, "Udp6InDatagrams", &Udp6InDatagrams); - arl_expect(arl_base, "Udp6NoPorts", &Udp6NoPorts); - arl_expect(arl_base, "Udp6InErrors", &Udp6InErrors); - arl_expect(arl_base, "Udp6OutDatagrams", &Udp6OutDatagrams); - arl_expect(arl_base, "Udp6RcvbufErrors", &Udp6RcvbufErrors); - arl_expect(arl_base, "Udp6SndbufErrors", &Udp6SndbufErrors); - arl_expect(arl_base, "Udp6InCsumErrors", &Udp6InCsumErrors); - arl_expect(arl_base, "Udp6IgnoredMulti", &Udp6IgnoredMulti); - arl_expect(arl_base, "UdpLite6InDatagrams", &UdpLite6InDatagrams); - arl_expect(arl_base, "UdpLite6NoPorts", &UdpLite6NoPorts); - arl_expect(arl_base, "UdpLite6InErrors", &UdpLite6InErrors); - arl_expect(arl_base, "UdpLite6OutDatagrams", &UdpLite6OutDatagrams); - arl_expect(arl_base, "UdpLite6RcvbufErrors", &UdpLite6RcvbufErrors); - arl_expect(arl_base, "UdpLite6SndbufErrors", &UdpLite6SndbufErrors); - arl_expect(arl_base, "UdpLite6InCsumErrors", &UdpLite6InCsumErrors); - } - - if(unlikely(!ff)) { - char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/snmp6"); - ff = procfile_open(config_get("plugin:proc:/proc/net/snmp6", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); - if(unlikely(!ff)) - return 1; - } - - ff = procfile_readall(ff); - if(unlikely(!ff)) - return 0; // we return 0, so that we will retry to open it next time - - size_t lines = procfile_lines(ff), l; - - arl_begin(arl_base); - - for(l = 0; l < lines ;l++) { - size_t words = procfile_linewords(ff, l); - if(unlikely(words < 2)) { - if(unlikely(words)) error("Cannot read /proc/net/snmp6 line %zu. Expected 2 params, read %zu.", l, words); - continue; - } - - if(unlikely(arl_check(arl_base, - procfile_lineword(ff, l, 0), - procfile_lineword(ff, l, 1)))) break; - } - - // -------------------------------------------------------------------- - - if(do_bandwidth == CONFIG_BOOLEAN_YES || (do_bandwidth == CONFIG_BOOLEAN_AUTO && - (Ip6InOctets || - Ip6OutOctets || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_bandwidth = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_received = NULL, - *rd_sent = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - "system" - , "ipv6" - , NULL - , "network" - , NULL - , "IPv6 Bandwidth" - , "kilobits/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_SYSTEM_IPV6 - , update_every - , RRDSET_TYPE_AREA - ); - - rd_received = rrddim_add(st, "InOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - rd_sent = rrddim_add(st, "OutOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_received, Ip6InOctets); - rrddim_set_by_pointer(st, rd_sent, Ip6OutOctets); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_ip_packets == CONFIG_BOOLEAN_YES || (do_ip_packets == CONFIG_BOOLEAN_AUTO && - (Ip6InReceives || - Ip6OutRequests || - Ip6InDelivers || - Ip6OutForwDatagrams || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ip_packets = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_received = NULL, - *rd_sent = NULL, - *rd_forwarded = NULL, - *rd_delivers = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "packets" - , NULL - , "packets" - , NULL - , "IPv6 Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_PACKETS - , update_every - , RRDSET_TYPE_LINE - ); - - rd_received = rrddim_add(st, "InReceives", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_sent = rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_forwarded = rrddim_add(st, "OutForwDatagrams", "forwarded", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_delivers = rrddim_add(st, "InDelivers", "delivers", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_received, Ip6InReceives); - rrddim_set_by_pointer(st, rd_sent, Ip6OutRequests); - rrddim_set_by_pointer(st, rd_forwarded, Ip6OutForwDatagrams); - rrddim_set_by_pointer(st, rd_delivers, Ip6InDelivers); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_ip_fragsout == CONFIG_BOOLEAN_YES || (do_ip_fragsout == CONFIG_BOOLEAN_AUTO && - (Ip6FragOKs || - Ip6FragFails || - Ip6FragCreates || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ip_fragsout = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_ok = NULL, - *rd_failed = NULL, - *rd_all = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "fragsout" - , NULL - , "fragments6" - , NULL - , "IPv6 Fragments Sent" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_FRAGSOUT - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_ok = rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_failed = rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_all = rrddim_add(st, "FragCreates", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_ok, Ip6FragOKs); - rrddim_set_by_pointer(st, rd_failed, Ip6FragFails); - rrddim_set_by_pointer(st, rd_all, Ip6FragCreates); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_ip_fragsin == CONFIG_BOOLEAN_YES || (do_ip_fragsin == CONFIG_BOOLEAN_AUTO && - (Ip6ReasmOKs || - Ip6ReasmFails || - Ip6ReasmTimeout || - Ip6ReasmReqds || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ip_fragsin = CONFIG_BOOLEAN_YES; - - static RRDSET *st = NULL; - static RRDDIM *rd_ok = NULL, - *rd_failed = NULL, - *rd_timeout = NULL, - *rd_all = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "fragsin" - , NULL - , "fragments6" - , NULL - , "IPv6 Fragments Reassembly" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_FRAGSIN - , update_every - , RRDSET_TYPE_LINE); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_ok = rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_failed = rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_timeout = rrddim_add(st, "ReasmTimeout", "timeout", -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_all = rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_ok, Ip6ReasmOKs); - rrddim_set_by_pointer(st, rd_failed, Ip6ReasmFails); - rrddim_set_by_pointer(st, rd_timeout, Ip6ReasmTimeout); - rrddim_set_by_pointer(st, rd_all, Ip6ReasmReqds); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_ip_errors == CONFIG_BOOLEAN_YES || (do_ip_errors == CONFIG_BOOLEAN_AUTO && - (Ip6InDiscards || - Ip6OutDiscards || - Ip6InHdrErrors || - Ip6InAddrErrors || - Ip6InUnknownProtos || - Ip6InTooBigErrors || - Ip6InTruncatedPkts || - Ip6InNoRoutes || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ip_errors = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_InDiscards = NULL, - *rd_OutDiscards = NULL, - *rd_InHdrErrors = NULL, - *rd_InAddrErrors = NULL, - *rd_InUnknownProtos = NULL, - *rd_InTooBigErrors = NULL, - *rd_InTruncatedPkts = NULL, - *rd_InNoRoutes = NULL, - *rd_OutNoRoutes = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "errors" - , NULL - , "errors" - , NULL - , "IPv6 Errors" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ERRORS - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_InDiscards = rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutDiscards = rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InHdrErrors = rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InAddrErrors = rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InUnknownProtos = rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InTooBigErrors = rrddim_add(st, "InTooBigErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InTruncatedPkts = rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InNoRoutes = rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutNoRoutes = rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InDiscards, Ip6InDiscards); - rrddim_set_by_pointer(st, rd_OutDiscards, Ip6OutDiscards); - rrddim_set_by_pointer(st, rd_InHdrErrors, Ip6InHdrErrors); - rrddim_set_by_pointer(st, rd_InAddrErrors, Ip6InAddrErrors); - rrddim_set_by_pointer(st, rd_InUnknownProtos, Ip6InUnknownProtos); - rrddim_set_by_pointer(st, rd_InTooBigErrors, Ip6InTooBigErrors); - rrddim_set_by_pointer(st, rd_InTruncatedPkts, Ip6InTruncatedPkts); - rrddim_set_by_pointer(st, rd_InNoRoutes, Ip6InNoRoutes); - rrddim_set_by_pointer(st, rd_OutNoRoutes, Ip6OutNoRoutes); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_udp_packets == CONFIG_BOOLEAN_YES || (do_udp_packets == CONFIG_BOOLEAN_AUTO && - (Udp6InDatagrams || - Udp6OutDatagrams || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_udp_packets = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_received = NULL, - *rd_sent = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "udppackets" - , NULL - , "udp6" - , NULL - , "IPv6 UDP Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_UDP_PACKETS - , update_every - , RRDSET_TYPE_LINE - ); - - rd_received = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_sent = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_received, Udp6InDatagrams); - rrddim_set_by_pointer(st, rd_sent, Udp6OutDatagrams); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_udp_errors == CONFIG_BOOLEAN_YES || (do_udp_errors == CONFIG_BOOLEAN_AUTO && - (Udp6InErrors || - Udp6NoPorts || - Udp6RcvbufErrors || - Udp6SndbufErrors || - Udp6InCsumErrors || - Udp6IgnoredMulti || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_udp_errors = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_RcvbufErrors = NULL, - *rd_SndbufErrors = NULL, - *rd_InErrors = NULL, - *rd_NoPorts = NULL, - *rd_InCsumErrors = NULL, - *rd_IgnoredMulti = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "udperrors" - , NULL - , "udp6" - , NULL - , "IPv6 UDP Errors" - , "events/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_UDP_ERRORS - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_RcvbufErrors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_SndbufErrors = rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_NoPorts = rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_IgnoredMulti = rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_RcvbufErrors, Udp6RcvbufErrors); - rrddim_set_by_pointer(st, rd_SndbufErrors, Udp6SndbufErrors); - rrddim_set_by_pointer(st, rd_InErrors, Udp6InErrors); - rrddim_set_by_pointer(st, rd_NoPorts, Udp6NoPorts); - rrddim_set_by_pointer(st, rd_InCsumErrors, Udp6InCsumErrors); - rrddim_set_by_pointer(st, rd_IgnoredMulti, Udp6IgnoredMulti); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_udplite_packets == CONFIG_BOOLEAN_YES || (do_udplite_packets == CONFIG_BOOLEAN_AUTO && - (UdpLite6InDatagrams || - UdpLite6OutDatagrams || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_udplite_packets = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_received = NULL, - *rd_sent = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "udplitepackets" - , NULL - , "udplite6" - , NULL - , "IPv6 UDPlite Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_UDPLITE_PACKETS - , update_every - , RRDSET_TYPE_LINE - ); - - rd_received = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_sent = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_received, UdpLite6InDatagrams); - rrddim_set_by_pointer(st, rd_sent, UdpLite6OutDatagrams); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_udplite_errors == CONFIG_BOOLEAN_YES || (do_udplite_errors == CONFIG_BOOLEAN_AUTO && - (UdpLite6InErrors || - UdpLite6NoPorts || - UdpLite6RcvbufErrors || - UdpLite6SndbufErrors || - Udp6InCsumErrors || - UdpLite6InCsumErrors || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_udplite_errors = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_RcvbufErrors = NULL, - *rd_SndbufErrors = NULL, - *rd_InErrors = NULL, - *rd_NoPorts = NULL, - *rd_InCsumErrors = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "udpliteerrors" - , NULL - , "udplite6" - , NULL - , "IPv6 UDP Lite Errors" - , "events/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_UDPLITE_ERRORS - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_RcvbufErrors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_SndbufErrors = rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_NoPorts = rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InErrors, UdpLite6InErrors); - rrddim_set_by_pointer(st, rd_NoPorts, UdpLite6NoPorts); - rrddim_set_by_pointer(st, rd_RcvbufErrors, UdpLite6RcvbufErrors); - rrddim_set_by_pointer(st, rd_SndbufErrors, UdpLite6SndbufErrors); - rrddim_set_by_pointer(st, rd_InCsumErrors, UdpLite6InCsumErrors); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_mcast == CONFIG_BOOLEAN_YES || (do_mcast == CONFIG_BOOLEAN_AUTO && - (Ip6OutMcastOctets || - Ip6InMcastOctets || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_mcast = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_Ip6InMcastOctets = NULL, - *rd_Ip6OutMcastOctets = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "mcast" - , NULL - , "multicast6" - , NULL - , "IPv6 Multicast Bandwidth" - , "kilobits/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_MCAST - , update_every - , RRDSET_TYPE_AREA - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_Ip6InMcastOctets = rrddim_add(st, "InMcastOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - rd_Ip6OutMcastOctets = rrddim_add(st, "OutMcastOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_Ip6InMcastOctets, Ip6InMcastOctets); - rrddim_set_by_pointer(st, rd_Ip6OutMcastOctets, Ip6OutMcastOctets); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_bcast == CONFIG_BOOLEAN_YES || (do_bcast == CONFIG_BOOLEAN_AUTO && - (Ip6OutBcastOctets || - Ip6InBcastOctets || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_bcast = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_Ip6InBcastOctets = NULL, - *rd_Ip6OutBcastOctets = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "bcast" - , NULL - , "broadcast6" - , NULL - , "IPv6 Broadcast Bandwidth" - , "kilobits/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_BCAST - , update_every - , RRDSET_TYPE_AREA - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_Ip6InBcastOctets = rrddim_add(st, "InBcastOctets", "received", 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - rd_Ip6OutBcastOctets = rrddim_add(st, "OutBcastOctets", "sent", -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_Ip6InBcastOctets, Ip6InBcastOctets); - rrddim_set_by_pointer(st, rd_Ip6OutBcastOctets, Ip6OutBcastOctets); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_mcast_p == CONFIG_BOOLEAN_YES || (do_mcast_p == CONFIG_BOOLEAN_AUTO && - (Ip6OutMcastPkts || - Ip6InMcastPkts || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_mcast_p = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_Ip6InMcastPkts = NULL, - *rd_Ip6OutMcastPkts = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "mcastpkts" - , NULL - , "multicast6" - , NULL - , "IPv6 Multicast Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_MCAST_PACKETS - , update_every - , RRDSET_TYPE_LINE - ); - rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - - rd_Ip6InMcastPkts = rrddim_add(st, "InMcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_Ip6OutMcastPkts = rrddim_add(st, "OutMcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_Ip6InMcastPkts, Ip6InMcastPkts); - rrddim_set_by_pointer(st, rd_Ip6OutMcastPkts, Ip6OutMcastPkts); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_icmp == CONFIG_BOOLEAN_YES || (do_icmp == CONFIG_BOOLEAN_AUTO && - (Icmp6InMsgs || - Icmp6OutMsgs || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_Icmp6InMsgs = NULL, - *rd_Icmp6OutMsgs = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "icmp" - , NULL - , "icmp6" - , NULL - , "IPv6 ICMP Messages" - , "messages/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ICMP - , update_every - , RRDSET_TYPE_LINE - ); - - rd_Icmp6InMsgs = rrddim_add(st, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_Icmp6OutMsgs = rrddim_add(st, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_Icmp6InMsgs, Icmp6InMsgs); - rrddim_set_by_pointer(st, rd_Icmp6OutMsgs, Icmp6OutMsgs); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_icmp_redir == CONFIG_BOOLEAN_YES || (do_icmp_redir == CONFIG_BOOLEAN_AUTO && - (Icmp6InRedirects || - Icmp6OutRedirects || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp_redir = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_Icmp6InRedirects = NULL, - *rd_Icmp6OutRedirects = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "icmpredir" - , NULL - , "icmp6" - , NULL - , "IPv6 ICMP Redirects" - , "redirects/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ICMP_REDIR - , update_every - , RRDSET_TYPE_LINE - ); - - rd_Icmp6InRedirects = rrddim_add(st, "InRedirects", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_Icmp6OutRedirects = rrddim_add(st, "OutRedirects", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_Icmp6InRedirects, Icmp6InRedirects); - rrddim_set_by_pointer(st, rd_Icmp6OutRedirects, Icmp6OutRedirects); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_icmp_errors == CONFIG_BOOLEAN_YES || (do_icmp_errors == CONFIG_BOOLEAN_AUTO && - (Icmp6InErrors || - Icmp6OutErrors || - Icmp6InCsumErrors || - Icmp6InDestUnreachs || - Icmp6InPktTooBigs || - Icmp6InTimeExcds || - Icmp6InParmProblems || - Icmp6OutDestUnreachs || - Icmp6OutPktTooBigs || - Icmp6OutTimeExcds || - Icmp6OutParmProblems || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp_errors = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_InErrors = NULL, - *rd_OutErrors = NULL, - *rd_InCsumErrors = NULL, - *rd_InDestUnreachs = NULL, - *rd_InPktTooBigs = NULL, - *rd_InTimeExcds = NULL, - *rd_InParmProblems = NULL, - *rd_OutDestUnreachs = NULL, - *rd_OutPktTooBigs = NULL, - *rd_OutTimeExcds = NULL, - *rd_OutParmProblems = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "icmperrors" - , NULL - , "icmp6" - , NULL - , "IPv6 ICMP Errors" - , "errors/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ICMP_ERRORS - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InErrors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutErrors = rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InCsumErrors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InDestUnreachs = rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InPktTooBigs = rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InTimeExcds = rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InParmProblems = rrddim_add(st, "InParmProblems", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutDestUnreachs = rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutPktTooBigs = rrddim_add(st, "OutPktTooBigs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutTimeExcds = rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutParmProblems = rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InErrors, Icmp6InErrors); - rrddim_set_by_pointer(st, rd_OutErrors, Icmp6OutErrors); - rrddim_set_by_pointer(st, rd_InCsumErrors, Icmp6InCsumErrors); - rrddim_set_by_pointer(st, rd_InDestUnreachs, Icmp6InDestUnreachs); - rrddim_set_by_pointer(st, rd_InPktTooBigs, Icmp6InPktTooBigs); - rrddim_set_by_pointer(st, rd_InTimeExcds, Icmp6InTimeExcds); - rrddim_set_by_pointer(st, rd_InParmProblems, Icmp6InParmProblems); - rrddim_set_by_pointer(st, rd_OutDestUnreachs, Icmp6OutDestUnreachs); - rrddim_set_by_pointer(st, rd_OutPktTooBigs, Icmp6OutPktTooBigs); - rrddim_set_by_pointer(st, rd_OutTimeExcds, Icmp6OutTimeExcds); - rrddim_set_by_pointer(st, rd_OutParmProblems, Icmp6OutParmProblems); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_icmp_echos == CONFIG_BOOLEAN_YES || (do_icmp_echos == CONFIG_BOOLEAN_AUTO && - (Icmp6InEchos || - Icmp6OutEchos || - Icmp6InEchoReplies || - Icmp6OutEchoReplies || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp_echos = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_InEchos = NULL, - *rd_OutEchos = NULL, - *rd_InEchoReplies = NULL, - *rd_OutEchoReplies = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "icmpechos" - , NULL - , "icmp6" - , NULL - , "IPv6 ICMP Echo" - , "messages/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ICMP_ECHOS - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InEchos = rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutEchos = rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InEchoReplies = rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutEchoReplies = rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InEchos, Icmp6InEchos); - rrddim_set_by_pointer(st, rd_OutEchos, Icmp6OutEchos); - rrddim_set_by_pointer(st, rd_InEchoReplies, Icmp6InEchoReplies); - rrddim_set_by_pointer(st, rd_OutEchoReplies, Icmp6OutEchoReplies); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_icmp_groupmemb == CONFIG_BOOLEAN_YES || (do_icmp_groupmemb == CONFIG_BOOLEAN_AUTO && - (Icmp6InGroupMembQueries || - Icmp6OutGroupMembQueries || - Icmp6InGroupMembResponses || - Icmp6OutGroupMembResponses || - Icmp6InGroupMembReductions || - Icmp6OutGroupMembReductions || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp_groupmemb = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_InQueries = NULL, - *rd_OutQueries = NULL, - *rd_InResponses = NULL, - *rd_OutResponses = NULL, - *rd_InReductions = NULL, - *rd_OutReductions = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "groupmemb" - , NULL - , "icmp6" - , NULL - , "IPv6 ICMP Group Membership" - , "messages/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ICMP_GROUPMEMB - , update_every - , RRDSET_TYPE_LINE); - - rd_InQueries = rrddim_add(st, "InQueries", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutQueries = rrddim_add(st, "OutQueries", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InResponses = rrddim_add(st, "InResponses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutResponses = rrddim_add(st, "OutResponses", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InReductions = rrddim_add(st, "InReductions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutReductions = rrddim_add(st, "OutReductions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InQueries, Icmp6InGroupMembQueries); - rrddim_set_by_pointer(st, rd_OutQueries, Icmp6OutGroupMembQueries); - rrddim_set_by_pointer(st, rd_InResponses, Icmp6InGroupMembResponses); - rrddim_set_by_pointer(st, rd_OutResponses, Icmp6OutGroupMembResponses); - rrddim_set_by_pointer(st, rd_InReductions, Icmp6InGroupMembReductions); - rrddim_set_by_pointer(st, rd_OutReductions, Icmp6OutGroupMembReductions); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_icmp_router == CONFIG_BOOLEAN_YES || (do_icmp_router == CONFIG_BOOLEAN_AUTO && - (Icmp6InRouterSolicits || - Icmp6OutRouterSolicits || - Icmp6InRouterAdvertisements || - Icmp6OutRouterAdvertisements || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp_router = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_InSolicits = NULL, - *rd_OutSolicits = NULL, - *rd_InAdvertisements = NULL, - *rd_OutAdvertisements = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "icmprouter" - , NULL - , "icmp6" - , NULL - , "IPv6 Router Messages" - , "messages/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ICMP_ROUTER - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InSolicits = rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutSolicits = rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InAdvertisements = rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutAdvertisements = rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InSolicits, Icmp6InRouterSolicits); - rrddim_set_by_pointer(st, rd_OutSolicits, Icmp6OutRouterSolicits); - rrddim_set_by_pointer(st, rd_InAdvertisements, Icmp6InRouterAdvertisements); - rrddim_set_by_pointer(st, rd_OutAdvertisements, Icmp6OutRouterAdvertisements); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_icmp_neighbor == CONFIG_BOOLEAN_YES || (do_icmp_neighbor == CONFIG_BOOLEAN_AUTO && - (Icmp6InNeighborSolicits || - Icmp6OutNeighborSolicits || - Icmp6InNeighborAdvertisements || - Icmp6OutNeighborAdvertisements || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp_neighbor = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_InSolicits = NULL, - *rd_OutSolicits = NULL, - *rd_InAdvertisements = NULL, - *rd_OutAdvertisements = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "icmpneighbor" - , NULL - , "icmp6" - , NULL - , "IPv6 Neighbor Messages" - , "messages/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ICMP_NEIGHBOR - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InSolicits = rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutSolicits = rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InAdvertisements = rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutAdvertisements = rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InSolicits, Icmp6InNeighborSolicits); - rrddim_set_by_pointer(st, rd_OutSolicits, Icmp6OutNeighborSolicits); - rrddim_set_by_pointer(st, rd_InAdvertisements, Icmp6InNeighborAdvertisements); - rrddim_set_by_pointer(st, rd_OutAdvertisements, Icmp6OutNeighborAdvertisements); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_icmp_mldv2 == CONFIG_BOOLEAN_YES || (do_icmp_mldv2 == CONFIG_BOOLEAN_AUTO && - (Icmp6InMLDv2Reports || - Icmp6OutMLDv2Reports || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp_mldv2 = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_InMLDv2Reports = NULL, - *rd_OutMLDv2Reports = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "icmpmldv2" - , NULL - , "icmp6" - , NULL - , "IPv6 ICMP MLDv2 Reports" - , "reports/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ICMP_LDV2 - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InMLDv2Reports = rrddim_add(st, "InMLDv2Reports", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutMLDv2Reports = rrddim_add(st, "OutMLDv2Reports", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InMLDv2Reports, Icmp6InMLDv2Reports); - rrddim_set_by_pointer(st, rd_OutMLDv2Reports, Icmp6OutMLDv2Reports); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_icmp_types == CONFIG_BOOLEAN_YES || (do_icmp_types == CONFIG_BOOLEAN_AUTO && - (Icmp6InType1 || - Icmp6InType128 || - Icmp6InType129 || - Icmp6InType136 || - Icmp6OutType1 || - Icmp6OutType128 || - Icmp6OutType129 || - Icmp6OutType133 || - Icmp6OutType135 || - Icmp6OutType143 || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_icmp_types = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_InType1 = NULL, - *rd_InType128 = NULL, - *rd_InType129 = NULL, - *rd_InType136 = NULL, - *rd_OutType1 = NULL, - *rd_OutType128 = NULL, - *rd_OutType129 = NULL, - *rd_OutType133 = NULL, - *rd_OutType135 = NULL, - *rd_OutType143 = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "icmptypes" - , NULL - , "icmp6" - , NULL - , "IPv6 ICMP Types" - , "messages/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ICMP_TYPES - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InType1 = rrddim_add(st, "InType1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InType128 = rrddim_add(st, "InType128", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InType129 = rrddim_add(st, "InType129", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InType136 = rrddim_add(st, "InType136", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutType1 = rrddim_add(st, "OutType1", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutType128 = rrddim_add(st, "OutType128", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutType129 = rrddim_add(st, "OutType129", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutType133 = rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutType135 = rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_OutType143 = rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InType1, Icmp6InType1); - rrddim_set_by_pointer(st, rd_InType128, Icmp6InType128); - rrddim_set_by_pointer(st, rd_InType129, Icmp6InType129); - rrddim_set_by_pointer(st, rd_InType136, Icmp6InType136); - rrddim_set_by_pointer(st, rd_OutType1, Icmp6OutType1); - rrddim_set_by_pointer(st, rd_OutType128, Icmp6OutType128); - rrddim_set_by_pointer(st, rd_OutType129, Icmp6OutType129); - rrddim_set_by_pointer(st, rd_OutType133, Icmp6OutType133); - rrddim_set_by_pointer(st, rd_OutType135, Icmp6OutType135); - rrddim_set_by_pointer(st, rd_OutType143, Icmp6OutType143); - rrdset_done(st); - } - - // -------------------------------------------------------------------- - - if(do_ect == CONFIG_BOOLEAN_YES || (do_ect == CONFIG_BOOLEAN_AUTO && - (Ip6InNoECTPkts || - Ip6InECT1Pkts || - Ip6InECT0Pkts || - Ip6InCEPkts || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { - do_ect = CONFIG_BOOLEAN_YES; - static RRDSET *st = NULL; - static RRDDIM *rd_InNoECTPkts = NULL, - *rd_InECT1Pkts = NULL, - *rd_InECT0Pkts = NULL, - *rd_InCEPkts = NULL; - - if(unlikely(!st)) { - st = rrdset_create_localhost( - RRD_TYPE_NET_SNMP6 - , "ect" - , NULL - , "packets" - , NULL - , "IPv6 ECT Packets" - , "packets/s" - , PLUGIN_PROC_NAME - , PLUGIN_PROC_MODULE_NET_SNMP6_NAME - , NETDATA_CHART_PRIO_IPV6_ECT - , update_every - , RRDSET_TYPE_LINE - ); - - rd_InNoECTPkts = rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InECT1Pkts = rrddim_add(st, "InECT1Pkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InECT0Pkts = rrddim_add(st, "InECT0Pkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_InCEPkts = rrddim_add(st, "InCEPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set_by_pointer(st, rd_InNoECTPkts, Ip6InNoECTPkts); - rrddim_set_by_pointer(st, rd_InECT1Pkts, Ip6InECT1Pkts); - rrddim_set_by_pointer(st, rd_InECT0Pkts, Ip6InECT0Pkts); - rrddim_set_by_pointer(st, rd_InCEPkts, Ip6InCEPkts); - rrdset_done(st); - } - - return 0; -} - diff --git a/collectors/proc.plugin/proc_net_sockstat.c b/collectors/proc.plugin/proc_net_sockstat.c index 994cbad7b..e94b891ca 100644 --- a/collectors/proc.plugin/proc_net_sockstat.c +++ b/collectors/proc.plugin/proc_net_sockstat.c @@ -27,14 +27,14 @@ static struct proc_net_sockstat { static int read_tcp_mem(void) { static char *filename = NULL; - static RRDVAR *tcp_mem_low_threshold = NULL, + static const RRDVAR_ACQUIRED *tcp_mem_low_threshold = NULL, *tcp_mem_pressure_threshold = NULL, *tcp_mem_high_threshold = NULL; if(unlikely(!tcp_mem_low_threshold)) { - tcp_mem_low_threshold = rrdvar_custom_host_variable_create(localhost, "tcp_mem_low"); - tcp_mem_pressure_threshold = rrdvar_custom_host_variable_create(localhost, "tcp_mem_pressure"); - tcp_mem_high_threshold = rrdvar_custom_host_variable_create(localhost, "tcp_mem_high"); + tcp_mem_low_threshold = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_mem_low"); + tcp_mem_pressure_threshold = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_mem_pressure"); + tcp_mem_high_threshold = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_mem_high"); } if(unlikely(!filename)) { @@ -69,7 +69,7 @@ static int read_tcp_mem(void) { static kernel_uint_t read_tcp_max_orphans(void) { static char *filename = NULL; - static RRDVAR *tcp_max_orphans_var = NULL; + static const RRDVAR_ACQUIRED *tcp_max_orphans_var = NULL; if(unlikely(!filename)) { char buffer[FILENAME_MAX + 1]; @@ -81,7 +81,7 @@ static kernel_uint_t read_tcp_max_orphans(void) { if(read_single_number_file(filename, &tcp_max_orphans) == 0) { if(unlikely(!tcp_max_orphans_var)) - tcp_max_orphans_var = rrdvar_custom_host_variable_create(localhost, "tcp_max_orphans"); + tcp_max_orphans_var = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_max_orphans"); rrdvar_custom_host_variable_set(localhost, tcp_max_orphans_var, tcp_max_orphans); return tcp_max_orphans; @@ -244,7 +244,6 @@ int do_proc_net_sockstat(int update_every, usec_t dt) { rd_used = rrddim_add(st, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_used, (collected_number)sockstat_root.sockets_used); rrdset_done(st); @@ -287,7 +286,6 @@ int do_proc_net_sockstat(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_timewait = rrddim_add(st, "timewait", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat_root.tcp_inuse); rrddim_set_by_pointer(st, rd_orphan, (collected_number)sockstat_root.tcp_orphan); @@ -323,7 +321,6 @@ int do_proc_net_sockstat(int update_every, usec_t dt) { rd_mem = rrddim_add(st, "mem", NULL, sysconf(_SC_PAGESIZE), 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_mem, (collected_number)sockstat_root.tcp_mem); rrdset_done(st); @@ -357,7 +354,6 @@ int do_proc_net_sockstat(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat_root.udp_inuse); rrdset_done(st); @@ -391,7 +387,6 @@ int do_proc_net_sockstat(int update_every, usec_t dt) { rd_mem = rrddim_add(st, "mem", NULL, sysconf(_SC_PAGESIZE), 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_mem, (collected_number)sockstat_root.udp_mem); rrdset_done(st); @@ -425,7 +420,6 @@ int do_proc_net_sockstat(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat_root.udplite_inuse); rrdset_done(st); @@ -459,7 +453,6 @@ int do_proc_net_sockstat(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat_root.raw_inuse); rrdset_done(st); @@ -493,7 +486,6 @@ int do_proc_net_sockstat(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat_root.frag_inuse); rrdset_done(st); @@ -527,7 +519,6 @@ int do_proc_net_sockstat(int update_every, usec_t dt) { rd_mem = rrddim_add(st, "mem", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_mem, (collected_number)sockstat_root.frag_memory); rrdset_done(st); diff --git a/collectors/proc.plugin/proc_net_sockstat6.c b/collectors/proc.plugin/proc_net_sockstat6.c index ce8c9e093..065cf6055 100644 --- a/collectors/proc.plugin/proc_net_sockstat6.c +++ b/collectors/proc.plugin/proc_net_sockstat6.c @@ -137,7 +137,6 @@ int do_proc_net_sockstat6(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat6_root.tcp6_inuse); rrdset_done(st); @@ -171,7 +170,6 @@ int do_proc_net_sockstat6(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat6_root.udp6_inuse); rrdset_done(st); @@ -205,7 +203,6 @@ int do_proc_net_sockstat6(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat6_root.udplite6_inuse); rrdset_done(st); @@ -239,7 +236,6 @@ int do_proc_net_sockstat6(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat6_root.raw6_inuse); rrdset_done(st); @@ -273,7 +269,6 @@ int do_proc_net_sockstat6(int update_every, usec_t dt) { rd_inuse = rrddim_add(st, "inuse", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inuse, (collected_number)sockstat6_root.frag6_inuse); rrdset_done(st); diff --git a/collectors/proc.plugin/proc_net_softnet_stat.c b/collectors/proc.plugin/proc_net_softnet_stat.c index 1f704a427..65239246a 100644 --- a/collectors/proc.plugin/proc_net_softnet_stat.c +++ b/collectors/proc.plugin/proc_net_softnet_stat.c @@ -101,7 +101,6 @@ int do_proc_net_softnet_stat(int update_every, usec_t dt) { if(unlikely(softnet_column_name(w))) rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); for(w = 0; w < allocated_columns ;w++) if(unlikely(softnet_column_name(w))) @@ -137,7 +136,6 @@ int do_proc_net_softnet_stat(int update_every, usec_t dt) { if(unlikely(softnet_column_name(w))) rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); for(w = 0; w < allocated_columns ;w++) if(unlikely(softnet_column_name(w))) diff --git a/collectors/proc.plugin/proc_net_stat_conntrack.c b/collectors/proc.plugin/proc_net_stat_conntrack.c index 642e33f8e..f9dbdf47c 100644 --- a/collectors/proc.plugin/proc_net_stat_conntrack.c +++ b/collectors/proc.plugin/proc_net_stat_conntrack.c @@ -12,7 +12,7 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { static usec_t get_max_every = 10 * USEC_PER_SEC, usec_since_last_max = 0; static int read_full = 1; static char *nf_conntrack_filename, *nf_conntrack_count_filename, *nf_conntrack_max_filename; - static RRDVAR *rrdvar_max = NULL; + static const RRDVAR_ACQUIRED *rrdvar_max = NULL; unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0, ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0; @@ -50,7 +50,7 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { if(!do_sockets && !read_full) return 1; - rrdvar_max = rrdvar_custom_host_variable_create(localhost, "netfilter_conntrack_max"); + rrdvar_max = rrdvar_custom_host_variable_add_and_acquire(localhost, "netfilter_conntrack_max"); } if(likely(read_full)) { @@ -152,7 +152,6 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { rd_connections = rrddim_add(st, "connections", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_connections, aentries); rrdset_done(st); @@ -187,7 +186,6 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { rd_ignore = rrddim_add(st, "ignore", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_invalid = rrddim_add(st, "invalid", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_new, anew); rrddim_set_by_pointer(st, rd_ignore, aignore); @@ -225,7 +223,6 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { rd_deleted = rrddim_add(st, "deleted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_delete_list = rrddim_add(st, "delete_list", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_inserted, ainsert); rrddim_set_by_pointer(st, rd_deleted, adelete); @@ -262,7 +259,6 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { rd_deleted = rrddim_add(st, "deleted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_new = rrddim_add(st, "new", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_created, aexpect_create); rrddim_set_by_pointer(st, rd_deleted, aexpect_delete); @@ -299,7 +295,6 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { rd_restarted = rrddim_add(st, "restarted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_found = rrddim_add(st, "found", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_searched, asearched); rrddim_set_by_pointer(st, rd_restarted, asearch_restart); @@ -338,7 +333,6 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { rd_drop = rrddim_add(st, "drop", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_early_drop = rrddim_add(st, "early_drop", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd_icmp_error, aicmp_error); rrddim_set_by_pointer(st, rd_insert_failed, ainsert_failed); diff --git a/collectors/proc.plugin/proc_net_stat_synproxy.c b/collectors/proc.plugin/proc_net_stat_synproxy.c index c74c5374d..0a74b3575 100644 --- a/collectors/proc.plugin/proc_net_stat_synproxy.c +++ b/collectors/proc.plugin/proc_net_stat_synproxy.c @@ -80,7 +80,6 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) { rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "received", syn_received); rrdset_done(st); @@ -111,7 +110,6 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) { rrddim_add(st, "reopened", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "reopened", conn_reopened); rrdset_done(st); @@ -144,7 +142,6 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) { rrddim_add(st, "invalid", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_add(st, "retransmits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); rrddim_set(st, "valid", cookie_valid); rrddim_set(st, "invalid", cookie_invalid); diff --git a/collectors/proc.plugin/proc_net_wireless.c b/collectors/proc.plugin/proc_net_wireless.c index c6ee4ff70..08ab2eada 100644 --- a/collectors/proc.plugin/proc_net_wireless.c +++ b/collectors/proc.plugin/proc_net_wireless.c @@ -199,7 +199,7 @@ static void configure_device(int do_status, int do_quality, int do_discarded_pac } static void add_labels_to_wireless(struct netwireless *w, RRDSET *st) { - rrdlabels_add(st->state->chart_labels, "device", w->name, RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device", w->name, RRDLABEL_SRC_AUTO); } int do_proc_net_wireless(int update_every, usec_t dt) @@ -269,8 +269,6 @@ int do_proc_net_wireless(int update_every, usec_t dt) add_labels_to_wireless(wireless_dev, wireless_dev->st_status); } - else - rrdset_next(wireless_dev->st_status); rrddim_set_by_pointer(wireless_dev->st_status, wireless_dev->rd_status, (collected_number)wireless_dev->status); @@ -302,8 +300,6 @@ int do_proc_net_wireless(int update_every, usec_t dt) add_labels_to_wireless(wireless_dev, wireless_dev->st_link); } - else - rrdset_next(wireless_dev->st_link); if (unlikely(!wireless_dev->st_level)) { wireless_dev->st_level = rrdset_create_localhost( @@ -325,8 +321,6 @@ int do_proc_net_wireless(int update_every, usec_t dt) add_labels_to_wireless(wireless_dev, wireless_dev->st_level); } - else - rrdset_next(wireless_dev->st_level); if (unlikely(!wireless_dev->st_noise)) { wireless_dev->st_noise = rrdset_create_localhost( @@ -348,8 +342,6 @@ int do_proc_net_wireless(int update_every, usec_t dt) add_labels_to_wireless(wireless_dev, wireless_dev->st_noise); } - else - rrdset_next(wireless_dev->st_noise); rrddim_set_by_pointer(wireless_dev->st_link, wireless_dev->rd_link, (collected_number)wireless_dev->link); rrdset_done(wireless_dev->st_link); @@ -393,8 +385,6 @@ int do_proc_net_wireless(int update_every, usec_t dt) add_labels_to_wireless(wireless_dev, wireless_dev->st_discarded_packets); } - else - rrdset_next(wireless_dev->st_discarded_packets); rrddim_set_by_pointer(wireless_dev->st_discarded_packets, wireless_dev->rd_nwid, (collected_number)wireless_dev->nwid); rrddim_set_by_pointer(wireless_dev->st_discarded_packets, wireless_dev->rd_crypt, (collected_number)wireless_dev->crypt); @@ -429,11 +419,8 @@ int do_proc_net_wireless(int update_every, usec_t dt) add_labels_to_wireless(wireless_dev, wireless_dev->st_missed_beacon); } - else - rrdset_next(wireless_dev->st_missed_beacon); rrddim_set_by_pointer(wireless_dev->st_missed_beacon, wireless_dev->rd_missed_beacon, (collected_number)wireless_dev->missed_beacon); - rrdset_done(wireless_dev->st_missed_beacon); } diff --git a/collectors/proc.plugin/proc_pagetypeinfo.c b/collectors/proc.plugin/proc_pagetypeinfo.c index 017edc49a..dc006aa59 100644 --- a/collectors/proc.plugin/proc_pagetypeinfo.c +++ b/collectors/proc.plugin/proc_pagetypeinfo.c @@ -79,7 +79,7 @@ int do_proc_pagetypeinfo(int update_every, usec_t dt) { static RRDSET **st_nodezonetype = NULL; // Local temp variables - size_t l, o, p; + long unsigned int l, o, p; struct pageline *pgl = NULL; // -------------------------------------------------------------------- @@ -149,7 +149,8 @@ int do_proc_pagetypeinfo(int update_every, usec_t dt) { pageorders_cnt -= 9; if (pageorders_cnt > MAX_PAGETYPE_ORDER) { - error("PLUGIN: PROC_PAGETYPEINFO: pageorder found (%lu) is higher than max %d", pageorders_cnt, MAX_PAGETYPE_ORDER); + error("PLUGIN: PROC_PAGETYPEINFO: pageorder found (%lu) is higher than max %d", + (long unsigned int) pageorders_cnt, MAX_PAGETYPE_ORDER); return 1; } @@ -157,7 +158,8 @@ int do_proc_pagetypeinfo(int update_every, usec_t dt) { if (!pagelines) { pagelines = callocz(pagelines_cnt, sizeof(struct pageline)); if (!pagelines) { - error("PLUGIN: PROC_PAGETYPEINFO: Cannot allocate %lu pagelines of %lu B", pagelines_cnt, sizeof(struct pageline)); + error("PLUGIN: PROC_PAGETYPEINFO: Cannot allocate %lu pagelines of %lu B", + (long unsigned int) pagelines_cnt, (long unsigned int) sizeof(struct pageline)); return 1; } } @@ -261,9 +263,9 @@ int do_proc_pagetypeinfo(int update_every, usec_t dt) { char node[50+1]; snprintfz(node, 50, "node%d", pgl->node); - rrdlabels_add(st_nodezonetype[p]->state->chart_labels, "node_id", node, RRDLABEL_SRC_AUTO); - rrdlabels_add(st_nodezonetype[p]->state->chart_labels, "node_zone", pgl->zone, RRDLABEL_SRC_AUTO); - rrdlabels_add(st_nodezonetype[p]->state->chart_labels, "node_type", pgl->type, RRDLABEL_SRC_AUTO); + rrdlabels_add(st_nodezonetype[p]->rrdlabels, "node_id", node, RRDLABEL_SRC_AUTO); + rrdlabels_add(st_nodezonetype[p]->rrdlabels, "node_zone", pgl->zone, RRDLABEL_SRC_AUTO); + rrdlabels_add(st_nodezonetype[p]->rrdlabels, "node_type", pgl->type, RRDLABEL_SRC_AUTO); for (o = 0; o < pageorders_cnt; o++) { char dimid[3+1]; @@ -289,7 +291,8 @@ int do_proc_pagetypeinfo(int update_every, usec_t dt) { size_t words = procfile_linewords(ff, l); if (words != 7+pageorders_cnt) { - error("PLUGIN: PROC_PAGETYPEINFO: Unable to read line %lu, %lu words found instead of %lu", l+1, words, 7+pageorders_cnt); + error("PLUGIN: PROC_PAGETYPEINFO: Unable to read line %lu, %lu words found instead of %lu", + l+1, (long unsigned int) words, (long unsigned int) 7+pageorders_cnt); break; } @@ -313,10 +316,8 @@ int do_proc_pagetypeinfo(int update_every, usec_t dt) { // Global system per order if (st_order) { - rrdset_next(st_order); - for (o = 0; o < pageorders_cnt; o++) { + for (o = 0; o < pageorders_cnt; o++) rrddim_set_by_pointer(st_order, systemorders[o].rd, systemorders[o].size); - } rrdset_done(st_order); } @@ -327,10 +328,8 @@ int do_proc_pagetypeinfo(int update_every, usec_t dt) { if (!st_nodezonetype[p]) continue; - rrdset_next(st_nodezonetype[p]); for (o = 0; o < pageorders_cnt; o++) rrddim_set_by_pointer(st_nodezonetype[p], pagelines[p].rd[o], pagelines[p].free_pages_size[o]); - rrdset_done(st_nodezonetype[p]); } } diff --git a/collectors/proc.plugin/proc_pressure.c b/collectors/proc.plugin/proc_pressure.c index 66884dbcb..6649aa630 100644 --- a/collectors/proc.plugin/proc_pressure.c +++ b/collectors/proc.plugin/proc_pressure.c @@ -91,9 +91,8 @@ static void proc_pressure_do_resource(procfile *ff, int res_idx, int some) { rrddim_add(pcs->share_time.st, some ? "some 60" : "full 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); pcs->share_time.rd300 = rrddim_add(pcs->share_time.st, some ? "some 300" : "full 300", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); - } else { - rrdset_next(pcs->share_time.st); } + pcs->share_time.value10 = strtod(procfile_lineword(ff, some ? 0 : 1, 2), NULL); pcs->share_time.value60 = strtod(procfile_lineword(ff, some ? 0 : 1, 4), NULL); pcs->share_time.value300 = strtod(procfile_lineword(ff, some ? 0 : 1, 6), NULL); @@ -113,9 +112,8 @@ static void proc_pressure_do_resource(procfile *ff, int res_idx, int some) { pressure_update_every, RRDSET_TYPE_LINE); pcs->total_time.rdtotal = rrddim_add(pcs->total_time.st, "time", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else { - rrdset_next(pcs->total_time.st); } + pcs->total_time.value_total = str2ull(procfile_lineword(ff, some ? 0 : 1, 8)) / 1000; } diff --git a/collectors/proc.plugin/proc_pressure.h b/collectors/proc.plugin/proc_pressure.h index a421cf8a4..0cb233152 100644 --- a/collectors/proc.plugin/proc_pressure.h +++ b/collectors/proc.plugin/proc_pressure.h @@ -38,6 +38,6 @@ struct pressure { } some, full; }; -extern void update_pressure_charts(struct pressure_charts *charts); +void update_pressure_charts(struct pressure_charts *charts); #endif //NETDATA_PROC_PRESSURE_H diff --git a/collectors/proc.plugin/proc_self_mountinfo.c b/collectors/proc.plugin/proc_self_mountinfo.c index 4456d5978..9310f2ffc 100644 --- a/collectors/proc.plugin/proc_self_mountinfo.c +++ b/collectors/proc.plugin/proc_self_mountinfo.c @@ -227,7 +227,8 @@ struct mountinfo *mountinfo_read(int do_statvfs) { struct mountinfo *root = NULL, *last = NULL, *mi = NULL; // create a dictionary to track uniqueness - DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_DONT_OVERWRITE_VALUE|DICTIONARY_FLAG_NAME_LINK_DONT_CLONE); + DICTIONARY *dict = dictionary_create( + DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_NAME_LINK_DONT_CLONE); unsigned long l, lines = procfile_lines(ff); for(l = 0; l < lines ;l++) { diff --git a/collectors/proc.plugin/proc_self_mountinfo.h b/collectors/proc.plugin/proc_self_mountinfo.h index b915550a7..4bd24d2d2 100644 --- a/collectors/proc.plugin/proc_self_mountinfo.h +++ b/collectors/proc.plugin/proc_self_mountinfo.h @@ -51,11 +51,11 @@ struct mountinfo { struct mountinfo *next; }; -extern struct mountinfo *mountinfo_find(struct mountinfo *root, unsigned long major, unsigned long minor, char *device); -extern struct mountinfo *mountinfo_find_by_filesystem_mount_source(struct mountinfo *root, const char *filesystem, const char *mount_source); -extern struct mountinfo *mountinfo_find_by_filesystem_super_option(struct mountinfo *root, const char *filesystem, const char *super_options); +struct mountinfo *mountinfo_find(struct mountinfo *root, unsigned long major, unsigned long minor, char *device); +struct mountinfo *mountinfo_find_by_filesystem_mount_source(struct mountinfo *root, const char *filesystem, const char *mount_source); +struct mountinfo *mountinfo_find_by_filesystem_super_option(struct mountinfo *root, const char *filesystem, const char *super_options); -extern void mountinfo_free_all(struct mountinfo *mi); -extern struct mountinfo *mountinfo_read(int do_statvfs); +void mountinfo_free_all(struct mountinfo *mi); +struct mountinfo *mountinfo_read(int do_statvfs); #endif /* NETDATA_PROC_SELF_MOUNTINFO_H */ diff --git a/collectors/proc.plugin/proc_softirqs.c b/collectors/proc.plugin/proc_softirqs.c index 7eff28c98..4c4df7668 100644 --- a/collectors/proc.plugin/proc_softirqs.c +++ b/collectors/proc.plugin/proc_softirqs.c @@ -128,7 +128,7 @@ int do_proc_softirqs(int update_every, usec_t dt) { // -------------------------------------------------------------------- static RRDSET *st_system_softirqs = NULL; - if(unlikely(!st_system_softirqs)) + if(unlikely(!st_system_softirqs)) { st_system_softirqs = rrdset_create_localhost( "system" , "softirqs" @@ -143,8 +143,7 @@ int do_proc_softirqs(int update_every, usec_t dt) { , update_every , RRDSET_TYPE_STACKED ); - else - rrdset_next(st_system_softirqs); + } for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); @@ -153,9 +152,9 @@ int do_proc_softirqs(int update_every, usec_t dt) { // some interrupt may have changed without changing the total number of lines // if the same number of interrupts have been added and removed between two // calls of this function. - if(unlikely(!irr->rd || strncmp(irr->name, irr->rd->name, MAX_INTERRUPT_NAME) != 0)) { + if(unlikely(!irr->rd || strncmp(irr->name, rrddim_name(irr->rd), MAX_INTERRUPT_NAME) != 0)) { irr->rd = rrddim_add(st_system_softirqs, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_set_name(st_system_softirqs, irr->rd, irr->name); + rrddim_reset_name(st_system_softirqs, irr->rd, irr->name); // also reset per cpu RRDDIMs to avoid repeating strncmp() in the per core loop if(likely(do_per_core != CONFIG_BOOLEAN_NO)) { @@ -220,10 +219,8 @@ int do_proc_softirqs(int update_every, usec_t dt) { char core[50+1]; snprintfz(core, 50, "cpu%d", c); - rrdlabels_add(core_st[c]->state->chart_labels, "cpu", core, RRDLABEL_SRC_AUTO); + rrdlabels_add(core_st[c]->rrdlabels, "cpu", core, RRDLABEL_SRC_AUTO); } - else - rrdset_next(core_st[c]); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); @@ -231,7 +228,7 @@ int do_proc_softirqs(int update_every, usec_t dt) { if(irr->used && (do_per_core == CONFIG_BOOLEAN_YES || irr->cpu[c].value)) { if(unlikely(!irr->cpu[c].rd)) { irr->cpu[c].rd = rrddim_add(core_st[c], irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_set_name(core_st[c], irr->cpu[c].rd, irr->name); + rrddim_reset_name(core_st[c], irr->cpu[c].rd, irr->name); } rrddim_set_by_pointer(core_st[c], irr->cpu[c].rd, irr->cpu[c].value); diff --git a/collectors/proc.plugin/proc_spl_kstat_zfs.c b/collectors/proc.plugin/proc_spl_kstat_zfs.c index fae112249..8938d6431 100644 --- a/collectors/proc.plugin/proc_spl_kstat_zfs.c +++ b/collectors/proc.plugin/proc_spl_kstat_zfs.c @@ -252,8 +252,8 @@ void disable_zfs_pool_state(struct zfs_pool *pool) pool->disabled = 1; } -int update_zfs_pool_state_chart(const char *name, void *pool_p, void *update_every_p) -{ +int update_zfs_pool_state_chart(const DICTIONARY_ITEM *item, void *pool_p, void *update_every_p) { + const char *name = dictionary_acquired_item_name(item); struct zfs_pool *pool = (struct zfs_pool *)pool_p; int update_every = *(int *)update_every_p; @@ -285,8 +285,7 @@ int update_zfs_pool_state_chart(const char *name, void *pool_p, void *update_eve pool->rd_offline = rrddim_add(pool->st, "offline", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); pool->rd_removed = rrddim_add(pool->st, "removed", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); pool->rd_unavail = rrddim_add(pool->st, "unavail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(pool->st); + } rrddim_set_by_pointer(pool->st, pool->rd_online, pool->online); rrddim_set_by_pointer(pool->st, pool->rd_degraded, pool->degraded); @@ -321,7 +320,7 @@ int do_proc_spl_kstat_zfs_pool_state(int update_every, usec_t dt) snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/spl/kstat/zfs"); dirname = config_get("plugin:proc:" ZFS_PROC_POOLS, "directory to monitor", filename); - zfs_pools = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + zfs_pools = dictionary_create(DICT_OPTION_SINGLE_THREADED); do_zfs_pool_state = 1; } diff --git a/collectors/proc.plugin/proc_stat.c b/collectors/proc.plugin/proc_stat.c index 6faba55a9..33fe93234 100644 --- a/collectors/proc.plugin/proc_stat.c +++ b/collectors/proc.plugin/proc_stat.c @@ -481,7 +481,7 @@ int do_proc_stat(int update_every, usec_t dt) { static uint32_t hash_intr, hash_ctxt, hash_processes, hash_procs_running, hash_procs_blocked; static char *core_throttle_count_filename = NULL, *package_throttle_count_filename = NULL, *scaling_cur_freq_filename = NULL, *time_in_state_filename = NULL, *schedstat_filename = NULL, *cpuidle_name_filename = NULL, *cpuidle_time_filename = NULL; - static RRDVAR *cpus_var = NULL; + static const RRDVAR_ACQUIRED *cpus_var = NULL; static int accurate_freq_avail = 0, accurate_freq_is_used = 0; size_t cores_found = (size_t)processors; @@ -713,9 +713,8 @@ int do_proc_stat(int update_every, usec_t dt) { rrddim_hide(cpu_chart->st, "idle"); if(unlikely(core == 0 && cpus_var == NULL)) - cpus_var = rrdvar_custom_host_variable_create(localhost, "active_processors"); + cpus_var = rrdvar_custom_host_variable_add_and_acquire(localhost, "active_processors"); } - else rrdset_next(cpu_chart->st); rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_user, user); rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_nice, nice); @@ -756,7 +755,6 @@ int do_proc_stat(int update_every, usec_t dt) { rd_interrupts = rrddim_add(st_intr, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_intr); rrddim_set_by_pointer(st_intr, rd_interrupts, value); rrdset_done(st_intr); @@ -786,7 +784,6 @@ int do_proc_stat(int update_every, usec_t dt) { rd_switches = rrddim_add(st_ctxt, "switches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_ctxt); rrddim_set_by_pointer(st_ctxt, rd_switches, value); rrdset_done(st_ctxt); @@ -828,7 +825,6 @@ int do_proc_stat(int update_every, usec_t dt) { rd_started = rrddim_add(st_forks, "started", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_forks); rrddim_set_by_pointer(st_forks, rd_started, processes); rrdset_done(st_forks); @@ -860,7 +856,6 @@ int do_proc_stat(int update_every, usec_t dt) { rd_running = rrddim_add(st_processes, "running", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_blocked = rrddim_add(st_processes, "blocked", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st_processes); rrddim_set_by_pointer(st_processes, rd_running, running); rrddim_set_by_pointer(st_processes, rd_blocked, blocked); @@ -875,7 +870,7 @@ int do_proc_stat(int update_every, usec_t dt) { static RRDSET *st_core_throttle_count = NULL; - if (unlikely(!st_core_throttle_count)) + if (unlikely(!st_core_throttle_count)) { st_core_throttle_count = rrdset_create_localhost( "cpu" , "core_throttling" @@ -890,8 +885,7 @@ int do_proc_stat(int update_every, usec_t dt) { , update_every , RRDSET_TYPE_LINE ); - else - rrdset_next(st_core_throttle_count); + } chart_per_core_files(&all_cpu_charts[1], all_cpu_charts_size - 1, CORE_THROTTLE_COUNT_INDEX, st_core_throttle_count, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrdset_done(st_core_throttle_count); @@ -905,7 +899,7 @@ int do_proc_stat(int update_every, usec_t dt) { static RRDSET *st_package_throttle_count = NULL; - if(unlikely(!st_package_throttle_count)) + if(unlikely(!st_package_throttle_count)) { st_package_throttle_count = rrdset_create_localhost( "cpu" , "package_throttling" @@ -920,8 +914,7 @@ int do_proc_stat(int update_every, usec_t dt) { , update_every , RRDSET_TYPE_LINE ); - else - rrdset_next(st_package_throttle_count); + } chart_per_core_files(&all_cpu_charts[1], all_cpu_charts_size - 1, PACKAGE_THROTTLE_COUNT_INDEX, st_package_throttle_count, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrdset_done(st_package_throttle_count); @@ -954,7 +947,7 @@ int do_proc_stat(int update_every, usec_t dt) { static RRDSET *st_scaling_cur_freq = NULL; - if(unlikely(!st_scaling_cur_freq)) + if(unlikely(!st_scaling_cur_freq)) { st_scaling_cur_freq = rrdset_create_localhost( "cpu" , "cpufreq" @@ -969,8 +962,7 @@ int do_proc_stat(int update_every, usec_t dt) { , update_every , RRDSET_TYPE_LINE ); - else - rrdset_next(st_scaling_cur_freq); + } chart_per_core_files(&all_cpu_charts[1], all_cpu_charts_size - 1, CPU_FREQ_INDEX, st_scaling_cur_freq, 1, 1000, RRD_ALGORITHM_ABSOLUTE); rrdset_done(st_scaling_cur_freq); @@ -1041,7 +1033,7 @@ int do_proc_stat(int update_every, usec_t dt) { char corebuf[50+1]; snprintfz(corebuf, 50, "cpu%zu", core); - rrdlabels_add(cpuidle_charts[core].st->state->chart_labels, "cpu", corebuf, RRDLABEL_SRC_AUTO); + rrdlabels_add(cpuidle_charts[core].st->rrdlabels, "cpu", corebuf, RRDLABEL_SRC_AUTO); char cpuidle_dim_id[RRD_ID_LENGTH_MAX + 1]; cpuidle_charts[core].active_time_rd = rrddim_add(cpuidle_charts[core].st, "active", "C0 (active)", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); @@ -1054,8 +1046,6 @@ int do_proc_stat(int update_every, usec_t dt) { 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } } - else - rrdset_next(cpuidle_charts[core].st); rrddim_set_by_pointer(cpuidle_charts[core].st, cpuidle_charts[core].active_time_rd, cpuidle_charts[core].active_time); for(state = 0; state < cpuidle_charts[core].cpuidle_state_len; state++) { diff --git a/collectors/proc.plugin/proc_sys_kernel_random_entropy_avail.c b/collectors/proc.plugin/proc_sys_kernel_random_entropy_avail.c index 20d2116ce..a04d43039 100644 --- a/collectors/proc.plugin/proc_sys_kernel_random_entropy_avail.c +++ b/collectors/proc.plugin/proc_sys_kernel_random_entropy_avail.c @@ -40,10 +40,8 @@ int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt) { rd = rrddim_add(st, "entropy", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); rrddim_set_by_pointer(st, rd, entropy); rrdset_done(st); - return 0; } diff --git a/collectors/proc.plugin/proc_uptime.c b/collectors/proc.plugin/proc_uptime.c index 28b00e0da..ddab7269b 100644 --- a/collectors/proc.plugin/proc_uptime.c +++ b/collectors/proc.plugin/proc_uptime.c @@ -35,12 +35,8 @@ int do_proc_uptime(int update_every, usec_t dt) { rd = rrddim_add(st, "uptime", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st); rrddim_set_by_pointer(st, rd, uptime_msec(uptime_filename)); - rrdset_done(st); - return 0; } diff --git a/collectors/proc.plugin/proc_vmstat.c b/collectors/proc.plugin/proc_vmstat.c index c1a137161..b8defc455 100644 --- a/collectors/proc.plugin/proc_vmstat.c +++ b/collectors/proc.plugin/proc_vmstat.c @@ -138,7 +138,6 @@ int do_proc_vmstat(int update_every, usec_t dt) { rd_in = rrddim_add(st_swapio, "in", NULL, sysconf(_SC_PAGESIZE), 1024, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st_swapio, "out", NULL, -sysconf(_SC_PAGESIZE), 1024, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_swapio); rrddim_set_by_pointer(st_swapio, rd_in, pswpin); rrddim_set_by_pointer(st_swapio, rd_out, pswpout); @@ -170,7 +169,6 @@ int do_proc_vmstat(int update_every, usec_t dt) { rd_in = rrddim_add(st_io, "in", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st_io, "out", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_io); rrddim_set_by_pointer(st_io, rd_in, pgpgin); rrddim_set_by_pointer(st_io, rd_out, pgpgout); @@ -204,7 +202,6 @@ int do_proc_vmstat(int update_every, usec_t dt) { rd_minor = rrddim_add(st_pgfaults, "minor", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_major = rrddim_add(st_pgfaults, "major", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_pgfaults); rrddim_set_by_pointer(st_pgfaults, rd_minor, pgfault); rrddim_set_by_pointer(st_pgfaults, rd_major, pgmajfault); @@ -240,7 +237,6 @@ int do_proc_vmstat(int update_every, usec_t dt) { rd_oom_kill = rrddim_add(st_oom_kill, "kills", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_oom_kill); rrddim_set_by_pointer(st_oom_kill, rd_oom_kill, oom_kill); rrdset_done(st_oom_kill); @@ -295,7 +291,6 @@ int do_proc_vmstat(int update_every, usec_t dt) { rd_hint_faults_local = rrddim_add(st_numa, "hint_faults_local", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_pages_migrated = rrddim_add(st_numa, "pages_migrated", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st_numa); rrddim_set_by_pointer(st_numa, rd_local, numa_local); rrddim_set_by_pointer(st_numa, rd_foreign, numa_foreign); diff --git a/collectors/proc.plugin/sys_block_zram.c b/collectors/proc.plugin/sys_block_zram.c index ddd1e7ae0..6bae54243 100644 --- a/collectors/proc.plugin/sys_block_zram.c +++ b/collectors/proc.plugin/sys_block_zram.c @@ -33,8 +33,6 @@ typedef struct zram_device { RRDDIM *rd_alloc_efficiency; } ZRAM_DEVICE; - // -------------------------------------------------------------------- - static int try_get_zram_major_number(procfile *file) { size_t i; unsigned int lines = procfile_lines(file); @@ -75,7 +73,7 @@ static inline void init_rrd(const char *name, ZRAM_DEVICE *d, int update_every) , RRDSET_TYPE_AREA); d->rd_compr_data_size = rrddim_add(d->st_usage, "compressed", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); d->rd_metadata_size = rrddim_add(d->st_usage, "metadata", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); - rrdlabels_add(d->st_usage->state->chart_labels, "device", name, RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_usage->rrdlabels, "device", name, RRDLABEL_SRC_AUTO); snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_savings.%s", name); d->st_savings = rrdset_create_localhost( @@ -93,7 +91,7 @@ static inline void init_rrd(const char *name, ZRAM_DEVICE *d, int update_every) , RRDSET_TYPE_AREA); d->rd_savings_size = rrddim_add(d->st_savings, "savings", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); d->rd_original_size = rrddim_add(d->st_savings, "original", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); - rrdlabels_add(d->st_savings->state->chart_labels, "device", name, RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_savings->rrdlabels, "device", name, RRDLABEL_SRC_AUTO); snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_ratio.%s", name); d->st_comp_ratio = rrdset_create_localhost( @@ -110,7 +108,7 @@ static inline void init_rrd(const char *name, ZRAM_DEVICE *d, int update_every) , update_every , RRDSET_TYPE_LINE); d->rd_comp_ratio = rrddim_add(d->st_comp_ratio, "ratio", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); - rrdlabels_add(d->st_comp_ratio->state->chart_labels, "device", name, RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_comp_ratio->rrdlabels, "device", name, RRDLABEL_SRC_AUTO); snprintfz(chart_name, RRD_ID_LENGTH_MAX, "zram_efficiency.%s", name); d->st_alloc_efficiency = rrdset_create_localhost( @@ -127,7 +125,7 @@ static inline void init_rrd(const char *name, ZRAM_DEVICE *d, int update_every) , update_every , RRDSET_TYPE_LINE); d->rd_alloc_efficiency = rrddim_add(d->st_alloc_efficiency, "percent", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE); - rrdlabels_add(d->st_alloc_efficiency->state->chart_labels, "device", name, RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_alloc_efficiency->rrdlabels, "device", name, RRDLABEL_SRC_AUTO); } static int init_devices(DICTIONARY *devices, unsigned int zram_id, int update_every) { @@ -177,9 +175,8 @@ static void free_device(DICTIONARY *dict, const char *name) rrdset_obsolete_and_pointer_null(d->st_savings); rrdset_obsolete_and_pointer_null(d->st_alloc_efficiency); rrdset_obsolete_and_pointer_null(d->st_comp_ratio); - dictionary_del_having_write_lock(dict, name); + dictionary_del(dict, name); } - // -------------------------------------------------------------------- static inline int read_mm_stat(procfile *ff, MM_STAT *stats) { ff = procfile_readall(ff); @@ -204,61 +201,51 @@ static inline int read_mm_stat(procfile *ff, MM_STAT *stats) { return 0; } -static inline int _collect_zram_metrics(const char* name, ZRAM_DEVICE *d, int advance, DICTIONARY* dict) { +static int collect_zram_metrics(const DICTIONARY_ITEM *item, void *entry, void *data) { + const char *name = dictionary_acquired_item_name(item); + ZRAM_DEVICE *dev = entry; + DICTIONARY *dict = data; + MM_STAT mm; int value; - if (unlikely(read_mm_stat(d->file, &mm) < 0)) - { + + if (unlikely(read_mm_stat(dev->file, &mm) < 0)) { free_device(dict, name); return -1; } - if (likely(advance)) - { - rrdset_next(d->st_usage); - rrdset_next(d->st_savings); - rrdset_next(d->st_comp_ratio); - rrdset_next(d->st_alloc_efficiency); - } // zram_usage - rrddim_set_by_pointer(d->st_usage, d->rd_compr_data_size, mm.compr_data_size); - rrddim_set_by_pointer(d->st_usage, d->rd_metadata_size, mm.mem_used_total - mm.compr_data_size); - rrdset_done(d->st_usage); + rrddim_set_by_pointer(dev->st_usage, dev->rd_compr_data_size, mm.compr_data_size); + rrddim_set_by_pointer(dev->st_usage, dev->rd_metadata_size, mm.mem_used_total - mm.compr_data_size); + rrdset_done(dev->st_usage); + // zram_savings - rrddim_set_by_pointer(d->st_savings, d->rd_savings_size, mm.compr_data_size - mm.orig_data_size); - rrddim_set_by_pointer(d->st_savings, d->rd_original_size, mm.orig_data_size); - rrdset_done(d->st_savings); + rrddim_set_by_pointer(dev->st_savings, dev->rd_savings_size, mm.compr_data_size - mm.orig_data_size); + rrddim_set_by_pointer(dev->st_savings, dev->rd_original_size, mm.orig_data_size); + rrdset_done(dev->st_savings); + // zram_ratio value = mm.compr_data_size == 0 ? 1 : mm.orig_data_size * 100 / mm.compr_data_size; - rrddim_set_by_pointer(d->st_comp_ratio, d->rd_comp_ratio, value); - rrdset_done(d->st_comp_ratio); + rrddim_set_by_pointer(dev->st_comp_ratio, dev->rd_comp_ratio, value); + rrdset_done(dev->st_comp_ratio); + // zram_efficiency value = mm.mem_used_total == 0 ? 100 : (mm.compr_data_size * 1000000 / mm.mem_used_total); - rrddim_set_by_pointer(d->st_alloc_efficiency, d->rd_alloc_efficiency, value); - rrdset_done(d->st_alloc_efficiency); - return 0; -} - -static int collect_first_zram_metrics(const char *name, void *entry, void *data) { - // collect without calling rrdset_next (init only) - return _collect_zram_metrics(name, (ZRAM_DEVICE *)entry, 0, (DICTIONARY *)data); -} + rrddim_set_by_pointer(dev->st_alloc_efficiency, dev->rd_alloc_efficiency, value); + rrdset_done(dev->st_alloc_efficiency); -static int collect_zram_metrics(const char *name, void *entry, void *data) { - (void)name; - // collect with calling rrdset_next - return _collect_zram_metrics(name, (ZRAM_DEVICE *)entry, 1, (DICTIONARY *)data); + return 0; } - // -------------------------------------------------------------------- - int do_sys_block_zram(int update_every, usec_t dt) { - (void)dt; static procfile *ff = NULL; static DICTIONARY *devices = NULL; static int initialized = 0; static int device_count = 0; int zram_id = -1; + + (void)dt; + if (unlikely(!initialized)) { initialized = 1; @@ -280,17 +267,13 @@ int do_sys_block_zram(int update_every, usec_t dt) { } procfile_close(ff); - devices = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + devices = dictionary_create(DICT_OPTION_SINGLE_THREADED); device_count = init_devices(devices, (unsigned int)zram_id, update_every); - if (device_count < 1) - return 1; - dictionary_walkthrough_write(devices, collect_first_zram_metrics, devices); - } - else - { - if (unlikely(device_count < 1)) - return 1; - dictionary_walkthrough_write(devices, collect_zram_metrics, devices); } + + if (unlikely(device_count < 1)) + return 1; + + dictionary_walkthrough_write(devices, collect_zram_metrics, devices); return 0; } \ No newline at end of file diff --git a/collectors/proc.plugin/sys_class_infiniband.c b/collectors/proc.plugin/sys_class_infiniband.c index 7e63bcbb4..fca0cb8a2 100644 --- a/collectors/proc.plugin/sys_class_infiniband.c +++ b/collectors/proc.plugin/sys_class_infiniband.c @@ -184,7 +184,7 @@ static struct ibport { RRDSET *st_hwpackets; RRDSET *st_hwerrors; - RRDSETVAR *stv_speed; + const RRDSETVAR_ACQUIRED *stv_speed; usec_t speed_last_collected_usec; @@ -543,15 +543,14 @@ int do_sys_class_infiniband(int update_every, usec_t dt) // x4 lanes multiplier as per Documentation/ABI/stable/sysfs-class-infiniband FOREACH_COUNTER_BYTES(GEN_RRD_DIM_ADD_CUSTOM, port, 4 * 8 * port->width, 1024, RRD_ALGORITHM_INCREMENTAL) - port->stv_speed = rrdsetvar_custom_chart_variable_create(port->st_bytes, "link_speed"); - } else - rrdset_next(port->st_bytes); + port->stv_speed = rrdsetvar_custom_chart_variable_add_and_acquire(port->st_bytes, "link_speed"); + } // Link read values to dimensions FOREACH_COUNTER_BYTES(GEN_RRD_DIM_SETP, port) // For link speed set only variable - rrdsetvar_custom_chart_variable_set(port->stv_speed, port->speed); + rrdsetvar_custom_chart_variable_set(port->st_bytes, port->stv_speed, port->speed); rrdset_done(port->st_bytes); } @@ -578,8 +577,7 @@ int do_sys_class_infiniband(int update_every, usec_t dt) // Create Dimensions rrdset_flag_set(port->st_packets, RRDSET_FLAG_DETAIL); FOREACH_COUNTER_PACKETS(GEN_RRD_DIM_ADD, port) - } else - rrdset_next(port->st_packets); + } // Link read values to dimensions FOREACH_COUNTER_PACKETS(GEN_RRD_DIM_SETP, port) @@ -608,8 +606,7 @@ int do_sys_class_infiniband(int update_every, usec_t dt) // Create Dimensions rrdset_flag_set(port->st_errors, RRDSET_FLAG_DETAIL); FOREACH_COUNTER_ERRORS(GEN_RRD_DIM_ADD, port) - } else - rrdset_next(port->st_errors); + } // Link read values to dimensions FOREACH_COUNTER_ERRORS(GEN_RRD_DIM_SETP, port) @@ -658,8 +655,7 @@ int do_sys_class_infiniband(int update_every, usec_t dt) port->name); port->do_hwerrors = CONFIG_BOOLEAN_NO; } - } else - rrdset_next(port->st_hwerrors); + } } if (port->do_hwpackets != CONFIG_BOOLEAN_NO) { @@ -695,8 +691,7 @@ int do_sys_class_infiniband(int update_every, usec_t dt) port->name); port->do_hwpackets = CONFIG_BOOLEAN_NO; } - } else - rrdset_next(port->st_hwpackets); + } } // Update values to rrd (done by vendor-specific function) diff --git a/collectors/proc.plugin/sys_class_power_supply.c b/collectors/proc.plugin/sys_class_power_supply.c index a80d46e93..dde421503 100644 --- a/collectors/proc.plugin/sys_class_power_supply.c +++ b/collectors/proc.plugin/sys_class_power_supply.c @@ -113,7 +113,7 @@ void power_supply_free(struct power_supply *ps) { } static void add_labels_to_power_supply(struct power_supply *ps, RRDSET *st) { - rrdlabels_add(st->state->chart_labels, "device", ps->name, RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device", ps->name, RRDLABEL_SRC_AUTO); } int do_sys_class_power_supply(int update_every, usec_t dt) { @@ -365,8 +365,6 @@ int do_sys_class_power_supply(int update_every, usec_t dt) { add_labels_to_power_supply(ps, ps->capacity->st); } - else - rrdset_next(ps->capacity->st); if(unlikely(!ps->capacity->rd)) ps->capacity->rd = rrddim_add(ps->capacity->st, "capacity", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_set_by_pointer(ps->capacity->st, ps->capacity->rd, ps->capacity->value); @@ -398,8 +396,6 @@ int do_sys_class_power_supply(int update_every, usec_t dt) { add_labels_to_power_supply(ps, pr->st); } - else - rrdset_next(pr->st); struct ps_property_dim *pd; for(pd = pr->property_dim_root; pd; pd = pd->next) { diff --git a/collectors/proc.plugin/sys_devices_system_edac_mc.c b/collectors/proc.plugin/sys_devices_system_edac_mc.c index 290157903..13d209781 100644 --- a/collectors/proc.plugin/sys_devices_system_edac_mc.c +++ b/collectors/proc.plugin/sys_devices_system_edac_mc.c @@ -78,8 +78,8 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) { struct mc *m; if(unlikely(do_ce == -1)) { - do_ce = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory correctable errors", CONFIG_BOOLEAN_AUTO); - do_ue = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory uncorrectable errors", CONFIG_BOOLEAN_AUTO); + do_ce = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory correctable errors", CONFIG_BOOLEAN_YES); + do_ue = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory uncorrectable errors", CONFIG_BOOLEAN_YES); } if(do_ce != CONFIG_BOOLEAN_NO) { @@ -150,8 +150,6 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) { , RRDSET_TYPE_LINE ); } - else - rrdset_next(ce_st); for(m = mc_root; m; m = m->next) { if (m->ce_count_filename && m->ce_updated) { @@ -189,8 +187,6 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) { , RRDSET_TYPE_LINE ); } - else - rrdset_next(ue_st); for(m = mc_root; m; m = m->next) { if (m->ue_count_filename && m->ue_updated) { diff --git a/collectors/proc.plugin/sys_devices_system_node.c b/collectors/proc.plugin/sys_devices_system_node.c index fd3394309..90aafd56a 100644 --- a/collectors/proc.plugin/sys_devices_system_node.c +++ b/collectors/proc.plugin/sys_devices_system_node.c @@ -115,7 +115,7 @@ int do_proc_sys_devices_system_node(int update_every, usec_t dt) { , RRDSET_TYPE_LINE ); - rrdlabels_add(m->numastat_st->state->chart_labels, "numa_node", m->name, RRDLABEL_SRC_AUTO); + rrdlabels_add(m->numastat_st->rrdlabels, "numa_node", m->name, RRDLABEL_SRC_AUTO); rrdset_flag_set(m->numastat_st, RRDSET_FLAG_DETAIL); @@ -127,7 +127,6 @@ int do_proc_sys_devices_system_node(int update_every, usec_t dt) { rrddim_add(m->numastat_st, "other_node", "other", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(m->numastat_st); size_t lines = procfile_lines(m->numastat_ff), l; for(l = 0; l < lines; l++) { diff --git a/collectors/proc.plugin/sys_fs_btrfs.c b/collectors/proc.plugin/sys_fs_btrfs.c index 158587a8f..3b9841fec 100644 --- a/collectors/proc.plugin/sys_fs_btrfs.c +++ b/collectors/proc.plugin/sys_fs_btrfs.c @@ -449,8 +449,8 @@ static inline int find_all_btrfs_pools(const char *path) { } static void add_labels_to_btrfs(BTRFS_NODE *n, RRDSET *st) { - rrdlabels_add(st->state->chart_labels, "device", n->id, RRDLABEL_SRC_AUTO); - rrdlabels_add(st->state->chart_labels, "device_label", n->label, RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device", n->id, RRDLABEL_SRC_AUTO); + rrdlabels_add(st->rrdlabels, "device_label", n->label, RRDLABEL_SRC_AUTO); } int do_sys_fs_btrfs(int update_every, usec_t dt) { @@ -587,7 +587,6 @@ int do_sys_fs_btrfs(int update_every, usec_t dt) { add_labels_to_btrfs(node, node->st_allocation_disks); } - else rrdset_next(node->st_allocation_disks); // unsigned long long disk_used = node->allocation_data_disk_used + node->allocation_metadata_disk_used + node->allocation_system_disk_used; unsigned long long disk_total = node->allocation_data_disk_total + node->allocation_metadata_disk_total + node->allocation_system_disk_total; @@ -642,7 +641,6 @@ int do_sys_fs_btrfs(int update_every, usec_t dt) { add_labels_to_btrfs(node, node->st_allocation_data); } - else rrdset_next(node->st_allocation_data); rrddim_set_by_pointer(node->st_allocation_data, node->rd_allocation_data_free, node->allocation_data_total_bytes - node->allocation_data_bytes_used); rrddim_set_by_pointer(node->st_allocation_data, node->rd_allocation_data_used, node->allocation_data_bytes_used); @@ -688,7 +686,6 @@ int do_sys_fs_btrfs(int update_every, usec_t dt) { add_labels_to_btrfs(node, node->st_allocation_metadata); } - else rrdset_next(node->st_allocation_metadata); rrddim_set_by_pointer(node->st_allocation_metadata, node->rd_allocation_metadata_free, node->allocation_metadata_total_bytes - node->allocation_metadata_bytes_used - node->allocation_global_rsv_size); rrddim_set_by_pointer(node->st_allocation_metadata, node->rd_allocation_metadata_used, node->allocation_metadata_bytes_used); @@ -734,7 +731,6 @@ int do_sys_fs_btrfs(int update_every, usec_t dt) { add_labels_to_btrfs(node, node->st_allocation_system); } - else rrdset_next(node->st_allocation_system); rrddim_set_by_pointer(node->st_allocation_system, node->rd_allocation_system_free, node->allocation_system_total_bytes - node->allocation_system_bytes_used); rrddim_set_by_pointer(node->st_allocation_system, node->rd_allocation_system_used, node->allocation_system_bytes_used); diff --git a/collectors/proc.plugin/sys_kernel_mm_ksm.c b/collectors/proc.plugin/sys_kernel_mm_ksm.c index a0e5690fe..e586d5554 100644 --- a/collectors/proc.plugin/sys_kernel_mm_ksm.c +++ b/collectors/proc.plugin/sys_kernel_mm_ksm.c @@ -119,8 +119,6 @@ int do_sys_kernel_mm_ksm(int update_every, usec_t dt) { rd_volatile = rrddim_add(st_mem_ksm, "volatile", NULL, -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); //rd_to_scan = rrddim_add(st_mem_ksm, "to_scan", "to scan", -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_mem_ksm); rrddim_set_by_pointer(st_mem_ksm, rd_shared, pages_shared * page_size); rrddim_set_by_pointer(st_mem_ksm, rd_unshared, pages_unshared * page_size); @@ -156,8 +154,6 @@ int do_sys_kernel_mm_ksm(int update_every, usec_t dt) { rd_savings = rrddim_add(st_mem_ksm_savings, "savings", NULL, -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); rd_offered = rrddim_add(st_mem_ksm_savings, "offered", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_mem_ksm_savings); rrddim_set_by_pointer(st_mem_ksm_savings, rd_savings, saved * page_size); rrddim_set_by_pointer(st_mem_ksm_savings, rd_offered, offered * page_size); @@ -189,11 +185,8 @@ int do_sys_kernel_mm_ksm(int update_every, usec_t dt) { rd_savings = rrddim_add(st_mem_ksm_ratios, "savings", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_mem_ksm_ratios); rrddim_set_by_pointer(st_mem_ksm_ratios, rd_savings, offered ? (saved * 1000000) / offered : 0); - rrdset_done(st_mem_ksm_ratios); } diff --git a/collectors/proc.plugin/zfs_common.c b/collectors/proc.plugin/zfs_common.c index 330bcf18b..cca0ae0e6 100644 --- a/collectors/proc.plugin/zfs_common.c +++ b/collectors/proc.plugin/zfs_common.c @@ -67,8 +67,6 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z rd_arc_target_min_size = rrddim_add(st_arc_size, "min", "min (hard limit)", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); rd_arc_target_max_size = rrddim_add(st_arc_size, "max", "max (high water)", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_arc_size); rrddim_set_by_pointer(st_arc_size, rd_arc_size, arcstats.size); rrddim_set_by_pointer(st_arc_size, rd_arc_target_size, arcstats.c); @@ -105,8 +103,6 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z rd_l2_asize = rrddim_add(st_l2_size, "actual", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); rd_l2_size = rrddim_add(st_l2_size, "size", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_l2_size); rrddim_set_by_pointer(st_l2_size, rd_l2_size, arcstats.l2_size); rrddim_set_by_pointer(st_l2_size, rd_l2_asize, arcstats.l2_asize); @@ -149,8 +145,6 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z if(arcstats.l2exist) rd_l2read = rrddim_add(st_reads, "l2reads", "l2", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_reads); rrddim_set_by_pointer(st_reads, rd_aread, aread); rrddim_set_by_pointer(st_reads, rd_dread, dread); @@ -191,8 +185,6 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z rd_l2_read_bytes = rrddim_add(st_l2bytes, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rd_l2_write_bytes = rrddim_add(st_l2bytes, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_l2bytes); rrddim_set_by_pointer(st_l2bytes, rd_l2_read_bytes, arcstats.l2_read_bytes); rrddim_set_by_pointer(st_l2bytes, rd_l2_write_bytes, arcstats.l2_write_bytes); @@ -227,12 +219,38 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z rd_ahits = rrddim_add(st_ahits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rd_amisses = rrddim_add(st_ahits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } - else - rrdset_next(st_ahits); rrddim_set_by_pointer(st_ahits, rd_ahits, arcstats.hits); rrddim_set_by_pointer(st_ahits, rd_amisses, arcstats.misses); rrdset_done(st_ahits); + + static RRDSET *st_ahits_rate = NULL; + static RRDDIM *rd_ahits_rate = NULL; + static RRDDIM *rd_amisses_rate = NULL; + + if (unlikely(!st_ahits_rate)) { + st_ahits_rate = rrdset_create_localhost( + "zfs" + , "hits_rate" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS ARC Hits Rate" + , "events/s" + , plugin + , module + , NETDATA_CHART_PRIO_ZFS_HITS + 1 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_ahits_rate = rrddim_add(st_ahits_rate, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_amisses_rate = rrddim_add(st_ahits_rate, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_ahits_rate, rd_ahits_rate, arcstats.hits); + rrddim_set_by_pointer(st_ahits_rate, rd_amisses_rate, arcstats.misses); + rrdset_done(st_ahits_rate); } // -------------------------------------------------------------------- @@ -263,12 +281,38 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z rd_dhits = rrddim_add(st_dhits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rd_dmisses = rrddim_add(st_dhits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } - else - rrdset_next(st_dhits); rrddim_set_by_pointer(st_dhits, rd_dhits, dhit); rrddim_set_by_pointer(st_dhits, rd_dmisses, dmiss); rrdset_done(st_dhits); + + static RRDSET *st_dhits_rate = NULL; + static RRDDIM *rd_dhits_rate = NULL; + static RRDDIM *rd_dmisses_rate = NULL; + + if (unlikely(!st_dhits_rate)) { + st_dhits_rate = rrdset_create_localhost( + "zfs" + , "dhits_rate" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Demand Hits Rate" + , "events/s" + , plugin + , module + , NETDATA_CHART_PRIO_ZFS_DHITS + 1 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_dhits_rate = rrddim_add(st_dhits_rate, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_dmisses_rate = rrddim_add(st_dhits_rate, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_dhits_rate, rd_dhits_rate, dhit); + rrddim_set_by_pointer(st_dhits_rate, rd_dmisses_rate, dmiss); + rrdset_done(st_dhits_rate); } // -------------------------------------------------------------------- @@ -299,12 +343,38 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z rd_phits = rrddim_add(st_phits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rd_pmisses = rrddim_add(st_phits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } - else - rrdset_next(st_phits); rrddim_set_by_pointer(st_phits, rd_phits, phit); rrddim_set_by_pointer(st_phits, rd_pmisses, pmiss); rrdset_done(st_phits); + + static RRDSET *st_phits_rate = NULL; + static RRDDIM *rd_phits_rate = NULL; + static RRDDIM *rd_pmisses_rate = NULL; + + if (unlikely(!st_phits_rate)) { + st_phits_rate = rrdset_create_localhost( + "zfs" + , "phits_rate" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Prefetch Hits Rate" + , "events/s" + , plugin + , module + , NETDATA_CHART_PRIO_ZFS_PHITS + 1 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_phits_rate = rrddim_add(st_phits_rate, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_pmisses_rate = rrddim_add(st_phits_rate, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_phits_rate, rd_phits_rate, phit); + rrddim_set_by_pointer(st_phits_rate, rd_pmisses_rate, pmiss); + rrdset_done(st_phits_rate); } // -------------------------------------------------------------------- @@ -335,12 +405,38 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z rd_mhits = rrddim_add(st_mhits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rd_mmisses = rrddim_add(st_mhits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } - else - rrdset_next(st_mhits); rrddim_set_by_pointer(st_mhits, rd_mhits, mhit); rrddim_set_by_pointer(st_mhits, rd_mmisses, mmiss); rrdset_done(st_mhits); + + static RRDSET *st_mhits_rate = NULL; + static RRDDIM *rd_mhits_rate = NULL; + static RRDDIM *rd_mmisses_rate = NULL; + + if (unlikely(!st_mhits_rate)) { + st_mhits_rate = rrdset_create_localhost( + "zfs" + , "mhits_rate" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Metadata Hits Rate" + , "events/s" + , plugin + , module + , NETDATA_CHART_PRIO_ZFS_MHITS + 1 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_mhits_rate = rrddim_add(st_mhits_rate, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_mmisses_rate = rrddim_add(st_mhits_rate, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_mhits_rate, rd_mhits_rate, mhit); + rrddim_set_by_pointer(st_mhits_rate, rd_mmisses_rate, mmiss); + rrdset_done(st_mhits_rate); } // -------------------------------------------------------------------- @@ -371,12 +467,38 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z rd_l2hits = rrddim_add(st_l2hits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rd_l2misses = rrddim_add(st_l2hits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } - else - rrdset_next(st_l2hits); rrddim_set_by_pointer(st_l2hits, rd_l2hits, l2hit); rrddim_set_by_pointer(st_l2hits, rd_l2misses, l2miss); rrdset_done(st_l2hits); + + static RRDSET *st_l2hits_rate = NULL; + static RRDDIM *rd_l2hits_rate = NULL; + static RRDDIM *rd_l2misses_rate = NULL; + + if (unlikely(!st_l2hits_rate)) { + st_l2hits_rate = rrdset_create_localhost( + "zfs" + , "l2hits_rate" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS L2 Hits Rate" + , "events/s" + , plugin + , module + , NETDATA_CHART_PRIO_ZFS_L2HITS + 1 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_l2hits_rate = rrddim_add(st_l2hits_rate, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_l2misses_rate = rrddim_add(st_l2hits_rate, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_l2hits_rate, rd_l2hits_rate, l2hit); + rrddim_set_by_pointer(st_l2hits_rate, rd_l2misses_rate, l2miss); + rrdset_done(st_l2hits_rate); } // -------------------------------------------------------------------- @@ -414,8 +536,6 @@ void generate_charts_arcstats(const char *plugin, const char *module, int show_z rd_mru = rrddim_add(st_list_hits, "mru", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_mrug = rrddim_add(st_list_hits, "mrug", "mru ghost", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_list_hits); rrddim_set_by_pointer(st_list_hits, rd_mfu, arcstats.mfu_hits); rrddim_set_by_pointer(st_list_hits, rd_mru, arcstats.mru_hits); @@ -480,8 +600,6 @@ void generate_charts_arc_summary(const char *plugin, const char *module, int sho rd_most_recent = rrddim_add(st_arc_size_breakdown, "recent", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL); rd_most_frequent = rrddim_add(st_arc_size_breakdown, "frequent", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL); } - else - rrdset_next(st_arc_size_breakdown); rrddim_set_by_pointer(st_arc_size_breakdown, rd_most_recent, mru_size); rrddim_set_by_pointer(st_arc_size_breakdown, rd_most_frequent, mfu_size); @@ -528,8 +646,6 @@ void generate_charts_arc_summary(const char *plugin, const char *module, int sho rd_indirect = rrddim_add(st_memory, "indirect", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); #endif } - else - rrdset_next(st_memory); #ifndef __FreeBSD__ rrddim_set_by_pointer(st_memory, rd_direct, arcstats.memory_direct_count); @@ -576,8 +692,6 @@ void generate_charts_arc_summary(const char *plugin, const char *module, int sho rd_mutex_misses = rrddim_add(st_important_ops, "mtxmis", "mutex miss", 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_hash_collisions = rrddim_add(st_important_ops, "hash_collisions", "hash collisions", 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_important_ops); rrddim_set_by_pointer(st_important_ops, rd_deleted, arcstats.deleted); rrddim_set_by_pointer(st_important_ops, rd_evict_skips, arcstats.evict_skip); @@ -614,12 +728,38 @@ void generate_charts_arc_summary(const char *plugin, const char *module, int sho rd_actual_hits = rrddim_add(st_actual_hits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rd_actual_misses = rrddim_add(st_actual_hits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } - else - rrdset_next(st_actual_hits); rrddim_set_by_pointer(st_actual_hits, rd_actual_hits, real_hits); rrddim_set_by_pointer(st_actual_hits, rd_actual_misses, real_misses); rrdset_done(st_actual_hits); + + static RRDSET *st_actual_hits_rate = NULL; + static RRDDIM *rd_actual_hits_rate = NULL; + static RRDDIM *rd_actual_misses_rate = NULL; + + if (unlikely(!st_actual_hits_rate)) { + st_actual_hits_rate = rrdset_create_localhost( + "zfs" + , "actual_hits_rate" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Actual Cache Hits Rate" + , "events/s" + , plugin + , module + , NETDATA_CHART_PRIO_ZFS_ACTUAL_HITS + 1 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_actual_hits_rate = rrddim_add(st_actual_hits_rate, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_actual_misses_rate = rrddim_add(st_actual_hits_rate, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_actual_hits_rate, rd_actual_hits_rate, real_hits); + rrddim_set_by_pointer(st_actual_hits_rate, rd_actual_misses_rate, real_misses); + rrdset_done(st_actual_hits_rate); } // -------------------------------------------------------------------- @@ -650,12 +790,38 @@ void generate_charts_arc_summary(const char *plugin, const char *module, int sho rd_demand_data_hits = rrddim_add(st_demand_data_hits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rd_demand_data_misses = rrddim_add(st_demand_data_hits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } - else - rrdset_next(st_demand_data_hits); rrddim_set_by_pointer(st_demand_data_hits, rd_demand_data_hits, arcstats.demand_data_hits); rrddim_set_by_pointer(st_demand_data_hits, rd_demand_data_misses, arcstats.demand_data_misses); rrdset_done(st_demand_data_hits); + + static RRDSET *st_demand_data_hits_rate = NULL; + static RRDDIM *rd_demand_data_hits_rate = NULL; + static RRDDIM *rd_demand_data_misses_rate = NULL; + + if (unlikely(!st_demand_data_hits_rate)) { + st_demand_data_hits_rate = rrdset_create_localhost( + "zfs" + , "demand_data_hits_rate" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Data Demand Efficiency Rate" + , "events/s" + , plugin + , module + , NETDATA_CHART_PRIO_ZFS_DEMAND_DATA_HITS + 1 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_demand_data_hits_rate = rrddim_add(st_demand_data_hits_rate, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_demand_data_misses_rate = rrddim_add(st_demand_data_hits_rate, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_demand_data_hits_rate, rd_demand_data_hits_rate, arcstats.demand_data_hits); + rrddim_set_by_pointer(st_demand_data_hits_rate, rd_demand_data_misses_rate, arcstats.demand_data_misses); + rrdset_done(st_demand_data_hits_rate); } // -------------------------------------------------------------------- @@ -687,12 +853,38 @@ void generate_charts_arc_summary(const char *plugin, const char *module, int sho rd_prefetch_data_hits = rrddim_add(st_prefetch_data_hits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rd_prefetch_data_misses = rrddim_add(st_prefetch_data_hits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } - else - rrdset_next(st_prefetch_data_hits); rrddim_set_by_pointer(st_prefetch_data_hits, rd_prefetch_data_hits, arcstats.prefetch_data_hits); rrddim_set_by_pointer(st_prefetch_data_hits, rd_prefetch_data_misses, arcstats.prefetch_data_misses); rrdset_done(st_prefetch_data_hits); + + static RRDSET *st_prefetch_data_hits_rate = NULL; + static RRDDIM *rd_prefetch_data_hits_rate = NULL; + static RRDDIM *rd_prefetch_data_misses_rate = NULL; + + if (unlikely(!st_prefetch_data_hits_rate)) { + st_prefetch_data_hits_rate = rrdset_create_localhost( + "zfs" + , "prefetch_data_hits_rate" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Data Prefetch Efficiency Rate" + , "events/s" + , plugin + , module + , NETDATA_CHART_PRIO_ZFS_PREFETCH_DATA_HITS + 1 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_prefetch_data_hits_rate = rrddim_add(st_prefetch_data_hits_rate, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_prefetch_data_misses_rate = rrddim_add(st_prefetch_data_hits_rate, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_prefetch_data_hits_rate, rd_prefetch_data_hits_rate, arcstats.prefetch_data_hits); + rrddim_set_by_pointer(st_prefetch_data_hits_rate, rd_prefetch_data_misses_rate, arcstats.prefetch_data_misses); + rrdset_done(st_prefetch_data_hits_rate); } // -------------------------------------------------------------------- @@ -723,8 +915,6 @@ void generate_charts_arc_summary(const char *plugin, const char *module, int sho rd_hash_elements_current = rrddim_add(st_hash_elements, "current", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_hash_elements_max = rrddim_add(st_hash_elements, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_hash_elements); rrddim_set_by_pointer(st_hash_elements, rd_hash_elements_current, arcstats.hash_elements); rrddim_set_by_pointer(st_hash_elements, rd_hash_elements_max, arcstats.hash_elements_max); @@ -759,8 +949,6 @@ void generate_charts_arc_summary(const char *plugin, const char *module, int sho rd_hash_chains_current = rrddim_add(st_hash_chains, "current", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_hash_chains_max = rrddim_add(st_hash_chains, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_hash_chains); rrddim_set_by_pointer(st_hash_chains, rd_hash_chains_current, arcstats.hash_chains); rrddim_set_by_pointer(st_hash_chains, rd_hash_chains_max, arcstats.hash_chain_max); @@ -769,4 +957,4 @@ void generate_charts_arc_summary(const char *plugin, const char *module, int sho // -------------------------------------------------------------------- -} \ No newline at end of file +} diff --git a/collectors/python.d.plugin/Makefile.am b/collectors/python.d.plugin/Makefile.am index 9377ebe8d..1bbbf8ca0 100644 --- a/collectors/python.d.plugin/Makefile.am +++ b/collectors/python.d.plugin/Makefile.am @@ -66,14 +66,13 @@ include megacli/Makefile.inc include memcached/Makefile.inc include mongodb/Makefile.inc include monit/Makefile.inc -include nginx_plus/Makefile.inc include nvidia_smi/Makefile.inc include nsd/Makefile.inc include ntpd/Makefile.inc include openldap/Makefile.inc include oracledb/Makefile.inc +include pandas/Makefile.inc include postfix/Makefile.inc -include postgres/Makefile.inc include proxysql/Makefile.inc include puppet/Makefile.inc include rabbitmq/Makefile.inc diff --git a/collectors/python.d.plugin/alarms/README.md b/collectors/python.d.plugin/alarms/README.md index ee1e59971..8dc666f5b 100644 --- a/collectors/python.d.plugin/alarms/README.md +++ b/collectors/python.d.plugin/alarms/README.md @@ -58,6 +58,9 @@ local: # a "," separated list of words you want to filter alarm names for. For example 'cpu,load' would filter for only # alarms with "cpu" or "load" in alarm name. Default includes all. alarm_contains_words: '' + # a "," separated list of words you want to exclude based on alarm name. For example 'cpu,load' would exclude + # all alarms with "cpu" or "load" in alarm name. Default excludes None. + alarm_excludes_words: '' ``` It will default to pulling all alarms at each time step from the Netdata rest api at `http://127.0.0.1:19999/api/v1/alarms?all` diff --git a/collectors/python.d.plugin/alarms/alarms.chart.py b/collectors/python.d.plugin/alarms/alarms.chart.py index 314b0e7a8..d19427358 100644 --- a/collectors/python.d.plugin/alarms/alarms.chart.py +++ b/collectors/python.d.plugin/alarms/alarms.chart.py @@ -39,6 +39,7 @@ DEFAULT_URL = 'http://127.0.0.1:19999/api/v1/alarms?all' DEFAULT_COLLECT_ALARM_VALUES = False DEFAULT_ALARM_STATUS_CHART_TYPE = 'line' DEFAULT_ALARM_CONTAINS_WORDS = '' +DEFAULT_ALARM_EXCLUDES_WORDS = '' class Service(UrlService): def __init__(self, configuration=None, name=None): @@ -51,6 +52,8 @@ class Service(UrlService): self.collected_dims = {'alarms': set(), 'values': set()} self.alarm_contains_words = self.configuration.get('alarm_contains_words', DEFAULT_ALARM_CONTAINS_WORDS) self.alarm_contains_words_list = [alarm_contains_word.lstrip(' ').rstrip(' ') for alarm_contains_word in self.alarm_contains_words.split(',')] + self.alarm_excludes_words = self.configuration.get('alarm_excludes_words', DEFAULT_ALARM_EXCLUDES_WORDS) + self.alarm_excludes_words_list = [alarm_excludes_word.lstrip(' ').rstrip(' ') for alarm_excludes_word in self.alarm_excludes_words.split(',')] def _get_data(self): raw_data = self._get_raw_data() @@ -62,6 +65,9 @@ class Service(UrlService): if self.alarm_contains_words != '': alarms = {alarm_name: alarms[alarm_name] for alarm_name in alarms for alarm_contains_word in self.alarm_contains_words_list if alarm_contains_word in alarm_name} + if self.alarm_excludes_words != '': + alarms = {alarm_name: alarms[alarm_name] for alarm_name in alarms for alarm_excludes_word in + self.alarm_excludes_words_list if alarm_excludes_word not in alarm_name} data = {a: self.sm[alarms[a]['status']] for a in alarms if alarms[a]['status'] in self.sm} self.update_charts('alarms', data) diff --git a/collectors/python.d.plugin/alarms/alarms.conf b/collectors/python.d.plugin/alarms/alarms.conf index cd48d4411..06d76c3b3 100644 --- a/collectors/python.d.plugin/alarms/alarms.conf +++ b/collectors/python.d.plugin/alarms/alarms.conf @@ -55,3 +55,6 @@ local: # a "," separated list of words you want to filter alarm names for. For example 'cpu,load' would filter for only # alarms with "cpu" or "load" in alarm name. Default includes all. alarm_contains_words: '' + # a "," separated list of words you want to exclude based on alarm name. For example 'cpu,load' would exclude + # all alarms with "cpu" or "load" in alarm name. Default excludes None. + alarm_excludes_words: '' diff --git a/collectors/python.d.plugin/nginx_plus/Makefile.inc b/collectors/python.d.plugin/nginx_plus/Makefile.inc deleted file mode 100644 index d3fdeaf2b..000000000 --- a/collectors/python.d.plugin/nginx_plus/Makefile.inc +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -# THIS IS NOT A COMPLETE Makefile -# IT IS INCLUDED BY ITS PARENT'S Makefile.am -# IT IS REQUIRED TO REFERENCE ALL FILES RELATIVE TO THE PARENT - -# install these files -dist_python_DATA += nginx_plus/nginx_plus.chart.py -dist_pythonconfig_DATA += nginx_plus/nginx_plus.conf - -# do not install these files, but include them in the distribution -dist_noinst_DATA += nginx_plus/README.md nginx_plus/Makefile.inc - diff --git a/collectors/python.d.plugin/nginx_plus/README.md b/collectors/python.d.plugin/nginx_plus/README.md deleted file mode 100644 index 489ac9c2a..000000000 --- a/collectors/python.d.plugin/nginx_plus/README.md +++ /dev/null @@ -1,165 +0,0 @@ - - -# NGINX Plus monitoring with Netdata - -Monitors one or more NGINX Plus servers depending on configuration. Servers can be either local or remote. - -Example nginx_plus configuration can be found in 'python.d/nginx_plus.conf' - -It produces following charts: - -1. **Requests total** in requests/s - - - total - -2. **Requests current** in requests - - - current - -3. **Connection Statistics** in connections/s - - - accepted - - dropped - -4. **Workers Statistics** in workers - - - idle - - active - -5. **SSL Handshakes** in handshakes/s - - - successful - - failed - -6. **SSL Session Reuses** in sessions/s - - - reused - -7. **SSL Memory Usage** in percent - - - usage - -8. **Processes** in processes - - - respawned - -For every server zone: - -1. **Processing** in requests - -- processing - -2. **Requests** in requests/s - - - requests - -3. **Responses** in requests/s - - - 1xx - - 2xx - - 3xx - - 4xx - - 5xx - -4. **Traffic** in kilobits/s - - - received - - sent - -For every upstream: - -1. **Peers Requests** in requests/s - - - peer name (dimension per peer) - -2. **All Peers Responses** in responses/s - - - 1xx - - 2xx - - 3xx - - 4xx - - 5xx - -3. **Peer Responses** in requests/s (for every peer) - - - 1xx - - 2xx - - 3xx - - 4xx - - 5xx - -4. **Peers Connections** in active - - - peer name (dimension per peer) - -5. **Peers Connections Usage** in percent - - - peer name (dimension per peer) - -6. **All Peers Traffic** in KB - - - received - - sent - -7. **Peer Traffic** in KB/s (for every peer) - - - received - - sent - -8. **Peer Timings** in ms (for every peer) - - - header - - response - -9. **Memory Usage** in percent - - - usage - -10. **Peers Status** in state - - - peer name (dimension per peer) - -11. **Peers Total Downtime** in seconds - - - peer name (dimension per peer) - -For every cache: - -1. **Traffic** in KB - - - served - - written - - bypass - -2. **Memory Usage** in percent - - - usage - -## Configuration - -Edit the `python.d/nginx_plus.conf` configuration file using `edit-config` from the Netdata [config -directory](/docs/configure/nodes.md), which is typically at `/etc/netdata`. - -```bash -cd /etc/netdata # Replace this path with your Netdata config directory, if different -sudo ./edit-config python.d/nginx_plus.conf -``` - -Needs only `url` to server's `status`. - -Here is an example for a local server: - -```yaml -local: - url : 'http://localhost/status' -``` - -Without configuration, module fail to start. - ---- - - diff --git a/collectors/python.d.plugin/nginx_plus/nginx_plus.chart.py b/collectors/python.d.plugin/nginx_plus/nginx_plus.chart.py deleted file mode 100644 index a6c035f68..000000000 --- a/collectors/python.d.plugin/nginx_plus/nginx_plus.chart.py +++ /dev/null @@ -1,487 +0,0 @@ -# -*- coding: utf-8 -*- -# Description: nginx_plus netdata python.d module -# Author: Ilya Mashchenko (ilyam8) -# SPDX-License-Identifier: GPL-3.0-or-later - -import re - -from collections import defaultdict -from copy import deepcopy -from json import loads - -try: - from collections import OrderedDict -except ImportError: - from third_party.ordereddict import OrderedDict - -from bases.FrameworkServices.UrlService import UrlService - -ORDER = [ - 'requests_total', - 'requests_current', - 'connections_statistics', - 'connections_workers', - 'ssl_handshakes', - 'ssl_session_reuses', - 'ssl_memory_usage', - 'processes' -] - -CHARTS = { - 'requests_total': { - 'options': [None, 'Requests Total', 'requests/s', 'requests', 'nginx_plus.requests_total', 'line'], - 'lines': [ - ['requests_total', 'total', 'incremental'] - ] - }, - 'requests_current': { - 'options': [None, 'Requests Current', 'requests', 'requests', 'nginx_plus.requests_current', 'line'], - 'lines': [ - ['requests_current', 'current'] - ] - }, - 'connections_statistics': { - 'options': [None, 'Connections Statistics', 'connections/s', - 'connections', 'nginx_plus.connections_statistics', 'stacked'], - 'lines': [ - ['connections_accepted', 'accepted', 'incremental'], - ['connections_dropped', 'dropped', 'incremental'] - ] - }, - 'connections_workers': { - 'options': [None, 'Workers Statistics', 'workers', - 'connections', 'nginx_plus.connections_workers', 'stacked'], - 'lines': [ - ['connections_idle', 'idle'], - ['connections_active', 'active'] - ] - }, - 'ssl_handshakes': { - 'options': [None, 'SSL Handshakes', 'handshakes/s', 'ssl', 'nginx_plus.ssl_handshakes', 'stacked'], - 'lines': [ - ['ssl_handshakes', 'successful', 'incremental'], - ['ssl_handshakes_failed', 'failed', 'incremental'] - ] - }, - 'ssl_session_reuses': { - 'options': [None, 'Session Reuses', 'sessions/s', 'ssl', 'nginx_plus.ssl_session_reuses', 'line'], - 'lines': [ - ['ssl_session_reuses', 'reused', 'incremental'] - ] - }, - 'ssl_memory_usage': { - 'options': [None, 'Memory Usage', 'percentage', 'ssl', 'nginx_plus.ssl_memory_usage', 'area'], - 'lines': [ - ['ssl_memory_usage', 'usage', 'absolute', 1, 100] - ] - }, - 'processes': { - 'options': [None, 'Processes', 'processes', 'processes', 'nginx_plus.processes', 'line'], - 'lines': [ - ['processes_respawned', 'respawned'] - ] - } -} - - -def cache_charts(cache): - family = 'cache {0}'.format(cache.real_name) - charts = OrderedDict() - - charts['{0}_traffic'.format(cache.name)] = { - 'options': [None, 'Traffic', 'KiB', family, 'nginx_plus.cache_traffic', 'stacked'], - 'lines': [ - ['_'.join([cache.name, 'hit_bytes']), 'served', 'absolute', 1, 1024], - ['_'.join([cache.name, 'miss_bytes_written']), 'written', 'absolute', 1, 1024], - ['_'.join([cache.name, 'miss_bytes']), 'bypass', 'absolute', 1, 1024] - ] - } - charts['{0}_memory_usage'.format(cache.name)] = { - 'options': [None, 'Memory Usage', 'percentage', family, 'nginx_plus.cache_memory_usage', 'area'], - 'lines': [ - ['_'.join([cache.name, 'memory_usage']), 'usage', 'absolute', 1, 100], - ] - } - return charts - - -def web_zone_charts(wz): - charts = OrderedDict() - family = 'web zone {name}'.format(name=wz.real_name) - - # Processing - charts['zone_{name}_processing'.format(name=wz.name)] = { - 'options': [None, 'Zone "{name}" Processing'.format(name=wz.name), 'requests', family, - 'nginx_plus.web_zone_processing', 'line'], - 'lines': [ - ['_'.join([wz.name, 'processing']), 'processing'] - ] - } - # Requests - charts['zone_{name}_requests'.format(name=wz.name)] = { - 'options': [None, 'Zone "{name}" Requests'.format(name=wz.name), 'requests/s', family, - 'nginx_plus.web_zone_requests', 'line'], - 'lines': [ - ['_'.join([wz.name, 'requests']), 'requests', 'incremental'] - ] - } - # Response Codes - charts['zone_{name}_responses'.format(name=wz.name)] = { - 'options': [None, 'Zone "{name}" Responses'.format(name=wz.name), 'requests/s', family, - 'nginx_plus.web_zone_responses', 'stacked'], - 'lines': [ - ['_'.join([wz.name, 'responses_2xx']), '2xx', 'incremental'], - ['_'.join([wz.name, 'responses_5xx']), '5xx', 'incremental'], - ['_'.join([wz.name, 'responses_3xx']), '3xx', 'incremental'], - ['_'.join([wz.name, 'responses_4xx']), '4xx', 'incremental'], - ['_'.join([wz.name, 'responses_1xx']), '1xx', 'incremental'] - ] - } - # Traffic - charts['zone_{name}_net'.format(name=wz.name)] = { - 'options': [None, 'Zone "{name}" Traffic'.format(name=wz.name), 'kilobits/s', family, - 'nginx_plus.zone_net', 'area'], - 'lines': [ - ['_'.join([wz.name, 'received']), 'received', 'incremental', 1, 1000], - ['_'.join([wz.name, 'sent']), 'sent', 'incremental', -1, 1000] - ] - } - return charts - - -def web_upstream_charts(wu): - def dimensions(value, a='absolute', m=1, d=1): - dims = list() - for p in wu: - dims.append(['_'.join([wu.name, p.server, value]), p.real_server, a, m, d]) - return dims - - charts = OrderedDict() - family = 'web upstream {name}'.format(name=wu.real_name) - - # Requests - charts['web_upstream_{name}_requests'.format(name=wu.name)] = { - 'options': [None, 'Peers Requests', 'requests/s', family, 'nginx_plus.web_upstream_requests', 'line'], - 'lines': dimensions('requests', 'incremental') - } - # Responses Codes - charts['web_upstream_{name}_all_responses'.format(name=wu.name)] = { - 'options': [None, 'All Peers Responses', 'responses/s', family, - 'nginx_plus.web_upstream_all_responses', 'stacked'], - 'lines': [ - ['_'.join([wu.name, 'responses_2xx']), '2xx', 'incremental'], - ['_'.join([wu.name, 'responses_5xx']), '5xx', 'incremental'], - ['_'.join([wu.name, 'responses_3xx']), '3xx', 'incremental'], - ['_'.join([wu.name, 'responses_4xx']), '4xx', 'incremental'], - ['_'.join([wu.name, 'responses_1xx']), '1xx', 'incremental'], - ] - } - for peer in wu: - charts['web_upstream_{0}_{1}_responses'.format(wu.name, peer.server)] = { - 'options': [None, 'Peer "{0}" Responses'.format(peer.real_server), 'responses/s', family, - 'nginx_plus.web_upstream_peer_responses', 'stacked'], - 'lines': [ - ['_'.join([wu.name, peer.server, 'responses_2xx']), '2xx', 'incremental'], - ['_'.join([wu.name, peer.server, 'responses_5xx']), '5xx', 'incremental'], - ['_'.join([wu.name, peer.server, 'responses_3xx']), '3xx', 'incremental'], - ['_'.join([wu.name, peer.server, 'responses_4xx']), '4xx', 'incremental'], - ['_'.join([wu.name, peer.server, 'responses_1xx']), '1xx', 'incremental'] - ] - } - # Connections - charts['web_upstream_{name}_connections'.format(name=wu.name)] = { - 'options': [None, 'Peers Connections', 'active', family, 'nginx_plus.web_upstream_connections', 'line'], - 'lines': dimensions('active') - } - charts['web_upstream_{name}_connections_usage'.format(name=wu.name)] = { - 'options': [None, 'Peers Connections Usage', 'percentage', family, - 'nginx_plus.web_upstream_connections_usage', 'line'], - 'lines': dimensions('connections_usage', d=100) - } - # Traffic - charts['web_upstream_{0}_all_net'.format(wu.name)] = { - 'options': [None, 'All Peers Traffic', 'kilobits/s', family, 'nginx_plus.web_upstream_all_net', 'area'], - 'lines': [ - ['{0}_received'.format(wu.name), 'received', 'incremental', 1, 1000], - ['{0}_sent'.format(wu.name), 'sent', 'incremental', -1, 1000] - ] - } - for peer in wu: - charts['web_upstream_{0}_{1}_net'.format(wu.name, peer.server)] = { - 'options': [None, 'Peer "{0}" Traffic'.format(peer.real_server), 'kilobits/s', family, - 'nginx_plus.web_upstream_peer_traffic', 'area'], - 'lines': [ - ['{0}_{1}_received'.format(wu.name, peer.server), 'received', 'incremental', 1, 1000], - ['{0}_{1}_sent'.format(wu.name, peer.server), 'sent', 'incremental', -1, 1000] - ] - } - # Response Time - for peer in wu: - charts['web_upstream_{0}_{1}_timings'.format(wu.name, peer.server)] = { - 'options': [None, 'Peer "{0}" Timings'.format(peer.real_server), 'milliseconds', family, - 'nginx_plus.web_upstream_peer_timings', 'line'], - 'lines': [ - ['_'.join([wu.name, peer.server, 'header_time']), 'header'], - ['_'.join([wu.name, peer.server, 'response_time']), 'response'] - ] - } - # Memory Usage - charts['web_upstream_{name}_memory_usage'.format(name=wu.name)] = { - 'options': [None, 'Memory Usage', 'percentage', family, 'nginx_plus.web_upstream_memory_usage', 'area'], - 'lines': [ - ['_'.join([wu.name, 'memory_usage']), 'usage', 'absolute', 1, 100] - ] - } - # State - charts['web_upstream_{name}_status'.format(name=wu.name)] = { - 'options': [None, 'Peers Status', 'state', family, 'nginx_plus.web_upstream_status', 'line'], - 'lines': dimensions('state') - } - # Downtime - charts['web_upstream_{name}_downtime'.format(name=wu.name)] = { - 'options': [None, 'Peers Downtime', 'seconds', family, 'nginx_plus.web_upstream_peer_downtime', 'line'], - 'lines': dimensions('downtime', d=1000) - } - - return charts - - -METRICS = { - 'SERVER': [ - 'processes.respawned', - 'connections.accepted', - 'connections.dropped', - 'connections.active', - 'connections.idle', - 'ssl.handshakes', - 'ssl.handshakes_failed', - 'ssl.session_reuses', - 'requests.total', - 'requests.current', - 'slabs.SSL.pages.free', - 'slabs.SSL.pages.used' - ], - 'WEB_ZONE': [ - 'processing', - 'requests', - 'responses.1xx', - 'responses.2xx', - 'responses.3xx', - 'responses.4xx', - 'responses.5xx', - 'discarded', - 'received', - 'sent' - ], - 'WEB_UPSTREAM_PEER': [ - 'id', - 'server', - 'name', - 'state', - 'active', - 'max_conns', - 'requests', - 'header_time', # alive only - 'response_time', # alive only - 'responses.1xx', - 'responses.2xx', - 'responses.3xx', - 'responses.4xx', - 'responses.5xx', - 'sent', - 'received', - 'downtime' - ], - 'WEB_UPSTREAM_SUMMARY': [ - 'responses.1xx', - 'responses.2xx', - 'responses.3xx', - 'responses.4xx', - 'responses.5xx', - 'sent', - 'received' - ], - 'CACHE': [ - 'hit.bytes', # served - 'miss.bytes_written', # written - 'miss.bytes' # bypass - - ] -} - -BAD_SYMBOLS = re.compile(r'[:/.-]+') - - -class Cache: - key = 'caches' - charts = cache_charts - - def __init__(self, **kw): - self.real_name = kw['name'] - self.name = BAD_SYMBOLS.sub('_', self.real_name) - - def memory_usage(self, data): - used = data['slabs'][self.real_name]['pages']['used'] - free = data['slabs'][self.real_name]['pages']['free'] - return used / float(free + used) * 1e4 - - def get_data(self, raw_data): - zone_data = raw_data['caches'][self.real_name] - data = parse_json(zone_data, METRICS['CACHE']) - data['memory_usage'] = self.memory_usage(raw_data) - return dict(('_'.join([self.name, k]), v) for k, v in data.items()) - - -class WebZone: - key = 'server_zones' - charts = web_zone_charts - - def __init__(self, **kw): - self.real_name = kw['name'] - self.name = BAD_SYMBOLS.sub('_', self.real_name) - - def get_data(self, raw_data): - zone_data = raw_data['server_zones'][self.real_name] - data = parse_json(zone_data, METRICS['WEB_ZONE']) - return dict(('_'.join([self.name, k]), v) for k, v in data.items()) - - -class WebUpstream: - key = 'upstreams' - charts = web_upstream_charts - - def __init__(self, **kw): - self.real_name = kw['name'] - self.name = BAD_SYMBOLS.sub('_', self.real_name) - self.peers = OrderedDict() - - peers = kw['response']['upstreams'][self.real_name]['peers'] - for peer in peers: - self.add_peer(peer['id'], peer['server']) - - def __iter__(self): - return iter(self.peers.values()) - - def add_peer(self, idx, server): - peer = WebUpstreamPeer(idx, server) - self.peers[peer.real_server] = peer - return peer - - def peers_stats(self, peers): - peers = {int(peer['id']): peer for peer in peers} - data = dict() - for peer in self.peers.values(): - if not peer.active: - continue - try: - data.update(peer.get_data(peers[peer.id])) - except KeyError: - peer.active = False - return data - - def memory_usage(self, data): - used = data['slabs'][self.real_name]['pages']['used'] - free = data['slabs'][self.real_name]['pages']['free'] - return used / float(free + used) * 1e4 - - def summary_stats(self, data): - rv = defaultdict(int) - for metric in METRICS['WEB_UPSTREAM_SUMMARY']: - for peer in self.peers.values(): - if peer.active: - metric = '_'.join(metric.split('.')) - rv[metric] += data['_'.join([peer.server, metric])] - return rv - - def get_data(self, raw_data): - data = dict() - peers = raw_data['upstreams'][self.real_name]['peers'] - data.update(self.peers_stats(peers)) - data.update(self.summary_stats(data)) - data['memory_usage'] = self.memory_usage(raw_data) - return dict(('_'.join([self.name, k]), v) for k, v in data.items()) - - -class WebUpstreamPeer: - def __init__(self, idx, server): - self.id = idx - self.real_server = server - self.server = BAD_SYMBOLS.sub('_', self.real_server) - self.active = True - - def get_data(self, raw): - data = dict(header_time=0, response_time=0, max_conns=0) - data.update(parse_json(raw, METRICS['WEB_UPSTREAM_PEER'])) - data['connections_usage'] = 0 if not data['max_conns'] else data['active'] / float(data['max_conns']) * 1e4 - data['state'] = int(data['state'] == 'up') - return dict(('_'.join([self.server, k]), v) for k, v in data.items()) - - -class Service(UrlService): - def __init__(self, configuration=None, name=None): - UrlService.__init__(self, configuration=configuration, name=name) - self.order = list(ORDER) - self.definitions = deepcopy(CHARTS) - self.objects = dict() - - def check(self): - if not self.url: - self.error('URL is not defined') - return None - - self._manager = self._build_manager() - if not self._manager: - return None - - raw_data = self._get_raw_data() - if not raw_data: - return None - - try: - response = loads(raw_data) - except ValueError: - return None - - for obj_cls in [WebZone, WebUpstream, Cache]: - for obj_name in response.get(obj_cls.key, list()): - obj = obj_cls(name=obj_name, response=response) - self.objects[obj.real_name] = obj - charts = obj_cls.charts(obj) - for chart in charts: - self.order.append(chart) - self.definitions[chart] = charts[chart] - - return bool(self.objects) - - def _get_data(self): - """ - Format data received from http request - :return: dict - """ - raw_data = self._get_raw_data() - if not raw_data: - return None - response = loads(raw_data) - - data = parse_json(response, METRICS['SERVER']) - data['ssl_memory_usage'] = data['slabs_SSL_pages_used'] / float(data['slabs_SSL_pages_free']) * 1e4 - - for obj in self.objects.values(): - if obj.real_name in response[obj.key]: - data.update(obj.get_data(response)) - - return data - - -def parse_json(raw_data, metrics): - data = dict() - for metric in metrics: - value = raw_data - metrics_list = metric.split('.') - try: - for m in metrics_list: - value = value[m] - except KeyError: - continue - data['_'.join(metrics_list)] = value - return data diff --git a/collectors/python.d.plugin/nginx_plus/nginx_plus.conf b/collectors/python.d.plugin/nginx_plus/nginx_plus.conf deleted file mode 100644 index 201eb0eb7..000000000 --- a/collectors/python.d.plugin/nginx_plus/nginx_plus.conf +++ /dev/null @@ -1,85 +0,0 @@ -# netdata python.d.plugin configuration for nginx_plus -# -# This file is in YaML format. Generally the format is: -# -# name: value -# -# There are 2 sections: -# - global variables -# - one or more JOBS -# -# JOBS allow you to collect values from multiple sources. -# Each source will have its own set of charts. -# -# JOB parameters have to be indented (using spaces only, example below). - -# ---------------------------------------------------------------------- -# Global Variables -# These variables set the defaults for all JOBs, however each JOB -# may define its own, overriding the defaults. - -# update_every sets the default data collection frequency. -# If unset, the python.d.plugin default is used. -# update_every: 1 - -# priority controls the order of charts at the netdata dashboard. -# Lower numbers move the charts towards the top of the page. -# If unset, the default for python.d.plugin is used. -# priority: 60000 - -# penalty indicates whether to apply penalty to update_every in case of failures. -# Penalty will increase every 5 failed updates in a row. Maximum penalty is 10 minutes. -# penalty: yes - -# autodetection_retry sets the job re-check interval in seconds. -# The job is not deleted if check fails. -# Attempts to start the job are made once every autodetection_retry. -# This feature is disabled by default. -# autodetection_retry: 0 - -# ---------------------------------------------------------------------- -# JOBS (data collection sources) -# -# The default JOBS share the same *name*. JOBS with the same name -# are mutually exclusive. Only one of them will be allowed running at -# any time. This allows autodetection to try several alternatives and -# pick the one that works. -# -# Any number of jobs is supported. -# -# All python.d.plugin JOBS (for all its modules) support a set of -# predefined parameters. These are: -# -# job_name: -# name: myname # the JOB's name as it will appear at the -# # dashboard (by default is the job_name) -# # JOBs sharing a name are mutually exclusive -# update_every: 1 # the JOB's data collection frequency -# priority: 60000 # the JOB's order on the dashboard -# penalty: yes # the JOB's penalty -# autodetection_retry: 0 # the JOB's re-check interval in seconds -# -# Additionally to the above, nginx_plus also supports the following: -# -# url: 'URL' # the URL to fetch nginx_plus's stats -# -# if the URL is password protected, the following are supported: -# -# user: 'username' -# pass: 'password' - -# ---------------------------------------------------------------------- -# AUTO-DETECTION JOBS -# only one of them will run (they have the same name) - -localhost: - name : 'local' - url : 'http://localhost/status' - -localipv4: - name : 'local' - url : 'http://127.0.0.1/status' - -localipv6: - name : 'local' - url : 'http://[::1]/status' diff --git a/collectors/python.d.plugin/nvidia_smi/README.md b/collectors/python.d.plugin/nvidia_smi/README.md index fb613064c..bb4169441 100644 --- a/collectors/python.d.plugin/nvidia_smi/README.md +++ b/collectors/python.d.plugin/nvidia_smi/README.md @@ -8,6 +8,8 @@ sidebar_label: "Nvidia GPUs" Monitors performance metrics (memory usage, fan speed, pcie bandwidth utilization, temperature, etc.) using `nvidia-smi` cli tool. +> **Warning**: this collector does not work when the Netdata Agent is [running in a container](https://learn.netdata.cloud/docs/agent/packaging/docker). + ## Requirements and Notes diff --git a/collectors/python.d.plugin/nvidia_smi/nvidia_smi.chart.py b/collectors/python.d.plugin/nvidia_smi/nvidia_smi.chart.py index 1913e94e4..23e90e658 100644 --- a/collectors/python.d.plugin/nvidia_smi/nvidia_smi.chart.py +++ b/collectors/python.d.plugin/nvidia_smi/nvidia_smi.chart.py @@ -4,11 +4,10 @@ # Author: Ilya Mashchenko (ilyam8) # User Memory Stat Author: Guido Scatena (scatenag) -import subprocess -import threading import os import pwd - +import subprocess +import threading import xml.etree.ElementTree as et from bases.FrameworkServices.SimpleService import SimpleService @@ -32,6 +31,7 @@ BAR_USAGE = 'bar1_mem_usage' TEMPERATURE = 'temperature' CLOCKS = 'clocks' POWER = 'power' +POWER_STATE = 'power_state' PROCESSES_MEM = 'processes_mem' USER_MEM = 'user_mem' USER_NUM = 'user_num' @@ -47,11 +47,15 @@ ORDER = [ TEMPERATURE, CLOCKS, POWER, + POWER_STATE, PROCESSES_MEM, USER_MEM, USER_NUM, ] +# https://docs.nvidia.com/gameworks/content/gameworkslibrary/coresdk/nvapi/group__gpupstate.html +POWER_STATES = ['P' + str(i) for i in range(0, 16)] + def gpu_charts(gpu): fam = gpu.full_name() @@ -125,6 +129,10 @@ def gpu_charts(gpu): ['power_draw', 'power', 'absolute', 1, 100], ] }, + POWER_STATE: { + 'options': [None, 'Power State', 'state', fam, 'nvidia_smi.power_state', 'line'], + 'lines': [['power_state_' + v.lower(), v, 'absolute'] for v in POWER_STATES] + }, PROCESSES_MEM: { 'options': [None, 'Memory Used by Each Process', 'MiB', fam, 'nvidia_smi.processes_mem', 'stacked'], 'lines': [] @@ -382,6 +390,10 @@ class GPU: def mem_clock(self): return self.root.find('clocks').find('mem_clock').text.split()[0] + @handle_attr_error + def power_state(self): + return str(self.root.find('power_readings').find('power_state').text.split()[0]) + @handle_value_error @handle_attr_error def power_draw(self): @@ -426,6 +438,13 @@ class GPU: 'mem_clock': self.mem_clock(), 'power_draw': self.power_draw(), } + + for v in POWER_STATES: + data['power_state_' + v.lower()] = 0 + p_state = self.power_state() + if p_state: + data['power_state_' + p_state.lower()] = 1 + processes = self.processes() or [] users = set() for p in processes: @@ -450,7 +469,7 @@ class Service(SimpleService): self.order = list() self.definitions = dict() self.loop_mode = configuration.get('loop_mode', True) - poll = int(configuration.get('poll_seconds', 1)) + poll = int(configuration.get('poll_seconds', self.get_update_every())) self.exclude_zero_memory_users = configuration.get('exclude_zero_memory_users', False) self.poller = NvidiaSMIPoller(poll) diff --git a/collectors/python.d.plugin/pandas/Makefile.inc b/collectors/python.d.plugin/pandas/Makefile.inc new file mode 100644 index 000000000..9f4f9b34b --- /dev/null +++ b/collectors/python.d.plugin/pandas/Makefile.inc @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +# THIS IS NOT A COMPLETE Makefile +# IT IS INCLUDED BY ITS PARENT'S Makefile.am +# IT IS REQUIRED TO REFERENCE ALL FILES RELATIVE TO THE PARENT + +# install these files +dist_python_DATA += pandas/pandas.chart.py +dist_pythonconfig_DATA += pandas/pandas.conf + +# do not install these files, but include them in the distribution +dist_noinst_DATA += pandas/README.md pandas/Makefile.inc + diff --git a/collectors/python.d.plugin/pandas/README.md b/collectors/python.d.plugin/pandas/README.md new file mode 100644 index 000000000..141549478 --- /dev/null +++ b/collectors/python.d.plugin/pandas/README.md @@ -0,0 +1,92 @@ + + +# Pandas Netdata Collector + + + Pandas + + +A python collector using [pandas](https://pandas.pydata.org/) to pull data and do pandas based +preprocessing before feeding to Netdata. + +## Requirements + +This collector depends on some Python (Python 3 only) packages that can usually be installed via `pip` or `pip3`. + +```bash +sudo pip install pandas requests +``` + +## Configuration + +Below is an example configuration to query some json weather data from [Open-Meteo](https://open-meteo.com), +do some data wrangling on it and save in format as expected by Netdata. + +```yaml +# example pulling some hourly temperature data +temperature: + name: "temperature" + update_every: 3 + chart_configs: + - name: "temperature_by_city" + title: "Temperature By City" + family: "temperature.today" + context: "pandas.temperature" + type: "line" + units: "Celsius" + df_steps: > + pd.DataFrame.from_dict( + {city: requests.get( + f'https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lng}&hourly=temperature_2m' + ).json()['hourly']['temperature_2m'] + for (city,lat,lng) + in [ + ('dublin', 53.3441, -6.2675), + ('athens', 37.9792, 23.7166), + ('london', 51.5002, -0.1262), + ('berlin', 52.5235, 13.4115), + ('paris', 48.8567, 2.3510), + ] + } + ); # use dictionary comprehension to make multiple requests; + df.describe(); # get aggregate stats for each city; + df.transpose()[['mean', 'max', 'min']].reset_index(); # just take mean, min, max; + df.rename(columns={'index':'city'}); # some column renaming; + df.pivot(columns='city').mean().to_frame().reset_index(); # force to be one row per city; + df.rename(columns={0:'degrees'}); # some column renaming; + pd.concat([df, df['city']+'_'+df['level_0']], axis=1); # add new column combining city and summary measurement label; + df.rename(columns={0:'measurement'}); # some column renaming; + df[['measurement', 'degrees']].set_index('measurement'); # just take two columns we want; + df.sort_index(); # sort by city name; + df.transpose(); # transpose so its just one wide row; +``` + +`chart_configs` is a list of dictionary objects where each one defines the sequence of `df_steps` to be run using [`pandas`](https://pandas.pydata.org/), +and the `name`, `title` etc to define the +[CHART variables](https://learn.netdata.cloud/docs/agent/collectors/python.d.plugin#global-variables-order-and-chart) +that will control how the results will look in netdata. + +The example configuration above would result in a `data` dictionary like the below being collected by Netdata +at each time step. They keys in this dictionary will be the +[dimension](https://learn.netdata.cloud/docs/agent/web#dimensions) names on the chart. + +```javascript +{'athens_max': 26.2, 'athens_mean': 19.45952380952381, 'athens_min': 12.2, 'berlin_max': 17.4, 'berlin_mean': 10.764285714285714, 'berlin_min': 5.7, 'dublin_max': 15.3, 'dublin_mean': 12.008928571428571, 'dublin_min': 6.6, 'london_max': 18.9, 'london_mean': 12.510714285714286, 'london_min': 5.2, 'paris_max': 19.4, 'paris_mean': 12.054166666666665, 'paris_min': 4.8} +``` + +Which, given the above configuration would end up as a chart like below in Netdata. + +![pandas collector temperature example chart](https://user-images.githubusercontent.com/2178292/195075312-8ce8cf68-5172-48e3-af09-104ffecfcdd6.png) + +## Notes +- Each line in `df_steps` must return a pandas +[DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html) object (`df`) at each step. +- You can use +[this colab notebook](https://colab.research.google.com/drive/1VYrddSegZqGtkWGFuiUbMbUk5f3rW6Hi?usp=sharing) +to mock up and work on your `df_steps` iteratively before adding them to your config. +- This collector is expecting one row in the final pandas DataFrame. It is that first row that will be taken +as the most recent values for each dimension on each chart using (`df.to_dict(orient='records')[0]`). +See [pd.to_dict()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_dict.html). diff --git a/collectors/python.d.plugin/pandas/pandas.chart.py b/collectors/python.d.plugin/pandas/pandas.chart.py new file mode 100644 index 000000000..8eb4452fb --- /dev/null +++ b/collectors/python.d.plugin/pandas/pandas.chart.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# Description: pandas netdata python.d module +# Author: Andrew Maguire (andrewm4894) +# SPDX-License-Identifier: GPL-3.0-or-later + +import pandas as pd + +try: + import requests + HAS_REQUESTS = True +except ImportError: + HAS_REQUESTS = False + +from bases.FrameworkServices.SimpleService import SimpleService + +ORDER = [] + +CHARTS = {} + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.order = ORDER + self.definitions = CHARTS + self.chart_configs = self.configuration.get('chart_configs', None) + self.line_sep = self.configuration.get('line_sep', ';') + + def run_code(self, df_steps): + """eval() each line of code and ensure the result is a pandas dataframe""" + + # process each line of code + lines = df_steps.split(self.line_sep) + for line in lines: + line_clean = line.strip('\n').strip(' ') + if line_clean != '' and line_clean[0] != '#': + df = eval(line_clean) + assert isinstance(df, pd.DataFrame), 'The result of each evaluated line of `df_steps` must be of type `pd.DataFrame`' + + # take top row of final df as data to be collected by netdata + data = df.to_dict(orient='records')[0] + + return data + + def check(self): + """ensure charts and dims all configured and that we can get data""" + + if not HAS_REQUESTS: + self.warn('requests library could not be imported') + + if not self.chart_configs: + self.error('chart_configs must be defined') + + data = dict() + + # add each chart as defined by the config + for chart_config in self.chart_configs: + if chart_config['name'] not in self.charts: + chart_template = { + 'options': [ + chart_config['name'], + chart_config['title'], + chart_config['units'], + chart_config['family'], + chart_config['context'], + chart_config['type'] + ], + 'lines': [] + } + self.charts.add_chart([chart_config['name']] + chart_template['options']) + + data_tmp = self.run_code(chart_config['df_steps']) + data.update(data_tmp) + + for dim in data_tmp: + self.charts[chart_config['name']].add_dimension([dim, dim, 'absolute', 1, 1]) + + return True + + def get_data(self): + """get data for each chart config""" + + data = dict() + + for chart_config in self.chart_configs: + data_tmp = self.run_code(chart_config['df_steps']) + data.update(data_tmp) + + return data diff --git a/collectors/python.d.plugin/pandas/pandas.conf b/collectors/python.d.plugin/pandas/pandas.conf new file mode 100644 index 000000000..6684af9d5 --- /dev/null +++ b/collectors/python.d.plugin/pandas/pandas.conf @@ -0,0 +1,191 @@ +# netdata python.d.plugin configuration for pandas +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +update_every: 5 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# penalty indicates whether to apply penalty to update_every in case of failures. +# Penalty will increase every 5 failed updates in a row. Maximum penalty is 10 minutes. +# penalty: yes + +# autodetection_retry sets the job re-check interval in seconds. +# The job is not deleted if check fails. +# Attempts to start the job are made once every autodetection_retry. +# This feature is disabled by default. +# autodetection_retry: 0 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear on the dashboard +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# penalty: yes # the JOB's penalty +# autodetection_retry: 0 # the JOB's re-check interval in seconds +# +# Additionally to the above, example also supports the following: +# +# num_lines: 4 # the number of lines to create +# lower: 0 # the lower bound of numbers to randomly sample from +# upper: 100 # the upper bound of numbers to randomly sample from +# +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS + +# Some example configurations, enable this collector, uncomment and example below and restart netdata to enable. + +# example pulling some hourly temperature data, a chart for today forecast (mean,min,max) and another chart for current. +# temperature: +# name: "temperature" +# update_every: 5 +# chart_configs: +# - name: "temperature_forecast_by_city" +# title: "Temperature By City - Today Forecast" +# family: "temperature.today" +# context: "pandas.temperature" +# type: "line" +# units: "Celsius" +# df_steps: > +# pd.DataFrame.from_dict( +# {city: requests.get(f'https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lng}&hourly=temperature_2m').json()['hourly']['temperature_2m'] +# for (city,lat,lng) +# in [ +# ('dublin', 53.3441, -6.2675), +# ('athens', 37.9792, 23.7166), +# ('london', 51.5002, -0.1262), +# ('berlin', 52.5235, 13.4115), +# ('paris', 48.8567, 2.3510), +# ('madrid', 40.4167, -3.7033), +# ('new_york', 40.71, -74.01), +# ('los_angeles', 34.05, -118.24), +# ] +# } +# ); +# df.describe(); # get aggregate stats for each city; +# df.transpose()[['mean', 'max', 'min']].reset_index(); # just take mean, min, max; +# df.rename(columns={'index':'city'}); # some column renaming; +# df.pivot(columns='city').mean().to_frame().reset_index(); # force to be one row per city; +# df.rename(columns={0:'degrees'}); # some column renaming; +# pd.concat([df, df['city']+'_'+df['level_0']], axis=1); # add new column combining city and summary measurement label; +# df.rename(columns={0:'measurement'}); # some column renaming; +# df[['measurement', 'degrees']].set_index('measurement'); # just take two columns we want; +# df.sort_index(); # sort by city name; +# df.transpose(); # transpose so its just one wide row; +# - name: "temperature_current_by_city" +# title: "Temperature By City - Current" +# family: "temperature.current" +# context: "pandas.temperature" +# type: "line" +# units: "Celsius" +# df_steps: > +# pd.DataFrame.from_dict( +# {city: requests.get(f'https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lng}¤t_weather=true').json()['current_weather'] +# for (city,lat,lng) +# in [ +# ('dublin', 53.3441, -6.2675), +# ('athens', 37.9792, 23.7166), +# ('london', 51.5002, -0.1262), +# ('berlin', 52.5235, 13.4115), +# ('paris', 48.8567, 2.3510), +# ('madrid', 40.4167, -3.7033), +# ('new_york', 40.71, -74.01), +# ('los_angeles', 34.05, -118.24), +# ] +# } +# ); +# df.transpose(); +# df[['temperature']]; +# df.transpose(); + +# example showing a read_csv from a url and some light pandas data wrangling. +# pull data in csv format from london demo server and then ratio of user cpus over system cpu averaged over last 60 seconds. +# example_csv: +# name: "example_csv" +# update_every: 2 +# chart_configs: +# - name: "london_system_cpu" +# title: "London System CPU - Ratios" +# family: "london_system_cpu" +# context: "pandas" +# type: "line" +# units: "n" +# df_steps: > +# pd.read_csv('https://london.my-netdata.io/api/v1/data?chart=system.cpu&format=csv&after=-60', storage_options={'User-Agent': 'netdata'}); +# df.drop('time', axis=1); +# df.mean().to_frame().transpose(); +# df.apply(lambda row: (row.user / row.system), axis = 1).to_frame(); +# df.rename(columns={0:'average_user_system_ratio'}); +# df*100; + +# example showing a read_json from a url and some light pandas data wrangling. +# pull data in json format (using requests.get() if json data is too complex for pd.read_json() ) from london demo server and work out 'total_bandwidth'. +# example_json: +# name: "example_json" +# update_every: 2 +# chart_configs: +# - name: "london_system_net" +# title: "London System Net - Total Bandwidth" +# family: "london_system_net" +# context: "pandas" +# type: "area" +# units: "kilobits/s" +# df_steps: > +# pd.DataFrame(requests.get('https://london.my-netdata.io/api/v1/data?chart=system.net&format=json&after=-1').json()['data'], columns=requests.get('https://london.my-netdata.io/api/v1/data?chart=system.net&format=json&after=-1').json()['labels']); +# df.drop('time', axis=1); +# abs(df); +# df.sum(axis=1).to_frame(); +# df.rename(columns={0:'total_bandwidth'}); + +# example showing a read_xml from a url and some light pandas data wrangling. +# pull weather forecast data in xml format, use xpath to pull out temperature forecast. +# example_xml: +# name: "example_xml" +# update_every: 2 +# line_sep: "|" +# chart_configs: +# - name: "temperature_forcast" +# title: "Temperature Forecast" +# family: "temp" +# context: "pandas.temp" +# type: "line" +# units: "celsius" +# df_steps: > +# pd.read_xml('http://metwdb-openaccess.ichec.ie/metno-wdb2ts/locationforecast?lat=54.7210798611;long=-8.7237392806', xpath='./product/time[1]/location/temperature', parser='etree')| +# df.rename(columns={'value': 'dublin'})| +# df[['dublin']]| \ No newline at end of file diff --git a/collectors/python.d.plugin/postfix/README.md b/collectors/python.d.plugin/postfix/README.md index ac16962a5..1a546c614 100644 --- a/collectors/python.d.plugin/postfix/README.md +++ b/collectors/python.d.plugin/postfix/README.md @@ -6,22 +6,31 @@ sidebar_label: "Postfix" # Postfix monitoring with Netdata -Monitors MTA email queue statistics using postqueue tool. +Monitors MTA email queue statistics using [postqueue](http://www.postfix.org/postqueue.1.html) tool. -Execute `postqueue -p` to grab postfix queue. +The collector executes `postqueue -p` to get Postfix queue statistics. -It produces only two charts: +## Requirements -1. **Postfix Queue Emails** +Postfix has internal access controls that limit activities on the mail queue. By default, all users are allowed to view +the queue. If your system is configured with stricter access controls, you need to grant the `netdata` user access to +view the mail queue. In order to do it, add `netdata` to `authorized_mailq_users` in the `/etc/postfix/main.cf` file. - - emails +See the `authorized_mailq_users` setting in +the [Postfix documentation](https://www.postfix.org/postconf.5.html) for more details. -2. **Postfix Queue Emails Size** in KB +## Charts - - size +It produces only two charts: -Configuration is not needed. +1. **Postfix Queue Emails** ---- + - emails +2. **Postfix Queue Emails Size** in KB + - size + +## Configuration + +Configuration is not needed. diff --git a/collectors/python.d.plugin/postgres/Makefile.inc b/collectors/python.d.plugin/postgres/Makefile.inc deleted file mode 100644 index 91a185cb9..000000000 --- a/collectors/python.d.plugin/postgres/Makefile.inc +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -# THIS IS NOT A COMPLETE Makefile -# IT IS INCLUDED BY ITS PARENT'S Makefile.am -# IT IS REQUIRED TO REFERENCE ALL FILES RELATIVE TO THE PARENT - -# install these files -dist_python_DATA += postgres/postgres.chart.py -dist_pythonconfig_DATA += postgres/postgres.conf - -# do not install these files, but include them in the distribution -dist_noinst_DATA += postgres/README.md postgres/Makefile.inc - diff --git a/collectors/python.d.plugin/postgres/README.md b/collectors/python.d.plugin/postgres/README.md deleted file mode 100644 index 7acb9a7a9..000000000 --- a/collectors/python.d.plugin/postgres/README.md +++ /dev/null @@ -1,145 +0,0 @@ - - -# PostgreSQL monitoring with Netdata - -> **Warning**: This module is deprecated and will be deleted in v1.37.0. -> Use [go.d/postgres](https://learn.netdata.cloud/docs/agent/collectors/go.d.plugin/modules/postgres). - -Collects database health and performance metrics. - -## Requirements - -- `python-psycopg2` package. You have to install it manually and make sure that it is available to the `netdata` user, either using `pip`, the package manager of your Linux distribution, or any other method you prefer. - -- PostgreSQL v9.4+ - -Following charts are drawn: - -1. **Database size** MB - - - size - -2. **Current Backend Processes** processes - - - active - -3. **Current Backend Process Usage** percentage - - - used - - available - -4. **Write-Ahead Logging Statistics** files/s - - - total - - ready - - done - -5. **Checkpoints** writes/s - - - scheduled - - requested - -6. **Current connections to db** count - - - connections - -7. **Tuples returned from db** tuples/s - - - sequential - - bitmap - -8. **Tuple reads from db** reads/s - - - disk - - cache - -9. **Transactions on db** transactions/s - - - committed - - rolled back - -10. **Tuples written to db** writes/s - - - inserted - - updated - - deleted - - conflicts - -11. **Locks on db** count per type - - - locks - -12. **Standby delta** KB - - - sent delta - - write delta - - flush delta - - replay delta - -13. **Standby lag** seconds - - - write lag - - flush lag - - replay lag - -14. **Average number of blocking transactions in db** processes - - - blocking - -## Configuration - -Edit the `python.d/postgres.conf` configuration file using `edit-config` from the Netdata [config -directory](/docs/configure/nodes.md), which is typically at `/etc/netdata`. - -```bash -cd /etc/netdata # Replace this path with your Netdata config directory, if different -sudo ./edit-config python.d/postgres.conf -``` - -When no configuration file is found, the module tries to connect to TCP/IP socket: `localhost:5432` with the -following collection jobs. - -```yaml -socket: - name : 'socket' - user : 'postgres' - database : 'postgres' - -tcp: - name : 'tcp' - user : 'postgres' - database : 'postgres' - host : 'localhost' - port : 5432 -``` - -**Note**: Every job collection must have a unique identifier. In cases that you monitor multiple DBs, every -job must have it's own name. Use a mnemonic of your preference (e.g us_east_db, us_east_tcp) - -## Troubleshooting - -To troubleshoot issues with the `postgres` collector, run the `python.d.plugin` with the debug option enabled. The output -should give you clues as to why the collector isn't working. - -First, navigate to your plugins directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on your -system, open `netdata.conf` and look for the setting `plugins directory`. Once you're in the plugin's directory, switch -to the `netdata` user. - -```bash -cd /usr/libexec/netdata/plugins.d/ -sudo su -s /bin/bash netdata -``` - -You can now run the `python.d.plugin` to debug the collector: - -```bash -./python.d.plugin postgres debug trace -``` - ---- - - diff --git a/collectors/python.d.plugin/postgres/postgres.chart.py b/collectors/python.d.plugin/postgres/postgres.chart.py deleted file mode 100644 index bd8f71a66..000000000 --- a/collectors/python.d.plugin/postgres/postgres.chart.py +++ /dev/null @@ -1,1436 +0,0 @@ -# -*- coding: utf-8 -*- -# Description: example netdata python.d module -# Authors: facetoe, dangtranhoang -# SPDX-License-Identifier: GPL-3.0-or-later - -from copy import deepcopy - -try: - import psycopg2 - from psycopg2 import extensions - from psycopg2.extras import DictCursor - from psycopg2 import OperationalError - - PSYCOPG2 = True -except ImportError: - PSYCOPG2 = False - -from bases.FrameworkServices.SimpleService import SimpleService - -DEFAULT_PORT = 5432 -DEFAULT_USER = 'postgres' -DEFAULT_CONNECT_TIMEOUT = 2 # seconds -DEFAULT_STATEMENT_TIMEOUT = 5000 # ms - -CONN_PARAM_DSN = 'dsn' -CONN_PARAM_HOST = 'host' -CONN_PARAM_PORT = 'port' -CONN_PARAM_DATABASE = 'database' -CONN_PARAM_USER = 'user' -CONN_PARAM_PASSWORD = 'password' -CONN_PARAM_CONN_TIMEOUT = 'connect_timeout' -CONN_PARAM_STATEMENT_TIMEOUT = 'statement_timeout' -CONN_PARAM_SSL_MODE = 'sslmode' -CONN_PARAM_SSL_ROOT_CERT = 'sslrootcert' -CONN_PARAM_SSL_CRL = 'sslcrl' -CONN_PARAM_SSL_CERT = 'sslcert' -CONN_PARAM_SSL_KEY = 'sslkey' - -QUERY_NAME_WAL = 'WAL' -QUERY_NAME_ARCHIVE = 'ARCHIVE' -QUERY_NAME_BACKENDS = 'BACKENDS' -QUERY_NAME_BACKEND_USAGE = 'BACKEND_USAGE' -QUERY_NAME_TABLE_STATS = 'TABLE_STATS' -QUERY_NAME_INDEX_STATS = 'INDEX_STATS' -QUERY_NAME_DATABASE = 'DATABASE' -QUERY_NAME_BGWRITER = 'BGWRITER' -QUERY_NAME_LOCKS = 'LOCKS' -QUERY_NAME_BLOCKERS = 'BLOCKERS' -QUERY_NAME_DATABASES = 'DATABASES' -QUERY_NAME_STANDBY = 'STANDBY' -QUERY_NAME_REPLICATION_SLOT = 'REPLICATION_SLOT' -QUERY_NAME_STANDBY_DELTA = 'STANDBY_DELTA' -QUERY_NAME_STANDBY_LAG = 'STANDBY_LAG' -QUERY_NAME_REPSLOT_FILES = 'REPSLOT_FILES' -QUERY_NAME_IF_SUPERUSER = 'IF_SUPERUSER' -QUERY_NAME_SERVER_VERSION = 'SERVER_VERSION' -QUERY_NAME_AUTOVACUUM = 'AUTOVACUUM' -QUERY_NAME_FORCED_AUTOVACUUM = 'FORCED_AUTOVACUUM' -QUERY_NAME_TX_WRAPAROUND = 'TX_WRAPAROUND' -QUERY_NAME_DIFF_LSN = 'DIFF_LSN' -QUERY_NAME_WAL_WRITES = 'WAL_WRITES' - -METRICS = { - QUERY_NAME_DATABASE: [ - 'connections', - 'xact_commit', - 'xact_rollback', - 'blks_read', - 'blks_hit', - 'tup_returned', - 'tup_fetched', - 'tup_inserted', - 'tup_updated', - 'tup_deleted', - 'conflicts', - 'temp_files', - 'temp_bytes', - 'size' - ], - QUERY_NAME_BACKENDS: [ - 'backends_active', - 'backends_idle' - ], - QUERY_NAME_BACKEND_USAGE: [ - 'available', - 'used' - ], - QUERY_NAME_INDEX_STATS: [ - 'index_count', - 'index_size' - ], - QUERY_NAME_TABLE_STATS: [ - 'table_size', - 'table_count' - ], - QUERY_NAME_WAL: [ - 'written_wal', - 'recycled_wal', - 'total_wal' - ], - QUERY_NAME_WAL_WRITES: [ - 'wal_writes' - ], - QUERY_NAME_ARCHIVE: [ - 'ready_count', - 'done_count', - 'file_count' - ], - QUERY_NAME_BGWRITER: [ - 'checkpoint_scheduled', - 'checkpoint_requested', - 'buffers_checkpoint', - 'buffers_clean', - 'maxwritten_clean', - 'buffers_backend', - 'buffers_alloc', - 'buffers_backend_fsync' - ], - QUERY_NAME_LOCKS: [ - 'ExclusiveLock', - 'RowShareLock', - 'SIReadLock', - 'ShareUpdateExclusiveLock', - 'AccessExclusiveLock', - 'AccessShareLock', - 'ShareRowExclusiveLock', - 'ShareLock', - 'RowExclusiveLock' - ], - QUERY_NAME_BLOCKERS: [ - 'blocking_pids_avg' - ], - QUERY_NAME_AUTOVACUUM: [ - 'analyze', - 'vacuum_analyze', - 'vacuum', - 'vacuum_freeze', - 'brin_summarize' - ], - QUERY_NAME_FORCED_AUTOVACUUM: [ - 'percent_towards_forced_vacuum' - ], - QUERY_NAME_TX_WRAPAROUND: [ - 'oldest_current_xid', - 'percent_towards_wraparound' - ], - QUERY_NAME_STANDBY_DELTA: [ - 'sent_delta', - 'write_delta', - 'flush_delta', - 'replay_delta' - ], - QUERY_NAME_STANDBY_LAG: [ - 'write_lag', - 'flush_lag', - 'replay_lag' - ], - QUERY_NAME_REPSLOT_FILES: [ - 'replslot_wal_keep', - 'replslot_files' - ] -} - -NO_VERSION = 0 -DEFAULT = 'DEFAULT' -V72 = 'V72' -V82 = 'V82' -V91 = 'V91' -V92 = 'V92' -V96 = 'V96' -V10 = 'V10' -V11 = 'V11' - -QUERY_WAL = { - DEFAULT: """ -SELECT - count(*) as total_wal, - count(*) FILTER (WHERE type = 'recycled') AS recycled_wal, - count(*) FILTER (WHERE type = 'written') AS written_wal -FROM - (SELECT - wal.name, - pg_walfile_name( - CASE pg_is_in_recovery() - WHEN true THEN NULL - ELSE pg_current_wal_lsn() - END ), - CASE - WHEN wal.name > pg_walfile_name( - CASE pg_is_in_recovery() - WHEN true THEN NULL - ELSE pg_current_wal_lsn() - END ) THEN 'recycled' - ELSE 'written' - END AS type - FROM pg_catalog.pg_ls_dir('pg_wal') AS wal(name) - WHERE name ~ '^[0-9A-F]{24}$' - ORDER BY - (pg_stat_file('pg_wal/'||name, true)).modification, - wal.name DESC) sub; -""", - V96: """ -SELECT - count(*) as total_wal, - count(*) FILTER (WHERE type = 'recycled') AS recycled_wal, - count(*) FILTER (WHERE type = 'written') AS written_wal -FROM - (SELECT - wal.name, - pg_xlogfile_name( - CASE pg_is_in_recovery() - WHEN true THEN NULL - ELSE pg_current_xlog_location() - END ), - CASE - WHEN wal.name > pg_xlogfile_name( - CASE pg_is_in_recovery() - WHEN true THEN NULL - ELSE pg_current_xlog_location() - END ) THEN 'recycled' - ELSE 'written' - END AS type - FROM pg_catalog.pg_ls_dir('pg_xlog') AS wal(name) - WHERE name ~ '^[0-9A-F]{24}$' - ORDER BY - (pg_stat_file('pg_xlog/'||name, true)).modification, - wal.name DESC) sub; -""", -} - -QUERY_ARCHIVE = { - DEFAULT: """ -SELECT - CAST(COUNT(*) AS INT) AS file_count, - CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.ready$$r$ as INT)),0) AS INT) AS ready_count, - CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.done$$r$ AS INT)),0) AS INT) AS done_count -FROM - pg_catalog.pg_ls_dir('pg_wal/archive_status') AS archive_files (archive_file); -""", - V96: """ -SELECT - CAST(COUNT(*) AS INT) AS file_count, - CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.ready$$r$ as INT)),0) AS INT) AS ready_count, - CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.done$$r$ AS INT)),0) AS INT) AS done_count -FROM - pg_catalog.pg_ls_dir('pg_xlog/archive_status') AS archive_files (archive_file); - -""", -} - -QUERY_BACKEND = { - DEFAULT: """ -SELECT - count(*) - (SELECT count(*) - FROM pg_stat_activity - WHERE state = 'idle') - AS backends_active, - (SELECT count(*) - FROM pg_stat_activity - WHERE state = 'idle') - AS backends_idle -FROM pg_stat_activity; -""", -} - -QUERY_BACKEND_USAGE = { - DEFAULT: """ -SELECT - COUNT(1) as used, - current_setting('max_connections')::int - current_setting('superuser_reserved_connections')::int - - COUNT(1) AS available -FROM pg_catalog.pg_stat_activity -WHERE backend_type IN ('client backend', 'background worker'); -""", - V10: """ -SELECT - SUM(s.conn) as used, - current_setting('max_connections')::int - current_setting('superuser_reserved_connections')::int - - SUM(s.conn) AS available -FROM ( - SELECT 's' as type, COUNT(1) as conn - FROM pg_catalog.pg_stat_activity - WHERE backend_type IN ('client backend', 'background worker') - UNION ALL - SELECT 'r', COUNT(1) - FROM pg_catalog.pg_stat_replication -) as s; -""", - V92: """ -SELECT - SUM(s.conn) as used, - current_setting('max_connections')::int - current_setting('superuser_reserved_connections')::int - - SUM(s.conn) AS available -FROM ( - SELECT 's' as type, COUNT(1) as conn - FROM pg_catalog.pg_stat_activity - WHERE query NOT LIKE 'autovacuum: %%' - UNION ALL - SELECT 'r', COUNT(1) - FROM pg_catalog.pg_stat_replication -) as s; -""", - V91: """ -SELECT - SUM(s.conn) as used, - current_setting('max_connections')::int - current_setting('superuser_reserved_connections')::int - - SUM(s.conn) AS available -FROM ( - SELECT 's' as type, COUNT(1) as conn - FROM pg_catalog.pg_stat_activity - WHERE current_query NOT LIKE 'autovacuum: %%' - UNION ALL - SELECT 'r', COUNT(1) - FROM pg_catalog.pg_stat_replication -) as s; -""", - V82: """ -SELECT - COUNT(1) as used, - current_setting('max_connections')::int - current_setting('superuser_reserved_connections')::int - - COUNT(1) AS available -FROM pg_catalog.pg_stat_activity -WHERE current_query NOT LIKE 'autovacuum: %%'; -""", - V72: """ -SELECT - COUNT(1) as used, - current_setting('max_connections')::int - current_setting('superuser_reserved_connections')::int - - COUNT(1) AS available -FROM pg_catalog.pg_stat_activity s -JOIN pg_catalog.pg_database d ON d.oid = s.datid -WHERE d.datallowconn; -""", -} - -QUERY_TABLE_STATS = { - DEFAULT: """ -SELECT - sum(relpages) * current_setting('block_size')::numeric AS table_size, - count(1) AS table_count -FROM pg_class -WHERE relkind IN ('r', 't', 'm'); -""", -} - -QUERY_INDEX_STATS = { - DEFAULT: """ -SELECT - sum(relpages) * current_setting('block_size')::numeric AS index_size, - count(1) AS index_count -FROM pg_class -WHERE relkind = 'i'; -""", -} - -QUERY_DATABASE = { - DEFAULT: """ -SELECT - datname AS database_name, - numbackends AS connections, - xact_commit AS xact_commit, - xact_rollback AS xact_rollback, - blks_read AS blks_read, - blks_hit AS blks_hit, - tup_returned AS tup_returned, - tup_fetched AS tup_fetched, - tup_inserted AS tup_inserted, - tup_updated AS tup_updated, - tup_deleted AS tup_deleted, - conflicts AS conflicts, - pg_database_size(datname) AS size, - temp_files AS temp_files, - temp_bytes AS temp_bytes -FROM pg_stat_database -WHERE datname IN %(databases)s ; -""", -} - -QUERY_BGWRITER = { - DEFAULT: """ -SELECT - checkpoints_timed AS checkpoint_scheduled, - checkpoints_req AS checkpoint_requested, - buffers_checkpoint * current_setting('block_size')::numeric buffers_checkpoint, - buffers_clean * current_setting('block_size')::numeric buffers_clean, - maxwritten_clean, - buffers_backend * current_setting('block_size')::numeric buffers_backend, - buffers_alloc * current_setting('block_size')::numeric buffers_alloc, - buffers_backend_fsync -FROM pg_stat_bgwriter; -""", -} - -QUERY_LOCKS = { - DEFAULT: """ -SELECT - pg_database.datname as database_name, - mode, - count(mode) AS locks_count -FROM pg_locks -INNER JOIN pg_database - ON pg_database.oid = pg_locks.database -GROUP BY datname, mode -ORDER BY datname, mode; -""", -} - -QUERY_BLOCKERS = { - DEFAULT: """ -WITH B AS ( -SELECT DISTINCT - pg_database.datname as database_name, - pg_locks.pid, - cardinality(pg_blocking_pids(pg_locks.pid)) AS blocking_pids -FROM pg_locks -INNER JOIN pg_database ON pg_database.oid = pg_locks.database -WHERE NOT pg_locks.granted) -SELECT database_name, AVG(blocking_pids) AS blocking_pids_avg -FROM B -GROUP BY database_name -""", - V96: """ -WITH B AS ( -SELECT DISTINCT - pg_database.datname as database_name, - blocked_locks.pid AS blocked_pid, - COUNT(blocking_locks.pid) AS blocking_pids -FROM pg_catalog.pg_locks blocked_locks -INNER JOIN pg_database ON pg_database.oid = blocked_locks.database -JOIN pg_catalog.pg_locks blocking_locks - ON blocking_locks.locktype = blocked_locks.locktype - AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database - AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation - AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page - AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple - AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid - AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid - AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid - AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid - AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid - AND blocking_locks.pid != blocked_locks.pid -WHERE NOT blocked_locks.GRANTED -GROUP BY database_name, blocked_pid) -SELECT database_name, AVG(blocking_pids) AS blocking_pids_avg -FROM B -GROUP BY database_name -""" -} - -QUERY_DATABASES = { - DEFAULT: """ -SELECT - datname -FROM pg_stat_database -WHERE - has_database_privilege( - (SELECT current_user), datname, 'connect') - AND NOT datname ~* '^template\d' -ORDER BY datname; -""", -} - -QUERY_STANDBY = { - DEFAULT: """ -SELECT - COALESCE(prs.slot_name, psr.application_name) application_name -FROM pg_stat_replication psr -LEFT OUTER JOIN pg_replication_slots prs on psr.pid = prs.active_pid -WHERE application_name IS NOT NULL; -""", -} - -QUERY_REPLICATION_SLOT = { - DEFAULT: """ -SELECT slot_name -FROM pg_replication_slots; -""" -} - -QUERY_STANDBY_DELTA = { - DEFAULT: """ -SELECT - COALESCE(prs.slot_name, psr.application_name) application_name, - pg_wal_lsn_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_wal_receive_lsn() - ELSE pg_current_wal_lsn() - END, - sent_lsn) AS sent_delta, - pg_wal_lsn_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_wal_receive_lsn() - ELSE pg_current_wal_lsn() - END, - write_lsn) AS write_delta, - pg_wal_lsn_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_wal_receive_lsn() - ELSE pg_current_wal_lsn() - END, - flush_lsn) AS flush_delta, - pg_wal_lsn_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_wal_receive_lsn() - ELSE pg_current_wal_lsn() - END, - replay_lsn) AS replay_delta -FROM pg_stat_replication psr -LEFT OUTER JOIN pg_replication_slots prs on psr.pid = prs.active_pid -WHERE application_name IS NOT NULL; -""", - V96: """ -SELECT - COALESCE(prs.slot_name, psr.application_name) application_name, - pg_xlog_location_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_xlog_receive_location() - ELSE pg_current_xlog_location() - END, - sent_location) AS sent_delta, - pg_xlog_location_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_xlog_receive_location() - ELSE pg_current_xlog_location() - END, - write_location) AS write_delta, - pg_xlog_location_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_xlog_receive_location() - ELSE pg_current_xlog_location() - END, - flush_location) AS flush_delta, - pg_xlog_location_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_xlog_receive_location() - ELSE pg_current_xlog_location() - END, - replay_location) AS replay_delta -FROM pg_stat_replication psr -LEFT OUTER JOIN pg_replication_slots prs on psr.pid = prs.active_pid -WHERE application_name IS NOT NULL; -""", -} - -QUERY_STANDBY_LAG = { - DEFAULT: """ -SELECT - COALESCE(prs.slot_name, psr.application_name) application_name, - COALESCE(EXTRACT(EPOCH FROM write_lag)::bigint, 0) AS write_lag, - COALESCE(EXTRACT(EPOCH FROM flush_lag)::bigint, 0) AS flush_lag, - COALESCE(EXTRACT(EPOCH FROM replay_lag)::bigint, 0) AS replay_lag -FROM pg_stat_replication psr -LEFT OUTER JOIN pg_replication_slots prs on psr.pid = prs.active_pid -WHERE application_name IS NOT NULL; -""" -} - -QUERY_REPSLOT_FILES = { - DEFAULT: """ -WITH wal_size AS ( - SELECT - setting::int AS val - FROM pg_settings - WHERE name = 'wal_segment_size' - ) -SELECT - slot_name, - slot_type, - replslot_wal_keep, - count(slot_file) AS replslot_files -FROM - (SELECT - slot.slot_name, - CASE - WHEN slot_file <> 'state' THEN 1 - END AS slot_file , - slot_type, - COALESCE ( - floor( - CASE WHEN pg_is_in_recovery() - THEN ( - pg_wal_lsn_diff(pg_last_wal_receive_lsn(), slot.restart_lsn) - -- this is needed to account for whole WAL retention and - -- not only size retention - + (pg_wal_lsn_diff(restart_lsn, '0/0') %% s.val) - ) / s.val - ELSE ( - pg_wal_lsn_diff(pg_current_wal_lsn(), slot.restart_lsn) - -- this is needed to account for whole WAL retention and - -- not only size retention - + (pg_walfile_name_offset(restart_lsn)).file_offset - ) / s.val - END - ),0) AS replslot_wal_keep - FROM pg_replication_slots slot - LEFT JOIN ( - SELECT - slot2.slot_name, - pg_ls_dir('pg_replslot/' || slot2.slot_name) AS slot_file - FROM pg_replication_slots slot2 - ) files (slot_name, slot_file) - ON slot.slot_name = files.slot_name - CROSS JOIN wal_size s - ) AS d -GROUP BY - slot_name, - slot_type, - replslot_wal_keep; -""", - V10: """ -WITH wal_size AS ( - SELECT - current_setting('wal_block_size')::INT * setting::INT AS val - FROM pg_settings - WHERE name = 'wal_segment_size' - ) -SELECT - slot_name, - slot_type, - replslot_wal_keep, - count(slot_file) AS replslot_files -FROM - (SELECT - slot.slot_name, - CASE - WHEN slot_file <> 'state' THEN 1 - END AS slot_file , - slot_type, - COALESCE ( - floor( - CASE WHEN pg_is_in_recovery() - THEN ( - pg_wal_lsn_diff(pg_last_wal_receive_lsn(), slot.restart_lsn) - -- this is needed to account for whole WAL retention and - -- not only size retention - + (pg_wal_lsn_diff(restart_lsn, '0/0') %% s.val) - ) / s.val - ELSE ( - pg_wal_lsn_diff(pg_current_wal_lsn(), slot.restart_lsn) - -- this is needed to account for whole WAL retention and - -- not only size retention - + (pg_walfile_name_offset(restart_lsn)).file_offset - ) / s.val - END - ),0) AS replslot_wal_keep - FROM pg_replication_slots slot - LEFT JOIN ( - SELECT - slot2.slot_name, - pg_ls_dir('pg_replslot/' || slot2.slot_name) AS slot_file - FROM pg_replication_slots slot2 - ) files (slot_name, slot_file) - ON slot.slot_name = files.slot_name - CROSS JOIN wal_size s - ) AS d -GROUP BY - slot_name, - slot_type, - replslot_wal_keep; -""", -} - -QUERY_SUPERUSER = { - DEFAULT: """ -SELECT current_setting('is_superuser') = 'on' AS is_superuser; -""", -} - -QUERY_SHOW_VERSION = { - DEFAULT: """ -SHOW server_version_num; -""", -} - -QUERY_AUTOVACUUM = { - DEFAULT: """ -SELECT - count(*) FILTER (WHERE query LIKE 'autovacuum: ANALYZE%%') AS analyze, - count(*) FILTER (WHERE query LIKE 'autovacuum: VACUUM ANALYZE%%') AS vacuum_analyze, - count(*) FILTER (WHERE query LIKE 'autovacuum: VACUUM%%' - AND query NOT LIKE 'autovacuum: VACUUM ANALYZE%%' - AND query NOT LIKE '%%to prevent wraparound%%') AS vacuum, - count(*) FILTER (WHERE query LIKE '%%to prevent wraparound%%') AS vacuum_freeze, - count(*) FILTER (WHERE query LIKE 'autovacuum: BRIN summarize%%') AS brin_summarize -FROM pg_stat_activity -WHERE query NOT LIKE '%%pg_stat_activity%%'; -""", -} - -QUERY_FORCED_AUTOVACUUM = { - DEFAULT: """ -WITH max_age AS ( - SELECT setting AS autovacuum_freeze_max_age - FROM pg_catalog.pg_settings - WHERE name = 'autovacuum_freeze_max_age' ) -, per_database_stats AS ( - SELECT datname - , m.autovacuum_freeze_max_age::int - , age(d.datfrozenxid) AS oldest_current_xid - FROM pg_catalog.pg_database d - JOIN max_age m ON (true) - WHERE d.datallowconn ) -SELECT max(ROUND(100*(oldest_current_xid/autovacuum_freeze_max_age::float))) AS percent_towards_forced_autovacuum -FROM per_database_stats; -""", -} - -QUERY_TX_WRAPAROUND = { - DEFAULT: """ -WITH max_age AS ( - SELECT 2000000000 as max_old_xid - FROM pg_catalog.pg_settings - WHERE name = 'autovacuum_freeze_max_age' ) -, per_database_stats AS ( - SELECT datname - , m.max_old_xid::int - , age(d.datfrozenxid) AS oldest_current_xid - FROM pg_catalog.pg_database d - JOIN max_age m ON (true) - WHERE d.datallowconn ) -SELECT max(oldest_current_xid) AS oldest_current_xid - , max(ROUND(100*(oldest_current_xid/max_old_xid::float))) AS percent_towards_wraparound -FROM per_database_stats; -""", -} - -QUERY_DIFF_LSN = { - DEFAULT: """ -SELECT - pg_wal_lsn_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_wal_receive_lsn() - ELSE pg_current_wal_lsn() - END, - '0/0') as wal_writes ; -""", - V96: """ -SELECT - pg_xlog_location_diff( - CASE pg_is_in_recovery() - WHEN true THEN pg_last_xlog_receive_location() - ELSE pg_current_xlog_location() - END, - '0/0') as wal_writes ; -""", -} - -def query_factory(name, version=NO_VERSION): - if name == QUERY_NAME_BACKENDS: - return QUERY_BACKEND[DEFAULT] - elif name == QUERY_NAME_BACKEND_USAGE: - if version < 80200: - return QUERY_BACKEND_USAGE[V72] - if version < 90100: - return QUERY_BACKEND_USAGE[V82] - if version < 90200: - return QUERY_BACKEND_USAGE[V91] - if version < 100000: - return QUERY_BACKEND_USAGE[V92] - elif version < 120000: - return QUERY_BACKEND_USAGE[V10] - return QUERY_BACKEND_USAGE[DEFAULT] - elif name == QUERY_NAME_TABLE_STATS: - return QUERY_TABLE_STATS[DEFAULT] - elif name == QUERY_NAME_INDEX_STATS: - return QUERY_INDEX_STATS[DEFAULT] - elif name == QUERY_NAME_DATABASE: - return QUERY_DATABASE[DEFAULT] - elif name == QUERY_NAME_BGWRITER: - return QUERY_BGWRITER[DEFAULT] - elif name == QUERY_NAME_LOCKS: - return QUERY_LOCKS[DEFAULT] - elif name == QUERY_NAME_BLOCKERS: - if version < 90600: - return QUERY_BLOCKERS[V96] - return QUERY_BLOCKERS[DEFAULT] - elif name == QUERY_NAME_DATABASES: - return QUERY_DATABASES[DEFAULT] - elif name == QUERY_NAME_STANDBY: - return QUERY_STANDBY[DEFAULT] - elif name == QUERY_NAME_REPLICATION_SLOT: - return QUERY_REPLICATION_SLOT[DEFAULT] - elif name == QUERY_NAME_IF_SUPERUSER: - return QUERY_SUPERUSER[DEFAULT] - elif name == QUERY_NAME_SERVER_VERSION: - return QUERY_SHOW_VERSION[DEFAULT] - elif name == QUERY_NAME_AUTOVACUUM: - return QUERY_AUTOVACUUM[DEFAULT] - elif name == QUERY_NAME_FORCED_AUTOVACUUM: - return QUERY_FORCED_AUTOVACUUM[DEFAULT] - elif name == QUERY_NAME_TX_WRAPAROUND: - return QUERY_TX_WRAPAROUND[DEFAULT] - elif name == QUERY_NAME_WAL: - if version < 100000: - return QUERY_WAL[V96] - return QUERY_WAL[DEFAULT] - elif name == QUERY_NAME_ARCHIVE: - if version < 100000: - return QUERY_ARCHIVE[V96] - return QUERY_ARCHIVE[DEFAULT] - elif name == QUERY_NAME_STANDBY_DELTA: - if version < 100000: - return QUERY_STANDBY_DELTA[V96] - return QUERY_STANDBY_DELTA[DEFAULT] - elif name == QUERY_NAME_STANDBY_LAG: - return QUERY_STANDBY_LAG[DEFAULT] - elif name == QUERY_NAME_REPSLOT_FILES: - if version < 110000: - return QUERY_REPSLOT_FILES[V10] - return QUERY_REPSLOT_FILES[DEFAULT] - elif name == QUERY_NAME_DIFF_LSN: - if version < 100000: - return QUERY_DIFF_LSN[V96] - return QUERY_DIFF_LSN[DEFAULT] - - raise ValueError('unknown query') - - -ORDER = [ - 'db_stat_temp_files', - 'db_stat_temp_bytes', - 'db_stat_blks', - 'db_stat_tuple_returned', - 'db_stat_tuple_write', - 'db_stat_transactions', - 'db_stat_connections', - 'db_stat_blocking_pids_avg', - 'database_size', - 'backend_process', - 'backend_usage', - 'index_count', - 'index_size', - 'table_count', - 'table_size', - 'wal', - 'wal_writes', - 'archive_wal', - 'checkpointer', - 'stat_bgwriter_alloc', - 'stat_bgwriter_checkpoint', - 'stat_bgwriter_backend', - 'stat_bgwriter_backend_fsync', - 'stat_bgwriter_bgwriter', - 'stat_bgwriter_maxwritten', - 'replication_slot', - 'standby_delta', - 'standby_lag', - 'autovacuum', - 'forced_autovacuum', - 'tx_wraparound_oldest_current_xid', - 'tx_wraparound_percent_towards_wraparound' -] - -CHARTS = { - 'db_stat_transactions': { - 'options': [None, 'Transactions on db', 'transactions/s', 'db statistics', 'postgres.db_stat_transactions', - 'line'], - 'lines': [ - ['xact_commit', 'committed', 'incremental'], - ['xact_rollback', 'rolled back', 'incremental'] - ] - }, - 'db_stat_connections': { - 'options': [None, 'Current connections to db', 'count', 'db statistics', 'postgres.db_stat_connections', - 'line'], - 'lines': [ - ['connections', 'connections', 'absolute'] - ] - }, - 'db_stat_blks': { - 'options': [None, 'Disk blocks reads from db', 'reads/s', 'db statistics', 'postgres.db_stat_blks', 'line'], - 'lines': [ - ['blks_read', 'disk', 'incremental'], - ['blks_hit', 'cache', 'incremental'] - ] - }, - 'db_stat_tuple_returned': { - 'options': [None, 'Tuples returned from db', 'tuples/s', 'db statistics', 'postgres.db_stat_tuple_returned', - 'line'], - 'lines': [ - ['tup_returned', 'sequential', 'incremental'], - ['tup_fetched', 'bitmap', 'incremental'] - ] - }, - 'db_stat_tuple_write': { - 'options': [None, 'Tuples written to db', 'writes/s', 'db statistics', 'postgres.db_stat_tuple_write', 'line'], - 'lines': [ - ['tup_inserted', 'inserted', 'incremental'], - ['tup_updated', 'updated', 'incremental'], - ['tup_deleted', 'deleted', 'incremental'], - ['conflicts', 'conflicts', 'incremental'] - ] - }, - 'db_stat_temp_bytes': { - 'options': [None, 'Temp files written to disk', 'KiB/s', 'db statistics', 'postgres.db_stat_temp_bytes', - 'line'], - 'lines': [ - ['temp_bytes', 'size', 'incremental', 1, 1024] - ] - }, - 'db_stat_temp_files': { - 'options': [None, 'Temp files written to disk', 'files', 'db statistics', 'postgres.db_stat_temp_files', - 'line'], - 'lines': [ - ['temp_files', 'files', 'incremental'] - ] - }, - 'db_stat_blocking_pids_avg': { - 'options': [None, 'Average number of blocking transactions in db', 'processes', 'db statistics', - 'postgres.db_stat_blocking_pids_avg', 'line'], - 'lines': [ - ['blocking_pids_avg', 'blocking', 'absolute'] - ] - }, - 'database_size': { - 'options': [None, 'Database size', 'MiB', 'database size', 'postgres.db_size', 'stacked'], - 'lines': [ - ] - }, - 'backend_process': { - 'options': [None, 'Current Backend Processes', 'processes', 'backend processes', 'postgres.backend_process', - 'line'], - 'lines': [ - ['backends_active', 'active', 'absolute'], - ['backends_idle', 'idle', 'absolute'] - ] - }, - 'backend_usage': { - 'options': [None, '% of Connections in use', 'percentage', 'backend processes', 'postgres.backend_usage', 'stacked'], - 'lines': [ - ['available', 'available', 'percentage-of-absolute-row'], - ['used', 'used', 'percentage-of-absolute-row'] - ] - }, - 'index_count': { - 'options': [None, 'Total indexes', 'index', 'indexes', 'postgres.index_count', 'line'], - 'lines': [ - ['index_count', 'total', 'absolute'] - ] - }, - 'index_size': { - 'options': [None, 'Indexes size', 'MiB', 'indexes', 'postgres.index_size', 'line'], - 'lines': [ - ['index_size', 'size', 'absolute', 1, 1024 * 1024] - ] - }, - 'table_count': { - 'options': [None, 'Total Tables', 'tables', 'tables', 'postgres.table_count', 'line'], - 'lines': [ - ['table_count', 'total', 'absolute'] - ] - }, - 'table_size': { - 'options': [None, 'Tables size', 'MiB', 'tables', 'postgres.table_size', 'line'], - 'lines': [ - ['table_size', 'size', 'absolute', 1, 1024 * 1024] - ] - }, - 'wal': { - 'options': [None, 'Write-Ahead Logs', 'files', 'wal', 'postgres.wal', 'line'], - 'lines': [ - ['written_wal', 'written', 'absolute'], - ['recycled_wal', 'recycled', 'absolute'], - ['total_wal', 'total', 'absolute'] - ] - }, - 'wal_writes': { - 'options': [None, 'Write-Ahead Logs', 'KiB/s', 'wal_writes', 'postgres.wal_writes', 'line'], - 'lines': [ - ['wal_writes', 'writes', 'incremental', 1, 1024] - ] - }, - 'archive_wal': { - 'options': [None, 'Archive Write-Ahead Logs', 'files/s', 'archive wal', 'postgres.archive_wal', 'line'], - 'lines': [ - ['file_count', 'total', 'incremental'], - ['ready_count', 'ready', 'incremental'], - ['done_count', 'done', 'incremental'] - ] - }, - 'checkpointer': { - 'options': [None, 'Checkpoints', 'writes', 'checkpointer', 'postgres.checkpointer', 'line'], - 'lines': [ - ['checkpoint_scheduled', 'scheduled', 'incremental'], - ['checkpoint_requested', 'requested', 'incremental'] - ] - }, - 'stat_bgwriter_alloc': { - 'options': [None, 'Buffers allocated', 'KiB/s', 'bgwriter', 'postgres.stat_bgwriter_alloc', 'line'], - 'lines': [ - ['buffers_alloc', 'alloc', 'incremental', 1, 1024] - ] - }, - 'stat_bgwriter_checkpoint': { - 'options': [None, 'Buffers written during checkpoints', 'KiB/s', 'bgwriter', - 'postgres.stat_bgwriter_checkpoint', 'line'], - 'lines': [ - ['buffers_checkpoint', 'checkpoint', 'incremental', 1, 1024] - ] - }, - 'stat_bgwriter_backend': { - 'options': [None, 'Buffers written directly by a backend', 'KiB/s', 'bgwriter', - 'postgres.stat_bgwriter_backend', 'line'], - 'lines': [ - ['buffers_backend', 'backend', 'incremental', 1, 1024] - ] - }, - 'stat_bgwriter_backend_fsync': { - 'options': [None, 'Fsync by backend', 'times', 'bgwriter', 'postgres.stat_bgwriter_backend_fsync', 'line'], - 'lines': [ - ['buffers_backend_fsync', 'backend fsync', 'incremental'] - ] - }, - 'stat_bgwriter_bgwriter': { - 'options': [None, 'Buffers written by the background writer', 'KiB/s', 'bgwriter', - 'postgres.bgwriter_bgwriter', 'line'], - 'lines': [ - ['buffers_clean', 'clean', 'incremental', 1, 1024] - ] - }, - 'stat_bgwriter_maxwritten': { - 'options': [None, 'Too many buffers written', 'times', 'bgwriter', 'postgres.stat_bgwriter_maxwritten', - 'line'], - 'lines': [ - ['maxwritten_clean', 'maxwritten', 'incremental'] - ] - }, - 'autovacuum': { - 'options': [None, 'Autovacuum workers', 'workers', 'autovacuum', 'postgres.autovacuum', 'line'], - 'lines': [ - ['analyze', 'analyze', 'absolute'], - ['vacuum', 'vacuum', 'absolute'], - ['vacuum_analyze', 'vacuum analyze', 'absolute'], - ['vacuum_freeze', 'vacuum freeze', 'absolute'], - ['brin_summarize', 'brin summarize', 'absolute'] - ] - }, - 'forced_autovacuum': { - 'options': [None, 'Percent towards forced autovacuum', 'percent', 'autovacuum', 'postgres.forced_autovacuum', 'line'], - 'lines': [ - ['percent_towards_forced_autovacuum', 'percent', 'absolute'] - ] - }, - 'tx_wraparound_oldest_current_xid': { - 'options': [None, 'Oldest current XID', 'xid', 'tx_wraparound', 'postgres.tx_wraparound_oldest_current_xid', 'line'], - 'lines': [ - ['oldest_current_xid', 'xid', 'absolute'] - ] - }, - 'tx_wraparound_percent_towards_wraparound': { - 'options': [None, 'Percent towards wraparound', 'percent', 'tx_wraparound', 'postgres.percent_towards_wraparound', 'line'], - 'lines': [ - ['percent_towards_wraparound', 'percent', 'absolute'] - ] - }, - 'standby_delta': { - 'options': [None, 'Standby delta', 'KiB', 'replication delta', 'postgres.standby_delta', 'line'], - 'lines': [ - ['sent_delta', 'sent delta', 'absolute', 1, 1024], - ['write_delta', 'write delta', 'absolute', 1, 1024], - ['flush_delta', 'flush delta', 'absolute', 1, 1024], - ['replay_delta', 'replay delta', 'absolute', 1, 1024] - ] - }, - 'standby_lag': { - 'options': [None, 'Standby lag', 'seconds', 'replication lag', 'postgres.standby_lag', 'line'], - 'lines': [ - ['write_lag', 'write lag', 'absolute'], - ['flush_lag', 'flush lag', 'absolute'], - ['replay_lag', 'replay lag', 'absolute'] - ] - }, - 'replication_slot': { - 'options': [None, 'Replication slot files', 'files', 'replication slot', 'postgres.replication_slot', 'line'], - 'lines': [ - ['replslot_wal_keep', 'wal keeped', 'absolute'], - ['replslot_files', 'pg_replslot files', 'absolute'] - ] - } -} - - -class Service(SimpleService): - def __init__(self, configuration=None, name=None): - SimpleService.__init__(self, configuration=configuration, name=name) - self.order = list(ORDER) - self.definitions = deepcopy(CHARTS) - self.do_table_stats = configuration.pop('table_stats', False) - self.do_index_stats = configuration.pop('index_stats', False) - self.databases_to_poll = configuration.pop('database_poll', None) - self.configuration = configuration - self.conn = None - self.conn_params = dict() - self.server_version = None - self.is_superuser = False - self.alive = False - self.databases = list() - self.secondaries = list() - self.replication_slots = list() - self.queries = dict() - self.data = dict() - - def reconnect(self): - return self.connect() - - def build_conn_params(self): - conf = self.configuration - - # connection URIs: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING - if conf.get(CONN_PARAM_DSN): - return {'dsn': conf[CONN_PARAM_DSN]} - - params = { - CONN_PARAM_HOST: conf.get(CONN_PARAM_HOST), - CONN_PARAM_PORT: conf.get(CONN_PARAM_PORT, DEFAULT_PORT), - CONN_PARAM_DATABASE: conf.get(CONN_PARAM_DATABASE), - CONN_PARAM_USER: conf.get(CONN_PARAM_USER, DEFAULT_USER), - CONN_PARAM_PASSWORD: conf.get(CONN_PARAM_PASSWORD), - CONN_PARAM_CONN_TIMEOUT: conf.get(CONN_PARAM_CONN_TIMEOUT, DEFAULT_CONNECT_TIMEOUT), - 'options': '-c statement_timeout={0}'.format( - conf.get(CONN_PARAM_STATEMENT_TIMEOUT, DEFAULT_STATEMENT_TIMEOUT)), - } - - # https://www.postgresql.org/docs/current/libpq-ssl.html - ssl_params = dict( - (k, v) for k, v in { - CONN_PARAM_SSL_MODE: conf.get(CONN_PARAM_SSL_MODE), - CONN_PARAM_SSL_ROOT_CERT: conf.get(CONN_PARAM_SSL_ROOT_CERT), - CONN_PARAM_SSL_CRL: conf.get(CONN_PARAM_SSL_CRL), - CONN_PARAM_SSL_CERT: conf.get(CONN_PARAM_SSL_CERT), - CONN_PARAM_SSL_KEY: conf.get(CONN_PARAM_SSL_KEY), - }.items() if v) - - if CONN_PARAM_SSL_MODE not in ssl_params and len(ssl_params) > 0: - raise ValueError("mandatory 'sslmode' param is missing, please set") - - params.update(ssl_params) - - return params - - def connect(self): - if self.conn: - self.conn.close() - self.conn = None - - try: - self.conn = psycopg2.connect(**self.conn_params) - self.conn.set_isolation_level(extensions.ISOLATION_LEVEL_AUTOCOMMIT) - self.conn.set_session(readonly=True) - except OperationalError as error: - self.error(error) - self.alive = False - else: - self.alive = True - - return self.alive - - def check(self): - if not PSYCOPG2: - self.error("'python-psycopg2' package is needed to use postgres module") - return False - - try: - self.conn_params = self.build_conn_params() - except ValueError as error: - self.error('error on creating connection params : {0}', error) - return False - - if not self.connect(): - self.error('failed to connect to {0}'.format(hide_password(self.conn_params))) - return False - - try: - self.check_queries() - except Exception as error: - self.error(error) - return False - - self.populate_queries() - self.create_dynamic_charts() - - return True - - def get_data(self): - if not self.alive and not self.reconnect(): - return None - - self.data = dict() - try: - cursor = self.conn.cursor(cursor_factory=DictCursor) - - self.data.update(zero_lock_types(self.databases)) - - for query, metrics in self.queries.items(): - self.query_stats(cursor, query, metrics) - - except OperationalError: - self.alive = False - return None - - cursor.close() - - return self.data - - def query_stats(self, cursor, query, metrics): - cursor.execute(query, dict(databases=tuple(self.databases))) - - for row in cursor: - for metric in metrics: - # databases - if 'database_name' in row: - dimension_id = '_'.join([row['database_name'], metric]) - # secondaries - elif 'application_name' in row: - dimension_id = '_'.join([row['application_name'], metric]) - # replication slots - elif 'slot_name' in row: - dimension_id = '_'.join([row['slot_name'], metric]) - # other - else: - dimension_id = metric - - if metric in row: - if row[metric] is not None: - self.data[dimension_id] = int(row[metric]) - elif 'locks_count' in row: - if metric == row['mode']: - self.data[dimension_id] = row['locks_count'] - - def check_queries(self): - cursor = self.conn.cursor() - - self.server_version = detect_server_version(cursor, query_factory(QUERY_NAME_SERVER_VERSION)) - self.debug('server version: {0}'.format(self.server_version)) - - self.is_superuser = check_if_superuser(cursor, query_factory(QUERY_NAME_IF_SUPERUSER)) - self.debug('superuser: {0}'.format(self.is_superuser)) - - self.databases = discover(cursor, query_factory(QUERY_NAME_DATABASES)) - self.debug('discovered databases {0}'.format(self.databases)) - if self.databases_to_poll: - to_poll = self.databases_to_poll.split() - self.databases = [db for db in self.databases if db in to_poll] or self.databases - - self.secondaries = discover(cursor, query_factory(QUERY_NAME_STANDBY)) - self.debug('discovered secondaries: {0}'.format(self.secondaries)) - - if self.server_version >= 94000: - self.replication_slots = discover(cursor, query_factory(QUERY_NAME_REPLICATION_SLOT)) - self.debug('discovered replication slots: {0}'.format(self.replication_slots)) - - cursor.close() - - def populate_queries(self): - self.queries[query_factory(QUERY_NAME_DATABASE)] = METRICS[QUERY_NAME_DATABASE] - self.queries[query_factory(QUERY_NAME_BACKENDS)] = METRICS[QUERY_NAME_BACKENDS] - self.queries[query_factory(QUERY_NAME_BACKEND_USAGE, self.server_version)] = METRICS[QUERY_NAME_BACKEND_USAGE] - self.queries[query_factory(QUERY_NAME_LOCKS)] = METRICS[QUERY_NAME_LOCKS] - self.queries[query_factory(QUERY_NAME_BGWRITER)] = METRICS[QUERY_NAME_BGWRITER] - self.queries[query_factory(QUERY_NAME_DIFF_LSN, self.server_version)] = METRICS[QUERY_NAME_WAL_WRITES] - self.queries[query_factory(QUERY_NAME_STANDBY_DELTA, self.server_version)] = METRICS[QUERY_NAME_STANDBY_DELTA] - self.queries[query_factory(QUERY_NAME_BLOCKERS, self.server_version)] = METRICS[QUERY_NAME_BLOCKERS] - - if self.do_index_stats: - self.queries[query_factory(QUERY_NAME_INDEX_STATS)] = METRICS[QUERY_NAME_INDEX_STATS] - if self.do_table_stats: - self.queries[query_factory(QUERY_NAME_TABLE_STATS)] = METRICS[QUERY_NAME_TABLE_STATS] - - if self.is_superuser: - self.queries[query_factory(QUERY_NAME_ARCHIVE, self.server_version)] = METRICS[QUERY_NAME_ARCHIVE] - - if self.server_version >= 90400: - self.queries[query_factory(QUERY_NAME_WAL, self.server_version)] = METRICS[QUERY_NAME_WAL] - - if self.server_version >= 100000: - v = METRICS[QUERY_NAME_REPSLOT_FILES] - self.queries[query_factory(QUERY_NAME_REPSLOT_FILES, self.server_version)] = v - - if self.server_version >= 90400: - self.queries[query_factory(QUERY_NAME_AUTOVACUUM)] = METRICS[QUERY_NAME_AUTOVACUUM] - - self.queries[query_factory(QUERY_NAME_FORCED_AUTOVACUUM)] = METRICS[QUERY_NAME_FORCED_AUTOVACUUM] - self.queries[query_factory(QUERY_NAME_TX_WRAPAROUND)] = METRICS[QUERY_NAME_TX_WRAPAROUND] - - if self.server_version >= 100000: - self.queries[query_factory(QUERY_NAME_STANDBY_LAG)] = METRICS[QUERY_NAME_STANDBY_LAG] - - def create_dynamic_charts(self): - for database_name in self.databases[::-1]: - dim = [ - database_name + '_size', - database_name, - 'absolute', - 1, - 1024 * 1024, - ] - self.definitions['database_size']['lines'].append(dim) - for chart_name in [name for name in self.order if name.startswith('db_stat')]: - add_database_stat_chart( - order=self.order, - definitions=self.definitions, - name=chart_name, - database_name=database_name, - ) - add_database_lock_chart( - order=self.order, - definitions=self.definitions, - database_name=database_name, - ) - - for application_name in self.secondaries[::-1]: - add_replication_standby_chart( - order=self.order, - definitions=self.definitions, - name='standby_delta', - application_name=application_name, - chart_family='replication delta', - ) - add_replication_standby_chart( - order=self.order, - definitions=self.definitions, - name='standby_lag', - application_name=application_name, - chart_family='replication lag', - ) - - for slot_name in self.replication_slots[::-1]: - add_replication_slot_chart( - order=self.order, - definitions=self.definitions, - name='replication_slot', - slot_name=slot_name, - ) - - -def discover(cursor, query): - cursor.execute(query) - result = list() - for v in [value[0] for value in cursor]: - if v not in result: - result.append(v) - return result - - -def check_if_superuser(cursor, query): - cursor.execute(query) - return cursor.fetchone()[0] - - -def detect_server_version(cursor, query): - cursor.execute(query) - return int(cursor.fetchone()[0]) - - -def zero_lock_types(databases): - result = dict() - for database in databases: - for lock_type in METRICS['LOCKS']: - key = '_'.join([database, lock_type]) - result[key] = 0 - - return result - - -def hide_password(config): - return dict((k, v if k != 'password' or not v else '*****') for k, v in config.items()) - - -def add_database_lock_chart(order, definitions, database_name): - def create_lines(database): - result = list() - for lock_type in METRICS['LOCKS']: - dimension_id = '_'.join([database, lock_type]) - result.append([dimension_id, lock_type, 'absolute']) - return result - - chart_name = database_name + '_locks' - order.insert(-1, chart_name) - definitions[chart_name] = { - 'options': - [None, 'Locks on db: ' + database_name, 'locks', 'db ' + database_name, 'postgres.db_locks', 'line'], - 'lines': create_lines(database_name) - } - - -def add_database_stat_chart(order, definitions, name, database_name): - def create_lines(database, lines): - result = list() - for line in lines: - new_line = ['_'.join([database, line[0]])] + line[1:] - result.append(new_line) - return result - - chart_template = CHARTS[name] - chart_name = '_'.join([database_name, name]) - order.insert(0, chart_name) - name, title, units, _, context, chart_type = chart_template['options'] - definitions[chart_name] = { - 'options': [name, title + ': ' + database_name, units, 'db ' + database_name, context, chart_type], - 'lines': create_lines(database_name, chart_template['lines'])} - - -def add_replication_standby_chart(order, definitions, name, application_name, chart_family): - def create_lines(standby, lines): - result = list() - for line in lines: - new_line = ['_'.join([standby, line[0]])] + line[1:] - result.append(new_line) - return result - - chart_template = CHARTS[name] - chart_name = '_'.join([application_name, name]) - position = order.index('database_size') - order.insert(position, chart_name) - name, title, units, _, context, chart_type = chart_template['options'] - definitions[chart_name] = { - 'options': [name, title + ': ' + application_name, units, chart_family, context, chart_type], - 'lines': create_lines(application_name, chart_template['lines'])} - - -def add_replication_slot_chart(order, definitions, name, slot_name): - def create_lines(slot, lines): - result = list() - for line in lines: - new_line = ['_'.join([slot, line[0]])] + line[1:] - result.append(new_line) - return result - - chart_template = CHARTS[name] - chart_name = '_'.join([slot_name, name]) - position = order.index('database_size') - order.insert(position, chart_name) - name, title, units, _, context, chart_type = chart_template['options'] - definitions[chart_name] = { - 'options': [name, title + ': ' + slot_name, units, 'replication slot files', context, chart_type], - 'lines': create_lines(slot_name, chart_template['lines'])} diff --git a/collectors/python.d.plugin/postgres/postgres.conf b/collectors/python.d.plugin/postgres/postgres.conf deleted file mode 100644 index 7e354d99b..000000000 --- a/collectors/python.d.plugin/postgres/postgres.conf +++ /dev/null @@ -1,134 +0,0 @@ -# netdata python.d.plugin configuration for postgresql -# -# This file is in YaML format. Generally the format is: -# -# name: value -# -# There are 2 sections: -# - global variables -# - one or more JOBS -# -# JOBS allow you to collect values from multiple sources. -# Each source will have its own set of charts. -# -# JOB parameters have to be indented (using spaces only, example below). - -# ---------------------------------------------------------------------- -# Global Variables -# These variables set the defaults for all JOBs, however each JOB -# may define its own, overriding the defaults. - -# update_every sets the default data collection frequency. -# If unset, the python.d.plugin default is used. -# update_every: 1 - -# priority controls the order of charts at the netdata dashboard. -# Lower numbers move the charts towards the top of the page. -# If unset, the default for python.d.plugin is used. -# priority: 60000 - -# penalty indicates whether to apply penalty to update_every in case of failures. -# Penalty will increase every 5 failed updates in a row. Maximum penalty is 10 minutes. -# penalty: yes - -# autodetection_retry sets the job re-check interval in seconds. -# The job is not deleted if check fails. -# Attempts to start the job are made once every autodetection_retry. -# This feature is disabled by default. -# autodetection_retry: 0 - -# ---------------------------------------------------------------------- -# JOBS (data collection sources) -# -# The default JOBS share the same *name*. JOBS with the same name -# are mutually exclusive. Only one of them will be allowed running at -# any time. This allows autodetection to try several alternatives and -# pick the one that works. -# -# Any number of jobs is supported. -# -# All python.d.plugin JOBS (for all its modules) support a set of -# predefined parameters. These are: -# -# job_name: -# name: myname # the JOB's name as it will appear at the -# # dashboard (by default is the job_name) -# # JOBs sharing a name are mutually exclusive -# update_every: 1 # the JOB's data collection frequency -# priority: 60000 # the JOB's order on the dashboard -# penalty: yes # the JOB's penalty -# autodetection_retry: 0 # the JOB's re-check interval in seconds -# -# A single connection is required in order to pull statistics. -# -# Connections can be configured with the following options: -# -# dsn : 'connection URI' # see https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING -# -# OR -# -# database : 'example_db_name' -# user : 'example_user' -# password : 'example_pass' -# host : 'localhost' -# port : 5432 -# connect_timeout : 2 # in seconds, default is 2 -# statement_timeout : 2000 # in ms, default is 2000 -# sslmode : mode # one of [disable, allow, prefer, require, verify-ca, verify-full] -# sslrootcert : path/to/rootcert # the location of the root certificate file -# sslcrl : path/to/crl # the location of the CRL file -# sslcert : path/to/cert # the location of the client certificate file -# sslkey : path/to/key # the location of the client key file -# -# SSL connection parameters description: https://www.postgresql.org/docs/current/libpq-ssl.html -# -# Additionally, the following options allow selective disabling of charts -# -# table_stats : false -# index_stats : false -# database_poll : 'dbase_name1 dbase_name2' # poll only specified databases (all other will be excluded from charts) -# -# Postgres permissions are configured at its pg_hba.conf file. You can -# "trust" local clients to allow netdata to connect, or you can create -# a postgres user for netdata and add its password below to allow -# netdata connect. -# -# Please note that when running Postgres from inside the container, -# the client (Netdata) is not considered local, unless it runs from inside -# the same container. -# -# Superuser access is needed for these charts: -# Write-Ahead Logs -# Archive Write-Ahead Logs -# -# Autovacuum charts is allowed since Postgres 9.4 -# ---------------------------------------------------------------------- - -socket: - name : 'local' - user : 'postgres' - database : 'postgres' - -tcp: - name : 'local' - database : 'postgres' - user : 'postgres' - password : 'postgres' - host : 'localhost' - port : 5432 - -tcpipv4: - name : 'local' - database : 'postgres' - user : 'postgres' - password : 'postgres' - host : '127.0.0.1' - port : 5432 - -tcpipv6: - name : 'local' - database : 'postgres' - user : 'postgres' - password : 'postgres' - host : '::1' - port : 5432 diff --git a/collectors/python.d.plugin/python.d.conf b/collectors/python.d.plugin/python.d.conf index 72e20fcd3..7b43ee205 100644 --- a/collectors/python.d.plugin/python.d.conf +++ b/collectors/python.d.plugin/python.d.conf @@ -56,14 +56,13 @@ logind: no # memcached: yes # mongodb: yes # monit: yes -# nginx_plus: yes # nvidia_smi: yes # nsd: yes # ntpd: yes # openldap: yes # oracledb: yes +# pandas: yes # postfix: yes -# postgres: yes # proxysql: yes # puppet: yes # rabbitmq: yes diff --git a/collectors/python.d.plugin/python.d.plugin.in b/collectors/python.d.plugin/python.d.plugin.in index c04cb3ff0..681ceb403 100644 --- a/collectors/python.d.plugin/python.d.plugin.in +++ b/collectors/python.d.plugin/python.d.plugin.in @@ -131,7 +131,7 @@ def dirs(): DIRS = dirs() -IS_ATTY = sys.stdout.isatty() +IS_ATTY = sys.stdout.isatty() or sys.stderr.isatty() MODULE_SUFFIX = '.chart.py' @@ -496,7 +496,16 @@ class FileLockRegistry: self.path = path self.locks = dict() + @staticmethod + def rename(name): + # go version name is 'docker' + if name.startswith("dockerd"): + name = "docker" + name[7:] + return name + + def register(self, name): + name = self.rename(name) if name in self.locks: return file = os.path.join(self.path, '{0}.collector.lock'.format(name)) @@ -505,6 +514,7 @@ class FileLockRegistry: self.locks[name] = lock def unregister(self, name): + name = self.rename(name) if name not in self.locks: return lock = self.locks[name] @@ -893,6 +903,11 @@ def main(): registry, ) + # cheap attempt to reduce chance of python.d job running before go.d + # TODO: better implementation needed + if not IS_ATTY: + time.sleep(1.5) + try: if not p.setup(): return diff --git a/collectors/python.d.plugin/python_modules/bases/FrameworkServices/SimpleService.py b/collectors/python.d.plugin/python_modules/bases/FrameworkServices/SimpleService.py index ed1b2e669..a7acc23b6 100644 --- a/collectors/python.d.plugin/python_modules/bases/FrameworkServices/SimpleService.py +++ b/collectors/python.d.plugin/python_modules/bases/FrameworkServices/SimpleService.py @@ -4,14 +4,13 @@ # Author: Ilya Mashchenko (ilyam8) # SPDX-License-Identifier: GPL-3.0-or-later - -from time import sleep, time - -from third_party.monotonic import monotonic +import os from bases.charts import Charts, ChartError, create_runtime_chart from bases.collection import safe_print from bases.loggers import PythonDLimitedLogger +from third_party.monotonic import monotonic +from time import sleep, time RUNTIME_CHART_UPDATE = 'BEGIN netdata.runtime_{job_name} {since_last}\n' \ 'SET run_time = {elapsed}\n' \ @@ -20,6 +19,8 @@ RUNTIME_CHART_UPDATE = 'BEGIN netdata.runtime_{job_name} {since_last}\n' \ PENALTY_EVERY = 5 MAX_PENALTY = 10 * 60 # 10 minutes +ND_INTERNAL_MONITORING_DISABLED = os.getenv("NETDATA_INTERNALS_MONITORING") == "NO" + class RuntimeCounters: def __init__(self, configuration): @@ -79,11 +80,13 @@ class SimpleService(PythonDLimitedLogger, object): self.module_name = clean_module_name(self.__module__) self.job_name = configuration.pop('job_name') + self.actual_job_name = self.job_name or self.module_name self.override_name = configuration.pop('override_name') self.fake_name = None self._runtime_counters = RuntimeCounters(configuration=configuration) self.charts = Charts(job_name=self.actual_name, + actual_job_name=self.actual_job_name, priority=configuration.pop('priority'), cleanup=configuration.pop('chart_cleanup'), get_update_every=self.get_update_every, @@ -208,9 +211,10 @@ class SimpleService(PythonDLimitedLogger, object): job.elapsed = int((monotonic() - job.start_mono) * 1e3) job.prev_update = job.start_real job.retries, job.penalty = 0, 0 - safe_print(RUNTIME_CHART_UPDATE.format(job_name=self.name, - since_last=since, - elapsed=job.elapsed)) + if not ND_INTERNAL_MONITORING_DISABLED: + safe_print(RUNTIME_CHART_UPDATE.format(job_name=self.name, + since_last=since, + elapsed=job.elapsed)) self.debug('update => [{status}] (elapsed time: {elapsed}, failed retries in a row: {retries})'.format( status='OK' if updated else 'FAILED', elapsed=job.elapsed if updated else '-', diff --git a/collectors/python.d.plugin/python_modules/bases/charts.py b/collectors/python.d.plugin/python_modules/bases/charts.py index 54986a937..203ad1672 100644 --- a/collectors/python.d.plugin/python_modules/bases/charts.py +++ b/collectors/python.d.plugin/python_modules/bases/charts.py @@ -3,6 +3,8 @@ # Author: Ilya Mashchenko (ilyam8) # SPDX-License-Identifier: GPL-3.0-or-later +import os + from bases.collection import safe_print CHART_PARAMS = ['type', 'id', 'name', 'title', 'units', 'family', 'context', 'chart_type', 'hidden'] @@ -18,15 +20,24 @@ CHART_CREATE = "CHART {type}.{id} '{name}' '{title}' '{units}' '{family}' '{cont CHART_OBSOLETE = "CHART {type}.{id} '{name}' '{title}' '{units}' '{family}' '{context}' " \ "{chart_type} {priority} {update_every} '{hidden} obsolete'\n" +CLABEL_COLLECT_JOB = "CLABEL '_collect_job' '{actual_job_name}' '0'\n" +CLABEL_COMMIT = "CLABEL_COMMIT\n" + DIMENSION_CREATE = "DIMENSION '{id}' '{name}' {algorithm} {multiplier} {divisor} '{hidden} {obsolete}'\n" DIMENSION_SET = "SET '{id}' = {value}\n" CHART_VARIABLE_SET = "VARIABLE CHART '{id}' = {value}\n" +# 1 is label source auto +# https://github.com/netdata/netdata/blob/cc2586de697702f86a3c34e60e23652dd4ddcb42/database/rrd.h#L205 RUNTIME_CHART_CREATE = "CHART netdata.runtime_{job_name} '' 'Execution time' 'ms' 'python.d' " \ "netdata.pythond_runtime line 145000 {update_every} '' 'python.d.plugin' '{module_name}'\n" \ + "CLABEL '_collect_job' '{actual_job_name}' '1'\n" \ + "CLABEL_COMMIT\n" \ "DIMENSION run_time 'run time' absolute 1 1\n" +ND_INTERNAL_MONITORING_DISABLED = os.getenv("NETDATA_INTERNALS_MONITORING") == "NO" + def create_runtime_chart(func): """ @@ -42,12 +53,14 @@ def create_runtime_chart(func): def wrapper(*args, **kwargs): self = args[0] - chart = RUNTIME_CHART_CREATE.format( - job_name=self.name, - update_every=self._runtime_counters.update_every, - module_name=self.module_name, - ) - safe_print(chart) + if not ND_INTERNAL_MONITORING_DISABLED: + chart = RUNTIME_CHART_CREATE.format( + job_name=self.name, + actual_job_name=self.actual_job_name, + update_every=self._runtime_counters.update_every, + module_name=self.module_name, + ) + safe_print(chart) ok = func(*args, **kwargs) return ok @@ -77,13 +90,14 @@ class Charts: Chart is a instance of Chart class. Charts adding must be done using Charts.add_chart() method only""" - def __init__(self, job_name, priority, cleanup, get_update_every, module_name): + def __init__(self, job_name, actual_job_name, priority, cleanup, get_update_every, module_name): """ :param job_name: :param priority: :param get_update_every: """ self.job_name = job_name + self.actual_job_name = actual_job_name self.priority = priority self.cleanup = cleanup self.get_update_every = get_update_every @@ -131,6 +145,7 @@ class Charts: new_chart.params['update_every'] = self.get_update_every() new_chart.params['priority'] = self.priority new_chart.params['module_name'] = self.module_name + new_chart.params['actual_job_name'] = self.actual_job_name self.priority += 1 self.charts[new_chart.id] = new_chart @@ -230,13 +245,14 @@ class Chart: :return: """ chart = CHART_CREATE.format(**self.params) + labels = CLABEL_COLLECT_JOB.format(**self.params) + CLABEL_COMMIT dimensions = ''.join([dimension.create() for dimension in self.dimensions]) variables = ''.join([var.set(var.value) for var in self.variables if var]) self.flags.push = False self.flags.created = True - safe_print(chart + dimensions + variables) + safe_print(chart + labels + dimensions + variables) def can_be_updated(self, data): for dim in self.dimensions: diff --git a/collectors/python.d.plugin/rabbitmq/README.md b/collectors/python.d.plugin/rabbitmq/README.md index 607e32c7f..927adcc68 100644 --- a/collectors/python.d.plugin/rabbitmq/README.md +++ b/collectors/python.d.plugin/rabbitmq/README.md @@ -113,4 +113,26 @@ socket: --- +### Per-Queue Chart configuration +RabbitMQ users with the "monitoring" tag cannot see all queue data. You'll need a user with read permissions. +To create a dedicated user for netdata: + +```bash +rabbitmqctl add_user netdata ChangeThisSuperSecretPassword +rabbitmqctl set_permissions netdata "^$" "^$" ".*" +``` + +See [set_permissions](https://www.rabbitmq.com/rabbitmqctl.8.html#set_permissions) for details. + +Once the user is set up, add `collect_queues_metrics: yes` to your `rabbitmq.conf`: + +```yaml +local: + name : 'local' + host : '127.0.0.1' + port : 15672 + user : 'netdata' + pass : 'ChangeThisSuperSecretPassword' + collect_queues_metrics : 'yes' +``` diff --git a/collectors/python.d.plugin/sensors/README.md b/collectors/python.d.plugin/sensors/README.md index 149589317..e791195d4 100644 --- a/collectors/python.d.plugin/sensors/README.md +++ b/collectors/python.d.plugin/sensors/README.md @@ -26,6 +26,8 @@ There have been reports from users that on certain servers, ACPI ring buffer err We are tracking such cases in issue [#827](https://github.com/netdata/netdata/issues/827). Please join this discussion for help. +When `lm-sensors` doesn't work on your device (e.g. for RPi temperatures), use [the legacy bash collector](https://learn.netdata.cloud/docs/agent/collectors/charts.d.plugin/sensors) + --- diff --git a/collectors/python.d.plugin/sensors/sensors.chart.py b/collectors/python.d.plugin/sensors/sensors.chart.py index f089e147a..701bf6414 100644 --- a/collectors/python.d.plugin/sensors/sensors.chart.py +++ b/collectors/python.d.plugin/sensors/sensors.chart.py @@ -3,6 +3,8 @@ # Author: Pawel Krupa (paulfantom) # SPDX-License-Identifier: GPL-3.0-or-later +from collections import defaultdict + from bases.FrameworkServices.SimpleService import SimpleService from third_party import lm_sensors as sensors @@ -77,11 +79,11 @@ TYPE_MAP = { 4: 'energy', 5: 'current', 6: 'humidity', - 7: 'max_main', - 16: 'vid', - 17: 'intrusion', - 18: 'max_other', - 24: 'beep_enable' + # 7: 'max_main', + # 16: 'vid', + # 17: 'intrusion', + # 18: 'max_other', + # 24: 'beep_enable' } @@ -91,64 +93,73 @@ class Service(SimpleService): self.order = list() self.definitions = dict() self.chips = configuration.get('chips') + self.priority = 60000 def get_data(self): - data = dict() + seen, data = dict(), dict() try: for chip in sensors.ChipIterator(): - prefix = sensors.chip_snprintf_name(chip) - for feature in sensors.FeatureIterator(chip): - sfi = sensors.SubFeatureIterator(chip, feature) - val = None - for sf in sfi: - try: - val = sensors.get_value(chip, sf.number) - break - except sensors.SensorsError: - continue - if val is None: + chip_name = sensors.chip_snprintf_name(chip) + seen[chip_name] = defaultdict(list) + + for feat in sensors.FeatureIterator(chip): + if feat.type not in TYPE_MAP: + continue + + feat_type = TYPE_MAP[feat.type] + feat_name = str(feat.name.decode()) + feat_label = sensors.get_label(chip, feat) + feat_limits = LIMITS.get(feat_type) + sub_feat = next(sensors.SubFeatureIterator(chip, feat)) # current value + + if not sub_feat: + continue + + try: + v = sensors.get_value(chip, sub_feat.number) + except sensors.SensorsError: + continue + + if v is None: + continue + + seen[chip_name][feat_type].append((feat_name, feat_label)) + + if feat_limits and (v < feat_limits[0] or v > feat_limits[1]): continue - type_name = TYPE_MAP[feature.type] - if type_name in LIMITS: - limit = LIMITS[type_name] - if val < limit[0] or val > limit[1]: - continue - data[prefix + '_' + str(feature.name.decode())] = int(val * 1000) + + data[chip_name + '_' + feat_name] = int(v * 1000) + except sensors.SensorsError as error: self.error(error) return None + self.update_sensors_charts(seen) + return data or None - def create_definitions(self): - for sensor in ORDER: - for chip in sensors.ChipIterator(): - chip_name = sensors.chip_snprintf_name(chip) - if self.chips and not any([chip_name.startswith(ex) for ex in self.chips]): + def update_sensors_charts(self, seen): + for chip_name, feat in seen.items(): + if self.chips and not any([chip_name.startswith(ex) for ex in self.chips]): + continue + + for feat_type, sub_feat in feat.items(): + if feat_type not in ORDER or feat_type not in CHARTS: + continue + + chart_id = '{}_{}'.format(chip_name, feat_type) + if chart_id in self.charts: continue - for feature in sensors.FeatureIterator(chip): - sfi = sensors.SubFeatureIterator(chip, feature) - vals = list() - for sf in sfi: - try: - vals.append(sensors.get_value(chip, sf.number)) - except sensors.SensorsError as error: - self.error('{0}: {1}'.format(sf.name, error)) - continue - if not vals or (vals[0] == 0 and feature.type != 1): - continue - if TYPE_MAP[feature.type] == sensor: - # create chart - name = chip_name + '_' + TYPE_MAP[feature.type] - if name not in self.order: - self.order.append(name) - chart_def = list(CHARTS[sensor]['options']) - self.definitions[name] = {'options': chart_def} - self.definitions[name]['lines'] = [] - line = list(CHARTS[sensor]['lines'][0]) - line[0] = chip_name + '_' + str(feature.name.decode()) - line[1] = sensors.get_label(chip, feature) - self.definitions[name]['lines'].append(line) + + params = [chart_id] + list(CHARTS[feat_type]['options']) + new_chart = self.charts.add_chart(params) + new_chart.params['priority'] = self.get_chart_priority(feat_type) + + for name, label in sub_feat: + lines = list(CHARTS[feat_type]['lines'][0]) + lines[0] = chip_name + '_' + name + lines[1] = label + new_chart.add_dimension(lines) def check(self): try: @@ -157,6 +168,12 @@ class Service(SimpleService): self.error(error) return False - self.create_definitions() + self.priority = self.charts.priority + + return bool(self.get_data() and self.charts) - return bool(self.get_data()) + def get_chart_priority(self, feat_type): + for i, v in enumerate(ORDER): + if v == feat_type: + return self.priority + i + return self.priority diff --git a/collectors/python.d.plugin/tor/README.md b/collectors/python.d.plugin/tor/README.md index 3e7b8997a..b57d77c08 100644 --- a/collectors/python.d.plugin/tor/README.md +++ b/collectors/python.d.plugin/tor/README.md @@ -41,10 +41,12 @@ priority : 60000 local_tcp: name: 'local' control_port: 9051 + password: # if required local_socket: name: 'local' control_port: '/var/run/tor/control' + password: # if required ``` ### prerequisite diff --git a/collectors/python.d.plugin/tor/tor.conf b/collectors/python.d.plugin/tor/tor.conf index 91b517a62..bf09b21fe 100644 --- a/collectors/python.d.plugin/tor/tor.conf +++ b/collectors/python.d.plugin/tor/tor.conf @@ -71,7 +71,9 @@ # local_tcp: # name: 'local' # control_port: 9051 +# password: # # local_socket: # name: 'local' # control_port: '/var/run/tor/control' +# password: diff --git a/collectors/statsd.plugin/statsd.c b/collectors/statsd.plugin/statsd.c index fef4206bc..67d7ed2e2 100644 --- a/collectors/statsd.plugin/statsd.c +++ b/collectors/statsd.plugin/statsd.c @@ -20,15 +20,10 @@ // -------------------------------------------------------------------------------------- -// #define STATSD_MULTITHREADED 1 - -#ifdef STATSD_MULTITHREADED // DO NOT ENABLE MULTITHREADING - IT IS NOT WELL TESTED -#define STATSD_DICTIONARY_OPTIONS DICTIONARY_FLAG_DONT_OVERWRITE_VALUE|DICTIONARY_FLAG_ADD_IN_FRONT -#else -#define STATSD_DICTIONARY_OPTIONS DICTIONARY_FLAG_DONT_OVERWRITE_VALUE|DICTIONARY_FLAG_ADD_IN_FRONT|DICTIONARY_FLAG_SINGLE_THREADED -#endif +// #define STATSD_MULTITHREADED 1 +#define STATSD_DICTIONARY_OPTIONS (DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_ADD_IN_FRONT) #define STATSD_DECIMAL_DETAIL 1000 // floating point values get multiplied by this, with the same divisor // -------------------------------------------------------------------------------------------------------------------- @@ -101,6 +96,7 @@ typedef enum statsd_metric_options { STATSD_METRIC_OPTION_CHECKED = 0x00000040, // set when the charting thread checks this metric for use in charts (its usefulness) STATSD_METRIC_OPTION_USEFUL = 0x00000080, // set when the charting thread finds the metric useful (i.e. used in a chart) STATSD_METRIC_OPTION_COLLECTION_FULL_LOGGED = 0x00000100, // set when the collection is full for this metric + STATSD_METRIC_OPTION_UPDATED_CHART_METADATA = 0x00000200, // set when the private chart metadata have been updated via tags } STATS_METRIC_OPTIONS; typedef enum statsd_metric_type { @@ -192,6 +188,7 @@ typedef struct statsd_app_chart_dimension { collected_number multiplier; // the multiplier of the dimension collected_number divisor; // the divisor of the dimension RRDDIM_FLAGS flags; // the RRDDIM flags for this dimension + RRDDIM_OPTIONS options; // the RRDDIM options for this dimension STATSD_APP_CHART_DIM_VALUE_TYPE value_type; // which value to use of the source metric @@ -371,9 +368,10 @@ static struct statsd { // -------------------------------------------------------------------------------------------------------------------- // statsd index management - add/find metrics -static void dictionary_metric_insert_callback(const char *name, void *value, void *data) { +static void dictionary_metric_insert_callback(const DICTIONARY_ITEM *item, void *value, void *data) { STATSD_INDEX *index = (STATSD_INDEX *)data; STATSD_METRIC *m = (STATSD_METRIC *)value; + const char *name = dictionary_acquired_item_name(item); debug(D_STATSD, "Creating new %s metric '%s'", index->name, name); @@ -390,9 +388,9 @@ static void dictionary_metric_insert_callback(const char *name, void *value, voi __atomic_fetch_add(&index->metrics, 1, __ATOMIC_RELAXED); } -static void dictionary_metric_delete_callback(const char *name, void *value, void *data) { +static void dictionary_metric_delete_callback(const DICTIONARY_ITEM *item, void *value, void *data) { (void)data; // STATSD_INDEX *index = (STATSD_INDEX *)data; - (void)name; + (void)item; STATSD_METRIC *m = (STATSD_METRIC *)value; if(m->type == STATSD_METRIC_TYPE_HISTOGRAM || m->type == STATSD_METRIC_TYPE_TIMER) { @@ -416,7 +414,7 @@ static inline STATSD_METRIC *statsd_find_or_add_metric(STATSD_INDEX *index, cons // no locks here, go faster // this will call the dictionary_metric_insert_callback() if an item // is inserted, otherwise it will return the existing one. - // We used the flag DICTIONARY_FLAG_DONT_OVERWRITE_VALUE to support this. + // We used the flag DICT_OPTION_DONT_OVERWRITE_VALUE to support this. STATSD_METRIC *m = dictionary_set(index->dict, name, NULL, sizeof(STATSD_METRIC)); #endif @@ -572,8 +570,8 @@ static inline void statsd_process_histogram_or_timer(STATSD_METRIC *m, const cha #define statsd_process_timer(m, value, sampling) statsd_process_histogram_or_timer(m, value, sampling, "timer") #define statsd_process_histogram(m, value, sampling) statsd_process_histogram_or_timer(m, value, sampling, "histogram") -static void dictionary_metric_set_value_insert_callback(const char *name, void *value, void *data) { - (void)name; +static void dictionary_metric_set_value_insert_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + (void)item; (void)value; STATSD_METRIC *m = (STATSD_METRIC *)data; m->set.unique++; @@ -617,8 +615,8 @@ static inline void statsd_process_set(STATSD_METRIC *m, const char *value) { } } -static void dictionary_metric_dict_value_insert_callback(const char *name, void *value, void *data) { - (void)name; +static void dictionary_metric_dict_value_insert_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + (void)item; (void)value; STATSD_METRIC *m = (STATSD_METRIC *)data; m->dictionary.unique++; @@ -772,14 +770,20 @@ static void statsd_process_metric(const char *name, const char *value, const cha statsd_parse_field_trim(tagvalue, tagvalue_end); if(tagkey && *tagkey && tagvalue && *tagvalue) { - if (!m->units && strcmp(tagkey, "units") == 0) + if (strcmp(tagkey, "units") == 0 && (!m->units || strcmp(m->units, tagvalue) != 0)) { m->units = strdupz(tagvalue); + m->options |= STATSD_METRIC_OPTION_UPDATED_CHART_METADATA; + } - if (!m->dimname && strcmp(tagkey, "name") == 0) + if (strcmp(tagkey, "name") == 0 && (!m->dimname || strcmp(m->dimname, tagvalue) != 0)) { m->dimname = strdupz(tagvalue); + m->options |= STATSD_METRIC_OPTION_UPDATED_CHART_METADATA; + } - if (!m->family && strcmp(tagkey, "family") == 0) + if (strcmp(tagkey, "family") == 0 && (!m->family || strcmp(m->family, tagvalue) != 0)) { m->family = strdupz(tagvalue); + m->options |= STATSD_METRIC_OPTION_UPDATED_CHART_METADATA; + } } } } @@ -1209,6 +1213,7 @@ static STATSD_APP_CHART_DIM *add_dimension_to_app_chart( , collected_number multiplier , collected_number divisor , RRDDIM_FLAGS flags + , RRDDIM_OPTIONS options , STATSD_APP_CHART_DIM_VALUE_TYPE value_type ) { STATSD_APP_CHART_DIM *dim = callocz(sizeof(STATSD_APP_CHART_DIM), 1); @@ -1221,6 +1226,7 @@ static STATSD_APP_CHART_DIM *add_dimension_to_app_chart( dim->divisor = divisor; dim->value_type = value_type; dim->flags = flags; + dim->options = options; if(!dim->multiplier) dim->multiplier = 1; @@ -1323,7 +1329,7 @@ static int statsd_readfile(const char *filename, STATSD_APP *app, STATSD_APP_CHA else if(app) { if(!strcmp(s, "dictionary")) { if(!app->dict) - app->dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + app->dict = dictionary_create(DICT_OPTION_SINGLE_THREADED); dict = app->dict; } @@ -1417,7 +1423,10 @@ static int statsd_readfile(const char *filename, STATSD_APP *app, STATSD_APP_CHA app->default_options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; } else if (!strcmp(name, "memory mode")) { - app->rrd_memory_mode = rrd_memory_mode_id(value); + // this is not supported anymore + // with the implementation of storage engines, all charts have the same storage engine always + // app->rrd_memory_mode = rrd_memory_mode_id(value); + ; } else if (!strcmp(name, "history")) { app->rrd_history_entries = atol(value); @@ -1462,29 +1471,30 @@ static int statsd_readfile(const char *filename, STATSD_APP *app, STATSD_APP_CHA } else if (!strcmp(name, "dimension")) { // metric [name [type [multiplier [divisor]]]] - char *words[10]; - pluginsd_split_words(value, words, 10, NULL, NULL, 0); + char *words[10] = { NULL }; + size_t num_words = pluginsd_split_words(value, words, 10, NULL, NULL, 0); int pattern = 0; size_t i = 0; - char *metric_name = words[i++]; + char *metric_name = get_word(words, num_words, i++); if(strcmp(metric_name, "pattern") == 0) { - metric_name = words[i++]; + metric_name = get_word(words, num_words, i++); pattern = 1; } - char *dim_name = words[i++]; - char *type = words[i++]; - char *multiplier = words[i++]; - char *divisor = words[i++]; - char *options = words[i++]; + char *dim_name = get_word(words, num_words, i++); + char *type = get_word(words, num_words, i++); + char *multiplier = get_word(words, num_words, i++); + char *divisor = get_word(words, num_words, i++); + char *opts = get_word(words, num_words, i++); RRDDIM_FLAGS flags = RRDDIM_FLAG_NONE; - if(options && *options) { - if(strstr(options, "hidden") != NULL) flags |= RRDDIM_FLAG_HIDDEN; - if(strstr(options, "noreset") != NULL) flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS; - if(strstr(options, "nooverflow") != NULL) flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS; + RRDDIM_OPTIONS options = RRDDIM_OPTION_NONE; + if(opts && *opts) { + if(strstr(opts, "hidden") != NULL) options |= RRDDIM_OPTION_HIDDEN; + if(strstr(opts, "noreset") != NULL) options |= RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS; + if(strstr(opts, "nooverflow") != NULL) options |= RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS; } if(!pattern) { @@ -1510,7 +1520,8 @@ static int statsd_readfile(const char *filename, STATSD_APP *app, STATSD_APP_CHA , (multiplier && *multiplier)?str2l(multiplier):1 , (divisor && *divisor)?str2l(divisor):1 , flags - , string2valuetype(type, line, filename) + , + options, string2valuetype(type, line, filename) ); if(pattern) @@ -1600,7 +1611,9 @@ static inline RRDSET *statsd_private_rrdset_create( , int update_every , RRDSET_TYPE chart_type ) { - statsd.private_charts++; + if(!m->st) + statsd.private_charts++; + RRDSET *st = rrdset_create_custom( localhost // host , type // type @@ -1630,7 +1643,9 @@ static inline RRDSET *statsd_private_rrdset_create( static inline void statsd_private_chart_gauge(STATSD_METRIC *m) { debug(D_STATSD, "updating private chart for gauge metric '%s'", m->name); - if(unlikely(!m->st)) { + if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) { + m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA; + char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1], context[RRD_ID_LENGTH_MAX + 1]; statsd_get_metric_type_and_id(m, type, id, context, "gauge", RRD_ID_LENGTH_MAX); @@ -1656,7 +1671,6 @@ static inline void statsd_private_chart_gauge(STATSD_METRIC *m) { if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT) m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(m->st); rrddim_set_by_pointer(m->st, m->rd_value, m->last); @@ -1669,7 +1683,9 @@ static inline void statsd_private_chart_gauge(STATSD_METRIC *m) { static inline void statsd_private_chart_counter_or_meter(STATSD_METRIC *m, const char *dim, const char *family) { debug(D_STATSD, "updating private chart for %s metric '%s'", dim, m->name); - if(unlikely(!m->st)) { + if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) { + m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA; + char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1], context[RRD_ID_LENGTH_MAX + 1]; statsd_get_metric_type_and_id(m, type, id, context, dim, RRD_ID_LENGTH_MAX); @@ -1695,7 +1711,6 @@ static inline void statsd_private_chart_counter_or_meter(STATSD_METRIC *m, const if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT) m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(m->st); rrddim_set_by_pointer(m->st, m->rd_value, m->last); @@ -1708,7 +1723,9 @@ static inline void statsd_private_chart_counter_or_meter(STATSD_METRIC *m, const static inline void statsd_private_chart_set(STATSD_METRIC *m) { debug(D_STATSD, "updating private chart for set metric '%s'", m->name); - if(unlikely(!m->st)) { + if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) { + m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA; + char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1], context[RRD_ID_LENGTH_MAX + 1]; statsd_get_metric_type_and_id(m, type, id, context, "set", RRD_ID_LENGTH_MAX); @@ -1734,7 +1751,6 @@ static inline void statsd_private_chart_set(STATSD_METRIC *m) { if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT) m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(m->st); rrddim_set_by_pointer(m->st, m->rd_value, m->last); @@ -1747,7 +1763,9 @@ static inline void statsd_private_chart_set(STATSD_METRIC *m) { static inline void statsd_private_chart_dictionary(STATSD_METRIC *m) { debug(D_STATSD, "updating private chart for dictionary metric '%s'", m->name); - if(unlikely(!m->st)) { + if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) { + m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA; + char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1], context[RRD_ID_LENGTH_MAX + 1]; statsd_get_metric_type_and_id(m, type, id, context, "dictionary", RRD_ID_LENGTH_MAX); @@ -1771,11 +1789,10 @@ static inline void statsd_private_chart_dictionary(STATSD_METRIC *m) { if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT) m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(m->st); STATSD_METRIC_DICTIONARY_ITEM *t; dfe_start_read(m->dictionary.dict, t) { - if (!t->rd) t->rd = rrddim_add(m->st, t_name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + if (!t->rd) t->rd = rrddim_add(m->st, t_dfe.name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(m->st, t->rd, (collected_number)t->count); } dfe_done(t); @@ -1789,7 +1806,9 @@ static inline void statsd_private_chart_dictionary(STATSD_METRIC *m) { static inline void statsd_private_chart_timer_or_histogram(STATSD_METRIC *m, const char *dim, const char *family, const char *units) { debug(D_STATSD, "updating private chart for %s metric '%s'", dim, m->name); - if(unlikely(!m->st)) { + if(unlikely(!m->st || m->options & STATSD_METRIC_OPTION_UPDATED_CHART_METADATA)) { + m->options &= ~STATSD_METRIC_OPTION_UPDATED_CHART_METADATA; + char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1], context[RRD_ID_LENGTH_MAX + 1]; statsd_get_metric_type_and_id(m, type, id, context, dim, RRD_ID_LENGTH_MAX); @@ -1821,7 +1840,6 @@ static inline void statsd_private_chart_timer_or_histogram(STATSD_METRIC *m, con if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT) m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(m->st); rrddim_set_by_pointer(m->st, m->histogram.ext->rd_min, m->histogram.ext->last_min); rrddim_set_by_pointer(m->st, m->histogram.ext->rd_max, m->histogram.ext->last_max); @@ -2130,6 +2148,7 @@ static inline void check_if_metric_is_for_app(STATSD_INDEX *index, STATSD_METRIC , dim->multiplier , dim->divisor , dim->flags + , dim->options , dim->value_type ); @@ -2186,11 +2205,13 @@ static inline RRDDIM *statsd_add_dim_to_app_chart(STATSD_APP *app, STATSD_APP_CH dim->rd = rrddim_add(chart->st, metric, dim->name, dim->multiplier, dim->divisor, dim->algorithm); if(dim->flags != RRDDIM_FLAG_NONE) dim->rd->flags |= dim->flags; + if(dim->options != RRDDIM_OPTION_NONE) dim->rd->options |= dim->options; return dim->rd; } dim->rd = rrddim_add(chart->st, dim->metric, dim->name, dim->multiplier, dim->divisor, dim->algorithm); if(dim->flags != RRDDIM_FLAG_NONE) dim->rd->flags |= dim->flags; + if(dim->options != RRDDIM_OPTION_NONE) dim->rd->options |= dim->options; return dim->rd; } @@ -2219,7 +2240,6 @@ static inline void statsd_update_app_chart(STATSD_APP *app, STATSD_APP_CHART *ch rrdset_flag_set(chart->st, RRDSET_FLAG_STORE_FIRST); // rrdset_flag_set(chart->st, RRDSET_FLAG_DEBUG); } - else rrdset_next(chart->st); STATSD_APP_CHART_DIM *dim; for(dim = chart->dimensions; dim ;dim = dim->next) { @@ -2228,7 +2248,7 @@ static inline void statsd_update_app_chart(STATSD_APP *app, STATSD_APP_CHART *ch statsd_add_dim_to_app_chart(app, chart, dim); if (unlikely(dim->value_ptr)) { - debug(D_STATSD, "updating dimension '%s' (%s) of chart '%s' (%s) for app '%s' with value " COLLECTED_NUMBER_FORMAT, dim->name, dim->rd->id, chart->id, chart->st->id, app->name, *dim->value_ptr); + debug(D_STATSD, "updating dimension '%s' (%s) of chart '%s' (%s) for app '%s' with value " COLLECTED_NUMBER_FORMAT, dim->name, rrddim_id(dim->rd), chart->id, rrdset_id(chart->st), app->name, *dim->value_ptr); rrddim_set_by_pointer(chart->st, dim->rd, *dim->value_ptr); } } @@ -2523,173 +2543,209 @@ void *statsd_main(void *ptr) { // ---------------------------------------------------------------------------------------------------------------- // statsd monitoring charts - RRDSET *st_metrics = rrdset_create_localhost( - "netdata" - , "statsd_metrics" - , NULL - , "statsd" - , NULL - , "Metrics in the netdata statsd database" - , "metrics" - , PLUGIN_STATSD_NAME - , "stats" - , 132010 - , statsd.update_every - , RRDSET_TYPE_STACKED - ); - RRDDIM *rd_metrics_gauge = rrddim_add(st_metrics, "gauges", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_metrics_counter = rrddim_add(st_metrics, "counters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_metrics_timer = rrddim_add(st_metrics, "timers", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_metrics_meter = rrddim_add(st_metrics, "meters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_metrics_histogram = rrddim_add(st_metrics, "histograms", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_metrics_set = rrddim_add(st_metrics, "sets", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_metrics_dictionary= rrddim_add(st_metrics, "dictionaries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - - RRDSET *st_useful_metrics = rrdset_create_localhost( - "netdata" - , "statsd_useful_metrics" - , NULL - , "statsd" - , NULL - , "Useful metrics in the netdata statsd database" - , "metrics" - , PLUGIN_STATSD_NAME - , "stats" - , 132010 - , statsd.update_every - , RRDSET_TYPE_STACKED - ); - RRDDIM *rd_useful_metrics_gauge = rrddim_add(st_useful_metrics, "gauges", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_useful_metrics_counter = rrddim_add(st_useful_metrics, "counters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_useful_metrics_timer = rrddim_add(st_useful_metrics, "timers", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_useful_metrics_meter = rrddim_add(st_useful_metrics, "meters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_useful_metrics_histogram = rrddim_add(st_useful_metrics, "histograms", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_useful_metrics_set = rrddim_add(st_useful_metrics, "sets", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDDIM *rd_useful_metrics_dictionary= rrddim_add(st_useful_metrics, "dictionaries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - - RRDSET *st_events = rrdset_create_localhost( - "netdata" - , "statsd_events" - , NULL - , "statsd" - , NULL - , "Events processed by the netdata statsd server" - , "events/s" - , PLUGIN_STATSD_NAME - , "stats" - , 132011 - , statsd.update_every - , RRDSET_TYPE_STACKED - ); - RRDDIM *rd_events_gauge = rrddim_add(st_events, "gauges", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_events_counter = rrddim_add(st_events, "counters", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_events_timer = rrddim_add(st_events, "timers", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_events_meter = rrddim_add(st_events, "meters", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_events_histogram = rrddim_add(st_events, "histograms", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_events_set = rrddim_add(st_events, "sets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_events_dictionary= rrddim_add(st_events, "dictionaries", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_events_unknown = rrddim_add(st_events, "unknown", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_events_errors = rrddim_add(st_events, "errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - - RRDSET *st_reads = rrdset_create_localhost( - "netdata" - , "statsd_reads" - , NULL - , "statsd" - , NULL - , "Read operations made by the netdata statsd server" - , "reads/s" - , PLUGIN_STATSD_NAME - , "stats" - , 132012 - , statsd.update_every - , RRDSET_TYPE_STACKED - ); - RRDDIM *rd_reads_tcp = rrddim_add(st_reads, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_reads_udp = rrddim_add(st_reads, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - - RRDSET *st_bytes = rrdset_create_localhost( - "netdata" - , "statsd_bytes" - , NULL - , "statsd" - , NULL - , "Bytes read by the netdata statsd server" - , "kilobits/s" - , PLUGIN_STATSD_NAME - , "stats" - , 132013 - , statsd.update_every - , RRDSET_TYPE_STACKED - ); - RRDDIM *rd_bytes_tcp = rrddim_add(st_bytes, "tcp", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_bytes_udp = rrddim_add(st_bytes, "udp", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); - - RRDSET *st_packets = rrdset_create_localhost( - "netdata" - , "statsd_packets" - , NULL - , "statsd" - , NULL - , "Network packets processed by the netdata statsd server" - , "packets/s" - , PLUGIN_STATSD_NAME - , "stats" - , 132014 - , statsd.update_every - , RRDSET_TYPE_STACKED - ); - RRDDIM *rd_packets_tcp = rrddim_add(st_packets, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_packets_udp = rrddim_add(st_packets, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - - RRDSET *st_tcp_connects = rrdset_create_localhost( - "netdata" - , "tcp_connects" - , NULL - , "statsd" - , NULL - , "statsd server TCP connects and disconnects" - , "events" - , PLUGIN_STATSD_NAME - , "stats" - , 132015 - , statsd.update_every - , RRDSET_TYPE_LINE - ); - RRDDIM *rd_tcp_connects = rrddim_add(st_tcp_connects, "connects", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - RRDDIM *rd_tcp_disconnects = rrddim_add(st_tcp_connects, "disconnects", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - - RRDSET *st_tcp_connected = rrdset_create_localhost( - "netdata" - , "tcp_connected" - , NULL - , "statsd" - , NULL - , "statsd server TCP connected sockets" - , "sockets" - , PLUGIN_STATSD_NAME - , "stats" - , 132016 - , statsd.update_every - , RRDSET_TYPE_LINE - ); - RRDDIM *rd_tcp_connected = rrddim_add(st_tcp_connected, "connected", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - - RRDSET *st_pcharts = rrdset_create_localhost( - "netdata" - , "private_charts" - , NULL - , "statsd" - , NULL - , "Private metric charts created by the netdata statsd server" - , "charts" - , PLUGIN_STATSD_NAME - , "stats" - , 132020 - , statsd.update_every - , RRDSET_TYPE_AREA - ); - RRDDIM *rd_pcharts = rrddim_add(st_pcharts, "charts", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + RRDSET *st_metrics = NULL; + RRDDIM *rd_metrics_gauge = NULL; + RRDDIM *rd_metrics_counter = NULL; + RRDDIM *rd_metrics_timer = NULL; + RRDDIM *rd_metrics_meter = NULL; + RRDDIM *rd_metrics_histogram = NULL; + RRDDIM *rd_metrics_set = NULL; + RRDDIM *rd_metrics_dictionary = NULL; + RRDSET *st_useful_metrics = NULL; + RRDDIM *rd_useful_metrics_gauge = NULL; + RRDDIM *rd_useful_metrics_counter = NULL; + RRDDIM *rd_useful_metrics_timer = NULL; + RRDDIM *rd_useful_metrics_meter = NULL; + RRDDIM *rd_useful_metrics_histogram = NULL; + RRDDIM *rd_useful_metrics_set = NULL; + RRDDIM *rd_useful_metrics_dictionary = NULL; + RRDSET *st_events = NULL; + RRDDIM *rd_events_gauge = NULL; + RRDDIM *rd_events_counter = NULL; + RRDDIM *rd_events_timer = NULL; + RRDDIM *rd_events_meter = NULL; + RRDDIM *rd_events_histogram = NULL; + RRDDIM *rd_events_set = NULL; + RRDDIM *rd_events_dictionary = NULL; + RRDDIM *rd_events_unknown = NULL; + RRDDIM *rd_events_errors = NULL; + RRDSET *st_reads = NULL; + RRDDIM *rd_reads_tcp = NULL; + RRDDIM *rd_reads_udp = NULL; + RRDSET *st_bytes = NULL; + RRDDIM *rd_bytes_tcp = NULL; + RRDDIM *rd_bytes_udp = NULL; + RRDSET *st_packets = NULL; + RRDDIM *rd_packets_tcp = NULL; + RRDDIM *rd_packets_udp = NULL; + RRDSET *st_tcp_connects = NULL; + RRDDIM *rd_tcp_connects = NULL; + RRDDIM *rd_tcp_disconnects = NULL; + RRDSET *st_tcp_connected = NULL; + RRDDIM *rd_tcp_connected = NULL; + RRDSET *st_pcharts = NULL; + RRDDIM *rd_pcharts = NULL; + + if(global_statistics_enabled) { + st_metrics = rrdset_create_localhost( + "netdata", + "statsd_metrics", + NULL, + "statsd", + NULL, + "Metrics in the netdata statsd database", + "metrics", + PLUGIN_STATSD_NAME, + "stats", + 132010, + statsd.update_every, + RRDSET_TYPE_STACKED); + rd_metrics_gauge = rrddim_add(st_metrics, "gauges", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_metrics_counter = rrddim_add(st_metrics, "counters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_metrics_timer = rrddim_add(st_metrics, "timers", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_metrics_meter = rrddim_add(st_metrics, "meters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_metrics_histogram = rrddim_add(st_metrics, "histograms", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_metrics_set = rrddim_add(st_metrics, "sets", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_metrics_dictionary = rrddim_add(st_metrics, "dictionaries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + st_useful_metrics = rrdset_create_localhost( + "netdata", + "statsd_useful_metrics", + NULL, + "statsd", + NULL, + "Useful metrics in the netdata statsd database", + "metrics", + PLUGIN_STATSD_NAME, + "stats", + 132010, + statsd.update_every, + RRDSET_TYPE_STACKED); + rd_useful_metrics_gauge = rrddim_add(st_useful_metrics, "gauges", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_useful_metrics_counter = rrddim_add(st_useful_metrics, "counters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_useful_metrics_timer = rrddim_add(st_useful_metrics, "timers", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_useful_metrics_meter = rrddim_add(st_useful_metrics, "meters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_useful_metrics_histogram = rrddim_add(st_useful_metrics, "histograms", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_useful_metrics_set = rrddim_add(st_useful_metrics, "sets", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_useful_metrics_dictionary = rrddim_add(st_useful_metrics, "dictionaries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + st_events = rrdset_create_localhost( + "netdata", + "statsd_events", + NULL, + "statsd", + NULL, + "Events processed by the netdata statsd server", + "events/s", + PLUGIN_STATSD_NAME, + "stats", + 132011, + statsd.update_every, + RRDSET_TYPE_STACKED); + rd_events_gauge = rrddim_add(st_events, "gauges", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_events_counter = rrddim_add(st_events, "counters", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_events_timer = rrddim_add(st_events, "timers", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_events_meter = rrddim_add(st_events, "meters", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_events_histogram = rrddim_add(st_events, "histograms", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_events_set = rrddim_add(st_events, "sets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_events_dictionary = rrddim_add(st_events, "dictionaries", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_events_unknown = rrddim_add(st_events, "unknown", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_events_errors = rrddim_add(st_events, "errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + st_reads = rrdset_create_localhost( + "netdata", + "statsd_reads", + NULL, + "statsd", + NULL, + "Read operations made by the netdata statsd server", + "reads/s", + PLUGIN_STATSD_NAME, + "stats", + 132012, + statsd.update_every, + RRDSET_TYPE_STACKED); + rd_reads_tcp = rrddim_add(st_reads, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_reads_udp = rrddim_add(st_reads, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + st_bytes = rrdset_create_localhost( + "netdata", + "statsd_bytes", + NULL, + "statsd", + NULL, + "Bytes read by the netdata statsd server", + "kilobits/s", + PLUGIN_STATSD_NAME, + "stats", + 132013, + statsd.update_every, + RRDSET_TYPE_STACKED); + rd_bytes_tcp = rrddim_add(st_bytes, "tcp", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + rd_bytes_udp = rrddim_add(st_bytes, "udp", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + + st_packets = rrdset_create_localhost( + "netdata", + "statsd_packets", + NULL, + "statsd", + NULL, + "Network packets processed by the netdata statsd server", + "packets/s", + PLUGIN_STATSD_NAME, + "stats", + 132014, + statsd.update_every, + RRDSET_TYPE_STACKED); + rd_packets_tcp = rrddim_add(st_packets, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_packets_udp = rrddim_add(st_packets, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + st_tcp_connects = rrdset_create_localhost( + "netdata", + "tcp_connects", + NULL, + "statsd", + NULL, + "statsd server TCP connects and disconnects", + "events", + PLUGIN_STATSD_NAME, + "stats", + 132015, + statsd.update_every, + RRDSET_TYPE_LINE); + rd_tcp_connects = rrddim_add(st_tcp_connects, "connects", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_tcp_disconnects = rrddim_add(st_tcp_connects, "disconnects", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + st_tcp_connected = rrdset_create_localhost( + "netdata", + "tcp_connected", + NULL, + "statsd", + NULL, + "statsd server TCP connected sockets", + "sockets", + PLUGIN_STATSD_NAME, + "stats", + 132016, + statsd.update_every, + RRDSET_TYPE_LINE); + rd_tcp_connected = rrddim_add(st_tcp_connected, "connected", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + st_pcharts = rrdset_create_localhost( + "netdata", + "private_charts", + NULL, + "statsd", + NULL, + "Private metric charts created by the netdata statsd server", + "charts", + PLUGIN_STATSD_NAME, + "stats", + 132020, + statsd.update_every, + RRDSET_TYPE_AREA); + rd_pcharts = rrddim_add(st_pcharts, "charts", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } // ---------------------------------------------------------------------------------------------------------------- // statsd thread to turn metrics into charts @@ -2699,7 +2755,7 @@ void *statsd_main(void *ptr) { heartbeat_init(&hb); while(!netdata_exit) { worker_is_idle(); - usec_t hb_dt = heartbeat_next(&hb, step); + heartbeat_next(&hb, step); worker_is_busy(WORKER_STATSD_FLUSH_GAUGES); statsd_flush_index_metrics(&statsd.gauges, statsd_flush_gauge); @@ -2728,68 +2784,58 @@ void *statsd_main(void *ptr) { if(unlikely(netdata_exit)) break; - if(likely(hb_dt)) { - rrdset_next(st_metrics); - rrdset_next(st_useful_metrics); - rrdset_next(st_events); - rrdset_next(st_reads); - rrdset_next(st_bytes); - rrdset_next(st_packets); - rrdset_next(st_tcp_connects); - rrdset_next(st_tcp_connected); - rrdset_next(st_pcharts); + if(global_statistics_enabled) { + rrddim_set_by_pointer(st_metrics, rd_metrics_gauge, (collected_number)statsd.gauges.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_counter, (collected_number)statsd.counters.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_timer, (collected_number)statsd.timers.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_meter, (collected_number)statsd.meters.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_histogram, (collected_number)statsd.histograms.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_set, (collected_number)statsd.sets.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_dictionary, (collected_number)statsd.dictionaries.metrics); + rrdset_done(st_metrics); + + rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_gauge, (collected_number)statsd.gauges.useful); + rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_counter, (collected_number)statsd.counters.useful); + rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_timer, (collected_number)statsd.timers.useful); + rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_meter, (collected_number)statsd.meters.useful); + rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_histogram, (collected_number)statsd.histograms.useful); + rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_set, (collected_number)statsd.sets.useful); + rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_dictionary, (collected_number)statsd.dictionaries.useful); + rrdset_done(st_useful_metrics); + + rrddim_set_by_pointer(st_events, rd_events_gauge, (collected_number)statsd.gauges.events); + rrddim_set_by_pointer(st_events, rd_events_counter, (collected_number)statsd.counters.events); + rrddim_set_by_pointer(st_events, rd_events_timer, (collected_number)statsd.timers.events); + rrddim_set_by_pointer(st_events, rd_events_meter, (collected_number)statsd.meters.events); + rrddim_set_by_pointer(st_events, rd_events_histogram, (collected_number)statsd.histograms.events); + rrddim_set_by_pointer(st_events, rd_events_set, (collected_number)statsd.sets.events); + rrddim_set_by_pointer(st_events, rd_events_dictionary, (collected_number)statsd.dictionaries.events); + rrddim_set_by_pointer(st_events, rd_events_unknown, (collected_number)statsd.unknown_types); + rrddim_set_by_pointer(st_events, rd_events_errors, (collected_number)statsd.socket_errors); + rrdset_done(st_events); + + rrddim_set_by_pointer(st_reads, rd_reads_tcp, (collected_number)statsd.tcp_socket_reads); + rrddim_set_by_pointer(st_reads, rd_reads_udp, (collected_number)statsd.udp_socket_reads); + rrdset_done(st_reads); + + rrddim_set_by_pointer(st_bytes, rd_bytes_tcp, (collected_number)statsd.tcp_bytes_read); + rrddim_set_by_pointer(st_bytes, rd_bytes_udp, (collected_number)statsd.udp_bytes_read); + rrdset_done(st_bytes); + + rrddim_set_by_pointer(st_packets, rd_packets_tcp, (collected_number)statsd.tcp_packets_received); + rrddim_set_by_pointer(st_packets, rd_packets_udp, (collected_number)statsd.udp_packets_received); + rrdset_done(st_packets); + + rrddim_set_by_pointer(st_tcp_connects, rd_tcp_connects, (collected_number)statsd.tcp_socket_connects); + rrddim_set_by_pointer(st_tcp_connects, rd_tcp_disconnects, (collected_number)statsd.tcp_socket_disconnects); + rrdset_done(st_tcp_connects); + + rrddim_set_by_pointer(st_tcp_connected, rd_tcp_connected, (collected_number)statsd.tcp_socket_connected); + rrdset_done(st_tcp_connected); + + rrddim_set_by_pointer(st_pcharts, rd_pcharts, (collected_number)statsd.private_charts); + rrdset_done(st_pcharts); } - - rrddim_set_by_pointer(st_metrics, rd_metrics_gauge, (collected_number)statsd.gauges.metrics); - rrddim_set_by_pointer(st_metrics, rd_metrics_counter, (collected_number)statsd.counters.metrics); - rrddim_set_by_pointer(st_metrics, rd_metrics_timer, (collected_number)statsd.timers.metrics); - rrddim_set_by_pointer(st_metrics, rd_metrics_meter, (collected_number)statsd.meters.metrics); - rrddim_set_by_pointer(st_metrics, rd_metrics_histogram, (collected_number)statsd.histograms.metrics); - rrddim_set_by_pointer(st_metrics, rd_metrics_set, (collected_number)statsd.sets.metrics); - rrddim_set_by_pointer(st_metrics, rd_metrics_dictionary, (collected_number)statsd.dictionaries.metrics); - rrdset_done(st_metrics); - - rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_gauge, (collected_number)statsd.gauges.useful); - rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_counter, (collected_number)statsd.counters.useful); - rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_timer, (collected_number)statsd.timers.useful); - rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_meter, (collected_number)statsd.meters.useful); - rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_histogram, (collected_number)statsd.histograms.useful); - rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_set, (collected_number)statsd.sets.useful); - rrddim_set_by_pointer(st_useful_metrics, rd_useful_metrics_dictionary, (collected_number)statsd.dictionaries.useful); - rrdset_done(st_useful_metrics); - - rrddim_set_by_pointer(st_events, rd_events_gauge, (collected_number)statsd.gauges.events); - rrddim_set_by_pointer(st_events, rd_events_counter, (collected_number)statsd.counters.events); - rrddim_set_by_pointer(st_events, rd_events_timer, (collected_number)statsd.timers.events); - rrddim_set_by_pointer(st_events, rd_events_meter, (collected_number)statsd.meters.events); - rrddim_set_by_pointer(st_events, rd_events_histogram, (collected_number)statsd.histograms.events); - rrddim_set_by_pointer(st_events, rd_events_set, (collected_number)statsd.sets.events); - rrddim_set_by_pointer(st_events, rd_events_dictionary, (collected_number)statsd.dictionaries.events); - rrddim_set_by_pointer(st_events, rd_events_unknown, (collected_number)statsd.unknown_types); - rrddim_set_by_pointer(st_events, rd_events_errors, (collected_number)statsd.socket_errors); - rrdset_done(st_events); - - rrddim_set_by_pointer(st_reads, rd_reads_tcp, (collected_number)statsd.tcp_socket_reads); - rrddim_set_by_pointer(st_reads, rd_reads_udp, (collected_number)statsd.udp_socket_reads); - rrdset_done(st_reads); - - rrddim_set_by_pointer(st_bytes, rd_bytes_tcp, (collected_number)statsd.tcp_bytes_read); - rrddim_set_by_pointer(st_bytes, rd_bytes_udp, (collected_number)statsd.udp_bytes_read); - rrdset_done(st_bytes); - - rrddim_set_by_pointer(st_packets, rd_packets_tcp, (collected_number)statsd.tcp_packets_received); - rrddim_set_by_pointer(st_packets, rd_packets_udp, (collected_number)statsd.udp_packets_received); - rrdset_done(st_packets); - - rrddim_set_by_pointer(st_tcp_connects, rd_tcp_connects, (collected_number)statsd.tcp_socket_connects); - rrddim_set_by_pointer(st_tcp_connects, rd_tcp_disconnects, (collected_number)statsd.tcp_socket_disconnects); - rrdset_done(st_tcp_connects); - - rrddim_set_by_pointer(st_tcp_connected, rd_tcp_connected, (collected_number)statsd.tcp_socket_connected); - rrdset_done(st_tcp_connected); - - rrddim_set_by_pointer(st_pcharts, rd_pcharts, (collected_number)statsd.private_charts); - rrdset_done(st_pcharts); } cleanup: ; // added semi-colon to prevent older gcc error: label at end of compound statement diff --git a/collectors/tc.plugin/plugin_tc.c b/collectors/tc.plugin/plugin_tc.c index f012c078d..a2e72ee33 100644 --- a/collectors/tc.plugin/plugin_tc.c +++ b/collectors/tc.plugin/plugin_tc.c @@ -12,60 +12,46 @@ #define TC_LINE_MAX 1024 struct tc_class { - avl_t avl; + STRING *id; + STRING *name; + STRING *leafid; + STRING *parentid; + + bool hasparent; + bool isleaf; + bool isqdisc; + bool render; + bool name_updated; + bool updated; - char *id; - uint32_t hash; - - char *name; - - char *leafid; - uint32_t leaf_hash; - - char *parentid; - uint32_t parent_hash; - - char hasparent; - char isleaf; - char isqdisc; - char render; + int unupdated; // the number of times, this has been found un-updated unsigned long long bytes; unsigned long long packets; unsigned long long dropped; - unsigned long long overlimits; - unsigned long long requeues; - unsigned long long lended; - unsigned long long borrowed; - unsigned long long giants; unsigned long long tokens; unsigned long long ctokens; + //unsigned long long overlimits; + //unsigned long long requeues; + //unsigned long long lended; + //unsigned long long borrowed; + //unsigned long long giants; + RRDDIM *rd_bytes; RRDDIM *rd_packets; RRDDIM *rd_dropped; RRDDIM *rd_tokens; RRDDIM *rd_ctokens; - - char name_updated; - char updated; // updated bytes - int unupdated; // the number of times, this has been found un-updated - - struct tc_class *next; - struct tc_class *prev; }; struct tc_device { - avl_t avl; - - char *id; - uint32_t hash; + STRING *id; + STRING *name; + STRING *family; - char *name; - char *family; - - char name_updated; - char family_updated; + bool name_updated; + bool family_updated; char enabled; char enabled_bytes; @@ -81,94 +67,117 @@ struct tc_device { RRDSET *st_tokens; RRDSET *st_ctokens; - avl_tree_type classes_index; + DICTIONARY *classes; +}; - struct tc_class *classes; - struct tc_class *last_class; - struct tc_device *next; - struct tc_device *prev; -}; +// ---------------------------------------------------------------------------- +// tc_class index + +static void tc_class_free_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + // struct tc_device *d = data; + struct tc_class *c = value; + string_freez(c->id); + string_freez(c->name); + string_freez(c->leafid); + string_freez(c->parentid); +} -struct tc_device *tc_device_root = NULL; +static bool tc_class_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) { + struct tc_device *d = data; (void)d; + struct tc_class *c = old_value; (void)c; + struct tc_class *new_c = new_value; (void)new_c; -// ---------------------------------------------------------------------------- -// tc_device index + error("TC: class '%s' is already in device '%s'. Ignoring duplicate.", dictionary_acquired_item_name(item), string2str(d->id)); -static int tc_device_compare(void* a, void* b) { - if(((struct tc_device *)a)->hash < ((struct tc_device *)b)->hash) return -1; - else if(((struct tc_device *)a)->hash > ((struct tc_device *)b)->hash) return 1; - else return strcmp(((struct tc_device *)a)->id, ((struct tc_device *)b)->id); + tc_class_free_callback(item, new_value, data); + + return true; } -avl_tree_type tc_device_root_index = { - NULL, - tc_device_compare -}; +static void tc_class_index_init(struct tc_device *d) { + if(!d->classes) { + d->classes = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_SINGLE_THREADED); + + dictionary_register_delete_callback(d->classes, tc_class_free_callback, d); + dictionary_register_conflict_callback(d->classes, tc_class_conflict_callback, d); + } +} -#define tc_device_index_add(st) (struct tc_device *)avl_insert(&tc_device_root_index, (avl_t *)(st)) -#define tc_device_index_del(st) (struct tc_device *)avl_remove(&tc_device_root_index, (avl_t *)(st)) +static void tc_class_index_destroy(struct tc_device *d) { + dictionary_destroy(d->classes); + d->classes = NULL; +} -static inline struct tc_device *tc_device_index_find(const char *id, uint32_t hash) { - struct tc_device tmp; - tmp.id = (char *)id; - tmp.hash = (hash)?hash:simple_hash(tmp.id); +static struct tc_class *tc_class_index_add(struct tc_device *d, struct tc_class *c) { + return dictionary_set(d->classes, string2str(c->id), c, sizeof(*c)); +} - return (struct tc_device *)avl_search(&(tc_device_root_index), (avl_t *)&tmp); +static void tc_class_index_del(struct tc_device *d, struct tc_class *c) { + dictionary_del(d->classes, string2str(c->id)); } +static inline struct tc_class *tc_class_index_find(struct tc_device *d, const char *id) { + return dictionary_get(d->classes, id); +} // ---------------------------------------------------------------------------- -// tc_class index +// tc_device index -static int tc_class_compare(void* a, void* b) { - if(((struct tc_class *)a)->hash < ((struct tc_class *)b)->hash) return -1; - else if(((struct tc_class *)a)->hash > ((struct tc_class *)b)->hash) return 1; - else return strcmp(((struct tc_class *)a)->id, ((struct tc_class *)b)->id); +static DICTIONARY *tc_device_root_index = NULL; + +static void tc_device_add_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct tc_device *d = value; + tc_class_index_init(d); } -#define tc_class_index_add(st, rd) (struct tc_class *)avl_insert(&((st)->classes_index), (avl_t *)(rd)) -#define tc_class_index_del(st, rd) (struct tc_class *)avl_remove(&((st)->classes_index), (avl_t *)(rd)) +static void tc_device_free_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct tc_device *d = value; -static inline struct tc_class *tc_class_index_find(struct tc_device *st, const char *id, uint32_t hash) { - struct tc_class tmp; - tmp.id = (char *)id; - tmp.hash = (hash)?hash:simple_hash(tmp.id); + tc_class_index_destroy(d); - return (struct tc_class *)avl_search(&(st->classes_index), (avl_t *) &tmp); + string_freez(d->id); + string_freez(d->name); + string_freez(d->family); } -// ---------------------------------------------------------------------------- +static void tc_device_index_init() { + if(!tc_device_root_index) { + tc_device_root_index = dictionary_create( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_SINGLE_THREADED | DICT_OPTION_ADD_IN_FRONT); -static inline void tc_class_free(struct tc_device *n, struct tc_class *c) { - if(c == n->classes) { - if(likely(c->next)) - n->classes = c->next; - else - n->classes = c->prev; + dictionary_register_insert_callback(tc_device_root_index, tc_device_add_callback, NULL); + dictionary_register_delete_callback(tc_device_root_index, tc_device_free_callback, NULL); } +} - if(c == n->last_class) { - if(unlikely(c->next)) - n->last_class = c->next; - else - n->last_class = c->prev; - } +static void tc_device_index_destroy() { + dictionary_destroy(tc_device_root_index); + tc_device_root_index = NULL; +} - if(c->next) c->next->prev = c->prev; - if(c->prev) c->prev->next = c->next; +static struct tc_device *tc_device_index_add(struct tc_device *d) { + return dictionary_set(tc_device_root_index, string2str(d->id), d, sizeof(*d)); +} - debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', unused=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->unupdated); +//static struct tc_device *tc_device_index_del(struct tc_device *d) { +// dictionary_del(tc_device_root_index, string2str(d->id)); +// return d; +//} - if(unlikely(tc_class_index_del(n, c) != c)) - error("plugin_tc: INTERNAL ERROR: attempt remove class '%s' from device '%s': removed a different calls", c->id, n->id); +static inline struct tc_device *tc_device_index_find(const char *id) { + return dictionary_get(tc_device_root_index, id); +} - freez(c->id); - freez(c->name); - freez(c->leafid); - freez(c->parentid); - freez(c); +// ---------------------------------------------------------------------------- + +static inline void tc_class_free(struct tc_device *n, struct tc_class *c) { + debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', unused=%d", + string2str(n->id), string2str(c->id), string2str(c->parentid), string2str(c->leafid), + c->unupdated); + + tc_class_index_del(n, c); } static inline void tc_device_classes_cleanup(struct tc_device *d) { @@ -179,23 +188,20 @@ static inline void tc_device_classes_cleanup(struct tc_device *d) { if(cleanup_every < 0) cleanup_every = -cleanup_every; } - d->name_updated = 0; - d->family_updated = 0; + d->name_updated = false; + d->family_updated = false; - struct tc_class *c = d->classes; - while(c) { - if(unlikely(cleanup_every && c->unupdated >= cleanup_every)) { - struct tc_class *nc = c->next; + struct tc_class *c; + dfe_start_write(d->classes, c) { + if(unlikely(cleanup_every && c->unupdated >= cleanup_every)) tc_class_free(d, c); - c = nc; - } - else { - c->updated = 0; - c->name_updated = 0; - c = c->next; + else { + c->updated = false; + c->name_updated = false; } } + dfe_done(c); } static inline void tc_device_commit(struct tc_device *d) { @@ -213,26 +219,26 @@ static inline void tc_device_commit(struct tc_device *d) { if(unlikely(d->enabled == (char)-1)) { char var_name[CONFIG_MAX_NAME + 1]; - snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id); + snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", string2str(d->id)); d->enabled = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces); - snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id); + snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", string2str(d->id)); d->enabled_bytes = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes); - snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id); + snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", string2str(d->id)); d->enabled_packets = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_packets); - snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id); + snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", string2str(d->id)); d->enabled_dropped = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped); - snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id); + snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", string2str(d->id)); d->enabled_tokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens); - snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id); + snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", string2str(d->id)); d->enabled_ctokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); - snprintfz(var_name, CONFIG_MAX_NAME, "show all classes for %s", d->id); + snprintfz(var_name, CONFIG_MAX_NAME, "show all classes for %s", string2str(d->id)); d->enabled_all_classes_qdiscs = (char)config_get_boolean_ondemand("plugin:tc", var_name, enabled_all_classes_qdiscs); } @@ -244,11 +250,10 @@ static inline void tc_device_commit(struct tc_device *d) { // prepare all classes // we set reasonable defaults for the rest of the code below - for(c = d->classes ; c ; c = c->next) { - c->render = 0; // do not render this class - - c->isleaf = 1; // this is a leaf class - c->hasparent = 0; // without a parent + dfe_start_read(d->classes, c) { + c->render = false; // do not render this class + c->isleaf = true; // this is a leaf class + c->hasparent = false; // without a parent if(unlikely(!c->updated)) c->unupdated++; // increase its unupdated counter @@ -262,21 +267,23 @@ static inline void tc_device_commit(struct tc_device *d) { updated_classes++; } } + dfe_done(c); if(unlikely(!d->enabled || (!updated_classes && !updated_qdiscs))) { - debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. It is not enabled/updated.", d->name?d->name:d->id); + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. It is not enabled/updated.", string2str(d->name?d->name:d->id)); tc_device_classes_cleanup(d); return; } if(unlikely(updated_classes && updated_qdiscs)) { - error("TC: device '%s' has active both classes (%d) and qdiscs (%d). Will render only qdiscs.", d->id, updated_classes, updated_qdiscs); + error("TC: device '%s' has active both classes (%d) and qdiscs (%d). Will render only qdiscs.", string2str(d->id), updated_classes, updated_qdiscs); // set all classes to !updated - for(c = d->classes ; c ; c = c->next) - if(unlikely(!c->isqdisc && c->updated)) - c->updated = 0; - + dfe_start_read(d->classes, c) { + if (unlikely(!c->isqdisc && c->updated)) + c->updated = false; + } + dfe_done(c); updated_classes = 0; } @@ -296,8 +303,9 @@ static inline void tc_device_commit(struct tc_device *d) { // so, here we remove the isleaf flag from nodes in the middle // and we add the hasparent flag to leaf nodes we found their parent if(likely(!d->enabled_all_classes_qdiscs)) { - for(c = d->classes; c; c = c->next) { - if(unlikely(!c->updated)) continue; + dfe_start_read(d->classes, c) { + if(unlikely(!c->updated)) + continue; //debug(D_TC_LOOP, "TC: In device '%s', %s '%s' has leafid: '%s' and parentid '%s'.", // d->id, @@ -307,30 +315,34 @@ static inline void tc_device_commit(struct tc_device *d) { // c->parentid?c->parentid:"NULL"); // find if c is leaf or not - for(x = d->classes; x; x = x->next) { - if(unlikely(!x->updated || c == x || !x->parentid)) continue; + dfe_start_read(d->classes, x) { + if(unlikely(!x->updated || c == x || !x->parentid)) + continue; // classes have both parentid and leafid // qdiscs have only parentid // the following works for both (it is an OR) - if((c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) || - (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0)) { + if((x->parentid && c->id == x->parentid) || + (c->leafid && x->parentid && c->leafid == x->parentid)) { // debug(D_TC_LOOP, "TC: In device '%s', %s '%s' (leafid: '%s') has as leaf %s '%s' (parentid: '%s').", d->name?d->name:d->id, c->isqdisc?"qdisc":"class", c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->isqdisc?"qdisc":"class", x->name?x->name:x->id, x->parentid?x->parentid:x->id); - c->isleaf = 0; - x->hasparent = 1; + c->isleaf = false; + x->hasparent = true; } } + dfe_done(x); } + dfe_done(c); } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + dfe_start_read(d->classes, c) { + if(unlikely(!c->updated)) + continue; // debug(D_TC_LOOP, "TC: device '%s', %s '%s' isleaf=%d, hasparent=%d", d->id, (c->isqdisc)?"qdisc":"class", c->id, c->isleaf, c->hasparent); if(unlikely((c->isleaf && c->hasparent) || d->enabled_all_classes_qdiscs)) { - c->render = 1; + c->render = true; active_nodes++; bytes_sum += c->bytes; packets_sum += c->packets; @@ -345,26 +357,29 @@ static inline void tc_device_commit(struct tc_device *d) { // debug(D_TC_LOOP, "TC: found root class/qdisc '%s'", root->id); //} } + dfe_done(c); #ifdef NETDATA_INTERNAL_CHECKS // dump all the list to see what we know if(unlikely(debug_flags & D_TC_LOOP)) { - for(c = d->classes ; c ; c = c->next) { - if(c->render) debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, OK", d->name, c->id); - else debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, IGNORE (updated: %d, isleaf: %d, hasparent: %d, parent: %s)", d->name?d->name:d->id, c->id, c->updated, c->isleaf, c->hasparent, c->parentid?c->parentid:"(unset)"); + dfe_start_read(d->classes, c) { + if(c->render) debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, OK", string2str(d->name), string2str(c->id)); + else debug(D_TC_LOOP, "TC: final nodes dump for '%s': class '%s', IGNORE (updated: %d, isleaf: %d, hasparent: %d, parent: '%s')", + string2str(d->name?d->name:d->id), string2str(c->id), c->updated, c->isleaf, c->hasparent, string2str(c->parentid)); } + dfe_done(c); } #endif if(unlikely(!active_nodes)) { - debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No useful classes/qdiscs.", d->name?d->name:d->id); + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No useful classes/qdiscs.", string2str(d->name?d->name:d->id)); tc_device_classes_cleanup(d); return; } debug(D_TC_LOOP, "TC: evaluating TC device '%s'. enabled = %d/%d (bytes: %d/%d, packets: %d/%d, dropped: %d/%d, tokens: %d/%d, ctokens: %d/%d, all_classes_qdiscs: %d/%d), classes: (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).", - d->name?d->name:d->id, + string2str(d->name?d->name:d->id), d->enabled, enable_new_interfaces, d->enabled_bytes, enable_bytes, d->enabled_packets, enable_packets, @@ -383,44 +398,54 @@ static inline void tc_device_commit(struct tc_device *d) { // bytes if(d->enabled_bytes == CONFIG_BOOLEAN_YES || (d->enabled_bytes == CONFIG_BOOLEAN_AUTO && - (bytes_sum || - netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { + (bytes_sum || netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) { d->enabled_bytes = CONFIG_BOOLEAN_YES; - if(unlikely(!d->st_bytes)) + if(unlikely(!d->st_bytes)) { d->st_bytes = rrdset_create_localhost( - RRD_TYPE_TC - , d->id - , d->name ? d->name : d->id - , d->family ? d->family : d->id - , RRD_TYPE_TC ".qos" - , "Class Usage" - , "kilobits/s" - , PLUGIN_TC_NAME - , NULL - , NETDATA_CHART_PRIO_TC_QOS - , localhost->rrd_update_every - , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED - ); - + RRD_TYPE_TC, + string2str(d->id), + string2str(d->name ? d->name : d->id), + string2str(d->family ? d->family : d->id), + RRD_TYPE_TC ".qos", + "Class Usage", + "kilobits/s", + PLUGIN_TC_NAME, + NULL, + NETDATA_CHART_PRIO_TC_QOS, + localhost->rrd_update_every, + d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED); + + rrdlabels_add(d->st_bytes->rrdlabels, "device", string2str(d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO); + } else { - rrdset_next(d->st_bytes); - if(unlikely(d->name_updated)) rrdset_set_name(d->st_bytes, d->name); + if(unlikely(d->name_updated)) + rrdset_reset_name(d->st_bytes, string2str(d->name)); + + if(d->name && d->name_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name), RRDLABEL_SRC_AUTO); + + if(d->family && d->family_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family), RRDLABEL_SRC_AUTO); // TODO // update the family } - for(c = d->classes ; c ; c = c->next) { + dfe_start_read(d->classes, c) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_bytes)) - c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); + c->rd_bytes = rrddim_add(d->st_bytes, string2str(c->id), string2str(c->name?c->name:c->id), 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); else if(unlikely(c->name_updated)) - rrddim_set_name(d->st_bytes, c->rd_bytes, c->name); + rrddim_reset_name(d->st_bytes, c->rd_bytes, string2str(c->name)); rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes); } + dfe_done(c); + rrdset_done(d->st_bytes); } @@ -435,47 +460,56 @@ static inline void tc_device_commit(struct tc_device *d) { if(unlikely(!d->st_packets)) { char id[RRD_ID_LENGTH_MAX + 1]; char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id); - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id); + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", string2str(d->id)); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", string2str(d->name ? d->name : d->id)); d->st_packets = rrdset_create_localhost( - RRD_TYPE_TC - , id - , name - , d->family ? d->family : d->id - , RRD_TYPE_TC ".qos_packets" - , "Class Packets" - , "packets/s" - , PLUGIN_TC_NAME - , NULL - , NETDATA_CHART_PRIO_TC_QOS_PACKETS - , localhost->rrd_update_every - , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED - ); + RRD_TYPE_TC, + id, + name, + string2str(d->family ? d->family : d->id), + RRD_TYPE_TC ".qos_packets", + "Class Packets", + "packets/s", + PLUGIN_TC_NAME, + NULL, + NETDATA_CHART_PRIO_TC_QOS_PACKETS, + localhost->rrd_update_every, + d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED); + + rrdlabels_add(d->st_bytes->rrdlabels, "device", string2str(d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO); } else { - rrdset_next(d->st_packets); - if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id); - rrdset_set_name(d->st_packets, name); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", string2str(d->name?d->name:d->id)); + rrdset_reset_name(d->st_packets, name); } + if(d->name && d->name_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name), RRDLABEL_SRC_AUTO); + + if(d->family && d->family_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family), RRDLABEL_SRC_AUTO); + // TODO // update the family } - for(c = d->classes ; c ; c = c->next) { + dfe_start_read(d->classes, c) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_packets)) - c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_packets = rrddim_add(d->st_packets, string2str(c->id), string2str(c->name?c->name:c->id), 1, 1, RRD_ALGORITHM_INCREMENTAL); else if(unlikely(c->name_updated)) - rrddim_set_name(d->st_packets, c->rd_packets, c->name); + rrddim_reset_name(d->st_packets, c->rd_packets, string2str(c->name)); rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets); } + dfe_done(c); + rrdset_done(d->st_packets); } @@ -490,47 +524,56 @@ static inline void tc_device_commit(struct tc_device *d) { if(unlikely(!d->st_dropped)) { char id[RRD_ID_LENGTH_MAX + 1]; char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id); - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id); + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", string2str(d->id)); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", string2str(d->name ? d->name : d->id)); d->st_dropped = rrdset_create_localhost( - RRD_TYPE_TC - , id - , name - , d->family ? d->family : d->id - , RRD_TYPE_TC ".qos_dropped" - , "Class Dropped Packets" - , "packets/s" - , PLUGIN_TC_NAME - , NULL - , NETDATA_CHART_PRIO_TC_QOS_DROPPED - , localhost->rrd_update_every - , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED - ); + RRD_TYPE_TC, + id, + name, + string2str(d->family ? d->family : d->id), + RRD_TYPE_TC ".qos_dropped", + "Class Dropped Packets", + "packets/s", + PLUGIN_TC_NAME, + NULL, + NETDATA_CHART_PRIO_TC_QOS_DROPPED, + localhost->rrd_update_every, + d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED); + + rrdlabels_add(d->st_bytes->rrdlabels, "device", string2str(d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO); } else { - rrdset_next(d->st_dropped); - if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id); - rrdset_set_name(d->st_dropped, name); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", string2str(d->name?d->name:d->id)); + rrdset_reset_name(d->st_dropped, name); } + if(d->name && d->name_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name), RRDLABEL_SRC_AUTO); + + if(d->family && d->family_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family), RRDLABEL_SRC_AUTO); + // TODO // update the family } - for(c = d->classes ; c ; c = c->next) { + dfe_start_read(d->classes, c) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_dropped)) - c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_dropped = rrddim_add(d->st_dropped, string2str(c->id), string2str(c->name?c->name:c->id), 1, 1, RRD_ALGORITHM_INCREMENTAL); else if(unlikely(c->name_updated)) - rrddim_set_name(d->st_dropped, c->rd_dropped, c->name); + rrddim_reset_name(d->st_dropped, c->rd_dropped, string2str(c->name)); rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped); } + dfe_done(c); + rrdset_done(d->st_dropped); } @@ -545,48 +588,57 @@ static inline void tc_device_commit(struct tc_device *d) { if(unlikely(!d->st_tokens)) { char id[RRD_ID_LENGTH_MAX + 1]; char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id); - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id); + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", string2str(d->id)); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", string2str(d->name ? d->name : d->id)); d->st_tokens = rrdset_create_localhost( - RRD_TYPE_TC - , id - , name - , d->family ? d->family : d->id - , RRD_TYPE_TC ".qos_tokens" - , "Class Tokens" - , "tokens" - , PLUGIN_TC_NAME - , NULL - , NETDATA_CHART_PRIO_TC_QOS_TOKENS - , localhost->rrd_update_every - , RRDSET_TYPE_LINE - ); + RRD_TYPE_TC, + id, + name, + string2str(d->family ? d->family : d->id), + RRD_TYPE_TC ".qos_tokens", + "Class Tokens", + "tokens", + PLUGIN_TC_NAME, + NULL, + NETDATA_CHART_PRIO_TC_QOS_TOKENS, + localhost->rrd_update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add(d->st_bytes->rrdlabels, "device", string2str(d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO); } else { - rrdset_next(d->st_tokens); - if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id); - rrdset_set_name(d->st_tokens, name); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", string2str(d->name?d->name:d->id)); + rrdset_reset_name(d->st_tokens, name); } + if(d->name && d->name_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name), RRDLABEL_SRC_AUTO); + + if(d->family && d->family_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family), RRDLABEL_SRC_AUTO); + // TODO // update the family } - for(c = d->classes ; c ; c = c->next) { + dfe_start_read(d->classes, c) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_tokens)) { - c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE); + c->rd_tokens = rrddim_add(d->st_tokens, string2str(c->id), string2str(c->name?c->name:c->id), 1, 1, RRD_ALGORITHM_ABSOLUTE); } else if(unlikely(c->name_updated)) - rrddim_set_name(d->st_tokens, c->rd_tokens, c->name); + rrddim_reset_name(d->st_tokens, c->rd_tokens, string2str(c->name)); rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens); } + dfe_done(c); + rrdset_done(d->st_tokens); } @@ -601,48 +653,58 @@ static inline void tc_device_commit(struct tc_device *d) { if(unlikely(!d->st_ctokens)) { char id[RRD_ID_LENGTH_MAX + 1]; char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id); - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id); + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", string2str(d->id)); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", string2str(d->name ? d->name : d->id)); d->st_ctokens = rrdset_create_localhost( - RRD_TYPE_TC - , id - , name - , d->family ? d->family : d->id - , RRD_TYPE_TC ".qos_ctokens" - , "Class cTokens" - , "ctokens" - , PLUGIN_TC_NAME - , NULL - , NETDATA_CHART_PRIO_TC_QOS_CTOKENS - , localhost->rrd_update_every - , RRDSET_TYPE_LINE - ); + RRD_TYPE_TC, + id, + name, + string2str(d->family ? d->family : d->id), + RRD_TYPE_TC ".qos_ctokens", + "Class cTokens", + "ctokens", + PLUGIN_TC_NAME, + NULL, + NETDATA_CHART_PRIO_TC_QOS_CTOKENS, + localhost->rrd_update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add(d->st_bytes->rrdlabels, "device", string2str(d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name?d->name:d->id), RRDLABEL_SRC_AUTO); + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family?d->family:d->id), RRDLABEL_SRC_AUTO); } else { - debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_ctokens); + debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", string2str(d->name?d->name:d->id)); if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id); - rrdset_set_name(d->st_ctokens, name); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", string2str(d->name?d->name:d->id)); + rrdset_reset_name(d->st_ctokens, name); } + if(d->name && d->name_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "name", string2str(d->name), RRDLABEL_SRC_AUTO); + + if(d->family && d->family_updated) + rrdlabels_add(d->st_bytes->rrdlabels, "family", string2str(d->family), RRDLABEL_SRC_AUTO); + // TODO // update the family } - for(c = d->classes ; c ; c = c->next) { + dfe_start_read(d->classes, c) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_ctokens)) - c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE); + c->rd_ctokens = rrddim_add(d->st_ctokens, string2str(c->id), string2str(c->name?c->name:c->id), 1, 1, RRD_ALGORITHM_ABSOLUTE); else if(unlikely(c->name_updated)) - rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name); + rrddim_reset_name(d->st_ctokens, c->rd_ctokens, string2str(c->name)); rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens); } + dfe_done(c); + rrdset_done(d->st_ctokens); } @@ -652,18 +714,18 @@ static inline void tc_device_commit(struct tc_device *d) { static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name) { if(unlikely(!name || !*name)) return; - struct tc_class *c = tc_class_index_find(d, id, 0); + struct tc_class *c = tc_class_index_find(d, id); if(likely(c)) { if(likely(c->name)) { - if(!strcmp(c->name, name)) return; - freez(c->name); + if(!strcmp(string2str(c->name), name)) return; + string_freez(c->name); c->name = NULL; } - if(likely(name && *name && strcmp(c->id, name) != 0)) { - debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", d->id, id, name); - c->name = strdupz(name); - c->name_updated = 1; + if(likely(name && *name && strcmp(string2str(c->id), name) != 0)) { + debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", string2str(d->id), id, name); + c->name = string_strdupz(name); + c->name_updated = true; } } } @@ -672,126 +734,68 @@ static inline void tc_device_set_device_name(struct tc_device *d, char *name) { if(unlikely(!name || !*name)) return; if(d->name) { - if(!strcmp(d->name, name)) return; - freez(d->name); + if(!strcmp(string2str(d->name), name)) return; + string_freez(d->name); d->name = NULL; } - if(likely(name && *name && strcmp(d->id, name) != 0)) { - debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name); - d->name = strdupz(name); - d->name_updated = 1; + if(likely(name && *name && strcmp(string2str(d->id), name) != 0)) { + debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", string2str(d->id), name); + d->name = string_strdupz(name); + d->name_updated = true; } } static inline void tc_device_set_device_family(struct tc_device *d, char *family) { - freez(d->family); + string_freez(d->family); d->family = NULL; - if(likely(family && *family && strcmp(d->id, family) != 0)) { - debug(D_TC_LOOP, "TC: Setting device '%s' family to '%s'", d->id, family); - d->family = strdupz(family); - d->family_updated = 1; + if(likely(family && *family && strcmp(string2str(d->id), family) != 0)) { + debug(D_TC_LOOP, "TC: Setting device '%s' family to '%s'", string2str(d->id), family); + d->family = string_strdupz(family); + d->family_updated = true; } // no need for null termination - it is already null } -static inline struct tc_device *tc_device_create(char *id) -{ - struct tc_device *d = tc_device_index_find(id, 0); +static inline struct tc_device *tc_device_create(char *id) { + struct tc_device *d = tc_device_index_find(id); if(!d) { debug(D_TC_LOOP, "TC: Creating device '%s'", id); - d = callocz(1, sizeof(struct tc_device)); - - d->id = strdupz(id); - d->hash = simple_hash(d->id); - d->enabled = (char)-1; - - avl_init(&d->classes_index, tc_class_compare); - if(unlikely(tc_device_index_add(d) != d)) - error("plugin_tc: INTERNAL ERROR: removing device '%s' removed a different device.", d->id); - - if(!tc_device_root) { - tc_device_root = d; - } - else { - d->next = tc_device_root; - tc_device_root->prev = d; - tc_device_root = d; - } + struct tc_device tmp = { + .id = string_strdupz(id), + .enabled = (char)-1, + }; + d = tc_device_index_add(&tmp); } return(d); } -static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char qdisc, char *parentid, char *leafid) -{ - struct tc_class *c = tc_class_index_find(n, id, 0); +static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, bool qdisc, char *parentid, char *leafid) { + struct tc_class *c = tc_class_index_find(n, id); if(!c) { - debug(D_TC_LOOP, "TC: Creating in device '%s', class id '%s', parentid '%s', leafid '%s'", n->id, id, parentid?parentid:"", leafid?leafid:""); + debug(D_TC_LOOP, "TC: Creating in device '%s', class id '%s', parentid '%s', leafid '%s'", + string2str(n->id), id, parentid?parentid:"", leafid?leafid:""); - c = callocz(1, sizeof(struct tc_class)); + struct tc_class tmp = { + .id = string_strdupz(id), + .isqdisc = qdisc, + .parentid = string_strdupz(parentid), + .leafid = string_strdupz(leafid), + }; - if(unlikely(!n->classes)) - n->classes = c; - - else if(likely(n->last_class)) { - n->last_class->next = c; - c->prev = n->last_class; - } - - n->last_class = c; - - c->id = strdupz(id); - c->hash = simple_hash(c->id); - - c->isqdisc = qdisc; - if(parentid && *parentid) { - c->parentid = strdupz(parentid); - c->parent_hash = simple_hash(c->parentid); - } - - if(leafid && *leafid) { - c->leafid = strdupz(leafid); - c->leaf_hash = simple_hash(c->leafid); - } - - if(unlikely(tc_class_index_add(n, c) != c)) - error("plugin_tc: INTERNAL ERROR: attempt index class '%s' on device '%s': already exists", c->id, n->id); + tc_class_index_add(n, &tmp); } return(c); } -static inline void tc_device_free(struct tc_device *n) -{ - if(n->next) n->next->prev = n->prev; - if(n->prev) n->prev->next = n->next; - if(tc_device_root == n) { - if(n->next) tc_device_root = n->next; - else tc_device_root = n->prev; - } - - if(unlikely(tc_device_index_del(n) != n)) - error("plugin_tc: INTERNAL ERROR: removing device '%s' removed a different device.", n->id); - - while(n->classes) tc_class_free(n, n->classes); - - freez(n->id); - freez(n->name); - freez(n->family); - freez(n); -} - -static inline void tc_device_free_all() -{ - while(tc_device_root) - tc_device_free(tc_device_root); -} - -#define PLUGINSD_MAX_WORDS 20 +//static inline void tc_device_free(struct tc_device *d) { +// tc_device_index_del(d); +//} static inline int tc_space(char c) { switch(c) { @@ -846,6 +850,8 @@ static pid_t tc_child_pid = 0; static void tc_main_cleanup(void *ptr) { worker_unregister(); + tc_device_index_destroy(); + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; static_thread->enabled = NETDATA_MAIN_THREAD_EXITING; @@ -876,8 +882,11 @@ static void tc_main_cleanup(void *ptr) { #define WORKER_TC_SETDEVICEGROUP 7 #define WORKER_TC_SETCLASSNAME 8 #define WORKER_TC_WORKTIME 9 +#define WORKER_TC_PLUGIN_TIME 10 +#define WORKER_TC_DEVICES 11 +#define WORKER_TC_CLASSES 12 -#if WORKER_UTILIZATION_MAX_JOB_TYPES < 10 +#if WORKER_UTILIZATION_MAX_JOB_TYPES < 13 #error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 10 #endif @@ -894,6 +903,11 @@ void *tc_main(void *ptr) { worker_register_job_name(WORKER_TC_SETCLASSNAME, "classname"); worker_register_job_name(WORKER_TC_WORKTIME, "worktime"); + worker_register_job_custom_metric(WORKER_TC_PLUGIN_TIME, "tc script execution time", "milliseconds/run", WORKER_METRIC_ABSOLUTE); + worker_register_job_custom_metric(WORKER_TC_DEVICES, "number of devices", "devices", WORKER_METRIC_ABSOLUTE); + worker_register_job_custom_metric(WORKER_TC_CLASSES, "number of classes", "classes", WORKER_METRIC_ABSOLUTE); + + tc_device_index_init(); netdata_thread_cleanup_push(tc_main_cleanup, ptr); char command[FILENAME_MAX + 1]; @@ -916,21 +930,21 @@ void *tc_main(void *ptr) { char *tc_script = config_get("plugin:tc", "script to run to get tc values", command); while(!netdata_exit) { - FILE *fp; + FILE *fp_child_input, *fp_child_output; struct tc_device *device = NULL; struct tc_class *class = NULL; snprintfz(command, TC_LINE_MAX, "exec %s %d", tc_script, localhost->rrd_update_every); debug(D_TC_LOOP, "executing '%s'", command); - fp = mypopen(command, (pid_t *)&tc_child_pid); - if(unlikely(!fp)) { + fp_child_output = netdata_popen(command, (pid_t *)&tc_child_pid, &fp_child_input); + if(unlikely(!fp_child_output)) { error("TC: Cannot popen(\"%s\", \"r\").", command); goto cleanup; } char buffer[TC_LINE_MAX+1] = ""; - while(fgets(buffer, TC_LINE_MAX, fp) != NULL) { + while(fgets(buffer, TC_LINE_MAX, fp_child_output) != NULL) { if(unlikely(netdata_exit)) break; buffer[TC_LINE_MAX] = '\0'; @@ -969,10 +983,10 @@ void *tc_main(void *ptr) { } if(likely(type && id && (parent_is_root || parent_is_parent))) { - char qdisc = 0; + bool qdisc = false; if(first_hash == QDISC_HASH) { - qdisc = 1; + qdisc = true; if(!strcmp(type, "ingress")) { // we don't want to get the ingress qdisc @@ -1051,10 +1065,10 @@ void *tc_main(void *ptr) { // debug(D_TC_LOOP, "SENT line '%s'", words[1]); if(likely(words[1] && *words[1])) { class->bytes = str2ull(words[1]); - class->updated = 1; + class->updated = true; } else { - class->updated = 0; + class->updated = false; } if(likely(words[3] && *words[3])) @@ -1063,24 +1077,24 @@ void *tc_main(void *ptr) { if(likely(words[6] && *words[6])) class->dropped = str2ull(words[6]); - if(likely(words[8] && *words[8])) - class->overlimits = str2ull(words[8]); + //if(likely(words[8] && *words[8])) + // class->overlimits = str2ull(words[8]); - if(likely(words[10] && *words[10])) - class->requeues = str2ull(words[8]); + //if(likely(words[10] && *words[10])) + // class->requeues = str2ull(words[8]); } else if(unlikely(device && class && class->updated && first_hash == LENDED_HASH && strcmp(words[0], "lended:") == 0)) { worker_is_busy(WORKER_TC_LENDED); // debug(D_TC_LOOP, "LENDED line '%s'", words[1]); - if(likely(words[1] && *words[1])) - class->lended = str2ull(words[1]); + //if(likely(words[1] && *words[1])) + // class->lended = str2ull(words[1]); - if(likely(words[3] && *words[3])) - class->borrowed = str2ull(words[3]); + //if(likely(words[3] && *words[3])) + // class->borrowed = str2ull(words[3]); - if(likely(words[5] && *words[5])) - class->giants = str2ull(words[5]); + //if(likely(words[5] && *words[5])) + // class->giants = str2ull(words[5]); } else if(unlikely(device && class && class->updated && first_hash == TOKENS_HASH && strcmp(words[0], "tokens:") == 0)) { worker_is_busy(WORKER_TC_TOKENS); @@ -1117,33 +1131,19 @@ void *tc_main(void *ptr) { } else if(unlikely(first_hash == WORKTIME_HASH && strcmp(words[0], "WORKTIME") == 0)) { worker_is_busy(WORKER_TC_WORKTIME); + worker_set_metric(WORKER_TC_PLUGIN_TIME, str2ll(words[1], NULL)); - // debug(D_TC_LOOP, "WORKTIME line '%s' '%s'", words[1], words[2]); - static RRDSET *sttime = NULL; - static RRDDIM *rd_run_time = NULL; - - if(unlikely(!sttime)) { - sttime = rrdset_create_localhost( - "netdata" - , "plugin_tc_time" - , NULL - , "workers plugin tc" - , "netdata.workers.tc.script_time" - , "Netdata TC script execution" - , "milliseconds/run" - , PLUGIN_TC_NAME - , NULL - , NETDATA_CHART_PRIO_NETDATA_TC_TIME - , localhost->rrd_update_every - , RRDSET_TYPE_AREA - ); - rd_run_time = rrddim_add(sttime, "run_time", "run time", 1, 1, RRD_ALGORITHM_ABSOLUTE); - } - else rrdset_next(sttime); + size_t number_of_devices = dictionary_entries(tc_device_root_index); + size_t number_of_classes = 0; - rrddim_set_by_pointer(sttime, rd_run_time, str2ll(words[1], NULL)); - rrdset_done(sttime); + struct tc_device *d; + dfe_start_read(tc_device_root_index, d) { + number_of_classes += dictionary_entries(d->classes); + } + dfe_done(d); + worker_set_metric(WORKER_TC_DEVICES, number_of_devices); + worker_set_metric(WORKER_TC_CLASSES, number_of_classes); } //else { // debug(D_TC_LOOP, "IGNORED line"); @@ -1153,7 +1153,7 @@ void *tc_main(void *ptr) { } // fgets() failed or loop broke - int code = mypclose(fp, (pid_t)tc_child_pid); + int code = netdata_pclose(fp_child_input, fp_child_output, (pid_t)tc_child_pid); tc_child_pid = 0; if(unlikely(device)) { @@ -1162,17 +1162,13 @@ void *tc_main(void *ptr) { class = NULL; } - if(unlikely(netdata_exit)) { - tc_device_free_all(); + if(unlikely(netdata_exit)) goto cleanup; - } if(code == 1 || code == 127) { // 1 = DISABLE // 127 = cannot even run it error("TC: tc-qos-helper.sh exited with code %d. Disabling it.", code); - - tc_device_free_all(); goto cleanup; } diff --git a/collectors/timex.plugin/plugin_timex.c b/collectors/timex.plugin/plugin_timex.c index 0390b9920..46cfc5796 100644 --- a/collectors/timex.plugin/plugin_timex.c +++ b/collectors/timex.plugin/plugin_timex.c @@ -109,8 +109,6 @@ void *timex_main(void *ptr) RRDSET_TYPE_LINE); rd_sync_state = rrddim_add(st_sync_state, "state", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else { - rrdset_next(st_sync_state); } rrddim_set_by_pointer(st_sync_state, rd_sync_state, sync_state != TIME_ERROR ? 1 : 0); @@ -137,13 +135,11 @@ void *timex_main(void *ptr) sta_codes[i].rd = rrddim_add(st_clock_status, sta_codes[i].name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - } else { - rrdset_next(st_clock_status); } - for (int i = 0; sta_codes[i].name != NULL; i++) { + for (int i = 0; sta_codes[i].name != NULL; i++) rrddim_set_by_pointer(st_clock_status, sta_codes[i].rd, timex_buf.status & sta_codes[i].code ? 1 : 0); - } + rrdset_done(st_clock_status); } @@ -167,8 +163,6 @@ void *timex_main(void *ptr) RRDSET_TYPE_LINE); rd_offset = rrddim_add(st_offset, "offset", NULL, 1, divisor, RRD_ALGORITHM_ABSOLUTE); - } else { - rrdset_next(st_offset); } rrddim_set_by_pointer(st_offset, rd_offset, timex_buf.offset); diff --git a/collectors/xenstat.plugin/xenstat_plugin.c b/collectors/xenstat.plugin/xenstat_plugin.c index 882f72ce9..ea98b9bb1 100644 --- a/collectors/xenstat.plugin/xenstat_plugin.c +++ b/collectors/xenstat.plugin/xenstat_plugin.c @@ -171,7 +171,7 @@ static struct domain_metrics *domain_metrics_free(struct domain_metrics *d) { struct vbd_metrics *vbd, *vbd_f; struct network_metrics *network, *network_f; - if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: freeing memory for domain '%s' id %d, uuid %s\n", d->name, d->id, d->uuid); + if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: freeing memory for domain '%s' id %u, uuid %s\n", d->name, d->id, d->uuid); for(cur = node_metrics.domain_root; cur ; last = cur, cur = cur->next) { if(unlikely(cur->hash == d->hash && !strcmp(cur->uuid, d->uuid))) break; @@ -401,7 +401,7 @@ static int xenstat_collect(xenstat_handle *xhandle, libxl_ctx *ctx, libxl_dominf if(unlikely(!d->name)) { d->name = strdupz(xenstat_domain_name(domain)); netdata_fix_chart_id(d->name); - if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: domain id %d, uuid %s has name '%s'\n", d->id, d->uuid, d->name); + if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: domain id %u, uuid %s has name '%s'\n", d->id, d->uuid, d->name); } d->running = xenstat_domain_running(domain); @@ -569,7 +569,7 @@ static void print_domain_vcpu_chart_definition(char *type, struct domain_metrics } static void print_domain_vbd_oo_chart_definition(char *type, unsigned int vbd, int obsolete_flag) { - printf("CHART %s.oo_req_vbd%u '' 'VBD%u \"Out Of\" Requests' 'requests/s' 'vbd' 'xendomain.oo_req_vbd' line %d %d %s %s\n" + printf("CHART %s.oo_req_vbd%u '' 'VBD%u \"Out Of\" Requests' 'requests/s' 'vbd' 'xendomain.oo_req_vbd' line %u %d %s %s\n" , type , vbd , vbd @@ -582,7 +582,7 @@ static void print_domain_vbd_oo_chart_definition(char *type, unsigned int vbd, i } static void print_domain_vbd_requests_chart_definition(char *type, unsigned int vbd, int obsolete_flag) { - printf("CHART %s.requests_vbd%u '' 'VBD%u Requests' 'requests/s' 'vbd' 'xendomain.requests_vbd' line %d %d %s %s\n" + printf("CHART %s.requests_vbd%u '' 'VBD%u Requests' 'requests/s' 'vbd' 'xendomain.requests_vbd' line %u %d %s %s\n" , type , vbd , vbd @@ -596,7 +596,7 @@ static void print_domain_vbd_requests_chart_definition(char *type, unsigned int } static void print_domain_vbd_sectors_chart_definition(char *type, unsigned int vbd, int obsolete_flag) { - printf("CHART %s.sectors_vbd%u '' 'VBD%u Read/Written Sectors' 'sectors/s' 'vbd' 'xendomain.sectors_vbd' line %d %d %s %s\n" + printf("CHART %s.sectors_vbd%u '' 'VBD%u Read/Written Sectors' 'sectors/s' 'vbd' 'xendomain.sectors_vbd' line %u %d %s %s\n" , type , vbd , vbd @@ -610,7 +610,7 @@ static void print_domain_vbd_sectors_chart_definition(char *type, unsigned int v } static void print_domain_network_bytes_chart_definition(char *type, unsigned int network, int obsolete_flag) { - printf("CHART %s.bytes_network%u '' 'Network%u Received/Sent Bytes' 'kilobits/s' 'network' 'xendomain.bytes_network' line %d %d %s %s\n" + printf("CHART %s.bytes_network%u '' 'Network%u Received/Sent Bytes' 'kilobits/s' 'network' 'xendomain.bytes_network' line %u %d %s %s\n" , type , network , network @@ -624,7 +624,7 @@ static void print_domain_network_bytes_chart_definition(char *type, unsigned int } static void print_domain_network_packets_chart_definition(char *type, unsigned int network, int obsolete_flag) { - printf("CHART %s.packets_network%u '' 'Network%u Received/Sent Packets' 'packets/s' 'network' 'xendomain.packets_network' line %d %d %s %s\n" + printf("CHART %s.packets_network%u '' 'Network%u Received/Sent Packets' 'packets/s' 'network' 'xendomain.packets_network' line %u %d %s %s\n" , type , network , network @@ -638,7 +638,7 @@ static void print_domain_network_packets_chart_definition(char *type, unsigned i } static void print_domain_network_errors_chart_definition(char *type, unsigned int network, int obsolete_flag) { - printf("CHART %s.errors_network%u '' 'Network%u Receive/Transmit Errors' 'errors/s' 'network' 'xendomain.errors_network' line %d %d %s %s\n" + printf("CHART %s.errors_network%u '' 'Network%u Receive/Transmit Errors' 'errors/s' 'network' 'xendomain.errors_network' line %u %d %s %s\n" , type , network , network @@ -652,7 +652,7 @@ static void print_domain_network_errors_chart_definition(char *type, unsigned in } static void print_domain_network_drops_chart_definition(char *type, unsigned int network, int obsolete_flag) { - printf("CHART %s.drops_network%u '' 'Network%u Receive/Transmit Drops' 'drops/s' 'network' 'xendomain.drops_network' line %d %d %s %s\n" + printf("CHART %s.drops_network%u '' 'Network%u Receive/Transmit Drops' 'drops/s' 'network' 'xendomain.drops_network' line %u %d %s %s\n" , type , network , network @@ -808,7 +808,7 @@ static void xenstat_send_domain_metrics() { if(unlikely(vbd_m->oo_req_chart_generated || vbd_m->requests_chart_generated || vbd_m->sectors_chart_generated)) { - if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: mark charts as obsolete for vbd %d, domain '%s', id %d, uuid %s\n", vbd_m->id, d->name, d->id, d->uuid); + if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: mark charts as obsolete for vbd %u, domain '%s', id %u, uuid %s\n", vbd_m->id, d->name, d->id, d->uuid); print_domain_vbd_oo_chart_definition(type, vbd_m->id, CHART_IS_OBSOLETE); print_domain_vbd_requests_chart_definition(type, vbd_m->id, CHART_IS_OBSOLETE); print_domain_vbd_sectors_chart_definition(type, vbd_m->id, CHART_IS_OBSOLETE); @@ -895,7 +895,7 @@ static void xenstat_send_domain_metrics() { || network_m->packets_chart_generated || network_m->errors_chart_generated || network_m->drops_chart_generated)) - if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: mark charts as obsolete for network %d, domain '%s', id %d, uuid %s\n", network_m->id, d->name, d->id, d->uuid); + if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: mark charts as obsolete for network %u, domain '%s', id %u, uuid %s\n", network_m->id, d->name, d->id, d->uuid); print_domain_network_bytes_chart_definition(type, network_m->id, CHART_IS_OBSOLETE); print_domain_network_packets_chart_definition(type, network_m->id, CHART_IS_OBSOLETE); print_domain_network_errors_chart_definition(type, network_m->id, CHART_IS_OBSOLETE); @@ -908,7 +908,7 @@ static void xenstat_send_domain_metrics() { } } else{ - if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: mark charts as obsolete for domain '%s', id %d, uuid %s\n", d->name, d->id, d->uuid); + if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: mark charts as obsolete for domain '%s', id %u, uuid %s\n", d->name, d->id, d->uuid); print_domain_states_chart_definition(type, CHART_IS_OBSOLETE); print_domain_cpu_chart_definition(type, CHART_IS_OBSOLETE); print_domain_vcpu_chart_definition(type, d, CHART_IS_OBSOLETE); @@ -1006,12 +1006,16 @@ int main(int argc, char **argv) { if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: calling xenstat_init()\n"); xhandle = xenstat_init(); - if (xhandle == NULL) + if (xhandle == NULL) { error("XENSTAT: failed to initialize xenstat library."); + return 1; + } if(unlikely(debug)) fprintf(stderr, "xenstat.plugin: calling libxl_ctx_alloc()\n"); if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, NULL)) { error("XENSTAT: failed to initialize xl context."); + xenstat_uninit(xhandle); + return 1; } libxl_dominfo_init(&info); @@ -1062,4 +1066,6 @@ int main(int argc, char **argv) { libxl_ctx_free(ctx); xenstat_uninit(xhandle); info("XENSTAT process exiting"); + + return 0; } diff --git a/config.cmake.h.in b/config.cmake.h.in new file mode 100644 index 000000000..e0faaa4cf --- /dev/null +++ b/config.cmake.h.in @@ -0,0 +1,56 @@ +/* This file was generated by CMAKE from config.cmake.h.in */ + +#define VERSION "@NETDATA_VERSION@" + +#define CONFIGURE_COMMAND "cmake" + +#define NETDATA_USER "@NETDATA_USER@" + +/* Using a bundled copy of protobuf */ +// #undef BUNDLED_PROTOBUF +#define HAVE_PROTOBUF 1 + +#define likely(x) @LIKELY_MACRO@ +#define unlikely(x) @UNLIKELY_MACRO@ + +/* La boring stuff */ +#cmakedefine HAVE_STRUCT_TIMESPEC + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#cmakedefine HAVE_SYS_STATFS_H +#cmakedefine HAVE_SYS_STATVFS_H +#cmakedefine HAVE_INTTYPES_H +#cmakedefine HAVE_STDINT_H +#cmakedefine STRERROR_R_CHAR_P + +#cmakedefine MAJOR_IN_MKDEV +#cmakedefine MAJOR_IN_SYSMACROS + +#cmakedefine HAVE_CRYPTO + +#cmakedefine ENABLE_PROMETHEUS_REMOTE_WRITE + +/* they are defined as REQUIRED in CMakeLists.txt */ +#define NETDATA_WITH_ZLIB 1 +#define ENABLE_JSONC 1 + +#cmakedefine ENABLE_ML + +#cmakedefine HAVE_LIBBPF + +/* NSA spy stuff */ +#define ENABLE_HTTPS 1 +#cmakedefine01 HAVE_X509_VERIFY_PARAM_set1_host + +#cmakedefine ENABLE_ACLK +#define ENABLE_DBENGINE +#define ENABLE_COMPRESSION // pkg_check_modules(LIBLZ4 REQUIRED liblz4) +#cmakedefine ENABLE_APPS_PLUGIN + + +#define __always_unused @ALWAYS_UNUSED_MACRO@ +#define __maybe_unused @MAYBE_UNUSED_MACRO@ diff --git a/configure.ac b/configure.ac index 40cd3869d..7ab7db413 100644 --- a/configure.ac +++ b/configure.ac @@ -42,7 +42,17 @@ AC_PROG_CXX AC_PROG_INSTALL PKG_PROG_PKG_CONFIG AC_USE_SYSTEM_EXTENSIONS -AC_PROG_RANLIB + +AC_LANG_PUSH([C]) +AX_COMPILER_VENDOR +AC_LANG_POP([C]) +if test "$ax_cv_c_compiler_vendor" = "clang"; then + AC_CHECK_PROG([AR], [llvm-ar], [llvm-ar], [ar]) + AC_CHECK_PROG([RANLIB], [llvm-ranlib], [llvm-ranlib], [ranlib]) +else + AC_CHECK_TOOL([RANLIB], [ranlib]) + AC_CHECK_TOOL([AR], [ar]) +fi # ----------------------------------------------------------------------------- # configurable options @@ -269,6 +279,8 @@ if test "${enable_accept4}" != "no"; then AC_CHECK_FUNCS_ONCE(accept4) fi +AC_CHECK_FUNCS_ONCE(malloc_usable_size) + # ----------------------------------------------------------------------------- # operating system detection @@ -315,6 +327,10 @@ AM_CONDITIONAL([MACOS], [test "${build_target}" = "macos"]) AM_CONDITIONAL([LINUX], [test "${build_target}" = "linux"]) AC_MSG_RESULT([Host OS: ${build_target}]) +# ----------------------------------------------------------------------------- +# backtrace + +AC_SEARCH_LIBS([backtrace], [execinfo], [AC_DEFINE([HAVE_BACKTRACE], [1], [backtrace availability])]) # ----------------------------------------------------------------------------- # pthreads @@ -498,7 +514,6 @@ if test "${enable_dbengine}" != "no" -a "${UV_LIBS}" -a "${LZ4_LIBS}" -a "${SSL_ AC_DEFINE([ENABLE_DBENGINE], [1], [netdata dbengine usability]) OPTIONAL_LZ4_CFLAGS="${LZ4_CFLAGS}" OPTIONAL_LZ4_LIBS="${LZ4_LIBS}" - OPTIONAL_JUDY_CFLAGS="${JUDY_CFLAGS}" OPTIONAL_SSL_CFLAGS="${SSL_CFLAGS}" OPTIONAL_SSL_LIBS="${SSL_LIBS}" else @@ -1110,7 +1125,7 @@ fi # Check if submodules have not been fetched. Fail if ML was explicitly requested. AC_MSG_CHECKING([if git submodules are present for machine learning functionality]) -if test -f "ml/kmeans/dlib/dlib/all/source.cpp" -a -f "ml/json/single_include/nlohmann/json.hpp"; then +if test -f "ml/dlib/dlib/all/source.cpp" -a -f "ml/json/single_include/nlohmann/json.hpp"; then AC_MSG_RESULT([yes]) have_ml_submodules="yes" else @@ -1150,7 +1165,7 @@ fi AM_CONDITIONAL([ENABLE_ML], [test "${build_ml}" = "yes"]) if test "${build_ml}" = "yes"; then AC_DEFINE([ENABLE_ML], [1], [anomaly detection usability]) - OPTIONAL_ML_CFLAGS="-DDLIB_NO_GUI_SUPPORT -I \$(abs_top_srcdir)/ml/kmeans/dlib" + OPTIONAL_ML_CFLAGS="-DDLIB_NO_GUI_SUPPORT -I \$(abs_top_srcdir)/ml/dlib" OPTIONAL_ML_LIBS="" fi @@ -1530,6 +1545,30 @@ AC_SUBST([OPTIONAL_ATOMIC_LIBS]) AC_LANG_POP([C++]) +# ----------------------------------------------------------------------------- + +AC_MSG_CHECKING(whether we can use dlsym) +OLD_LIBS="${LIBS}" +LIBS="-ldl" +AC_LINK_IFELSE([AC_LANG_SOURCE([[ + #include + static void *(*libc_malloc)(size_t); + int main() { + libc_malloc = dlsym(RTLD_NEXT, "malloc"); + } +]])], CAN_USE_DLSYM=yes, CAN_USE_DLSYM=no) +LIBS="${OLD_LIBS}" +AC_MSG_RESULT($CAN_USE_DLSYM) + +if test "x$CAN_USE_DLSYM" = xyes; then + AC_DEFINE([HAVE_DLSYM], [1], [dlsym usability]) + OPTIONAL_DL_LIBS="-ldl" +fi +AC_SUBST([OPTIONAL_DL_LIBS]) + +# ----------------------------------------------------------------------------- + + AC_DEFINE_UNQUOTED([NETDATA_USER], ["${with_user}"], [use this user to drop privileged]) varlibdir="${localstatedir}/lib/netdata" @@ -1541,6 +1580,8 @@ configdir="${sysconfdir}/netdata" libconfigdir="${libdir}/netdata/conf.d" logdir="${localstatedir}/log/netdata" pluginsdir="${libexecdir}/netdata/plugins.d" +netdata_user="${with_user}" +libsysdir="${libdir}/netdata/system" AC_SUBST([varlibdir]) AC_SUBST([registrydir]) @@ -1552,12 +1593,14 @@ AC_SUBST([libconfigdir]) AC_SUBST([logdir]) AC_SUBST([pluginsdir]) AC_SUBST([webdir]) +AC_SUBST([netdata_user]) +AC_SUBST([libsysdir]) CFLAGS="${originalCFLAGS} ${OPTIONAL_LTO_CFLAGS} ${OPTIONAL_PROTOBUF_CFLAGS} ${OPTIONAL_MATH_CFLAGS} ${OPTIONAL_NFACCT_CFLAGS} \ ${OPTIONAL_ZLIB_CFLAGS} ${OPTIONAL_UUID_CFLAGS} \ ${OPTIONAL_LIBCAP_CFLAGS} ${OPTIONAL_IPMIMONITORING_CFLAGS} ${OPTIONAL_CUPS_CFLAGS} ${OPTIONAL_XENSTAT_FLAGS} \ ${OPTIONAL_KINESIS_CFLAGS} ${OPTIONAL_PUBSUB_CFLAGS} ${OPTIONAL_PROMETHEUS_REMOTE_WRITE_CFLAGS} \ - ${OPTIONAL_MONGOC_CFLAGS} ${LWS_CFLAGS} ${OPTIONAL_JSONC_STATIC_CFLAGS} ${OPTIONAL_BPF_CFLAGS} ${OPTIONAL_JUDY_CFLAGS} \ + ${OPTIONAL_MONGOC_CFLAGS} ${LWS_CFLAGS} ${OPTIONAL_JSONC_STATIC_CFLAGS} ${OPTIONAL_BPF_CFLAGS} ${JUDY_CFLAGS} \ ${OPTIONAL_ACLK_CFLAGS} ${OPTIONAL_ML_CFLAGS} ${OPTIONAL_ML_TESTS_CFLAGS} ${OPTIONAL_OS_DEP_CFLAGS}" CXXFLAGS="${CFLAGS} ${CXX11FLAG}" @@ -1657,7 +1700,6 @@ AC_CONFIG_FILES([ collectors/apps.plugin/Makefile collectors/cgroups.plugin/Makefile collectors/charts.d.plugin/Makefile - collectors/checks.plugin/Makefile collectors/diskspace.plugin/Makefile collectors/timex.plugin/Makefile collectors/fping.plugin/Makefile @@ -1680,7 +1722,6 @@ AC_CONFIG_FILES([ daemon/Makefile database/Makefile database/engine/Makefile - database/engine/metadata_log/Makefile database/ram/Makefile database/sqlite/Makefile diagrams/Makefile @@ -1696,8 +1737,6 @@ AC_CONFIG_FILES([ exporting/tests/Makefile health/Makefile health/notifications/Makefile - ml/Makefile - ml/kmeans/Makefile libnetdata/Makefile libnetdata/tests/Makefile libnetdata/adaptive_resortable_list/Makefile @@ -1718,6 +1757,7 @@ AC_CONFIG_FILES([ libnetdata/simple_pattern/Makefile libnetdata/socket/Makefile libnetdata/statistical/Makefile + libnetdata/string/Makefile libnetdata/storage_number/Makefile libnetdata/storage_number/tests/Makefile libnetdata/threads/Makefile diff --git a/contrib/debian/netdata.postinst b/contrib/debian/netdata.postinst index cf6a76060..daea8cb40 100644 --- a/contrib/debian/netdata.postinst +++ b/contrib/debian/netdata.postinst @@ -64,7 +64,7 @@ case "$1" in fi if [ -f "/usr/libexec/netdata/plugins.d/go.d.plugin" ]; then - setcap cap_net_admin+epi /usr/libexec/netdata/plugins.d/go.d.plugin + setcap "cap_net_admin+epi cap_net_raw=eip" /usr/libexec/netdata/plugins.d/go.d.plugin fi chmod 4750 /usr/libexec/netdata/plugins.d/cgroup-network diff --git a/coverity-scan.sh b/coverity-scan.sh index 63605b5b8..9737545d9 100755 --- a/coverity-scan.sh +++ b/coverity-scan.sh @@ -40,7 +40,7 @@ set -e INSTALL_DIR="/opt" # the version of coverity to use -COVERITY_BUILD_VERSION="${COVERITY_BUILD_VERSION:-cov-analysis-linux64-2021.12}" +COVERITY_BUILD_VERSION="${COVERITY_BUILD_VERSION:-cov-analysis-linux64-2022.06}" # TODO: For some reasons this does not fully load on Debian 10 (Haven't checked if it happens on other distros yet), it breaks source packaging/installer/functions.sh || echo "Failed to fully load the functions library" diff --git a/daemon/README.md b/daemon/README.md index 3ebb405b2..c5951c694 100644 --- a/daemon/README.md +++ b/daemon/README.md @@ -116,7 +116,7 @@ The command line options of the Netdata 1.10.0 version are the following: | '-' '-' '-' '-' real-time performance monitoring, done right! +----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+---> - Copyright (C) 2016-2020, Netdata, Inc. + Copyright (C) 2016-2022, Netdata, Inc. Released under GNU General Public License v3 or later. All rights reserved. @@ -127,7 +127,8 @@ The command line options of the Netdata 1.10.0 version are the following: License : https://github.com/netdata/netdata/blob/master/LICENSE.md Twitter : https://twitter.com/linuxnetdata - Facebook : https://www.facebook.com/linuxnetdata/ + LinkedIn : https://linkedin.com/company/netdata-cloud/ + Facebook : https://facebook.com/linuxnetdata/ SYNOPSIS: netdata [options] @@ -191,7 +192,7 @@ The command line options of the Netdata 1.10.0 version are the following: -W simple-pattern pattern string Check if string matches pattern and exit. - -W "claim -token=TOKEN -rooms=ROOM1,ROOM2 url=https://app.netdata.cloud" + -W "claim -token=TOKEN -rooms=ROOM1,ROOM2 url=https://api.netdata.cloud" Connect the agent to the workspace rooms pointed to by TOKEN and ROOM*. Signals netdata handles: diff --git a/daemon/analytics.c b/daemon/analytics.c index 370818b8a..3d0e514d6 100644 --- a/daemon/analytics.c +++ b/daemon/analytics.c @@ -10,8 +10,8 @@ extern void analytics_build_info (BUFFER *b); extern int aclk_connected; struct collector { - char *plugin; - char *module; + const char *plugin; + const char *module; }; struct array_printer { @@ -249,8 +249,7 @@ void analytics_exporters(void) buffer_free(bi); } -int collector_counter_callb(const char *name, void *entry, void *data) { - (void)name; +int collector_counter_callb(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data) { struct array_printer *ap = (struct array_printer *)data; struct collector *col = (struct collector *)entry; @@ -279,19 +278,22 @@ int collector_counter_callb(const char *name, void *entry, void *data) { void analytics_collectors(void) { RRDSET *st; - DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED); char name[500]; BUFFER *bt = buffer_create(1000); - rrdset_foreach_read(st, localhost) - { - if (rrdset_is_available_for_viewers(st)) { - struct collector col = { .plugin = st->plugin_name ? st->plugin_name : "", - .module = st->module_name ? st->module_name : "" }; - snprintfz(name, 499, "%s:%s", col.plugin, col.module); - dictionary_set(dict, name, &col, sizeof(struct collector)); - } + rrdset_foreach_read(st, localhost) { + if(!rrdset_is_available_for_viewers(st)) + continue; + + struct collector col = { + .plugin = rrdset_plugin_name(st), + .module = rrdset_module_name(st) + }; + snprintfz(name, 499, "%s:%s", col.plugin, col.module); + dictionary_set(dict, name, &col, sizeof(struct collector)); } + rrdset_foreach_done(st); struct array_printer ap; ap.c = 0; @@ -335,11 +337,12 @@ void analytics_alarms_notifications(void) BUFFER *b = buffer_create(1000); int cnt = 0; - FILE *fp = mypopen(script, &command_pid); - if (fp) { + FILE *fp_child_input; + FILE *fp_child_output = netdata_popen(script, &command_pid, &fp_child_input); + if (fp_child_output) { char line[200 + 1]; - while (fgets(line, 200, fp) != NULL) { + while (fgets(line, 200, fp_child_output) != NULL) { char *end = line; while (*end && *end != '\n') end++; @@ -352,7 +355,7 @@ void analytics_alarms_notifications(void) cnt++; } - mypclose(fp, command_pid); + netdata_pclose(fp_child_input, fp_child_output, command_pid); } freez(script); @@ -382,8 +385,8 @@ void analytics_https(void) BUFFER *b = buffer_create(30); #ifdef ENABLE_HTTPS analytics_exporting_connectors_ssl(b); - buffer_strcat(b, netdata_client_ctx && localhost->ssl.flags == NETDATA_SSL_HANDSHAKE_COMPLETE && __atomic_load_n(&localhost->rrdpush_sender_connected, __ATOMIC_SEQ_CST) ? "streaming|" : "|"); - buffer_strcat(b, netdata_srv_ctx ? "web" : ""); + buffer_strcat(b, netdata_ssl_client_ctx && rrdhost_flag_check(localhost, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED) && localhost->sender->ssl.flags == NETDATA_SSL_HANDSHAKE_COMPLETE ? "streaming|" : "|"); + buffer_strcat(b, netdata_ssl_srv_ctx ? "web" : ""); #else buffer_strcat(b, "||"); #endif @@ -396,12 +399,11 @@ void analytics_charts(void) { RRDSET *st; int c = 0; + rrdset_foreach_read(st, localhost) - { - if (rrdset_is_available_for_viewers(st)) { - c++; - } - } + if(rrdset_is_available_for_viewers(st)) c++; + rrdset_foreach_done(st); + { char b[7]; snprintfz(b, 6, "%d", c); @@ -413,22 +415,19 @@ void analytics_metrics(void) { RRDSET *st; long int dimensions = 0; - RRDDIM *rd; - rrdset_foreach_read(st, localhost) - { - rrdset_rdlock(st); - + rrdset_foreach_read(st, localhost) { if (rrdset_is_available_for_viewers(st)) { - rrddim_foreach_read(rd, st) - { - if (rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if (rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) continue; dimensions++; } + rrddim_foreach_done(rd); } - - rrdset_unlock(st); } + rrdset_foreach_done(st); + { char b[7]; snprintfz(b, 6, "%ld", dimensions); @@ -441,7 +440,7 @@ void analytics_alarms(void) int alarm_warn = 0, alarm_crit = 0, alarm_normal = 0; char b[10]; RRDCALC *rc; - for (rc = localhost->alarms; rc; rc = rc->next) { + foreach_rrdcalc_in_rrdhost_read(localhost, rc) { if (unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) continue; @@ -456,6 +455,7 @@ void analytics_alarms(void) alarm_normal++; } } + foreach_rrdcalc_in_rrdhost_done(rc); snprintfz(b, 9, "%d", alarm_normal); analytics_set_data(&analytics_data.netdata_alarms_normal, b); @@ -525,21 +525,16 @@ void analytics_gather_immutable_meta_data(void) */ void analytics_gather_mutable_meta_data(void) { - rrdhost_rdlock(localhost); - analytics_collectors(); analytics_alarms(); analytics_charts(); analytics_metrics(); analytics_aclk(); - - rrdhost_unlock(localhost); - analytics_mirrored_hosts(); analytics_alarms_notifications(); analytics_set_data( - &analytics_data.netdata_config_is_parent, (localhost->next || configured_as_parent()) ? "true" : "false"); + &analytics_data.netdata_config_is_parent, (rrdhost_hosts_available() > 1 || configured_as_parent()) ? "true" : "false"); char *claim_id = get_agent_claimid(); analytics_set_data(&analytics_data.netdata_host_agent_claimed, claim_id ? "true" : "false"); @@ -1022,11 +1017,12 @@ void send_statistics(const char *action, const char *action_result, const char * info("%s '%s' '%s' '%s'", as_script, action, action_result, action_data); - FILE *fp = mypopen(command_to_run, &command_pid); - if (fp) { + FILE *fp_child_input; + FILE *fp_child_output = netdata_popen(command_to_run, &command_pid, &fp_child_input); + if (fp_child_output) { char buffer[4 + 1]; - char *s = fgets(buffer, 4, fp); - int exit_code = mypclose(fp, command_pid); + char *s = fgets(buffer, 4, fp_child_output); + int exit_code = netdata_pclose(fp_child_input, fp_child_output, command_pid); if (exit_code) error("Execution of anonymous statistics script returned %d.", exit_code); if (s && strncmp(buffer, "200", 3)) diff --git a/daemon/analytics.h b/daemon/analytics.h index 76c1cbb7e..d1ffcec18 100644 --- a/daemon/analytics.h +++ b/daemon/analytics.h @@ -69,18 +69,18 @@ struct analytics_data { uint8_t dashboard_hits; }; -extern void analytics_get_data(char *name, BUFFER *wb); -extern void set_late_global_environment(void); -extern void analytics_free_data(void); -extern void set_global_environment(void); -extern void send_statistics(const char *action, const char *action_result, const char *action_data); -extern void analytics_log_shell(void); -extern void analytics_log_json(void); -extern void analytics_log_prometheus(void); -extern void analytics_log_dashboard(void); -extern void analytics_gather_mutable_meta_data(void); -extern void analytics_report_oom_score(long long int score); -extern void get_system_timezone(void); +void analytics_get_data(char *name, BUFFER *wb); +void set_late_global_environment(void); +void analytics_free_data(void); +void set_global_environment(void); +void send_statistics(const char *action, const char *action_result, const char *action_data); +void analytics_log_shell(void); +void analytics_log_json(void); +void analytics_log_prometheus(void); +void analytics_log_dashboard(void); +void analytics_gather_mutable_meta_data(void); +void analytics_report_oom_score(long long int score); +void get_system_timezone(void); extern struct analytics_data analytics_data; diff --git a/daemon/buildinfo.c b/daemon/buildinfo.c index 0a64547af..ef813a961 100644 --- a/daemon/buildinfo.c +++ b/daemon/buildinfo.c @@ -197,6 +197,11 @@ #define FEAT_YES_NO(x) ((x) ? "YES" : "NO") +#ifdef NETDATA_TRACE_ALLOCATIONS +#define FEAT_TRACE_ALLOC 1 +#else +#define FEAT_TRACE_ALLOC 0 +#endif char *get_value_from_key(char *buffer, char *key) { char *s = NULL, *t = NULL; @@ -266,9 +271,7 @@ void print_build_info(void) { printf(" dbengine: %s\n", FEAT_YES_NO(FEAT_DBENGINE)); printf(" Native HTTPS: %s\n", FEAT_YES_NO(FEAT_NATIVE_HTTPS)); printf(" Netdata Cloud: %s %s\n", FEAT_YES_NO(FEAT_CLOUD), FEAT_CLOUD_MSG); - printf(" ACLK Next Generation: %s\n", FEAT_YES_NO(FEAT_CLOUD)); - printf(" ACLK-NG New Cloud Protocol: %s\n", FEAT_YES_NO(1)); - printf(" ACLK Legacy: %s\n", FEAT_YES_NO(0)); + printf(" ACLK: %s\n", FEAT_YES_NO(FEAT_CLOUD)); printf(" TLS Host Verification: %s\n", FEAT_YES_NO(FEAT_TLS_HOST_VERIFY)); printf(" Machine Learning: %s\n", FEAT_YES_NO(FEAT_ML)); printf(" Stream Compression: %s\n", FEAT_YES_NO(FEAT_STREAM_COMPRESSION)); @@ -300,6 +303,9 @@ void print_build_info(void) { printf(" GCP PubSub: %s\n", FEAT_YES_NO(FEAT_PUBSUB)); printf(" MongoDB: %s\n", FEAT_YES_NO(FEAT_MONGO)); printf(" Prometheus Remote Write: %s\n", FEAT_YES_NO(FEAT_REMOTE_WRITE)); + + printf("Debug/Developer Features:\n"); + printf(" Trace Allocations: %s\n", FEAT_YES_NO(FEAT_TRACE_ALLOC)); }; #define FEAT_JSON_BOOL(x) ((x) ? "true" : "false") @@ -318,9 +324,7 @@ void print_build_info_json(void) { #else printf(" \"cloud-disabled\": false,\n"); #endif - printf(" \"aclk-ng\": %s,\n", FEAT_JSON_BOOL(FEAT_CLOUD)); - printf(" \"aclk-ng-new-cloud-proto\": %s,\n", FEAT_JSON_BOOL(1)); - printf(" \"aclk-legacy\": %s,\n", FEAT_JSON_BOOL(0)); + printf(" \"aclk\": %s,\n", FEAT_JSON_BOOL(FEAT_CLOUD)); printf(" \"tls-host-verify\": %s,\n", FEAT_JSON_BOOL(FEAT_TLS_HOST_VERIFY)); printf(" \"machine-learning\": %s\n", FEAT_JSON_BOOL(FEAT_ML)); @@ -358,6 +362,8 @@ void print_build_info_json(void) { printf(" \"mongodb\": %s,\n", FEAT_JSON_BOOL(FEAT_MONGO)); printf(" \"prom-remote-write\": %s\n", FEAT_JSON_BOOL(FEAT_REMOTE_WRITE)); printf(" }\n"); + printf(" \"debug-n-devel\": {\n"); + printf(" \"trace-allocations\": %s\n }\n",FEAT_JSON_BOOL(FEAT_TRACE_ALLOC)); printf("}\n"); }; @@ -377,7 +383,7 @@ void analytics_build_info(BUFFER *b) { add_to_bi(b, "Native HTTPS"); #endif #ifdef ENABLE_ACLK - add_to_bi(b, "Netdata Cloud|ACLK Next Generation|New Cloud Protocol Support"); + add_to_bi(b, "Netdata Cloud"); #endif #if (FEAT_TLS_HOST_VERIFY!=0) add_to_bi(b, "TLS Host Verification"); @@ -458,4 +464,7 @@ void analytics_build_info(BUFFER *b) { #ifdef ENABLE_PROMETHEUS_REMOTE_WRITE add_to_bi(b, "Prometheus Remote Write"); #endif +#ifdef NETDATA_TRACE_ALLOCATIONS + add_to_bi(b, "DebugTraceAlloc"); +#endif } diff --git a/daemon/buildinfo.h b/daemon/buildinfo.h index 542a0e92e..d3b439fc3 100644 --- a/daemon/buildinfo.h +++ b/daemon/buildinfo.h @@ -3,12 +3,12 @@ #ifndef NETDATA_BUILDINFO_H #define NETDATA_BUILDINFO_H 1 -extern void print_build_info(void); +void print_build_info(void); -extern void print_build_info_json(void); +void print_build_info_json(void); -extern char *get_value_from_key(char *buffer, char *key); +char *get_value_from_key(char *buffer, char *key); -extern void get_install_type(char **install_type, char **prebuilt_arch, char **prebuilt_dist); +void get_install_type(char **install_type, char **prebuilt_arch, char **prebuilt_dist); #endif // NETDATA_BUILDINFO_H diff --git a/daemon/commands.c b/daemon/commands.c index 13d8dbd40..6288ee59b 100644 --- a/daemon/commands.c +++ b/daemon/commands.c @@ -217,7 +217,7 @@ static cmd_status_t cmd_reload_labels_execute(char *args, char **message) reload_host_labels(); BUFFER *wb = buffer_create(10); - rrdlabels_log_to_buffer(localhost->host_labels, wb); + rrdlabels_log_to_buffer(localhost->rrdlabels, wb); (*message)=strdupz(buffer_tostring(wb)); buffer_free(wb); @@ -515,7 +515,7 @@ static void pipe_read_cb(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf } else if (nread) { size_t to_copy; - to_copy = MIN(nread, MAX_COMMAND_LENGTH - 1 - cmd_ctx->command_string_size); + to_copy = MIN((size_t) nread, MAX_COMMAND_LENGTH - 1 - cmd_ctx->command_string_size); memcpy(cmd_ctx->command_string + cmd_ctx->command_string_size, buf->base, to_copy); cmd_ctx->command_string_size += to_copy; cmd_ctx->command_string[cmd_ctx->command_string_size] = '\0'; diff --git a/daemon/commands.h b/daemon/commands.h index 1253e2dc1..f0e38ce93 100644 --- a/daemon/commands.h +++ b/daemon/commands.h @@ -76,7 +76,7 @@ typedef struct command_info { typedef void (command_lock_t) (unsigned index); cmd_status_t execute_command(cmd_t idx, char *args, char **message); -extern void commands_init(void); -extern void commands_exit(void); +void commands_init(void); +void commands_exit(void); #endif //NETDATA_COMMANDS_H diff --git a/daemon/common.h b/daemon/common.h index 2a45ffe70..f3d868661 100644 --- a/daemon/common.h +++ b/daemon/common.h @@ -69,7 +69,7 @@ #include "claim/claim.h" // netdata agent cloud link -#include "aclk/aclk_api.h" +#include "aclk/aclk.h" // global GUID map functions diff --git a/daemon/daemon.h b/daemon/daemon.h index bec3df9fc..2a8a58ec6 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -3,14 +3,14 @@ #ifndef NETDATA_DAEMON_H #define NETDATA_DAEMON_H 1 -extern int become_user(const char *username, int pid_fd); +int become_user(const char *username, int pid_fd); -extern int become_daemon(int dont_fork, const char *user); +int become_daemon(int dont_fork, const char *user); -extern void netdata_cleanup_and_exit(int i); -extern void send_statistics(const char *action, const char *action_result, const char *action_data); +void netdata_cleanup_and_exit(int i); +void send_statistics(const char *action, const char *action_result, const char *action_data); -extern void get_netdata_execution_path(void); +void get_netdata_execution_path(void); extern char pidfile[]; extern char exepath[]; diff --git a/daemon/global_statistics.c b/daemon/global_statistics.c index 249369519..53fd6c45a 100644 --- a/daemon/global_statistics.c +++ b/daemon/global_statistics.c @@ -9,27 +9,72 @@ #define WORKER_JOB_WORKERS 2 #define WORKER_JOB_DBENGINE 3 #define WORKER_JOB_HEARTBEAT 4 +#define WORKER_JOB_STRINGS 5 +#define WORKER_JOB_DICTIONARIES 6 +#define WORKER_JOB_MALLOC_TRACE 7 -#if WORKER_UTILIZATION_MAX_JOB_TYPES < 5 -#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 5 +#if WORKER_UTILIZATION_MAX_JOB_TYPES < 8 +#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 8 #endif -static struct global_statistics { - volatile uint16_t connected_clients; - - volatile uint64_t web_requests; - volatile uint64_t web_usec; - volatile uint64_t web_usec_max; - volatile uint64_t bytes_received; - volatile uint64_t bytes_sent; - volatile uint64_t content_size; - volatile uint64_t compressed_content_size; +bool global_statistics_enabled = true; - volatile uint64_t web_client_count; +static struct global_statistics { + uint16_t connected_clients; + + uint64_t web_requests; + uint64_t web_usec; + uint64_t web_usec_max; + uint64_t bytes_received; + uint64_t bytes_sent; + uint64_t content_size; + uint64_t compressed_content_size; + + uint64_t web_client_count; + + uint64_t api_data_queries_made; + uint64_t api_data_db_points_read; + uint64_t api_data_result_points_generated; + + uint64_t api_weights_queries_made; + uint64_t api_weights_db_points_read; + uint64_t api_weights_result_points_generated; + + uint64_t api_badges_queries_made; + uint64_t api_badges_db_points_read; + uint64_t api_badges_result_points_generated; + + uint64_t health_queries_made; + uint64_t health_db_points_read; + uint64_t health_result_points_generated; + + uint64_t ml_queries_made; + uint64_t ml_db_points_read; + uint64_t ml_result_points_generated; + + uint64_t exporters_queries_made; + uint64_t exporters_db_points_read; + + uint64_t backfill_queries_made; + uint64_t backfill_db_points_read; + + uint64_t db_points_stored_per_tier[RRD_STORAGE_TIERS]; + + uint64_t sqlite3_queries_made; + uint64_t sqlite3_queries_ok; + uint64_t sqlite3_queries_failed; + uint64_t sqlite3_queries_failed_busy; + uint64_t sqlite3_queries_failed_locked; + uint64_t sqlite3_rows; + uint64_t sqlite3_metadata_cache_hit; + uint64_t sqlite3_context_cache_hit; + uint64_t sqlite3_metadata_cache_miss; + uint64_t sqlite3_context_cache_miss; + uint64_t sqlite3_metadata_cache_spill; + uint64_t sqlite3_context_cache_spill; + uint64_t sqlite3_metadata_cache_write; + uint64_t sqlite3_context_cache_write; - volatile uint64_t rrdr_queries_made; - volatile uint64_t rrdr_db_points_read; - volatile uint64_t rrdr_result_points_generated; } global_statistics = { .connected_clients = 0, .web_requests = 0, @@ -40,22 +85,98 @@ static struct global_statistics { .compressed_content_size = 0, .web_client_count = 1, - .rrdr_queries_made = 0, - .rrdr_db_points_read = 0, - .rrdr_result_points_generated = 0, + .api_data_queries_made = 0, + .api_data_db_points_read = 0, + .api_data_result_points_generated = 0, }; -void rrdr_query_completed(uint64_t db_points_read, uint64_t result_points_generated) { - __atomic_fetch_add(&global_statistics.rrdr_queries_made, 1, __ATOMIC_RELAXED); - __atomic_fetch_add(&global_statistics.rrdr_db_points_read, db_points_read, __ATOMIC_RELAXED); - __atomic_fetch_add(&global_statistics.rrdr_result_points_generated, result_points_generated, __ATOMIC_RELAXED); +void global_statistics_rrdset_done_chart_collection_completed(size_t *points_read_per_tier_array) { + for(size_t tier = 0; tier < storage_tiers ;tier++) { + __atomic_fetch_add(&global_statistics.db_points_stored_per_tier[tier], points_read_per_tier_array[tier], __ATOMIC_RELAXED); + points_read_per_tier_array[tier] = 0; + } +} + +void global_statistics_ml_query_completed(size_t points_read) { + __atomic_fetch_add(&global_statistics.ml_queries_made, 1, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.ml_db_points_read, points_read, __ATOMIC_RELAXED); +} + +void global_statistics_exporters_query_completed(size_t points_read) { + __atomic_fetch_add(&global_statistics.exporters_queries_made, 1, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.exporters_db_points_read, points_read, __ATOMIC_RELAXED); +} + +void global_statistics_backfill_query_completed(size_t points_read) { + __atomic_fetch_add(&global_statistics.backfill_queries_made, 1, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.backfill_db_points_read, points_read, __ATOMIC_RELAXED); +} + +void global_statistics_sqlite3_query_completed(bool success, bool busy, bool locked) { + __atomic_fetch_add(&global_statistics.sqlite3_queries_made, 1, __ATOMIC_RELAXED); + + if(success) { + __atomic_fetch_add(&global_statistics.sqlite3_queries_ok, 1, __ATOMIC_RELAXED); + } + else { + __atomic_fetch_add(&global_statistics.sqlite3_queries_failed, 1, __ATOMIC_RELAXED); + + if(busy) + __atomic_fetch_add(&global_statistics.sqlite3_queries_failed_busy, 1, __ATOMIC_RELAXED); + + if(locked) + __atomic_fetch_add(&global_statistics.sqlite3_queries_failed_locked, 1, __ATOMIC_RELAXED); + } +} + +void global_statistics_sqlite3_row_completed(void) { + __atomic_fetch_add(&global_statistics.sqlite3_rows, 1, __ATOMIC_RELAXED); +} + +void global_statistics_rrdr_query_completed(size_t queries, uint64_t db_points_read, uint64_t result_points_generated, QUERY_SOURCE query_source) { + switch(query_source) { + case QUERY_SOURCE_API_DATA: + __atomic_fetch_add(&global_statistics.api_data_queries_made, queries, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.api_data_db_points_read, db_points_read, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.api_data_result_points_generated, result_points_generated, __ATOMIC_RELAXED); + break; + + case QUERY_SOURCE_ML: + __atomic_fetch_add(&global_statistics.ml_queries_made, queries, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.ml_db_points_read, db_points_read, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.ml_result_points_generated, result_points_generated, __ATOMIC_RELAXED); + break; + + case QUERY_SOURCE_API_WEIGHTS: + __atomic_fetch_add(&global_statistics.api_weights_queries_made, queries, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.api_weights_db_points_read, db_points_read, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.api_weights_result_points_generated, result_points_generated, __ATOMIC_RELAXED); + break; + + case QUERY_SOURCE_API_BADGE: + __atomic_fetch_add(&global_statistics.api_badges_queries_made, queries, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.api_badges_db_points_read, db_points_read, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.api_badges_result_points_generated, result_points_generated, __ATOMIC_RELAXED); + break; + + case QUERY_SOURCE_HEALTH: + __atomic_fetch_add(&global_statistics.health_queries_made, queries, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.health_db_points_read, db_points_read, __ATOMIC_RELAXED); + __atomic_fetch_add(&global_statistics.health_result_points_generated, result_points_generated, __ATOMIC_RELAXED); + break; + + default: + case QUERY_SOURCE_UNITTEST: + case QUERY_SOURCE_UNKNOWN: + break; + } } -void finished_web_request_statistics(uint64_t dt, - uint64_t bytes_received, - uint64_t bytes_sent, - uint64_t content_size, - uint64_t compressed_content_size) { +void global_statistics_web_request_completed(uint64_t dt, + uint64_t bytes_received, + uint64_t bytes_sent, + uint64_t content_size, + uint64_t compressed_content_size) { uint64_t old_web_usec_max = global_statistics.web_usec_max; while(dt > old_web_usec_max) __atomic_compare_exchange(&global_statistics.web_usec_max, &old_web_usec_max, &dt, 1, __ATOMIC_RELAXED, __ATOMIC_RELAXED); @@ -68,35 +189,77 @@ void finished_web_request_statistics(uint64_t dt, __atomic_fetch_add(&global_statistics.compressed_content_size, compressed_content_size, __ATOMIC_RELAXED); } -uint64_t web_client_connected(void) { +uint64_t global_statistics_web_client_connected(void) { __atomic_fetch_add(&global_statistics.connected_clients, 1, __ATOMIC_RELAXED); return __atomic_fetch_add(&global_statistics.web_client_count, 1, __ATOMIC_RELAXED); } -void web_client_disconnected(void) { +void global_statistics_web_client_disconnected(void) { __atomic_fetch_sub(&global_statistics.connected_clients, 1, __ATOMIC_RELAXED); } - static inline void global_statistics_copy(struct global_statistics *gs, uint8_t options) { - gs->connected_clients = __atomic_fetch_add(&global_statistics.connected_clients, 0, __ATOMIC_RELAXED); - gs->web_requests = __atomic_fetch_add(&global_statistics.web_requests, 0, __ATOMIC_RELAXED); - gs->web_usec = __atomic_fetch_add(&global_statistics.web_usec, 0, __ATOMIC_RELAXED); - gs->web_usec_max = __atomic_fetch_add(&global_statistics.web_usec_max, 0, __ATOMIC_RELAXED); - gs->bytes_received = __atomic_fetch_add(&global_statistics.bytes_received, 0, __ATOMIC_RELAXED); - gs->bytes_sent = __atomic_fetch_add(&global_statistics.bytes_sent, 0, __ATOMIC_RELAXED); - gs->content_size = __atomic_fetch_add(&global_statistics.content_size, 0, __ATOMIC_RELAXED); - gs->compressed_content_size = __atomic_fetch_add(&global_statistics.compressed_content_size, 0, __ATOMIC_RELAXED); - gs->web_client_count = __atomic_fetch_add(&global_statistics.web_client_count, 0, __ATOMIC_RELAXED); - - gs->rrdr_queries_made = __atomic_fetch_add(&global_statistics.rrdr_queries_made, 0, __ATOMIC_RELAXED); - gs->rrdr_db_points_read = __atomic_fetch_add(&global_statistics.rrdr_db_points_read, 0, __ATOMIC_RELAXED); - gs->rrdr_result_points_generated = __atomic_fetch_add(&global_statistics.rrdr_result_points_generated, 0, __ATOMIC_RELAXED); + gs->connected_clients = __atomic_load_n(&global_statistics.connected_clients, __ATOMIC_RELAXED); + gs->web_requests = __atomic_load_n(&global_statistics.web_requests, __ATOMIC_RELAXED); + gs->web_usec = __atomic_load_n(&global_statistics.web_usec, __ATOMIC_RELAXED); + gs->web_usec_max = __atomic_load_n(&global_statistics.web_usec_max, __ATOMIC_RELAXED); + gs->bytes_received = __atomic_load_n(&global_statistics.bytes_received, __ATOMIC_RELAXED); + gs->bytes_sent = __atomic_load_n(&global_statistics.bytes_sent, __ATOMIC_RELAXED); + gs->content_size = __atomic_load_n(&global_statistics.content_size, __ATOMIC_RELAXED); + gs->compressed_content_size = __atomic_load_n(&global_statistics.compressed_content_size, __ATOMIC_RELAXED); + gs->web_client_count = __atomic_load_n(&global_statistics.web_client_count, __ATOMIC_RELAXED); + + gs->api_data_queries_made = __atomic_load_n(&global_statistics.api_data_queries_made, __ATOMIC_RELAXED); + gs->api_data_db_points_read = __atomic_load_n(&global_statistics.api_data_db_points_read, __ATOMIC_RELAXED); + gs->api_data_result_points_generated = __atomic_load_n(&global_statistics.api_data_result_points_generated, __ATOMIC_RELAXED); + + gs->api_weights_queries_made = __atomic_load_n(&global_statistics.api_weights_queries_made, __ATOMIC_RELAXED); + gs->api_weights_db_points_read = __atomic_load_n(&global_statistics.api_weights_db_points_read, __ATOMIC_RELAXED); + gs->api_weights_result_points_generated = __atomic_load_n(&global_statistics.api_weights_result_points_generated, __ATOMIC_RELAXED); + + gs->api_badges_queries_made = __atomic_load_n(&global_statistics.api_badges_queries_made, __ATOMIC_RELAXED); + gs->api_badges_db_points_read = __atomic_load_n(&global_statistics.api_badges_db_points_read, __ATOMIC_RELAXED); + gs->api_badges_result_points_generated = __atomic_load_n(&global_statistics.api_badges_result_points_generated, __ATOMIC_RELAXED); + + gs->health_queries_made = __atomic_load_n(&global_statistics.health_queries_made, __ATOMIC_RELAXED); + gs->health_db_points_read = __atomic_load_n(&global_statistics.health_db_points_read, __ATOMIC_RELAXED); + gs->health_result_points_generated = __atomic_load_n(&global_statistics.health_result_points_generated, __ATOMIC_RELAXED); + + gs->ml_queries_made = __atomic_load_n(&global_statistics.ml_queries_made, __ATOMIC_RELAXED); + gs->ml_db_points_read = __atomic_load_n(&global_statistics.ml_db_points_read, __ATOMIC_RELAXED); + gs->ml_result_points_generated = __atomic_load_n(&global_statistics.ml_result_points_generated, __ATOMIC_RELAXED); + + gs->exporters_queries_made = __atomic_load_n(&global_statistics.exporters_queries_made, __ATOMIC_RELAXED); + gs->exporters_db_points_read = __atomic_load_n(&global_statistics.exporters_db_points_read, __ATOMIC_RELAXED); + gs->backfill_queries_made = __atomic_load_n(&global_statistics.backfill_queries_made, __ATOMIC_RELAXED); + gs->backfill_db_points_read = __atomic_load_n(&global_statistics.backfill_db_points_read, __ATOMIC_RELAXED); + + for(size_t tier = 0; tier < storage_tiers ;tier++) + gs->db_points_stored_per_tier[tier] = __atomic_load_n(&global_statistics.db_points_stored_per_tier[tier], __ATOMIC_RELAXED); if(options & GLOBAL_STATS_RESET_WEB_USEC_MAX) { uint64_t n = 0; __atomic_compare_exchange(&global_statistics.web_usec_max, (uint64_t *) &gs->web_usec_max, &n, 1, __ATOMIC_RELAXED, __ATOMIC_RELAXED); } + + gs->sqlite3_queries_made = __atomic_load_n(&global_statistics.sqlite3_queries_made, __ATOMIC_RELAXED); + gs->sqlite3_queries_ok = __atomic_load_n(&global_statistics.sqlite3_queries_ok, __ATOMIC_RELAXED); + gs->sqlite3_queries_failed = __atomic_load_n(&global_statistics.sqlite3_queries_failed, __ATOMIC_RELAXED); + gs->sqlite3_queries_failed_busy = __atomic_load_n(&global_statistics.sqlite3_queries_failed_busy, __ATOMIC_RELAXED); + gs->sqlite3_queries_failed_locked = __atomic_load_n(&global_statistics.sqlite3_queries_failed_locked, __ATOMIC_RELAXED); + gs->sqlite3_rows = __atomic_load_n(&global_statistics.sqlite3_rows, __ATOMIC_RELAXED); + + gs->sqlite3_metadata_cache_hit = (uint64_t) sql_metadata_cache_stats(SQLITE_DBSTATUS_CACHE_HIT); + gs->sqlite3_context_cache_hit = (uint64_t) sql_context_cache_stats(SQLITE_DBSTATUS_CACHE_HIT); + + gs->sqlite3_metadata_cache_miss = (uint64_t) sql_metadata_cache_stats(SQLITE_DBSTATUS_CACHE_MISS); + gs->sqlite3_context_cache_miss = (uint64_t) sql_context_cache_stats(SQLITE_DBSTATUS_CACHE_MISS); + + gs->sqlite3_metadata_cache_spill = (uint64_t) sql_metadata_cache_stats(SQLITE_DBSTATUS_CACHE_SPILL); + gs->sqlite3_context_cache_spill = (uint64_t) sql_context_cache_stats(SQLITE_DBSTATUS_CACHE_SPILL); + + gs->sqlite3_metadata_cache_write = (uint64_t) sql_metadata_cache_stats(SQLITE_DBSTATUS_CACHE_WRITE); + gs->sqlite3_context_cache_write = (uint64_t) sql_context_cache_stats(SQLITE_DBSTATUS_CACHE_WRITE); } static void global_statistics_charts(void) { @@ -116,6 +279,7 @@ static void global_statistics_charts(void) { struct global_statistics gs; struct rusage me; + struct replication_query_statistics replication = replication_get_query_statistics(); global_statistics_copy(&gs, GLOBAL_STATS_RESET_WEB_USEC_MAX); getrusage(RUSAGE_SELF, &me); @@ -145,8 +309,6 @@ static void global_statistics_charts(void) { rd_cpu_user = rrddim_add(st_cpu, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); rd_cpu_system = rrddim_add(st_cpu, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_cpu); rrddim_set_by_pointer(st_cpu, rd_cpu_user, me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec); rrddim_set_by_pointer(st_cpu, rd_cpu_system, me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec); @@ -175,8 +337,7 @@ static void global_statistics_charts(void) { RRDSET_TYPE_LINE); rd_uptime = rrddim_add(st_uptime, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_uptime); + } rrddim_set_by_pointer(st_uptime, rd_uptime, netdata_uptime); rrdset_done(st_uptime); @@ -206,8 +367,6 @@ static void global_statistics_charts(void) { rd_clients = rrddim_add(st_clients, "clients", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_clients); rrddim_set_by_pointer(st_clients, rd_clients, gs.connected_clients); rrdset_done(st_clients); @@ -237,8 +396,6 @@ static void global_statistics_charts(void) { rd_requests = rrddim_add(st_reqs, "requests", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_reqs); rrddim_set_by_pointer(st_reqs, rd_requests, (collected_number) gs.web_requests); rrdset_done(st_reqs); @@ -270,8 +427,6 @@ static void global_statistics_charts(void) { rd_in = rrddim_add(st_bytes, "in", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); rd_out = rrddim_add(st_bytes, "out", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_bytes); rrddim_set_by_pointer(st_bytes, rd_in, (collected_number) gs.bytes_received); rrddim_set_by_pointer(st_bytes, rd_out, (collected_number) gs.bytes_sent); @@ -304,8 +459,6 @@ static void global_statistics_charts(void) { rd_average = rrddim_add(st_duration, "average", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); rd_max = rrddim_add(st_duration, "max", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_duration); uint64_t gweb_usec = gs.web_usec; uint64_t gweb_requests = gs.web_requests; @@ -352,8 +505,6 @@ static void global_statistics_charts(void) { rd_savings = rrddim_add(st_compression, "savings", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(st_compression); // since we don't lock here to read the global statistics // read the smaller value first @@ -377,69 +528,354 @@ static void global_statistics_charts(void) { // ---------------------------------------------------------------- - if(gs.rrdr_queries_made) { - static RRDSET *st_rrdr_queries = NULL; - static RRDDIM *rd_queries = NULL; - - if (unlikely(!st_rrdr_queries)) { - st_rrdr_queries = rrdset_create_localhost( + { + static RRDSET *st_queries = NULL; + static RRDDIM *rd_api_data_queries = NULL; + static RRDDIM *rd_api_weights_queries = NULL; + static RRDDIM *rd_api_badges_queries = NULL; + static RRDDIM *rd_health_queries = NULL; + static RRDDIM *rd_ml_queries = NULL; + static RRDDIM *rd_exporters_queries = NULL; + static RRDDIM *rd_backfill_queries = NULL; + static RRDDIM *rd_replication_queries = NULL; + + if (unlikely(!st_queries)) { + st_queries = rrdset_create_localhost( "netdata" , "queries" , NULL , "queries" , NULL - , "Netdata API Queries" + , "Netdata DB Queries" , "queries/s" , "netdata" , "stats" , 131000 , localhost->rrd_update_every - , RRDSET_TYPE_LINE + , RRDSET_TYPE_STACKED ); - rd_queries = rrddim_add(st_rrdr_queries, "queries", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_api_data_queries = rrddim_add(st_queries, "/api/v1/data", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_api_weights_queries = rrddim_add(st_queries, "/api/v1/weights", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_api_badges_queries = rrddim_add(st_queries, "/api/v1/badge", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_health_queries = rrddim_add(st_queries, "health", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ml_queries = rrddim_add(st_queries, "ml", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_exporters_queries = rrddim_add(st_queries, "exporters", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_backfill_queries = rrddim_add(st_queries, "backfill", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_replication_queries = rrddim_add(st_queries, "replication", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_rrdr_queries); - rrddim_set_by_pointer(st_rrdr_queries, rd_queries, (collected_number)gs.rrdr_queries_made); + rrddim_set_by_pointer(st_queries, rd_api_data_queries, (collected_number)gs.api_data_queries_made); + rrddim_set_by_pointer(st_queries, rd_api_weights_queries, (collected_number)gs.api_weights_queries_made); + rrddim_set_by_pointer(st_queries, rd_api_badges_queries, (collected_number)gs.api_badges_queries_made); + rrddim_set_by_pointer(st_queries, rd_health_queries, (collected_number)gs.health_queries_made); + rrddim_set_by_pointer(st_queries, rd_ml_queries, (collected_number)gs.ml_queries_made); + rrddim_set_by_pointer(st_queries, rd_exporters_queries, (collected_number)gs.exporters_queries_made); + rrddim_set_by_pointer(st_queries, rd_backfill_queries, (collected_number)gs.backfill_queries_made); + rrddim_set_by_pointer(st_queries, rd_replication_queries, (collected_number)replication.queries_finished); - rrdset_done(st_rrdr_queries); + rrdset_done(st_queries); } // ---------------------------------------------------------------- - if(gs.rrdr_db_points_read || gs.rrdr_result_points_generated) { - static RRDSET *st_rrdr_points = NULL; - static RRDDIM *rd_points_read = NULL; - static RRDDIM *rd_points_generated = NULL; - - if (unlikely(!st_rrdr_points)) { - st_rrdr_points = rrdset_create_localhost( + { + static RRDSET *st_points_read = NULL; + static RRDDIM *rd_api_data_points_read = NULL; + static RRDDIM *rd_api_weights_points_read = NULL; + static RRDDIM *rd_api_badges_points_read = NULL; + static RRDDIM *rd_health_points_read = NULL; + static RRDDIM *rd_ml_points_read = NULL; + static RRDDIM *rd_exporters_points_read = NULL; + static RRDDIM *rd_backfill_points_read = NULL; + static RRDDIM *rd_replication_points_read = NULL; + + if (unlikely(!st_points_read)) { + st_points_read = rrdset_create_localhost( "netdata" - , "db_points" + , "db_points_read" , NULL , "queries" , NULL - , "Netdata API Points" + , "Netdata DB Points Query Read" , "points/s" , "netdata" , "stats" , 131001 , localhost->rrd_update_every - , RRDSET_TYPE_AREA + , RRDSET_TYPE_STACKED ); - rd_points_read = rrddim_add(st_rrdr_points, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rd_points_generated = rrddim_add(st_rrdr_points, "generated", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_api_data_points_read = rrddim_add(st_points_read, "/api/v1/data", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_api_weights_points_read = rrddim_add(st_points_read, "/api/v1/weights", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_api_badges_points_read = rrddim_add(st_points_read, "/api/v1/badge", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_health_points_read = rrddim_add(st_points_read, "health", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ml_points_read = rrddim_add(st_points_read, "ml", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_exporters_points_read = rrddim_add(st_points_read, "exporters", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_backfill_points_read = rrddim_add(st_points_read, "backfill", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_replication_points_read = rrddim_add(st_points_read, "replication", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else - rrdset_next(st_rrdr_points); - rrddim_set_by_pointer(st_rrdr_points, rd_points_read, (collected_number)gs.rrdr_db_points_read); - rrddim_set_by_pointer(st_rrdr_points, rd_points_generated, (collected_number)gs.rrdr_result_points_generated); + rrddim_set_by_pointer(st_points_read, rd_api_data_points_read, (collected_number)gs.api_data_db_points_read); + rrddim_set_by_pointer(st_points_read, rd_api_weights_points_read, (collected_number)gs.api_weights_db_points_read); + rrddim_set_by_pointer(st_points_read, rd_api_badges_points_read, (collected_number)gs.api_badges_db_points_read); + rrddim_set_by_pointer(st_points_read, rd_health_points_read, (collected_number)gs.health_db_points_read); + rrddim_set_by_pointer(st_points_read, rd_ml_points_read, (collected_number)gs.ml_db_points_read); + rrddim_set_by_pointer(st_points_read, rd_exporters_points_read, (collected_number)gs.exporters_db_points_read); + rrddim_set_by_pointer(st_points_read, rd_backfill_points_read, (collected_number)gs.backfill_db_points_read); + rrddim_set_by_pointer(st_points_read, rd_replication_points_read, (collected_number)replication.points_read); + + rrdset_done(st_points_read); + } + + // ---------------------------------------------------------------- + + if(gs.api_data_result_points_generated || replication.points_generated) { + static RRDSET *st_points_generated = NULL; + static RRDDIM *rd_api_data_points_generated = NULL; + static RRDDIM *rd_api_weights_points_generated = NULL; + static RRDDIM *rd_api_badges_points_generated = NULL; + static RRDDIM *rd_health_points_generated = NULL; + static RRDDIM *rd_ml_points_generated = NULL; + static RRDDIM *rd_replication_points_generated = NULL; + + if (unlikely(!st_points_generated)) { + st_points_generated = rrdset_create_localhost( + "netdata" + , "db_points_results" + , NULL + , "queries" + , NULL + , "Netdata Points in Query Results" + , "points/s" + , "netdata" + , "stats" + , 131002 + , localhost->rrd_update_every + , RRDSET_TYPE_STACKED + ); + + rd_api_data_points_generated = rrddim_add(st_points_generated, "/api/v1/data", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_api_weights_points_generated = rrddim_add(st_points_generated, "/api/v1/weights", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_api_badges_points_generated = rrddim_add(st_points_generated, "/api/v1/badge", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_health_points_generated = rrddim_add(st_points_generated, "health", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ml_points_generated = rrddim_add(st_points_generated, "ml", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_replication_points_generated = rrddim_add(st_points_generated, "replication", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_points_generated, rd_api_data_points_generated, (collected_number)gs.api_data_result_points_generated); + rrddim_set_by_pointer(st_points_generated, rd_api_weights_points_generated, (collected_number)gs.api_weights_result_points_generated); + rrddim_set_by_pointer(st_points_generated, rd_api_badges_points_generated, (collected_number)gs.api_badges_result_points_generated); + rrddim_set_by_pointer(st_points_generated, rd_health_points_generated, (collected_number)gs.health_result_points_generated); + rrddim_set_by_pointer(st_points_generated, rd_ml_points_generated, (collected_number)gs.ml_result_points_generated); + rrddim_set_by_pointer(st_points_generated, rd_replication_points_generated, (collected_number)replication.points_generated); + + rrdset_done(st_points_generated); + } + + // ---------------------------------------------------------------- + + { + static RRDSET *st_points_stored = NULL; + static RRDDIM *rds[RRD_STORAGE_TIERS] = {}; + + if (unlikely(!st_points_stored)) { + st_points_stored = rrdset_create_localhost( + "netdata" + , "db_points_stored" + , NULL + , "queries" + , NULL + , "Netdata DB Points Stored" + , "points/s" + , "netdata" + , "stats" + , 131003 + , localhost->rrd_update_every + , RRDSET_TYPE_STACKED + ); + + for(size_t tier = 0; tier < storage_tiers ;tier++) { + char buf[30 + 1]; + snprintfz(buf, 30, "tier%zu", tier); + rds[tier] = rrddim_add(st_points_stored, buf, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + } + + for(size_t tier = 0; tier < storage_tiers ;tier++) + rrddim_set_by_pointer(st_points_stored, rds[tier], (collected_number)gs.db_points_stored_per_tier[tier]); + + rrdset_done(st_points_stored); + } + + // ---------------------------------------------------------------- + + if(gs.sqlite3_queries_made) { + static RRDSET *st_sqlite3_queries = NULL; + static RRDDIM *rd_queries = NULL; + + if (unlikely(!st_sqlite3_queries)) { + st_sqlite3_queries = rrdset_create_localhost( + "netdata" + , "sqlite3_queries" + , NULL + , "sqlite3" + , NULL + , "Netdata SQLite3 Queries" + , "queries/s" + , "netdata" + , "stats" + , 131100 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + rd_queries = rrddim_add(st_sqlite3_queries, "queries", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_sqlite3_queries, rd_queries, (collected_number)gs.sqlite3_queries_made); + + rrdset_done(st_sqlite3_queries); + } + + // ---------------------------------------------------------------- + + if(gs.sqlite3_queries_ok || gs.sqlite3_queries_failed) { + static RRDSET *st_sqlite3_queries_by_status = NULL; + static RRDDIM *rd_ok = NULL, *rd_failed = NULL, *rd_busy = NULL, *rd_locked = NULL; + + if (unlikely(!st_sqlite3_queries_by_status)) { + st_sqlite3_queries_by_status = rrdset_create_localhost( + "netdata" + , "sqlite3_queries_by_status" + , NULL + , "sqlite3" + , NULL + , "Netdata SQLite3 Queries by status" + , "queries/s" + , "netdata" + , "stats" + , 131101 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + rd_ok = rrddim_add(st_sqlite3_queries_by_status, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_failed = rrddim_add(st_sqlite3_queries_by_status, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_busy = rrddim_add(st_sqlite3_queries_by_status, "busy", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_locked = rrddim_add(st_sqlite3_queries_by_status, "locked", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_sqlite3_queries_by_status, rd_ok, (collected_number)gs.sqlite3_queries_made); + rrddim_set_by_pointer(st_sqlite3_queries_by_status, rd_failed, (collected_number)gs.sqlite3_queries_failed); + rrddim_set_by_pointer(st_sqlite3_queries_by_status, rd_busy, (collected_number)gs.sqlite3_queries_failed_busy); + rrddim_set_by_pointer(st_sqlite3_queries_by_status, rd_locked, (collected_number)gs.sqlite3_queries_failed_locked); + + rrdset_done(st_sqlite3_queries_by_status); + } + + // ---------------------------------------------------------------- + + if(gs.sqlite3_rows) { + static RRDSET *st_sqlite3_rows = NULL; + static RRDDIM *rd_rows = NULL; + + if (unlikely(!st_sqlite3_rows)) { + st_sqlite3_rows = rrdset_create_localhost( + "netdata" + , "sqlite3_rows" + , NULL + , "sqlite3" + , NULL + , "Netdata SQLite3 Rows" + , "rows/s" + , "netdata" + , "stats" + , 131102 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + rd_rows = rrddim_add(st_sqlite3_rows, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_sqlite3_rows, rd_rows, (collected_number)gs.sqlite3_rows); + + rrdset_done(st_sqlite3_rows); + } + + if(gs.sqlite3_metadata_cache_hit) { + static RRDSET *st_sqlite3_cache = NULL; + static RRDDIM *rd_cache_hit = NULL; + static RRDDIM *rd_cache_miss= NULL; + static RRDDIM *rd_cache_spill= NULL; + static RRDDIM *rd_cache_write= NULL; + + if (unlikely(!st_sqlite3_cache)) { + st_sqlite3_cache = rrdset_create_localhost( + "netdata" + , "sqlite3_metatada_cache" + , NULL + , "sqlite3" + , NULL + , "Netdata SQLite3 metadata cache" + , "ops/s" + , "netdata" + , "stats" + , 131103 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + rd_cache_hit = rrddim_add(st_sqlite3_cache, "cache_hit", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_cache_miss = rrddim_add(st_sqlite3_cache, "cache_miss", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_cache_spill = rrddim_add(st_sqlite3_cache, "cache_spill", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_cache_write = rrddim_add(st_sqlite3_cache, "cache_write", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_sqlite3_cache, rd_cache_hit, (collected_number)gs.sqlite3_metadata_cache_hit); + rrddim_set_by_pointer(st_sqlite3_cache, rd_cache_miss, (collected_number)gs.sqlite3_metadata_cache_miss); + rrddim_set_by_pointer(st_sqlite3_cache, rd_cache_spill, (collected_number)gs.sqlite3_metadata_cache_spill); + rrddim_set_by_pointer(st_sqlite3_cache, rd_cache_write, (collected_number)gs.sqlite3_metadata_cache_write); + + rrdset_done(st_sqlite3_cache); + } + + if(gs.sqlite3_context_cache_hit) { + static RRDSET *st_sqlite3_cache = NULL; + static RRDDIM *rd_cache_hit = NULL; + static RRDDIM *rd_cache_miss= NULL; + static RRDDIM *rd_cache_spill= NULL; + static RRDDIM *rd_cache_write= NULL; + + if (unlikely(!st_sqlite3_cache)) { + st_sqlite3_cache = rrdset_create_localhost( + "netdata" + , "sqlite3_context_cache" + , NULL + , "sqlite3" + , NULL + , "Netdata SQLite3 context cache" + , "ops/s" + , "netdata" + , "stats" + , 131104 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + rd_cache_hit = rrddim_add(st_sqlite3_cache, "cache_hit", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_cache_miss = rrddim_add(st_sqlite3_cache, "cache_miss", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_cache_spill = rrddim_add(st_sqlite3_cache, "cache_spill", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_cache_write = rrddim_add(st_sqlite3_cache, "cache_write", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_sqlite3_cache, rd_cache_hit, (collected_number)gs.sqlite3_context_cache_hit); + rrddim_set_by_pointer(st_sqlite3_cache, rd_cache_miss, (collected_number)gs.sqlite3_context_cache_miss); + rrddim_set_by_pointer(st_sqlite3_cache, rd_cache_spill, (collected_number)gs.sqlite3_context_cache_spill); + rrddim_set_by_pointer(st_sqlite3_cache, rd_cache_write, (collected_number)gs.sqlite3_context_cache_write); - rrdset_done(st_rrdr_points); + rrdset_done(st_sqlite3_cache); } // ---------------------------------------------------------------- @@ -454,13 +890,14 @@ static void dbengine_statistics_charts(void) { unsigned dbengine_contexts = 0, counted_multihost_db[RRD_STORAGE_TIERS] = { 0 }, i; rrdhost_foreach_read(host) { - if (host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && !rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED)) { + if (!rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED)) { /* get localhost's DB engine's statistics for each tier */ - for(int tier = 0; tier < storage_tiers ;tier++) { - if(!host->storage_instance[tier]) continue; + for(size_t tier = 0; tier < storage_tiers ;tier++) { + if(host->db[tier].mode != RRD_MEMORY_MODE_DBENGINE) continue; + if(!host->db[tier].instance) continue; - if(is_storage_engine_shared(host->storage_instance[tier])) { + if(is_storage_engine_shared(host->db[tier].instance)) { if(counted_multihost_db[tier]) continue; else @@ -468,7 +905,7 @@ static void dbengine_statistics_charts(void) { } ++dbengine_contexts; - rrdeng_get_37_statistics((struct rrdengine_instance *)host->storage_instance[tier], local_stats_array); + rrdeng_get_37_statistics((struct rrdengine_instance *)host->db[tier].instance, local_stats_array); for (i = 0; i < RRDENG_NR_STATS; ++i) { /* aggregate statistics across hosts */ stats_array[i] += local_stats_array[i]; @@ -508,8 +945,7 @@ static void dbengine_statistics_charts(void) { RRDSET_TYPE_LINE); rd_savings = rrddim_add(st_compression, "savings", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_compression); + } unsigned long long ratio; unsigned long long compressed_content_size = stats_array[12]; @@ -548,8 +984,7 @@ static void dbengine_statistics_charts(void) { RRDSET_TYPE_LINE); rd_hit_ratio = rrddim_add(st_pg_cache_hit_ratio, "ratio", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_pg_cache_hit_ratio); + } static unsigned long long old_hits = 0; static unsigned long long old_misses = 0; @@ -607,8 +1042,7 @@ static void dbengine_statistics_charts(void) { rd_evictions = rrddim_add(st_pg_cache_pages, "evictions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_used_by_collectors = rrddim_add(st_pg_cache_pages, "used_by_collectors", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_pg_cache_pages); + } rrddim_set_by_pointer(st_pg_cache_pages, rd_descriptors, (collected_number)stats_array[27]); rrddim_set_by_pointer(st_pg_cache_pages, rd_populated, (collected_number)stats_array[3]); @@ -648,8 +1082,7 @@ static void dbengine_statistics_charts(void) { rd_deletions = rrddim_add(st_long_term_pages, "deletions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); rd_flushing_pressure_deletions = rrddim_add( st_long_term_pages, "flushing_pressure_deletions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st_long_term_pages); + } rrddim_set_by_pointer(st_long_term_pages, rd_total, (collected_number)stats_array[2]); rrddim_set_by_pointer(st_long_term_pages, rd_insertions, (collected_number)stats_array[5]); @@ -683,8 +1116,7 @@ static void dbengine_statistics_charts(void) { rd_reads = rrddim_add(st_io_stats, "reads", NULL, 1, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); rd_writes = rrddim_add(st_io_stats, "writes", NULL, -1, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st_io_stats); + } rrddim_set_by_pointer(st_io_stats, rd_reads, (collected_number)stats_array[17]); rrddim_set_by_pointer(st_io_stats, rd_writes, (collected_number)stats_array[15]); @@ -715,8 +1147,7 @@ static void dbengine_statistics_charts(void) { rd_reads = rrddim_add(st_io_stats, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_writes = rrddim_add(st_io_stats, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st_io_stats); + } rrddim_set_by_pointer(st_io_stats, rd_reads, (collected_number)stats_array[18]); rrddim_set_by_pointer(st_io_stats, rd_writes, (collected_number)stats_array[16]); @@ -750,8 +1181,7 @@ static void dbengine_statistics_charts(void) { rd_fs_errors = rrddim_add(st_errors, "fs_errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); pg_cache_over_half_dirty_events = rrddim_add(st_errors, "pg_cache_over_half_dirty_events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(st_errors); + } rrddim_set_by_pointer(st_errors, rd_io_errors, (collected_number)stats_array[30]); rrddim_set_by_pointer(st_errors, rd_fs_errors, (collected_number)stats_array[31]); @@ -783,8 +1213,7 @@ static void dbengine_statistics_charts(void) { rd_fd_current = rrddim_add(st_fd, "current", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_fd_max = rrddim_add(st_fd, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_fd); + } rrddim_set_by_pointer(st_fd, rd_fd_current, (collected_number)stats_array[32]); /* Careful here, modify this accordingly if the File-Descriptor budget ever changes */ @@ -825,8 +1254,7 @@ static void dbengine_statistics_charts(void) { rd_cache_metadata = rrddim_add(st_ram_usage, "cache metadata", NULL, 1, 1024*1024, RRD_ALGORITHM_ABSOLUTE); rd_pages_metadata = rrddim_add(st_ram_usage, "pages metadata", NULL, 1, 1024*1024, RRD_ALGORITHM_ABSOLUTE); rd_index_metadata = rrddim_add(st_ram_usage, "index metadata", NULL, 1, 1024*1024, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_ram_usage); + } API_producers = (collected_number)stats_array[0]; pages_on_disk = (collected_number)stats_array[2]; @@ -852,6 +1280,90 @@ static void dbengine_statistics_charts(void) { #endif } +static void update_strings_charts() { + static RRDSET *st_ops = NULL, *st_entries = NULL, *st_mem = NULL; + static RRDDIM *rd_ops_inserts = NULL, *rd_ops_deletes = NULL, *rd_ops_searches = NULL, *rd_ops_duplications = NULL, *rd_ops_releases = NULL; + static RRDDIM *rd_entries_entries = NULL, *rd_entries_refs = NULL; + static RRDDIM *rd_mem = NULL; + + size_t inserts, deletes, searches, entries, references, memory, duplications, releases; + + string_statistics(&inserts, &deletes, &searches, &entries, &references, &memory, &duplications, &releases); + + if (unlikely(!st_ops)) { + st_ops = rrdset_create_localhost( + "netdata" + , "strings_ops" + , NULL + , "strings" + , NULL + , "Strings operations" + , "ops/s" + , "netdata" + , "stats" + , 910000 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE); + + rd_ops_inserts = rrddim_add(st_ops, "inserts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ops_deletes = rrddim_add(st_ops, "deletes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ops_searches = rrddim_add(st_ops, "searches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ops_duplications = rrddim_add(st_ops, "duplications", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ops_releases = rrddim_add(st_ops, "releases", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(st_ops, rd_ops_inserts, (collected_number)inserts); + rrddim_set_by_pointer(st_ops, rd_ops_deletes, (collected_number)deletes); + rrddim_set_by_pointer(st_ops, rd_ops_searches, (collected_number)searches); + rrddim_set_by_pointer(st_ops, rd_ops_duplications, (collected_number)duplications); + rrddim_set_by_pointer(st_ops, rd_ops_releases, (collected_number)releases); + rrdset_done(st_ops); + + if (unlikely(!st_entries)) { + st_entries = rrdset_create_localhost( + "netdata" + , "strings_entries" + , NULL + , "strings" + , NULL + , "Strings entries" + , "entries" + , "netdata" + , "stats" + , 910001 + , localhost->rrd_update_every + , RRDSET_TYPE_AREA); + + rd_entries_entries = rrddim_add(st_entries, "entries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_entries_refs = rrddim_add(st_entries, "references", NULL, 1, -1, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(st_entries, rd_entries_entries, (collected_number)entries); + rrddim_set_by_pointer(st_entries, rd_entries_refs, (collected_number)references); + rrdset_done(st_entries); + + if (unlikely(!st_mem)) { + st_mem = rrdset_create_localhost( + "netdata" + , "strings_memory" + , NULL + , "strings" + , NULL + , "Strings memory" + , "bytes" + , "netdata" + , "stats" + , 910001 + , localhost->rrd_update_every + , RRDSET_TYPE_AREA); + + rd_mem = rrddim_add(st_mem, "memory", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(st_mem, rd_mem, (collected_number)memory); + rrdset_done(st_mem); +} + static void update_heartbeat_charts() { static RRDSET *st_heartbeat = NULL; static RRDDIM *rd_heartbeat_min = NULL; @@ -876,8 +1388,7 @@ static void update_heartbeat_charts() { rd_heartbeat_min = rrddim_add(st_heartbeat, "min", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_heartbeat_max = rrddim_add(st_heartbeat, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rd_heartbeat_avg = rrddim_add(st_heartbeat, "average", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(st_heartbeat); + } usec_t min, max, average; size_t count; @@ -892,62 +1403,569 @@ static void update_heartbeat_charts() { } // --------------------------------------------------------------------------------------------------------------------- -// worker utilization +// dictionary statistics -#define WORKERS_MIN_PERCENT_DEFAULT 10000.0 +struct dictionary_categories { + struct dictionary_stats *stats; + const char *family; + const char *context_prefix; + int priority; + + RRDSET *st_dicts; + RRDDIM *rd_dicts_active; + RRDDIM *rd_dicts_deleted; + + RRDSET *st_items; + RRDDIM *rd_items_entries; + RRDDIM *rd_items_referenced; + RRDDIM *rd_items_pending_deletion; + + RRDSET *st_ops; + RRDDIM *rd_ops_creations; + RRDDIM *rd_ops_destructions; + RRDDIM *rd_ops_flushes; + RRDDIM *rd_ops_traversals; + RRDDIM *rd_ops_walkthroughs; + RRDDIM *rd_ops_garbage_collections; + RRDDIM *rd_ops_searches; + RRDDIM *rd_ops_inserts; + RRDDIM *rd_ops_resets; + RRDDIM *rd_ops_deletes; + + RRDSET *st_callbacks; + RRDDIM *rd_callbacks_inserts; + RRDDIM *rd_callbacks_conflicts; + RRDDIM *rd_callbacks_reacts; + RRDDIM *rd_callbacks_deletes; + + RRDSET *st_memory; + RRDDIM *rd_memory_indexed; + RRDDIM *rd_memory_values; + RRDDIM *rd_memory_dict; + + RRDSET *st_spins; + RRDDIM *rd_spins_use; + RRDDIM *rd_spins_search; + RRDDIM *rd_spins_insert; + RRDDIM *rd_spins_delete; + +} dictionary_categories[] = { + { .stats = &dictionary_stats_category_other, "dictionaries", "dictionaries", 900000 }, + + // terminator + { .stats = NULL, NULL, NULL, 0 }, +}; -struct worker_job_type { - char name[WORKER_UTILIZATION_MAX_JOB_NAME_LENGTH + 1]; - size_t jobs_started; - usec_t busy_time; +#define load_dictionary_stats_entry(x) total += (size_t)(stats.x = __atomic_load_n(&c->stats->x, __ATOMIC_RELAXED)) - RRDDIM *rd_jobs_started; - RRDDIM *rd_busy_time; -}; +static void update_dictionary_category_charts(struct dictionary_categories *c) { + struct dictionary_stats stats; + stats.name = c->stats->name; -struct worker_thread { - pid_t pid; - int enabled; + // ------------------------------------------------------------------------ - int cpu_enabled; - double cpu; + size_t total = 0; + load_dictionary_stats_entry(dictionaries.active); + load_dictionary_stats_entry(dictionaries.deleted); - kernel_uint_t utime; - kernel_uint_t stime; + if(c->st_dicts || total != 0) { + if (unlikely(!c->st_dicts)) { + char id[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s.%s.dictionaries", c->context_prefix, stats.name); - kernel_uint_t utime_old; - kernel_uint_t stime_old; + char context[RRD_ID_LENGTH_MAX + 1]; + snprintfz(context, RRD_ID_LENGTH_MAX, "netdata.%s.category.dictionaries", c->context_prefix); - usec_t collected_time; - usec_t collected_time_old; + c->st_dicts = rrdset_create_localhost( + "netdata" + , id + , NULL + , c->family + , context + , "Dictionaries" + , "dictionaries" + , "netdata" + , "stats" + , c->priority + 0 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); - size_t jobs_started; - usec_t busy_time; + c->rd_dicts_active = rrddim_add(c->st_dicts, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + c->rd_dicts_deleted = rrddim_add(c->st_dicts, "deleted", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); - struct worker_thread *next; -}; + rrdlabels_add(c->st_dicts->rrdlabels, "category", stats.name, RRDLABEL_SRC_AUTO); + } -struct worker_utilization { - const char *name; - const char *family; - size_t priority; - uint32_t flags; + rrddim_set_by_pointer(c->st_dicts, c->rd_dicts_active, (collected_number)stats.dictionaries.active); + rrddim_set_by_pointer(c->st_dicts, c->rd_dicts_deleted, (collected_number)stats.dictionaries.deleted); + rrdset_done(c->st_dicts); + } - char *name_lowercase; + // ------------------------------------------------------------------------ - struct worker_job_type per_job_type[WORKER_UTILIZATION_MAX_JOB_TYPES]; + total = 0; + load_dictionary_stats_entry(items.entries); + load_dictionary_stats_entry(items.referenced); + load_dictionary_stats_entry(items.pending_deletion); - size_t workers_registered; - size_t workers_busy; - usec_t workers_total_busy_time; - usec_t workers_total_duration; - size_t workers_total_jobs_started; - double workers_min_busy_time; - double workers_max_busy_time; + if(c->st_items || total != 0) { + if (unlikely(!c->st_items)) { + char id[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s.%s.items", c->context_prefix, stats.name); - size_t workers_cpu_registered; - double workers_cpu_min; - double workers_cpu_max; + char context[RRD_ID_LENGTH_MAX + 1]; + snprintfz(context, RRD_ID_LENGTH_MAX, "netdata.%s.category.items", c->context_prefix); + + c->st_items = rrdset_create_localhost( + "netdata" + , id + , NULL + , c->family + , context + , "Dictionary Items" + , "items" + , "netdata" + , "stats" + , c->priority + 1 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + c->rd_items_entries = rrddim_add(c->st_items, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + c->rd_items_pending_deletion = rrddim_add(c->st_items, "deleted", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); + c->rd_items_referenced = rrddim_add(c->st_items, "referenced", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(c->st_items->rrdlabels, "category", stats.name, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer(c->st_items, c->rd_items_entries, stats.items.entries); + rrddim_set_by_pointer(c->st_items, c->rd_items_pending_deletion, stats.items.pending_deletion); + rrddim_set_by_pointer(c->st_items, c->rd_items_referenced, stats.items.referenced); + rrdset_done(c->st_items); + } + + // ------------------------------------------------------------------------ + + total = 0; + load_dictionary_stats_entry(ops.creations); + load_dictionary_stats_entry(ops.destructions); + load_dictionary_stats_entry(ops.flushes); + load_dictionary_stats_entry(ops.traversals); + load_dictionary_stats_entry(ops.walkthroughs); + load_dictionary_stats_entry(ops.garbage_collections); + load_dictionary_stats_entry(ops.searches); + load_dictionary_stats_entry(ops.inserts); + load_dictionary_stats_entry(ops.resets); + load_dictionary_stats_entry(ops.deletes); + + if(c->st_ops || total != 0) { + if (unlikely(!c->st_ops)) { + char id[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s.%s.ops", c->context_prefix, stats.name); + + char context[RRD_ID_LENGTH_MAX + 1]; + snprintfz(context, RRD_ID_LENGTH_MAX, "netdata.%s.category.ops", c->context_prefix); + + c->st_ops = rrdset_create_localhost( + "netdata" + , id + , NULL + , c->family + , context + , "Dictionary Operations" + , "ops/s" + , "netdata" + , "stats" + , c->priority + 2 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + c->rd_ops_creations = rrddim_add(c->st_ops, "creations", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_ops_destructions = rrddim_add(c->st_ops, "destructions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_ops_flushes = rrddim_add(c->st_ops, "flushes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_ops_traversals = rrddim_add(c->st_ops, "traversals", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_ops_walkthroughs = rrddim_add(c->st_ops, "walkthroughs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_ops_garbage_collections = rrddim_add(c->st_ops, "garbage_collections", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_ops_searches = rrddim_add(c->st_ops, "searches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_ops_inserts = rrddim_add(c->st_ops, "inserts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_ops_resets = rrddim_add(c->st_ops, "resets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_ops_deletes = rrddim_add(c->st_ops, "deletes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(c->st_ops->rrdlabels, "category", stats.name, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer(c->st_ops, c->rd_ops_creations, (collected_number)stats.ops.creations); + rrddim_set_by_pointer(c->st_ops, c->rd_ops_destructions, (collected_number)stats.ops.destructions); + rrddim_set_by_pointer(c->st_ops, c->rd_ops_flushes, (collected_number)stats.ops.flushes); + rrddim_set_by_pointer(c->st_ops, c->rd_ops_traversals, (collected_number)stats.ops.traversals); + rrddim_set_by_pointer(c->st_ops, c->rd_ops_walkthroughs, (collected_number)stats.ops.walkthroughs); + rrddim_set_by_pointer(c->st_ops, c->rd_ops_garbage_collections, (collected_number)stats.ops.garbage_collections); + rrddim_set_by_pointer(c->st_ops, c->rd_ops_searches, (collected_number)stats.ops.searches); + rrddim_set_by_pointer(c->st_ops, c->rd_ops_inserts, (collected_number)stats.ops.inserts); + rrddim_set_by_pointer(c->st_ops, c->rd_ops_resets, (collected_number)stats.ops.resets); + rrddim_set_by_pointer(c->st_ops, c->rd_ops_deletes, (collected_number)stats.ops.deletes); + + rrdset_done(c->st_ops); + } + + // ------------------------------------------------------------------------ + + total = 0; + load_dictionary_stats_entry(callbacks.inserts); + load_dictionary_stats_entry(callbacks.conflicts); + load_dictionary_stats_entry(callbacks.reacts); + load_dictionary_stats_entry(callbacks.deletes); + + if(c->st_callbacks || total != 0) { + if (unlikely(!c->st_callbacks)) { + char id[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s.%s.callbacks", c->context_prefix, stats.name); + + char context[RRD_ID_LENGTH_MAX + 1]; + snprintfz(context, RRD_ID_LENGTH_MAX, "netdata.%s.category.callbacks", c->context_prefix); + + c->st_callbacks = rrdset_create_localhost( + "netdata" + , id + , NULL + , c->family + , context + , "Dictionary Callbacks" + , "callbacks/s" + , "netdata" + , "stats" + , c->priority + 3 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + c->rd_callbacks_inserts = rrddim_add(c->st_callbacks, "inserts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_callbacks_deletes = rrddim_add(c->st_callbacks, "deletes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_callbacks_conflicts = rrddim_add(c->st_callbacks, "conflicts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_callbacks_reacts = rrddim_add(c->st_callbacks, "reacts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(c->st_callbacks->rrdlabels, "category", stats.name, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer(c->st_callbacks, c->rd_callbacks_inserts, (collected_number)stats.callbacks.inserts); + rrddim_set_by_pointer(c->st_callbacks, c->rd_callbacks_conflicts, (collected_number)stats.callbacks.conflicts); + rrddim_set_by_pointer(c->st_callbacks, c->rd_callbacks_reacts, (collected_number)stats.callbacks.reacts); + rrddim_set_by_pointer(c->st_callbacks, c->rd_callbacks_deletes, (collected_number)stats.callbacks.deletes); + + rrdset_done(c->st_callbacks); + } + + // ------------------------------------------------------------------------ + + total = 0; + load_dictionary_stats_entry(memory.indexed); + load_dictionary_stats_entry(memory.values); + load_dictionary_stats_entry(memory.dict); + + if(c->st_memory || total != 0) { + if (unlikely(!c->st_memory)) { + char id[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s.%s.memory", c->context_prefix, stats.name); + + char context[RRD_ID_LENGTH_MAX + 1]; + snprintfz(context, RRD_ID_LENGTH_MAX, "netdata.%s.category.memory", c->context_prefix); + + c->st_memory = rrdset_create_localhost( + "netdata" + , id + , NULL + , c->family + , context + , "Dictionary Memory" + , "bytes" + , "netdata" + , "stats" + , c->priority + 4 + , localhost->rrd_update_every + , RRDSET_TYPE_STACKED + ); + + c->rd_memory_indexed = rrddim_add(c->st_memory, "index", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + c->rd_memory_values = rrddim_add(c->st_memory, "data", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + c->rd_memory_dict = rrddim_add(c->st_memory, "structures", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(c->st_memory->rrdlabels, "category", stats.name, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer(c->st_memory, c->rd_memory_indexed, (collected_number)stats.memory.indexed); + rrddim_set_by_pointer(c->st_memory, c->rd_memory_values, (collected_number)stats.memory.values); + rrddim_set_by_pointer(c->st_memory, c->rd_memory_dict, (collected_number)stats.memory.dict); + + rrdset_done(c->st_memory); + } + + // ------------------------------------------------------------------------ + + total = 0; + load_dictionary_stats_entry(spin_locks.use_spins); + load_dictionary_stats_entry(spin_locks.search_spins); + load_dictionary_stats_entry(spin_locks.insert_spins); + load_dictionary_stats_entry(spin_locks.delete_spins); + + if(c->st_spins || total != 0) { + if (unlikely(!c->st_spins)) { + char id[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s.%s.spins", c->context_prefix, stats.name); + + char context[RRD_ID_LENGTH_MAX + 1]; + snprintfz(context, RRD_ID_LENGTH_MAX, "netdata.%s.category.spins", c->context_prefix); + + c->st_spins = rrdset_create_localhost( + "netdata" + , id + , NULL + , c->family + , context + , "Dictionary Spins" + , "count" + , "netdata" + , "stats" + , c->priority + 5 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + c->rd_spins_use = rrddim_add(c->st_spins, "use", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_spins_search = rrddim_add(c->st_spins, "search", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_spins_insert = rrddim_add(c->st_spins, "insert", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + c->rd_spins_delete = rrddim_add(c->st_spins, "delete", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(c->st_spins->rrdlabels, "category", stats.name, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer(c->st_spins, c->rd_spins_use, (collected_number)stats.spin_locks.use_spins); + rrddim_set_by_pointer(c->st_spins, c->rd_spins_search, (collected_number)stats.spin_locks.search_spins); + rrddim_set_by_pointer(c->st_spins, c->rd_spins_insert, (collected_number)stats.spin_locks.insert_spins); + rrddim_set_by_pointer(c->st_spins, c->rd_spins_delete, (collected_number)stats.spin_locks.delete_spins); + + rrdset_done(c->st_spins); + } +} + +#ifdef NETDATA_TRACE_ALLOCATIONS + +struct memory_trace_data { + RRDSET *st_memory; + RRDSET *st_allocations; + RRDSET *st_avg_alloc; + RRDSET *st_ops; +}; + +static int do_memory_trace_item(void *item, void *data) { + struct memory_trace_data *tmp = data; + struct malloc_trace *p = item; + + // ------------------------------------------------------------------------ + + if(!p->rd_bytes) + p->rd_bytes = rrddim_add(tmp->st_memory, p->function, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + collected_number bytes = (collected_number)__atomic_load_n(&p->bytes, __ATOMIC_RELAXED); + rrddim_set_by_pointer(tmp->st_memory, p->rd_bytes, bytes); + + // ------------------------------------------------------------------------ + + if(!p->rd_allocations) + p->rd_allocations = rrddim_add(tmp->st_allocations, p->function, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + collected_number allocs = (collected_number)__atomic_load_n(&p->allocations, __ATOMIC_RELAXED); + rrddim_set_by_pointer(tmp->st_allocations, p->rd_allocations, allocs); + + // ------------------------------------------------------------------------ + + if(!p->rd_avg_alloc) + p->rd_avg_alloc = rrddim_add(tmp->st_avg_alloc, p->function, NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); + + collected_number avg_alloc = (allocs)?(bytes * 100 / allocs):0; + rrddim_set_by_pointer(tmp->st_avg_alloc, p->rd_avg_alloc, avg_alloc); + + // ------------------------------------------------------------------------ + + if(!p->rd_ops) + p->rd_ops = rrddim_add(tmp->st_ops, p->function, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + collected_number ops = 0; + ops += (collected_number)__atomic_load_n(&p->malloc_calls, __ATOMIC_RELAXED); + ops += (collected_number)__atomic_load_n(&p->calloc_calls, __ATOMIC_RELAXED); + ops += (collected_number)__atomic_load_n(&p->realloc_calls, __ATOMIC_RELAXED); + ops += (collected_number)__atomic_load_n(&p->strdup_calls, __ATOMIC_RELAXED); + ops += (collected_number)__atomic_load_n(&p->free_calls, __ATOMIC_RELAXED); + rrddim_set_by_pointer(tmp->st_ops, p->rd_ops, ops); + + // ------------------------------------------------------------------------ + + return 1; +} +static void malloc_trace_statistics(void) { + static struct memory_trace_data tmp = { + .st_memory = NULL, + .st_allocations = NULL, + .st_avg_alloc = NULL, + .st_ops = NULL, + }; + + if(!tmp.st_memory) { + tmp.st_memory = rrdset_create_localhost( + "netdata" + , "memory_size" + , NULL + , "memory" + , "netdata.memory.size" + , "Netdata Memory Used by Function" + , "bytes" + , "netdata" + , "stats" + , 900000 + , localhost->rrd_update_every + , RRDSET_TYPE_STACKED + ); + } + + if(!tmp.st_ops) { + tmp.st_ops = rrdset_create_localhost( + "netdata" + , "memory_operations" + , NULL + , "memory" + , "netdata.memory.operations" + , "Netdata Memory Operations by Function" + , "ops/s" + , "netdata" + , "stats" + , 900001 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + } + + if(!tmp.st_allocations) { + tmp.st_allocations = rrdset_create_localhost( + "netdata" + , "memory_allocations" + , NULL + , "memory" + , "netdata.memory.allocations" + , "Netdata Memory Allocations by Function" + , "allocations" + , "netdata" + , "stats" + , 900002 + , localhost->rrd_update_every + , RRDSET_TYPE_STACKED + ); + } + + if(!tmp.st_avg_alloc) { + tmp.st_avg_alloc = rrdset_create_localhost( + "netdata" + , "memory_avg_alloc" + , NULL + , "memory" + , "netdata.memory.avg_alloc" + , "Netdata Average Allocation Size by Function" + , "bytes" + , "netdata" + , "stats" + , 900003 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + } + + malloc_trace_walkthrough(do_memory_trace_item, &tmp); + + rrdset_done(tmp.st_memory); + rrdset_done(tmp.st_ops); + rrdset_done(tmp.st_allocations); + rrdset_done(tmp.st_avg_alloc); +} +#endif + +static void dictionary_statistics(void) { + for(int i = 0; dictionary_categories[i].stats ;i++) { + update_dictionary_category_charts(&dictionary_categories[i]); + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// worker utilization + +#define WORKERS_MIN_PERCENT_DEFAULT 10000.0 + +struct worker_job_type_gs { + STRING *name; + STRING *units; + + size_t jobs_started; + usec_t busy_time; + + RRDDIM *rd_jobs_started; + RRDDIM *rd_busy_time; + + WORKER_METRIC_TYPE type; + NETDATA_DOUBLE min_value; + NETDATA_DOUBLE max_value; + NETDATA_DOUBLE sum_value; + size_t count_value; + + RRDSET *st; + RRDDIM *rd_min; + RRDDIM *rd_max; + RRDDIM *rd_avg; +}; + +struct worker_thread { + pid_t pid; + bool enabled; + + bool cpu_enabled; + double cpu; + + kernel_uint_t utime; + kernel_uint_t stime; + + kernel_uint_t utime_old; + kernel_uint_t stime_old; + + usec_t collected_time; + usec_t collected_time_old; + + size_t jobs_started; + usec_t busy_time; + + struct worker_thread *next; + struct worker_thread *prev; +}; + +struct worker_utilization { + const char *name; + const char *family; + size_t priority; + uint32_t flags; + + char *name_lowercase; + + struct worker_job_type_gs per_job_type[WORKER_UTILIZATION_MAX_JOB_TYPES]; + + size_t workers_max_job_id; + size_t workers_registered; + size_t workers_busy; + usec_t workers_total_busy_time; + usec_t workers_total_duration; + size_t workers_total_jobs_started; + double workers_min_busy_time; + double workers_max_busy_time; + + size_t workers_cpu_registered; + double workers_cpu_min; + double workers_cpu_max; double workers_cpu_total; struct worker_thread *threads; @@ -983,6 +2001,7 @@ static struct worker_utilization all_workers_utilization[] = { { .name = "WEB", .family = "workers web server", .priority = 1000000 }, { .name = "ACLKQUERY", .family = "workers aclk query", .priority = 1000000 }, { .name = "ACLKSYNC", .family = "workers aclk host sync", .priority = 1000000 }, + { .name = "METASYNC", .family = "workers metadata sync", .priority = 1000000 }, { .name = "PLUGINSD", .family = "workers plugins.d", .priority = 1000000 }, { .name = "STATSD", .family = "workers plugin statsd", .priority = 1000000 }, { .name = "STATSDFLUSH", .family = "workers plugin statsd flush", .priority = 1000000 }, @@ -996,7 +2015,9 @@ static struct worker_utilization all_workers_utilization[] = { { .name = "TC", .family = "workers plugin tc", .priority = 1000000 }, { .name = "TIMEX", .family = "workers plugin timex", .priority = 1000000 }, { .name = "IDLEJITTER", .family = "workers plugin idlejitter", .priority = 1000000 }, - { .name = "RRDCONTEXT", .family = "workers aclk contexts", .priority = 1000000 }, + { .name = "RRDCONTEXT", .family = "workers contexts", .priority = 1000000 }, + { .name = "REPLICATION", .family = "workers replication sender", .priority = 1000000 }, + { .name = "SERVICE", .family = "workers service", .priority = 1000000 }, // has to be terminated with a NULL { .name = NULL, .family = NULL } @@ -1027,21 +2048,21 @@ static void workers_total_cpu_utilization_chart(void) { RRDSET_TYPE_STACKED); } - rrdset_next(st); - for(i = 0; all_workers_utilization[i].name ;i++) { struct worker_utilization *wu = &all_workers_utilization[i]; if(!wu->workers_cpu_registered) continue; if(!wu->rd_total_cpu_utilizaton) - wu->rd_total_cpu_utilizaton = rrddim_add(st, wu->name_lowercase, NULL, 1, 10000ULL, RRD_ALGORITHM_ABSOLUTE); + wu->rd_total_cpu_utilizaton = rrddim_add(st, wu->name_lowercase, NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE); - rrddim_set_by_pointer(st, wu->rd_total_cpu_utilizaton, (collected_number)((double)wu->workers_cpu_total * 10000.0)); + rrddim_set_by_pointer(st, wu->rd_total_cpu_utilizaton, (collected_number)((double)wu->workers_cpu_total * 100.0)); } rrdset_done(st); } +#define WORKER_CHART_DECIMAL_PRECISION 100 + static void workers_utilization_update_chart(struct worker_utilization *wu) { if(!wu->workers_registered) return; @@ -1079,28 +2100,26 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) { // we add the min and max dimensions only when we have multiple workers if(unlikely(!wu->rd_workers_time_min && wu->workers_registered > 1)) - wu->rd_workers_time_min = rrddim_add(wu->st_workers_time, "min", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE); + wu->rd_workers_time_min = rrddim_add(wu->st_workers_time, "min", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); if(unlikely(!wu->rd_workers_time_max && wu->workers_registered > 1)) - wu->rd_workers_time_max = rrddim_add(wu->st_workers_time, "max", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE); + wu->rd_workers_time_max = rrddim_add(wu->st_workers_time, "max", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); if(unlikely(!wu->rd_workers_time_avg)) - wu->rd_workers_time_avg = rrddim_add(wu->st_workers_time, "average", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE); - - rrdset_next(wu->st_workers_time); + wu->rd_workers_time_avg = rrddim_add(wu->st_workers_time, "average", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); if(unlikely(wu->workers_min_busy_time == WORKERS_MIN_PERCENT_DEFAULT)) wu->workers_min_busy_time = 0.0; if(wu->rd_workers_time_min) - rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_min, (collected_number)((double)wu->workers_min_busy_time * 10000.0)); + rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_min, (collected_number)((double)wu->workers_min_busy_time * WORKER_CHART_DECIMAL_PRECISION)); if(wu->rd_workers_time_max) - rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_max, (collected_number)((double)wu->workers_max_busy_time * 10000.0)); + rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_max, (collected_number)((double)wu->workers_max_busy_time * WORKER_CHART_DECIMAL_PRECISION)); if(wu->workers_total_duration == 0) rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_avg, 0); else - rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_avg, (collected_number)((double)wu->workers_total_busy_time * 100.0 * 10000.0 / (double)wu->workers_total_duration)); + rrddim_set_by_pointer(wu->st_workers_time, wu->rd_workers_time_avg, (collected_number)((double)wu->workers_total_busy_time * 100.0 * WORKER_CHART_DECIMAL_PRECISION / (double)wu->workers_total_duration)); rrdset_done(wu->st_workers_time); @@ -1132,28 +2151,26 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) { } if (unlikely(!wu->rd_workers_cpu_min && wu->workers_registered > 1)) - wu->rd_workers_cpu_min = rrddim_add(wu->st_workers_cpu, "min", NULL, 1, 10000ULL, RRD_ALGORITHM_ABSOLUTE); + wu->rd_workers_cpu_min = rrddim_add(wu->st_workers_cpu, "min", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); if (unlikely(!wu->rd_workers_cpu_max && wu->workers_registered > 1)) - wu->rd_workers_cpu_max = rrddim_add(wu->st_workers_cpu, "max", NULL, 1, 10000ULL, RRD_ALGORITHM_ABSOLUTE); + wu->rd_workers_cpu_max = rrddim_add(wu->st_workers_cpu, "max", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); if(unlikely(!wu->rd_workers_cpu_avg)) - wu->rd_workers_cpu_avg = rrddim_add(wu->st_workers_cpu, "average", NULL, 1, 10000ULL, RRD_ALGORITHM_ABSOLUTE); - - rrdset_next(wu->st_workers_cpu); + wu->rd_workers_cpu_avg = rrddim_add(wu->st_workers_cpu, "average", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); if(unlikely(wu->workers_cpu_min == WORKERS_MIN_PERCENT_DEFAULT)) wu->workers_cpu_min = 0.0; if(wu->rd_workers_cpu_min) - rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_min, (collected_number)(wu->workers_cpu_min * 10000ULL)); + rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_min, (collected_number)(wu->workers_cpu_min * WORKER_CHART_DECIMAL_PRECISION)); if(wu->rd_workers_cpu_max) - rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_max, (collected_number)(wu->workers_cpu_max * 10000ULL)); + rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_max, (collected_number)(wu->workers_cpu_max * WORKER_CHART_DECIMAL_PRECISION)); if(wu->workers_cpu_registered == 0) rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_avg, 0); else - rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_avg, (collected_number)( wu->workers_cpu_total * 10000ULL / (NETDATA_DOUBLE)wu->workers_cpu_registered )); + rrddim_set_by_pointer(wu->st_workers_cpu, wu->rd_workers_cpu_avg, (collected_number)( wu->workers_cpu_total * WORKER_CHART_DECIMAL_PRECISION / (NETDATA_DOUBLE)wu->workers_cpu_registered )); rrdset_done(wu->st_workers_cpu); } @@ -1184,15 +2201,16 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) { ); } - rrdset_next(wu->st_workers_jobs_per_job_type); - { size_t i; - for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) { - if (wu->per_job_type[i].name[0]) { + for(i = 0; i <= wu->workers_max_job_id ;i++) { + if(unlikely(wu->per_job_type[i].type != WORKER_METRIC_IDLE_BUSY)) + continue; + + if (wu->per_job_type[i].name) { if(unlikely(!wu->per_job_type[i].rd_jobs_started)) - wu->per_job_type[i].rd_jobs_started = rrddim_add(wu->st_workers_jobs_per_job_type, wu->per_job_type[i].name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + wu->per_job_type[i].rd_jobs_started = rrddim_add(wu->st_workers_jobs_per_job_type, string2str(wu->per_job_type[i].name), NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_set_by_pointer(wu->st_workers_jobs_per_job_type, wu->per_job_type[i].rd_jobs_started, (collected_number)(wu->per_job_type[i].jobs_started)); } @@ -1226,15 +2244,16 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) { ); } - rrdset_next(wu->st_workers_busy_per_job_type); - { size_t i; - for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) { - if (wu->per_job_type[i].name[0]) { + for(i = 0; i <= wu->workers_max_job_id ;i++) { + if(unlikely(wu->per_job_type[i].type != WORKER_METRIC_IDLE_BUSY)) + continue; + + if (wu->per_job_type[i].name) { if(unlikely(!wu->per_job_type[i].rd_busy_time)) - wu->per_job_type[i].rd_busy_time = rrddim_add(wu->st_workers_busy_per_job_type, wu->per_job_type[i].name, NULL, 1, USEC_PER_MS, RRD_ALGORITHM_ABSOLUTE); + wu->per_job_type[i].rd_busy_time = rrddim_add(wu->st_workers_busy_per_job_type, string2str(wu->per_job_type[i].name), NULL, 1, USEC_PER_MS, RRD_ALGORITHM_ABSOLUTE); rrddim_set_by_pointer(wu->st_workers_busy_per_job_type, wu->per_job_type[i].rd_busy_time, (collected_number)(wu->per_job_type[i].busy_time)); } @@ -1271,13 +2290,123 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) { wu->rd_workers_threads_free = rrddim_add(wu->st_workers_threads, "free", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); wu->rd_workers_threads_busy = rrddim_add(wu->st_workers_threads, "busy", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else - rrdset_next(wu->st_workers_threads); rrddim_set_by_pointer(wu->st_workers_threads, wu->rd_workers_threads_free, (collected_number)(wu->workers_registered - wu->workers_busy)); rrddim_set_by_pointer(wu->st_workers_threads, wu->rd_workers_threads_busy, (collected_number)(wu->workers_busy)); rrdset_done(wu->st_workers_threads); } + + // ---------------------------------------------------------------------- + // custom metric types WORKER_METRIC_ABSOLUTE + + { + size_t i; + for (i = 0; i <= wu->workers_max_job_id; i++) { + if(wu->per_job_type[i].type != WORKER_METRIC_ABSOLUTE) + continue; + + if(!wu->per_job_type[i].count_value) + continue; + + if(!wu->per_job_type[i].st) { + size_t job_name_len = string_strlen(wu->per_job_type[i].name); + if(job_name_len > RRD_ID_LENGTH_MAX) job_name_len = RRD_ID_LENGTH_MAX; + + char job_name_sanitized[job_name_len + 1]; + rrdset_strncpyz_name(job_name_sanitized, string2str(wu->per_job_type[i].name), job_name_len); + + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(name, RRD_ID_LENGTH_MAX, "workers_%s_value_%s", wu->name_lowercase, job_name_sanitized); + + char context[RRD_ID_LENGTH_MAX + 1]; + snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.value.%s", wu->name_lowercase, job_name_sanitized); + + char title[1000 + 1]; + snprintf(title, 1000, "Netdata Workers %s Value of %s", wu->name_lowercase, string2str(wu->per_job_type[i].name)); + + wu->per_job_type[i].st = rrdset_create_localhost( + "netdata" + , name + , NULL + , wu->family + , context + , title + , (wu->per_job_type[i].units)?string2str(wu->per_job_type[i].units):"value" + , "netdata" + , "stats" + , wu->priority + 5 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + wu->per_job_type[i].rd_min = rrddim_add(wu->per_job_type[i].st, "min", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); + wu->per_job_type[i].rd_max = rrddim_add(wu->per_job_type[i].st, "max", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); + wu->per_job_type[i].rd_avg = rrddim_add(wu->per_job_type[i].st, "average", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(wu->per_job_type[i].st, wu->per_job_type[i].rd_min, (collected_number)(wu->per_job_type[i].min_value * WORKER_CHART_DECIMAL_PRECISION)); + rrddim_set_by_pointer(wu->per_job_type[i].st, wu->per_job_type[i].rd_max, (collected_number)(wu->per_job_type[i].max_value * WORKER_CHART_DECIMAL_PRECISION)); + rrddim_set_by_pointer(wu->per_job_type[i].st, wu->per_job_type[i].rd_avg, (collected_number)(wu->per_job_type[i].sum_value / wu->per_job_type[i].count_value * WORKER_CHART_DECIMAL_PRECISION)); + + rrdset_done(wu->per_job_type[i].st); + } + } + + // ---------------------------------------------------------------------- + // custom metric types WORKER_METRIC_INCREMENTAL + + { + size_t i; + for (i = 0; i <= wu->workers_max_job_id ; i++) { + if(wu->per_job_type[i].type != WORKER_METRIC_INCREMENT && wu->per_job_type[i].type != WORKER_METRIC_INCREMENTAL_TOTAL) + continue; + + if(!wu->per_job_type[i].count_value) + continue; + + if(!wu->per_job_type[i].st) { + size_t job_name_len = string_strlen(wu->per_job_type[i].name); + if(job_name_len > RRD_ID_LENGTH_MAX) job_name_len = RRD_ID_LENGTH_MAX; + + char job_name_sanitized[job_name_len + 1]; + rrdset_strncpyz_name(job_name_sanitized, string2str(wu->per_job_type[i].name), job_name_len); + + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(name, RRD_ID_LENGTH_MAX, "workers_%s_rate_%s", wu->name_lowercase, job_name_sanitized); + + char context[RRD_ID_LENGTH_MAX + 1]; + snprintf(context, RRD_ID_LENGTH_MAX, "netdata.workers.%s.rate.%s", wu->name_lowercase, job_name_sanitized); + + char title[1000 + 1]; + snprintf(title, 1000, "Netdata Workers %s Rate of %s", wu->name_lowercase, string2str(wu->per_job_type[i].name)); + + wu->per_job_type[i].st = rrdset_create_localhost( + "netdata" + , name + , NULL + , wu->family + , context + , title + , (wu->per_job_type[i].units)?string2str(wu->per_job_type[i].units):"rate" + , "netdata" + , "stats" + , wu->priority + 5 + , localhost->rrd_update_every + , RRDSET_TYPE_LINE + ); + + wu->per_job_type[i].rd_min = rrddim_add(wu->per_job_type[i].st, "min", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); + wu->per_job_type[i].rd_max = rrddim_add(wu->per_job_type[i].st, "max", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); + wu->per_job_type[i].rd_avg = rrddim_add(wu->per_job_type[i].st, "average", NULL, 1, WORKER_CHART_DECIMAL_PRECISION, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(wu->per_job_type[i].st, wu->per_job_type[i].rd_min, (collected_number)(wu->per_job_type[i].min_value * WORKER_CHART_DECIMAL_PRECISION)); + rrddim_set_by_pointer(wu->per_job_type[i].st, wu->per_job_type[i].rd_max, (collected_number)(wu->per_job_type[i].max_value * WORKER_CHART_DECIMAL_PRECISION)); + rrddim_set_by_pointer(wu->per_job_type[i].st, wu->per_job_type[i].rd_avg, (collected_number)(wu->per_job_type[i].sum_value / wu->per_job_type[i].count_value * WORKER_CHART_DECIMAL_PRECISION)); + + rrdset_done(wu->per_job_type[i].st); + } + } } static void workers_utilization_reset_statistics(struct worker_utilization *wu) { @@ -1304,12 +2433,17 @@ static void workers_utilization_reset_statistics(struct worker_utilization *wu) wu->per_job_type[i].jobs_started = 0; wu->per_job_type[i].busy_time = 0; + + wu->per_job_type[i].min_value = NAN; + wu->per_job_type[i].max_value = NAN; + wu->per_job_type[i].sum_value = NAN; + wu->per_job_type[i].count_value = 0; } struct worker_thread *wt; for(wt = wu->threads; wt ; wt = wt->next) { - wt->enabled = 0; - wt->cpu_enabled = 0; + wt->enabled = false; + wt->cpu_enabled = false; } } @@ -1337,31 +2471,33 @@ static int read_thread_cpu_time_from_proc_stat(pid_t pid __maybe_unused, kernel_ #endif } +static Pvoid_t workers_by_pid_JudyL_array = NULL; + static void workers_threads_cleanup(struct worker_utilization *wu) { - struct worker_thread *t; - - // free threads at the beginning of the linked list - while(wu->threads && !wu->threads->enabled) { - t = wu->threads; - wu->threads = t->next; - t->next = NULL; - freez(t); - } + netdata_thread_disable_cancelability(); - // free threads in the middle of the linked list - for(t = wu->threads; t && t->next ; t = t->next) { - if(t->next->enabled) continue; + struct worker_thread *t = wu->threads; + while(t) { + struct worker_thread *next = t->next; - struct worker_thread *to_remove = t->next; - t->next = to_remove->next; - to_remove->next = NULL; - freez(to_remove); + if(!t->enabled) { + JudyLDel(&workers_by_pid_JudyL_array, t->pid, PJE0); + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(wu->threads, t, prev, next); + freez(t); + } + t = next; } -} -static struct worker_thread *worker_thread_find(struct worker_utilization *wu, pid_t pid) { - struct worker_thread *wt; - for(wt = wu->threads; wt && wt->pid != pid ; wt = wt->next) ; + netdata_thread_enable_cancelability(); + } + +static struct worker_thread *worker_thread_find(struct worker_utilization *wu __maybe_unused, pid_t pid) { + struct worker_thread *wt = NULL; + + Pvoid_t *PValue = JudyLGet(workers_by_pid_JudyL_array, pid, PJE0); + if(PValue) + wt = *PValue; + return wt; } @@ -1371,9 +2507,11 @@ static struct worker_thread *worker_thread_create(struct worker_utilization *wu, wt = (struct worker_thread *)callocz(1, sizeof(struct worker_thread)); wt->pid = pid; + Pvoid_t *PValue = JudyLIns(&workers_by_pid_JudyL_array, pid, PJE0); + *PValue = wt; + // link it - wt->next = wu->threads; - wu->threads = wt; + DOUBLE_LINKED_LIST_APPEND_UNSAFE(wu->threads, wt, prev, next); return wt; } @@ -1386,13 +2524,27 @@ static struct worker_thread *worker_thread_find_or_create(struct worker_utilizat return wt; } -static void worker_utilization_charts_callback(void *ptr, pid_t pid __maybe_unused, const char *thread_tag __maybe_unused, size_t utilization_usec __maybe_unused, size_t duration_usec __maybe_unused, size_t jobs_started __maybe_unused, size_t is_running __maybe_unused, const char **job_types_names __maybe_unused, size_t *job_types_jobs_started __maybe_unused, usec_t *job_types_busy_time __maybe_unused) { +static void worker_utilization_charts_callback(void *ptr + , pid_t pid __maybe_unused + , const char *thread_tag __maybe_unused + , size_t max_job_id __maybe_unused + , size_t utilization_usec __maybe_unused + , size_t duration_usec __maybe_unused + , size_t jobs_started __maybe_unused + , size_t is_running __maybe_unused + , STRING **job_types_names __maybe_unused + , STRING **job_types_units __maybe_unused + , WORKER_METRIC_TYPE *job_types_metric_types __maybe_unused + , size_t *job_types_jobs_started __maybe_unused + , usec_t *job_types_busy_time __maybe_unused + , NETDATA_DOUBLE *job_types_custom_metrics __maybe_unused + ) { struct worker_utilization *wu = (struct worker_utilization *)ptr; // find the worker_thread in the list struct worker_thread *wt = worker_thread_find_or_create(wu, pid); - wt->enabled = 1; + wt->enabled = true; wt->busy_time = utilization_usec; wt->jobs_started = jobs_started; @@ -1400,6 +2552,9 @@ static void worker_utilization_charts_callback(void *ptr, pid_t pid __maybe_unus wt->stime_old = wt->stime; wt->collected_time_old = wt->collected_time; + if(max_job_id > wu->workers_max_job_id) + wu->workers_max_job_id = max_job_id; + wu->workers_total_busy_time += utilization_usec; wu->workers_total_duration += duration_usec; wu->workers_total_jobs_started += jobs_started; @@ -1415,13 +2570,33 @@ static void worker_utilization_charts_callback(void *ptr, pid_t pid __maybe_unus // accumulate per job type statistics size_t i; - for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) { + for(i = 0; i <= max_job_id ;i++) { + if(!wu->per_job_type[i].name && job_types_names[i]) + wu->per_job_type[i].name = string_dup(job_types_names[i]); + + if(!wu->per_job_type[i].units && job_types_units[i]) + wu->per_job_type[i].units = string_dup(job_types_units[i]); + + wu->per_job_type[i].type = job_types_metric_types[i]; + wu->per_job_type[i].jobs_started += job_types_jobs_started[i]; wu->per_job_type[i].busy_time += job_types_busy_time[i]; - // new job type found - if(unlikely(!wu->per_job_type[i].name[0] && job_types_names[i])) - strncpyz(wu->per_job_type[i].name, job_types_names[i], WORKER_UTILIZATION_MAX_JOB_NAME_LENGTH); + NETDATA_DOUBLE value = job_types_custom_metrics[i]; + if(netdata_double_isnumber(value)) { + if(!wu->per_job_type[i].count_value) { + wu->per_job_type[i].count_value = 1; + wu->per_job_type[i].min_value = value; + wu->per_job_type[i].max_value = value; + wu->per_job_type[i].sum_value = value; + } + else { + wu->per_job_type[i].count_value++; + wu->per_job_type[i].sum_value += value; + if(value < wu->per_job_type[i].min_value) wu->per_job_type[i].min_value = value; + if(value > wu->per_job_type[i].max_value) wu->per_job_type[i].max_value = value; + } + } } // find its CPU utilization @@ -1433,13 +2608,13 @@ static void worker_utilization_charts_callback(void *ptr, pid_t pid __maybe_unus double stime = (double)(wt->stime - wt->stime_old) / (double)system_hz * 100.0 * (double)USEC_PER_SEC / (double)delta; double cpu = utime + stime; wt->cpu = cpu; - wt->cpu_enabled = 1; + wt->cpu_enabled = true; wu->workers_cpu_total += cpu; if(cpu < wu->workers_cpu_min) wu->workers_cpu_min = cpu; if(cpu > wu->workers_cpu_max) wu->workers_cpu_max = cpu; } - wu->workers_cpu_registered += wt->cpu_enabled; + wu->workers_cpu_registered += (wt->cpu_enabled) ? 1 : 0; } static void worker_utilization_charts(void) { @@ -1462,7 +2637,7 @@ static void worker_utilization_charts(void) { } static void worker_utilization_finish(void) { - int i; + int i, j; for(i = 0; all_workers_utilization[i].name ;i++) { struct worker_utilization *wu = &all_workers_utilization[i]; @@ -1471,9 +2646,18 @@ static void worker_utilization_finish(void) { wu->name_lowercase = NULL; } + for(j = 0; j < WORKER_UTILIZATION_MAX_JOB_TYPES ;j++) { + string_freez(wu->per_job_type[j].name); + wu->per_job_type[j].name = NULL; + + string_freez(wu->per_job_type[j].units); + wu->per_job_type[j].units = NULL; + } + // mark all threads as not enabled struct worker_thread *t; - for(t = wu->threads; t ; t = t->next) t->enabled = 0; + for(t = wu->threads; t ; t = t->next) + t->enabled = false; // let the cleanup job free them workers_threads_cleanup(wu); @@ -1481,6 +2665,19 @@ static void worker_utilization_finish(void) { } // --------------------------------------------------------------------------------------------------------------------- +// global statistics thread + + +static void global_statistics_register_workers(void) { + worker_register("STATS"); + worker_register_job_name(WORKER_JOB_GLOBAL, "global"); + worker_register_job_name(WORKER_JOB_REGISTRY, "registry"); + worker_register_job_name(WORKER_JOB_WORKERS, "workers"); + worker_register_job_name(WORKER_JOB_DBENGINE, "dbengine"); + worker_register_job_name(WORKER_JOB_STRINGS, "strings"); + worker_register_job_name(WORKER_JOB_DICTIONARIES, "dictionaries"); + worker_register_job_name(WORKER_JOB_MALLOC_TRACE, "malloc_trace"); +} static void global_statistics_cleanup(void *ptr) { @@ -1498,11 +2695,7 @@ static void global_statistics_cleanup(void *ptr) void *global_statistics_main(void *ptr) { - worker_register("STATS"); - worker_register_job_name(WORKER_JOB_GLOBAL, "global"); - worker_register_job_name(WORKER_JOB_REGISTRY, "registry"); - worker_register_job_name(WORKER_JOB_WORKERS, "workers"); - worker_register_job_name(WORKER_JOB_DBENGINE, "dbengine"); + global_statistics_register_workers(); netdata_thread_cleanup_push(global_statistics_cleanup, ptr); @@ -1523,22 +2716,78 @@ void *global_statistics_main(void *ptr) worker_is_idle(); heartbeat_next(&hb, step); - worker_is_busy(WORKER_JOB_WORKERS); - worker_utilization_charts(); - worker_is_busy(WORKER_JOB_GLOBAL); global_statistics_charts(); worker_is_busy(WORKER_JOB_REGISTRY); registry_statistics(); - worker_is_busy(WORKER_JOB_DBENGINE); - dbengine_statistics_charts(); + if(dbengine_enabled) { + worker_is_busy(WORKER_JOB_DBENGINE); + dbengine_statistics_charts(); + } worker_is_busy(WORKER_JOB_HEARTBEAT); update_heartbeat_charts(); + + worker_is_busy(WORKER_JOB_STRINGS); + update_strings_charts(); + + worker_is_busy(WORKER_JOB_DICTIONARIES); + dictionary_statistics(); + +#ifdef NETDATA_TRACE_ALLOCATIONS + worker_is_busy(WORKER_JOB_MALLOC_TRACE); + malloc_trace_statistics(); +#endif } netdata_thread_cleanup_pop(1); return NULL; } + + +// --------------------------------------------------------------------------------------------------------------------- +// workers thread + +static void global_statistics_workers_cleanup(void *ptr) +{ + worker_unregister(); + + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; + static_thread->enabled = NETDATA_MAIN_THREAD_EXITING; + + info("cleaning up..."); + + worker_utilization_finish(); + + static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; +} + +void *global_statistics_workers_main(void *ptr) +{ + global_statistics_register_workers(); + + netdata_thread_cleanup_push(global_statistics_workers_cleanup, ptr); + + int update_every = + (int)config_get_number(CONFIG_SECTION_GLOBAL_STATISTICS, "update every", localhost->rrd_update_every); + if (update_every < localhost->rrd_update_every) + update_every = localhost->rrd_update_every; + + usec_t step = update_every * USEC_PER_SEC; + heartbeat_t hb; + heartbeat_init(&hb); + + while (!netdata_exit) { + worker_is_idle(); + heartbeat_next(&hb, step); + + worker_is_busy(WORKER_JOB_WORKERS); + worker_utilization_charts(); + } + + netdata_thread_cleanup_pop(1); + return NULL; +} + diff --git a/daemon/global_statistics.h b/daemon/global_statistics.h index 268b5319e..f7d6775cf 100644 --- a/daemon/global_statistics.h +++ b/daemon/global_statistics.h @@ -3,20 +3,28 @@ #ifndef NETDATA_GLOBAL_STATISTICS_H #define NETDATA_GLOBAL_STATISTICS_H 1 -#include "common.h" +#include "database/rrd.h" // ---------------------------------------------------------------------------- // global statistics -extern void rrdr_query_completed(uint64_t db_points_read, uint64_t result_points_generated); +void global_statistics_ml_query_completed(size_t points_read); +void global_statistics_exporters_query_completed(size_t points_read); +void global_statistics_backfill_query_completed(size_t points_read); +void global_statistics_rrdr_query_completed(size_t queries, uint64_t db_points_read, uint64_t result_points_generated, QUERY_SOURCE query_source); +void global_statistics_sqlite3_query_completed(bool success, bool busy, bool locked); +void global_statistics_sqlite3_row_completed(void); +void global_statistics_rrdset_done_chart_collection_completed(size_t *points_read_per_tier_array); -extern void finished_web_request_statistics(uint64_t dt, - uint64_t bytes_received, - uint64_t bytes_sent, - uint64_t content_size, - uint64_t compressed_content_size); +void global_statistics_web_request_completed(uint64_t dt, + uint64_t bytes_received, + uint64_t bytes_sent, + uint64_t content_size, + uint64_t compressed_content_size); -extern uint64_t web_client_connected(void); -extern void web_client_disconnected(void); +uint64_t global_statistics_web_client_connected(void); +void global_statistics_web_client_disconnected(void); + +extern bool global_statistics_enabled; #endif /* NETDATA_GLOBAL_STATISTICS_H */ diff --git a/daemon/main.c b/daemon/main.c index ada3c14f2..6b591385d 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -4,6 +4,7 @@ #include "buildinfo.h" #include "static_threads.h" +bool unittest_running = false; int netdata_zero_metrics_enabled; int netdata_anonymous_statistics_enabled; @@ -55,13 +56,19 @@ void netdata_cleanup_and_exit(int ret) { // free the database info("EXIT: freeing database memory..."); #ifdef ENABLE_DBENGINE - for(int tier = 0; tier < storage_tiers ; tier++) - rrdeng_prepare_exit(multidb_ctx[tier]); + if(dbengine_enabled) { + for (size_t tier = 0; tier < storage_tiers; tier++) + rrdeng_prepare_exit(multidb_ctx[tier]); + } #endif + metadata_sync_shutdown_prepare(); rrdhost_free_all(); + metadata_sync_shutdown(); #ifdef ENABLE_DBENGINE - for(int tier = 0; tier < storage_tiers ; tier++) - rrdeng_exit(multidb_ctx[tier]); + if(dbengine_enabled) { + for (size_t tier = 0; tier < storage_tiers; tier++) + rrdeng_exit(multidb_ctx[tier]); + } #endif } sql_close_context_database(); @@ -255,7 +262,8 @@ void cancel_main_threads() { for (i = 0; static_threads[i].name != NULL ; i++) freez(static_threads[i].thread); - free(static_threads); + + freez(static_threads); } struct option_def option_definitions[] = { @@ -303,7 +311,7 @@ int help(int exitcode) { " | '-' '-' '-' '-' real-time performance monitoring, done right! \n" " +----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+--->\n" "\n" - " Copyright (C) 2016-2020, Netdata, Inc. \n" + " Copyright (C) 2016-2022, Netdata, Inc. \n" " Released under GNU General Public License v3 or later.\n" " All rights reserved.\n" "\n" @@ -314,7 +322,8 @@ int help(int exitcode) { " License : https://github.com/netdata/netdata/blob/master/LICENSE.md\n" "\n" " Twitter : https://twitter.com/linuxnetdata\n" - " Facebook : https://www.facebook.com/linuxnetdata/\n" + " LinkedIn : https://linkedin.com/company/netdata-cloud/\n" + " Facebook : https://facebook.com/linuxnetdata/\n" "\n" "\n" ); @@ -379,10 +388,10 @@ int help(int exitcode) { static void security_init(){ char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s/ssl/key.pem",netdata_configured_user_config_dir); - security_key = config_get(CONFIG_SECTION_WEB, "ssl key", filename); + netdata_ssl_security_key = config_get(CONFIG_SECTION_WEB, "ssl key", filename); snprintfz(filename, FILENAME_MAX, "%s/ssl/cert.pem",netdata_configured_user_config_dir); - security_cert = config_get(CONFIG_SECTION_WEB, "ssl certificate", filename); + netdata_ssl_security_cert = config_get(CONFIG_SECTION_WEB, "ssl certificate", filename); tls_version = config_get(CONFIG_SECTION_WEB, "tls version", "1.3"); tls_ciphers = config_get(CONFIG_SECTION_WEB, "tls ciphers", "none"); @@ -402,6 +411,9 @@ static void log_init(void) { snprintfz(filename, FILENAME_MAX, "%s/access.log", netdata_configured_log_dir); stdaccess_filename = config_get(CONFIG_SECTION_LOGS, "access", filename); + snprintfz(filename, FILENAME_MAX, "%s/health.log", netdata_configured_log_dir); + stdhealth_filename = config_get(CONFIG_SECTION_LOGS, "health", filename); + #ifdef ENABLE_ACLK aclklog_enabled = config_get_boolean(CONFIG_SECTION_CLOUD, "conversation log", CONFIG_BOOLEAN_NO); if (aclklog_enabled) { @@ -667,7 +679,7 @@ static void get_netdata_configured_variables() { // ------------------------------------------------------------------------ // get default Database Engine page cache size in MiB - db_engine_use_malloc = config_get_boolean(CONFIG_SECTION_DB, "dbengine page cache with malloc", CONFIG_BOOLEAN_NO); + db_engine_use_malloc = config_get_boolean(CONFIG_SECTION_DB, "dbengine page cache with malloc", CONFIG_BOOLEAN_YES); default_rrdeng_page_cache_mb = (int) config_get_number(CONFIG_SECTION_DB, "dbengine page cache size MB", default_rrdeng_page_cache_mb); if(default_rrdeng_page_cache_mb < RRDENG_MIN_PAGE_CACHE_SIZE_MB) { error("Invalid page cache size %d given. Defaulting to %d.", default_rrdeng_page_cache_mb, RRDENG_MIN_PAGE_CACHE_SIZE_MB); @@ -735,12 +747,6 @@ static void get_netdata_configured_variables() { config_set_number(CONFIG_SECTION_DB, "gap when lost iterations above", gap_when_lost_iterations_above); } - // -------------------------------------------------------------------- - // rrdcontext - - rrdcontext_enabled = config_get_boolean(CONFIG_SECTION_CLOUD, "rrdcontexts", rrdcontext_enabled); - - // -------------------------------------------------------------------- // get various system parameters @@ -801,12 +807,13 @@ int get_system_info(struct rrdhost_system_info *system_info) { info("Executing %s", script); - FILE *fp = mypopen(script, &command_pid); - if(fp) { + FILE *fp_child_input; + FILE *fp_child_output = netdata_popen(script, &command_pid, &fp_child_input); + if(fp_child_output) { char line[200 + 1]; // Removed the double strlens, if the Coverity tainted string warning reappears I'll revert. // One time init code, but I'm curious about the warning... - while (fgets(line, 200, fp) != NULL) { + while (fgets(line, 200, fp_child_output) != NULL) { char *value=line; while (*value && *value != '=') value++; if (*value=='=') { @@ -827,7 +834,7 @@ int get_system_info(struct rrdhost_system_info *system_info) { } } } - mypclose(fp, command_pid); + netdata_pclose(fp_child_input, fp_child_output, command_pid); } freez(script); return 0; @@ -976,6 +983,8 @@ int main(int argc, char **argv) { } if(strcmp(optarg, "unittest") == 0) { + unittest_running = true; + if (unit_test_static_threads()) return 1; if (unit_test_buffer()) @@ -987,7 +996,6 @@ int main(int argc, char **argv) { // No call to load the config file on this code-path post_conf_load(&user); get_netdata_configured_variables(); - rrdcontext_enabled = CONFIG_BOOLEAN_NO; default_rrd_update_every = 1; default_rrd_memory_mode = RRD_MEMORY_MODE_RAM; default_health_enabled = 0; @@ -1004,8 +1012,11 @@ int main(int argc, char **argv) { if(test_dbengine()) return 1; #endif if(test_sqlite()) return 1; + if(string_unittest(10000)) return 1; if (dictionary_unittest(10000)) return 1; + if(aral_unittest(10000)) + return 1; if (rrdlabels_unittest()) return 1; if (ctx_unittest()) @@ -1013,6 +1024,9 @@ int main(int argc, char **argv) { fprintf(stderr, "\n\nALL TESTS PASSED\n\n"); return 0; } + else if(strcmp(optarg, "escapetest") == 0) { + return command_argument_sanitization_tests(); + } #ifdef ENABLE_ML_TESTS else if(strcmp(optarg, "mltest") == 0) { return test_ml(argc, argv); @@ -1020,17 +1034,33 @@ int main(int argc, char **argv) { #endif #ifdef ENABLE_DBENGINE else if(strcmp(optarg, "mctest") == 0) { + unittest_running = true; return mc_unittest(); } else if(strcmp(optarg, "ctxtest") == 0) { + unittest_running = true; return ctx_unittest(); } else if(strcmp(optarg, "dicttest") == 0) { + unittest_running = true; return dictionary_unittest(10000); } + else if(strcmp(optarg, "araltest") == 0) { + unittest_running = true; + return aral_unittest(10000); + } + else if(strcmp(optarg, "stringtest") == 0) { + unittest_running = true; + return string_unittest(10000); + } else if(strcmp(optarg, "rrdlabelstest") == 0) { + unittest_running = true; return rrdlabels_unittest(); } + else if(strcmp(optarg, "metatest") == 0) { + unittest_running = true; + return metadata_unittest(); + } else if(strncmp(optarg, createdataset_string, strlen(createdataset_string)) == 0) { optarg += strlen(createdataset_string); unsigned history_seconds = strtoul(optarg, NULL, 0); @@ -1284,6 +1314,7 @@ int main(int argc, char **argv) { } #endif + if(!config_loaded) { load_netdata_conf(NULL, 0); @@ -1291,6 +1322,11 @@ int main(int argc, char **argv) { load_cloud_conf(0); } + char *nd_disable_cloud = getenv("NETDATA_DISABLE_CLOUD"); + if (nd_disable_cloud && !strncmp(nd_disable_cloud, "1", 1)) { + appconfig_set(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", "false"); + } + // ------------------------------------------------------------------------ // initialize netdata @@ -1303,6 +1339,12 @@ int main(int argc, char **argv) { i = (int)config_get_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for netdata", 1); if(i > 0) mallopt(M_ARENA_MAX, 1); + + +#ifdef NETDATA_INTERNAL_CHECKS + mallopt(M_PERTURB, 0x5A); + // mallopt(M_MXFAST, 0); +#endif #endif // initialize the system clocks @@ -1401,8 +1443,13 @@ int main(int argc, char **argv) { if(st->enabled && st->init_routine) st->init_routine(); - } + if(st->env_name) + setenv(st->env_name, st->enabled?"YES":"NO", 1); + + if(st->global_variable) + *st->global_variable = (st->enabled) ? true : false; + } // -------------------------------------------------------------------- // create the listening sockets diff --git a/daemon/main.h b/daemon/main.h index 63d6c5a09..a4e2b3aa7 100644 --- a/daemon/main.h +++ b/daemon/main.h @@ -22,9 +22,9 @@ struct option_def { const char *default_value; }; -extern void cancel_main_threads(void); -extern int killpid(pid_t pid); -extern void netdata_cleanup_and_exit(int ret) NORETURN; -extern void send_statistics(const char *action, const char *action_result, const char *action_data); +void cancel_main_threads(void); +int killpid(pid_t pid); +void netdata_cleanup_and_exit(int ret) NORETURN; +void send_statistics(const char *action, const char *action_result, const char *action_data); #endif /* NETDATA_MAIN_H */ diff --git a/daemon/service.c b/daemon/service.c index 61cc1281a..a7db7ceb7 100644 --- a/daemon/service.c +++ b/daemon/service.c @@ -5,12 +5,253 @@ /* Run service jobs every X seconds */ #define SERVICE_HEARTBEAT 10 +#define TIME_TO_RUN_OBSOLETIONS_ON_CHILD_CONNECT (3600 / 2) +#define ITERATIONS_TO_RUN_OBSOLETIONS_ON_CHILD_CONNECT 60 + +#define WORKER_JOB_CHILD_CHART_OBSOLETION_CHECK 1 +#define WORKER_JOB_CLEANUP_OBSOLETE_CHARTS 2 +#define WORKER_JOB_ARCHIVE_CHART 3 +#define WORKER_JOB_ARCHIVE_CHART_DIMENSIONS 4 +#define WORKER_JOB_ARCHIVE_DIMENSION 5 +#define WORKER_JOB_CLEANUP_ORPHAN_HOSTS 6 +#define WORKER_JOB_CLEANUP_OBSOLETE_CHARTS_ON_HOSTS 7 +#define WORKER_JOB_FREE_HOST 9 +#define WORKER_JOB_SAVE_HOST_CHARTS 10 +#define WORKER_JOB_DELETE_HOST_CHARTS 11 +#define WORKER_JOB_FREE_CHART 12 +#define WORKER_JOB_SAVE_CHART 13 +#define WORKER_JOB_DELETE_CHART 14 +#define WORKER_JOB_FREE_DIMENSION 15 + +static void svc_rrddim_obsolete_to_archive(RRDDIM *rd) { + RRDSET *st = rd->rrdset; + + if(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED) || !rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) + return; + + worker_is_busy(WORKER_JOB_ARCHIVE_DIMENSION); + + rrddim_flag_set(rd, RRDDIM_FLAG_ARCHIVED); + rrddim_flag_clear(rd, RRDDIM_FLAG_OBSOLETE); + + const char *cache_filename = rrddim_cache_filename(rd); + if(cache_filename) { + info("Deleting dimension file '%s'.", cache_filename); + if (unlikely(unlink(cache_filename) == -1)) + error("Cannot delete dimension file '%s'", cache_filename); + } + + if (rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { + rrddimvar_delete_all(rd); + + /* only a collector can mark a chart as obsolete, so we must remove the reference */ + + size_t tiers_available = 0, tiers_said_yes = 0; + for(size_t tier = 0; tier < storage_tiers ;tier++) { + if(rd->tiers[tier]) { + tiers_available++; + + if(rd->tiers[tier]->collect_ops->finalize(rd->tiers[tier]->db_collection_handle)) + tiers_said_yes++; + + rd->tiers[tier]->db_collection_handle = NULL; + } + } + + if (tiers_available == tiers_said_yes && tiers_said_yes) { + /* This metric has no data and no references */ + metaqueue_delete_dimension_uuid(&rd->metric_uuid); + } + else { + /* Do not delete this dimension */ + return; + } + } + + worker_is_busy(WORKER_JOB_FREE_DIMENSION); + rrddim_free(st, rd); +} + +static bool svc_rrdset_archive_obsolete_dimensions(RRDSET *st, bool all_dimensions) { + worker_is_busy(WORKER_JOB_ARCHIVE_CHART_DIMENSIONS); + + RRDDIM *rd; + time_t now = now_realtime_sec(); + + bool done_all_dimensions = true; + + dfe_start_write(st->rrddim_root_index, rd) { + if(unlikely( + all_dimensions || + (rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE) && (rd->last_collected_time.tv_sec + rrdset_free_obsolete_time < now)) + )) { + + if(dictionary_acquired_item_references(rd_dfe.item) == 1) { + info("Removing obsolete dimension '%s' (%s) of '%s' (%s).", rrddim_name(rd), rrddim_id(rd), rrdset_name(st), rrdset_id(st)); + svc_rrddim_obsolete_to_archive(rd); + } + else + done_all_dimensions = false; + } + else + done_all_dimensions = false; + } + dfe_done(rd); + + return done_all_dimensions; +} + +static void svc_rrdset_obsolete_to_archive(RRDSET *st) { + worker_is_busy(WORKER_JOB_ARCHIVE_CHART); + + if(!svc_rrdset_archive_obsolete_dimensions(st, true)) + return; + + rrdset_flag_set(st, RRDSET_FLAG_ARCHIVED); + rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); + + rrdcalc_unlink_all_rrdset_alerts(st); + + rrdsetvar_release_and_delete_all(st); + + // has to be run after all dimensions are archived - or use-after-free will occur + rrdvar_delete_all(st->rrdvars); + + if(st->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) { + if(rrdhost_option_check(st->rrdhost, RRDHOST_OPTION_DELETE_OBSOLETE_CHARTS)) { + worker_is_busy(WORKER_JOB_DELETE_CHART); + rrdset_delete_files(st); + } + else { + worker_is_busy(WORKER_JOB_SAVE_CHART); + rrdset_save(st); + } + + worker_is_busy(WORKER_JOB_FREE_CHART); + rrdset_free(st); + } +} + +static void svc_rrdhost_cleanup_obsolete_charts(RRDHOST *host) { + worker_is_busy(WORKER_JOB_CLEANUP_OBSOLETE_CHARTS); + + time_t now = now_realtime_sec(); + RRDSET *st; + rrdset_foreach_reentrant(st, host) { + if(rrdset_is_replicating(st)) + continue; + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) + && st->last_accessed_time + rrdset_free_obsolete_time < now + && st->last_updated.tv_sec + rrdset_free_obsolete_time < now + && st->last_collected_time.tv_sec + rrdset_free_obsolete_time < now + )) { + svc_rrdset_obsolete_to_archive(st); + } + else if(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS)) { + rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS); + svc_rrdset_archive_obsolete_dimensions(st, false); + } + } + rrdset_foreach_done(st); +} + +static void svc_rrdset_check_obsoletion(RRDHOST *host) { + worker_is_busy(WORKER_JOB_CHILD_CHART_OBSOLETION_CHECK); + + time_t now = now_realtime_sec(); + time_t last_entry_t; + RRDSET *st; + rrdset_foreach_read(st, host) { + if(rrdset_is_replicating(st)) + continue; + + last_entry_t = rrdset_last_entry_t(st); + + if(last_entry_t && last_entry_t < host->senders_connect_time && + host->senders_connect_time + TIME_TO_RUN_OBSOLETIONS_ON_CHILD_CONNECT + ITERATIONS_TO_RUN_OBSOLETIONS_ON_CHILD_CONNECT * st->update_every + < now) + + rrdset_is_obsolete(st); + } + rrdset_foreach_done(st); +} + +static void svc_rrd_cleanup_obsolete_charts_from_all_hosts() { + worker_is_busy(WORKER_JOB_CLEANUP_OBSOLETE_CHARTS_ON_HOSTS); + + rrd_rdlock(); + + RRDHOST *host; + rrdhost_foreach_read(host) { + if(rrdhost_receiver_replicating_charts(host) || rrdhost_sender_replicating_charts(host)) + continue; + + if(rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_OBSOLETE_CHARTS|RRDHOST_FLAG_PENDING_OBSOLETE_DIMENSIONS)) { + rrdhost_flag_clear(host, RRDHOST_FLAG_PENDING_OBSOLETE_CHARTS|RRDHOST_FLAG_PENDING_OBSOLETE_DIMENSIONS); + svc_rrdhost_cleanup_obsolete_charts(host); + } + + if(host != localhost + && host->trigger_chart_obsoletion_check + && ( + ( + host->senders_last_chart_command + && host->senders_last_chart_command + host->health_delay_up_to < now_realtime_sec() + ) + || (host->senders_connect_time + TIME_TO_RUN_OBSOLETIONS_ON_CHILD_CONNECT < now_realtime_sec()) + ) + ) { + svc_rrdset_check_obsoletion(host); + host->trigger_chart_obsoletion_check = 0; + } + } + + rrd_unlock(); +} + +static void svc_rrdhost_cleanup_orphan_hosts(RRDHOST *protected_host) { + worker_is_busy(WORKER_JOB_CLEANUP_ORPHAN_HOSTS); + rrd_wrlock(); + + time_t now = now_realtime_sec(); + + RRDHOST *host; + +restart_after_removal: + rrdhost_foreach_write(host) { + if(!rrdhost_should_be_removed(host, protected_host, now)) + continue; + + info("Host '%s' with machine guid '%s' is obsolete - cleaning up.", rrdhost_hostname(host), host->machine_guid); + + if (rrdhost_option_check(host, RRDHOST_OPTION_DELETE_ORPHAN_HOST) + /* don't delete multi-host DB host files */ + && !(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_storage_engine_shared(host->db[0].instance)) + ) { + worker_is_busy(WORKER_JOB_DELETE_HOST_CHARTS); + rrdhost_delete_charts(host); + } + else { + worker_is_busy(WORKER_JOB_SAVE_HOST_CHARTS); + rrdhost_save_charts(host); + } + + worker_is_busy(WORKER_JOB_FREE_HOST); + rrdhost_free(host, 0); + goto restart_after_removal; + } + + rrd_unlock(); +} + static void service_main_cleanup(void *ptr) { struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; static_thread->enabled = NETDATA_MAIN_THREAD_EXITING; debug(D_SYSTEM, "Cleaning up..."); + worker_unregister(); static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; } @@ -20,6 +261,22 @@ static void service_main_cleanup(void *ptr) */ void *service_main(void *ptr) { + worker_register("SERVICE"); + worker_register_job_name(WORKER_JOB_CHILD_CHART_OBSOLETION_CHECK, "child chart obsoletion check"); + worker_register_job_name(WORKER_JOB_CLEANUP_OBSOLETE_CHARTS, "cleanup obsolete charts"); + worker_register_job_name(WORKER_JOB_ARCHIVE_CHART, "archive chart"); + worker_register_job_name(WORKER_JOB_ARCHIVE_CHART_DIMENSIONS, "archive chart dimensions"); + worker_register_job_name(WORKER_JOB_ARCHIVE_DIMENSION, "archive dimension"); + worker_register_job_name(WORKER_JOB_CLEANUP_ORPHAN_HOSTS, "cleanup orphan hosts"); + worker_register_job_name(WORKER_JOB_CLEANUP_OBSOLETE_CHARTS_ON_HOSTS, "cleanup obsolete charts on all hosts"); + worker_register_job_name(WORKER_JOB_FREE_HOST, "free host"); + worker_register_job_name(WORKER_JOB_SAVE_HOST_CHARTS, "save host charts"); + worker_register_job_name(WORKER_JOB_DELETE_HOST_CHARTS, "delete host charts"); + worker_register_job_name(WORKER_JOB_FREE_CHART, "free chart"); + worker_register_job_name(WORKER_JOB_SAVE_CHART, "save chart"); + worker_register_job_name(WORKER_JOB_DELETE_CHART, "delete chart"); + worker_register_job_name(WORKER_JOB_FREE_DIMENSION, "free dimension"); + netdata_thread_cleanup_push(service_main_cleanup, ptr); heartbeat_t hb; heartbeat_init(&hb); @@ -28,14 +285,11 @@ void *service_main(void *ptr) debug(D_SYSTEM, "Service thread starts"); while (!netdata_exit) { + worker_is_idle(); heartbeat_next(&hb, step); - rrd_cleanup_obsolete_charts(); - - rrd_wrlock(); - rrdhost_cleanup_orphan_hosts_nolock(localhost); - rrd_unlock(); - + svc_rrd_cleanup_obsolete_charts_from_all_hosts(); + svc_rrdhost_cleanup_orphan_hosts(localhost); } netdata_thread_cleanup_pop(1); diff --git a/daemon/signals.c b/daemon/signals.c index b991d46bf..c857a9b57 100644 --- a/daemon/signals.c +++ b/daemon/signals.c @@ -82,7 +82,7 @@ void signals_init(void) { // This prevents zombie processes when running in a container. if (getpid() == 1) { info("SIGNAL: Enabling reaper"); - myp_init(); + netdata_popen_tracking_init(); reaper_enabled = 1; } else { info("SIGNAL: Not enabling reaper"); @@ -139,7 +139,7 @@ void signals_reset(void) { } if (reaper_enabled == 1) - myp_free(); + netdata_popen_tracking_cleanup(); } // reap_child reaps the child identified by pid. @@ -198,7 +198,7 @@ static void reap_children() { } else if (i.si_pid == 0) { // No child exited. return; - } else if (myp_reap(i.si_pid) == 0) { + } else if (netdata_popen_tracking_pid_shoud_be_reaped(i.si_pid) == 0) { // myp managed, sleep for a short time to avoid busy wait while // this is handled by myp. usleep(10000); diff --git a/daemon/signals.h b/daemon/signals.h index 3fa2b0f43..12b1ed198 100644 --- a/daemon/signals.h +++ b/daemon/signals.h @@ -3,11 +3,11 @@ #ifndef NETDATA_SIGNALS_H #define NETDATA_SIGNALS_H 1 -extern void signals_init(void); -extern void signals_block(void); -extern void signals_unblock(void); -extern void signals_restore_SIGCHLD(void); -extern void signals_reset(void); -extern void signals_handle(void) NORETURN; +void signals_init(void); +void signals_block(void); +void signals_unblock(void); +void signals_restore_SIGCHLD(void); +void signals_reset(void); +void signals_handle(void) NORETURN; #endif //NETDATA_SIGNALS_H diff --git a/daemon/static_threads.c b/daemon/static_threads.c index 96e279906..b7730bc31 100644 --- a/daemon/static_threads.c +++ b/daemon/static_threads.c @@ -2,16 +2,19 @@ #include "common.h" -extern void *aclk_starter(void *ptr); -extern void *analytics_main(void *ptr); -extern void *checks_main(void *ptr); -extern void *cpuidlejitter_main(void *ptr); -extern void *global_statistics_main(void *ptr); -extern void *health_main(void *ptr); -extern void *pluginsd_main(void *ptr); -extern void *service_main(void *ptr); -extern void *statsd_main(void *ptr); -extern void *timex_main(void *ptr); +void *aclk_main(void *ptr); +void *analytics_main(void *ptr); +void *cpuidlejitter_main(void *ptr); +void *global_statistics_main(void *ptr); +void *global_statistics_workers_main(void *ptr); +void *health_main(void *ptr); +void *pluginsd_main(void *ptr); +void *service_main(void *ptr); +void *statsd_main(void *ptr); +void *timex_main(void *ptr); +void *replication_thread_main(void *ptr __maybe_unused); + +extern bool global_statistics_enabled; const struct netdata_static_thread static_threads_common[] = { { @@ -23,15 +26,6 @@ const struct netdata_static_thread static_threads_common[] = { .init_routine = NULL, .start_routine = timex_main }, - { - .name = "PLUGIN[check]", - .config_section = CONFIG_SECTION_PLUGINS, - .config_name = "checks", - .enabled = 0, - .thread = NULL, - .init_routine = NULL, - .start_routine = checks_main - }, { .name = "PLUGIN[idlejitter]", .config_section = CONFIG_SECTION_PLUGINS, @@ -52,21 +46,25 @@ const struct netdata_static_thread static_threads_common[] = { }, { .name = "GLOBAL_STATS", - .config_section = NULL, - .config_name = NULL, + .config_section = CONFIG_SECTION_PLUGINS, + .config_name = "netdata monitoring", + .env_name = "NETDATA_INTERNALS_MONITORING", + .global_variable = &global_statistics_enabled, .enabled = 1, .thread = NULL, .init_routine = NULL, .start_routine = global_statistics_main }, { - .name = "HEALTH", - .config_section = NULL, - .config_name = NULL, + .name = "WORKERS_STATS", + .config_section = CONFIG_SECTION_PLUGINS, + .config_name = "netdata monitoring", + .env_name = "NETDATA_INTERNALS_MONITORING", + .global_variable = &global_statistics_enabled, .enabled = 1, .thread = NULL, .init_routine = NULL, - .start_routine = health_main + .start_routine = global_statistics_workers_main }, { .name = "PLUGINSD", @@ -131,12 +129,12 @@ const struct netdata_static_thread static_threads_common[] = { .enabled = 1, .thread = NULL, .init_routine = NULL, - .start_routine = aclk_starter + .start_routine = aclk_main }, #endif { - .name = "rrdcontext", + .name = "RRDCONTEXT", .config_section = NULL, .config_name = NULL, .enabled = 1, @@ -145,7 +143,27 @@ const struct netdata_static_thread static_threads_common[] = { .start_routine = rrdcontext_main }, - {NULL, NULL, NULL, 0, NULL, NULL, NULL} + { + .name = "REPLICATION", + .config_section = NULL, + .config_name = NULL, + .enabled = 1, + .thread = NULL, + .init_routine = NULL, + .start_routine = replication_thread_main + }, + + // terminator + { + .name = NULL, + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 0, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + } }; struct netdata_static_thread * diff --git a/daemon/static_threads.h b/daemon/static_threads.h index dac615e76..9597da704 100644 --- a/daemon/static_threads.h +++ b/daemon/static_threads.h @@ -26,6 +26,12 @@ struct netdata_static_thread { // the threaded worker void *(*start_routine) (void *); + + // the environment variable to create + char *env_name; + + // global variable + bool *global_variable; }; #define NETDATA_MAIN_THREAD_RUNNING CONFIG_BOOLEAN_YES diff --git a/daemon/static_threads_linux.c b/daemon/static_threads_linux.c index 5f7a67768..260b2c176 100644 --- a/daemon/static_threads_linux.c +++ b/daemon/static_threads_linux.c @@ -46,15 +46,45 @@ const struct netdata_static_thread static_threads_linux[] = { .start_routine = cgroups_main }, - {NULL, NULL, NULL, 0, NULL, NULL, NULL} + // terminator + { + .name = NULL, + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 0, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + } }; const struct netdata_static_thread static_threads_freebsd[] = { - {NULL, NULL, NULL, 0, NULL, NULL, NULL} + // terminator + { + .name = NULL, + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 0, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + } }; const struct netdata_static_thread static_threads_macos[] = { - {NULL, NULL, NULL, 0, NULL, NULL, NULL} + // terminator + { + .name = NULL, + .config_section = NULL, + .config_name = NULL, + .env_name = NULL, + .enabled = 0, + .thread = NULL, + .init_routine = NULL, + .start_routine = NULL + } }; struct netdata_static_thread *static_threads_get() { diff --git a/daemon/static_threads_macos.c b/daemon/static_threads_macos.c index ae34a1363..72c032454 100644 --- a/daemon/static_threads_macos.c +++ b/daemon/static_threads_macos.c @@ -12,18 +12,20 @@ const struct netdata_static_thread static_threads_macos[] = { .enabled = 1, .thread = NULL, .init_routine = NULL, - .start_routine = macos_main + .start_routine = macos_main, + .env_name = NULL, + .global_variable = NULL, }, - {NULL, NULL, NULL, 0, NULL, NULL, NULL} + {NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL} }; const struct netdata_static_thread static_threads_freebsd[] = { - {NULL, NULL, NULL, 0, NULL, NULL, NULL} + {NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL} }; const struct netdata_static_thread static_threads_linux[] = { - {NULL, NULL, NULL, 0, NULL, NULL, NULL} + {NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL} }; struct netdata_static_thread *static_threads_get() { diff --git a/daemon/system-info.sh b/daemon/system-info.sh index 101ccb0bf..68cdc4812 100755 --- a/daemon/system-info.sh +++ b/daemon/system-info.sh @@ -33,7 +33,8 @@ if [ -z "${VIRTUALIZATION}" ]; then if command -v systemd-detect-virt >/dev/null 2>&1; then VIRTUALIZATION="$(systemd-detect-virt -v)" VIRT_DETECTION="systemd-detect-virt" - CONTAINER=${CONTAINER:-$(systemd-detect-virt -c)} + CONTAINER_DETECT_TMP="$(systemd-detect-virt -c)" + [ -n "$CONTAINER_DETECT_TMP" ] && CONTAINER="$CONTAINER_DETECT_TMP" CONT_DETECTION="systemd-detect-virt" elif command -v lscpu >/dev/null 2>&1; then VIRTUALIZATION=$(lscpu | grep "Hypervisor vendor:" | cut -d: -f 2 | awk '{$1=$1};1') diff --git a/daemon/unit_test.c b/daemon/unit_test.c index 8ba251b9a..f69861869 100644 --- a/daemon/unit_test.c +++ b/daemon/unit_test.c @@ -2,6 +2,61 @@ #include "common.h" +static bool cmd_arg_sanitization_test(const char *expected, const char *src, char *dst, size_t dst_size) { + bool ok = sanitize_command_argument_string(dst, src, dst_size); + + if (!expected) + return ok == false; + + return strcmp(expected, dst) == 0; +} + +bool command_argument_sanitization_tests() { + char dst[1024]; + + for (size_t i = 0; i != 5; i++) { + const char *expected = i == 4 ? "'\\''" : NULL; + if (cmd_arg_sanitization_test(expected, "'", dst, i) == false) { + fprintf(stderr, "expected: >>>%s<<<, got: >>>%s<<<\n", expected, dst); + return 1; + } + } + + for (size_t i = 0; i != 9; i++) { + const char *expected = i == 8 ? "'\\'''\\''" : NULL; + if (cmd_arg_sanitization_test(expected, "''", dst, i) == false) { + fprintf(stderr, "expected: >>>%s<<<, got: >>>%s<<<\n", expected, dst); + return 1; + } + } + + for (size_t i = 0; i != 7; i++) { + const char *expected = i == 6 ? "'\\''a" : NULL; + if (cmd_arg_sanitization_test(expected, "'a", dst, i) == false) { + fprintf(stderr, "expected: >>>%s<<<, got: >>>%s<<<\n", expected, dst); + return 1; + } + } + + for (size_t i = 0; i != 7; i++) { + const char *expected = i == 6 ? "a'\\''" : NULL; + if (cmd_arg_sanitization_test(expected, "a'", dst, i) == false) { + fprintf(stderr, "expected: >>>%s<<<, got: >>>%s<<<\n", expected, dst); + return 1; + } + } + + for (size_t i = 0; i != 22; i++) { + const char *expected = i == 21 ? "foo'\\''a'\\'''\\'''\\''b" : NULL; + if (cmd_arg_sanitization_test(expected, "--foo'a'''b", dst, i) == false) { + fprintf(stderr, "expected: >>>%s<<<, got: >>>%s<<<\n length: %zu\n", expected, dst, strlen(dst)); + return 1; + } + } + + return 0; +} + static int check_number_printing(void) { struct { NETDATA_DOUBLE n; @@ -1197,16 +1252,18 @@ int run_test(struct test *test) fprintf(stderr, " > %s: feeding position %lu\n", test->name, c+1); } - fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd->name, test->feed[c].value); + fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rrddim_name(rd), test->feed[c].value); rrddim_set(st, "dim1", test->feed[c].value); last = test->feed[c].value; if(rd2) { - fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd2->name, test->feed2[c]); + fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rrddim_name(rd2), test->feed2[c]); rrddim_set(st, "dim2", test->feed2[c]); } - rrdset_done(st); + struct timeval now; + now_realtime_timeval(&now); + rrdset_timed_done(st, now, false); // align the first entry to second boundary if(!c) { @@ -1231,7 +1288,7 @@ int run_test(struct test *test) int same = (roundndd(v * 10000000.0) == roundndd(n * 10000000.0))?1:0; fprintf(stderr, " %s/%s: checking position %lu (at %"PRId64" secs), expecting value " NETDATA_DOUBLE_FORMAT ", found " NETDATA_DOUBLE_FORMAT ", %s\n", - test->name, rd->name, c+1, + test->name, rrddim_name(rd), c+1, (int64_t)((rrdset_first_entry_t(st) + c * st->update_every) - time_start), n, v, (same)?"OK":"### E R R O R ###"); @@ -1243,7 +1300,7 @@ int run_test(struct test *test) same = (roundndd(v * 10000000.0) == roundndd(n * 10000000.0))?1:0; fprintf(stderr, " %s/%s: checking position %lu (at %"PRId64" secs), expecting value " NETDATA_DOUBLE_FORMAT ", found " NETDATA_DOUBLE_FORMAT ", %s\n", - test->name, rd2->name, c+1, + test->name, rrddim_name(rd2), c+1, (int64_t)((rrdset_first_entry_t(st) + c * st->update_every) - time_start), n, v, (same)?"OK":"### E R R O R ###"); if(!same) errors++; @@ -1258,39 +1315,39 @@ static int test_variable_renames(void) { fprintf(stderr, "Creating chart\n"); RRDSET *st = rrdset_create_localhost("chart", "ID", NULL, "family", "context", "Unit Testing", "a value", "unittest", NULL, 1, 1, RRDSET_TYPE_LINE); - fprintf(stderr, "Created chart with id '%s', name '%s'\n", st->id, st->name); + fprintf(stderr, "Created chart with id '%s', name '%s'\n", rrdset_id(st), rrdset_name(st)); fprintf(stderr, "Creating dimension DIM1\n"); RRDDIM *rd1 = rrddim_add(st, "DIM1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd1->id, rd1->name); + fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rrddim_id(rd1), rrddim_name(rd1)); fprintf(stderr, "Creating dimension DIM2\n"); RRDDIM *rd2 = rrddim_add(st, "DIM2", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd2->id, rd2->name); + fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rrddim_id(rd2), rrddim_name(rd2)); fprintf(stderr, "Renaming chart to CHARTNAME1\n"); - rrdset_set_name(st, "CHARTNAME1"); - fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", st->id, st->name); + rrdset_reset_name(st, "CHARTNAME1"); + fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", rrdset_id(st), rrdset_name(st)); fprintf(stderr, "Renaming chart to CHARTNAME2\n"); - rrdset_set_name(st, "CHARTNAME2"); - fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", st->id, st->name); + rrdset_reset_name(st, "CHARTNAME2"); + fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", rrdset_id(st), rrdset_name(st)); fprintf(stderr, "Renaming dimension DIM1 to DIM1NAME1\n"); - rrddim_set_name(st, rd1, "DIM1NAME1"); - fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd1->id, rd1->name); + rrddim_reset_name(st, rd1, "DIM1NAME1"); + fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rrddim_id(rd1), rrddim_name(rd1)); fprintf(stderr, "Renaming dimension DIM1 to DIM1NAME2\n"); - rrddim_set_name(st, rd1, "DIM1NAME2"); - fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd1->id, rd1->name); + rrddim_reset_name(st, rd1, "DIM1NAME2"); + fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rrddim_id(rd1), rrddim_name(rd1)); fprintf(stderr, "Renaming dimension DIM2 to DIM2NAME1\n"); - rrddim_set_name(st, rd2, "DIM2NAME1"); - fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd2->id, rd2->name); + rrddim_reset_name(st, rd2, "DIM2NAME1"); + fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rrddim_id(rd2), rrddim_name(rd2)); fprintf(stderr, "Renaming dimension DIM2 to DIM2NAME2\n"); - rrddim_set_name(st, rd2, "DIM2NAME2"); - fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd2->id, rd2->name); + rrddim_reset_name(st, rd2, "DIM2NAME2"); + fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rrddim_id(rd2), rrddim_name(rd2)); BUFFER *buf = buffer_create(1); health_api_v1_chart_variables2json(st, buf); @@ -1447,9 +1504,8 @@ int unit_test(long delay, long shift) long increment = 1000; collected_number i = 0; - unsigned long c, dimensions = 0; + unsigned long c, dimensions = rrdset_number_of_dimensions(st); RRDDIM *rd; - for(rd = st->dimensions ; rd ; rd = rd->next) dimensions++; for(c = 0; c < 20 ;c++) { i += increment; @@ -1470,8 +1526,10 @@ int unit_test(long delay, long shift) } // prevent it from deleting the dimensions - for(rd = st->dimensions ; rd ; rd = rd->next) + rrddim_foreach_read(rd, st) { rd->last_collected_time.tv_sec = st->last_collected_time.tv_sec; + } + rrddim_foreach_done(rd); rrdset_done(st); } @@ -1486,10 +1544,10 @@ int unit_test(long delay, long shift) for(c = 0 ; c < st->counter ; c++) { fprintf(stderr, "\nPOSITION: c = %lu, EXPECTED VALUE %lu\n", c, (oincrement + c * increment + increment * (1000000 - shift) / 1000000 )* 10); - for(rd = st->dimensions ; rd ; rd = rd->next) { + rrddim_foreach_read(rd, st) { sn = rd->db[c]; cn = unpack_storage_number(sn); - fprintf(stderr, "\t %s " NETDATA_DOUBLE_FORMAT " (PACKED AS " STORAGE_NUMBER_FORMAT ") -> ", rd->id, cn, sn); + fprintf(stderr, "\t %s " NETDATA_DOUBLE_FORMAT " (PACKED AS " STORAGE_NUMBER_FORMAT ") -> ", rrddim_id(rd), cn, sn); if(rd == rdabs) v = ( oincrement @@ -1508,6 +1566,7 @@ int unit_test(long delay, long shift) ret = 1; } } + rrddim_foreach_done(rd); } if(ret) @@ -1527,19 +1586,19 @@ int test_sqlite(void) { return 1; } - rc = sqlite3_exec(db_meta, "CREATE TABLE IF NOT EXISTS mine (id1, id2);", 0, 0, NULL); + rc = sqlite3_exec_monitored(db_meta, "CREATE TABLE IF NOT EXISTS mine (id1, id2);", 0, 0, NULL); if (rc != SQLITE_OK) { fprintf(stderr,"Failed to test SQLite: Create table failed\n"); return 1; } - rc = sqlite3_exec(db_meta, "DELETE FROM MINE LIMIT 1;", 0, 0, NULL); + rc = sqlite3_exec_monitored(db_meta, "DELETE FROM MINE LIMIT 1;", 0, 0, NULL); if (rc != SQLITE_OK) { fprintf(stderr,"Failed to test SQLite: Delete with LIMIT failed\n"); return 1; } - rc = sqlite3_exec(db_meta, "UPDATE MINE SET id1=1 LIMIT 1;", 0, 0, NULL); + rc = sqlite3_exec_monitored(db_meta, "UPDATE MINE SET id1=1 LIMIT 1;", 0, 0, NULL); if (rc != SQLITE_OK) { fprintf(stderr,"Failed to test SQLite: Update with LIMIT failed\n"); return 1; @@ -1548,58 +1607,24 @@ int test_sqlite(void) { BUFFER *sql = buffer_create(ACLK_SYNC_QUERY_SIZE); char *uuid_str = "0000_000"; - buffer_sprintf(sql, TABLE_ACLK_CHART, uuid_str); - rc = sqlite3_exec(db_meta, buffer_tostring(sql), 0, 0, NULL); - buffer_flush(sql); - if (rc != SQLITE_OK) - goto error; - - buffer_sprintf(sql, TABLE_ACLK_CHART_PAYLOAD, uuid_str); - rc = sqlite3_exec(db_meta, buffer_tostring(sql), 0, 0, NULL); - buffer_flush(sql); - if (rc != SQLITE_OK) - goto error; - - buffer_sprintf(sql, TABLE_ACLK_CHART_LATEST, uuid_str); - rc = sqlite3_exec(db_meta, buffer_tostring(sql), 0, 0, NULL); - if (rc != SQLITE_OK) - goto error; - buffer_flush(sql); - - buffer_sprintf(sql, INDEX_ACLK_CHART, uuid_str, uuid_str); - rc = sqlite3_exec(db_meta, buffer_tostring(sql), 0, 0, NULL); - if (rc != SQLITE_OK) - goto error; - buffer_flush(sql); - - buffer_sprintf(sql, INDEX_ACLK_CHART_LATEST, uuid_str, uuid_str); - rc = sqlite3_exec(db_meta, buffer_tostring(sql), 0, 0, NULL); - if (rc != SQLITE_OK) - goto error; - buffer_flush(sql); - - buffer_sprintf(sql, TRIGGER_ACLK_CHART_PAYLOAD, uuid_str, uuid_str, uuid_str); - rc = sqlite3_exec(db_meta, buffer_tostring(sql), 0, 0, NULL); - if (rc != SQLITE_OK) - goto error; - buffer_flush(sql); - buffer_sprintf(sql, TABLE_ACLK_ALERT, uuid_str); - rc = sqlite3_exec(db_meta, buffer_tostring(sql), 0, 0, NULL); + rc = sqlite3_exec_monitored(db_meta, buffer_tostring(sql), 0, 0, NULL); if (rc != SQLITE_OK) goto error; buffer_flush(sql); buffer_sprintf(sql, INDEX_ACLK_ALERT, uuid_str, uuid_str); - rc = sqlite3_exec(db_meta, buffer_tostring(sql), 0, 0, NULL); + rc = sqlite3_exec_monitored(db_meta, buffer_tostring(sql), 0, 0, NULL); if (rc != SQLITE_OK) goto error; buffer_flush(sql); buffer_free(sql); fprintf(stderr,"SQLite is OK\n"); + rc = sqlite3_close_v2(db_meta); return 0; error: + rc = sqlite3_close_v2(db_meta); fprintf(stderr,"SQLite statement failed: %s\n", buffer_tostring(sql)); buffer_free(sql); fprintf(stderr,"SQLite tests failed\n"); @@ -1634,28 +1659,28 @@ int unit_test_bitmap256(void) { if (test_bitmap.data[0] == 0xffffffffffffffff) fprintf(stderr, "%s() INDEX 0 is fully set OK\n", __FUNCTION__); else { - fprintf(stderr, "%s() INDEX 0 is %lx expected 0xffffffffffffffff\n", __FUNCTION__, test_bitmap.data[0]); + fprintf(stderr, "%s() INDEX 0 is %"PRIu64" expected 0xffffffffffffffff\n", __FUNCTION__, test_bitmap.data[0]); return 1; } if (test_bitmap.data[1] == 0xffffffffffffffff) fprintf(stderr, "%s() INDEX 1 is fully set OK\n", __FUNCTION__); else { - fprintf(stderr, "%s() INDEX 1 is %lx expected 0xffffffffffffffff\n", __FUNCTION__, test_bitmap.data[0]); + fprintf(stderr, "%s() INDEX 1 is %"PRIu64" expected 0xffffffffffffffff\n", __FUNCTION__, test_bitmap.data[0]); return 1; } if (test_bitmap.data[2] == 0xffffffffffffffff) fprintf(stderr, "%s() INDEX 2 is fully set OK\n", __FUNCTION__); else { - fprintf(stderr, "%s() INDEX 2 is %lx expected 0xffffffffffffffff\n", __FUNCTION__, test_bitmap.data[0]); + fprintf(stderr, "%s() INDEX 2 is %"PRIu64" expected 0xffffffffffffffff\n", __FUNCTION__, test_bitmap.data[0]); return 1; } if (test_bitmap.data[3] == 0xffffffffffffffff) fprintf(stderr, "%s() INDEX 3 is fully set OK\n", __FUNCTION__); else { - fprintf(stderr, "%s() INDEX 3 is %lx expected 0xffffffffffffffff\n", __FUNCTION__, test_bitmap.data[0]); + fprintf(stderr, "%s() INDEX 3 is %"PRIu64" expected 0xffffffffffffffff\n", __FUNCTION__, test_bitmap.data[0]); return 1; } @@ -1704,28 +1729,28 @@ int unit_test_bitmap256(void) { if (test_bitmap.data[0] == 0x1111111111111111) fprintf(stderr, "%s() INDEX 0 is 0x1111111111111111 set OK\n", __FUNCTION__); else { - fprintf(stderr, "%s() INDEX 0 is %lx expected 0x1111111111111111\n", __FUNCTION__, test_bitmap.data[0]); + fprintf(stderr, "%s() INDEX 0 is %"PRIu64" expected 0x1111111111111111\n", __FUNCTION__, test_bitmap.data[0]); return 1; } if (test_bitmap.data[1] == 0x1111111111111111) fprintf(stderr, "%s() INDEX 1 is 0x1111111111111111 set OK\n", __FUNCTION__); else { - fprintf(stderr, "%s() INDEX 1 is %lx expected 0x1111111111111111\n", __FUNCTION__, test_bitmap.data[1]); + fprintf(stderr, "%s() INDEX 1 is %"PRIu64" expected 0x1111111111111111\n", __FUNCTION__, test_bitmap.data[1]); return 1; } if (test_bitmap.data[2] == 0x1111111111111111) fprintf(stderr, "%s() INDEX 2 is 0x1111111111111111 set OK\n", __FUNCTION__); else { - fprintf(stderr, "%s() INDEX 2 is %lx expected 0x1111111111111111\n", __FUNCTION__, test_bitmap.data[2]); + fprintf(stderr, "%s() INDEX 2 is %"PRIu64" expected 0x1111111111111111\n", __FUNCTION__, test_bitmap.data[2]); return 1; } if (test_bitmap.data[3] == 0x1111111111111111) fprintf(stderr, "%s() INDEX 3 is 0x1111111111111111 set OK\n", __FUNCTION__); else { - fprintf(stderr, "%s() INDEX 3 is %lx expected 0x1111111111111111\n", __FUNCTION__, test_bitmap.data[3]); + fprintf(stderr, "%s() INDEX 3 is %"PRIu64" expected 0x1111111111111111\n", __FUNCTION__, test_bitmap.data[3]); return 1; } @@ -1771,6 +1796,9 @@ static RRDHOST *dbengine_rrdhost_find_or_create(char *name) , default_rrdpush_destination , default_rrdpush_api_key , default_rrdpush_send_charts_matching + , default_rrdpush_enable_replication + , default_rrdpush_seconds_to_replicate + , default_rrdpush_replication_step , NULL , 0 ); @@ -1826,7 +1854,10 @@ static void test_dbengine_create_charts(RRDHOST *host, RRDSET *st[CHARTS], RRDDI for (j = 0; j < DIMS; ++j) { rrddim_set_by_pointer_fake_time(rd[i][j], 69, 2 * API_RELATIVE_TIME_MAX); // set first value to 69 } - rrdset_done(st[i]); + + struct timeval now; + now_realtime_timeval(&now); + rrdset_timed_done(st[i], now, false); } // Fluh pages for subsequent real values for (i = 0 ; i < CHARTS ; ++i) { @@ -1850,6 +1881,8 @@ static time_t test_dbengine_create_metrics(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS // feed it with the test data for (i = 0 ; i < CHARTS ; ++i) { for (j = 0 ; j < DIMS ; ++j) { + rd[i][j]->tiers[0]->collect_ops->change_collection_frequency(rd[i][j]->tiers[0]->db_collection_handle, update_every); + rd[i][j]->last_collected_time.tv_sec = st[i]->last_collected_time.tv_sec = st[i]->last_updated.tv_sec = time_now; rd[i][j]->last_collected_time.tv_usec = @@ -1858,7 +1891,8 @@ static time_t test_dbengine_create_metrics(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS } for (c = 0; c < REGION_POINTS[current_region] ; ++c) { time_now += update_every; // time_now = start + (c + 1) * update_every - for (i = 0 ; i < CHARTS ; ++i) { + + for (i = 0 ; i < CHARTS ; ++i) { st[i]->usec_since_last_update = USEC_PER_SEC * update_every; for (j = 0; j < DIMS; ++j) { @@ -1866,7 +1900,12 @@ static time_t test_dbengine_create_metrics(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS j * REGION_POINTS[current_region] + c; rrddim_set_by_pointer_fake_time(rd[i][j], next, time_now); } - rrdset_done(st[i]); + + struct timeval now; + now.tv_sec = time_now; + now.tv_usec = 0; + + rrdset_timed_done(st[i], now, false); } } return time_now; //time_end @@ -1882,7 +1921,7 @@ static int test_dbengine_check_metrics(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DI int i, j, k, c, errors, update_every; collected_number last; NETDATA_DOUBLE value, expected; - struct rrddim_query_handle handle; + struct storage_engine_query_handle handle; size_t value_errors = 0, time_errors = 0; update_every = REGION_UPDATE_EVERY[current_region]; @@ -1893,13 +1932,13 @@ static int test_dbengine_check_metrics(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DI time_now = time_start + (c + 1) * update_every; for (i = 0 ; i < CHARTS ; ++i) { for (j = 0; j < DIMS; ++j) { - rd[i][j]->tiers[0]->query_ops.init(rd[i][j]->tiers[0]->db_metric_handle, &handle, time_now, time_now + QUERY_BATCH * update_every, TIER_QUERY_FETCH_SUM); + rd[i][j]->tiers[0]->query_ops->init(rd[i][j]->tiers[0]->db_metric_handle, &handle, time_now, time_now + QUERY_BATCH * update_every); for (k = 0; k < QUERY_BATCH; ++k) { last = ((collected_number)i * DIMS) * REGION_POINTS[current_region] + j * REGION_POINTS[current_region] + c + k; expected = unpack_storage_number(pack_storage_number((NETDATA_DOUBLE)last, SN_DEFAULT_FLAGS)); - STORAGE_POINT sp = rd[i][j]->tiers[0]->query_ops.next_metric(&handle); + STORAGE_POINT sp = rd[i][j]->tiers[0]->query_ops->next_metric(&handle); value = sp.sum; time_retrieved = sp.start_time; end_time = sp.end_time; @@ -1909,19 +1948,19 @@ static int test_dbengine_check_metrics(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DI if(!value_errors) fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, expecting value " NETDATA_DOUBLE_FORMAT ", found " NETDATA_DOUBLE_FORMAT ", ### E R R O R ###\n", - st[i]->name, rd[i][j]->name, (unsigned long)time_now + k * update_every, expected, value); + rrdset_name(st[i]), rrddim_name(rd[i][j]), (unsigned long)time_now + k * update_every, expected, value); value_errors++; errors++; } if(end_time != time_now + k * update_every) { if(!time_errors) fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, found timestamp %lu ### E R R O R ###\n", - st[i]->name, rd[i][j]->name, (unsigned long)time_now + k * update_every, (unsigned long)time_retrieved); + rrdset_name(st[i]), rrddim_name(rd[i][j]), (unsigned long)time_now + k * update_every, (unsigned long)time_retrieved); time_errors++; errors++; } } - rd[i][j]->tiers[0]->query_ops.finalize(&handle); + rd[i][j]->tiers[0]->query_ops->finalize(&handle); } } } @@ -1940,7 +1979,7 @@ static int test_dbengine_check_rrdr(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS] int current_region, time_t time_start, time_t time_end) { int update_every = REGION_UPDATE_EVERY[current_region]; - fprintf(stderr, "%s() running on region %d, start time %ld, end time %ld, update every %d...\n", __FUNCTION__, current_region, time_start, time_end, update_every); + fprintf(stderr, "%s() running on region %d, start time %lld, end time %lld, update every %d...\n", __FUNCTION__, current_region, (long long)time_start, (long long)time_end, update_every); uint8_t same; time_t time_now, time_retrieved; int i, j, errors, value_errors = 0, time_errors = 0; @@ -1952,22 +1991,25 @@ static int test_dbengine_check_rrdr(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS] long points = (time_end - time_start) / update_every; for (i = 0 ; i < CHARTS ; ++i) { ONEWAYALLOC *owa = onewayalloc_create(0); - RRDR *r = rrd2rrdr(owa, st[i], points, time_start, time_end, - RRDR_GROUPING_AVERAGE, 0, RRDR_OPTION_NATURAL_POINTS, - NULL, NULL, NULL, 0, 0); - + RRDR *r = rrd2rrdr_legacy(owa, st[i], points, time_start, time_end, + RRDR_GROUPING_AVERAGE, 0, RRDR_OPTION_NATURAL_POINTS, + NULL, NULL, 0, 0, QUERY_SOURCE_UNITTEST); if (!r) { - fprintf(stderr, " DB-engine unittest %s: empty RRDR on region %d ### E R R O R ###\n", st[i]->name, current_region); + fprintf(stderr, " DB-engine unittest %s: empty RRDR on region %d ### E R R O R ###\n", rrdset_name(st[i]), current_region); return ++errors; } else { - assert(r->st == st[i]); - for (c = 0; c != rrdr_rows(r) ; ++c) { + assert(r->internal.qt->request.st == st[i]); + for (c = 0; c != (long)rrdr_rows(r) ; ++c) { RRDDIM *d; time_now = time_start + (c + 1) * update_every; time_retrieved = r->t[c]; // for each dimension - for (j = 0, d = r->st->dimensions ; d && j < r->d ; ++j, d = d->next) { + rrddim_foreach_read(d, r->internal.qt->request.st) { + if(unlikely(d_dfe.counter >= r->d)) break; // d_counter is provided by the dictionary dfe + + j = (int)d_dfe.counter; + NETDATA_DOUBLE *cn = &r->v[ c * r->d ]; value = cn[j]; assert(rd[i][j] == d); @@ -1980,16 +2022,17 @@ static int test_dbengine_check_rrdr(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS] if(value_errors < 20) fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, expecting value " NETDATA_DOUBLE_FORMAT ", RRDR found " NETDATA_DOUBLE_FORMAT ", ### E R R O R ###\n", - st[i]->name, rd[i][j]->name, (unsigned long)time_now, expected, value); + rrdset_name(st[i]), rrddim_name(rd[i][j]), (unsigned long)time_now, expected, value); value_errors++; } if(time_retrieved != time_now) { if(time_errors < 20) fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, found RRDR timestamp %lu ### E R R O R ###\n", - st[i]->name, rd[i][j]->name, (unsigned long)time_now, (unsigned long)time_retrieved); + rrdset_name(st[i]), rrddim_name(rd[i][j]), (unsigned long)time_now, (unsigned long)time_retrieved); time_errors++; } } + rrddim_foreach_done(d); } rrdr_free(owa, r); } @@ -2086,24 +2129,29 @@ int test_dbengine(void) long point_offset = (time_start[current_region] - time_start[0]) / update_every; for (i = 0 ; i < CHARTS ; ++i) { ONEWAYALLOC *owa = onewayalloc_create(0); - RRDR *r = rrd2rrdr(owa, st[i], points, time_start[0] + update_every, - time_end[REGIONS - 1], RRDR_GROUPING_AVERAGE, 0, - RRDR_OPTION_NATURAL_POINTS, NULL, NULL, NULL, 0, 0); + RRDR *r = rrd2rrdr_legacy(owa, st[i], points, time_start[0] + update_every, + time_end[REGIONS - 1], RRDR_GROUPING_AVERAGE, 0, + RRDR_OPTION_NATURAL_POINTS, NULL, NULL, 0, 0, QUERY_SOURCE_UNITTEST); + if (!r) { - fprintf(stderr, " DB-engine unittest %s: empty RRDR ### E R R O R ###\n", st[i]->name); + fprintf(stderr, " DB-engine unittest %s: empty RRDR ### E R R O R ###\n", rrdset_name(st[i])); ++errors; } else { long c; - assert(r->st == st[i]); + assert(r->internal.qt->request.st == st[i]); // test current region values only, since they must be left unchanged - for (c = point_offset ; c < point_offset + rrdr_rows(r) / REGIONS / 2 ; ++c) { + for (c = point_offset ; c < (long)(point_offset + rrdr_rows(r) / REGIONS / 2) ; ++c) { RRDDIM *d; time_t time_now = time_start[current_region] + (c - point_offset + 2) * update_every; time_t time_retrieved = r->t[c]; // for each dimension - for(j = 0, d = r->st->dimensions ; d && j < r->d ; ++j, d = d->next) { + rrddim_foreach_read(d, r->internal.qt->request.st) { + if(unlikely(d_dfe.counter >= r->d)) break; // d_counter is provided by the dictionary dfe + + j = (int)d_dfe.counter; + NETDATA_DOUBLE *cn = &r->v[ c * r->d ]; NETDATA_DOUBLE value = cn[j]; assert(rd[i][j] == d); @@ -2116,16 +2164,17 @@ int test_dbengine(void) if(!value_errors) fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, expecting value " NETDATA_DOUBLE_FORMAT ", RRDR found " NETDATA_DOUBLE_FORMAT ", ### E R R O R ###\n", - st[i]->name, rd[i][j]->name, (unsigned long)time_now, expected, value); + rrdset_name(st[i]), rrddim_name(rd[i][j]), (unsigned long)time_now, expected, value); value_errors++; } if(time_retrieved != time_now) { if(!time_errors) fprintf(stderr, " DB-engine unittest %s/%s: at %lu secs, found RRDR timestamp %lu ### E R R O R ###\n", - st[i]->name, rd[i][j]->name, (unsigned long)time_now, (unsigned long)time_retrieved); + rrdset_name(st[i]), rrddim_name(rd[i][j]), (unsigned long)time_now, (unsigned long)time_retrieved); time_errors++; } } + rrddim_foreach_done(d); } rrdr_free(owa, r); } @@ -2133,9 +2182,9 @@ int test_dbengine(void) } error_out: rrd_wrlock(); - rrdeng_prepare_exit((struct rrdengine_instance *)host->storage_instance[0]); + rrdeng_prepare_exit((struct rrdengine_instance *)host->db[0].instance); rrdhost_delete_charts(host); - rrdeng_exit((struct rrdengine_instance *)host->storage_instance[0]); + rrdeng_exit((struct rrdengine_instance *)host->db[0].instance); rrd_unlock(); return errors + value_errors + time_errors; @@ -2313,7 +2362,7 @@ static void query_dbengine_chart(void *arg) time_t time_now, time_retrieved, end_time; collected_number generatedv; NETDATA_DOUBLE value, expected; - struct rrddim_query_handle handle; + struct storage_engine_query_handle handle; size_t value_errors = 0, time_errors = 0; do { @@ -2340,23 +2389,23 @@ static void query_dbengine_chart(void *arg) time_before = MIN(time_after + duration, time_max); /* up to 1 hour queries */ } - rd->tiers[0]->query_ops.init(rd->tiers[0]->db_metric_handle, &handle, time_after, time_before, TIER_QUERY_FETCH_SUM); + rd->tiers[0]->query_ops->init(rd->tiers[0]->db_metric_handle, &handle, time_after, time_before); ++thread_info->queries_nr; for (time_now = time_after ; time_now <= time_before ; time_now += update_every) { generatedv = generate_dbengine_chart_value(i, j, time_now); expected = unpack_storage_number(pack_storage_number((NETDATA_DOUBLE) generatedv, SN_DEFAULT_FLAGS)); - if (unlikely(rd->tiers[0]->query_ops.is_finished(&handle))) { + if (unlikely(rd->tiers[0]->query_ops->is_finished(&handle))) { if (!thread_info->delete_old_data) { /* data validation only when we don't delete */ fprintf(stderr, " DB-engine stresstest %s/%s: at %lu secs, expecting value " NETDATA_DOUBLE_FORMAT ", found data gap, ### E R R O R ###\n", - st->name, rd->name, (unsigned long) time_now, expected); + rrdset_name(st), rrddim_name(rd), (unsigned long) time_now, expected); ++thread_info->errors; } break; } - STORAGE_POINT sp = rd->tiers[0]->query_ops.next_metric(&handle); + STORAGE_POINT sp = rd->tiers[0]->query_ops->next_metric(&handle); value = sp.sum; time_retrieved = sp.start_time; end_time = sp.end_time; @@ -2365,7 +2414,7 @@ static void query_dbengine_chart(void *arg) if (!thread_info->delete_old_data) { /* data validation only when we don't delete */ fprintf(stderr, " DB-engine stresstest %s/%s: at %lu secs, expecting value " NETDATA_DOUBLE_FORMAT ", found data gap, ### E R R O R ###\n", - st->name, rd->name, (unsigned long) time_now, expected); + rrdset_name(st), rrddim_name(rd), (unsigned long) time_now, expected); ++thread_info->errors; } break; @@ -2378,7 +2427,7 @@ static void query_dbengine_chart(void *arg) if(!value_errors) fprintf(stderr, " DB-engine stresstest %s/%s: at %lu secs, expecting value " NETDATA_DOUBLE_FORMAT ", found " NETDATA_DOUBLE_FORMAT ", ### E R R O R ###\n", - st->name, rd->name, (unsigned long) time_now, expected, value); + rrdset_name(st), rrddim_name(rd), (unsigned long) time_now, expected, value); value_errors++; thread_info->errors++; } @@ -2388,13 +2437,13 @@ static void query_dbengine_chart(void *arg) if(!time_errors) fprintf(stderr, " DB-engine stresstest %s/%s: at %lu secs, found timestamp %lu ### E R R O R ###\n", - st->name, rd->name, (unsigned long) time_now, (unsigned long) time_retrieved); + rrdset_name(st), rrddim_name(rd), (unsigned long) time_now, (unsigned long) time_retrieved); time_errors++; thread_info->errors++; } } } - rd->tiers[0]->query_ops.finalize(&handle); + rd->tiers[0]->query_ops->finalize(&handle); } while(!thread_info->done); if(value_errors) @@ -2515,7 +2564,7 @@ void dbengine_stress_test(unsigned TEST_DURATION_SEC, unsigned DSET_CHARTS, unsi test_duration = now_realtime_sec() - (time_start - HISTORY_SECONDS); if (!test_duration) test_duration = 1; - fprintf(stderr, "\nDB-engine stress test finished in %ld seconds.\n", test_duration); + fprintf(stderr, "\nDB-engine stress test finished in %lld seconds.\n", (long long)test_duration); unsigned long stored_metrics_nr = 0; for (i = 0 ; i < DSET_CHARTS ; ++i) { stored_metrics_nr += chart_threads[i]->stored_metrics_nr; @@ -2530,8 +2579,8 @@ void dbengine_stress_test(unsigned TEST_DURATION_SEC, unsigned DSET_CHARTS, unsi fprintf(stderr, "%lu metric data points were queried by %u reader threads.\n", queried_metrics_nr, QUERY_THREADS); fprintf(stderr, "Query starting time is randomly chosen from the beginning of the time-series up to the time of\n" "the latest data point, and ending time from 1 second up to 1 hour after the starting time.\n"); - fprintf(stderr, "Performance is %lu written data points/sec and %lu read data points/sec.\n", - stored_metrics_nr / test_duration, queried_metrics_nr / test_duration); + fprintf(stderr, "Performance is %lld written data points/sec and %lld read data points/sec.\n", + (long long)(stored_metrics_nr / test_duration), (long long)(queried_metrics_nr / test_duration)); for (i = 0 ; i < DSET_CHARTS ; ++i) { freez(chart_threads[i]); @@ -2542,9 +2591,9 @@ void dbengine_stress_test(unsigned TEST_DURATION_SEC, unsigned DSET_CHARTS, unsi } freez(query_threads); rrd_wrlock(); - rrdeng_prepare_exit((struct rrdengine_instance *)host->storage_instance[0]); + rrdeng_prepare_exit((struct rrdengine_instance *)host->db[0].instance); rrdhost_delete_charts(host); - rrdeng_exit((struct rrdengine_instance *)host->storage_instance[0]); + rrdeng_exit((struct rrdengine_instance *)host->db[0].instance); rrd_unlock(); } diff --git a/daemon/unit_test.h b/daemon/unit_test.h index 2d2533afe..f79bd5c40 100644 --- a/daemon/unit_test.h +++ b/daemon/unit_test.h @@ -3,20 +3,24 @@ #ifndef NETDATA_UNIT_TEST_H #define NETDATA_UNIT_TEST_H 1 -extern int unit_test_storage(void); -extern int unit_test(long delay, long shift); -extern int run_all_mockup_tests(void); -extern int unit_test_str2ld(void); -extern int unit_test_buffer(void); -extern int unit_test_static_threads(void); -extern int test_sqlite(void); -extern int unit_test_bitmap256(void); +#include "stdbool.h" + +int unit_test_storage(void); +int unit_test(long delay, long shift); +int run_all_mockup_tests(void); +int unit_test_str2ld(void); +int unit_test_buffer(void); +int unit_test_static_threads(void); +int test_sqlite(void); +int unit_test_bitmap256(void); #ifdef ENABLE_DBENGINE -extern int test_dbengine(void); -extern void generate_dbengine_dataset(unsigned history_seconds); -extern void dbengine_stress_test(unsigned TEST_DURATION_SEC, unsigned DSET_CHARTS, unsigned QUERY_THREADS, +int test_dbengine(void); +void generate_dbengine_dataset(unsigned history_seconds); +void dbengine_stress_test(unsigned TEST_DURATION_SEC, unsigned DSET_CHARTS, unsigned QUERY_THREADS, unsigned RAMP_UP_SECONDS, unsigned PAGE_CACHE_MB, unsigned DISK_SPACE_MB); #endif +bool command_argument_sanitization_tests(); + #endif /* NETDATA_UNIT_TEST_H */ diff --git a/database/engine/Makefile.am b/database/engine/Makefile.am index 43405001d..59250a997 100644 --- a/database/engine/Makefile.am +++ b/database/engine/Makefile.am @@ -4,7 +4,6 @@ AUTOMAKE_OPTIONS = subdir-objects MAINTAINERCLEANFILES = $(srcdir)/Makefile.in SUBDIRS = \ - metadata_log \ $(NULL) dist_noinst_DATA = \ diff --git a/database/engine/datafile.c b/database/engine/datafile.c index 2ed98ef88..9c70068d9 100644 --- a/database/engine/datafile.c +++ b/database/engine/datafile.c @@ -174,7 +174,7 @@ int create_data_file(struct rrdengine_datafile *datafile) rrd_stat_atomic_add(&global_io_errors, 1); } uv_fs_req_cleanup(&req); - free(superblock); + posix_memfree(superblock); if (ret < 0) { destroy_data_file(datafile); return ret; @@ -218,7 +218,7 @@ static int check_data_file_superblock(uv_file file) ret = 0; } error: - free(superblock); + posix_memfree(superblock); return ret; } @@ -444,44 +444,17 @@ void finalize_data_files(struct rrdengine_instance *ctx) struct rrdengine_journalfile *journalfile; struct extent_info *extent, *next_extent; - size_t extents_number = 0; - size_t extents_bytes = 0; - size_t page_compressed_sizes = 0; - - size_t files_number = 0; - size_t files_bytes = 0; - for (datafile = ctx->datafiles.first ; datafile != NULL ; datafile = next_datafile) { journalfile = datafile->journalfile; next_datafile = datafile->next; for (extent = datafile->extents.first ; extent != NULL ; extent = next_extent) { - extents_number++; - extents_bytes += sizeof(*extent) + sizeof(struct rrdeng_page_descr *) * extent->number_of_pages; - page_compressed_sizes += extent->size; - next_extent = extent->next; freez(extent); } close_journal_file(journalfile, datafile); close_data_file(datafile); - - files_number++; - files_bytes += sizeof(*journalfile) + sizeof(*datafile); - freez(journalfile); freez(datafile); } - - if(!files_number) files_number = 1; - if(!extents_number) extents_number = 1; - - info("DBENGINE STATISTICS ON DATAFILES:" - " Files %zu, structures %zu bytes, %0.2f bytes per file." - " Extents %zu, structures %zu bytes, %0.2f bytes per extent." - " Compressed size of all pages: %zu bytes." - , files_number, files_bytes, (double)files_bytes/files_number - , extents_number, extents_bytes, (double)extents_bytes/extents_number - , page_compressed_sizes - ); } diff --git a/database/engine/datafile.h b/database/engine/datafile.h index ae94bfdd0..1cf256aff 100644 --- a/database/engine/datafile.h +++ b/database/engine/datafile.h @@ -52,16 +52,16 @@ struct rrdengine_datafile_list { struct rrdengine_datafile *last; /* newest */ }; -extern void df_extent_insert(struct extent_info *extent); -extern void datafile_list_insert(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile); -extern void datafile_list_delete(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile); -extern void generate_datafilepath(struct rrdengine_datafile *datafile, char *str, size_t maxlen); -extern int close_data_file(struct rrdengine_datafile *datafile); -extern int unlink_data_file(struct rrdengine_datafile *datafile); -extern int destroy_data_file(struct rrdengine_datafile *datafile); -extern int create_data_file(struct rrdengine_datafile *datafile); -extern int create_new_datafile_pair(struct rrdengine_instance *ctx, unsigned tier, unsigned fileno); -extern int init_data_files(struct rrdengine_instance *ctx); -extern void finalize_data_files(struct rrdengine_instance *ctx); +void df_extent_insert(struct extent_info *extent); +void datafile_list_insert(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile); +void datafile_list_delete(struct rrdengine_instance *ctx, struct rrdengine_datafile *datafile); +void generate_datafilepath(struct rrdengine_datafile *datafile, char *str, size_t maxlen); +int close_data_file(struct rrdengine_datafile *datafile); +int unlink_data_file(struct rrdengine_datafile *datafile); +int destroy_data_file(struct rrdengine_datafile *datafile); +int create_data_file(struct rrdengine_datafile *datafile); +int create_new_datafile_pair(struct rrdengine_instance *ctx, unsigned tier, unsigned fileno); +int init_data_files(struct rrdengine_instance *ctx); +void finalize_data_files(struct rrdengine_instance *ctx); #endif /* NETDATA_DATAFILE_H */ \ No newline at end of file diff --git a/database/engine/journalfile.c b/database/engine/journalfile.c index dc61f569d..500dd7880 100644 --- a/database/engine/journalfile.c +++ b/database/engine/journalfile.c @@ -17,7 +17,7 @@ static void flush_transaction_buffer_cb(uv_fs_t* req) } uv_fs_req_cleanup(req); - free(io_descr->buf); + posix_memfree(io_descr->buf); freez(io_descr); } @@ -225,7 +225,7 @@ int create_journal_file(struct rrdengine_journalfile *journalfile, struct rrdeng rrd_stat_atomic_add(&global_io_errors, 1); } uv_fs_req_cleanup(&req); - free(superblock); + posix_memfree(superblock); if (ret < 0) { destroy_journal_file(journalfile, datafile); return ret; @@ -268,7 +268,7 @@ static int check_journal_file_superblock(uv_file file) ret = 0; } error: - free(superblock); + posix_memfree(superblock); return ret; } @@ -311,20 +311,46 @@ static void restore_extent_metadata(struct rrdengine_instance *ctx, struct rrden } continue; } - uint64_t start_time = jf_metric_data->descr[i].start_time; - uint64_t end_time = jf_metric_data->descr[i].end_time; + uint64_t start_time_ut = jf_metric_data->descr[i].start_time_ut; + uint64_t end_time_ut = jf_metric_data->descr[i].end_time_ut; + size_t entries = jf_metric_data->descr[i].page_length / page_type_size[page_type]; + time_t update_every_s = (entries > 1) ? ((end_time_ut - start_time_ut) / USEC_PER_SEC / (entries - 1)) : 0; + + if (unlikely(start_time_ut > end_time_ut)) { + ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].counter++; + if(ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].latest_end_time_ut < end_time_ut) + ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].latest_end_time_ut = end_time_ut; + continue; + } - if (unlikely(start_time > end_time)) { - error("Invalid page encountered, start time %lu > end time %lu", start_time , end_time ); + if (unlikely(start_time_ut == end_time_ut && entries != 1)) { + ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].counter++; + if(ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].latest_end_time_ut < end_time_ut) + ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].latest_end_time_ut = end_time_ut; continue; } - if (unlikely(start_time == end_time)) { - size_t entries = jf_metric_data->descr[i].page_length / page_type_size[page_type]; - if (unlikely(entries > 1)) { - error("Invalid page encountered, start time %lu = end time but %zu entries were found", start_time, entries); - continue; - } + if (unlikely(!entries)) { + ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].counter++; + if(ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].latest_end_time_ut < end_time_ut) + ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].latest_end_time_ut = end_time_ut; + continue; + } + + if(entries > 1 && update_every_s == 0) { + ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].counter++; + if(ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].latest_end_time_ut < end_time_ut) + ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].latest_end_time_ut = end_time_ut; + continue; + } + + if(start_time_ut + update_every_s * USEC_PER_SEC * (entries - 1) != end_time_ut) { + ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].counter++; + if(ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].latest_end_time_ut < end_time_ut) + ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].latest_end_time_ut = end_time_ut; + + // let this be + // end_time_ut = start_time_ut + update_every_s * USEC_PER_SEC * (entries - 1); } temp_id = (uuid_t *)jf_metric_data->descr[i].uuid; @@ -340,7 +366,7 @@ static void restore_extent_metadata(struct rrdengine_instance *ctx, struct rrden uv_rwlock_wrlock(&pg_cache->metrics_index.lock); PValue = JudyHSIns(&pg_cache->metrics_index.JudyHS_array, temp_id, sizeof(uuid_t), PJE0); fatal_assert(NULL == *PValue); /* TODO: figure out concurrency model */ - *PValue = page_index = create_page_index(temp_id); + *PValue = page_index = create_page_index(temp_id, ctx); page_index->prev = pg_cache->metrics_index.last_page_index; pg_cache->metrics_index.last_page_index = page_index; uv_rwlock_wrunlock(&pg_cache->metrics_index.lock); @@ -348,21 +374,32 @@ static void restore_extent_metadata(struct rrdengine_instance *ctx, struct rrden descr = pg_cache_create_descr(); descr->page_length = jf_metric_data->descr[i].page_length; - descr->start_time = start_time; - descr->end_time = end_time; + descr->start_time_ut = start_time_ut; + descr->end_time_ut = end_time_ut; + descr->update_every_s = (update_every_s > 0) ? (uint32_t)update_every_s : (page_index->latest_update_every_s); descr->id = &page_index->id; descr->extent = extent; descr->type = page_type; extent->pages[valid_pages++] = descr; pg_cache_insert(ctx, page_index, descr); + + if(page_index->latest_time_ut == descr->end_time_ut) + page_index->latest_update_every_s = descr->update_every_s; + + if(descr->update_every_s == 0) + fatal( + "DBENGINE: page descriptor update every is zero, end_time_ut = %llu, start_time_ut = %llu, entries = %zu", + (unsigned long long)end_time_ut, (unsigned long long)start_time_ut, entries); } extent->number_of_pages = valid_pages; if (likely(valid_pages)) df_extent_insert(extent); - else + else { freez(extent); + ctx->load_errors[LOAD_ERRORS_DROPPED_EXTENT].counter++; + } } /* @@ -442,27 +479,30 @@ static uint64_t iterate_transactions(struct rrdengine_instance *ctx, struct rrde //data_file_size = journalfile->datafile->pos; TODO: utilize this? max_id = 1; - ret = posix_memalign((void *)&buf, RRDFILE_ALIGNMENT, READAHEAD_BYTES); - if (unlikely(ret)) { - fatal("posix_memalign:%s", strerror(ret)); + bool journal_is_mmapped = (journalfile->data != NULL); + if (unlikely(!journal_is_mmapped)) { + ret = posix_memalign((void *)&buf, RRDFILE_ALIGNMENT, READAHEAD_BYTES); + if (unlikely(ret)) + fatal("posix_memalign:%s", strerror(ret)); } - + else + buf = journalfile->data + sizeof(struct rrdeng_jf_sb); for (pos = sizeof(struct rrdeng_jf_sb) ; pos < file_size ; pos += READAHEAD_BYTES) { size_bytes = MIN(READAHEAD_BYTES, file_size - pos); - iov = uv_buf_init(buf, size_bytes); - ret = uv_fs_read(NULL, &req, file, &iov, 1, pos, NULL); - if (ret < 0) { - error("uv_fs_read: pos=%"PRIu64", %s", pos, uv_strerror(ret)); + if (unlikely(!journal_is_mmapped)) { + iov = uv_buf_init(buf, size_bytes); + ret = uv_fs_read(NULL, &req, file, &iov, 1, pos, NULL); + if (ret < 0) { + error("uv_fs_read: pos=%" PRIu64 ", %s", pos, uv_strerror(ret)); + uv_fs_req_cleanup(&req); + goto skip_file; + } + fatal_assert(req.result >= 0); uv_fs_req_cleanup(&req); - goto skip_file; + ++ctx->stats.io_read_requests; + ctx->stats.io_read_bytes += size_bytes; } - fatal_assert(req.result >= 0); - uv_fs_req_cleanup(&req); - ctx->stats.io_read_bytes += size_bytes; - ++ctx->stats.io_read_requests; - //pos_i = pos; - //while (pos_i < pos + size_bytes) { for (pos_i = 0 ; pos_i < size_bytes ; ) { unsigned max_size; @@ -475,9 +515,12 @@ static uint64_t iterate_transactions(struct rrdengine_instance *ctx, struct rrde pos_i += ret; max_id = MAX(max_id, id); } + if (likely(journal_is_mmapped)) + buf += size_bytes; } skip_file: - free(buf); + if (unlikely(!journal_is_mmapped)) + posix_memfree(buf); return max_id; } @@ -512,12 +555,16 @@ int load_journal_file(struct rrdengine_instance *ctx, struct rrdengine_journalfi journalfile->file = file; journalfile->pos = file_size; + journalfile->data = netdata_mmap(path, file_size, MAP_SHARED, 0); + info("Loading journal file \"%s\" using %s.", path, journalfile->data?"MMAP":"uv_fs_read"); max_id = iterate_transactions(ctx, journalfile); ctx->commit_log.transaction_id = MAX(ctx->commit_log.transaction_id, max_id + 1); info("Journal file \"%s\" loaded (size:%"PRIu64").", path, file_size); + if (likely(journalfile->data)) + netdata_munmap(journalfile->data, file_size); return 0; error: diff --git a/database/engine/journalfile.h b/database/engine/journalfile.h index f6c43cd16..011c5065f 100644 --- a/database/engine/journalfile.h +++ b/database/engine/journalfile.h @@ -19,7 +19,7 @@ struct rrdengine_journalfile; struct rrdengine_journalfile { uv_file file; uint64_t pos; - + void *data; struct rrdengine_datafile *datafile; }; @@ -33,17 +33,17 @@ struct transaction_commit_log { unsigned buf_size; }; -extern void generate_journalfilepath(struct rrdengine_datafile *datafile, char *str, size_t maxlen); -extern void journalfile_init(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile); -extern void *wal_get_transaction_buffer(struct rrdengine_worker_config* wc, unsigned size); -extern void wal_flush_transaction_buffer(struct rrdengine_worker_config* wc); -extern int close_journal_file(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile); -extern int unlink_journal_file(struct rrdengine_journalfile *journalfile); -extern int destroy_journal_file(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile); -extern int create_journal_file(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile); -extern int load_journal_file(struct rrdengine_instance *ctx, struct rrdengine_journalfile *journalfile, +void generate_journalfilepath(struct rrdengine_datafile *datafile, char *str, size_t maxlen); +void journalfile_init(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile); +void *wal_get_transaction_buffer(struct rrdengine_worker_config* wc, unsigned size); +void wal_flush_transaction_buffer(struct rrdengine_worker_config* wc); +int close_journal_file(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile); +int unlink_journal_file(struct rrdengine_journalfile *journalfile); +int destroy_journal_file(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile); +int create_journal_file(struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile); +int load_journal_file(struct rrdengine_instance *ctx, struct rrdengine_journalfile *journalfile, struct rrdengine_datafile *datafile); -extern void init_commit_log(struct rrdengine_instance *ctx); +void init_commit_log(struct rrdengine_instance *ctx); #endif /* NETDATA_JOURNALFILE_H */ \ No newline at end of file diff --git a/database/engine/metadata_log/Makefile.am b/database/engine/metadata_log/Makefile.am deleted file mode 100644 index 161784b8f..000000000 --- a/database/engine/metadata_log/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in - -dist_noinst_DATA = \ - README.md \ - $(NULL) diff --git a/database/engine/metadata_log/compaction.c b/database/engine/metadata_log/compaction.c deleted file mode 100644 index ba19e1edf..000000000 --- a/database/engine/metadata_log/compaction.c +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#define NETDATA_RRD_INTERNALS - -#include "metadatalog.h" - -/* Return 0 on success. */ -int compaction_failure_recovery(struct metalog_instance *ctx, struct metadata_logfile **metalogfiles, - unsigned *matched_files) -{ - int ret; - unsigned starting_fileno, fileno, i, j, recovered_files; - struct metadata_logfile *metalogfile = NULL, *compactionfile = NULL, **tmp_metalogfiles; - char *dbfiles_path = ctx->rrdeng_ctx->dbfiles_path; - - for (i = 0 ; i < *matched_files ; ++i) { - metalogfile = metalogfiles[i]; - if (0 == metalogfile->starting_fileno) - continue; /* skip standard metadata log files */ - break; /* this is a compaction temporary file */ - } - if (i == *matched_files) /* no recovery needed */ - return 0; - info("Starting metadata log file failure recovery procedure in \"%s\".", dbfiles_path); - - if (*matched_files - i > 1) { /* Can't have more than 1 temporary compaction files */ - error("Metadata log files are in an invalid state. Cannot proceed."); - return 1; - } - compactionfile = metalogfile; - starting_fileno = compactionfile->starting_fileno; - fileno = compactionfile->fileno; - /* scratchpad space to move file pointers around */ - tmp_metalogfiles = callocz(*matched_files, sizeof(*tmp_metalogfiles)); - - for (j = 0, recovered_files = 0 ; j < i ; ++j) { - metalogfile = metalogfiles[j]; - fatal_assert(0 == metalogfile->starting_fileno); - if (metalogfile->fileno < starting_fileno) { - tmp_metalogfiles[recovered_files++] = metalogfile; - continue; - } - break; /* reached compaction file serial number */ - } - - if ((j == i) /* Shouldn't be possible, invalid compaction temporary file */ || - (metalogfile->fileno == starting_fileno && metalogfile->fileno == fileno)) { - error("Deleting invalid compaction temporary file \"%s/"METALOG_PREFIX METALOG_FILE_NUMBER_PRINT_TMPL - METALOG_EXTENSION"\"", dbfiles_path, starting_fileno, fileno); - unlink_metadata_logfile(compactionfile); - freez(compactionfile); - freez(tmp_metalogfiles); - --*matched_files; /* delete the last one */ - - info("Finished metadata log file failure recovery procedure in \"%s\".", dbfiles_path); - return 0; - } - - for ( ; j < i ; ++j) { /* continue iterating through normal metadata log files */ - metalogfile = metalogfiles[j]; - fatal_assert(0 == metalogfile->starting_fileno); - if (metalogfile->fileno < fileno) { /* It has already been compacted */ - error("Deleting invalid metadata log file \"%s/"METALOG_PREFIX METALOG_FILE_NUMBER_PRINT_TMPL - METALOG_EXTENSION"\"", dbfiles_path, 0U, metalogfile->fileno); - unlink_metadata_logfile(metalogfile); - freez(metalogfile); - continue; - } - tmp_metalogfiles[recovered_files++] = metalogfile; - } - - /* compaction temporary file is valid */ - tmp_metalogfiles[recovered_files++] = compactionfile; - ret = rename_metadata_logfile(compactionfile, 0, starting_fileno); - if (ret < 0) { - error("Cannot rename temporary compaction files. Cannot proceed."); - freez(tmp_metalogfiles); - return 1; - } - - memcpy(metalogfiles, tmp_metalogfiles, recovered_files * sizeof(*metalogfiles)); - *matched_files = recovered_files; - freez(tmp_metalogfiles); - - info("Finished metadata log file failure recovery procedure in \"%s\".", dbfiles_path); - return 0; -} diff --git a/database/engine/metadata_log/compaction.h b/database/engine/metadata_log/compaction.h deleted file mode 100644 index d04613440..000000000 --- a/database/engine/metadata_log/compaction.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_COMPACTION_H -#define NETDATA_COMPACTION_H - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include "../rrdengine.h" - -extern int compaction_failure_recovery(struct metalog_instance *ctx, struct metadata_logfile **metalogfiles, - unsigned *matched_files); - -#endif /* NETDATA_COMPACTION_H */ diff --git a/database/engine/metadata_log/logfile.c b/database/engine/metadata_log/logfile.c deleted file mode 100644 index 07eb9b6fe..000000000 --- a/database/engine/metadata_log/logfile.c +++ /dev/null @@ -1,447 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#include -#include "metadatalog.h" -#include "metalogpluginsd.h" - - -void generate_metadata_logfile_path(struct metadata_logfile *metalogfile, char *str, size_t maxlen) -{ - (void) snprintfz(str, maxlen, "%s/" METALOG_PREFIX METALOG_FILE_NUMBER_PRINT_TMPL METALOG_EXTENSION, - metalogfile->ctx->rrdeng_ctx->dbfiles_path, metalogfile->starting_fileno, metalogfile->fileno); -} - -void metadata_logfile_init(struct metadata_logfile *metalogfile, struct metalog_instance *ctx, unsigned starting_fileno, - unsigned fileno) -{ - metalogfile->starting_fileno = starting_fileno; - metalogfile->fileno = fileno; - metalogfile->file = (uv_file)0; - metalogfile->pos = 0; - metalogfile->next = NULL; - metalogfile->ctx = ctx; -} - -int rename_metadata_logfile(struct metadata_logfile *metalogfile, unsigned new_starting_fileno, unsigned new_fileno) -{ - //struct metalog_instance *ctx = metalogfile->ctx; - uv_fs_t req; - int ret; - char oldpath[RRDENG_PATH_MAX], newpath[RRDENG_PATH_MAX]; - unsigned backup_starting_fileno, backup_fileno; - - backup_starting_fileno = metalogfile->starting_fileno; - backup_fileno = metalogfile->fileno; - generate_metadata_logfile_path(metalogfile, oldpath, sizeof(oldpath)); - metalogfile->starting_fileno = new_starting_fileno; - metalogfile->fileno = new_fileno; - generate_metadata_logfile_path(metalogfile, newpath, sizeof(newpath)); - - info("Renaming metadata log file \"%s\" to \"%s\".", oldpath, newpath); - ret = uv_fs_rename(NULL, &req, oldpath, newpath, NULL); - if (ret < 0) { - error("uv_fs_rename(%s): %s", oldpath, uv_strerror(ret)); - //++ctx->stats.fs_errors; /* this is racy, may miss some errors */ - rrd_stat_atomic_add(&global_fs_errors, 1); - /* restore previous values */ - metalogfile->starting_fileno = backup_starting_fileno; - metalogfile->fileno = backup_fileno; - } - uv_fs_req_cleanup(&req); - - return ret; -} - -int unlink_metadata_logfile(struct metadata_logfile *metalogfile) -{ - //struct metalog_instance *ctx = metalogfile->ctx; - uv_fs_t req; - int ret; - char path[RRDENG_PATH_MAX]; - - generate_metadata_logfile_path(metalogfile, path, sizeof(path)); - - ret = uv_fs_unlink(NULL, &req, path, NULL); - if (ret < 0) { - error("uv_fs_fsunlink(%s): %s", path, uv_strerror(ret)); -// ++ctx->stats.fs_errors; - rrd_stat_atomic_add(&global_fs_errors, 1); - } - uv_fs_req_cleanup(&req); - - return ret; -} - -static int check_metadata_logfile_superblock(uv_file file) -{ - int ret; - struct rrdeng_metalog_sb *superblock; - uv_buf_t iov; - uv_fs_t req; - - ret = posix_memalign((void *)&superblock, RRDFILE_ALIGNMENT, sizeof(*superblock)); - if (unlikely(ret)) { - fatal("posix_memalign:%s", strerror(ret)); - } - iov = uv_buf_init((void *)superblock, sizeof(*superblock)); - - ret = uv_fs_read(NULL, &req, file, &iov, 1, 0, NULL); - if (ret < 0) { - error("uv_fs_read: %s", uv_strerror(ret)); - uv_fs_req_cleanup(&req); - goto error; - } - fatal_assert(req.result >= 0); - uv_fs_req_cleanup(&req); - - if (strncmp(superblock->magic_number, RRDENG_METALOG_MAGIC, RRDENG_MAGIC_SZ)) { - error("File has invalid superblock."); - ret = UV_EINVAL; - } else { - ret = 0; - } - if (superblock->version > RRDENG_METALOG_VER) { - error("File has unknown version %"PRIu16". Compatibility is not guaranteed.", superblock->version); - } -error: - free(superblock); - return ret; -} - -void replay_record(struct metadata_logfile *metalogfile, struct rrdeng_metalog_record_header *header, void *payload) -{ - struct metalog_instance *ctx = metalogfile->ctx; - char *line, *nextline, *record_end; - int ret; - - debug(D_METADATALOG, "RECORD contents: %.*s", (int)header->payload_length, (char *)payload); - record_end = (char *)payload + header->payload_length - 1; - *record_end = '\0'; - - for (line = payload ; line ; line = nextline) { - nextline = strchr(line, '\n'); - if (nextline) { - *nextline++ = '\0'; - } - ret = parser_action(ctx->metalog_parser_object->parser, line); - debug(D_METADATALOG, "parser_action ret:%d", ret); - if (ret) - return; /* skip record due to error */ - }; -} - -/* This function only works with buffered I/O */ -static inline int metalogfile_read(struct metadata_logfile *metalogfile, void *buf, size_t len, uint64_t offset) -{ -// struct metalog_instance *ctx; - uv_file file; - uv_buf_t iov; - uv_fs_t req; - int ret; - -// ctx = metalogfile->ctx; - file = metalogfile->file; - iov = uv_buf_init(buf, len); - ret = uv_fs_read(NULL, &req, file, &iov, 1, offset, NULL); - if (unlikely(ret < 0 && ret != req.result)) { - fatal("uv_fs_read: %s", uv_strerror(ret)); - } - if (req.result < 0) { -// ++ctx->stats.io_errors; - rrd_stat_atomic_add(&global_io_errors, 1); - error("%s: uv_fs_read - %s - record at offset %"PRIu64"(%u) in metadata logfile %u-%u.", __func__, - uv_strerror((int)req.result), offset, (unsigned)len, metalogfile->starting_fileno, metalogfile->fileno); - } - uv_fs_req_cleanup(&req); -// ctx->stats.io_read_bytes += len; -// ++ctx->stats.io_read_requests; - - return ret; -} - -/* Return 0 on success */ -static int metadata_record_integrity_check(void *record) -{ - int ret; - uint32_t data_size; - struct rrdeng_metalog_record_header *header; - struct rrdeng_metalog_record_trailer *trailer; - uLong crc; - - header = record; - data_size = header->header_length + header->payload_length; - trailer = record + data_size; - - crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, record, data_size); - ret = crc32cmp(trailer->checksum, crc); - - return ret; -} - -#define MAX_READ_BYTES (RRDENG_BLOCK_SIZE * 32) /* no record should be over 128KiB in this version */ - -/* - * Iterates metadata log file records and creates database objects (host/chart/dimension) - */ -static void iterate_records(struct metadata_logfile *metalogfile) -{ - uint32_t file_size, pos, bytes_remaining, record_size; - void *buf; - struct rrdeng_metalog_record_header *header; - struct metalog_instance *ctx = metalogfile->ctx; - struct metalog_pluginsd_state *state = ctx->metalog_parser_object->private; - const size_t min_header_size = offsetof(struct rrdeng_metalog_record_header, header_length) + - sizeof(header->header_length); - - file_size = metalogfile->pos; - state->metalogfile = metalogfile; - - buf = mallocz(MAX_READ_BYTES); - - for (pos = sizeof(struct rrdeng_metalog_sb) ; pos < file_size ; pos += record_size) { - bytes_remaining = file_size - pos; - if (bytes_remaining < min_header_size) { - error("%s: unexpected end of file in metadata logfile %u-%u.", __func__, metalogfile->starting_fileno, - metalogfile->fileno); - break; - } - if (metalogfile_read(metalogfile, buf, min_header_size, pos) < 0) - break; - header = (struct rrdeng_metalog_record_header *)buf; - if (METALOG_STORE_PADDING == header->type) { - info("%s: Skipping padding in metadata logfile %u-%u.", __func__, metalogfile->starting_fileno, - metalogfile->fileno); - record_size = ALIGN_BYTES_FLOOR(pos + RRDENG_BLOCK_SIZE) - pos; - continue; - } - if (metalogfile_read(metalogfile, buf + min_header_size, sizeof(*header) - min_header_size, - pos + min_header_size) < 0) - break; - record_size = header->header_length + header->payload_length + sizeof(struct rrdeng_metalog_record_trailer); - if (header->header_length < min_header_size || record_size > bytes_remaining) { - error("%s: Corrupted record in metadata logfile %u-%u.", __func__, metalogfile->starting_fileno, - metalogfile->fileno); - break; - } - if (record_size > MAX_READ_BYTES) { - error("%s: Record is too long (%u bytes) in metadata logfile %u-%u.", __func__, record_size, - metalogfile->starting_fileno, metalogfile->fileno); - continue; - } - if (metalogfile_read(metalogfile, buf + sizeof(*header), record_size - sizeof(*header), - pos + sizeof(*header)) < 0) - break; - if (metadata_record_integrity_check(buf)) { - error("%s: Record at offset %"PRIu32" was read from disk. CRC32 check: FAILED", __func__, pos); - continue; - } - debug(D_METADATALOG, "%s: Record at offset %"PRIu32" was read from disk. CRC32 check: SUCCEEDED", __func__, - pos); - - replay_record(metalogfile, header, buf + header->header_length); - } - - freez(buf); -} - -int load_metadata_logfile(struct metalog_instance *ctx, struct metadata_logfile *metalogfile) -{ - UNUSED(ctx); - uv_fs_t req; - uv_file file; - int ret, fd, error; - uint64_t file_size; - char path[RRDENG_PATH_MAX]; - - generate_metadata_logfile_path(metalogfile, path, sizeof(path)); - if (file_is_migrated(path)) - return 0; - - fd = open_file_buffered_io(path, O_RDWR, &file); - if (fd < 0) { -// ++ctx->stats.fs_errors; - rrd_stat_atomic_add(&global_fs_errors, 1); - return fd; - } - info("Loading metadata log \"%s\".", path); - - ret = check_file_properties(file, &file_size, sizeof(struct rrdeng_metalog_sb)); - if (ret) - goto error; - - ret = check_metadata_logfile_superblock(file); - if (ret) - goto error; -// ctx->stats.io_read_bytes += sizeof(struct rrdeng_jf_sb); -// ++ctx->stats.io_read_requests; - - metalogfile->file = file; - metalogfile->pos = file_size; - - iterate_records(metalogfile); - - info("Metadata log \"%s\" migrated to the database (size:%"PRIu64").", path, file_size); - add_migrated_file(path, file_size); - return 0; - -error: - error = ret; - ret = uv_fs_close(NULL, &req, file, NULL); - if (ret < 0) { - error("uv_fs_close(%s): %s", path, uv_strerror(ret)); -// ++ctx->stats.fs_errors; - rrd_stat_atomic_add(&global_fs_errors, 1); - } - uv_fs_req_cleanup(&req); - return error; -} - -static int scan_metalog_files_cmp(const void *a, const void *b) -{ - struct metadata_logfile *file1, *file2; - char path1[RRDENG_PATH_MAX], path2[RRDENG_PATH_MAX]; - - file1 = *(struct metadata_logfile **)a; - file2 = *(struct metadata_logfile **)b; - generate_metadata_logfile_path(file1, path1, sizeof(path1)); - generate_metadata_logfile_path(file2, path2, sizeof(path2)); - return strcmp(path1, path2); -} - -/* Returns number of metadata logfiles that were loaded or < 0 on error */ -static int scan_metalog_files(struct metalog_instance *ctx) -{ - int ret; - unsigned starting_no, no, matched_files, i, failed_to_load; - static uv_fs_t req; - uv_dirent_t dent; - struct metadata_logfile **metalogfiles, *metalogfile; - char *dbfiles_path = ctx->rrdeng_ctx->dbfiles_path; - - ret = uv_fs_scandir(NULL, &req, dbfiles_path, 0, NULL); - if (ret < 0) { - fatal_assert(req.result < 0); - uv_fs_req_cleanup(&req); - error("uv_fs_scandir(%s): %s", dbfiles_path, uv_strerror(ret)); -// ++ctx->stats.fs_errors; - rrd_stat_atomic_add(&global_fs_errors, 1); - return ret; - } - info("Found %d files in path %s", ret, dbfiles_path); - - metalogfiles = callocz(MIN(ret, MAX_DATAFILES), sizeof(*metalogfiles)); - for (matched_files = 0 ; UV_EOF != uv_fs_scandir_next(&req, &dent) && matched_files < MAX_DATAFILES ; ) { - info("Scanning file \"%s/%s\"", dbfiles_path, dent.name); - ret = sscanf(dent.name, METALOG_PREFIX METALOG_FILE_NUMBER_SCAN_TMPL METALOG_EXTENSION, &starting_no, &no); - if (2 == ret) { - info("Matched file \"%s/%s\"", dbfiles_path, dent.name); - metalogfile = mallocz(sizeof(*metalogfile)); - metadata_logfile_init(metalogfile, ctx, starting_no, no); - metalogfiles[matched_files++] = metalogfile; - } - } - uv_fs_req_cleanup(&req); - - if (0 == matched_files) { - freez(metalogfiles); - return 0; - } - if (matched_files == MAX_DATAFILES) { - error("Warning: hit maximum database engine file limit of %d files", MAX_DATAFILES); - } - qsort(metalogfiles, matched_files, sizeof(*metalogfiles), scan_metalog_files_cmp); - ret = compaction_failure_recovery(ctx, metalogfiles, &matched_files); - if (ret) { /* If the files are corrupted fail */ - for (i = 0 ; i < matched_files ; ++i) { - freez(metalogfiles[i]); - } - freez(metalogfiles); - return UV_EINVAL; - } - //ctx->last_fileno = metalogfiles[matched_files - 1]->fileno; - - struct plugind cd = { - .enabled = 1, - .update_every = 0, - .pid = 0, - .serial_failures = 0, - .successful_collections = 0, - .obsolete = 0, - .started_t = INVALID_TIME, - .next = NULL, - .version = 0, - }; - - struct metalog_pluginsd_state metalog_parser_state; - metalog_pluginsd_state_init(&metalog_parser_state, ctx); - - PARSER_USER_OBJECT metalog_parser_object = { - .enabled = cd.enabled, - .host = ctx->rrdeng_ctx->host, - .cd = &cd, - .trust_durations = 0, - .private = &metalog_parser_state - }; - - PARSER *parser = parser_init(metalog_parser_object.host, &metalog_parser_object, NULL, PARSER_INPUT_SPLIT); - parser_add_keyword(parser, PLUGINSD_KEYWORD_HOST, metalog_pluginsd_host); - parser_add_keyword(parser, PLUGINSD_KEYWORD_GUID, pluginsd_guid); - parser_add_keyword(parser, PLUGINSD_KEYWORD_CONTEXT, pluginsd_context); - parser_add_keyword(parser, PLUGINSD_KEYWORD_TOMBSTONE, pluginsd_tombstone); - parser->plugins_action->dimension_action = &metalog_pluginsd_dimension_action; - parser->plugins_action->chart_action = &metalog_pluginsd_chart_action; - parser->plugins_action->guid_action = &metalog_pluginsd_guid_action; - parser->plugins_action->context_action = &metalog_pluginsd_context_action; - parser->plugins_action->tombstone_action = &metalog_pluginsd_tombstone_action; - parser->plugins_action->host_action = &metalog_pluginsd_host_action; - - - metalog_parser_object.parser = parser; - ctx->metalog_parser_object = &metalog_parser_object; - - for (failed_to_load = 0, i = 0 ; i < matched_files ; ++i) { - metalogfile = metalogfiles[i]; - db_lock(); - db_execute("BEGIN TRANSACTION;"); - ret = load_metadata_logfile(ctx, metalogfile); - if (0 != ret) { - error("Deleting invalid metadata log file \"%s/"METALOG_PREFIX METALOG_FILE_NUMBER_PRINT_TMPL - METALOG_EXTENSION"\"", dbfiles_path, metalogfile->starting_fileno, metalogfile->fileno); - unlink_metadata_logfile(metalogfile); - ++failed_to_load; - db_execute("ROLLBACK TRANSACTION;"); - } - else - db_execute("COMMIT TRANSACTION;"); - db_unlock(); - freez(metalogfile); - } - matched_files -= failed_to_load; - debug(D_METADATALOG, "PARSER ended"); - - parser_destroy(parser); - - size_t count __maybe_unused = metalog_parser_object.count; - - debug(D_METADATALOG, "Parsing count=%u", (unsigned)count); - - freez(metalogfiles); - return matched_files; -} - -/* Return 0 on success. */ -int init_metalog_files(struct metalog_instance *ctx) -{ - int ret; - char *dbfiles_path = ctx->rrdeng_ctx->dbfiles_path; - - ret = scan_metalog_files(ctx); - if (ret < 0) { - error("Failed to scan path \"%s\".", dbfiles_path); - return ret; - }/* else if (0 == ret) { - ctx->last_fileno = 1; - }*/ - - return 0; -} diff --git a/database/engine/metadata_log/logfile.h b/database/engine/metadata_log/logfile.h deleted file mode 100644 index df12ac714..000000000 --- a/database/engine/metadata_log/logfile.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_LOGFILE_H -#define NETDATA_LOGFILE_H - -#include "metadatalogprotocol.h" -#include "../rrdengine.h" - -/* Forward declarations */ -struct metadata_logfile; -struct metalog_worker_config; - -#define METALOG_PREFIX "metadatalog-" -#define METALOG_EXTENSION ".mlf" - -/* only one event loop is supported for now */ -struct metadata_logfile { - unsigned fileno; /* Starts at 1 */ - unsigned starting_fileno; /* 0 for normal files, staring number during compaction */ - uv_file file; - uint64_t pos; - struct metalog_instance *ctx; - struct metadata_logfile *next; -}; - -struct metadata_logfile_list { - struct metadata_logfile *first; /* oldest */ - struct metadata_logfile *last; /* newest */ -}; - -extern void generate_metadata_logfile_path(struct metadata_logfile *metadatalog, char *str, size_t maxlen); -extern int rename_metadata_logfile(struct metadata_logfile *metalogfile, unsigned new_starting_fileno, - unsigned new_fileno); -extern int unlink_metadata_logfile(struct metadata_logfile *metalogfile); -extern int load_metadata_logfile(struct metalog_instance *ctx, struct metadata_logfile *logfile); -extern int init_metalog_files(struct metalog_instance *ctx); - - -#endif /* NETDATA_LOGFILE_H */ diff --git a/database/engine/metadata_log/metadatalog.h b/database/engine/metadata_log/metadatalog.h deleted file mode 100644 index 483036a91..000000000 --- a/database/engine/metadata_log/metadatalog.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_METADATALOG_H -#define NETDATA_METADATALOG_H - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include "../rrdengine.h" -#include "metadatalogprotocol.h" -#include "logfile.h" -#include "metadatalogapi.h" -#include "compaction.h" - -/* Forward declarations */ -struct metalog_instance; -struct parser_user_object; - -#define METALOG_FILE_NUMBER_SCAN_TMPL "%5u-%5u" -#define METALOG_FILE_NUMBER_PRINT_TMPL "%5.5u-%5.5u" - -struct metalog_instance { - struct rrdengine_instance *rrdeng_ctx; - struct parser_user_object *metalog_parser_object; - uint8_t initialized; /* set to 1 to mark context initialized */ -}; - -#endif /* NETDATA_METADATALOG_H */ diff --git a/database/engine/metadata_log/metadatalogapi.c b/database/engine/metadata_log/metadatalogapi.c deleted file mode 100755 index b206cca05..000000000 --- a/database/engine/metadata_log/metadatalogapi.c +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#define NETDATA_RRD_INTERNALS - -#include "metadatalog.h" - -/* - * Returns 0 on success, negative on error - */ -int metalog_init(struct rrdengine_instance *rrdeng_parent_ctx) -{ - struct metalog_instance *ctx; - int error; - - ctx = callocz(1, sizeof(*ctx)); - ctx->initialized = 0; - rrdeng_parent_ctx->metalog_ctx = ctx; - - ctx->rrdeng_ctx = rrdeng_parent_ctx; - error = init_metalog_files(ctx); - if (error) { - goto error_after_init_rrd_files; - } - ctx->initialized = 1; /* notify dbengine that the metadata log has finished initializing */ - return 0; - -error_after_init_rrd_files: - freez(ctx); - return UV_EIO; -} - -/* This function is called by dbengine rotation logic when the metric has no writers */ -void metalog_delete_dimension_by_uuid(struct metalog_instance *ctx, uuid_t *metric_uuid) -{ - uuid_t multihost_uuid; - - delete_dimension_uuid(metric_uuid); - rrdeng_convert_legacy_uuid_to_multihost(ctx->rrdeng_ctx->machine_guid, metric_uuid, &multihost_uuid); - delete_dimension_uuid(&multihost_uuid); -} diff --git a/database/engine/metadata_log/metadatalogapi.h b/database/engine/metadata_log/metadatalogapi.h deleted file mode 100644 index d558b9317..000000000 --- a/database/engine/metadata_log/metadatalogapi.h +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_METADATALOGAPI_H -#define NETDATA_METADATALOGAPI_H - -extern void metalog_commit_delete_chart(RRDSET *st); -extern void metalog_delete_dimension_by_uuid(struct metalog_instance *ctx, uuid_t *metric_uuid); - -/* must call once before using anything */ -extern int metalog_init(struct rrdengine_instance *rrdeng_parent_ctx); - -#endif /* NETDATA_METADATALOGAPI_H */ diff --git a/database/engine/metadata_log/metadatalogprotocol.h b/database/engine/metadata_log/metadatalogprotocol.h deleted file mode 100644 index 1017213ae..000000000 --- a/database/engine/metadata_log/metadatalogprotocol.h +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_METADATALOGPROTOCOL_H -#define NETDATA_METADATALOGPROTOCOL_H - -#include "../rrddiskprotocol.h" - -#define RRDENG_METALOG_MAGIC "netdata-metadata-log" - -#define RRDENG_METALOG_VER (1) - -#define RRDENG_METALOG_SB_PADDING_SZ (RRDENG_BLOCK_SIZE - (RRDENG_MAGIC_SZ + sizeof(uint16_t))) -/* - * Metadata log persistent super-block - */ -struct rrdeng_metalog_sb { - char magic_number[RRDENG_MAGIC_SZ]; - uint16_t version; - uint8_t padding[RRDENG_METALOG_SB_PADDING_SZ]; -} __attribute__ ((packed)); - -/* - * Metadata log record types - */ -#define METALOG_STORE_PADDING (0) -#define METALOG_CREATE_OBJECT (1) -#define METALOG_DELETE_OBJECT (2) -#define METALOG_OTHER (3) /* reserved */ - -/* - * Metadata log record header - */ -struct rrdeng_metalog_record_header { - /* when set to METALOG_STORE_PADDING jump to start of next block */ - uint8_t type; - - uint16_t header_length; - uint32_t payload_length; - /****************************************************** - * No fields above this point can ever change. * - ****************************************************** - * All fields below this point are subject to change. * - ******************************************************/ -} __attribute__ ((packed)); - -/* - * Metadata log record trailer - */ -struct rrdeng_metalog_record_trailer { - uint8_t checksum[CHECKSUM_SZ]; /* CRC32 */ -} __attribute__ ((packed)); - -#endif /* NETDATA_METADATALOGPROTOCOL_H */ diff --git a/database/engine/metadata_log/metalogpluginsd.c b/database/engine/metadata_log/metalogpluginsd.c deleted file mode 100755 index a5301bc10..000000000 --- a/database/engine/metadata_log/metalogpluginsd.c +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -#define NETDATA_RRD_INTERNALS - -#include "metadatalog.h" -#include "metalogpluginsd.h" - -extern struct config stream_config; - -PARSER_RC metalog_pluginsd_host_action( - void *user, char *machine_guid, char *hostname, char *registry_hostname, int update_every, char *os, char *timezone, - char *tags) -{ - struct metalog_pluginsd_state *state = ((PARSER_USER_OBJECT *)user)->private; - - RRDHOST *host = rrdhost_find_by_guid(machine_guid, 0); - if (host) { - if (unlikely(host->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE)) { - error("Archived host '%s' has memory mode '%s', but the archived one is '%s'. Ignoring archived state.", - host->hostname, rrd_memory_mode_name(host->rrd_memory_mode), - rrd_memory_mode_name(RRD_MEMORY_MODE_DBENGINE)); - ((PARSER_USER_OBJECT *) user)->host = NULL; /* Ignore objects if memory mode is not dbengine */ - } - ((PARSER_USER_OBJECT *) user)->host = host; - return PARSER_RC_OK; - } - - if (strcmp(machine_guid, registry_get_this_machine_guid()) == 0) { - ((PARSER_USER_OBJECT *) user)->host = host; - return PARSER_RC_OK; - } - - if (likely(!uuid_parse(machine_guid, state->host_uuid))) { - int rc = sql_store_host(&state->host_uuid, hostname, registry_hostname, update_every, os, timezone, tags, 1); - if (unlikely(rc)) { - errno = 0; - error("Failed to store host %s with UUID %s in the database", hostname, machine_guid); - } - } - else { - errno = 0; - error("Host machine GUID %s is not valid", machine_guid); - } - - return PARSER_RC_OK; -} - -PARSER_RC metalog_pluginsd_chart_action(void *user, char *type, char *id, char *name, char *family, char *context, - char *title, char *units, char *plugin, char *module, int priority, - int update_every, RRDSET_TYPE chart_type, char *options) -{ - UNUSED(options); - - struct metalog_pluginsd_state *state = ((PARSER_USER_OBJECT *)user)->private; - RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host; - - if (unlikely(uuid_is_null(state->host_uuid))) { - debug(D_METADATALOG, "Ignoring chart belonging to missing or ignored host."); - return PARSER_RC_OK; - } - uuid_copy(state->chart_uuid, state->uuid); - uuid_clear(state->uuid); /* Consume UUID */ - (void) sql_store_chart(&state->chart_uuid, &state->host_uuid, - type, id, name, family, context, title, units, - plugin, module, priority, update_every, - chart_type, RRD_MEMORY_MODE_DBENGINE, host ? host->rrd_history_entries : 1); - ((PARSER_USER_OBJECT *)user)->st_exists = 1; - - return PARSER_RC_OK; -} - -PARSER_RC metalog_pluginsd_dimension_action(void *user, RRDSET *st, char *id, char *name, char *algorithm, - long multiplier, long divisor, char *options, RRD_ALGORITHM algorithm_type) -{ - struct metalog_pluginsd_state *state = ((PARSER_USER_OBJECT *)user)->private; - UNUSED(user); - UNUSED(options); - UNUSED(algorithm); - UNUSED(st); - - if (unlikely(uuid_is_null(state->chart_uuid))) { - debug(D_METADATALOG, "Ignoring dimension belonging to missing or ignored chart."); - info("Ignoring dimension belonging to missing or ignored chart."); - return PARSER_RC_OK; - } - - if (unlikely(uuid_is_null(state->uuid))) { - debug(D_METADATALOG, "Ignoring dimension without unknown UUID"); - info("Ignoring dimension without unknown UUID"); - return PARSER_RC_OK; - } - - (void) sql_store_dimension(&state->uuid, &state->chart_uuid, id, name, multiplier, divisor, algorithm_type); - uuid_clear(state->uuid); /* Consume UUID */ - - return PARSER_RC_OK; -} - -PARSER_RC metalog_pluginsd_guid_action(void *user, uuid_t *uuid) -{ - struct metalog_pluginsd_state *state = ((PARSER_USER_OBJECT *)user)->private; - - uuid_copy(state->uuid, *uuid); - - return PARSER_RC_OK; -} - -PARSER_RC metalog_pluginsd_context_action(void *user, uuid_t *uuid) -{ - struct metalog_pluginsd_state *state = ((PARSER_USER_OBJECT *)user)->private; - - int rc = find_uuid_type(uuid); - - if (rc == 1) { - uuid_copy(state->host_uuid, *uuid); - ((PARSER_USER_OBJECT *)user)->st_exists = 0; - ((PARSER_USER_OBJECT *)user)->host_exists = 1; - } else if (rc == 2) { - uuid_copy(state->chart_uuid, *uuid); - ((PARSER_USER_OBJECT *)user)->st_exists = 1; - } else - uuid_copy(state->uuid, *uuid); - - return PARSER_RC_OK; -} - -PARSER_RC metalog_pluginsd_tombstone_action(void *user, uuid_t *uuid) -{ - UNUSED(user); - UNUSED(uuid); - - return PARSER_RC_OK; -} - -void metalog_pluginsd_state_init(struct metalog_pluginsd_state *state, struct metalog_instance *ctx) -{ - state->ctx = ctx; - state->skip_record = 0; - uuid_clear(state->uuid); - state->metalogfile = NULL; -} diff --git a/database/engine/metadata_log/metalogpluginsd.h b/database/engine/metadata_log/metalogpluginsd.h deleted file mode 100644 index 4fd8c3900..000000000 --- a/database/engine/metadata_log/metalogpluginsd.h +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_METALOGPLUGINSD_H -#define NETDATA_METALOGPLUGINSD_H - -#include "collectors/plugins.d/pluginsd_parser.h" -#include "collectors/plugins.d/plugins_d.h" -#include "parser/parser.h" - -struct metalog_pluginsd_state { - struct metalog_instance *ctx; - uuid_t uuid; - uuid_t host_uuid; - uuid_t chart_uuid; - uint8_t skip_record; /* skip this record due to errors in parsing */ - struct metadata_logfile *metalogfile; /* current metadata log file being replayed */ -}; - -extern void metalog_pluginsd_state_init(struct metalog_pluginsd_state *state, struct metalog_instance *ctx); - -extern PARSER_RC metalog_pluginsd_chart_action(void *user, char *type, char *id, char *name, char *family, - char *context, char *title, char *units, char *plugin, char *module, - int priority, int update_every, RRDSET_TYPE chart_type, char *options); -extern PARSER_RC metalog_pluginsd_dimension_action(void *user, RRDSET *st, char *id, char *name, char *algorithm, - long multiplier, long divisor, char *options, - RRD_ALGORITHM algorithm_type); -extern PARSER_RC metalog_pluginsd_guid_action(void *user, uuid_t *uuid); -extern PARSER_RC metalog_pluginsd_context_action(void *user, uuid_t *uuid); -extern PARSER_RC metalog_pluginsd_tombstone_action(void *user, uuid_t *uuid); -extern PARSER_RC metalog_pluginsd_host(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC metalog_pluginsd_host_action(void *user, char *machine_guid, char *hostname, char *registry_hostname, int update_every, char *os, char *timezone, char *tags); - -#endif /* NETDATA_METALOGPLUGINSD_H */ diff --git a/database/engine/pagecache.c b/database/engine/pagecache.c index 39f7642d0..d65cb35a5 100644 --- a/database/engine/pagecache.c +++ b/database/engine/pagecache.c @@ -4,8 +4,8 @@ #include "rrdengine.h" ARAL page_descr_aral = { - .element_size = sizeof(struct rrdeng_page_descr), - .elements = 20000, + .requested_element_size = sizeof(struct rrdeng_page_descr), + .initial_elements = 20000, .filename = "page_descriptors", .cache_dir = &netdata_configured_cache_dir, .use_mmap = false, @@ -127,12 +127,13 @@ struct rrdeng_page_descr *pg_cache_create_descr(void) descr = rrdeng_page_descr_mallocz(); descr->page_length = 0; - descr->start_time = INVALID_TIME; - descr->end_time = INVALID_TIME; + descr->start_time_ut = INVALID_TIME; + descr->end_time_ut = INVALID_TIME; descr->id = NULL; descr->extent = NULL; descr->pg_cache_descr_state = 0; descr->pg_cache_descr = NULL; + descr->update_every_s = 0; return descr; } @@ -476,7 +477,7 @@ uint8_t pg_cache_punch_hole(struct rrdengine_instance *ctx, struct rrdeng_page_d uv_rwlock_rdunlock(&pg_cache->metrics_index.lock); uv_rwlock_wrlock(&page_index->lock); - ret = JudyLDel(&page_index->JudyL_array, (Word_t)(descr->start_time / USEC_PER_SEC), PJE0); + ret = JudyLDel(&page_index->JudyL_array, (Word_t)(descr->start_time_ut / USEC_PER_SEC), PJE0); if (unlikely(0 == ret)) { uv_rwlock_wrunlock(&page_index->lock); if (unlikely(debug_flags & D_RRDENGINE)) { @@ -506,7 +507,7 @@ uint8_t pg_cache_punch_hole(struct rrdengine_instance *ctx, struct rrdeng_page_d while (!pg_cache_try_get_unsafe(descr, 1)) { debug(D_RRDENGINE, "%s: Waiting for locked page:", __func__); if (unlikely(debug_flags & D_RRDENGINE)) - print_page_cache_descr(descr); + print_page_cache_descr(descr, "", true); pg_cache_wait_event_unsafe(descr); } } @@ -517,7 +518,7 @@ uint8_t pg_cache_punch_hole(struct rrdengine_instance *ctx, struct rrdeng_page_d while (unlikely(pg_cache_descr->flags & RRD_PAGE_DIRTY)) { debug(D_RRDENGINE, "%s: Found dirty page, waiting for it to be flushed:", __func__); if (unlikely(debug_flags & D_RRDENGINE)) - print_page_cache_descr(descr); + print_page_cache_descr(descr, "", true); pg_cache_wait_event_unsafe(descr); } } @@ -548,8 +549,8 @@ static inline int is_page_in_time_range(struct rrdeng_page_descr *descr, usec_t { usec_t pg_start, pg_end; - pg_start = descr->start_time; - pg_end = descr->end_time; + pg_start = descr->start_time_ut; + pg_end = descr->end_time_ut; return (pg_start < start_time && pg_end >= start_time) || (pg_start >= start_time && pg_start <= end_time); @@ -557,7 +558,7 @@ static inline int is_page_in_time_range(struct rrdeng_page_descr *descr, usec_t static inline int is_point_in_time_in_page(struct rrdeng_page_descr *descr, usec_t point_in_time) { - return (point_in_time >= descr->start_time && point_in_time <= descr->end_time); + return (point_in_time >= descr->start_time_ut && point_in_time <= descr->end_time_ut); } /* The caller must hold the page index lock */ @@ -592,14 +593,14 @@ static inline struct rrdeng_page_descr * /* Update metric oldest and latest timestamps efficiently when adding new values */ void pg_cache_add_new_metric_time(struct pg_cache_page_index *page_index, struct rrdeng_page_descr *descr) { - usec_t oldest_time = page_index->oldest_time; - usec_t latest_time = page_index->latest_time; + usec_t oldest_time = page_index->oldest_time_ut; + usec_t latest_time = page_index->latest_time_ut; - if (unlikely(oldest_time == INVALID_TIME || descr->start_time < oldest_time)) { - page_index->oldest_time = descr->start_time; + if (unlikely(oldest_time == INVALID_TIME || descr->start_time_ut < oldest_time)) { + page_index->oldest_time_ut = descr->start_time_ut; } - if (likely(descr->end_time > latest_time || latest_time == INVALID_TIME)) { - page_index->latest_time = descr->end_time; + if (likely(descr->end_time_ut > latest_time || latest_time == INVALID_TIME)) { + page_index->latest_time_ut = descr->end_time_ut; } } @@ -618,23 +619,23 @@ void pg_cache_update_metric_times(struct pg_cache_page_index *page_index) firstPValue = JudyLFirst(page_index->JudyL_array, &firstIndex, PJE0); if (likely(NULL != firstPValue)) { descr = *firstPValue; - oldest_time = descr->start_time; + oldest_time = descr->start_time_ut; } lastIndex = (Word_t)-1; lastPValue = JudyLLast(page_index->JudyL_array, &lastIndex, PJE0); if (likely(NULL != lastPValue)) { descr = *lastPValue; - latest_time = descr->end_time; + latest_time = descr->end_time_ut; } uv_rwlock_rdunlock(&page_index->lock); if (unlikely(NULL == firstPValue)) { fatal_assert(NULL == lastPValue); - page_index->oldest_time = page_index->latest_time = INVALID_TIME; + page_index->oldest_time_ut = page_index->latest_time_ut = INVALID_TIME; return; } - page_index->oldest_time = oldest_time; - page_index->latest_time = latest_time; + page_index->oldest_time_ut = oldest_time; + page_index->latest_time_ut = latest_time; } /* If index is NULL lookup by UUID (descr->id) */ @@ -669,7 +670,7 @@ void pg_cache_insert(struct rrdengine_instance *ctx, struct pg_cache_page_index } uv_rwlock_wrlock(&page_index->lock); - PValue = JudyLIns(&page_index->JudyL_array, (Word_t)(descr->start_time / USEC_PER_SEC), PJE0); + PValue = JudyLIns(&page_index->JudyL_array, (Word_t)(descr->start_time_ut / USEC_PER_SEC), PJE0); *PValue = descr; ++page_index->page_count; pg_cache_add_new_metric_time(page_index, descr); @@ -681,7 +682,7 @@ void pg_cache_insert(struct rrdengine_instance *ctx, struct pg_cache_page_index uv_rwlock_wrunlock(&pg_cache->pg_cache_rwlock); } -usec_t pg_cache_oldest_time_in_range(struct rrdengine_instance *ctx, uuid_t *id, usec_t start_time, usec_t end_time) +usec_t pg_cache_oldest_time_in_range(struct rrdengine_instance *ctx, uuid_t *id, usec_t start_time_ut, usec_t end_time_ut) { struct page_cache *pg_cache = &ctx->pg_cache; struct rrdeng_page_descr *descr = NULL; @@ -699,25 +700,25 @@ usec_t pg_cache_oldest_time_in_range(struct rrdengine_instance *ctx, uuid_t *id, } uv_rwlock_rdlock(&page_index->lock); - descr = find_first_page_in_time_range(page_index, start_time, end_time); + descr = find_first_page_in_time_range(page_index, start_time_ut, end_time_ut); if (NULL == descr) { uv_rwlock_rdunlock(&page_index->lock); return INVALID_TIME; } uv_rwlock_rdunlock(&page_index->lock); - return descr->start_time; + return descr->start_time_ut; } /** * Return page information for the first page before point_in_time that satisfies the filter. * @param ctx DB context * @param page_index page index of a metric - * @param point_in_time the pages that are searched must be older than this timestamp + * @param point_in_time_ut the pages that are searched must be older than this timestamp * @param filter decides if the page satisfies the caller's criteria * @param page_info the result of the search is set in this pointer */ void pg_cache_get_filtered_info_prev(struct rrdengine_instance *ctx, struct pg_cache_page_index *page_index, - usec_t point_in_time, pg_cache_page_info_filter_t *filter, + usec_t point_in_time_ut, pg_cache_page_info_filter_t *filter, struct rrdeng_page_info *page_info) { struct page_cache *pg_cache = &ctx->pg_cache; @@ -728,7 +729,7 @@ void pg_cache_get_filtered_info_prev(struct rrdengine_instance *ctx, struct pg_c (void)pg_cache; fatal_assert(NULL != page_index); - Index = (Word_t)(point_in_time / USEC_PER_SEC); + Index = (Word_t)(point_in_time_ut / USEC_PER_SEC); uv_rwlock_rdlock(&page_index->lock); do { PValue = JudyLPrev(page_index->JudyL_array, &Index, PJE0); @@ -736,12 +737,12 @@ void pg_cache_get_filtered_info_prev(struct rrdengine_instance *ctx, struct pg_c } while (descr != NULL && !filter(descr)); if (unlikely(NULL == descr)) { page_info->page_length = 0; - page_info->start_time = INVALID_TIME; - page_info->end_time = INVALID_TIME; + page_info->start_time_ut = INVALID_TIME; + page_info->end_time_ut = INVALID_TIME; } else { page_info->page_length = descr->page_length; - page_info->start_time = descr->start_time; - page_info->end_time = descr->end_time; + page_info->start_time_ut = descr->start_time_ut; + page_info->end_time_ut = descr->end_time_ut; } uv_rwlock_rdunlock(&page_index->lock); } @@ -750,7 +751,7 @@ void pg_cache_get_filtered_info_prev(struct rrdengine_instance *ctx, struct pg_c * Searches for an unallocated page without triggering disk I/O. Attempts to reserve the page and get a reference. * @param ctx DB context * @param id lookup by UUID - * @param start_time exact starting time in usec + * @param start_time_ut exact starting time in usec * @param ret_page_indexp Sets the page index pointer (*ret_page_indexp) for the given UUID. * @return the page descriptor or NULL on failure. It can fail if: * 1. The page is already allocated to the page cache. @@ -758,7 +759,7 @@ void pg_cache_get_filtered_info_prev(struct rrdengine_instance *ctx, struct pg_c * 3. It did not succeed to reserve a spot in the page cache. */ struct rrdeng_page_descr *pg_cache_lookup_unpopulated_and_lock(struct rrdengine_instance *ctx, uuid_t *id, - usec_t start_time) + usec_t start_time_ut) { struct page_cache *pg_cache = &ctx->pg_cache; struct rrdeng_page_descr *descr = NULL; @@ -781,7 +782,7 @@ struct rrdeng_page_descr *pg_cache_lookup_unpopulated_and_lock(struct rrdengine_ } uv_rwlock_rdlock(&page_index->lock); - Index = (Word_t)(start_time / USEC_PER_SEC); + Index = (Word_t)(start_time_ut / USEC_PER_SEC); PValue = JudyLGet(page_index->JudyL_array, Index, PJE0); if (likely(NULL != PValue)) { descr = *PValue; @@ -818,15 +819,15 @@ struct rrdeng_page_descr *pg_cache_lookup_unpopulated_and_lock(struct rrdengine_ * Does not get a reference. * @param ctx DB context * @param id UUID - * @param start_time inclusive starting time in usec - * @param end_time inclusive ending time in usec + * @param start_time_ut inclusive starting time in usec + * @param end_time_ut inclusive ending time in usec * @param page_info_arrayp It allocates (*page_arrayp) and populates it with information of pages that overlap * with the time range [start_time,end_time]. The caller must free (*page_info_arrayp) with freez(). * If page_info_arrayp is set to NULL nothing was allocated. * @param ret_page_indexp Sets the page index pointer (*ret_page_indexp) for the given UUID. * @return the number of pages that overlap with the time range [start_time,end_time]. */ -unsigned pg_cache_preload(struct rrdengine_instance *ctx, uuid_t *id, usec_t start_time, usec_t end_time, +unsigned pg_cache_preload(struct rrdengine_instance *ctx, uuid_t *id, usec_t start_time_ut, usec_t end_time_ut, struct rrdeng_page_info **page_info_arrayp, struct pg_cache_page_index **ret_page_indexp) { struct page_cache *pg_cache = &ctx->pg_cache; @@ -854,14 +855,14 @@ unsigned pg_cache_preload(struct rrdengine_instance *ctx, uuid_t *id, usec_t sta } uv_rwlock_rdlock(&page_index->lock); - descr = find_first_page_in_time_range(page_index, start_time, end_time); + descr = find_first_page_in_time_range(page_index, start_time_ut, end_time_ut); if (NULL == descr) { uv_rwlock_rdunlock(&page_index->lock); debug(D_RRDENGINE, "%s: No page was found to attempt preload.", __func__); *ret_page_indexp = NULL; return 0; } else { - Index = (Word_t)(descr->start_time / USEC_PER_SEC); + Index = (Word_t)(descr->start_time_ut / USEC_PER_SEC); } if (page_info_arrayp) { page_info_array_max_size = PAGE_CACHE_MAX_PRELOAD_PAGES * sizeof(struct rrdeng_page_info); @@ -869,7 +870,7 @@ unsigned pg_cache_preload(struct rrdengine_instance *ctx, uuid_t *id, usec_t sta } for (count = 0, preload_count = 0 ; - descr != NULL && is_page_in_time_range(descr, start_time, end_time) ; + descr != NULL && is_page_in_time_range(descr, start_time_ut, end_time_ut) ; PValue = JudyLNext(page_index->JudyL_array, &Index, PJE0), descr = unlikely(NULL == PValue) ? NULL : *PValue) { /* Iterate all pages in range */ @@ -881,8 +882,8 @@ unsigned pg_cache_preload(struct rrdengine_instance *ctx, uuid_t *id, usec_t sta page_info_array_max_size += PAGE_CACHE_MAX_PRELOAD_PAGES * sizeof(struct rrdeng_page_info); *page_info_arrayp = reallocz(*page_info_arrayp, page_info_array_max_size); } - (*page_info_arrayp)[count].start_time = descr->start_time; - (*page_info_arrayp)[count].end_time = descr->end_time; + (*page_info_arrayp)[count].start_time_ut = descr->start_time_ut; + (*page_info_arrayp)[count].end_time_ut = descr->end_time_ut; (*page_info_arrayp)[count].page_length = descr->page_length; } ++count; @@ -974,7 +975,7 @@ unsigned pg_cache_preload(struct rrdengine_instance *ctx, uuid_t *id, usec_t sta */ struct rrdeng_page_descr * pg_cache_lookup(struct rrdengine_instance *ctx, struct pg_cache_page_index *index, uuid_t *id, - usec_t point_in_time) + usec_t point_in_time_ut) { struct page_cache *pg_cache = &ctx->pg_cache; struct rrdeng_page_descr *descr = NULL; @@ -1003,15 +1004,15 @@ struct rrdeng_page_descr * page_not_in_cache = 0; uv_rwlock_rdlock(&page_index->lock); while (1) { - Index = (Word_t)(point_in_time / USEC_PER_SEC); + Index = (Word_t)(point_in_time_ut / USEC_PER_SEC); PValue = JudyLLast(page_index->JudyL_array, &Index, PJE0); if (likely(NULL != PValue)) { descr = *PValue; } if (NULL == PValue || 0 == descr->page_length || - (INVALID_TIME != point_in_time && - !is_point_in_time_in_page(descr, point_in_time))) { + (INVALID_TIME != point_in_time_ut && + !is_point_in_time_in_page(descr, point_in_time_ut))) { /* non-empty page not found */ uv_rwlock_rdunlock(&page_index->lock); @@ -1038,7 +1039,7 @@ struct rrdeng_page_descr * debug(D_RRDENGINE, "%s: Waiting for page to be asynchronously read from disk:", __func__); if(unlikely(debug_flags & D_RRDENGINE)) - print_page_cache_descr(descr); + print_page_cache_descr(descr, "", true); while (!(pg_cache_descr->flags & RRD_PAGE_POPULATED)) { pg_cache_wait_event_unsafe(descr); } @@ -1053,7 +1054,7 @@ struct rrdeng_page_descr * uv_rwlock_rdunlock(&page_index->lock); debug(D_RRDENGINE, "%s: Waiting for page to be unlocked:", __func__); if(unlikely(debug_flags & D_RRDENGINE)) - print_page_cache_descr(descr); + print_page_cache_descr(descr, "", true); if (!(flags & RRD_PAGE_POPULATED)) page_not_in_cache = 1; pg_cache_wait_event_unsafe(descr); @@ -1081,7 +1082,7 @@ struct rrdeng_page_descr * */ struct rrdeng_page_descr * pg_cache_lookup_next(struct rrdengine_instance *ctx, struct pg_cache_page_index *index, uuid_t *id, - usec_t start_time, usec_t end_time) + usec_t start_time_ut, usec_t end_time_ut) { struct page_cache *pg_cache = &ctx->pg_cache; struct rrdeng_page_descr *descr = NULL; @@ -1110,7 +1111,7 @@ pg_cache_lookup_next(struct rrdengine_instance *ctx, struct pg_cache_page_index uv_rwlock_rdlock(&page_index->lock); int retry_count = 0; while (1) { - descr = find_first_page_in_time_range(page_index, start_time, end_time); + descr = find_first_page_in_time_range(page_index, start_time_ut, end_time_ut); if (NULL == descr || 0 == descr->page_length || retry_count == default_rrdeng_page_fetch_retries) { /* non-empty page not found */ if (retry_count == default_rrdeng_page_fetch_retries) @@ -1140,7 +1141,7 @@ pg_cache_lookup_next(struct rrdengine_instance *ctx, struct pg_cache_page_index debug(D_RRDENGINE, "%s: Waiting for page to be asynchronously read from disk:", __func__); if(unlikely(debug_flags & D_RRDENGINE)) - print_page_cache_descr(descr); + print_page_cache_descr(descr, "", true); while (!(pg_cache_descr->flags & RRD_PAGE_POPULATED)) { pg_cache_wait_event_unsafe(descr); } @@ -1155,7 +1156,7 @@ pg_cache_lookup_next(struct rrdengine_instance *ctx, struct pg_cache_page_index uv_rwlock_rdunlock(&page_index->lock); debug(D_RRDENGINE, "%s: Waiting for page to be unlocked:", __func__); if(unlikely(debug_flags & D_RRDENGINE)) - print_page_cache_descr(descr); + print_page_cache_descr(descr, "", true); if (!(flags & RRD_PAGE_POPULATED)) page_not_in_cache = 1; @@ -1180,7 +1181,7 @@ pg_cache_lookup_next(struct rrdengine_instance *ctx, struct pg_cache_page_index return descr; } -struct pg_cache_page_index *create_page_index(uuid_t *id) +struct pg_cache_page_index *create_page_index(uuid_t *id, struct rrdengine_instance *ctx) { struct pg_cache_page_index *page_index; @@ -1188,11 +1189,15 @@ struct pg_cache_page_index *create_page_index(uuid_t *id) page_index->JudyL_array = (Pvoid_t) NULL; uuid_copy(page_index->id, *id); fatal_assert(0 == uv_rwlock_init(&page_index->lock)); - page_index->oldest_time = INVALID_TIME; - page_index->latest_time = INVALID_TIME; + page_index->oldest_time_ut = INVALID_TIME; + page_index->latest_time_ut = INVALID_TIME; page_index->prev = NULL; page_index->page_count = 0; + page_index->refcount = 0; page_index->writers = 0; + page_index->ctx = ctx; + page_index->alignment = NULL; + page_index->latest_update_every_s = default_rrd_update_every; return page_index; } @@ -1238,24 +1243,6 @@ void init_page_cache(struct rrdengine_instance *ctx) init_committed_page_index(ctx); } - - -/* - * METRIC # number - * 1. INDEX: JudyHS # bytes - * 2. DATA: page_index # bytes - * - * PAGE (1 page of 1 metric) # number - * 1. INDEX AT METRIC: page_index->JudyL_array # bytes - * 2. DATA: descr # bytes - * - * PAGE CACHE (1 page of 1 metric at the cache) # number - * 1. pg_cache_descr (if PG_CACHE_DESCR_ALLOCATED) # bytes - * 2. data (if RRD_PAGE_POPULATED) # bytes - * - */ - - void free_page_cache(struct rrdengine_instance *ctx) { struct page_cache *pg_cache = &ctx->pg_cache; @@ -1265,30 +1252,15 @@ void free_page_cache(struct rrdengine_instance *ctx) struct rrdeng_page_descr *descr; struct page_cache_descr *pg_cache_descr; - Word_t metrics_number = 0, - metrics_bytes = 0, - metrics_index_bytes = 0, - metrics_duration = 0; - - Word_t pages_number = 0, - pages_bytes = 0, - pages_index_bytes = 0; - - Word_t pages_size_per_type[256] = { 0 }, - pages_count_per_type[256] = { 0 }; - - Word_t cache_pages_number = 0, - cache_pages_bytes = 0, - cache_pages_data_bytes = 0; - - size_t points_in_db = 0, - uncompressed_points_size = 0, - seconds_in_db = 0, - single_point_pages = 0; - - Word_t pages_dirty_index_bytes = 0; - - usec_t oldest_time_ut = LONG_MAX, latest_time_ut = 0; + // if we are exiting, the OS will recover all memory so do not slow down the shutdown process + // Do the cleanup if we are compiling with NETDATA_INTERNAL_CHECKS + // This affects the reporting of dbengine statistics which are available in real time + // via the /api/v1/dbengine_stats endpoint +#ifndef NETDATA_DBENGINE_FREE + if (netdata_exit) + return; +#endif + Word_t metrics_index_bytes = 0, pages_index_bytes = 0, pages_dirty_index_bytes = 0; /* Free committed page index */ pages_dirty_index_bytes = JudyLFreeArray(&pg_cache->committed_page_index.JudyL_array, PJE0); @@ -1305,116 +1277,30 @@ void free_page_cache(struct rrdengine_instance *ctx) PValue = JudyLFirst(page_index->JudyL_array, &Index, PJE0); descr = unlikely(NULL == PValue) ? NULL : *PValue; - size_t metric_duration = 0; - size_t metric_update_every = 0; - size_t metric_single_point_pages = 0; - while (descr != NULL) { /* Iterate all page descriptors of this metric */ if (descr->pg_cache_descr_state & PG_CACHE_DESCR_ALLOCATED) { - cache_pages_number++; - /* Check rrdenglocking.c */ pg_cache_descr = descr->pg_cache_descr; if (pg_cache_descr->flags & RRD_PAGE_POPULATED) { dbengine_page_free(pg_cache_descr->page); - cache_pages_data_bytes += RRDENG_BLOCK_SIZE; } rrdeng_destroy_pg_cache_descr(ctx, pg_cache_descr); - cache_pages_bytes += sizeof(*pg_cache_descr); } - - if(descr->start_time < oldest_time_ut) - oldest_time_ut = descr->start_time; - - if(descr->end_time > latest_time_ut) - latest_time_ut = descr->end_time; - - pages_size_per_type[descr->type] += descr->page_length; - pages_count_per_type[descr->type]++; - - size_t points_in_page = (descr->page_length / PAGE_POINT_SIZE_BYTES(descr)); - size_t page_duration = ((descr->end_time - descr->start_time) / USEC_PER_SEC); - size_t update_every = (page_duration == 0) ? 1 : page_duration / (points_in_page - 1); - - if (!page_duration && metric_update_every) { - page_duration = metric_update_every; - update_every = metric_update_every; - } - else if(page_duration) - metric_update_every = update_every; - - uncompressed_points_size += descr->page_length; - - if(page_duration > 0) { - page_duration = update_every * points_in_page; - metric_duration += page_duration; - seconds_in_db += page_duration; - points_in_db += descr->page_length / PAGE_POINT_SIZE_BYTES(descr); - } - else - metric_single_point_pages++; - rrdeng_page_descr_freez(descr); - pages_bytes += sizeof(*descr); - pages_number++; PValue = JudyLNext(page_index->JudyL_array, &Index, PJE0); descr = unlikely(NULL == PValue) ? NULL : *PValue; } - if(metric_single_point_pages && metric_update_every) { - points_in_db += metric_single_point_pages; - seconds_in_db += metric_update_every * metric_single_point_pages; - metric_duration += metric_update_every * metric_single_point_pages; - } - else - single_point_pages += metric_single_point_pages; - /* Free page index */ pages_index_bytes += JudyLFreeArray(&page_index->JudyL_array, PJE0); fatal_assert(NULL == page_index->JudyL_array); freez(page_index); - - metrics_number++; - metrics_bytes += sizeof(*page_index); - metrics_duration += metric_duration; } /* Free metrics index */ metrics_index_bytes = JudyHSFreeArray(&pg_cache->metrics_index.JudyHS_array, PJE0); fatal_assert(NULL == pg_cache->metrics_index.JudyHS_array); - - if(!metrics_number) metrics_number = 1; - if(!pages_number) pages_number = 1; - if(!cache_pages_number) cache_pages_number = 1; - if(!points_in_db) points_in_db = 1; - if(latest_time_ut == oldest_time_ut) oldest_time_ut -= USEC_PER_SEC; - - if(single_point_pages) { - long double avg_duration = (long double)seconds_in_db / points_in_db; - points_in_db += single_point_pages; - seconds_in_db += (size_t)(avg_duration * single_point_pages); - } - - info("DBENGINE STATISTICS ON METRICS:" - " Metrics: %lu (structures %lu bytes - per metric %0.2f, index (HS) %lu bytes - per metric %0.2f bytes - duration %zu secs) |" - " Page descriptors: %lu (structures %lu bytes - per page %0.2f bytes, index (L) %lu bytes - per page %0.2f, dirty index %lu bytes). |" - " Page cache: %lu pages (structures %lu bytes - per page %0.2f bytes, data %lu bytes). |" - " Points in db %zu, uncompressed size of points database %zu bytes. |" - " Duration of all points %zu seconds, average point duration %0.2f seconds." - " Duration of the database %llu seconds, average metric duration %0.2f seconds, average metric lifetime %0.2f%%." - , metrics_number, metrics_bytes, (double)metrics_bytes/metrics_number, metrics_index_bytes, (double)metrics_index_bytes/metrics_number, metrics_duration - , pages_number, pages_bytes, (double)pages_bytes/pages_number, pages_index_bytes, (double)pages_index_bytes/pages_number, pages_dirty_index_bytes - , cache_pages_number, cache_pages_bytes, (double)cache_pages_bytes/cache_pages_number, cache_pages_data_bytes - , points_in_db, uncompressed_points_size - , seconds_in_db, (double)seconds_in_db/points_in_db - , (latest_time_ut - oldest_time_ut) / USEC_PER_SEC, (double)metrics_duration/metrics_number - , (double)metrics_duration/metrics_number * 100.0 / ((latest_time_ut - oldest_time_ut) / USEC_PER_SEC) - ); - - for(int i = 0; i < 256 ;i++) { - if(pages_count_per_type[i]) - info("DBENGINE STATISTICS ON PAGE TYPES: page type %d total pages %lu, average page size %0.2f bytes", i, pages_count_per_type[i], (double)pages_size_per_type[i]/pages_count_per_type[i]); - } + info("Freed %lu bytes of memory from page cache.", pages_dirty_index_bytes + pages_index_bytes + metrics_index_bytes); } diff --git a/database/engine/pagecache.h b/database/engine/pagecache.h index b938b9e05..2f4d6b332 100644 --- a/database/engine/pagecache.h +++ b/database/engine/pagecache.h @@ -60,18 +60,19 @@ struct rrdeng_page_descr { volatile unsigned long pg_cache_descr_state; /* page information */ - usec_t start_time; - usec_t end_time; - uint32_t page_length; + usec_t start_time_ut; + usec_t end_time_ut; + uint32_t update_every_s:24; uint8_t type; + uint32_t page_length; }; #define PAGE_INFO_SCRATCH_SZ (8) struct rrdeng_page_info { uint8_t scratch[PAGE_INFO_SCRATCH_SZ]; /* scratch area to be used by page-cache users */ - usec_t start_time; - usec_t end_time; + usec_t start_time_ut; + usec_t end_time_ut; uint32_t page_length; }; @@ -80,6 +81,11 @@ typedef int pg_cache_page_info_filter_t(struct rrdeng_page_descr *); #define PAGE_CACHE_MAX_PRELOAD_PAGES (256) +struct pg_alignment { + uint32_t page_length; + uint32_t refcount; +}; + /* maps time ranges to pages */ struct pg_cache_page_index { uuid_t id; @@ -89,6 +95,7 @@ struct pg_cache_page_index { */ Pvoid_t JudyL_array; Word_t page_count; + unsigned short refcount; unsigned short writers; uv_rwlock_t lock; @@ -96,13 +103,17 @@ struct pg_cache_page_index { * Only one effective writer, data deletion workqueue. * It's also written during the DB loading phase. */ - usec_t oldest_time; + usec_t oldest_time_ut; /* * Only one effective writer, data collection thread. * It's also written by the data deletion workqueue when data collection is disabled for this metric. */ - usec_t latest_time; + usec_t latest_time_ut; + + struct rrdengine_instance *ctx; + struct pg_alignment *alignment; + uint32_t latest_update_every_s; struct pg_cache_page_index *prev; }; @@ -152,93 +163,93 @@ struct page_cache { /* TODO: add statistics */ unsigned populated_pages; }; -extern void pg_cache_wake_up_waiters_unsafe(struct rrdeng_page_descr *descr); -extern void pg_cache_wake_up_waiters(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); -extern void pg_cache_wait_event_unsafe(struct rrdeng_page_descr *descr); -extern unsigned long pg_cache_wait_event(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); -extern void pg_cache_replaceQ_insert(struct rrdengine_instance *ctx, +void pg_cache_wake_up_waiters_unsafe(struct rrdeng_page_descr *descr); +void pg_cache_wake_up_waiters(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); +void pg_cache_wait_event_unsafe(struct rrdeng_page_descr *descr); +unsigned long pg_cache_wait_event(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); +void pg_cache_replaceQ_insert(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); -extern void pg_cache_replaceQ_delete(struct rrdengine_instance *ctx, +void pg_cache_replaceQ_delete(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); -extern void pg_cache_replaceQ_set_hot(struct rrdengine_instance *ctx, +void pg_cache_replaceQ_set_hot(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); -extern struct rrdeng_page_descr *pg_cache_create_descr(void); -extern int pg_cache_try_get_unsafe(struct rrdeng_page_descr *descr, int exclusive_access); -extern void pg_cache_put_unsafe(struct rrdeng_page_descr *descr); -extern void pg_cache_put(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); -extern void pg_cache_insert(struct rrdengine_instance *ctx, struct pg_cache_page_index *index, +struct rrdeng_page_descr *pg_cache_create_descr(void); +int pg_cache_try_get_unsafe(struct rrdeng_page_descr *descr, int exclusive_access); +void pg_cache_put_unsafe(struct rrdeng_page_descr *descr); +void pg_cache_put(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); +void pg_cache_insert(struct rrdengine_instance *ctx, struct pg_cache_page_index *index, struct rrdeng_page_descr *descr); -extern uint8_t pg_cache_punch_hole(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr, +uint8_t pg_cache_punch_hole(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr, uint8_t remove_dirty, uint8_t is_exclusive_holder, uuid_t *metric_id); -extern usec_t pg_cache_oldest_time_in_range(struct rrdengine_instance *ctx, uuid_t *id, - usec_t start_time, usec_t end_time); -extern void pg_cache_get_filtered_info_prev(struct rrdengine_instance *ctx, struct pg_cache_page_index *page_index, - usec_t point_in_time, pg_cache_page_info_filter_t *filter, +usec_t pg_cache_oldest_time_in_range(struct rrdengine_instance *ctx, uuid_t *id, + usec_t start_time_ut, usec_t end_time_ut); +void pg_cache_get_filtered_info_prev(struct rrdengine_instance *ctx, struct pg_cache_page_index *page_index, + usec_t point_in_time_ut, pg_cache_page_info_filter_t *filter, struct rrdeng_page_info *page_info); -extern struct rrdeng_page_descr *pg_cache_lookup_unpopulated_and_lock(struct rrdengine_instance *ctx, uuid_t *id, - usec_t start_time); -extern unsigned - pg_cache_preload(struct rrdengine_instance *ctx, uuid_t *id, usec_t start_time, usec_t end_time, +struct rrdeng_page_descr *pg_cache_lookup_unpopulated_and_lock(struct rrdengine_instance *ctx, uuid_t *id, + usec_t start_time_ut); +unsigned + pg_cache_preload(struct rrdengine_instance *ctx, uuid_t *id, usec_t start_time_ut, usec_t end_time_ut, struct rrdeng_page_info **page_info_arrayp, struct pg_cache_page_index **ret_page_indexp); -extern struct rrdeng_page_descr * +struct rrdeng_page_descr * pg_cache_lookup(struct rrdengine_instance *ctx, struct pg_cache_page_index *index, uuid_t *id, - usec_t point_in_time); -extern struct rrdeng_page_descr * + usec_t point_in_time_ut); +struct rrdeng_page_descr * pg_cache_lookup_next(struct rrdengine_instance *ctx, struct pg_cache_page_index *index, uuid_t *id, - usec_t start_time, usec_t end_time); -extern struct pg_cache_page_index *create_page_index(uuid_t *id); -extern void init_page_cache(struct rrdengine_instance *ctx); -extern void free_page_cache(struct rrdengine_instance *ctx); -extern void pg_cache_add_new_metric_time(struct pg_cache_page_index *page_index, struct rrdeng_page_descr *descr); -extern void pg_cache_update_metric_times(struct pg_cache_page_index *page_index); -extern unsigned long pg_cache_hard_limit(struct rrdengine_instance *ctx); -extern unsigned long pg_cache_soft_limit(struct rrdengine_instance *ctx); -extern unsigned long pg_cache_committed_hard_limit(struct rrdengine_instance *ctx); - -extern void rrdeng_page_descr_aral_go_singlethreaded(void); -extern void rrdeng_page_descr_aral_go_multithreaded(void); -extern void rrdeng_page_descr_use_malloc(void); -extern void rrdeng_page_descr_use_mmap(void); -extern bool rrdeng_page_descr_is_mmap(void); -extern struct rrdeng_page_descr *rrdeng_page_descr_mallocz(void); -extern void rrdeng_page_descr_freez(struct rrdeng_page_descr *descr); + usec_t start_time_ut, usec_t end_time_ut); +struct pg_cache_page_index *create_page_index(uuid_t *id, struct rrdengine_instance *ctx); +void init_page_cache(struct rrdengine_instance *ctx); +void free_page_cache(struct rrdengine_instance *ctx); +void pg_cache_add_new_metric_time(struct pg_cache_page_index *page_index, struct rrdeng_page_descr *descr); +void pg_cache_update_metric_times(struct pg_cache_page_index *page_index); +unsigned long pg_cache_hard_limit(struct rrdengine_instance *ctx); +unsigned long pg_cache_soft_limit(struct rrdengine_instance *ctx); +unsigned long pg_cache_committed_hard_limit(struct rrdengine_instance *ctx); + +void rrdeng_page_descr_aral_go_singlethreaded(void); +void rrdeng_page_descr_aral_go_multithreaded(void); +void rrdeng_page_descr_use_malloc(void); +void rrdeng_page_descr_use_mmap(void); +bool rrdeng_page_descr_is_mmap(void); +struct rrdeng_page_descr *rrdeng_page_descr_mallocz(void); +void rrdeng_page_descr_freez(struct rrdeng_page_descr *descr); static inline void - pg_cache_atomic_get_pg_info(struct rrdeng_page_descr *descr, usec_t *end_timep, uint32_t *page_lengthp) + pg_cache_atomic_get_pg_info(struct rrdeng_page_descr *descr, usec_t *end_time_ut_p, uint32_t *page_lengthp) { - usec_t end_time, old_end_time; + usec_t end_time_ut, old_end_time_ut; uint32_t page_length; if (NULL == descr->extent) { /* this page is currently being modified, get consistent info locklessly */ do { - end_time = descr->end_time; + end_time_ut = descr->end_time_ut; __sync_synchronize(); - old_end_time = end_time; + old_end_time_ut = end_time_ut; page_length = descr->page_length; __sync_synchronize(); - end_time = descr->end_time; + end_time_ut = descr->end_time_ut; __sync_synchronize(); - } while ((end_time != old_end_time || (end_time & 1) != 0)); + } while ((end_time_ut != old_end_time_ut || (end_time_ut & 1) != 0)); - *end_timep = end_time; + *end_time_ut_p = end_time_ut; *page_lengthp = page_length; } else { - *end_timep = descr->end_time; + *end_time_ut_p = descr->end_time_ut; *page_lengthp = descr->page_length; } } /* The caller must hold a reference to the page and must have already set the new data */ -static inline void pg_cache_atomic_set_pg_info(struct rrdeng_page_descr *descr, usec_t end_time, uint32_t page_length) +static inline void pg_cache_atomic_set_pg_info(struct rrdeng_page_descr *descr, usec_t end_time_ut, uint32_t page_length) { - fatal_assert(!(end_time & 1)); + fatal_assert(!(end_time_ut & 1)); __sync_synchronize(); - descr->end_time |= 1; /* mark start of uncertainty period by adding 1 microsecond */ + descr->end_time_ut |= 1; /* mark start of uncertainty period by adding 1 microsecond */ __sync_synchronize(); descr->page_length = page_length; __sync_synchronize(); - descr->end_time = end_time; /* mark end of uncertainty period */ + descr->end_time_ut = end_time_ut; /* mark end of uncertainty period */ } #endif /* NETDATA_PAGECACHE_H */ diff --git a/database/engine/rrddiskprotocol.h b/database/engine/rrddiskprotocol.h index cb57385a4..5b4be9498 100644 --- a/database/engine/rrddiskprotocol.h +++ b/database/engine/rrddiskprotocol.h @@ -46,8 +46,8 @@ struct rrdeng_extent_page_descr { uint8_t uuid[UUID_SZ]; uint32_t page_length; - uint64_t start_time; - uint64_t end_time; + uint64_t start_time_ut; + uint64_t end_time_ut; } __attribute__ ((packed)); /* diff --git a/database/engine/rrdengine.c b/database/engine/rrdengine.c index 8b35051d8..e4cd37e98 100644 --- a/database/engine/rrdengine.c +++ b/database/engine/rrdengine.c @@ -30,7 +30,7 @@ void dbengine_page_free(void *page) { if (unlikely(db_engine_use_malloc)) freez(page); else - munmap(page, RRDENG_BLOCK_SIZE); + netdata_munmap(page, RRDENG_BLOCK_SIZE); } static void sanity_check(void) @@ -206,8 +206,8 @@ void read_cached_extent_cb(struct rrdengine_worker_config* wc, unsigned idx, str /* care, we don't hold the descriptor mutex */ if (!uuid_compare(*extent->pages[j]->id, *descr->id) && extent->pages[j]->page_length == descr->page_length && - extent->pages[j]->start_time == descr->start_time && - extent->pages[j]->end_time == descr->end_time) { + extent->pages[j]->start_time_ut == descr->start_time_ut && + extent->pages[j]->end_time_ut == descr->end_time_ut) { break; } page_offset += extent->pages[j]->page_length; @@ -272,11 +272,9 @@ static void fill_page_with_nulls(void *page, uint32_t page_length, uint8_t type) } } -void read_extent_cb(uv_fs_t* req) +static void do_extent_processing (struct rrdengine_worker_config *wc, struct extent_io_descriptor *xt_io_descr, bool read_failed) { - struct rrdengine_worker_config* wc = req->loop->data; struct rrdengine_instance *ctx = wc->ctx; - struct extent_io_descriptor *xt_io_descr; struct rrdeng_page_descr *descr; struct page_cache_descr *pg_cache_descr; int ret; @@ -289,21 +287,20 @@ void read_extent_cb(uv_fs_t* req) struct rrdeng_df_extent_trailer *trailer; uLong crc; - xt_io_descr = req->data; header = xt_io_descr->buf; payload_length = header->payload_length; count = header->number_of_pages; payload_offset = sizeof(*header) + sizeof(header->descr[0]) * count; trailer = xt_io_descr->buf + xt_io_descr->bytes - sizeof(*trailer); - if (req->result < 0) { + if (unlikely(read_failed)) { struct rrdengine_datafile *datafile = xt_io_descr->descr_array[0]->extent->datafile; ++ctx->stats.io_errors; rrd_stat_atomic_add(&global_io_errors, 1); have_read_error = 1; - error("%s: uv_fs_read - %s - extent at offset %"PRIu64"(%u) in datafile %u-%u.", __func__, - uv_strerror((int)req->result), xt_io_descr->pos, xt_io_descr->bytes, datafile->tier, datafile->fileno); + error("%s: uv_fs_read - extent at offset %"PRIu64"(%u) in datafile %u-%u.", __func__, xt_io_descr->pos, + xt_io_descr->bytes, datafile->tier, datafile->fileno); goto after_crc_check; } crc = crc32(0L, Z_NULL, 0); @@ -378,8 +375,8 @@ after_crc_check: /* care, we don't hold the descriptor mutex */ if (!uuid_compare(*(uuid_t *) header->descr[i].uuid, *descrj->id) && header->descr[i].page_length == descrj->page_length && - header->descr[i].start_time == descrj->start_time && - header->descr[i].end_time == descrj->end_time) { + header->descr[i].start_time_ut == descrj->start_time_ut && + header->descr[i].end_time_ut == descrj->end_time_ut) { descr = descrj; break; } @@ -387,7 +384,7 @@ after_crc_check: is_prefetched_page = 0; if (!descr) { /* This extent page has not been requested. Try populating it for locality (best effort). */ descr = pg_cache_lookup_unpopulated_and_lock(ctx, (uuid_t *)header->descr[i].uuid, - header->descr[i].start_time); + header->descr[i].start_time_ut); if (!descr) continue; /* Failed to reserve a suitable page */ is_prefetched_page = 1; @@ -421,11 +418,67 @@ after_crc_check: } if (xt_io_descr->completion) completion_mark_complete(xt_io_descr->completion); +} + +static void read_extent_cb(uv_fs_t *req) +{ + struct rrdengine_worker_config *wc = req->loop->data; + struct extent_io_descriptor *xt_io_descr; + + xt_io_descr = req->data; + do_extent_processing(wc, xt_io_descr, req->result < 0); uv_fs_req_cleanup(req); - free(xt_io_descr->buf); + posix_memfree(xt_io_descr->buf); freez(xt_io_descr); } +static void read_mmap_extent_cb(uv_work_t *req, int status __maybe_unused) +{ + struct rrdengine_worker_config *wc = req->loop->data; + struct rrdengine_instance *ctx = wc->ctx; + struct extent_io_descriptor *xt_io_descr; + xt_io_descr = req->data; + + if (likely(xt_io_descr->map_base)) { + do_extent_processing(wc, xt_io_descr, false); + munmap(xt_io_descr->map_base, xt_io_descr->map_length); + freez(xt_io_descr); + return; + } + + // MMAP failed, so do uv_fs_read + int ret = posix_memalign((void *)&xt_io_descr->buf, RRDFILE_ALIGNMENT, ALIGN_BYTES_CEILING(xt_io_descr->bytes)); + if (unlikely(ret)) { + fatal("posix_memalign:%s", strerror(ret)); + } + unsigned real_io_size = ALIGN_BYTES_CEILING( xt_io_descr->bytes); + xt_io_descr->iov = uv_buf_init((void *)xt_io_descr->buf, real_io_size); + xt_io_descr->req.data = xt_io_descr; + ret = uv_fs_read(req->loop, &xt_io_descr->req, xt_io_descr->file, &xt_io_descr->iov, 1, (unsigned) xt_io_descr->pos, read_extent_cb); + fatal_assert(-1 != ret); + ctx->stats.io_read_bytes += real_io_size; + ctx->stats.io_read_extent_bytes += real_io_size; +} + +static void do_mmap_read_extent(uv_work_t *req) +{ + struct extent_io_descriptor *xt_io_descr = (struct extent_io_descriptor * )req->data; + struct rrdengine_worker_config *wc = req->loop->data; + struct rrdengine_instance *ctx = wc->ctx; + + off_t map_start = ALIGN_BYTES_FLOOR(xt_io_descr->pos); + size_t length = ALIGN_BYTES_CEILING(xt_io_descr->pos + xt_io_descr->bytes) - map_start; + unsigned real_io_size = xt_io_descr->bytes; + + void *data = mmap(NULL, length, PROT_READ, MAP_SHARED, xt_io_descr->file, map_start); + if (likely(data != MAP_FAILED)) { + xt_io_descr->map_base = data; + xt_io_descr->map_length = length; + xt_io_descr->buf = data + (xt_io_descr->pos - map_start); + ctx->stats.io_read_bytes += real_io_size; + ctx->stats.io_read_extent_bytes += real_io_size; + } +} static void do_read_extent(struct rrdengine_worker_config* wc, struct rrdeng_page_descr **descr, @@ -435,8 +488,7 @@ static void do_read_extent(struct rrdengine_worker_config* wc, struct rrdengine_instance *ctx = wc->ctx; struct page_cache_descr *pg_cache_descr; int ret; - unsigned i, size_bytes, pos, real_io_size; -// uint32_t payload_length; + unsigned i, size_bytes, pos; struct extent_io_descriptor *xt_io_descr; struct rrdengine_datafile *datafile; struct extent_info *extent = descr[0]->extent; @@ -452,18 +504,17 @@ static void do_read_extent(struct rrdengine_worker_config* wc, rrdeng_page_descr_mutex_lock(ctx, descr[i]); pg_cache_descr = descr[i]->pg_cache_descr; pg_cache_descr->flags |= RRD_PAGE_READ_PENDING; -// payload_length = descr[i]->page_length; rrdeng_page_descr_mutex_unlock(ctx, descr[i]); - xt_io_descr->descr_array[i] = descr[i]; } xt_io_descr->descr_count = count; + xt_io_descr->file = datafile->file; xt_io_descr->bytes = size_bytes; xt_io_descr->pos = pos; - xt_io_descr->req.data = xt_io_descr; + xt_io_descr->req_worker.data = xt_io_descr; xt_io_descr->completion = NULL; - /* xt_io_descr->descr_commit_idx_array[0] */ xt_io_descr->release_descr = release_descr; + xt_io_descr->buf = NULL; xt_is_cached = !lookup_in_xt_cache(wc, extent, &xt_idx); if (xt_is_cached) { @@ -483,19 +534,10 @@ static void do_read_extent(struct rrdengine_worker_config* wc, } } - ret = posix_memalign((void *)&xt_io_descr->buf, RRDFILE_ALIGNMENT, ALIGN_BYTES_CEILING(size_bytes)); - if (unlikely(ret)) { - fatal("posix_memalign:%s", strerror(ret)); - /* freez(xt_io_descr); - return;*/ - } - real_io_size = ALIGN_BYTES_CEILING(size_bytes); - xt_io_descr->iov = uv_buf_init((void *)xt_io_descr->buf, real_io_size); - ret = uv_fs_read(wc->loop, &xt_io_descr->req, datafile->file, &xt_io_descr->iov, 1, pos, read_extent_cb); + ret = uv_queue_work(wc->loop, &xt_io_descr->req_worker, do_mmap_read_extent, read_mmap_extent_cb); fatal_assert(-1 != ret); - ctx->stats.io_read_bytes += real_io_size; + ++ctx->stats.io_read_requests; - ctx->stats.io_read_extent_bytes += real_io_size; ++ctx->stats.io_read_extents; ctx->stats.pg_cache_backfills += count; } @@ -696,7 +738,7 @@ void flush_pages_cb(uv_fs_t* req) if (xt_io_descr->completion) completion_mark_complete(xt_io_descr->completion); uv_fs_req_cleanup(req); - free(xt_io_descr->buf); + posix_memfree(xt_io_descr->buf); freez(xt_io_descr); uv_rwlock_wrlock(&pg_cache->committed_page_index.lock); @@ -820,8 +862,8 @@ static int do_flush_pages(struct rrdengine_worker_config* wc, int force, struct header->descr[i].type = descr->type; uuid_copy(*(uuid_t *)header->descr[i].uuid, *descr->id); header->descr[i].page_length = descr->page_length; - header->descr[i].start_time = descr->start_time; - header->descr[i].end_time = descr->end_time; + header->descr[i].start_time_ut = descr->start_time_ut; + header->descr[i].end_time_ut = descr->end_time_ut; pos += sizeof(header->descr[i]); } for (i = 0 ; i < count ; ++i) { @@ -922,7 +964,6 @@ static void after_delete_old_data(struct rrdengine_worker_config* wc) wc->now_deleting_files = NULL; wc->cleanup_thread_deleting_files = 0; - aclk_data_rotated(); rrdcontext_db_rotation(); /* interrupt event loop */ @@ -948,12 +989,12 @@ static void delete_old_data(void *arg) for (i = 0 ; i < count ; ++i) { descr = extent->pages[i]; can_delete_metric = pg_cache_punch_hole(ctx, descr, 0, 0, &metric_id); - if (unlikely(can_delete_metric && ctx->metalog_ctx->initialized)) { + if (unlikely(can_delete_metric)) { /* * If the metric is empty, has no active writers and if the metadata log has been initialized then * attempt to delete the corresponding netdata dimension. */ - metalog_delete_dimension_by_uuid(ctx->metalog_ctx, &metric_id); + metaqueue_delete_dimension_uuid(&metric_id); } } next = extent->next; @@ -1044,7 +1085,70 @@ static void rrdeng_cleanup_finished_threads(struct rrdengine_worker_config* wc) /* return 0 on success */ int init_rrd_files(struct rrdengine_instance *ctx) { - return init_data_files(ctx); + int ret = init_data_files(ctx); + + BUFFER *wb = buffer_create(1000); + size_t all_errors = 0; + usec_t now = now_realtime_usec(); + + if(ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].counter) { + buffer_sprintf(wb, "%s%zu pages had start time > end time (latest: %llu secs ago)" + , (all_errors)?", ":"" + , ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].counter + , (now - ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].latest_end_time_ut) / USEC_PER_SEC + ); + all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_FLIPPED_TIME].counter; + } + + if(ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].counter) { + buffer_sprintf(wb, "%s%zu pages had start time = end time with more than 1 entries (latest: %llu secs ago)" + , (all_errors)?", ":"" + , ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].counter + , (now - ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].latest_end_time_ut) / USEC_PER_SEC + ); + all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_EQUAL_TIME].counter; + } + + if(ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].counter) { + buffer_sprintf(wb, "%s%zu pages had zero points (latest: %llu secs ago)" + , (all_errors)?", ":"" + , ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].counter + , (now - ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].latest_end_time_ut) / USEC_PER_SEC + ); + all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_ZERO_ENTRIES].counter; + } + + if(ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].counter) { + buffer_sprintf(wb, "%s%zu pages had update every == 0 with entries > 1 (latest: %llu secs ago)" + , (all_errors)?", ":"" + , ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].counter + , (now - ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].latest_end_time_ut) / USEC_PER_SEC + ); + all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_UPDATE_ZERO].counter; + } + + if(ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].counter) { + buffer_sprintf(wb, "%s%zu pages had a different number of points compared to their timestamps (latest: %llu secs ago; these page have been loaded)" + , (all_errors)?", ":"" + , ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].counter + , (now - ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].latest_end_time_ut) / USEC_PER_SEC + ); + all_errors += ctx->load_errors[LOAD_ERRORS_PAGE_FLEXY_TIME].counter; + } + + if(ctx->load_errors[LOAD_ERRORS_DROPPED_EXTENT].counter) { + buffer_sprintf(wb, "%s%zu extents have been dropped because they didn't have any valid pages" + , (all_errors)?", ":"" + , ctx->load_errors[LOAD_ERRORS_DROPPED_EXTENT].counter + ); + all_errors += ctx->load_errors[LOAD_ERRORS_DROPPED_EXTENT].counter; + } + + if(all_errors) + info("DBENGINE: tier %d: %s", ctx->tier, buffer_tostring(wb)); + + buffer_free(wb); + return ret; } void finalize_rrd_files(struct rrdengine_instance *ctx) @@ -1139,10 +1243,6 @@ void timer_cb(uv_timer_t* handle) uv_stop(handle->loop); uv_update_time(handle->loop); - if (unlikely(!ctx->metalog_ctx->initialized)) { - worker_is_idle(); - return; /* Wait for the metadata log to initialize */ - } rrdeng_test_quota(wc); debug(D_RRDENGINE, "%s: timeout reached.", __func__); if (likely(!wc->now_deleting_files && !wc->now_invalidating_dirty_pages)) { @@ -1329,7 +1429,7 @@ void rrdeng_worker(void* arg) } /* cleanup operations of the event loop */ - info("Shutting down RRD engine event loop."); + info("Shutting down RRD engine event loop for tier %d", ctx->tier); /* * uv_async_send after uv_close does not seem to crash in linux at the moment, @@ -1344,7 +1444,7 @@ void rrdeng_worker(void* arg) wal_flush_transaction_buffer(wc); uv_run(loop, UV_RUN_DEFAULT); - info("Shutting down RRD engine event loop complete."); + info("Shutting down RRD engine event loop for tier %d complete", ctx->tier); /* TODO: don't let the API block by waiting to enqueue commands */ uv_cond_destroy(&wc->cmd_cond); /* uv_mutex_destroy(&wc->cmd_mutex); */ diff --git a/database/engine/rrdengine.h b/database/engine/rrdengine.h index 4b383b622..fedadbe86 100644 --- a/database/engine/rrdengine.h +++ b/database/engine/rrdengine.h @@ -17,7 +17,6 @@ #include "rrdenginelib.h" #include "datafile.h" #include "journalfile.h" -#include "metadata_log/metadatalog.h" #include "rrdengineapi.h" #include "pagecache.h" #include "rrdenglocking.h" @@ -37,29 +36,25 @@ struct rrdengine_instance; #define RRDENG_FILE_NUMBER_PRINT_TMPL "%1.1u-%10.10u" struct rrdeng_collect_handle { - struct rrdeng_metric_handle *metric_handle; + struct pg_cache_page_index *page_index; struct rrdeng_page_descr *descr; unsigned long page_correlation_id; - struct rrdengine_instance *ctx; // set to 1 when this dimension is not page aligned with the other dimensions in the chart uint8_t unaligned_page; }; struct rrdeng_query_handle { - struct rrdeng_metric_handle *metric_handle; struct rrdeng_page_descr *descr; struct rrdengine_instance *ctx; struct pg_cache_page_index *page_index; - time_t next_page_time; - time_t now; + time_t wanted_start_time_s; + time_t now_s; unsigned position; unsigned entries; - TIER_QUERY_FETCH tier_query_fetch_type; storage_number *page; - usec_t page_end_time; + usec_t page_end_time_ut; uint32_t page_length; - usec_t dt; - time_t dt_sec; + time_t dt_s; }; typedef enum { @@ -110,8 +105,12 @@ struct rrdeng_cmdqueue { struct extent_io_descriptor { uv_fs_t req; + uv_work_t req_worker; uv_buf_t iov; + uv_file file; void *buf; + void *map_base; + size_t map_length; uint64_t pos; unsigned bytes; struct completion *completion; @@ -230,8 +229,16 @@ extern rrdeng_stats_t global_flushing_pressure_page_deletions; /* number of dele #define SET_QUIESCE (1) /* set it before shutting down the instance, quiesce long running operations */ #define QUIESCED (2) /* is set after all threads have finished running */ +typedef enum { + LOAD_ERRORS_PAGE_FLIPPED_TIME = 0, + LOAD_ERRORS_PAGE_EQUAL_TIME = 1, + LOAD_ERRORS_PAGE_ZERO_ENTRIES = 2, + LOAD_ERRORS_PAGE_UPDATE_ZERO = 3, + LOAD_ERRORS_PAGE_FLEXY_TIME = 4, + LOAD_ERRORS_DROPPED_EXTENT = 5, +} INVALID_PAGE_ID; + struct rrdengine_instance { - struct metalog_instance *metalog_ctx; struct rrdengine_worker_config worker_config; struct completion rrdengine_completion; struct page_cache pg_cache; @@ -254,16 +261,21 @@ struct rrdengine_instance { uint8_t page_type; /* Default page type for this context */ struct rrdengine_statistics stats; + + struct { + size_t counter; + usec_t latest_end_time_ut; + } load_errors[6]; }; -extern void *dbengine_page_alloc(void); -extern void dbengine_page_free(void *page); +void *dbengine_page_alloc(void); +void dbengine_page_free(void *page); -extern int init_rrd_files(struct rrdengine_instance *ctx); -extern void finalize_rrd_files(struct rrdengine_instance *ctx); -extern void rrdeng_test_quota(struct rrdengine_worker_config* wc); -extern void rrdeng_worker(void* arg); -extern void rrdeng_enq_cmd(struct rrdengine_worker_config* wc, struct rrdeng_cmd *cmd); -extern struct rrdeng_cmd rrdeng_deq_cmd(struct rrdengine_worker_config* wc); +int init_rrd_files(struct rrdengine_instance *ctx); +void finalize_rrd_files(struct rrdengine_instance *ctx); +void rrdeng_test_quota(struct rrdengine_worker_config* wc); +void rrdeng_worker(void* arg); +void rrdeng_enq_cmd(struct rrdengine_worker_config* wc, struct rrdeng_cmd *cmd); +struct rrdeng_cmd rrdeng_deq_cmd(struct rrdengine_worker_config* wc); #endif /* NETDATA_RRDENGINE_H */ diff --git a/database/engine/rrdengineapi.c b/database/engine/rrdengineapi.c index f4da29407..27503baee 100755 --- a/database/engine/rrdengineapi.c +++ b/database/engine/rrdengineapi.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "rrdengine.h" +#include "../storage_engine.h" /* Default global database instance */ struct rrdengine_instance multidb_ctx_storage_tier0; @@ -35,14 +36,31 @@ int default_multidb_disk_quota_mb = 256; /* Default behaviour is to unblock data collection if the page cache is full of dirty pages by dropping metrics */ uint8_t rrdeng_drop_metrics_under_page_cache_pressure = 1; -static inline struct rrdengine_instance *get_rrdeng_ctx_from_host(RRDHOST *host, int tier) { - if(tier < 0 || tier >= RRD_STORAGE_TIERS) tier = 0; - if(!host->storage_instance[tier]) tier = 0; - return (struct rrdengine_instance *)host->storage_instance[tier]; +// ---------------------------------------------------------------------------- +// metrics groups + +STORAGE_METRICS_GROUP *rrdeng_metrics_group_get(STORAGE_INSTANCE *db_instance __maybe_unused, uuid_t *uuid __maybe_unused) { + return callocz(1, sizeof(struct pg_alignment)); +} + +void rrdeng_metrics_group_release(STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg) { + if(!smg) return; + + struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; + struct pg_alignment *pa = (struct pg_alignment *)smg; + struct page_cache *pg_cache = &ctx->pg_cache; + + uv_rwlock_rdlock(&pg_cache->metrics_index.lock); + if(pa->refcount == 0) + freez(pa); + uv_rwlock_rdunlock(&pg_cache->metrics_index.lock); } +// ---------------------------------------------------------------------------- +// metric handle for legacy dbs + /* This UUID is not unique across hosts */ -void rrdeng_generate_legacy_uuid(const char *dim_id, char *chart_id, uuid_t *ret_uuid) +void rrdeng_generate_legacy_uuid(const char *dim_id, const char *chart_id, uuid_t *ret_uuid) { EVP_MD_CTX *evpctx; unsigned char hash_value[EVP_MAX_MD_SIZE]; @@ -75,98 +93,136 @@ void rrdeng_convert_legacy_uuid_to_multihost(char machine_guid[GUID_LEN + 1], uu memcpy(ret_uuid, hash_value, sizeof(uuid_t)); } -struct rrdeng_metric_handle { - RRDDIM *rd; - struct rrdengine_instance *ctx; - uuid_t *rrdeng_uuid; // database engine metric UUID - struct pg_cache_page_index *page_index; -}; +STORAGE_METRIC_HANDLE *rrdeng_metric_get_legacy(STORAGE_INSTANCE *db_instance, const char *rd_id, const char *st_id, STORAGE_METRICS_GROUP *smg) { + uuid_t legacy_uuid; + rrdeng_generate_legacy_uuid(rd_id, st_id, &legacy_uuid); + return rrdeng_metric_get(db_instance, &legacy_uuid, smg); +} -void rrdeng_metric_free(STORAGE_METRIC_HANDLE *db_metric_handle) { - freez(db_metric_handle); +// ---------------------------------------------------------------------------- +// metric handle + +void rrdeng_metric_release(STORAGE_METRIC_HANDLE *db_metric_handle) { + struct pg_cache_page_index *page_index = (struct pg_cache_page_index *)db_metric_handle; + + unsigned short refcount = __atomic_sub_fetch(&page_index->refcount, 1, __ATOMIC_SEQ_CST); + if(refcount == 0 && page_index->alignment) { + __atomic_sub_fetch(&page_index->alignment->refcount, 1, __ATOMIC_SEQ_CST); + page_index->alignment = NULL; + } } -STORAGE_METRIC_HANDLE *rrdeng_metric_init(RRDDIM *rd, STORAGE_INSTANCE *db_instance) { +STORAGE_METRIC_HANDLE *rrdeng_metric_dup(STORAGE_METRIC_HANDLE *db_metric_handle) { + struct pg_cache_page_index *page_index = (struct pg_cache_page_index *)db_metric_handle; + __atomic_add_fetch(&page_index->refcount, 1, __ATOMIC_SEQ_CST); + return db_metric_handle; +} + +STORAGE_METRIC_HANDLE *rrdeng_metric_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid, STORAGE_METRICS_GROUP *smg) { struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; - struct page_cache *pg_cache; - uuid_t legacy_uuid; - uuid_t multihost_legacy_uuid; - Pvoid_t *PValue; + struct pg_alignment *pa = (struct pg_alignment *)smg; + struct page_cache *pg_cache = &ctx->pg_cache; struct pg_cache_page_index *page_index = NULL; - int is_multihost_child = 0; - RRDHOST *host = rd->rrdset->rrdhost; - - pg_cache = &ctx->pg_cache; - - rrdeng_generate_legacy_uuid(rd->id, rd->rrdset->id, &legacy_uuid); - if (host != localhost && is_storage_engine_shared((STORAGE_INSTANCE *)ctx)) - is_multihost_child = 1; uv_rwlock_rdlock(&pg_cache->metrics_index.lock); - PValue = JudyHSGet(pg_cache->metrics_index.JudyHS_array, &legacy_uuid, sizeof(uuid_t)); - if (likely(NULL != PValue)) { + Pvoid_t *PValue = JudyHSGet(pg_cache->metrics_index.JudyHS_array, uuid, sizeof(uuid_t)); + if (likely(NULL != PValue)) page_index = *PValue; - } uv_rwlock_rdunlock(&pg_cache->metrics_index.lock); - if (is_multihost_child || NULL == PValue) { - /* First time we see the legacy UUID or metric belongs to child host in multi-host DB. - * Drop legacy support, normal path */ - - uv_rwlock_rdlock(&pg_cache->metrics_index.lock); - PValue = JudyHSGet(pg_cache->metrics_index.JudyHS_array, &rd->metric_uuid, sizeof(uuid_t)); - if (likely(NULL != PValue)) { - page_index = *PValue; - } - uv_rwlock_rdunlock(&pg_cache->metrics_index.lock); - if (NULL == PValue) { - uv_rwlock_wrlock(&pg_cache->metrics_index.lock); - PValue = JudyHSIns(&pg_cache->metrics_index.JudyHS_array, &rd->metric_uuid, sizeof(uuid_t), PJE0); - fatal_assert(NULL == *PValue); /* TODO: figure out concurrency model */ - *PValue = page_index = create_page_index(&rd->metric_uuid); - page_index->prev = pg_cache->metrics_index.last_page_index; - pg_cache->metrics_index.last_page_index = page_index; - uv_rwlock_wrunlock(&pg_cache->metrics_index.lock); + + if (likely(page_index)) { + __atomic_add_fetch(&page_index->refcount, 1, __ATOMIC_SEQ_CST); + + if(pa) { + if(page_index->alignment && page_index->alignment != pa && page_index->writers > 0) + fatal("DBENGINE: page_index has a different alignment (page_index refcount is %u, writers is %u).", + page_index->refcount, page_index->writers); + + page_index->alignment = pa; + __atomic_add_fetch(&pa->refcount, 1, __ATOMIC_SEQ_CST); } - } else { - /* There are legacy UUIDs in the database, implement backward compatibility */ + } - rrdeng_convert_legacy_uuid_to_multihost(rd->rrdset->rrdhost->machine_guid, &legacy_uuid, - &multihost_legacy_uuid); + return (STORAGE_METRIC_HANDLE *)page_index; +} + +STORAGE_METRIC_HANDLE *rrdeng_metric_create(STORAGE_INSTANCE *db_instance, uuid_t *uuid, STORAGE_METRICS_GROUP *smg) { + internal_fatal(!db_instance, "DBENGINE: db_instance is NULL"); - int need_to_store = uuid_compare(rd->metric_uuid, multihost_legacy_uuid); + struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; + struct pg_alignment *pa = (struct pg_alignment *)smg; + struct pg_cache_page_index *page_index; + struct page_cache *pg_cache = &ctx->pg_cache; - uuid_copy(rd->metric_uuid, multihost_legacy_uuid); + uv_rwlock_wrlock(&pg_cache->metrics_index.lock); + Pvoid_t *PValue = JudyHSIns(&pg_cache->metrics_index.JudyHS_array, uuid, sizeof(uuid_t), PJE0); + fatal_assert(NULL == *PValue); /* TODO: figure out concurrency model */ + *PValue = page_index = create_page_index(uuid, ctx); + page_index->prev = pg_cache->metrics_index.last_page_index; + pg_cache->metrics_index.last_page_index = page_index; + page_index->alignment = pa; + page_index->refcount = 1; + if(pa) + pa->refcount++; + uv_rwlock_wrunlock(&pg_cache->metrics_index.lock); + + return (STORAGE_METRIC_HANDLE *)page_index; +} + +STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg) { + STORAGE_METRIC_HANDLE *db_metric_handle; + + db_metric_handle = rrdeng_metric_get(db_instance, &rd->metric_uuid, smg); + if(!db_metric_handle) { + db_metric_handle = rrdeng_metric_get_legacy(db_instance, rrddim_id(rd), rrdset_id(rd->rrdset), smg); + if(db_metric_handle) { + struct pg_cache_page_index *page_index = (struct pg_cache_page_index *)db_metric_handle; + uuid_copy(rd->metric_uuid, page_index->id); + } + } + if(!db_metric_handle) + db_metric_handle = rrdeng_metric_create(db_instance, &rd->metric_uuid, smg); - if (unlikely(need_to_store && !ctx->tier)) - (void)sql_store_dimension(&rd->metric_uuid, rd->rrdset->chart_uuid, rd->id, rd->name, rd->multiplier, rd->divisor, - rd->algorithm); +#ifdef NETDATA_INTERNAL_CHECKS + struct pg_cache_page_index *page_index = (struct pg_cache_page_index *)db_metric_handle; + if(uuid_compare(rd->metric_uuid, page_index->id) != 0) { + char uuid1[UUID_STR_LEN + 1]; + char uuid2[UUID_STR_LEN + 1]; + + uuid_unparse(rd->metric_uuid, uuid1); + uuid_unparse(page_index->id, uuid2); + fatal("DBENGINE: uuids do not match, asked for metric '%s', but got page_index of metric '%s'", uuid1, uuid2); } - struct rrdeng_metric_handle *mh = mallocz(sizeof(struct rrdeng_metric_handle)); - mh->rd = rd; - mh->ctx = ctx; - mh->rrdeng_uuid = &page_index->id; - mh->page_index = page_index; - return (STORAGE_METRIC_HANDLE *)mh; + struct rrdengine_instance *ctx = (struct rrdengine_instance *)db_instance; + if(page_index->ctx != ctx) + fatal("DBENGINE: mixed up rrdengine instances, asked for metric from %p, got from %p", ctx, page_index->ctx); +#endif + + return db_metric_handle; } + +// ---------------------------------------------------------------------------- +// collect ops + /* * Gets a handle for storing metrics to the database. * The handle must be released with rrdeng_store_metric_final(). */ -STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle) { - struct rrdeng_metric_handle *metric_handle = (struct rrdeng_metric_handle *)db_metric_handle; - +STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every) { + struct pg_cache_page_index *page_index = (struct pg_cache_page_index *)db_metric_handle; struct rrdeng_collect_handle *handle; - struct pg_cache_page_index *page_index; + + if(!page_index->alignment) + fatal("DBENGINE: metric group is required for collect operations"); handle = callocz(1, sizeof(struct rrdeng_collect_handle)); - handle->metric_handle = metric_handle; - handle->ctx = metric_handle->ctx; + handle->page_index = page_index; handle->descr = NULL; handle->unaligned_page = 0; + page_index->latest_update_every_s = update_every; - page_index = metric_handle->page_index; uv_rwlock_wrlock(&page_index->lock); ++page_index->writers; uv_rwlock_wrunlock(&page_index->lock); @@ -214,7 +270,7 @@ static int page_has_only_empty_metrics(struct rrdeng_page_descr *descr) void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_handle) { struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; // struct rrdeng_metric_handle *metric_handle = (struct rrdeng_metric_handle *)handle->metric_handle; - struct rrdengine_instance *ctx = handle->ctx; + struct rrdengine_instance *ctx = handle->page_index->ctx; struct rrdeng_page_descr *descr = handle->descr; if (unlikely(!ctx)) return; @@ -227,9 +283,7 @@ void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_h page_is_empty = page_has_only_empty_metrics(descr); if (page_is_empty) { - debug(D_RRDENGINE, "Page has empty metrics only, deleting:"); - if (unlikely(debug_flags & D_RRDENGINE)) - print_page_cache_descr(descr); + print_page_cache_descr(descr, "Page has empty metrics only, deleting", true); pg_cache_put(ctx, descr); pg_cache_punch_hole(ctx, descr, 1, 0, NULL); } else @@ -242,8 +296,8 @@ void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_h handle->descr = NULL; } -void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, - usec_t point_in_time, +static void rrdeng_store_metric_next_internal(STORAGE_COLLECT_HANDLE *collection_handle, + usec_t point_in_time_ut, NETDATA_DOUBLE n, NETDATA_DOUBLE min_value, NETDATA_DOUBLE max_value, @@ -252,11 +306,10 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, SN_FLAGS flags) { struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; - struct rrdeng_metric_handle *metric_handle = (struct rrdeng_metric_handle *)handle->metric_handle; - struct rrdengine_instance *ctx = handle->ctx; + struct pg_cache_page_index *page_index = handle->page_index; + struct rrdengine_instance *ctx = handle->page_index->ctx; struct page_cache *pg_cache = &ctx->pg_cache; struct rrdeng_page_descr *descr = handle->descr; - RRDDIM *rd = metric_handle->rd; void *page; uint8_t must_flush_unaligned_page = 0, perfect_page_alignment = 0; @@ -264,21 +317,33 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, if (descr) { /* Make alignment decisions */ - if (descr->page_length == rd->rrdset->rrddim_page_alignment) { +#ifdef NETDATA_INTERNAL_CHECKS + if(descr->end_time_ut + page_index->latest_update_every_s * USEC_PER_SEC != point_in_time_ut) { + char buffer[200 + 1]; + snprintfz(buffer, 200, + "metrics collected are %s, end_time_ut = %llu, point_in_time_ut = %llu, update_every = %u, delta = %llu", + (point_in_time_ut / USEC_PER_SEC - descr->end_time_ut / USEC_PER_SEC > page_index->latest_update_every_s)?"far apart":"not aligned", + descr->end_time_ut / USEC_PER_SEC, + point_in_time_ut / USEC_PER_SEC, + page_index->latest_update_every_s, + point_in_time_ut / USEC_PER_SEC - descr->end_time_ut / USEC_PER_SEC); + print_page_cache_descr(descr, buffer, false); + } +#endif + + if (descr->page_length == page_index->alignment->page_length) { /* this is the leading dimension that defines chart alignment */ perfect_page_alignment = 1; } /* is the metric far enough out of alignment with the others? */ - if (unlikely(descr->page_length + PAGE_POINT_SIZE_BYTES(descr) < rd->rrdset->rrddim_page_alignment)) { + if (unlikely(descr->page_length + PAGE_POINT_SIZE_BYTES(descr) < page_index->alignment->page_length)) { handle->unaligned_page = 1; - debug(D_RRDENGINE, "Metric page is not aligned with chart:"); - if (unlikely(debug_flags & D_RRDENGINE)) - print_page_cache_descr(descr); + print_page_cache_descr(descr, "Metric page is not aligned with chart", true); } if (unlikely(handle->unaligned_page && /* did the other metrics change page? */ - rd->rrdset->rrddim_page_alignment <= PAGE_POINT_SIZE_BYTES(descr))) { - debug(D_RRDENGINE, "Flushing unaligned metric page."); + page_index->alignment->page_length <= PAGE_POINT_SIZE_BYTES(descr))) { + print_page_cache_descr(descr, "must_flush_unaligned_page = 1", true); must_flush_unaligned_page = 1; handle->unaligned_page = 0; } @@ -286,16 +351,21 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, if (unlikely(NULL == descr || descr->page_length + PAGE_POINT_SIZE_BYTES(descr) > RRDENG_BLOCK_SIZE || must_flush_unaligned_page)) { - rrdeng_store_metric_flush_current_page(collection_handle); - page = rrdeng_create_page(ctx, &metric_handle->page_index->id, &descr); + if(descr) { + print_page_cache_descr(descr, "flushing metric", true); + rrdeng_store_metric_flush_current_page(collection_handle); + } + + page = rrdeng_create_page(ctx, &page_index->id, &descr); fatal_assert(page); + descr->update_every_s = page_index->latest_update_every_s; handle->descr = descr; handle->page_correlation_id = rrd_atomic_fetch_add(&pg_cache->committed_page_index.latest_corr_id, 1); - if (0 == rd->rrdset->rrddim_page_alignment) { + if (0 == page_index->alignment->page_length) { /* this is the leading dimension that defines chart alignment */ perfect_page_alignment = 1; } @@ -330,13 +400,13 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, break; } - pg_cache_atomic_set_pg_info(descr, point_in_time, descr->page_length + PAGE_POINT_SIZE_BYTES(descr)); + pg_cache_atomic_set_pg_info(descr, point_in_time_ut, descr->page_length + PAGE_POINT_SIZE_BYTES(descr)); if (perfect_page_alignment) - rd->rrdset->rrddim_page_alignment = descr->page_length; - if (unlikely(INVALID_TIME == descr->start_time)) { + page_index->alignment->page_length = descr->page_length; + if (unlikely(INVALID_TIME == descr->start_time_ut)) { unsigned long new_metric_API_producers, old_metric_API_max_producers, ret_metric_API_max_producers; - descr->start_time = point_in_time; + descr->start_time_ut = point_in_time_ut; new_metric_API_producers = rrd_atomic_add_fetch(&ctx->stats.metric_API_producers, 1); while (unlikely(new_metric_API_producers > (old_metric_API_max_producers = ctx->metric_API_max_producers))) { @@ -350,20 +420,111 @@ void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, } } - pg_cache_insert(ctx, metric_handle->page_index, descr); + pg_cache_insert(ctx, page_index, descr); } else { - pg_cache_add_new_metric_time(metric_handle->page_index, descr); + pg_cache_add_new_metric_time(page_index, descr); } + +// { +// unsigned char u[16] = { 0x0C, 0x0A, 0x40, 0xD6, 0x2A, 0x43, 0x4A, 0x7C, 0x95, 0xF7, 0xD1, 0x1E, 0x0C, 0x9E, 0x8A, 0xE7 }; +// if(uuid_compare(u, page_index->id) == 0) { +// char buffer[100]; +// snprintfz(buffer, 100, "store system.cpu, collect:%u, page_index first:%u, last:%u", +// (uint32_t)(point_in_time / USEC_PER_SEC), +// (uint32_t)(page_index->oldest_time / USEC_PER_SEC), +// (uint32_t)(page_index->latest_time / USEC_PER_SEC)); +// +// print_page_cache_descr(descr, buffer, false); +// } +// } } +void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, + usec_t point_in_time_ut, + NETDATA_DOUBLE n, + NETDATA_DOUBLE min_value, + NETDATA_DOUBLE max_value, + uint16_t count, + uint16_t anomaly_count, + SN_FLAGS flags) +{ + struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; + struct pg_cache_page_index *page_index = handle->page_index; + struct rrdeng_page_descr *descr = handle->descr; + + if(likely(descr)) { + usec_t last_point_in_time_ut = descr->end_time_ut; + usec_t update_every_ut = page_index->latest_update_every_s * USEC_PER_SEC; + size_t points_gap = (point_in_time_ut <= last_point_in_time_ut) ? + (size_t)0 : + (size_t)((point_in_time_ut - last_point_in_time_ut) / update_every_ut); + + if(unlikely(points_gap != 1)) { + if (unlikely(points_gap <= 0)) { + time_t now = now_realtime_sec(); + static __thread size_t counter = 0; + static __thread time_t last_time_logged = 0; + counter++; + + if(now - last_time_logged > 600) { + error("DBENGINE: collected point is in the past (repeated %zu times in the last %zu secs). Ignoring these data collection points.", + counter, (size_t)(last_time_logged?(now - last_time_logged):0)); + + last_time_logged = now; + counter = 0; + } + return; + } + + size_t point_size = PAGE_POINT_SIZE_BYTES(descr); + size_t page_size_in_points = RRDENG_BLOCK_SIZE / point_size; + size_t used_points = descr->page_length / point_size; + size_t remaining_points_in_page = page_size_in_points - used_points; + + bool new_point_is_aligned = true; + if(unlikely((point_in_time_ut - last_point_in_time_ut) / points_gap != update_every_ut)) + new_point_is_aligned = false; + + if(unlikely(points_gap > remaining_points_in_page || !new_point_is_aligned)) { +// char buffer[200]; +// snprintfz(buffer, 200, "data collection skipped %zu points, last stored point %llu, new point %llu, update every %d. Cutting page.", +// points_gap, last_point_in_time_ut / USEC_PER_SEC, point_in_time_ut / USEC_PER_SEC, page_index->latest_update_every_s); +// print_page_cache_descr(descr, buffer, false); + + rrdeng_store_metric_flush_current_page(collection_handle); + } + else { +// char buffer[200]; +// snprintfz(buffer, 200, "data collection skipped %zu points, last stored point %llu, new point %llu, update every %d. Filling the gap.", +// points_gap, last_point_in_time_ut / USEC_PER_SEC, point_in_time_ut / USEC_PER_SEC, page_index->latest_update_every_s); +// print_page_cache_descr(descr, buffer, false); + + // loop to fill the gap + usec_t step_ut = page_index->latest_update_every_s * USEC_PER_SEC; + usec_t last_point_filled_ut = last_point_in_time_ut + step_ut; + + while (last_point_filled_ut < point_in_time_ut) { + rrdeng_store_metric_next_internal( + collection_handle, last_point_filled_ut, NAN, NAN, NAN, + 1, 0, SN_EMPTY_SLOT); + + last_point_filled_ut += step_ut; + } + } + } + } + + rrdeng_store_metric_next_internal(collection_handle, point_in_time_ut, n, min_value, max_value, count, anomaly_count, flags); +} + + /* * Releases the database reference from the handle for storing metrics. * Returns 1 if it's safe to delete the dimension. */ int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle) { struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; - struct rrdeng_metric_handle *metric_handle = (struct rrdeng_metric_handle *)handle->metric_handle; - struct pg_cache_page_index *page_index = metric_handle->page_index; + struct pg_cache_page_index *page_index = handle->page_index; uint8_t can_delete_metric = 0; @@ -378,6 +539,18 @@ int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle) { return can_delete_metric; } +void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every) { + struct rrdeng_collect_handle *handle = (struct rrdeng_collect_handle *)collection_handle; + struct pg_cache_page_index *page_index = handle->page_index; + rrdeng_store_metric_flush_current_page(collection_handle); + uv_rwlock_rdlock(&page_index->lock); + page_index->latest_update_every_s = update_every; + uv_rwlock_rdunlock(&page_index->lock); +} + +// ---------------------------------------------------------------------------- +// query ops + //static inline uint32_t *pginfo_to_dt(struct rrdeng_page_info *page_info) //{ // return (uint32_t *)&page_info->scratch[0]; @@ -392,49 +565,45 @@ int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle) { * Gets a handle for loading metrics from the database. * The handle must be released with rrdeng_load_metric_final(). */ -void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_query_handle *rrdimm_handle, time_t start_time, time_t end_time, TIER_QUERY_FETCH tier_query_fetch_type) +void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *rrdimm_handle, time_t start_time_s, time_t end_time_s) { - struct rrdeng_metric_handle *metric_handle = (struct rrdeng_metric_handle *)db_metric_handle; - struct rrdengine_instance *ctx = metric_handle->ctx; - RRDDIM *rd = metric_handle->rd; + struct pg_cache_page_index *page_index = (struct pg_cache_page_index *)db_metric_handle; + struct rrdengine_instance *ctx = page_index->ctx; // fprintf(stderr, "%s: %s/%s start time %ld, end time %ld\n", __FUNCTION__ , rd->rrdset->name, rd->name, start_time, end_time); struct rrdeng_query_handle *handle; unsigned pages_nr; - rrdimm_handle->start_time = start_time; - rrdimm_handle->end_time = end_time; + if(!page_index->latest_update_every_s) + page_index->latest_update_every_s = default_rrd_update_every; + + rrdimm_handle->start_time_s = start_time_s; + rrdimm_handle->end_time_s = end_time_s; handle = callocz(1, sizeof(struct rrdeng_query_handle)); - handle->next_page_time = start_time; - handle->now = start_time; - handle->tier_query_fetch_type = tier_query_fetch_type; - // TODO we should store the dt of each page in each page - // this will produce wrong values for dt in case the user changes - // the update every of the charts or the tier grouping iterations - handle->dt_sec = get_tier_grouping(ctx->tier) * (time_t)rd->update_every; - handle->dt = handle->dt_sec * USEC_PER_SEC; + handle->wanted_start_time_s = start_time_s; + handle->now_s = start_time_s; handle->position = 0; handle->ctx = ctx; - handle->metric_handle = metric_handle; handle->descr = NULL; + handle->dt_s = page_index->latest_update_every_s; rrdimm_handle->handle = (STORAGE_QUERY_HANDLE *)handle; - pages_nr = pg_cache_preload(ctx, metric_handle->rrdeng_uuid, start_time * USEC_PER_SEC, end_time * USEC_PER_SEC, + pages_nr = pg_cache_preload(ctx, &page_index->id, start_time_s * USEC_PER_SEC, end_time_s * USEC_PER_SEC, NULL, &handle->page_index); if (unlikely(NULL == handle->page_index || 0 == pages_nr)) // there are no metrics to load - handle->next_page_time = INVALID_TIME; + handle->wanted_start_time_s = INVALID_TIME; } -static int rrdeng_load_page_next(struct rrddim_query_handle *rrdimm_handle) { +static int rrdeng_load_page_next(struct storage_engine_query_handle *rrdimm_handle, bool debug_this __maybe_unused) { struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrdimm_handle->handle; struct rrdengine_instance *ctx = handle->ctx; struct rrdeng_page_descr *descr = handle->descr; uint32_t page_length; - usec_t page_end_time; + usec_t page_end_time_ut; unsigned position; if (likely(descr)) { @@ -446,14 +615,15 @@ static int rrdeng_load_page_next(struct rrddim_query_handle *rrdimm_handle) { pg_cache_put(ctx, descr); handle->descr = NULL; - handle->next_page_time = (handle->page_end_time / USEC_PER_SEC) + 1; + handle->wanted_start_time_s = (time_t)((handle->page_end_time_ut / USEC_PER_SEC) + handle->dt_s); - if (unlikely(handle->next_page_time > rrdimm_handle->end_time)) + if (unlikely(handle->wanted_start_time_s > rrdimm_handle->end_time_s)) return 1; } - usec_t next_page_time = handle->next_page_time * USEC_PER_SEC; - descr = pg_cache_lookup_next(ctx, handle->page_index, &handle->page_index->id, next_page_time, rrdimm_handle->end_time * USEC_PER_SEC); + usec_t wanted_start_time_ut = handle->wanted_start_time_s * USEC_PER_SEC; + descr = pg_cache_lookup_next(ctx, handle->page_index, &handle->page_index->id, + wanted_start_time_ut, rrdimm_handle->end_time_s * USEC_PER_SEC); if (NULL == descr) return 1; @@ -462,77 +632,116 @@ static int rrdeng_load_page_next(struct rrddim_query_handle *rrdimm_handle) { #endif handle->descr = descr; - pg_cache_atomic_get_pg_info(descr, &page_end_time, &page_length); - if (unlikely(INVALID_TIME == descr->start_time || INVALID_TIME == page_end_time)) + pg_cache_atomic_get_pg_info(descr, &page_end_time_ut, &page_length); + if (unlikely(INVALID_TIME == descr->start_time_ut || INVALID_TIME == page_end_time_ut || 0 == descr->update_every_s)) { + error("DBENGINE: discarding invalid page descriptor (start_time = %llu, end_time = %llu, update_every_s = %d)", + descr->start_time_ut, page_end_time_ut, descr->update_every_s); return 1; + } - if (unlikely(descr->start_time != page_end_time && next_page_time > descr->start_time)) { + if (unlikely(descr->start_time_ut != page_end_time_ut && wanted_start_time_ut > descr->start_time_ut)) { // we're in the middle of the page somewhere unsigned entries = page_length / PAGE_POINT_SIZE_BYTES(descr); - position = ((uint64_t)(next_page_time - descr->start_time)) * (entries - 1) / - (page_end_time - descr->start_time); + position = ((uint64_t)(wanted_start_time_ut - descr->start_time_ut)) * (entries - 1) / + (page_end_time_ut - descr->start_time_ut); } else position = 0; - handle->page_end_time = page_end_time; + handle->page_end_time_ut = page_end_time_ut; handle->page_length = page_length; + handle->entries = page_length / PAGE_POINT_SIZE_BYTES(descr); handle->page = descr->pg_cache_descr->page; - usec_t entries = handle->entries = page_length / PAGE_POINT_SIZE_BYTES(descr); - if (likely(entries > 1)) - handle->dt = (page_end_time - descr->start_time) / (entries - 1); - else { - // TODO we should store the dt of each page in each page - // now we keep the dt of whatever was before - ; - } - - handle->dt_sec = (time_t)(handle->dt / USEC_PER_SEC); + handle->dt_s = descr->update_every_s; handle->position = position; +// if(debug_this) +// info("DBENGINE: rrdeng_load_page_next(), " +// "position:%d, " +// "start_time_ut:%llu, " +// "page_end_time_ut:%llu, " +// "next_page_time_ut:%llu, " +// "in_out:%s" +// , position +// , descr->start_time_ut +// , page_end_time_ut +// , +// wanted_start_time_ut, in_out?"true":"false" +// ); + return 0; } // Returns the metric and sets its timestamp into current_time // IT IS REQUIRED TO **ALWAYS** SET ALL RETURN VALUES (current_time, end_time, flags) // IT IS REQUIRED TO **ALWAYS** KEEP TRACK OF TIME, EVEN OUTSIDE THE DATABASE BOUNDARIES -STORAGE_POINT rrdeng_load_metric_next(struct rrddim_query_handle *rrdimm_handle) { - struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrdimm_handle->handle; +STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *rrddim_handle) { + struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrddim_handle->handle; // struct rrdeng_metric_handle *metric_handle = handle->metric_handle; - STORAGE_POINT sp; struct rrdeng_page_descr *descr = handle->descr; + time_t now = handle->now_s + handle->dt_s; + +// bool debug_this = false; +// { +// unsigned char u[16] = { 0x0C, 0x0A, 0x40, 0xD6, 0x2A, 0x43, 0x4A, 0x7C, 0x95, 0xF7, 0xD1, 0x1E, 0x0C, 0x9E, 0x8A, 0xE7 }; +// if(uuid_compare(u, handle->page_index->id) == 0) { +// char buffer[100]; +// snprintfz(buffer, 100, "load system.cpu, now:%u, dt:%u, position:%u page_index first:%u, last:%u", +// (uint32_t)(now), +// (uint32_t)(handle->dt_s), +// (uint32_t)(handle->position), +// (uint32_t)(handle->page_index->oldest_time / USEC_PER_SEC), +// (uint32_t)(handle->page_index->latest_time / USEC_PER_SEC)); +// +// print_page_cache_descr(descr, buffer, false); +// debug_this = true; +// } +// } + + STORAGE_POINT sp; unsigned position = handle->position + 1; - time_t now = handle->now + handle->dt_sec; storage_number_tier1_t tier1_value; - if (unlikely(INVALID_TIME == handle->next_page_time)) { - handle->next_page_time = INVALID_TIME; - handle->now = now; - storage_point_empty(sp, now - handle->dt_sec, now); + if (unlikely(INVALID_TIME == handle->wanted_start_time_s)) { + handle->wanted_start_time_s = INVALID_TIME; + handle->now_s = now; + storage_point_empty(sp, now - handle->dt_s, now); return sp; } if (unlikely(!descr || position >= handle->entries)) { // We need to get a new page - if(rrdeng_load_page_next(rrdimm_handle)) { + if(rrdeng_load_page_next(rrddim_handle, false)) { // next calls will not load any more metrics - handle->next_page_time = INVALID_TIME; - handle->now = now; - storage_point_empty(sp, now - handle->dt_sec, now); + handle->wanted_start_time_s = INVALID_TIME; + handle->now_s = now; + storage_point_empty(sp, now - handle->dt_s, now); return sp; } descr = handle->descr; position = handle->position; - now = (time_t)((descr->start_time + position * handle->dt) / USEC_PER_SEC); + now = (time_t)((descr->start_time_ut / USEC_PER_SEC) + position * descr->update_every_s); + +// if(debug_this) { +// char buffer[100]; +// snprintfz(buffer, 100, "NEW PAGE system.cpu, now:%u, dt:%u, position:%u page_index first:%u, last:%u", +// (uint32_t)(now), +// (uint32_t)(handle->dt_s), +// (uint32_t)(handle->position), +// (uint32_t)(handle->page_index->oldest_time / USEC_PER_SEC), +// (uint32_t)(handle->page_index->latest_time / USEC_PER_SEC)); +// +// print_page_cache_descr(descr, buffer, false); +// } } - sp.start_time = now - handle->dt_sec; + sp.start_time = now - handle->dt_s; sp.end_time = now; handle->position = position; - handle->now = now; + handle->now_s = now; switch(descr->type) { case PAGE_METRICS: { @@ -567,24 +776,32 @@ STORAGE_POINT rrdeng_load_metric_next(struct rrddim_query_handle *rrdimm_handle) break; } - if (unlikely(now >= rrdimm_handle->end_time)) { + if (unlikely(now >= rrddim_handle->end_time_s)) { // next calls will not load any more metrics - handle->next_page_time = INVALID_TIME; + handle->wanted_start_time_s = INVALID_TIME; } +// if(debug_this) +// info("DBENGINE: returning point: " +// "time from %ld to %ld // query from %ld to %ld // wanted_start_time_s %ld" +// , sp.start_time, sp.end_time +// , rrddim_handle->start_time_s, rrddim_handle->end_time_s +// , handle->wanted_start_time_s +// ); + return sp; } -int rrdeng_load_metric_is_finished(struct rrddim_query_handle *rrdimm_handle) +int rrdeng_load_metric_is_finished(struct storage_engine_query_handle *rrdimm_handle) { struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrdimm_handle->handle; - return (INVALID_TIME == handle->next_page_time); + return (INVALID_TIME == handle->wanted_start_time_s); } /* * Releases the database reference from the handle for loading metrics. */ -void rrdeng_load_metric_finalize(struct rrddim_query_handle *rrdimm_handle) +void rrdeng_load_metric_finalize(struct storage_engine_query_handle *rrdimm_handle) { struct rrdeng_query_handle *handle = (struct rrdeng_query_handle *)rrdimm_handle->handle; struct rrdengine_instance *ctx = handle->ctx; @@ -603,46 +820,12 @@ void rrdeng_load_metric_finalize(struct rrddim_query_handle *rrdimm_handle) } time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle) { - struct rrdeng_metric_handle *metric_handle = (struct rrdeng_metric_handle *)db_metric_handle; - - struct pg_cache_page_index *page_index = metric_handle->page_index; - return page_index->latest_time / USEC_PER_SEC; + struct pg_cache_page_index *page_index = (struct pg_cache_page_index *)db_metric_handle; + return (time_t)(page_index->latest_time_ut / USEC_PER_SEC); } time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle) { - struct rrdeng_metric_handle *metric_handle = (struct rrdeng_metric_handle *)db_metric_handle; - - struct pg_cache_page_index *page_index = metric_handle->page_index; - return page_index->oldest_time / USEC_PER_SEC; -} - -int rrdeng_metric_latest_time_by_uuid(uuid_t *dim_uuid, time_t *first_entry_t, time_t *last_entry_t, int tier) -{ - struct page_cache *pg_cache; - struct rrdengine_instance *ctx; - Pvoid_t *PValue; - struct pg_cache_page_index *page_index = NULL; - - ctx = get_rrdeng_ctx_from_host(localhost, tier); - if (unlikely(!ctx)) { - error("Failed to fetch multidb context"); - return 1; - } - pg_cache = &ctx->pg_cache; - - uv_rwlock_rdlock(&pg_cache->metrics_index.lock); - PValue = JudyHSGet(pg_cache->metrics_index.JudyHS_array, dim_uuid, sizeof(uuid_t)); - if (likely(NULL != PValue)) { - page_index = *PValue; - } - uv_rwlock_rdunlock(&pg_cache->metrics_index.lock); - - if (likely(page_index)) { - *first_entry_t = page_index->oldest_time / USEC_PER_SEC; - *last_entry_t = page_index->latest_time / USEC_PER_SEC; - return 0; - } - - return 1; + struct pg_cache_page_index *page_index = (struct pg_cache_page_index *)db_metric_handle; + return (time_t)(page_index->oldest_time_ut / USEC_PER_SEC); } int rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *si, uuid_t *dim_uuid, time_t *first_entry_t, time_t *last_entry_t) @@ -667,8 +850,8 @@ int rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *si, uuid_t *dim_uuid, time uv_rwlock_rdunlock(&pg_cache->metrics_index.lock); if (likely(page_index)) { - *first_entry_t = page_index->oldest_time / USEC_PER_SEC; - *last_entry_t = page_index->latest_time / USEC_PER_SEC; + *first_entry_t = page_index->oldest_time_ut / USEC_PER_SEC; + *last_entry_t = page_index->latest_time_ut / USEC_PER_SEC; return 0; } @@ -695,7 +878,7 @@ void *rrdeng_create_page(struct rrdengine_instance *ctx, uuid_t *id, struct rrde debug(D_RRDENGINE, "Created new page:"); if (unlikely(debug_flags & D_RRDENGINE)) - print_page_cache_descr(descr); + print_page_cache_descr(descr, "", true); rrdeng_page_descr_mutex_unlock(ctx, descr); *ret_descr = descr; return page; @@ -767,13 +950,13 @@ void *rrdeng_get_latest_page(struct rrdengine_instance *ctx, uuid_t *id, void ** } /* Gets a reference for the page */ -void *rrdeng_get_page(struct rrdengine_instance *ctx, uuid_t *id, usec_t point_in_time, void **handle) +void *rrdeng_get_page(struct rrdengine_instance *ctx, uuid_t *id, usec_t point_in_time_ut, void **handle) { struct rrdeng_page_descr *descr; struct page_cache_descr *pg_cache_descr; debug(D_RRDENGINE, "Reading existing page:"); - descr = pg_cache_lookup(ctx, NULL, id, point_in_time); + descr = pg_cache_lookup(ctx, NULL, id, point_in_time_ut); if (NULL == descr) { *handle = NULL; @@ -849,7 +1032,7 @@ void rrdeng_put_page(struct rrdengine_instance *ctx, void *handle) * Returns 0 on success, negative on error */ int rrdeng_init(RRDHOST *host, struct rrdengine_instance **ctxp, char *dbfiles_path, unsigned page_cache_mb, - unsigned disk_space_mb, int tier) { + unsigned disk_space_mb, size_t tier) { struct rrdengine_instance *ctx; int error; uint32_t max_open_files; @@ -897,7 +1080,6 @@ int rrdeng_init(RRDHOST *host, struct rrdengine_instance **ctxp, char *dbfiles_p ctx->drop_metrics_under_page_cache_pressure = rrdeng_drop_metrics_under_page_cache_pressure; ctx->metric_API_max_producers = 0; ctx->quiesce = NO_QUIESCE; - ctx->metalog_ctx = NULL; /* only set this after the metadata log has finished initializing */ ctx->host = host; memset(&ctx->worker_config, 0, sizeof(ctx->worker_config)); @@ -918,11 +1100,11 @@ int rrdeng_init(RRDHOST *host, struct rrdengine_instance **ctxp, char *dbfiles_p if (ctx->worker_config.error) { goto error_after_rrdeng_worker; } - error = metalog_init(ctx); - if (error) { - error("Failed to initialize metadata log file event loop."); - goto error_after_rrdeng_worker; - } +// error = metalog_init(ctx); +// if (error) { +// error("Failed to initialize metadata log file event loop."); +// goto error_after_rrdeng_worker; +// } return 0; @@ -1010,13 +1192,13 @@ RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx) { size_t points = descr->page_length / PAGE_POINT_SIZE_BYTES(descr); if(likely(points > 1)) - update_every_usec = (descr->end_time - descr->start_time) / (points - 1); + update_every_usec = (descr->end_time_ut - descr->start_time_ut) / (points - 1); else { update_every_usec = default_rrd_update_every * get_tier_grouping(ctx->tier) * USEC_PER_SEC; stats.single_point_pages++; } - time_t duration_secs = (time_t)((descr->end_time - descr->start_time + update_every_usec)/USEC_PER_SEC); + time_t duration_secs = (time_t)((descr->end_time_ut - descr->start_time_ut + update_every_usec)/USEC_PER_SEC); stats.extents_pages++; stats.pages_uncompressed_bytes += descr->page_length; @@ -1028,11 +1210,11 @@ RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx) { stats.page_types[descr->type].pages_duration_secs += duration_secs; stats.page_types[descr->type].points += points; - if(!stats.first_t || (descr->start_time - update_every_usec) < stats.first_t) - stats.first_t = (descr->start_time - update_every_usec) / USEC_PER_SEC; + if(!stats.first_t || (descr->start_time_ut - update_every_usec) < stats.first_t) + stats.first_t = (descr->start_time_ut - update_every_usec) / USEC_PER_SEC; - if(!stats.last_t || descr->end_time > stats.last_t) - stats.last_t = descr->end_time / USEC_PER_SEC; + if(!stats.last_t || descr->end_time_ut > stats.last_t) + stats.last_t = descr->end_time_ut / USEC_PER_SEC; } } } @@ -1072,7 +1254,7 @@ RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx) { } } - stats.sizeof_metric = struct_natural_alignment(sizeof(struct pg_cache_page_index)); + stats.sizeof_metric = struct_natural_alignment(sizeof(struct pg_cache_page_index) + sizeof(struct pg_alignment)); stats.sizeof_page = struct_natural_alignment(sizeof(struct rrdeng_page_descr)); stats.sizeof_datafile = struct_natural_alignment(sizeof(struct rrdengine_datafile)) + struct_natural_alignment(sizeof(struct rrdengine_journalfile)); stats.sizeof_page_in_cache = struct_natural_alignment(sizeof(struct page_cache_descr)); diff --git a/database/engine/rrdengineapi.h b/database/engine/rrdengineapi.h index 509aa48ca..85375044f 100644 --- a/database/engine/rrdengineapi.h +++ b/database/engine/rrdengineapi.h @@ -25,58 +25,63 @@ extern size_t page_type_size[]; #define PAGE_POINT_SIZE_BYTES(x) page_type_size[(x)->type] struct rrdeng_region_info { - time_t start_time; + time_t start_time_s; int update_every; unsigned points; }; -extern void *rrdeng_create_page(struct rrdengine_instance *ctx, uuid_t *id, struct rrdeng_page_descr **ret_descr); -extern void rrdeng_commit_page(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr, +void *rrdeng_create_page(struct rrdengine_instance *ctx, uuid_t *id, struct rrdeng_page_descr **ret_descr); +void rrdeng_commit_page(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr, Word_t page_correlation_id); -extern void *rrdeng_get_latest_page(struct rrdengine_instance *ctx, uuid_t *id, void **handle); -extern void *rrdeng_get_page(struct rrdengine_instance *ctx, uuid_t *id, usec_t point_in_time, void **handle); -extern void rrdeng_put_page(struct rrdengine_instance *ctx, void *handle); +void *rrdeng_get_latest_page(struct rrdengine_instance *ctx, uuid_t *id, void **handle); +void *rrdeng_get_page(struct rrdengine_instance *ctx, uuid_t *id, usec_t point_in_time_ut, void **handle); +void rrdeng_put_page(struct rrdengine_instance *ctx, void *handle); -extern void rrdeng_generate_legacy_uuid(const char *dim_id, char *chart_id, uuid_t *ret_uuid); -extern void rrdeng_convert_legacy_uuid_to_multihost(char machine_guid[GUID_LEN + 1], uuid_t *legacy_uuid, +void rrdeng_generate_legacy_uuid(const char *dim_id, const char *chart_id, uuid_t *ret_uuid); +void rrdeng_convert_legacy_uuid_to_multihost(char machine_guid[GUID_LEN + 1], uuid_t *legacy_uuid, uuid_t *ret_uuid); -extern STORAGE_METRIC_HANDLE *rrdeng_metric_init(RRDDIM *rd, STORAGE_INSTANCE *db_instance); -extern void rrdeng_metric_free(STORAGE_METRIC_HANDLE *db_metric_handle); +STORAGE_METRIC_HANDLE *rrdeng_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg); +STORAGE_METRIC_HANDLE *rrdeng_metric_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid, STORAGE_METRICS_GROUP *smg); +STORAGE_METRIC_HANDLE *rrdeng_metric_create(STORAGE_INSTANCE *db_instance, uuid_t *uuid, STORAGE_METRICS_GROUP *smg); +STORAGE_METRIC_HANDLE *rrdeng_metric_get_legacy(STORAGE_INSTANCE *db_instance, const char *rd_id, const char *st_id, STORAGE_METRICS_GROUP *smg); +void rrdeng_metric_release(STORAGE_METRIC_HANDLE *db_metric_handle); +STORAGE_METRIC_HANDLE *rrdeng_metric_dup(STORAGE_METRIC_HANDLE *db_metric_handle); -extern STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle); -extern void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_handle); -extern void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time, NETDATA_DOUBLE n, +STORAGE_COLLECT_HANDLE *rrdeng_store_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every); +void rrdeng_store_metric_flush_current_page(STORAGE_COLLECT_HANDLE *collection_handle); +void rrdeng_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every); +void rrdeng_store_metric_next(STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time_ut, NETDATA_DOUBLE n, NETDATA_DOUBLE min_value, NETDATA_DOUBLE max_value, uint16_t count, uint16_t anomaly_count, SN_FLAGS flags); -extern int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle); +int rrdeng_store_metric_finalize(STORAGE_COLLECT_HANDLE *collection_handle); -extern unsigned rrdeng_variable_step_boundaries(RRDSET *st, time_t start_time, time_t end_time, - struct rrdeng_region_info **region_info_arrayp, unsigned *max_intervalp, struct context_param *context_param_list); +void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *rrdimm_handle, + time_t start_time_s, time_t end_time_s); +STORAGE_POINT rrdeng_load_metric_next(struct storage_engine_query_handle *rrddim_handle); -extern void rrdeng_load_metric_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_query_handle *rrdimm_handle, - time_t start_time, time_t end_time, TIER_QUERY_FETCH tier_query_fetch_type); -extern STORAGE_POINT rrdeng_load_metric_next(struct rrddim_query_handle *rrdimm_handle); -extern int rrdeng_load_metric_is_finished(struct rrddim_query_handle *rrdimm_handle); -extern void rrdeng_load_metric_finalize(struct rrddim_query_handle *rrdimm_handle); -extern time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle); -extern time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle); +int rrdeng_load_metric_is_finished(struct storage_engine_query_handle *rrdimm_handle); +void rrdeng_load_metric_finalize(struct storage_engine_query_handle *rrdimm_handle); +time_t rrdeng_metric_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle); +time_t rrdeng_metric_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle); -extern void rrdeng_get_37_statistics(struct rrdengine_instance *ctx, unsigned long long *array); +void rrdeng_get_37_statistics(struct rrdengine_instance *ctx, unsigned long long *array); /* must call once before using anything */ -extern int rrdeng_init(RRDHOST *host, struct rrdengine_instance **ctxp, char *dbfiles_path, unsigned page_cache_mb, - unsigned disk_space_mb, int tier); +int rrdeng_init(RRDHOST *host, struct rrdengine_instance **ctxp, char *dbfiles_path, unsigned page_cache_mb, + unsigned disk_space_mb, size_t tier); -extern int rrdeng_exit(struct rrdengine_instance *ctx); -extern void rrdeng_prepare_exit(struct rrdengine_instance *ctx); -extern int rrdeng_metric_latest_time_by_uuid(uuid_t *dim_uuid, time_t *first_entry_t, time_t *last_entry_t, int tier); -extern int rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *si, uuid_t *dim_uuid, time_t *first_entry_t, time_t *last_entry_t); +int rrdeng_exit(struct rrdengine_instance *ctx); +void rrdeng_prepare_exit(struct rrdengine_instance *ctx); +int rrdeng_metric_retention_by_uuid(STORAGE_INSTANCE *si, uuid_t *dim_uuid, time_t *first_entry_t, time_t *last_entry_t); + +extern STORAGE_METRICS_GROUP *rrdeng_metrics_group_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid); +extern void rrdeng_metrics_group_release(STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg); typedef struct rrdengine_size_statistics { size_t default_granularity_secs; @@ -134,6 +139,6 @@ typedef struct rrdengine_size_statistics { double average_page_size_bytes; } RRDENG_SIZE_STATS; -extern RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx); +RRDENG_SIZE_STATS rrdeng_size_statistics(struct rrdengine_instance *ctx); #endif /* NETDATA_RRDENGINEAPI_H */ diff --git a/database/engine/rrdenginelib.c b/database/engine/rrdenginelib.c index 287b86be8..58bd9c437 100644 --- a/database/engine/rrdenginelib.c +++ b/database/engine/rrdenginelib.c @@ -4,28 +4,45 @@ #define BUFSIZE (512) /* Caller must hold descriptor lock */ -void print_page_cache_descr(struct rrdeng_page_descr *descr) +void print_page_cache_descr(struct rrdeng_page_descr *descr, const char *msg, bool log_debug) { - struct page_cache_descr *pg_cache_descr = descr->pg_cache_descr; - char uuid_str[UUID_STR_LEN]; - char str[BUFSIZE + 1]; - int pos = 0; + if(log_debug && !(debug_flags & D_RRDENGINE)) + return; - uuid_unparse_lower(*descr->id, uuid_str); - pos += snprintfz(str, BUFSIZE - pos, "page(%p) id=%s\n" - "--->len:%"PRIu32" time:%"PRIu64"->%"PRIu64" xt_offset:", - pg_cache_descr->page, uuid_str, - descr->page_length, - (uint64_t)descr->start_time, - (uint64_t)descr->end_time); - if (!descr->extent) { - pos += snprintfz(str + pos, BUFSIZE - pos, "N/A"); - } else { - pos += snprintfz(str + pos, BUFSIZE - pos, "%"PRIu64, descr->extent->offset); + BUFFER *wb = buffer_create(512); + + if(!descr) { + buffer_sprintf(wb, "DBENGINE: %s : descr is NULL", msg); } + else { + struct page_cache_descr *pg_cache_descr = descr->pg_cache_descr; + char uuid_str[UUID_STR_LEN]; + + uuid_unparse_lower(*descr->id, uuid_str); + buffer_sprintf(wb, "DBENGINE: %s : page(%p) metric:%s, len:%"PRIu32", time:%"PRIu64"->%"PRIu64", update_every:%u, type:%u, xt_offset:", + msg, + pg_cache_descr->page, uuid_str, + descr->page_length, + (uint64_t)descr->start_time_ut, + (uint64_t)descr->end_time_ut, + (uint32_t)descr->update_every_s, + (uint32_t)descr->type + ); + if (!descr->extent) { + buffer_strcat(wb, "N/A"); + } else { + buffer_sprintf(wb, "%"PRIu64, descr->extent->offset); + } + + buffer_sprintf(wb, ", flags:0x%2.2lX refcnt:%u", pg_cache_descr->flags, pg_cache_descr->refcnt); + } + + if(log_debug) + debug(D_RRDENGINE, "%s", buffer_tostring(wb)); + else + internal_error(true, "%s", buffer_tostring(wb)); - snprintfz(str + pos, BUFSIZE - pos, " flags:0x%2.2lX refcnt:%u\n\n", pg_cache_descr->flags, pg_cache_descr->refcnt); - debug(D_RRDENGINE, "%s", str); + buffer_free(wb); } void print_page_descr(struct rrdeng_page_descr *descr) @@ -39,8 +56,8 @@ void print_page_descr(struct rrdeng_page_descr *descr) "--->len:%"PRIu32" time:%"PRIu64"->%"PRIu64" xt_offset:", uuid_str, descr->page_length, - (uint64_t)descr->start_time, - (uint64_t)descr->end_time); + (uint64_t)descr->start_time_ut, + (uint64_t)descr->end_time_ut); if (!descr->extent) { pos += snprintfz(str + pos, BUFSIZE - pos, "N/A"); } else { diff --git a/database/engine/rrdenginelib.h b/database/engine/rrdenginelib.h index 32eebf103..6b1a15fb1 100644 --- a/database/engine/rrdenginelib.h +++ b/database/engine/rrdenginelib.h @@ -83,10 +83,10 @@ static inline void crc32set(void *crcp, uLong crc) *(uint32_t *)crcp = crc; } -extern void print_page_cache_descr(struct rrdeng_page_descr *page_cache_descr); -extern void print_page_descr(struct rrdeng_page_descr *descr); -extern int check_file_properties(uv_file file, uint64_t *file_size, size_t min_size); -extern int open_file_for_io(char *path, int flags, uv_file *file, int direct); +void print_page_cache_descr(struct rrdeng_page_descr *descr, const char *msg, bool log_debug); +void print_page_descr(struct rrdeng_page_descr *descr); +int check_file_properties(uv_file file, uint64_t *file_size, size_t min_size); +int open_file_for_io(char *path, int flags, uv_file *file, int direct); static inline int open_file_direct_io(char *path, int flags, uv_file *file) { return open_file_for_io(path, flags, file, 1); @@ -95,8 +95,8 @@ static inline int open_file_buffered_io(char *path, int flags, uv_file *file) { return open_file_for_io(path, flags, file, 0); } -extern char *get_rrdeng_statistics(struct rrdengine_instance *ctx, char *str, size_t size); -extern int compute_multidb_diskspace(); -extern int is_legacy_child(const char *machine_guid); +char *get_rrdeng_statistics(struct rrdengine_instance *ctx, char *str, size_t size); +int compute_multidb_diskspace(); +int is_legacy_child(const char *machine_guid); #endif /* NETDATA_RRDENGINELIB_H */ diff --git a/database/engine/rrdenglocking.h b/database/engine/rrdenglocking.h index 127ddc90c..078eab38b 100644 --- a/database/engine/rrdenglocking.h +++ b/database/engine/rrdenglocking.h @@ -8,10 +8,10 @@ /* Forward declarations */ struct page_cache_descr; -extern struct page_cache_descr *rrdeng_create_pg_cache_descr(struct rrdengine_instance *ctx); -extern void rrdeng_destroy_pg_cache_descr(struct rrdengine_instance *ctx, struct page_cache_descr *pg_cache_descr); -extern void rrdeng_page_descr_mutex_lock(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); -extern void rrdeng_page_descr_mutex_unlock(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); -extern void rrdeng_try_deallocate_pg_cache_descr(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); +struct page_cache_descr *rrdeng_create_pg_cache_descr(struct rrdengine_instance *ctx); +void rrdeng_destroy_pg_cache_descr(struct rrdengine_instance *ctx, struct page_cache_descr *pg_cache_descr); +void rrdeng_page_descr_mutex_lock(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); +void rrdeng_page_descr_mutex_unlock(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); +void rrdeng_try_deallocate_pg_cache_descr(struct rrdengine_instance *ctx, struct rrdeng_page_descr *descr); #endif /* NETDATA_RRDENGLOCKING_H */ \ No newline at end of file diff --git a/database/ram/rrddim_mem.c b/database/ram/rrddim_mem.c index 3226d3c0d..43f32350b 100644 --- a/database/ram/rrddim_mem.c +++ b/database/ram/rrddim_mem.c @@ -1,22 +1,76 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "rrddim_mem.h" +#include "Judy.h" + +static Pvoid_t rrddim_JudyHS_array = NULL; +static netdata_rwlock_t rrddim_JudyHS_rwlock = NETDATA_RWLOCK_INITIALIZER; + +// ---------------------------------------------------------------------------- +// metrics groups + +STORAGE_METRICS_GROUP *rrddim_metrics_group_get(STORAGE_INSTANCE *db_instance __maybe_unused, uuid_t *uuid __maybe_unused) { + return NULL; +} + +void rrddim_metrics_group_release(STORAGE_INSTANCE *db_instance __maybe_unused, STORAGE_METRICS_GROUP *smg __maybe_unused) { + // if(!smg) return; // smg may be NULL + ; +} // ---------------------------------------------------------------------------- // RRDDIM legacy data collection functions -STORAGE_METRIC_HANDLE *rrddim_metric_init(RRDDIM *rd, STORAGE_INSTANCE *db_instance __maybe_unused) { +STORAGE_METRIC_HANDLE * +rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *db_instance __maybe_unused, STORAGE_METRICS_GROUP *smg __maybe_unused) { + STORAGE_METRIC_HANDLE *t = rrddim_metric_get(db_instance, &rd->metric_uuid, smg); + if(!t) { + netdata_rwlock_wrlock(&rrddim_JudyHS_rwlock); + Pvoid_t *PValue = JudyHSIns(&rrddim_JudyHS_array, &rd->metric_uuid, sizeof(uuid_t), PJE0); + fatal_assert(NULL == *PValue); + *PValue = rd; + t = (STORAGE_METRIC_HANDLE *)rd; + netdata_rwlock_unlock(&rrddim_JudyHS_rwlock); + } + + if((RRDDIM *)t != rd) + fatal("RRDDIM_MEM: incorrect pointer returned from index."); + return (STORAGE_METRIC_HANDLE *)rd; } -void rrddim_metric_free(STORAGE_METRIC_HANDLE *db_metric_handle __maybe_unused) { - ; +STORAGE_METRIC_HANDLE * +rrddim_metric_get(STORAGE_INSTANCE *db_instance __maybe_unused, uuid_t *uuid, STORAGE_METRICS_GROUP *smg __maybe_unused) { + RRDDIM *rd = NULL; + netdata_rwlock_rdlock(&rrddim_JudyHS_rwlock); + Pvoid_t *PValue = JudyHSGet(rrddim_JudyHS_array, uuid, sizeof(uuid_t)); + if (likely(NULL != PValue)) + rd = *PValue; + netdata_rwlock_unlock(&rrddim_JudyHS_rwlock); + + return (STORAGE_METRIC_HANDLE *)rd; +} + +STORAGE_METRIC_HANDLE *rrddim_metric_dup(STORAGE_METRIC_HANDLE *db_metric_handle) { + return db_metric_handle; +} + +void rrddim_metric_release(STORAGE_METRIC_HANDLE *db_metric_handle __maybe_unused) { + RRDDIM *rd = (RRDDIM *)db_metric_handle; + + netdata_rwlock_wrlock(&rrddim_JudyHS_rwlock); + JudyHSDel(&rrddim_JudyHS_array, &rd->metric_uuid, sizeof(uuid_t), PJE0); + netdata_rwlock_unlock(&rrddim_JudyHS_rwlock); +} + +void rrddim_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every __maybe_unused) { + rrddim_store_metric_flush(collection_handle); } -STORAGE_COLLECT_HANDLE *rrddim_collect_init(STORAGE_METRIC_HANDLE *db_metric_handle) { +STORAGE_COLLECT_HANDLE *rrddim_collect_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every __maybe_unused) { RRDDIM *rd = (RRDDIM *)db_metric_handle; rd->db[rd->rrdset->current_entry] = pack_storage_number(NAN, SN_FLAG_NONE); - struct mem_collect_handle *ch = calloc(1, sizeof(struct mem_collect_handle)); + struct mem_collect_handle *ch = callocz(1, sizeof(struct mem_collect_handle)); ch->rd = rd; return (STORAGE_COLLECT_HANDLE *)ch; } @@ -41,12 +95,15 @@ void rrddim_collect_store_metric(STORAGE_COLLECT_HANDLE *collection_handle, usec void rrddim_store_metric_flush(STORAGE_COLLECT_HANDLE *collection_handle) { struct mem_collect_handle *ch = (struct mem_collect_handle *)collection_handle; + RRDDIM *rd = ch->rd; - memset(rd->db, 0, rd->entries * sizeof(storage_number)); + for(int i = 0; i < rd->rrdset->entries ;i++) + rd->db[i] = SN_EMPTY_SLOT; + } int rrddim_collect_finalize(STORAGE_COLLECT_HANDLE *collection_handle) { - free(collection_handle); + freez(collection_handle); return 0; } @@ -91,7 +148,7 @@ static inline size_t rrddim_time2slot(RRDDIM *rd, time_t t) { } if(unlikely(ret >= entries)) { - error("INTERNAL ERROR: rrddim_time2slot() on %s returns values outside entries", rd->name); + error("INTERNAL ERROR: rrddim_time2slot() on %s returns values outside entries", rrddim_name(rd)); ret = entries - 1; } @@ -119,12 +176,12 @@ static inline time_t rrddim_slot2time(RRDDIM *rd, size_t slot) { ret = last_entry_t - (time_t)(update_every * (last_slot - slot)); if(unlikely(ret < first_entry_t)) { - error("INTERNAL ERROR: rrddim_slot2time() on %s returns time too far in the past", rd->name); + error("INTERNAL ERROR: rrddim_slot2time() on %s returns time too far in the past", rrddim_name(rd)); ret = first_entry_t; } if(unlikely(ret > last_entry_t)) { - error("INTERNAL ERROR: rrddim_slot2time() on %s returns time into the future", rd->name); + error("INTERNAL ERROR: rrddim_slot2time() on %s returns time into the future", rrddim_name(rd)); ret = last_entry_t; } @@ -134,15 +191,13 @@ static inline time_t rrddim_slot2time(RRDDIM *rd, size_t slot) { // ---------------------------------------------------------------------------- // RRDDIM legacy database query functions -void rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_query_handle *handle, time_t start_time, time_t end_time, TIER_QUERY_FETCH tier_query_fetch_type) { - UNUSED(tier_query_fetch_type); - +void rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *handle, time_t start_time, time_t end_time) { RRDDIM *rd = (RRDDIM *)db_metric_handle; handle->rd = rd; - handle->start_time = start_time; - handle->end_time = end_time; - struct mem_query_handle* h = calloc(1, sizeof(struct mem_query_handle)); + handle->start_time_s = start_time; + handle->end_time_s = end_time; + struct mem_query_handle* h = mallocz(sizeof(struct mem_query_handle)); h->slot = rrddim_time2slot(rd, start_time); h->last_slot = rrddim_time2slot(rd, end_time); h->dt = rd->rrdset->update_every; @@ -159,7 +214,7 @@ void rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_qu // Returns the metric and sets its timestamp into current_time // IT IS REQUIRED TO **ALWAYS** SET ALL RETURN VALUES (current_time, end_time, flags) // IT IS REQUIRED TO **ALWAYS** KEEP TRACK OF TIME, EVEN OUTSIDE THE DATABASE BOUNDARIES -STORAGE_POINT rrddim_query_next_metric(struct rrddim_query_handle *handle) { +STORAGE_POINT rrddim_query_next_metric(struct storage_engine_query_handle *handle) { RRDDIM *rd = handle->rd; struct mem_query_handle* h = (struct mem_query_handle*)handle->handle; size_t entries = rd->rrdset->entries; @@ -198,15 +253,15 @@ STORAGE_POINT rrddim_query_next_metric(struct rrddim_query_handle *handle) { return sp; } -int rrddim_query_is_finished(struct rrddim_query_handle *handle) { +int rrddim_query_is_finished(struct storage_engine_query_handle *handle) { struct mem_query_handle* h = (struct mem_query_handle*)handle->handle; - return (h->next_timestamp > handle->end_time); + return (h->next_timestamp > handle->end_time_s); } -void rrddim_query_finalize(struct rrddim_query_handle *handle) { +void rrddim_query_finalize(struct storage_engine_query_handle *handle) { #ifdef NETDATA_INTERNAL_CHECKS if(!rrddim_query_is_finished(handle)) - error("QUERY: query for chart '%s' dimension '%s' has been stopped unfinished", handle->rd->rrdset->id, handle->rd->name); + error("QUERY: query for chart '%s' dimension '%s' has been stopped unfinished", rrdset_id(handle->rd->rrdset), rrddim_name(handle->rd)); #endif freez(handle->handle); } diff --git a/database/ram/rrddim_mem.h b/database/ram/rrddim_mem.h index 400bdd0c2..297388f51 100644 --- a/database/ram/rrddim_mem.h +++ b/database/ram/rrddim_mem.h @@ -20,24 +20,30 @@ struct mem_query_handle { size_t last_slot; }; -extern STORAGE_METRIC_HANDLE *rrddim_metric_init(RRDDIM *rd, STORAGE_INSTANCE *db_instance); -extern void rrddim_metric_free(STORAGE_METRIC_HANDLE *db_metric_handle); +STORAGE_METRIC_HANDLE *rrddim_metric_get_or_create(RRDDIM *rd, STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg); +STORAGE_METRIC_HANDLE *rrddim_metric_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid, STORAGE_METRICS_GROUP *smg); +STORAGE_METRIC_HANDLE *rrddim_metric_dup(STORAGE_METRIC_HANDLE *db_metric_handle); +void rrddim_metric_release(STORAGE_METRIC_HANDLE *db_metric_handle); -extern STORAGE_COLLECT_HANDLE *rrddim_collect_init(STORAGE_METRIC_HANDLE *db_metric_handle); -extern void rrddim_collect_store_metric(STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time, NETDATA_DOUBLE number, +STORAGE_METRICS_GROUP *rrddim_metrics_group_get(STORAGE_INSTANCE *db_instance, uuid_t *uuid); +void rrddim_metrics_group_release(STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *smg); + +STORAGE_COLLECT_HANDLE *rrddim_collect_init(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every); +void rrddim_store_metric_change_collection_frequency(STORAGE_COLLECT_HANDLE *collection_handle, int update_every); +void rrddim_collect_store_metric(STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time, NETDATA_DOUBLE number, NETDATA_DOUBLE min_value, NETDATA_DOUBLE max_value, uint16_t count, uint16_t anomaly_count, SN_FLAGS flags); -extern void rrddim_store_metric_flush(STORAGE_COLLECT_HANDLE *collection_handle); -extern int rrddim_collect_finalize(STORAGE_COLLECT_HANDLE *collection_handle); - -extern void rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_query_handle *handle, time_t start_time, time_t end_time, TIER_QUERY_FETCH tier_query_fetch_type); -extern STORAGE_POINT rrddim_query_next_metric(struct rrddim_query_handle *handle); -extern int rrddim_query_is_finished(struct rrddim_query_handle *handle); -extern void rrddim_query_finalize(struct rrddim_query_handle *handle); -extern time_t rrddim_query_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle); -extern time_t rrddim_query_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle); +void rrddim_store_metric_flush(STORAGE_COLLECT_HANDLE *collection_handle); +int rrddim_collect_finalize(STORAGE_COLLECT_HANDLE *collection_handle); + +void rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *handle, time_t start_time, time_t end_time); +STORAGE_POINT rrddim_query_next_metric(struct storage_engine_query_handle *handle); +int rrddim_query_is_finished(struct storage_engine_query_handle *handle); +void rrddim_query_finalize(struct storage_engine_query_handle *handle); +time_t rrddim_query_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle); +time_t rrddim_query_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle); #endif diff --git a/database/rrd.c b/database/rrd.c index f91039ea5..df364419e 100644 --- a/database/rrd.c +++ b/database/rrd.c @@ -154,3 +154,15 @@ char *rrdset_cache_dir(RRDHOST *host, const char *id) { return ret; } +// ---------------------------------------------------------------------------- +// RRD - string management + +STRING *rrd_string_strdupz(const char *s) { + if(unlikely(!s || !*s)) return string_strdupz(s); + + char *tmp = strdupz(s); + json_fix_string(tmp); + STRING *ret = string_strdupz(tmp); + freez(tmp); + return ret; +} diff --git a/database/rrd.h b/database/rrd.h index 605ff50bc..f071ee254 100644 --- a/database/rrd.h +++ b/database/rrd.h @@ -11,26 +11,40 @@ extern "C" { // to enable type checking at compile time typedef struct storage_instance STORAGE_INSTANCE; typedef struct storage_metric_handle STORAGE_METRIC_HANDLE; +typedef struct storage_alignment STORAGE_METRICS_GROUP; // forward typedefs typedef struct rrdhost RRDHOST; typedef struct rrddim RRDDIM; typedef struct rrdset RRDSET; -typedef struct rrdvar RRDVAR; -typedef struct rrdsetvar RRDSETVAR; -typedef struct rrddimvar RRDDIMVAR; typedef struct rrdcalc RRDCALC; typedef struct rrdcalctemplate RRDCALCTEMPLATE; typedef struct alarm_entry ALARM_ENTRY; -typedef struct context_param CONTEXT_PARAM; + +typedef struct rrdfamily_acquired RRDFAMILY_ACQUIRED; +typedef struct rrdvar_acquired RRDVAR_ACQUIRED; +typedef struct rrdsetvar_acquired RRDSETVAR_ACQUIRED; +typedef struct rrdcalc_acquired RRDCALC_ACQUIRED; + +typedef struct rrdhost_acquired RRDHOST_ACQUIRED; +typedef struct rrdset_acquired RRDSET_ACQUIRED; +typedef struct rrddim_acquired RRDDIM_ACQUIRED; typedef void *ml_host_t; typedef void *ml_dimension_t; +typedef enum { + QUERY_SOURCE_UNKNOWN, + QUERY_SOURCE_API_DATA, + QUERY_SOURCE_API_BADGE, + QUERY_SOURCE_API_WEIGHTS, + QUERY_SOURCE_HEALTH, + QUERY_SOURCE_ML, + QUERY_SOURCE_UNITTEST, +} QUERY_SOURCE; + // forward declarations struct rrddim_tier; -struct rrdset_volatile; -struct context_param; #ifdef ENABLE_DBENGINE struct rrdeng_page_descr; @@ -51,8 +65,10 @@ struct pg_cache_page_index; #include "sqlite/sqlite_health.h" #include "rrdcontext.h" -extern int storage_tiers; -extern int storage_tiers_grouping_iterations[RRD_STORAGE_TIERS]; +extern bool unittest_running; +extern bool dbengine_enabled; +extern size_t storage_tiers; +extern size_t storage_tiers_grouping_iterations[RRD_STORAGE_TIERS]; typedef enum { RRD_BACKFILL_NONE, @@ -75,11 +91,6 @@ struct context_param { uint8_t flags; }; -#define META_CHART_UPDATED 1 -#define META_PLUGIN_UPDATED 2 -#define META_MODULE_UPDATED 4 -#define META_CHART_ACTIVATED 8 - #define UPDATE_EVERY 1 #define UPDATE_EVERY_MAX 3600 @@ -122,7 +133,9 @@ typedef enum rrd_memory_mode { RRD_MEMORY_MODE_MAP = 2, RRD_MEMORY_MODE_SAVE = 3, RRD_MEMORY_MODE_ALLOC = 4, - RRD_MEMORY_MODE_DBENGINE = 5 + RRD_MEMORY_MODE_DBENGINE = 5, + + // this is 8-bit } RRD_MEMORY_MODE; #define RRD_MEMORY_MODE_NONE_NAME "none" @@ -134,8 +147,8 @@ typedef enum rrd_memory_mode { extern RRD_MEMORY_MODE default_rrd_memory_mode; -extern const char *rrd_memory_mode_name(RRD_MEMORY_MODE id); -extern RRD_MEMORY_MODE rrd_memory_mode_id(const char *name); +const char *rrd_memory_mode_name(RRD_MEMORY_MODE id); +RRD_MEMORY_MODE rrd_memory_mode_id(const char *name); // ---------------------------------------------------------------------------- @@ -145,7 +158,9 @@ typedef enum rrd_algorithm { RRD_ALGORITHM_ABSOLUTE = 0, RRD_ALGORITHM_INCREMENTAL = 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL = 2, - RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL = 3 + RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL = 3, + + // this is 8-bit } RRD_ALGORITHM; #define RRD_ALGORITHM_ABSOLUTE_NAME "absolute" @@ -153,43 +168,50 @@ typedef enum rrd_algorithm { #define RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL_NAME "percentage-of-incremental-row" #define RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL_NAME "percentage-of-absolute-row" -extern RRD_ALGORITHM rrd_algorithm_id(const char *name); -extern const char *rrd_algorithm_name(RRD_ALGORITHM algorithm); +RRD_ALGORITHM rrd_algorithm_id(const char *name); +const char *rrd_algorithm_name(RRD_ALGORITHM algorithm); // ---------------------------------------------------------------------------- // RRD FAMILY -struct rrdfamily { - avl_t avl; +const RRDFAMILY_ACQUIRED *rrdfamily_add_and_acquire(RRDHOST *host, const char *id); +void rrdfamily_release(RRDHOST *host, const RRDFAMILY_ACQUIRED *rfa); +void rrdfamily_index_init(RRDHOST *host); +void rrdfamily_index_destroy(RRDHOST *host); +DICTIONARY *rrdfamily_rrdvars_dict(const RRDFAMILY_ACQUIRED *rf); - const char *family; - uint32_t hash_family; - size_t use_count; +// ---------------------------------------------------------------------------- +// flags & options - avl_tree_lock rrdvar_root_index; -}; -typedef struct rrdfamily RRDFAMILY; +// options are permanent configuration options (no atomics to alter/access them) +typedef enum rrddim_options { + RRDDIM_OPTION_NONE = 0, + RRDDIM_OPTION_HIDDEN = (1 << 0), // this dimension will not be offered to callers + RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS = (1 << 1), // do not offer RESET or OVERFLOW info to callers + RRDDIM_OPTION_BACKFILLED_HIGH_TIERS = (1 << 2), // when set, we have backfilled higher tiers + // this is 8-bit +} RRDDIM_OPTIONS; -// ---------------------------------------------------------------------------- -// flags -// use this for configuration flags, not for state control -// flags are set/unset in a manner that is not thread safe -// and may lead to missing information. +#define rrddim_option_check(rd, option) ((rd)->options & (option)) +#define rrddim_option_set(rd, option) (rd)->options |= (option) +#define rrddim_option_clear(rd, option) (rd)->options &= ~(option) +// flags are runtime changing status flags (atomics are required to alter/access them) typedef enum rrddim_flags { RRDDIM_FLAG_NONE = 0, - RRDDIM_FLAG_HIDDEN = (1 << 0), // this dimension will not be offered to callers - RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS = (1 << 1), // do not offer RESET or OVERFLOW info to callers + RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION = (1 << 0), + RRDDIM_FLAG_OBSOLETE = (1 << 2), // this is marked by the collector/module as obsolete - // No new values have been collected for this dimension since agent start or it was marked RRDDIM_FLAG_OBSOLETE at + // No new values have been collected for this dimension since agent start, or it was marked RRDDIM_FLAG_OBSOLETE at // least rrdset_free_obsolete_time seconds ago. RRDDIM_FLAG_ARCHIVED = (1 << 3), - RRDDIM_FLAG_ACLK = (1 << 4), + RRDDIM_FLAG_METADATA_UPDATE = (1 << 4), // Metadata needs to go to the database - RRDDIM_FLAG_PENDING_FOREACH_ALARM = (1 << 5), // set when foreach alarm has not been initialized yet RRDDIM_FLAG_META_HIDDEN = (1 << 6), // Status of hidden option in the metadata database + + // this is 8 bit } RRDDIM_FLAGS; #define rrddim_flag_check(rd, flag) (__atomic_load_n(&((rd)->flags), __ATOMIC_SEQ_CST) & (flag)) @@ -211,62 +233,55 @@ typedef enum rrdlabel_source { #define RRDLABEL_FLAG_INTERNAL (RRDLABEL_FLAG_OLD | RRDLABEL_FLAG_NEW | RRDLABEL_FLAG_PERMANENT) -extern DICTIONARY *rrdlabels_create(void); -extern void rrdlabels_destroy(DICTIONARY *labels_dict); -extern void rrdlabels_add(DICTIONARY *dict, const char *name, const char *value, RRDLABEL_SRC ls); -extern void rrdlabels_add_pair(DICTIONARY *dict, const char *string, RRDLABEL_SRC ls); -extern void rrdlabels_get_value_to_buffer_or_null(DICTIONARY *labels, BUFFER *wb, const char *key, const char *quote, const char *null); +size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, unsigned char *char_map, bool utf, const char *empty, size_t *multibyte_length); -extern void rrdlabels_unmark_all(DICTIONARY *labels); -extern void rrdlabels_remove_all_unmarked(DICTIONARY *labels); +DICTIONARY *rrdlabels_create(void); +void rrdlabels_destroy(DICTIONARY *labels_dict); +void rrdlabels_add(DICTIONARY *dict, const char *name, const char *value, RRDLABEL_SRC ls); +void rrdlabels_add_pair(DICTIONARY *dict, const char *string, RRDLABEL_SRC ls); +void rrdlabels_get_value_to_buffer_or_null(DICTIONARY *labels, BUFFER *wb, const char *key, const char *quote, const char *null); +void rrdlabels_get_value_to_char_or_null(DICTIONARY *labels, char **value, const char *key); +void rrdlabels_flush(DICTIONARY *labels_dict); -extern int rrdlabels_walkthrough_read(DICTIONARY *labels, int (*callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *data); -extern int rrdlabels_sorted_walkthrough_read(DICTIONARY *labels, int (*callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *data); +void rrdlabels_unmark_all(DICTIONARY *labels); +void rrdlabels_remove_all_unmarked(DICTIONARY *labels); -extern void rrdlabels_log_to_buffer(DICTIONARY *labels, BUFFER *wb); -extern bool rrdlabels_match_simple_pattern(DICTIONARY *labels, const char *simple_pattern_txt); -extern bool rrdlabels_match_simple_pattern_parsed(DICTIONARY *labels, SIMPLE_PATTERN *pattern, char equal); -extern int rrdlabels_to_buffer(DICTIONARY *labels, BUFFER *wb, const char *before_each, const char *equal, const char *quote, const char *between_them, bool (*filter_callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *filter_data, void (*name_sanitizer)(char *dst, const char *src, size_t dst_size), void (*value_sanitizer)(char *dst, const char *src, size_t dst_size)); +int rrdlabels_walkthrough_read(DICTIONARY *labels, int (*callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *data); +int rrdlabels_sorted_walkthrough_read(DICTIONARY *labels, int (*callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *data); -extern void rrdlabels_migrate_to_these(DICTIONARY *dst, DICTIONARY *src); -extern void rrdlabels_copy(DICTIONARY *dst, DICTIONARY *src); +void rrdlabels_log_to_buffer(DICTIONARY *labels, BUFFER *wb); +bool rrdlabels_match_simple_pattern(DICTIONARY *labels, const char *simple_pattern_txt); +bool rrdlabels_match_simple_pattern_parsed(DICTIONARY *labels, SIMPLE_PATTERN *pattern, char equal); +int rrdlabels_to_buffer(DICTIONARY *labels, BUFFER *wb, const char *before_each, const char *equal, const char *quote, const char *between_them, bool (*filter_callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *filter_data, void (*name_sanitizer)(char *dst, const char *src, size_t dst_size), void (*value_sanitizer)(char *dst, const char *src, size_t dst_size)); -void reload_host_labels(void); -extern void rrdset_update_rrdlabels(RRDSET *st, DICTIONARY *new_rrdlabels); +void rrdlabels_migrate_to_these(DICTIONARY *dst, DICTIONARY *src); +void rrdlabels_copy(DICTIONARY *dst, DICTIONARY *src); -extern int rrdlabels_unittest(void); +void reload_host_labels(void); +void rrdset_update_rrdlabels(RRDSET *st, DICTIONARY *new_rrdlabels); +void rrdset_save_rrdlabels_to_sql(RRDSET *st); +void rrdhost_set_is_parent_label(int count); +int rrdlabels_unittest(void); // unfortunately this break when defined in exporting_engine.h -extern bool exporting_labels_filter_callback(const char *name, const char *value, RRDLABEL_SRC ls, void *data); +bool exporting_labels_filter_callback(const char *name, const char *value, RRDLABEL_SRC ls, void *data); // ---------------------------------------------------------------------------- // RRD DIMENSION - this is a metric struct rrddim { - // ------------------------------------------------------------------------ - // binary indexing structures - - avl_t avl; // the binary index - this has to be first member! - uuid_t metric_uuid; // global UUID for this metric (unique_across hosts) // ------------------------------------------------------------------------ - // the dimension definition - - const char *id; // the id of this dimension (for internal identification) - const char *name; // the name of this dimension (as presented to user) - // this is a pointer to the config structure - // since the config always has a higher priority - // (the user overwrites the name of the charts) - uint32_t hash; // a simple hash of the id, to speed up searching / indexing - // instead of strcmp() every item in the binary index - // we first compare the hashes - uint32_t hash_name; // a simple hash of the name + // dimension definition + STRING *id; // the id of this dimension (for internal identification) + STRING *name; // the name of this dimension (as presented to user) - RRD_ALGORITHM algorithm; // the algorithm that is applied to add new collected values - RRD_MEMORY_MODE rrd_memory_mode; // the memory mode for this dimension - RRDDIM_FLAGS flags; // configuration flags for the dimension + RRD_ALGORITHM algorithm:8; // the algorithm that is applied to add new collected values + RRDDIM_OPTIONS options:8; // permanent configuration options + RRD_MEMORY_MODE rrd_memory_mode:8; // the memory mode for this dimension + /*RRDDIM_FLAGS*/ uint8_t flags; // run time changing status flags bool updated; // 1 when the dimension has been updated since the last processing bool exposed; // 1 when set what have sent this dimension to the central netdata @@ -274,20 +289,31 @@ struct rrddim { collected_number multiplier; // the multiplier of the collected values collected_number divisor; // the divider of the collected values + int update_every; // every how many seconds is this updated + // TODO - remove update_every from rrddim + // it is always the same in rrdset + // ------------------------------------------------------------------------ - // members for temporary data we need for calculations + // operational state members - struct timeval last_collected_time; // when was this dimension last updated - // this is actual date time we updated the last_collected_value - // THIS IS DIFFERENT FROM THE SAME MEMBER OF RRDSET + ml_dimension_t ml_dimension; // machine learning data about this dimension -#ifdef ENABLE_ACLK - int aclk_live_status; -#endif - ml_dimension_t ml_dimension; + // ------------------------------------------------------------------------ + // linking to siblings and parents + + struct rrdset *rrdset; + + RRDMETRIC_ACQUIRED *rrdmetric; // the rrdmetric of this dimension + + // ------------------------------------------------------------------------ + // data collection members struct rrddim_tier *tiers[RRD_STORAGE_TIERS]; // our tiers of databases + struct timeval last_collected_time; // when was this dimension last updated + // this is actual date time we updated the last_collected_value + // THIS IS DIFFERENT FROM THE SAME MEMBER OF RRDSET + size_t collections_counter; // the number of times we added values to this rrddim collected_number collected_value_max; // the absolute maximum of the collected value @@ -298,68 +324,42 @@ struct rrddim { collected_number collected_value; // the current value, as collected - resets to 0 after being used collected_number last_collected_value; // the last value that was collected, after being processed - // the *_volume members are used to calculate the accuracy of the rounding done by the - // storage number - they are printed to debug.log when debug is enabled for a set. - NETDATA_DOUBLE collected_volume; // the sum of all collected values so far - NETDATA_DOUBLE stored_volume; // the sum of all stored values so far - - struct rrddim *next; // linking of dimensions within the same data set - struct rrdset *rrdset; - RRDMETRIC_ACQUIRED *rrdmetric; // the rrdmetric of this dimension +#ifdef NETDATA_LOG_COLLECTION_ERRORS + usec_t rrddim_store_metric_last_ut; // the timestamp we last called rrddim_store_metric() + size_t rrddim_store_metric_count; // the rrddim_store_metric() counter + const char *rrddim_store_metric_last_caller; // the name of the function that last called rrddim_store_metric() +#endif // ------------------------------------------------------------------------ - // members for checking the data when loading from disk - - long entries; // how many entries this dimension has in ram - // this is the same to the entries of the data set - // we set it here, to check the data when we load it from disk. - - int update_every; // every how many seconds is this updated + // db mode RAM, SAVE, MAP, ALLOC, NONE specifics + // TODO - they should be managed by storage engine + // (RRDDIM_DB_STATE ptr to an undefined structure, and a call to clean this up during destruction) size_t memsize; // the memory allocated for this dimension (without RRDDIM) - - struct rrddimvar *variables; - - // ------------------------------------------------------------------------ - // the values stored in this dimension, using our floating point numbers - void *rd_on_file; // pointer to the header written on disk storage_number *db; // the array of values }; +#define rrddim_id(rd) string2str((rd)->id) +#define rrddim_name(rd) string2str((rd) ->name) + // returns the RRDDIM cache filename, or NULL if it does not exist -extern const char *rrddim_cache_filename(RRDDIM *rd); +const char *rrddim_cache_filename(RRDDIM *rd); // updated the header with the latest RRDDIM value, for memory mode MAP and SAVE -extern void rrddim_memory_file_update(RRDDIM *rd); +void rrddim_memory_file_update(RRDDIM *rd); // free the memory file structures for memory mode MAP and SAVE -extern void rrddim_memory_file_free(RRDDIM *rd); +void rrddim_memory_file_free(RRDDIM *rd); -extern bool rrddim_memory_load_or_create_map_save(RRDSET *st, RRDDIM *rd, RRD_MEMORY_MODE memory_mode); +bool rrddim_memory_load_or_create_map_save(RRDSET *st, RRDDIM *rd, RRD_MEMORY_MODE memory_mode); // return the v019 header size of RRDDIM files -extern size_t rrddim_memory_file_header_size(void); - -extern void rrddim_memory_file_save(RRDDIM *rd); +size_t rrddim_memory_file_header_size(void); -// ---------------------------------------------------------------------------- -// engine-specific iterator state for dimension data collection -typedef struct storage_collect_handle STORAGE_COLLECT_HANDLE; - -// ---------------------------------------------------------------------------- -// engine-specific iterator state for dimension data queries -typedef struct storage_query_handle STORAGE_QUERY_HANDLE; +void rrddim_memory_file_save(RRDDIM *rd); // ---------------------------------------------------------------------------- -// iterator state for RRD dimension data queries -struct rrddim_query_handle { - RRDDIM *rd; - time_t start_time; - time_t end_time; - TIER_QUERY_FETCH tier_query_fetch_type; - STORAGE_QUERY_HANDLE* handle; -}; typedef struct storage_point { NETDATA_DOUBLE min; // when count > 1, this is the minimum among them @@ -397,11 +397,19 @@ typedef struct storage_point { #define storage_point_is_unset(x) (!(x).count) #define storage_point_is_empty(x) (!netdata_double_isnumber((x).sum)) +// ---------------------------------------------------------------------------- +// engine-specific iterator state for dimension data collection +typedef struct storage_collect_handle STORAGE_COLLECT_HANDLE; + +// ---------------------------------------------------------------------------- +// engine-specific iterator state for dimension data queries +typedef struct storage_query_handle STORAGE_QUERY_HANDLE; + // ------------------------------------------------------------------------ // function pointers that handle data collection -struct rrddim_collect_ops { +struct storage_engine_collect_ops { // an initialization function to run before starting collection - STORAGE_COLLECT_HANDLE *(*init)(STORAGE_METRIC_HANDLE *db_metric_handle); + STORAGE_COLLECT_HANDLE *(*init)(STORAGE_METRIC_HANDLE *db_metric_handle, uint32_t update_every); // run this to store each metric into the database void (*store_metric)(STORAGE_COLLECT_HANDLE *collection_handle, usec_t point_in_time, NETDATA_DOUBLE number, NETDATA_DOUBLE min_value, @@ -410,24 +418,38 @@ struct rrddim_collect_ops { // run this to flush / reset the current data collection sequence void (*flush)(STORAGE_COLLECT_HANDLE *collection_handle); - // an finalization function to run after collection is over + // a finalization function to run after collection is over // returns 1 if it's safe to delete the dimension int (*finalize)(STORAGE_COLLECT_HANDLE *collection_handle); + + void (*change_collection_frequency)(STORAGE_COLLECT_HANDLE *collection_handle, int update_every); + + STORAGE_METRICS_GROUP *(*metrics_group_get)(STORAGE_INSTANCE *db_instance, uuid_t *uuid); + void (*metrics_group_release)(STORAGE_INSTANCE *db_instance, STORAGE_METRICS_GROUP *sa); +}; + +// ---------------------------------------------------------------------------- +// iterator state for RRD dimension data queries +struct storage_engine_query_handle { + RRDDIM *rd; + time_t start_time_s; + time_t end_time_s; + STORAGE_QUERY_HANDLE* handle; }; // function pointers that handle database queries -struct rrddim_query_ops { +struct storage_engine_query_ops { // run this before starting a series of next_metric() database queries - void (*init)(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_query_handle *handle, time_t start_time, time_t end_time, TIER_QUERY_FETCH tier_query_fetch_type); + void (*init)(STORAGE_METRIC_HANDLE *db_metric_handle, struct storage_engine_query_handle *handle, time_t start_time, time_t end_time); // run this to load each metric number from the database - STORAGE_POINT (*next_metric)(struct rrddim_query_handle *handle); + STORAGE_POINT (*next_metric)(struct storage_engine_query_handle *handle); // run this to test if the series of next_metric() database queries is finished - int (*is_finished)(struct rrddim_query_handle *handle); + int (*is_finished)(struct storage_engine_query_handle *handle); // run this after finishing a series of load_metric() database queries - void (*finalize)(struct rrddim_query_handle *handle); + void (*finalize)(struct storage_engine_query_handle *handle); // get the timestamp of the last entry of this metric time_t (*latest_time)(STORAGE_METRIC_HANDLE *db_metric_handle); @@ -436,45 +458,60 @@ struct rrddim_query_ops { time_t (*oldest_time)(STORAGE_METRIC_HANDLE *db_metric_handle); }; +typedef struct storage_engine STORAGE_ENGINE; + +// ------------------------------------------------------------------------ +// function pointers for all APIs provided by a storage engine +typedef struct storage_engine_api { + // metric management + STORAGE_METRIC_HANDLE *(*metric_get)(STORAGE_INSTANCE *instance, uuid_t *uuid, STORAGE_METRICS_GROUP *smg); + STORAGE_METRIC_HANDLE *(*metric_get_or_create)(RRDDIM *rd, STORAGE_INSTANCE *instance, STORAGE_METRICS_GROUP *smg); + void (*metric_release)(STORAGE_METRIC_HANDLE *); + STORAGE_METRIC_HANDLE *(*metric_dup)(STORAGE_METRIC_HANDLE *); + + // operations + struct storage_engine_collect_ops collect_ops; + struct storage_engine_query_ops query_ops; +} STORAGE_ENGINE_API; + +struct storage_engine { + RRD_MEMORY_MODE id; + const char* name; + STORAGE_ENGINE_API api; +}; + +STORAGE_ENGINE* storage_engine_get(RRD_MEMORY_MODE mmode); +STORAGE_ENGINE* storage_engine_find(const char* name); // ---------------------------------------------------------------------------- // Storage tier data for every dimension struct rrddim_tier { - int tier_grouping; - RRD_MEMORY_MODE mode; // the memory mode of this tier - RRD_BACKFILL backfill; // backfilling configuration + size_t tier_grouping; STORAGE_METRIC_HANDLE *db_metric_handle; // the metric handle inside the database STORAGE_COLLECT_HANDLE *db_collection_handle; // the data collection handle STORAGE_POINT virtual_point; time_t next_point_time; - usec_t last_collected_ut; - struct rrddim_collect_ops collect_ops; - struct rrddim_query_ops query_ops; + struct storage_engine_collect_ops *collect_ops; + struct storage_engine_query_ops *query_ops; }; -extern void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, int tier, time_t now); - -// ---------------------------------------------------------------------------- -// volatile state per chart -struct rrdset_volatile { - char *old_title; - char *old_units; - char *old_context; - uuid_t hash_id; - DICTIONARY *chart_labels; - bool is_ar_chart; -}; +void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now); // ---------------------------------------------------------------------------- // these loop macros make sure the linked list is accessed with the right lock #define rrddim_foreach_read(rd, st) \ - for((rd) = (st)->dimensions, rrdset_check_rdlock(st); (rd) ; (rd) = (rd)->next) + dfe_start_read((st)->rrddim_root_index, rd) #define rrddim_foreach_write(rd, st) \ - for((rd) = (st)->dimensions, rrdset_check_wrlock(st); (rd) ; (rd) = (rd)->next) + dfe_start_write((st)->rrddim_root_index, rd) +#define rrddim_foreach_reentrant(rd, st) \ + dfe_start_reentrant((st)->rrddim_root_index, rd) + +#define rrddim_foreach_done(rd) \ + dfe_done(rd) // ---------------------------------------------------------------------------- // RRDSET - this is a chart @@ -484,166 +521,213 @@ struct rrdset_volatile { // and may lead to missing information. typedef enum rrdset_flags { - RRDSET_FLAG_DETAIL = 1 << 1, // if set, the data set should be considered as a detail of another - // (the master data set should be the one that has the same family and is not detail) - RRDSET_FLAG_DEBUG = 1 << 2, // enables or disables debugging for a chart - RRDSET_FLAG_OBSOLETE = 1 << 3, // this is marked by the collector/module as obsolete - RRDSET_FLAG_EXPORTING_SEND = 1 << 4, // if set, this chart should be sent to Prometheus web API and external databases - RRDSET_FLAG_EXPORTING_IGNORE = 1 << 5, // if set, this chart should not be sent to Prometheus web API and external databases - RRDSET_FLAG_UPSTREAM_SEND = 1 << 6, // if set, this chart should be sent upstream (streaming) - RRDSET_FLAG_UPSTREAM_IGNORE = 1 << 7, // if set, this chart should not be sent upstream (streaming) - RRDSET_FLAG_UPSTREAM_EXPOSED = 1 << 8, // if set, we have sent this chart definition to netdata parent (streaming) - RRDSET_FLAG_STORE_FIRST = 1 << 9, // if set, do not eliminate the first collection during interpolation - RRDSET_FLAG_HETEROGENEOUS = 1 << 10, // if set, the chart is not homogeneous (dimensions in it have multiple algorithms, multipliers or dividers) - RRDSET_FLAG_HOMOGENEOUS_CHECK = 1 << 11, // if set, the chart should be checked to determine if the dimensions are homogeneous - RRDSET_FLAG_HIDDEN = 1 << 12, // if set, do not show this chart on the dashboard, but use it for exporting - RRDSET_FLAG_SYNC_CLOCK = 1 << 13, // if set, microseconds on next data collection will be ignored (the chart will be synced to now) - RRDSET_FLAG_OBSOLETE_DIMENSIONS = 1 << 14, // this is marked by the collector/module when a chart has obsolete dimensions - // No new values have been collected for this chart since agent start or it was marked RRDSET_FLAG_OBSOLETE at - // least rrdset_free_obsolete_time seconds ago. - RRDSET_FLAG_ARCHIVED = 1 << 15, - RRDSET_FLAG_ACLK = 1 << 16, - RRDSET_FLAG_PENDING_FOREACH_ALARMS = 1 << 17, // contains dims with uninitialized foreach alarms - RRDSET_FLAG_ANOMALY_DETECTION = 1 << 18 // flag to identify anomaly detection charts. + RRDSET_FLAG_DETAIL = (1 << 1), // if set, the data set should be considered as a detail of another + // (the master data set should be the one that has the same family and is not detail) + RRDSET_FLAG_DEBUG = (1 << 2), // enables or disables debugging for a chart + RRDSET_FLAG_OBSOLETE = (1 << 3), // this is marked by the collector/module as obsolete + RRDSET_FLAG_EXPORTING_SEND = (1 << 4), // if set, this chart should be sent to Prometheus web API and external databases + RRDSET_FLAG_EXPORTING_IGNORE = (1 << 5), // if set, this chart should not be sent to Prometheus web API and external databases + + RRDSET_FLAG_UPSTREAM_SEND = (1 << 6), // if set, this chart should be sent upstream (streaming) + RRDSET_FLAG_UPSTREAM_IGNORE = (1 << 7), // if set, this chart should not be sent upstream (streaming) + RRDSET_FLAG_UPSTREAM_EXPOSED = (1 << 8), // if set, we have sent this chart definition to netdata parent (streaming) + + RRDSET_FLAG_STORE_FIRST = (1 << 9), // if set, do not eliminate the first collection during interpolation + RRDSET_FLAG_HETEROGENEOUS = (1 << 10), // if set, the chart is not homogeneous (dimensions in it have multiple algorithms, multipliers or dividers) + RRDSET_FLAG_HOMOGENEOUS_CHECK = (1 << 11), // if set, the chart should be checked to determine if the dimensions are homogeneous + RRDSET_FLAG_HIDDEN = (1 << 12), // if set, do not show this chart on the dashboard, but use it for exporting + RRDSET_FLAG_SYNC_CLOCK = (1 << 13), // if set, microseconds on next data collection will be ignored (the chart will be synced to now) + RRDSET_FLAG_OBSOLETE_DIMENSIONS = (1 << 14), // this is marked by the collector/module when a chart has obsolete dimensions + // No new values have been collected for this chart since agent start, or it was marked RRDSET_FLAG_OBSOLETE at + // least rrdset_free_obsolete_time seconds ago. + RRDSET_FLAG_ARCHIVED = (1 << 15), + RRDSET_FLAG_METADATA_UPDATE = (1 << 16), // Mark that metadata needs to be stored + RRDSET_FLAG_ANOMALY_DETECTION = (1 << 18), // flag to identify anomaly detection charts. + RRDSET_FLAG_INDEXED_ID = (1 << 19), // the rrdset is indexed by its id + RRDSET_FLAG_INDEXED_NAME = (1 << 20), // the rrdset is indexed by its name + + RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION = (1 << 21), + + RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS = (1 << 22), // the sending side has replication in progress + RRDSET_FLAG_SENDER_REPLICATION_FINISHED = (1 << 23), // the sending side has completed replication + RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS = (1 << 24), // the receiving side has replication in progress + RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED = (1 << 25), // the receiving side has completed replication + + RRDSET_FLAG_UPSTREAM_SEND_VARIABLES = (1 << 26), // a custom variable has been updated and needs to be exposed to parent } RRDSET_FLAGS; #define rrdset_flag_check(st, flag) (__atomic_load_n(&((st)->flags), __ATOMIC_SEQ_CST) & (flag)) #define rrdset_flag_set(st, flag) __atomic_or_fetch(&((st)->flags), flag, __ATOMIC_SEQ_CST) -#define rrdset_flag_clear(st, flag) __atomic_and_fetch(&((st)->flags), ~flag, __ATOMIC_SEQ_CST) +#define rrdset_flag_clear(st, flag) __atomic_and_fetch(&((st)->flags), ~(flag), __ATOMIC_SEQ_CST) -struct rrdset { - // ------------------------------------------------------------------------ - // binary indexing structures +#define rrdset_is_replicating(st) (rrdset_flag_check(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS|RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS) \ + && !rrdset_flag_check(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED|RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED)) - avl_t avl; // the index, with key the id - this has to be first! - avl_t avlname; // the index, with key the name +struct rrdset { + uuid_t chart_uuid; // the global UUID for this chart // ------------------------------------------------------------------------ - // the set configuration - - char id[RRD_ID_LENGTH_MAX + 1]; // id of the data set - - const char *name; // the name of this dimension (as presented to user) - // this is a pointer to the config structure - // since the config always has a higher priority - // (the user overwrites the name of the charts) + // chart configuration + + struct { + STRING *type; // the type of {type}.{id} + STRING *id; // the id of {type}.{id} + STRING *name; // the name of {type}.{name} + } parts; + + STRING *id; // the unique ID of the rrdset as {type}.{id} + STRING *name; // the unique name of the rrdset as {type}.{name} + STRING *family; // grouping sets under the same family + STRING *title; // title shown to user + STRING *units; // units of measurement + STRING *context; // the template of this data set + STRING *plugin_name; // the name of the plugin that generated this + STRING *module_name; // the name of the plugin module that generated this - char *type; // the type of graph RRD_TYPE_* (a category, for determining graphing options) - char *family; // grouping sets under the same family - char *title; // title shown to user - char *units; // units of measurement - - char *context; // the template of this data set - uint32_t hash_context; // the hash of the chart's context + RRDSET_TYPE chart_type; // line, area, stacked - RRDINSTANCE_ACQUIRED *rrdinstance; // the rrdinstance of this chart - RRDCONTEXT_ACQUIRED *rrdcontext; // the rrdcontext this chart belongs to + long priority; // the sorting priority of this chart - RRDSET_TYPE chart_type; // line, area, stacked + int update_every; // data collection frequency - int update_every; // every how many seconds is this updated? + DICTIONARY *rrdlabels; // chart labels + DICTIONARY *rrdsetvar_root_index; // chart variables + DICTIONARY *rrddimvar_root_index; // dimension variables + // we use this dictionary to manage their allocation - long entries; // total number of entries in the data set + // ------------------------------------------------------------------------ + // operational state members - long current_entry; // the entry that is currently being updated - // it goes around in a round-robin fashion + RRDSET_FLAGS flags; // flags + RRD_MEMORY_MODE rrd_memory_mode; // the db mode of this rrdset - RRDSET_FLAGS flags; // configuration flags - RRDSET_FLAGS *exporting_flags; // array of flags for exporting connector instances + DICTIONARY *rrddim_root_index; // dimensions index int gap_when_lost_iterations_above; // after how many lost iterations a gap should be stored // netdata will interpolate values for gaps lower than this + // TODO - use the global - all charts have the same value - long priority; // the sorting priority of this chart - + STORAGE_METRICS_GROUP *storage_metrics_groups[RRD_STORAGE_TIERS]; // ------------------------------------------------------------------------ - // members for temporary data we need for calculations + // linking to siblings and parents - RRD_MEMORY_MODE rrd_memory_mode; // if set to 1, this is memory mapped + RRDHOST *rrdhost; // pointer to RRDHOST this chart belongs to - char *cache_dir; // the directory to store dimensions + RRDINSTANCE_ACQUIRED *rrdinstance; // the rrdinstance of this chart + RRDCONTEXT_ACQUIRED *rrdcontext; // the rrdcontext this chart belongs to - netdata_rwlock_t rrdset_rwlock; // protects dimensions linked list + // ------------------------------------------------------------------------ + // data collection members size_t counter; // the number of times we added values to this database size_t counter_done; // the number of times rrdset_done() has been called - union { - time_t last_accessed_time; // the last time this RRDSET has been accessed - time_t last_entry_t; // the last_entry_t computed for transient RRDSET - }; - time_t upstream_resync_time; // the timestamp up to which we should resync clock upstream - - char *plugin_name; // the name of the plugin that generated this - char *module_name; // the name of the plugin module that generated this - uuid_t *chart_uuid; // Store the global GUID for this chart - // this object. - struct rrdset_volatile *state; // volatile state that is not persistently stored - - size_t rrddim_page_alignment; // keeps metric pages in alignment when using dbengine - - uint32_t hash; // a simple hash on the id, to speed up searching - // we first compare hashes, and only if the hashes are equal we do string comparisons - - uint32_t hash_name; // a simple hash on the name + time_t last_accessed_time; // the last time this RRDSET has been accessed usec_t usec_since_last_update; // the time in microseconds since the last collection of data struct timeval last_updated; // when this data set was last updated (updated every time the rrd_stats_done() function) struct timeval last_collected_time; // when did this data set last collected values - total_number collected_total; // used internally to calculate percentages - total_number last_collected_total; // used internally to calculate percentages - - RRDFAMILY *rrdfamily; // pointer to RRDFAMILY this chart belongs to - RRDHOST *rrdhost; // pointer to RRDHOST this chart belongs to + size_t rrdlabels_last_saved_version; - struct rrdset *next; // linking of rrdsets + DICTIONARY *functions_view; // collector functions this rrdset supports, can be NULL // ------------------------------------------------------------------------ - // local variables + // data collection - streaming to parents, temp variables - NETDATA_DOUBLE green; // green threshold for this chart - NETDATA_DOUBLE red; // red threshold for this chart - - avl_tree_lock rrdvar_root_index; // RRDVAR index for this chart - RRDSETVAR *variables; // RRDSETVAR linked list for this chart (one RRDSETVAR, many RRDVARs) - RRDCALC *alarms; // RRDCALC linked list for this chart + time_t upstream_resync_time; // the timestamp up to which we should resync clock upstream // ------------------------------------------------------------------------ - // members for checking the data when loading from disk + // db mode SAVE, MAP specifics + // TODO - they should be managed by storage engine + // (RRDSET_DB_STATE ptr to an undefined structure, and a call to clean this up during destruction) + char *cache_dir; // the directory to store dimensions unsigned long memsize; // how much mem we have allocated for this (without dimensions) void *st_on_file; // compatibility with V019 RRDSET files // ------------------------------------------------------------------------ - // the dimensions + // db mode RAM, SAVE, MAP, ALLOC, NONE specifics + // TODO - they should be managed by storage engine + // (RRDSET_DB_STATE ptr to an undefined structure, and a call to clean this up during destruction) - avl_tree_lock dimensions_index; // the root of the dimensions index - RRDDIM *dimensions; // the actual data for every dimension + long entries; // total number of entries in the data set + + long current_entry; // the entry that is currently being updated + // it goes around in a round-robin fashion + + // ------------------------------------------------------------------------ + // exporting to 3rd party time-series members + // TODO - they should be managed by exporting engine + // (RRDSET_EXPORTING_STATE ptr to an undefined structure, and a call to clean this up during destruction) + + RRDSET_FLAGS *exporting_flags; // array of flags for exporting connector instances + + // ------------------------------------------------------------------------ + // health monitoring members + // TODO - they should be managed by health + // (RRDSET_HEALTH_STATE ptr to an undefined structure, and a call to clean this up during destruction) + + NETDATA_DOUBLE green; // green threshold for this chart + NETDATA_DOUBLE red; // red threshold for this chart + + DICTIONARY *rrdvars; // RRDVAR index for this chart + const RRDFAMILY_ACQUIRED *rrdfamily; // pointer to RRDFAMILY dictionary item, this chart belongs to + + struct { + netdata_rwlock_t rwlock; // protection for RRDCALC *base + RRDCALC *base; // double linked list of RRDCALC related to this RRDSET + } alerts; + +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + struct { + bool log_next_data_collection; + bool start_streaming; + time_t after; + time_t before; + } replay; +#endif // NETDATA_LOG_REPLICATION_REQUESTS }; -#define rrdset_rdlock(st) netdata_rwlock_rdlock(&((st)->rrdset_rwlock)) -#define rrdset_wrlock(st) netdata_rwlock_wrlock(&((st)->rrdset_rwlock)) -#define rrdset_unlock(st) netdata_rwlock_unlock(&((st)->rrdset_rwlock)) +#define rrdset_plugin_name(st) string2str((st)->plugin_name) +#define rrdset_module_name(st) string2str((st)->module_name) +#define rrdset_units(st) string2str((st)->units) +#define rrdset_parts_type(st) string2str((st)->parts.type) +#define rrdset_family(st) string2str((st)->family) +#define rrdset_title(st) string2str((st)->title) +#define rrdset_context(st) string2str((st)->context) +#define rrdset_name(st) string2str((st)->name) +#define rrdset_id(st) string2str((st)->id) +STRING *rrd_string_strdupz(const char *s); // ---------------------------------------------------------------------------- // these loop macros make sure the linked list is accessed with the right lock #define rrdset_foreach_read(st, host) \ - for((st) = (host)->rrdset_root, rrdhost_check_rdlock(host); st ; (st) = (st)->next) + dfe_start_read((host)->rrdset_root_index, st) #define rrdset_foreach_write(st, host) \ - for((st) = (host)->rrdset_root, rrdhost_check_wrlock(host); st ; (st) = (st)->next) + dfe_start_write((host)->rrdset_root_index, st) + +#define rrdset_foreach_reentrant(st, host) \ + dfe_start_reentrant((host)->rrdset_root_index, st) + +#define rrdset_foreach_done(st) \ + dfe_done(st) +#define rrdset_number_of_dimensions(st) \ + dictionary_entries((st)->rrddim_root_index) -extern void rrdset_memory_file_save(RRDSET *st); -extern void rrdset_memory_file_free(RRDSET *st); -extern void rrdset_memory_file_update(RRDSET *st); -extern const char *rrdset_cache_filename(RRDSET *st); -extern bool rrdset_memory_load_or_create_map_save(RRDSET *st_on_file, RRD_MEMORY_MODE memory_mode); +void rrdset_memory_file_save(RRDSET *st); +void rrdset_memory_file_free(RRDSET *st); +void rrdset_memory_file_update(RRDSET *st); +const char *rrdset_cache_filename(RRDSET *st); +bool rrdset_memory_load_or_create_map_save(RRDSET *st_on_file, RRD_MEMORY_MODE memory_mode); + +#include "rrdfunctions.h" // ---------------------------------------------------------------------------- // RRDHOST flags @@ -652,30 +736,70 @@ extern bool rrdset_memory_load_or_create_map_save(RRDSET *st_on_file, RRD_MEMORY // and may lead to missing information. typedef enum rrdhost_flags { - RRDHOST_FLAG_ORPHAN = (1 << 0), // this host is orphan (not receiving data) - RRDHOST_FLAG_DELETE_OBSOLETE_CHARTS = (1 << 1), // delete files of obsolete charts - RRDHOST_FLAG_DELETE_ORPHAN_HOST = (1 << 2), // delete the entire host when orphan - RRDHOST_FLAG_EXPORTING_SEND = (1 << 3), // send it to external databases - RRDHOST_FLAG_EXPORTING_DONT_SEND = (1 << 4), // don't send it to external databases - RRDHOST_FLAG_ARCHIVED = (1 << 5), // The host is archived, no collected charts yet - RRDHOST_FLAG_MULTIHOST = (1 << 6), // Host belongs to localhost/megadb - RRDHOST_FLAG_PENDING_FOREACH_ALARMS = (1 << 7), // contains dims with uninitialized foreach alarms - RRDHOST_FLAG_STREAM_LABELS_UPDATE = (1 << 8), - RRDHOST_FLAG_STREAM_LABELS_STOP = (1 << 9), - RRDHOST_FLAG_ACLK_STREAM_CONTEXTS = (1 << 10), // when set, we should send ACLK stream context updates + // Orphan, Archived and Obsolete flags + RRDHOST_FLAG_ORPHAN = (1 << 10), // this host is orphan (not receiving data) + RRDHOST_FLAG_ARCHIVED = (1 << 11), // The host is archived, no collected charts yet + RRDHOST_FLAG_PENDING_OBSOLETE_CHARTS = (1 << 12), // the host has pending chart obsoletions + RRDHOST_FLAG_PENDING_OBSOLETE_DIMENSIONS = (1 << 13), // the host has pending dimension obsoletions + + // Streaming sender + RRDHOST_FLAG_RRDPUSH_SENDER_INITIALIZED = (1 << 14), // the host has initialized rrdpush structures + RRDHOST_FLAG_RRDPUSH_SENDER_SPAWN = (1 << 15), // When set, the sender thread is running + RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED = (1 << 16), // When set, the host is connected to a parent + RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS = (1 << 17), // when set, rrdset_done() should push metrics to parent + RRDHOST_FLAG_RRDPUSH_SENDER_LOGGED_STATUS = (1 << 18), // when set, we have logged the status of metrics streaming + RRDHOST_FLAG_RRDPUSH_SENDER_JOIN = (1 << 19), // When set, we want to join the sender thread + + // Health + RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION = (1 << 20), // contains charts and dims with uninitialized variables + RRDHOST_FLAG_INITIALIZED_HEALTH = (1 << 21), // the host has initialized health structures + + // Exporting + RRDHOST_FLAG_EXPORTING_SEND = (1 << 22), // send it to external databases + RRDHOST_FLAG_EXPORTING_DONT_SEND = (1 << 23), // don't send it to external databases + + // ACLK + RRDHOST_FLAG_ACLK_STREAM_CONTEXTS = (1 << 24), // when set, we should send ACLK stream context updates + // Metadata + RRDHOST_FLAG_METADATA_UPDATE = (1 << 25), // metadata needs to be stored in the database + + RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED = ( 1 << 26), // set when the receiver part is disconnected } RRDHOST_FLAGS; #define rrdhost_flag_check(host, flag) (__atomic_load_n(&((host)->flags), __ATOMIC_SEQ_CST) & (flag)) #define rrdhost_flag_set(host, flag) __atomic_or_fetch(&((host)->flags), flag, __ATOMIC_SEQ_CST) -#define rrdhost_flag_clear(host, flag) __atomic_and_fetch(&((host)->flags), ~flag, __ATOMIC_SEQ_CST) +#define rrdhost_flag_clear(host, flag) __atomic_and_fetch(&((host)->flags), ~(flag), __ATOMIC_SEQ_CST) #ifdef NETDATA_INTERNAL_CHECKS #define rrdset_debug(st, fmt, args...) do { if(unlikely(debug_flags & D_RRD_STATS && rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) \ - debug_int(__FILE__, __FUNCTION__, __LINE__, "%s: " fmt, st->name, ##args); } while(0) + debug_int(__FILE__, __FUNCTION__, __LINE__, "%s: " fmt, rrdset_name(st), ##args); } while(0) #else #define rrdset_debug(st, fmt, args...) debug_dummy() #endif +typedef enum { + // Indexing + RRDHOST_OPTION_INDEXED_MACHINE_GUID = (1 << 0), // when set, we have indexed its machine guid + RRDHOST_OPTION_INDEXED_HOSTNAME = (1 << 1), // when set, we have indexed its hostname + + // Streaming configuration + RRDHOST_OPTION_SENDER_ENABLED = (1 << 2), // set when the host is configured to send metrics to a parent + + // Configuration options + RRDHOST_OPTION_DELETE_OBSOLETE_CHARTS = (1 << 3), // delete files of obsolete charts + RRDHOST_OPTION_DELETE_ORPHAN_HOST = (1 << 4), // delete the entire host when orphan + + RRDHOST_OPTION_REPLICATION = (1 << 5), // when set, we support replication for this host +} RRDHOST_OPTIONS; + +#define rrdhost_option_check(host, flag) ((host)->options & (flag)) +#define rrdhost_option_set(host, flag) (host)->options |= flag +#define rrdhost_option_clear(host, flag) (host)->options &= ~(flag) + +#define rrdhost_has_rrdpush_sender_enabled(host) (rrdhost_option_check(host, RRDHOST_OPTION_SENDER_ENABLED) && (host)->sender) + +#define rrdhost_can_send_definitions_to_parent(host) (rrdhost_has_rrdpush_sender_enabled(host) && rrdhost_flag_check(host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED)) + // ---------------------------------------------------------------------------- // Health data @@ -689,34 +813,30 @@ struct alarm_entry { time_t duration; time_t non_clear_duration; - char *name; - uint32_t hash_name; - - char *chart; - uint32_t hash_chart; - char *chart_context; + STRING *name; + STRING *chart; + STRING *chart_context; + STRING *family; - char *family; + STRING *classification; + STRING *component; + STRING *type; - char *classification; - char *component; - char *type; - - char *exec; - char *recipient; + STRING *exec; + STRING *recipient; time_t exec_run_timestamp; int exec_code; uint64_t exec_spawn_serial; - char *source; - char *units; - char *info; + STRING *source; + STRING *units; + STRING *info; NETDATA_DOUBLE old_value; NETDATA_DOUBLE new_value; - char *old_value_string; - char *new_value_string; + STRING *old_value_string; + STRING *new_value_string; RRDCALC_STATUS old_status; RRDCALC_STATUS new_status; @@ -736,6 +856,20 @@ struct alarm_entry { struct alarm_entry *prev_in_progress; }; +#define ae_name(ae) string2str((ae)->name) +#define ae_chart_name(ae) string2str((ae)->chart) +#define ae_chart_context(ae) string2str((ae)->chart_context) +#define ae_family(ae) string2str((ae)->family) +#define ae_classification(ae) string2str((ae)->classification) +#define ae_component(ae) string2str((ae)->component) +#define ae_type(ae) string2str((ae)->type) +#define ae_exec(ae) string2str((ae)->exec) +#define ae_recipient(ae) string2str((ae)->recipient) +#define ae_source(ae) string2str((ae)->source) +#define ae_units(ae) string2str((ae)->units) +#define ae_info(ae) string2str((ae)->info) +#define ae_old_value_string(ae) string2str((ae)->old_value_string) +#define ae_new_value_string(ae) string2str((ae)->new_value_string) typedef struct alarm_log { uint32_t next_log_id; @@ -789,79 +923,71 @@ struct rrdhost_system_info { }; struct rrdhost { - avl_t avl; // the index of hosts + char machine_guid[GUID_LEN + 1]; // the unique ID of this host // ------------------------------------------------------------------------ // host information - char *hostname; // the hostname of this host - uint32_t hash_hostname; // the hostname hash + STRING *hostname; // the hostname of this host + STRING *registry_hostname; // the registry hostname for this host + STRING *os; // the O/S type of the host + STRING *tags; // tags for this host + STRING *timezone; // the timezone of the host + STRING *abbrev_timezone; // the abbriviated timezone of the host + STRING *program_name; // the program name that collects metrics for this host + STRING *program_version; // the program version that collects metrics for this host - char *registry_hostname; // the registry hostname for this host - - char machine_guid[GUID_LEN + 1]; // the unique ID of this host - uint32_t hash_machine_guid; // the hash of the unique ID - - const char *os; // the O/S type of the host - const char *tags; // tags for this host - const char *timezone; // the timezone of the host - - const char *abbrev_timezone; // the abbriviated timezone of the host int32_t utc_offset; // the offset in seconds from utc - RRDHOST_FLAGS flags; // flags about this RRDHOST + RRDHOST_OPTIONS options; // configuration option for this RRDHOST (no atomics on this) + RRDHOST_FLAGS flags; // runtime flags about this RRDHOST (atomics on this) RRDHOST_FLAGS *exporting_flags; // array of flags for exporting connector instances int rrd_update_every; // the update frequency of the host long rrd_history_entries; // the number of history entries for the host's charts - RRD_MEMORY_MODE rrd_memory_mode; // the memory more for the charts of this host + + RRD_MEMORY_MODE rrd_memory_mode; // the configured memory more for the charts of this host + // the actual per tier is at .db[tier].mode char *cache_dir; // the directory to save RRD cache files char *varlib_dir; // the directory to save health log - char *program_name; // the program name that collects metrics for this host - char *program_version; // the program version that collects metrics for this host + struct { + RRD_MEMORY_MODE mode; // the db mode for this tier + STORAGE_ENGINE *eng; // the storage engine API for this tier + STORAGE_INSTANCE *instance; // the db instance for this tier + size_t tier_grouping; // tier 0 iterations aggregated on this tier + } db[RRD_STORAGE_TIERS]; struct rrdhost_system_info *system_info; // information collected from the host environment // ------------------------------------------------------------------------ - // streaming of data to remote hosts - rrdpush + // streaming of data to remote hosts - rrdpush sender - unsigned int rrdpush_send_enabled; // 1 when this host sends metrics to another netdata char *rrdpush_send_destination; // where to send metrics to char *rrdpush_send_api_key; // the api key at the receiving netdata struct rrdpush_destinations *destinations; // a linked list of possible destinations struct rrdpush_destinations *destination; // the current destination from the above list + SIMPLE_PATTERN *rrdpush_send_charts_matching; // pattern to match the charts to be sent + + time_t rrdpush_seconds_to_replicate; // max time we want to replicate from the child + time_t rrdpush_replication_step; // seconds per replication step + size_t rrdpush_receiver_replicating_charts; // the number of charts currently being replicated from a child // the following are state information for the threading // streaming metrics from this netdata to an upstream netdata struct sender_state *sender; - volatile unsigned int rrdpush_sender_spawn; // 1 when the sender thread has been spawn netdata_thread_t rrdpush_sender_thread; // the sender thread + size_t rrdpush_sender_replicating_charts; // the number of charts currently being replicated to a parent void *dbsync_worker; - bool rrdpush_sender_connected; // 1 when the sender is ready to push metrics - int rrdpush_sender_socket; // the fd of the socket to the remote host, or -1 - - volatile unsigned int rrdpush_sender_error_shown; // 1 when we have logged a communication error - volatile unsigned int rrdpush_sender_join; // 1 when we have to join the sending thread - - SIMPLE_PATTERN *rrdpush_send_charts_matching; // pattern to match the charts to be sent - - int rrdpush_sender_pipe[2]; // collector to sender thread signaling - //BUFFER *rrdpush_sender_buffer; // collector fills it, sender sends it - - //uint32_t stream_version; //Set the current version of the stream. - // ------------------------------------------------------------------------ - // streaming of data from remote hosts - rrdpush - - volatile size_t connected_senders; // when remote hosts are streaming to this - // host, this is the counter of connected clients + // streaming of data from remote hosts - rrdpush receiver time_t senders_connect_time; // the time the last sender was connected time_t senders_last_chart_command; // the time of the last CHART streaming command time_t senders_disconnected_time; // the time the last sender was disconnected + int senders_count; // number of senders currently streaming struct receiver_state *receiver; netdata_mutex_t receiver_lock; @@ -871,45 +997,30 @@ struct rrdhost { // ------------------------------------------------------------------------ // health monitoring options - unsigned int health_enabled; // 1 when this host has health enabled - time_t health_delay_up_to; // a timestamp to delay alarms processing up to - char *health_default_exec; // the full path of the alarms notifications program - char *health_default_recipient; // the default recipient for all alarms - char *health_log_filename; // the alarms event log filename - size_t health_log_entries_written; // the number of alarm events written to the alarms event log - FILE *health_log_fp; // the FILE pointer to the open alarms event log file - uint32_t health_default_warn_repeat_every; // the default value for the interval between repeating warning notifications - uint32_t health_default_crit_repeat_every; // the default value for the interval between repeating critical notifications - + unsigned int health_enabled; // 1 when this host has health enabled + bool health_spawn; // true when health thread is running + netdata_thread_t health_thread; // the health thread + unsigned int aclk_alert_reloaded; // 1 on thread start and health reload, 0 after removed are sent + time_t health_delay_up_to; // a timestamp to delay alarms processing up to + STRING *health_default_exec; // the full path of the alarms notifications program + STRING *health_default_recipient; // the default recipient for all alarms + char *health_log_filename; // the alarms event log filename + size_t health_log_entries_written; // the number of alarm events written to the alarms event log + FILE *health_log_fp; // the FILE pointer to the open alarms event log file + uint32_t health_default_warn_repeat_every; // the default value for the interval between repeating warning notifications + uint32_t health_default_crit_repeat_every; // the default value for the interval between repeating critical notifications // all RRDCALCs are primarily allocated and linked here - // RRDCALCs may be linked to charts at any point - // (charts may or may not exist when these are loaded) - RRDCALC *alarms; - RRDCALC *alarms_with_foreach; - avl_tree_lock alarms_idx_health_log; - avl_tree_lock alarms_idx_name; + DICTIONARY *rrdcalc_root_index; + + // templates of alarms + DICTIONARY *rrdcalctemplate_root_index; ALARM_LOG health_log; // alarms historical events (event log) uint32_t health_last_processed_id; // the last processed health id from the log uint32_t health_max_unique_id; // the max alarm log unique id given for the host uint32_t health_max_alarm_id; // the max alarm id given for the host - // templates of alarms - // these are used to create alarms when charts - // are created or renamed, that match them - RRDCALCTEMPLATE *templates; - RRDCALCTEMPLATE *alarms_template_with_foreach; - - - // ------------------------------------------------------------------------ - // the charts of the host - - RRDSET *rrdset_root; // the host charts - - unsigned int obsolete_charts_count; - - // ------------------------------------------------------------------------ // locks @@ -921,37 +1032,46 @@ struct rrdhost { // ------------------------------------------------------------------------ // Support for host-level labels - DICTIONARY *host_labels; + DICTIONARY *rrdlabels; // ------------------------------------------------------------------------ - // indexes + // Support for functions + DICTIONARY *functions; // collector functions this rrdset supports, can be NULL - avl_tree_lock rrdset_root_index; // the host's charts index (by id) - avl_tree_lock rrdset_root_index_name; // the host's charts index (by name) + // ------------------------------------------------------------------------ + // indexes - avl_tree_lock rrdfamily_root_index; // the host's chart families index - avl_tree_lock rrdvar_root_index; // the host's chart variables index + DICTIONARY *rrdset_root_index; // the host's charts index (by id) + DICTIONARY *rrdset_root_index_name; // the host's charts index (by name) - STORAGE_INSTANCE *storage_instance[RRD_STORAGE_TIERS]; // the database instances of the storage tiers + DICTIONARY *rrdfamily_root_index; // the host's chart families index + DICTIONARY *rrdvars; // the host's chart variables index + // this includes custom host variables - RRDCONTEXTS *rrdctx_queue; + RRDCONTEXTS *rrdctx_hub_queue; + RRDCONTEXTS *rrdctx_post_processing_queue; RRDCONTEXTS *rrdctx; uuid_t host_uuid; // Global GUID for this host uuid_t *node_id; // Cloud node_id -#ifdef ENABLE_HTTPS - struct netdata_ssl ssl; //Structure used to encrypt the connection - struct netdata_ssl stream_ssl; //Structure used to encrypt the stream -#endif - netdata_mutex_t aclk_state_lock; aclk_rrdhost_state aclk_state; struct rrdhost *next; + struct rrdhost *prev; }; extern RRDHOST *localhost; +#define rrdhost_hostname(host) string2str((host)->hostname) +#define rrdhost_registry_hostname(host) string2str((host)->registry_hostname) +#define rrdhost_os(host) string2str((host)->os) +#define rrdhost_tags(host) string2str((host)->tags) +#define rrdhost_timezone(host) string2str((host)->timezone) +#define rrdhost_abbrev_timezone(host) string2str((host)->abbrev_timezone) +#define rrdhost_program_name(host) string2str((host)->program_name) +#define rrdhost_program_version(host) string2str((host)->program_version) + #define rrdhost_rdlock(host) netdata_rwlock_rdlock(&((host)->rrdhost_rwlock)) #define rrdhost_wrlock(host) netdata_rwlock_wrlock(&((host)->rrdhost_rwlock)) #define rrdhost_unlock(host) netdata_rwlock_unlock(&((host)->rrdhost_rwlock)) @@ -959,6 +1079,19 @@ extern RRDHOST *localhost; #define rrdhost_aclk_state_lock(host) netdata_mutex_lock(&((host)->aclk_state_lock)) #define rrdhost_aclk_state_unlock(host) netdata_mutex_unlock(&((host)->aclk_state_lock)) +#define rrdhost_receiver_replicating_charts(host) (__atomic_load_n(&((host)->rrdpush_receiver_replicating_charts), __ATOMIC_RELAXED)) +#define rrdhost_receiver_replicating_charts_plus_one(host) (__atomic_add_fetch(&((host)->rrdpush_receiver_replicating_charts), 1, __ATOMIC_RELAXED)) +#define rrdhost_receiver_replicating_charts_minus_one(host) (__atomic_sub_fetch(&((host)->rrdpush_receiver_replicating_charts), 1, __ATOMIC_RELAXED)) +#define rrdhost_receiver_replicating_charts_zero(host) (__atomic_store_n(&((host)->rrdpush_receiver_replicating_charts), 0, __ATOMIC_RELAXED)) + +#define rrdhost_sender_replicating_charts(host) (__atomic_load_n(&((host)->rrdpush_sender_replicating_charts), __ATOMIC_RELAXED)) +#define rrdhost_sender_replicating_charts_plus_one(host) (__atomic_add_fetch(&((host)->rrdpush_sender_replicating_charts), 1, __ATOMIC_RELAXED)) +#define rrdhost_sender_replicating_charts_minus_one(host) (__atomic_sub_fetch(&((host)->rrdpush_sender_replicating_charts), 1, __ATOMIC_RELAXED)) +#define rrdhost_sender_replicating_charts_zero(host) (__atomic_store_n(&((host)->rrdpush_sender_replicating_charts), 0, __ATOMIC_RELAXED)) + +extern DICTIONARY *rrdhost_root_index; +long rrdhost_hosts_available(void); + // ---------------------------------------------------------------------------- // these loop macros make sure the linked list is accessed with the right lock @@ -980,19 +1113,24 @@ extern netdata_rwlock_t rrd_rwlock; // ---------------------------------------------------------------------------- -extern bool is_storage_engine_shared(STORAGE_INSTANCE *engine); +bool is_storage_engine_shared(STORAGE_INSTANCE *engine); +void rrdset_index_init(RRDHOST *host); +void rrdset_index_destroy(RRDHOST *host); + +void rrddim_index_init(RRDSET *st); +void rrddim_index_destroy(RRDSET *st); // ---------------------------------------------------------------------------- extern size_t rrd_hosts_available; extern time_t rrdhost_free_orphan_time; -extern int rrd_init(char *hostname, struct rrdhost_system_info *system_info); +int rrd_init(char *hostname, struct rrdhost_system_info *system_info); -extern RRDHOST *rrdhost_find_by_hostname(const char *hostname, uint32_t hash); -extern RRDHOST *rrdhost_find_by_guid(const char *guid, uint32_t hash); +RRDHOST *rrdhost_find_by_hostname(const char *hostname); +RRDHOST *rrdhost_find_by_guid(const char *guid); -extern RRDHOST *rrdhost_find_or_create( +RRDHOST *rrdhost_find_or_create( const char *hostname , const char *registry_hostname , const char *guid @@ -1011,11 +1149,14 @@ extern RRDHOST *rrdhost_find_or_create( , char *rrdpush_destination , char *rrdpush_api_key , char *rrdpush_send_charts_matching + , bool rrdpush_enable_replication + , time_t rrdpush_seconds_to_replicate + , time_t rrdpush_replication_step , struct rrdhost_system_info *system_info , bool is_archived ); -extern void rrdhost_update(RRDHOST *host +void rrdhost_update(RRDHOST *host , const char *hostname , const char *registry_hostname , const char *guid @@ -1034,18 +1175,21 @@ extern void rrdhost_update(RRDHOST *host , char *rrdpush_destination , char *rrdpush_api_key , char *rrdpush_send_charts_matching + , bool rrdpush_enable_replication + , time_t rrdpush_seconds_to_replicate + , time_t rrdpush_replication_step , struct rrdhost_system_info *system_info ); -extern int rrdhost_set_system_info_variable(struct rrdhost_system_info *system_info, char *name, char *value); +int rrdhost_set_system_info_variable(struct rrdhost_system_info *system_info, char *name, char *value); #if defined(NETDATA_INTERNAL_CHECKS) && defined(NETDATA_VERIFY_LOCKS) -extern void __rrdhost_check_wrlock(RRDHOST *host, const char *file, const char *function, const unsigned long line); -extern void __rrdhost_check_rdlock(RRDHOST *host, const char *file, const char *function, const unsigned long line); -extern void __rrdset_check_rdlock(RRDSET *st, const char *file, const char *function, const unsigned long line); -extern void __rrdset_check_wrlock(RRDSET *st, const char *file, const char *function, const unsigned long line); -extern void __rrd_check_rdlock(const char *file, const char *function, const unsigned long line); -extern void __rrd_check_wrlock(const char *file, const char *function, const unsigned long line); +void __rrdhost_check_wrlock(RRDHOST *host, const char *file, const char *function, const unsigned long line); +void __rrdhost_check_rdlock(RRDHOST *host, const char *file, const char *function, const unsigned long line); +void __rrdset_check_rdlock(RRDSET *st, const char *file, const char *function, const unsigned long line); +void __rrdset_check_wrlock(RRDSET *st, const char *file, const char *function, const unsigned long line); +void __rrd_check_rdlock(const char *file, const char *function, const unsigned long line); +void __rrd_check_wrlock(const char *file, const char *function, const unsigned long line); #define rrdhost_check_rdlock(host) __rrdhost_check_rdlock(host, __FILE__, __FUNCTION__, __LINE__) #define rrdhost_check_wrlock(host) __rrdhost_check_wrlock(host, __FILE__, __FUNCTION__, __LINE__) @@ -1066,9 +1210,9 @@ extern void __rrd_check_wrlock(const char *file, const char *function, const uns // ---------------------------------------------------------------------------- // RRDSET functions -extern int rrdset_set_name(RRDSET *st, const char *name); +int rrdset_reset_name(RRDSET *st, const char *name); -extern RRDSET *rrdset_create_custom(RRDHOST *host +RRDSET *rrdset_create_custom(RRDHOST *host , const char *type , const char *id , const char *name @@ -1090,22 +1234,22 @@ extern RRDSET *rrdset_create_custom(RRDHOST *host #define rrdset_create_localhost(type, id, name, family, context, title, units, plugin, module, priority, update_every, chart_type) \ rrdset_create(localhost, type, id, name, family, context, title, units, plugin, module, priority, update_every, chart_type) -extern void rrdhost_free_all(void); -extern void rrdhost_save_all(void); -extern void rrdhost_cleanup_all(void); +void rrdhost_free_all(void); +void rrdhost_save_all(void); +void rrdhost_cleanup_all(void); -extern void rrdhost_cleanup_orphan_hosts_nolock(RRDHOST *protected_host); -extern void rrdhost_system_info_free(struct rrdhost_system_info *system_info); -extern void rrdhost_free(RRDHOST *host, bool force); -extern void rrdhost_save_charts(RRDHOST *host); -extern void rrdhost_delete_charts(RRDHOST *host); -extern void rrd_cleanup_obsolete_charts(); +void rrdhost_system_info_free(struct rrdhost_system_info *system_info); +void rrdhost_free(RRDHOST *host, bool force); +void rrdhost_save_charts(RRDHOST *host); +void rrdhost_delete_charts(RRDHOST *host); -extern int rrdhost_should_be_removed(RRDHOST *host, RRDHOST *protected_host, time_t now); +int rrdhost_should_be_removed(RRDHOST *host, RRDHOST *protected_host, time_t now); -extern void rrdset_update_heterogeneous_flag(RRDSET *st); +void rrdset_update_heterogeneous_flag(RRDSET *st); -extern RRDSET *rrdset_find(RRDHOST *host, const char *id); +time_t rrdset_set_update_every(RRDSET *st, time_t update_every); + +RRDSET *rrdset_find(RRDHOST *host, const char *id); #define rrdset_find_localhost(id) rrdset_find(localhost, id) /* This will not return charts that are archived */ static inline RRDSET *rrdset_find_active_localhost(const char *id) @@ -1116,7 +1260,7 @@ static inline RRDSET *rrdset_find_active_localhost(const char *id) return st; } -extern RRDSET *rrdset_find_bytype(RRDHOST *host, const char *type, const char *id); +RRDSET *rrdset_find_bytype(RRDHOST *host, const char *type, const char *id); #define rrdset_find_bytype_localhost(type, id) rrdset_find_bytype(localhost, type, id) /* This will not return charts that are archived */ static inline RRDSET *rrdset_find_active_bytype_localhost(const char *type, const char *id) @@ -1127,7 +1271,7 @@ static inline RRDSET *rrdset_find_active_bytype_localhost(const char *type, cons return st; } -extern RRDSET *rrdset_find_byname(RRDHOST *host, const char *name); +RRDSET *rrdset_find_byname(RRDHOST *host, const char *name); #define rrdset_find_byname_localhost(name) rrdset_find_byname(localhost, name) /* This will not return charts that are archived */ static inline RRDSET *rrdset_find_active_byname_localhost(const char *name) @@ -1138,190 +1282,115 @@ static inline RRDSET *rrdset_find_active_byname_localhost(const char *name) return st; } -extern void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds); -extern void rrdset_next_usec(RRDSET *st, usec_t microseconds); +void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds); +void rrdset_next_usec(RRDSET *st, usec_t microseconds); +void rrdset_timed_next(RRDSET *st, struct timeval now, usec_t microseconds); #define rrdset_next(st) rrdset_next_usec(st, 0ULL) -extern void rrdset_done(RRDSET *st); +void rrdset_timed_done(RRDSET *st, struct timeval now, bool pending_rrdset_next); +void rrdset_done(RRDSET *st); -extern void rrdset_is_obsolete(RRDSET *st); -extern void rrdset_isnot_obsolete(RRDSET *st); +void rrdset_is_obsolete(RRDSET *st); +void rrdset_isnot_obsolete(RRDSET *st); // checks if the RRDSET should be offered to viewers -#define rrdset_is_available_for_viewers(st) (!rrdset_flag_check(st, RRDSET_FLAG_HIDDEN) && !rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && !rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED) && (st)->dimensions && (st)->rrd_memory_mode != RRD_MEMORY_MODE_NONE) -#define rrdset_is_available_for_exporting_and_alarms(st) (!rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && !rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED) && (st)->dimensions) -#define rrdset_is_archived(st) (rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED) && (st)->dimensions) - -// get the timestamp of the last entry in the round robin database -static inline time_t rrddim_last_entry_t(RRDDIM *rd) { - time_t latest = rd->tiers[0]->query_ops.latest_time(rd->tiers[0]->db_metric_handle); - - for(int tier = 1; tier < storage_tiers ;tier++) { - if(unlikely(!rd->tiers[tier])) continue; - - time_t t = rd->tiers[tier]->query_ops.latest_time(rd->tiers[tier]->db_metric_handle); - if(t > latest) - latest = t; - } - - return latest; -} - -static inline time_t rrddim_first_entry_t(RRDDIM *rd) { - time_t oldest = 0; - - for(int tier = 0; tier < storage_tiers ;tier++) { - if(unlikely(!rd->tiers[tier])) continue; - - time_t t = rd->tiers[tier]->query_ops.oldest_time(rd->tiers[tier]->db_metric_handle); - if(t != 0 && (oldest == 0 || t < oldest)) - oldest = t; - } - - return oldest; -} - -// get the timestamp of the last entry in the round robin database -static inline time_t rrdset_last_entry_t_nolock(RRDSET *st) { - RRDDIM *rd; - time_t last_entry_t = 0; - - rrddim_foreach_read(rd, st) { - time_t t = rrddim_last_entry_t(rd); - if(t > last_entry_t) last_entry_t = t; - } - - return last_entry_t; -} - -static inline time_t rrdset_last_entry_t(RRDSET *st) { - time_t last_entry_t; - - netdata_rwlock_rdlock(&st->rrdset_rwlock); - last_entry_t = rrdset_last_entry_t_nolock(st); - netdata_rwlock_unlock(&st->rrdset_rwlock); - - return last_entry_t; -} - -// get the timestamp of first entry in the round robin database -static inline time_t rrdset_first_entry_t_nolock(RRDSET *st) { - RRDDIM *rd; - time_t first_entry_t = LONG_MAX; - - rrddim_foreach_read(rd, st) { - time_t t = rrddim_first_entry_t(rd); - if(t < first_entry_t) - first_entry_t = t; - } - - if (unlikely(LONG_MAX == first_entry_t)) return 0; - return first_entry_t; -} - -static inline time_t rrdset_first_entry_t(RRDSET *st) -{ - time_t first_entry_t; - - netdata_rwlock_rdlock(&st->rrdset_rwlock); - first_entry_t = rrdset_first_entry_t_nolock(st); - netdata_rwlock_unlock(&st->rrdset_rwlock); - - return first_entry_t; -} - +#define rrdset_is_available_for_viewers(st) (!rrdset_flag_check(st, RRDSET_FLAG_HIDDEN) && !rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && !rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED) && rrdset_number_of_dimensions(st) && (st)->rrd_memory_mode != RRD_MEMORY_MODE_NONE) +#define rrdset_is_available_for_exporting_and_alarms(st) (!rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && !rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED) && rrdset_number_of_dimensions(st)) +#define rrdset_is_archived(st) (rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED) && rrdset_number_of_dimensions(st)) + +time_t rrddim_first_entry_t(RRDDIM *rd); +time_t rrddim_first_entry_t_of_tier(RRDDIM *rd, size_t tier); +time_t rrddim_last_entry_t(RRDDIM *rd); +time_t rrdset_last_entry_t(RRDSET *st); +time_t rrdset_first_entry_t_of_tier(RRDSET *st, size_t tier); +time_t rrdset_first_entry_t(RRDSET *st); time_t rrdhost_last_entry_t(RRDHOST *h); // ---------------------------------------------------------------------------- // RRD DIMENSION functions -extern void rrdcalc_link_to_rrddim(RRDDIM *rd, RRDSET *st, RRDHOST *host); -extern RRDDIM *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collected_number multiplier, - collected_number divisor, RRD_ALGORITHM algorithm, RRD_MEMORY_MODE memory_mode);//, - //int is_archived, uuid_t *dim_uuid); -#define rrddim_add(st, id, name, multiplier, divisor, algorithm) rrddim_add_custom(st, id, name, multiplier, divisor, \ - algorithm, (st)->rrd_memory_mode)//, 0, NULL) - -extern int rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name); -extern int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm); -extern int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, collected_number multiplier); -extern int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, collected_number divisor); - -extern RRDDIM *rrddim_find(RRDSET *st, const char *id); -/* This will not return dimensions that are archived */ -static inline RRDDIM *rrddim_find_active(RRDSET *st, const char *id) -{ - RRDDIM *rd = rrddim_find(st, id); - if (unlikely(rd && rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED))) - return NULL; - return rd; -} +RRDDIM *rrddim_add_custom(RRDSET *st + , const char *id + , const char *name + , collected_number multiplier + , collected_number divisor + , RRD_ALGORITHM algorithm + , RRD_MEMORY_MODE memory_mode + ); + +#define rrddim_add(st, id, name, multiplier, divisor, algorithm) \ + rrddim_add_custom(st, id, name, multiplier, divisor, algorithm, (st)->rrd_memory_mode) +int rrddim_reset_name(RRDSET *st, RRDDIM *rd, const char *name); +int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm); +int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, collected_number multiplier); +int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, collected_number divisor); -extern int rrddim_hide(RRDSET *st, const char *id); -extern int rrddim_unhide(RRDSET *st, const char *id); +RRDDIM *rrddim_find(RRDSET *st, const char *id); +RRDDIM_ACQUIRED *rrddim_find_and_acquire(RRDSET *st, const char *id); +RRDDIM *rrddim_acquired_to_rrddim(RRDDIM_ACQUIRED *rda); +void rrddim_acquired_release(RRDDIM_ACQUIRED *rda); +RRDDIM *rrddim_find_active(RRDSET *st, const char *id); -extern void rrddim_is_obsolete(RRDSET *st, RRDDIM *rd); -extern void rrddim_isnot_obsolete(RRDSET *st, RRDDIM *rd); +int rrddim_hide(RRDSET *st, const char *id); +int rrddim_unhide(RRDSET *st, const char *id); + +void rrddim_is_obsolete(RRDSET *st, RRDDIM *rd); +void rrddim_isnot_obsolete(RRDSET *st, RRDDIM *rd); + +collected_number rrddim_timed_set_by_pointer(RRDSET *st, RRDDIM *rd, struct timeval collected_time, collected_number value); +collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value); +collected_number rrddim_set(RRDSET *st, const char *id, collected_number value); -extern collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value); -extern collected_number rrddim_set(RRDSET *st, const char *id, collected_number value); #ifdef ENABLE_ACLK -extern time_t calc_dimension_liveness(RRDDIM *rd, time_t now); +time_t calc_dimension_liveness(RRDDIM *rd, time_t now); +#endif +long align_entries_to_pagesize(RRD_MEMORY_MODE mode, long entries); + +#ifdef NETDATA_LOG_COLLECTION_ERRORS +#define rrddim_store_metric(rd, point_end_time_ut, n, flags) rrddim_store_metric_with_trace(rd, point_end_time_ut, n, flags, __FUNCTION__) +void rrddim_store_metric_with_trace(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n, SN_FLAGS flags, const char *function); +#else +void rrddim_store_metric(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n, SN_FLAGS flags); #endif -extern long align_entries_to_pagesize(RRD_MEMORY_MODE mode, long entries); // ---------------------------------------------------------------------------- // Miscellaneous functions -extern int alarm_compare_id(void *a, void *b); -extern int alarm_compare_name(void *a, void *b); +char *rrdset_strncpyz_name(char *to, const char *from, size_t length); // ---------------------------------------------------------------------------- // RRD internal functions -#ifdef NETDATA_RRD_INTERNALS - -extern avl_tree_lock rrdhost_root_index; +void rrdset_delete_files(RRDSET *st); +void rrdset_save(RRDSET *st); +void rrdset_free(RRDSET *st); -extern char *rrdset_strncpyz_name(char *to, const char *from, size_t length); -extern char *rrdset_cache_dir(RRDHOST *host, const char *id); - -extern void rrddim_free(RRDSET *st, RRDDIM *rd); - -extern int rrddim_compare(void* a, void* b); -extern int rrdset_compare(void* a, void* b); -extern int rrdset_compare_name(void* a, void* b); -extern int rrdfamily_compare(void *a, void *b); +#ifdef NETDATA_RRD_INTERNALS -extern RRDFAMILY *rrdfamily_create(RRDHOST *host, const char *id); -extern void rrdfamily_free(RRDHOST *host, RRDFAMILY *rc); +char *rrdset_cache_dir(RRDHOST *host, const char *id); -#define rrdset_index_add(host, st) (RRDSET *)avl_insert_lock(&((host)->rrdset_root_index), (avl_t *)(st)) -#define rrdset_index_del(host, st) (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index), (avl_t *)(st)) -extern RRDSET *rrdset_index_del_name(RRDHOST *host, RRDSET *st); +void rrddim_free(RRDSET *st, RRDDIM *rd); -extern void rrdset_free(RRDSET *st); -extern void rrdset_reset(RRDSET *st); -extern void rrdset_save(RRDSET *st); -extern void rrdset_delete_files(RRDSET *st); -extern void rrdset_delete_obsolete_dimensions(RRDSET *st); +void rrdset_reset(RRDSET *st); +void rrdset_delete_obsolete_dimensions(RRDSET *st); -extern RRDHOST *rrdhost_create( +RRDHOST *rrdhost_create( const char *hostname, const char *registry_hostname, const char *guid, const char *os, const char *timezone, const char *abbrev_timezone, int32_t utc_offset,const char *tags, const char *program_name, const char *program_version, int update_every, long entries, RRD_MEMORY_MODE memory_mode, unsigned int health_enabled, unsigned int rrdpush_enabled, - char *rrdpush_destination, char *rrdpush_api_key, char *rrdpush_send_charts_matching, struct rrdhost_system_info *system_info, - int is_localhost, bool is_archived); + char *rrdpush_destination, char *rrdpush_api_key, char *rrdpush_send_charts_matching, + bool rrdpush_enable_replication, time_t rrdpush_seconds_to_replicate, time_t rrdpush_replication_step, + struct rrdhost_system_info *system_info, int is_localhost, bool is_archived); #endif /* NETDATA_RRD_INTERNALS */ -extern void set_host_properties( - RRDHOST *host, int update_every, RRD_MEMORY_MODE memory_mode, const char *hostname, const char *registry_hostname, - const char *guid, const char *os, const char *tags, const char *tzone, const char *abbrev_tzone, int32_t utc_offset, +void set_host_properties( + RRDHOST *host, int update_every, RRD_MEMORY_MODE memory_mode, const char *registry_hostname, + const char *os, const char *tags, const char *tzone, const char *abbrev_tzone, int32_t utc_offset, const char *program_name, const char *program_version); -extern int get_tier_grouping(int tier); +size_t get_tier_grouping(size_t tier); // ---------------------------------------------------------------------------- // RRD DB engine declarations @@ -1331,8 +1400,8 @@ extern int get_tier_grouping(int tier); #endif #include "sqlite/sqlite_functions.h" #include "sqlite/sqlite_context.h" +#include "sqlite/sqlite_metadata.h" #include "sqlite/sqlite_aclk.h" -#include "sqlite/sqlite_aclk_chart.h" #include "sqlite/sqlite_aclk_alert.h" #include "sqlite/sqlite_aclk_node.h" #include "sqlite/sqlite_health.h" diff --git a/database/rrdcalc.c b/database/rrdcalc.c index cab60468b..aad945a90 100644 --- a/database/rrdcalc.c +++ b/database/rrdcalc.c @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#define NETDATA_HEALTH_INTERNALS #include "rrd.h" // ---------------------------------------------------------------------------- -// RRDCALC management +// RRDCALC helpers inline const char *rrdcalc_status2string(RRDCALC_STATUS status) { switch(status) { @@ -35,55 +34,219 @@ inline const char *rrdcalc_status2string(RRDCALC_STATUS status) { } } -static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) { +uint32_t rrdcalc_get_unique_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id) { + netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock); + + // re-use old IDs, by looking them up in the alarm log + ALARM_ENTRY *ae = NULL; + for(ae = host->health_log.alarms; ae ;ae = ae->next) { + if(unlikely(name == ae->name && chart == ae->chart)) { + if(next_event_id) *next_event_id = ae->alarm_event_id + 1; + break; + } + } + + uint32_t alarm_id; + + if(ae) + alarm_id = ae->alarm_id; + + else { + if (unlikely(!host->health_log.next_alarm_id)) + host->health_log.next_alarm_id = (uint32_t)now_realtime_sec(); + + alarm_id = host->health_log.next_alarm_id++; + } + + netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); + return alarm_id; +} + +// ---------------------------------------------------------------------------- +// RRDCALC replacing info text variables with RRDSET labels + +static STRING *rrdcalc_replace_variables_with_rrdset_labels(const char *line, RRDCALC *rc) { + if (!line || !*line) + return NULL; + + size_t pos = 0; + char *temp = strdupz(line); + char var[RRDCALC_VAR_MAX]; + char *m, *lbl_value = NULL; + + while ((m = strchr(temp + pos, '$'))) { + int i = 0; + char *e = m; + while (*e) { + + if (*e == ' ' || i == RRDCALC_VAR_MAX - 1) + break; + else + var[i] = *e; + + e++; + i++; + } + + var[i] = '\0'; + pos = m - temp + 1; + + if (!strcmp(var, RRDCALC_VAR_FAMILY)) { + char *buf = find_and_replace(temp, var, (rc->rrdset && rc->rrdset->family) ? rrdset_family(rc->rrdset) : "", m); + freez(temp); + temp = buf; + } + else if (!strncmp(var, RRDCALC_VAR_LABEL, RRDCALC_VAR_LABEL_LEN)) { + if(likely(rc->rrdset && rc->rrdset->rrdlabels)) { + rrdlabels_get_value_to_char_or_null(rc->rrdset->rrdlabels, &lbl_value, var+RRDCALC_VAR_LABEL_LEN); + if (lbl_value) { + char *buf = find_and_replace(temp, var, lbl_value, m); + freez(temp); + temp = buf; + freez(lbl_value); + } + } + } + } + + STRING *ret = string_strdupz(temp); + freez(temp); + + return ret; +} + +void rrdcalc_update_info_using_rrdset_labels(RRDCALC *rc) { + if(!rc->rrdset || !rc->original_info || !rc->rrdset->rrdlabels) return; + + size_t labels_version = dictionary_version(rc->rrdset->rrdlabels); + if(rc->labels_version != labels_version) { + + STRING *old = rc->info; + rc->info = rrdcalc_replace_variables_with_rrdset_labels(rrdcalc_original_info(rc), rc); + string_freez(old); + + rc->labels_version = labels_version; + } +} + +// ---------------------------------------------------------------------------- +// RRDCALC index management for RRDSET + +// the dictionary requires a unique key for every item +// we use {chart id}.{alert name} for both the RRDHOST and RRDSET alert indexes. + +#define RRDCALC_MAX_KEY_SIZE 1024 +static size_t rrdcalc_key(char *dst, size_t dst_len, const char *chart, const char *alert) { + return snprintfz(dst, dst_len, "%s/%s", chart, alert); +} + +const RRDCALC_ACQUIRED *rrdcalc_from_rrdset_get(RRDSET *st, const char *alert_name) { + char key[RRDCALC_MAX_KEY_SIZE + 1]; + size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_id(st), alert_name); + + const RRDCALC_ACQUIRED *rca = (const RRDCALC_ACQUIRED *)dictionary_get_and_acquire_item_advanced(st->rrdhost->rrdcalc_root_index, key, (ssize_t)(key_len + 1)); + + if(!rca) { + key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_name(st), alert_name); + rca = (const RRDCALC_ACQUIRED *)dictionary_get_and_acquire_item_advanced(st->rrdhost->rrdcalc_root_index, key, (ssize_t)(key_len + 1)); + } + + return rca; +} + +void rrdcalc_from_rrdset_release(RRDSET *st, const RRDCALC_ACQUIRED *rca) { + if(!rca) return; + + dictionary_acquired_item_release(st->rrdhost->rrdcalc_root_index, (const DICTIONARY_ITEM *)rca); +} + +RRDCALC *rrdcalc_acquired_to_rrdcalc(const RRDCALC_ACQUIRED *rca) { + if(rca) + return dictionary_acquired_item_value((const DICTIONARY_ITEM *)rca); + + return NULL; +} + +// ---------------------------------------------------------------------------- +// RRDCALC managing the linking with RRDSET + +static void rrdcalc_link_to_rrdset(RRDSET *st, RRDCALC *rc) { RRDHOST *host = st->rrdhost; - debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname); + debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rrdcalc_chart_name(rc), rrdcalc_name(rc), rrdset_id(st), rrdhost_hostname(host)); rc->last_status_change = now_realtime_sec(); rc->rrdset = st; - rc->rrdset_next = st->alarms; - rc->rrdset_prev = NULL; - - if(rc->rrdset_next) - rc->rrdset_next->rrdset_prev = rc; - - st->alarms = rc; + netdata_rwlock_wrlock(&st->alerts.rwlock); + DOUBLE_LINKED_LIST_APPEND_UNSAFE(st->alerts.base, rc, prev, next); + netdata_rwlock_unlock(&st->alerts.rwlock); if(rc->update_every < rc->rrdset->update_every) { - error("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rc->rrdset->id, rc->name, rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every); + error("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rrdset_id(rc->rrdset), rrdcalc_name(rc), rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every); rc->update_every = rc->rrdset->update_every; } if(!isnan(rc->green) && isnan(st->green)) { debug(D_HEALTH, "Health alarm '%s.%s' green threshold set from " NETDATA_DOUBLE_FORMAT_AUTO - " to " NETDATA_DOUBLE_FORMAT_AUTO ".", rc->rrdset->id, rc->name, rc->rrdset->green, rc->green); + " to " NETDATA_DOUBLE_FORMAT_AUTO ".", rrdset_id(rc->rrdset), rrdcalc_name(rc), rc->rrdset->green, rc->green); st->green = rc->green; } if(!isnan(rc->red) && isnan(st->red)) { debug(D_HEALTH, "Health alarm '%s.%s' red threshold set from " NETDATA_DOUBLE_FORMAT_AUTO " to " NETDATA_DOUBLE_FORMAT_AUTO - ".", rc->rrdset->id, rc->name, rc->rrdset->red, rc->red); + ".", rrdset_id(rc->rrdset), rrdcalc_name(rc), rc->rrdset->red, rc->red); st->red = rc->red; } - rc->local = rrdvar_create_and_index("local", &st->rrdvar_root_index, rc->name, RRDVAR_TYPE_CALCULATED, RRDVAR_OPTION_RRDCALC_LOCAL_VAR, &rc->value); - rc->family = rrdvar_create_and_index("family", &st->rrdfamily->rrdvar_root_index, rc->name, RRDVAR_TYPE_CALCULATED, RRDVAR_OPTION_RRDCALC_FAMILY_VAR, &rc->value); - - char fullname[RRDVAR_MAX_LENGTH + 1]; - snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rc->name); - rc->hostid = rrdvar_create_and_index("host", &host->rrdvar_root_index, fullname, RRDVAR_TYPE_CALCULATED, RRDVAR_OPTION_RRDCALC_HOST_CHARTID_VAR, &rc->value); + char buf[RRDVAR_MAX_LENGTH + 1]; + snprintfz(buf, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_name(st), rrdcalc_name(rc)); + STRING *rrdset_name_rrdcalc_name = string_strdupz(buf); + snprintfz(buf, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_id(st), rrdcalc_name(rc)); + STRING *rrdset_id_rrdcalc_name = string_strdupz(buf); - snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rc->name); - rc->hostname = rrdvar_create_and_index("host", &host->rrdvar_root_index, fullname, RRDVAR_TYPE_CALCULATED, RRDVAR_OPTION_RRDCALC_HOST_CHARTNAME_VAR, &rc->value); - - if(rc->hostid && !rc->hostname) - rc->hostid->options |= RRDVAR_OPTION_RRDCALC_HOST_CHARTNAME_VAR; + rc->rrdvar_local = rrdvar_add_and_acquire( + "local", + st->rrdvars, + rc->name, + RRDVAR_TYPE_CALCULATED, + RRDVAR_FLAG_RRDCALC_LOCAL_VAR, + &rc->value); - if(!rc->units) rc->units = strdupz(st->units); + rc->rrdvar_family = rrdvar_add_and_acquire( + "family", + rrdfamily_rrdvars_dict(st->rrdfamily), + rc->name, + RRDVAR_TYPE_CALCULATED, + RRDVAR_FLAG_RRDCALC_FAMILY_VAR, + &rc->value); + + rc->rrdvar_host_chart_name = rrdvar_add_and_acquire( + "host", + host->rrdvars, + rrdset_name_rrdcalc_name, + RRDVAR_TYPE_CALCULATED, + RRDVAR_FLAG_RRDCALC_HOST_CHARTNAME_VAR, + &rc->value); + + rc->rrdvar_host_chart_id = rrdvar_add_and_acquire( + "host", + host->rrdvars, + rrdset_id_rrdcalc_name, + RRDVAR_TYPE_CALCULATED, + RRDVAR_FLAG_RRDCALC_HOST_CHARTID_VAR | ((rc->rrdvar_host_chart_name) ? 0 : RRDVAR_FLAG_RRDCALC_HOST_CHARTNAME_VAR), + &rc->value); + + string_freez(rrdset_id_rrdcalc_name); + string_freez(rrdset_name_rrdcalc_name); + + if(!rc->units) + rc->units = string_dup(st->units); + + rrdcalc_update_info_using_rrdset_labels(rc); time_t now = now_realtime_sec(); + ALARM_ENTRY *ae = health_create_alarm_entry( host, rc->id, @@ -108,216 +271,224 @@ static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) { rc->units, rc->info, 0, - 0); - health_alarm_log(host, ae); -} - -static int rrdcalc_is_matching_rrdset(RRDCALC *rc, RRDSET *st) { - if((rc->hash_chart != st->hash || strcmp(rc->chart, st->id) != 0) && - (rc->hash_chart != st->hash_name || strcmp(rc->chart, st->name) != 0)) - return 0; - - if (rc->module_pattern && !simple_pattern_matches(rc->module_pattern, st->module_name)) - return 0; - - if (rc->plugin_pattern && !simple_pattern_matches(rc->plugin_pattern, st->plugin_name)) - return 0; + rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0); - if (st->rrdhost->host_labels && rc->host_labels_pattern && !rrdlabels_match_simple_pattern_parsed(st->rrdhost->host_labels, rc->host_labels_pattern, '=')) - return 0; - - return 1; -} - -// this has to be called while the RRDHOST is locked -inline void rrdsetcalc_link_matching(RRDSET *st) { - RRDHOST *host = st->rrdhost; - // debug(D_HEALTH, "find matching alarms for chart '%s'", st->id); - - RRDCALC *rc; - for(rc = host->alarms; rc ; rc = rc->next) { - if(unlikely(rc->rrdset)) - continue; - - if(unlikely(rrdcalc_is_matching_rrdset(rc, st))) - rrdsetcalc_link(st, rc); - } + health_alarm_log_add_entry(host, ae); } -// this has to be called while the RRDHOST is locked -inline void rrdsetcalc_unlink(RRDCALC *rc) { +static void rrdcalc_unlink_from_rrdset(RRDCALC *rc, bool having_ll_wrlock) { RRDSET *st = rc->rrdset; if(!st) { - debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); - error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); + debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rrdcalc_chart_name(rc), rrdcalc_name(rc)); + error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rrdcalc_chart_name(rc), rrdcalc_name(rc)); return; } RRDHOST *host = st->rrdhost; time_t now = now_realtime_sec(); - ALARM_ENTRY *ae = health_create_alarm_entry( - host, - rc->id, - rc->next_event_id++, - rc->config_hash_id, - now, - rc->name, - rc->rrdset->id, - rc->rrdset->context, - rc->rrdset->family, - rc->classification, - rc->component, - rc->type, - rc->exec, - rc->recipient, - now - rc->last_status_change, - rc->old_value, - rc->value, - rc->status, - RRDCALC_STATUS_REMOVED, - rc->source, - rc->units, - rc->info, - 0, - 0); - health_alarm_log(host, ae); - debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname); + if (likely(rc->status != RRDCALC_STATUS_REMOVED)) { + ALARM_ENTRY *ae = health_create_alarm_entry( + host, + rc->id, + rc->next_event_id++, + rc->config_hash_id, + now, + rc->name, + rc->rrdset->id, + rc->rrdset->context, + rc->rrdset->family, + rc->classification, + rc->component, + rc->type, + rc->exec, + rc->recipient, + now - rc->last_status_change, + rc->old_value, + rc->value, + rc->status, + RRDCALC_STATUS_REMOVED, + rc->source, + rc->units, + rc->info, + 0, + 0); + + health_alarm_log_add_entry(host, ae); + } + + debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rrdcalc_chart_name(rc), rrdcalc_name(rc), rrdset_id(st), rrdhost_hostname(host)); // unlink it - if(rc->rrdset_prev) - rc->rrdset_prev->rrdset_next = rc->rrdset_next; - if(rc->rrdset_next) - rc->rrdset_next->rrdset_prev = rc->rrdset_prev; + if(!having_ll_wrlock) + netdata_rwlock_wrlock(&st->alerts.rwlock); - if(st->alarms == rc) - st->alarms = rc->rrdset_next; + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(st->alerts.base, rc, prev, next); - rc->rrdset_prev = rc->rrdset_next = NULL; + if(!having_ll_wrlock) + netdata_rwlock_unlock(&st->alerts.rwlock); - rrdvar_free(host, &st->rrdvar_root_index, rc->local); - rc->local = NULL; + rc->rrdset = NULL; - rrdvar_free(host, &st->rrdfamily->rrdvar_root_index, rc->family); - rc->family = NULL; + rrdvar_release_and_del(st->rrdvars, rc->rrdvar_local); + rc->rrdvar_local = NULL; - rrdvar_free(host, &host->rrdvar_root_index, rc->hostid); - rc->hostid = NULL; + rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rc->rrdvar_family); + rc->rrdvar_family = NULL; - rrdvar_free(host, &host->rrdvar_root_index, rc->hostname); - rc->hostname = NULL; + rrdvar_release_and_del(host->rrdvars, rc->rrdvar_host_chart_id); + rc->rrdvar_host_chart_id = NULL; - rc->rrdset = NULL; + rrdvar_release_and_del(host->rrdvars, rc->rrdvar_host_chart_name); + rc->rrdvar_host_chart_name = NULL; // RRDCALC will remain in RRDHOST // so that if the matching chart is found in the future // it will be applied automatically } -RRDCALC *rrdcalc_find(RRDSET *st, const char *name) { - RRDCALC *rc; - uint32_t hash = simple_hash(name); +static inline bool rrdcalc_check_if_it_matches_rrdset(RRDCALC *rc, RRDSET *st) { + if ( (rc->chart != st->id) + && (rc->chart != st->name)) + return false; - for( rc = st->alarms; rc ; rc = rc->rrdset_next ) { - if(unlikely(rc->hash == hash && !strcmp(rc->name, name))) - return rc; - } + if (rc->module_pattern && !simple_pattern_matches(rc->module_pattern, rrdset_module_name(st))) + return false; - return NULL; + if (rc->plugin_pattern && !simple_pattern_matches(rc->plugin_pattern, rrdset_plugin_name(st))) + return false; + + if (st->rrdhost->rrdlabels && rc->host_labels_pattern && !rrdlabels_match_simple_pattern_parsed(st->rrdhost->rrdlabels, rc->host_labels_pattern, '=')) + return false; + + return true; } -inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name) { +void rrdcalc_link_matching_alerts_to_rrdset(RRDSET *st) { + RRDHOST *host = st->rrdhost; + // debug(D_HEALTH, "find matching alarms for chart '%s'", st->id); + RRDCALC *rc; + foreach_rrdcalc_in_rrdhost_read(host, rc) { + if(rc->rrdset) + continue; - if(unlikely(!chart)) { - error("attempt to find RRDCALC '%s' without giving a chart name", name); - return 1; + if(unlikely(rrdcalc_check_if_it_matches_rrdset(rc, st))) + rrdcalc_link_to_rrdset(st, rc); } + foreach_rrdcalc_in_rrdhost_done(rc); +} - if(unlikely(!hash_chart)) hash_chart = simple_hash(chart); - if(unlikely(!hash_name)) hash_name = simple_hash(name); +static inline int rrdcalc_check_and_link_rrdset_callback(RRDSET *st, void *rrdcalc) { + RRDCALC *rc = rrdcalc; - // make sure it does not already exist - for(rc = host->alarms; rc ; rc = rc->next) { - if (unlikely(rc->chart && rc->hash == hash_name && rc->hash_chart == hash_chart && !strcmp(name, rc->name) && !strcmp(chart, rc->chart))) { - debug(D_HEALTH, "Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); - info("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); - return 1; - } + if(unlikely(rrdcalc_check_if_it_matches_rrdset(rc, st))) { + rrdcalc_link_to_rrdset(st, rc); + return -1; } return 0; } -inline uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id) { - if(chart && name) { - uint32_t hash_chart = simple_hash(chart); - uint32_t hash_name = simple_hash(name); - - // re-use old IDs, by looking them up in the alarm log - ALARM_ENTRY *ae; - for(ae = host->health_log.alarms; ae ;ae = ae->next) { - if(unlikely(ae->hash_name == hash_name && ae->hash_chart == hash_chart && !strcmp(name, ae->name) && !strcmp(chart, ae->chart))) { - if(next_event_id) *next_event_id = ae->alarm_event_id + 1; - return ae->alarm_id; - } +// ---------------------------------------------------------------------------- +// RRDCALC rrdhost index management - constructor + +struct rrdcalc_constructor { + RRDHOST *rrdhost; // the host we operate upon + RRDCALC *from_config; // points to the original RRDCALC, as loaded from the config + RRDCALCTEMPLATE *from_rrdcalctemplate; // the template this alert is generated from + RRDSET *rrdset; // when this comes from rrdcalctemplate, we have a matching rrdset + const char *overwrite_alert_name; // when we have a dimension foreach, the alert is renamed + const char *overwrite_dimensions; // when we have a dimension foreach, the dimensions filter is renamed + + enum { + RRDCALC_REACT_NONE, + RRDCALC_REACT_NEW, + } react_action; + + bool existing_from_template; +}; + +static void rrdcalc_rrdhost_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *constructor_data) { + RRDCALC *rc = rrdcalc; + struct rrdcalc_constructor *ctr = constructor_data; + RRDHOST *host = ctr->rrdhost; + + rc->key = string_strdupz(dictionary_acquired_item_name(item)); + + if(ctr->from_rrdcalctemplate) { + rc->run_flags |= RRDCALC_FLAG_FROM_TEMPLATE; + + RRDCALCTEMPLATE *rt = ctr->from_rrdcalctemplate; + RRDSET *st = ctr->rrdset; + + rc->next_event_id = 1; + rc->name = (ctr->overwrite_alert_name) ? string_strdupz(ctr->overwrite_alert_name) : string_dup(rt->name); + rc->chart = string_dup(st->id); + uuid_copy(rc->config_hash_id, rt->config_hash_id); + + rc->dimensions = (ctr->overwrite_dimensions) ? string_strdupz(ctr->overwrite_dimensions) : string_dup(rt->dimensions); + rc->foreach_dimension = NULL; + rc->foreach_dimension_pattern = NULL; + + rc->green = rt->green; + rc->red = rt->red; + rc->value = NAN; + rc->old_value = NAN; + + rc->delay_up_duration = rt->delay_up_duration; + rc->delay_down_duration = rt->delay_down_duration; + rc->delay_max_duration = rt->delay_max_duration; + rc->delay_multiplier = rt->delay_multiplier; + + rc->last_repeat = 0; + rc->times_repeat = 0; + rc->warn_repeat_every = rt->warn_repeat_every; + rc->crit_repeat_every = rt->crit_repeat_every; + + rc->group = rt->group; + rc->after = rt->after; + rc->before = rt->before; + rc->update_every = rt->update_every; + rc->options = rt->options; + + rc->exec = string_dup(rt->exec); + rc->recipient = string_dup(rt->recipient); + rc->source = string_dup(rt->source); + rc->units = string_dup(rt->units); + rc->info = string_dup(rt->info); + rc->original_info = string_dup(rt->info); + + rc->classification = string_dup(rt->classification); + rc->component = string_dup(rt->component); + rc->type = string_dup(rt->type); + + if(rt->calculation) { + rc->calculation = expression_parse(rt->calculation->source, NULL, NULL); + if(!rc->calculation) + error("Health alarm '%s.%s': failed to parse calculation expression '%s'", rrdset_id(st), rrdcalctemplate_name(rt), rt->calculation->source); + } + if(rt->warning) { + rc->warning = expression_parse(rt->warning->source, NULL, NULL); + if(!rc->warning) + error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", rrdset_id(st), rrdcalctemplate_name(rt), rt->warning->source); + } + if(rt->critical) { + rc->critical = expression_parse(rt->critical->source, NULL, NULL); + if(!rc->critical) + error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", rrdset_id(st), rrdcalctemplate_name(rt), rt->critical->source); } } - - if (unlikely(!host->health_log.next_alarm_id)) - host->health_log.next_alarm_id = (uint32_t)now_realtime_sec(); - - return host->health_log.next_alarm_id++; -} - -/** - * Alarm name with dimension - * - * Change the name of the current alarm appending a new diagram. - * - * @param name the alarm name - * @param namelen is the length of the previous vector. - * @param dim the dimension of the chart. - * @param dimlen is the length of the previous vector. - * - * @return It returns the new name on success and the old otherwise - */ -char *alarm_name_with_dim(char *name, size_t namelen, const char *dim, size_t dimlen) { - char *newname,*move; - - newname = mallocz(namelen + dimlen + 2); - move = newname; - memcpy(move, name, namelen); - move += namelen; - - *move++ = '_'; - memcpy(move, dim, dimlen); - move += dimlen; - *move = '\0'; - - return newname; -} - -/** - * Remove pipe comma - * - * Remove the pipes and commas converting to space. - * - * @param str the string to change. - */ -void dimension_remove_pipe_comma(char *str) { - while(*str) { - if(*str == '|' || *str == ',') *str = ' '; - - str++; + else if(ctr->from_config) { + // dictionary has already copied all the members values and pointers + // no need for additional work in this case + ; } -} -inline void rrdcalc_add_to_host(RRDHOST *host, RRDCALC *rc) { - rrdhost_check_rdlock(host); + rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->name, &rc->next_event_id); if(rc->calculation) { rc->calculation->status = &rc->status; @@ -343,351 +514,241 @@ inline void rrdcalc_add_to_host(RRDHOST *host, RRDCALC *rc) { rc->critical->rrdcalc = rc; } - if(!rc->foreachdim) { - // link it to the host alarms list - if(likely(host->alarms)) { - // append it - RRDCALC *t; - for(t = host->alarms; t && t->next ; t = t->next) ; - t->next = rc; - } - else { - host->alarms = rc; - } - - // link it to its chart - RRDSET *st; - rrdset_foreach_read(st, host) { - if(rrdcalc_is_matching_rrdset(rc, st)) { - rrdsetcalc_link(st, rc); - break; - } - } - } else { - //link it case there is a foreach - if(likely(host->alarms_with_foreach)) { - // append it - RRDCALC *t; - for(t = host->alarms_with_foreach; t && t->next ; t = t->next) ; - t->next = rc; - } - else { - host->alarms_with_foreach = rc; - } + debug(D_HEALTH, "Health added alarm '%s.%s': exec '%s', recipient '%s', green " NETDATA_DOUBLE_FORMAT_AUTO + ", red " NETDATA_DOUBLE_FORMAT_AUTO + ", lookup: group %d, after %d, before %d, options %u, dimensions '%s', for each dimension '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f, warn_repeat_every %u, crit_repeat_every %u", + rrdcalc_chart_name(rc), + rrdcalc_name(rc), + (rc->exec)?rrdcalc_exec(rc):"DEFAULT", + (rc->recipient)?rrdcalc_recipient(rc):"DEFAULT", + rc->green, + rc->red, + (int)rc->group, + rc->after, + rc->before, + rc->options, + (rc->dimensions)?rrdcalc_dimensions(rc):"NONE", + (rc->foreach_dimension)?rrdcalc_foreachdim(rc):"NONE", + rc->update_every, + (rc->calculation)?rc->calculation->parsed_as:"NONE", + (rc->warning)?rc->warning->parsed_as:"NONE", + (rc->critical)?rc->critical->parsed_as:"NONE", + rrdcalc_source(rc), + rc->delay_up_duration, + rc->delay_down_duration, + rc->delay_max_duration, + rc->delay_multiplier, + rc->warn_repeat_every, + rc->crit_repeat_every + ); - //I am not linking this alarm direct to the host here, this will be done when the children is created - } + ctr->react_action = RRDCALC_REACT_NEW; } -inline RRDCALC *rrdcalc_create_from_template(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart) { - debug(D_HEALTH, "Health creating dynamic alarm (from template) '%s.%s'", chart, rt->name); - - if(rrdcalc_exists(host, chart, rt->name, 0, 0)) - return NULL; - - RRDCALC *rc = callocz(1, sizeof(RRDCALC)); - rc->next_event_id = 1; - rc->name = strdupz(rt->name); - rc->hash = simple_hash(rc->name); - rc->chart = strdupz(chart); - rc->hash_chart = simple_hash(rc->chart); - uuid_copy(rc->config_hash_id, rt->config_hash_id); - - rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->name, &rc->next_event_id); - - if(rt->dimensions) rc->dimensions = strdupz(rt->dimensions); - if(rt->foreachdim) { - rc->foreachdim = strdupz(rt->foreachdim); - rc->spdim = health_pattern_from_foreach(rc->foreachdim); - } - rc->foreachcounter = rt->foreachcounter; - - rc->green = rt->green; - rc->red = rt->red; - rc->value = NAN; - rc->old_value = NAN; - - rc->delay_up_duration = rt->delay_up_duration; - rc->delay_down_duration = rt->delay_down_duration; - rc->delay_max_duration = rt->delay_max_duration; - rc->delay_multiplier = rt->delay_multiplier; - - rc->last_repeat = 0; - rc->times_repeat = 0; - rc->warn_repeat_every = rt->warn_repeat_every; - rc->crit_repeat_every = rt->crit_repeat_every; - - rc->group = rt->group; - rc->after = rt->after; - rc->before = rt->before; - rc->update_every = rt->update_every; - rc->options = rt->options; - - if(rt->exec) rc->exec = strdupz(rt->exec); - if(rt->recipient) rc->recipient = strdupz(rt->recipient); - if(rt->source) rc->source = strdupz(rt->source); - if(rt->units) rc->units = strdupz(rt->units); - if(rt->info) rc->info = strdupz(rt->info); - - if (rt->classification) rc->classification = strdupz(rt->classification); - if (rt->component) rc->component = strdupz(rt->component); - if (rt->type) rc->type = strdupz(rt->type); - - if(rt->calculation) { - rc->calculation = expression_parse(rt->calculation->source, NULL, NULL); - if(!rc->calculation) - error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, rt->name, rt->calculation->source); - } - if(rt->warning) { - rc->warning = expression_parse(rt->warning->source, NULL, NULL); - if(!rc->warning) - error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, rt->name, rt->warning->source); - } - if(rt->critical) { - rc->critical = expression_parse(rt->critical->source, NULL, NULL); - if(!rc->critical) - error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, rt->name, rt->critical->source); - } +static bool rrdcalc_rrdhost_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *rrdcalc_new __maybe_unused, void *constructor_data ) { + RRDCALC *rc = rrdcalc; + struct rrdcalc_constructor *ctr = constructor_data; - debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', recipient '%s', green " NETDATA_DOUBLE_FORMAT_AUTO - ", red " NETDATA_DOUBLE_FORMAT_AUTO - ", lookup: group %d, after %d, before %d, options %u, dimensions '%s', for each dimension '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f, warn_repeat_every %u, crit_repeat_every %u", - (rc->chart)?rc->chart:"NOCHART", - rc->name, - (rc->exec)?rc->exec:"DEFAULT", - (rc->recipient)?rc->recipient:"DEFAULT", - rc->green, - rc->red, - (int)rc->group, - rc->after, - rc->before, - rc->options, - (rc->dimensions)?rc->dimensions:"NONE", - (rc->foreachdim)?rc->foreachdim:"NONE", - rc->update_every, - (rc->calculation)?rc->calculation->parsed_as:"NONE", - (rc->warning)?rc->warning->parsed_as:"NONE", - (rc->critical)?rc->critical->parsed_as:"NONE", - rc->source, - rc->delay_up_duration, - rc->delay_down_duration, - rc->delay_max_duration, - rc->delay_multiplier, - rc->warn_repeat_every, - rc->crit_repeat_every - ); + if(rc->run_flags & RRDCALC_FLAG_FROM_TEMPLATE) + ctr->existing_from_template = true; + else + ctr->existing_from_template = false; - rrdcalc_add_to_host(host, rc); - if(!rt->foreachdim) { - RRDCALC *rdcmp = (RRDCALC *) avl_insert_lock(&(host)->alarms_idx_health_log,(avl_t *)rc); - if (rdcmp != rc) { - error("Cannot insert the alarm index ID %s",rc->name); - } - } + ctr->react_action = RRDCALC_REACT_NONE; - return rc; + return false; } -/** - * Create from RRDCALC - * - * Create a new alarm using another alarm as template. - * - * @param rc is the alarm that will be used as source - * @param host is the host structure. - * @param name is the newest chart name. - * @param dimension is the current dimension - * @param foreachdim the whole list of dimension - * - * @return it returns the new alarm changed. - */ -inline RRDCALC *rrdcalc_create_from_rrdcalc(RRDCALC *rc, RRDHOST *host, const char *name, const char *dimension) { - RRDCALC *newrc = callocz(1, sizeof(RRDCALC)); - - newrc->next_event_id = 1; - newrc->id = rrdcalc_get_unique_id(host, rc->chart, name, &rc->next_event_id); - newrc->name = (char *)name; - newrc->hash = simple_hash(newrc->name); - newrc->chart = strdupz(rc->chart); - newrc->hash_chart = simple_hash(rc->chart); - uuid_copy(newrc->config_hash_id, *((uuid_t *) &rc->config_hash_id)); - - newrc->dimensions = strdupz(dimension); - newrc->foreachdim = NULL; - rc->foreachcounter++; - newrc->foreachcounter = rc->foreachcounter; - - newrc->green = rc->green; - newrc->red = rc->red; - newrc->value = NAN; - newrc->old_value = NAN; - - newrc->delay_up_duration = rc->delay_up_duration; - newrc->delay_down_duration = rc->delay_down_duration; - newrc->delay_max_duration = rc->delay_max_duration; - newrc->delay_multiplier = rc->delay_multiplier; - - newrc->last_repeat = 0; - newrc->times_repeat = 0; - newrc->warn_repeat_every = rc->warn_repeat_every; - newrc->crit_repeat_every = rc->crit_repeat_every; - - newrc->group = rc->group; - newrc->after = rc->after; - newrc->before = rc->before; - newrc->update_every = rc->update_every; - newrc->options = rc->options; - - if(rc->exec) newrc->exec = strdupz(rc->exec); - if(rc->recipient) newrc->recipient = strdupz(rc->recipient); - if(rc->source) newrc->source = strdupz(rc->source); - if(rc->units) newrc->units = strdupz(rc->units); - if(rc->info) newrc->info = strdupz(rc->info); - - if (rc->classification) newrc->classification = strdupz(rc->classification); - if (rc->component) newrc->component = strdupz(rc->component); - if (rc->type) newrc->type = strdupz(rc->type); +static void rrdcalc_rrdhost_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *constructor_data) { + RRDCALC *rc = rrdcalc; + struct rrdcalc_constructor *ctr = constructor_data; + RRDHOST *host = ctr->rrdhost; - if(rc->calculation) { - newrc->calculation = expression_parse(rc->calculation->source, NULL, NULL); - if(!newrc->calculation) - error("Health alarm '%s.%s': failed to parse calculation expression '%s'", rc->chart, rc->name, rc->calculation->source); - } + if(ctr->react_action == RRDCALC_REACT_NEW) { + if(ctr->rrdset) + rrdcalc_link_to_rrdset(ctr->rrdset, rc); - if(rc->warning) { - newrc->warning = expression_parse(rc->warning->source, NULL, NULL); - if(!newrc->warning) - error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", rc->chart, rc->name, rc->warning->source); - } - - if(rc->critical) { - newrc->critical = expression_parse(rc->critical->source, NULL, NULL); - if(!newrc->critical) - error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", rc->chart, rc->name, rc->critical->source); + else if (ctr->from_rrdcalctemplate) + rrdcontext_foreach_instance_with_rrdset_in_context(host, string2str(ctr->from_rrdcalctemplate->context), rrdcalc_check_and_link_rrdset_callback, rc); } - - return newrc; } -void rrdcalc_free(RRDCALC *rc) { +// ---------------------------------------------------------------------------- +// RRDCALC rrdhost index management - destructor + +static void rrdcalc_free_internals(RRDCALC *rc) { if(unlikely(!rc)) return; expression_free(rc->calculation); expression_free(rc->warning); expression_free(rc->critical); - freez(rc->name); - freez(rc->chart); - freez(rc->family); - freez(rc->dimensions); - freez(rc->foreachdim); - freez(rc->exec); - freez(rc->recipient); - freez(rc->source); - freez(rc->units); - freez(rc->info); - freez(rc->classification); - freez(rc->component); - freez(rc->type); - simple_pattern_free(rc->spdim); - freez(rc->host_labels); + string_freez(rc->key); + string_freez(rc->name); + string_freez(rc->chart); + string_freez(rc->dimensions); + string_freez(rc->foreach_dimension); + string_freez(rc->exec); + string_freez(rc->recipient); + string_freez(rc->source); + string_freez(rc->units); + string_freez(rc->info); + string_freez(rc->original_info); + string_freez(rc->classification); + string_freez(rc->component); + string_freez(rc->type); + string_freez(rc->host_labels); + string_freez(rc->module_match); + string_freez(rc->plugin_match); + + simple_pattern_free(rc->foreach_dimension_pattern); simple_pattern_free(rc->host_labels_pattern); - freez(rc->module_match); simple_pattern_free(rc->module_pattern); - freez(rc->plugin_match); simple_pattern_free(rc->plugin_pattern); - freez(rc); } -void rrdcalc_unlink_and_free(RRDHOST *host, RRDCALC *rc) { - if(unlikely(!rc)) return; +static void rrdcalc_rrdhost_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *rrdhost __maybe_unused) { + RRDCALC *rc = rrdcalc; + //RRDHOST *host = rrdhost; - debug(D_HEALTH, "Health removing alarm '%s.%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname); + if(unlikely(rc->rrdset)) + rrdcalc_unlink_from_rrdset(rc, false); - // unlink it from RRDSET - if(rc->rrdset) rrdsetcalc_unlink(rc); + // any destruction actions that require other locks + // have to be placed in rrdcalc_del(), because the object is actually locked for deletion - // unlink it from RRDHOST - if(unlikely(rc == host->alarms)) - host->alarms = rc->next; - else { - RRDCALC *t; - for(t = host->alarms; t && t->next != rc; t = t->next) ; - if(t) { - t->next = rc->next; - rc->next = NULL; - } - else - error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname); - } + rrdcalc_free_internals(rc); +} - RRDCALC *rdcmp = (RRDCALC *) avl_search_lock(&(host)->alarms_idx_health_log, (avl_t *)rc); - if (rdcmp) { - rdcmp = (RRDCALC *) avl_remove_lock(&(host)->alarms_idx_health_log, (avl_t *)rc); - if (!rdcmp) { - error("Cannot remove the health alarm index from health_log"); - } - } +// ---------------------------------------------------------------------------- +// RRDCALC rrdhost index management - index API - rdcmp = (RRDCALC *) avl_search_lock(&(host)->alarms_idx_name, (avl_t *)rc); - if (rdcmp) { - rdcmp = (RRDCALC *) avl_remove_lock(&(host)->alarms_idx_name, (avl_t *)rc); - if (!rdcmp) { - error("Cannot remove the health alarm index from idx_name"); - } +void rrdcalc_rrdhost_index_init(RRDHOST *host) { + if(!host->rrdcalc_root_index) { + host->rrdcalc_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + + dictionary_register_insert_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_insert_callback, NULL); + dictionary_register_conflict_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_conflict_callback, NULL); + dictionary_register_react_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_react_callback, NULL); + dictionary_register_delete_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_delete_callback, host); } +} - rrdcalc_free(rc); +void rrdcalc_rrdhost_index_destroy(RRDHOST *host) { + dictionary_destroy(host->rrdcalc_root_index); + host->rrdcalc_root_index = NULL; } -void rrdcalc_foreach_unlink_and_free(RRDHOST *host, RRDCALC *rc) { +void rrdcalc_add_from_rrdcalctemplate(RRDHOST *host, RRDCALCTEMPLATE *rt, RRDSET *st, const char *overwrite_alert_name, const char *overwrite_dimensions) { + char key[RRDCALC_MAX_KEY_SIZE + 1]; + size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_id(st), + overwrite_alert_name?overwrite_alert_name:string2str(rt->name)); + + struct rrdcalc_constructor tmp = { + .rrdhost = host, + .from_config = NULL, + .from_rrdcalctemplate = rt, + .rrdset = st, + .overwrite_alert_name = overwrite_alert_name, + .overwrite_dimensions = overwrite_dimensions, + .react_action = RRDCALC_REACT_NONE, + .existing_from_template = false, + }; + + dictionary_set_advanced(host->rrdcalc_root_index, key, (ssize_t)(key_len + 1), NULL, sizeof(RRDCALC), &tmp); + if(tmp.react_action != RRDCALC_REACT_NEW && tmp.existing_from_template == false) + error("RRDCALC: from template '%s' on chart '%s' with key '%s', failed to be added to host '%s'. It is manually configured.", + string2str(rt->name), rrdset_id(st), key, rrdhost_hostname(host)); +} - if(unlikely(rc == host->alarms_with_foreach)) - host->alarms_with_foreach = rc->next; - else { - RRDCALC *t; - for(t = host->alarms_with_foreach; t && t->next != rc; t = t->next) ; - if(t) { - t->next = rc->next; - rc->next = NULL; +int rrdcalc_add_from_config(RRDHOST *host, RRDCALC *rc) { + if(!rc->chart) { + error("Health configuration for alarm '%s' does not have a chart", rrdcalc_name(rc)); + return 0; + } + + if(!rc->update_every) { + error("Health configuration for alarm '%s.%s' has no frequency (parameter 'every'). Ignoring it.", rrdcalc_chart_name(rc), rrdcalc_name(rc)); + return 0; + } + + if(!RRDCALC_HAS_DB_LOOKUP(rc) && !rc->calculation && !rc->warning && !rc->critical) { + error("Health configuration for alarm '%s.%s' is useless (no db lookup, no calculation, no warning and no critical expressions)", rrdcalc_chart_name(rc), rrdcalc_name(rc)); + return 0; + } + + char key[RRDCALC_MAX_KEY_SIZE + 1]; + size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, string2str(rc->chart), string2str(rc->name)); + + struct rrdcalc_constructor tmp = { + .rrdhost = host, + .from_config = rc, + .from_rrdcalctemplate = NULL, + .rrdset = NULL, + .react_action = RRDCALC_REACT_NONE, + }; + + int ret = 1; + RRDCALC *t = dictionary_set_advanced(host->rrdcalc_root_index, key, (ssize_t)(key_len + 1), rc, sizeof(RRDCALC), &tmp); + if(tmp.react_action == RRDCALC_REACT_NEW) { + // we copied rc into the dictionary, so we have to free the container here + freez(rc); + rc = t; + + // since we loaded this config from configuration, we need to check if we can link it to alarms + RRDSET *st; + rrdset_foreach_read(st, host) { + if (unlikely(rrdcalc_check_and_link_rrdset_callback(st, rc) == -1)) + break; } - else - error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname); + rrdset_foreach_done(st); } + else { + error( + "RRDCALC: from config '%s' on chart '%s' failed to be added to host '%s'. It already exists.", + string2str(rc->name), + string2str(rc->chart), + rrdhost_hostname(host)); + + ret = 0; + + // free all of it, internals and the container + rrdcalc_free_unused_rrdcalc_loaded_from_config(rc); + } + + return ret; +} - rrdcalc_free(rc); +static void rrdcalc_unlink_and_delete(RRDHOST *host, RRDCALC *rc, bool having_ll_wrlock) { + if(rc->rrdset) + rrdcalc_unlink_from_rrdset(rc, having_ll_wrlock); + + dictionary_del_advanced(host->rrdcalc_root_index, string2str(rc->key), (ssize_t)string_strlen(rc->key) + 1); } -static void rrdcalc_labels_unlink_alarm_loop(RRDHOST *host, RRDCALC *alarms) { - for(RRDCALC *rc = alarms ; rc ; ) { - RRDCALC *rc_next = rc->next; - if (!rc->host_labels) { - rc = rc_next; +// ---------------------------------------------------------------------------- +// RRDCALC cleanup API functions + +void rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(RRDHOST *host) { + RRDCALC *rc; + foreach_rrdcalc_in_rrdhost_reentrant(host, rc) { + if (!rc->host_labels) continue; - } - if(!rrdlabels_match_simple_pattern_parsed(host->host_labels, rc->host_labels_pattern, '=')) { + if(!rrdlabels_match_simple_pattern_parsed(host->rrdlabels, rc->host_labels_pattern, '=')) { info("Health configuration for alarm '%s' cannot be applied, because the host %s does not have the label(s) '%s'", - rc->name, - host->hostname, - rc->host_labels); + rrdcalc_name(rc), + rrdhost_hostname(host), + rrdcalc_host_labels(rc)); - if(host->alarms == alarms) - rrdcalc_unlink_and_free(host, rc); - else - rrdcalc_foreach_unlink_and_free(host, rc); + rrdcalc_unlink_and_delete(host, rc, false); } - rc = rc_next; } + foreach_rrdcalc_in_rrdhost_done(rc); } -void rrdcalc_labels_unlink_alarm_from_host(RRDHOST *host) { - rrdcalc_labels_unlink_alarm_loop(host, host->alarms); - rrdcalc_labels_unlink_alarm_loop(host, host->alarms_with_foreach); -} - -void rrdcalc_labels_unlink() { +void rrdcalc_delete_alerts_not_matching_host_labels_from_all_hosts() { rrd_rdlock(); RRDHOST *host; @@ -695,70 +756,45 @@ void rrdcalc_labels_unlink() { if (unlikely(!host->health_enabled)) continue; - if (host->host_labels) { - rrdhost_wrlock(host); - - rrdcalc_labels_unlink_alarm_from_host(host); - - rrdhost_unlock(host); - } + if (host->rrdlabels) + rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(host); } rrd_unlock(); } -// ---------------------------------------------------------------------------- -// Alarm - - -/** - * Alarm is repeating - * - * Is this alarm repeating ? - * - * @param host The structure that has the binary tree - * @param alarm_id the id of the alarm to search - * - * @return It returns 1 case it is repeating and 0 otherwise - */ -int alarm_isrepeating(RRDHOST *host, uint32_t alarm_id) { - RRDCALC findme; - findme.id = alarm_id; - RRDCALC *rc = (RRDCALC *)avl_search_lock(&host->alarms_idx_health_log, (avl_t *)&findme); - if (!rc) { - return 0; +void rrdcalc_unlink_all_rrdset_alerts(RRDSET *st) { + RRDCALC *rc, *last = NULL; + netdata_rwlock_wrlock(&st->alerts.rwlock); + while((rc = st->alerts.base)) { + if(last == rc) { + error("RRDCALC: malformed list of alerts linked to chart - cannot cleanup - giving up."); + break; + } + last = rc; + + if(rc->run_flags & RRDCALC_FLAG_FROM_TEMPLATE) { + // if the alert comes from a template we can just delete it + rrdcalc_unlink_and_delete(st->rrdhost, rc, true); + } + else { + // this is a configuration for a specific chart + // it should stay in the list + rrdcalc_unlink_from_rrdset(rc, true); + } + } - return rrdcalc_isrepeating(rc); + netdata_rwlock_unlock(&st->alerts.rwlock); } -/** - * Entry is repeating - * - * Check whether the id of alarm entry is yet present in the host structure - * - * @param host The structure that has the binary tree - * @param ae the alarm entry - * - * @return It returns 1 case it is repeating and 0 otherwise - */ -int alarm_entry_isrepeating(RRDHOST *host, ALARM_ENTRY *ae) { - return alarm_isrepeating(host, ae->alarm_id); +void rrdcalc_delete_all(RRDHOST *host) { + dictionary_flush(host->rrdcalc_root_index); } -/** - * Max last repeat - * - * Check the maximum last_repeat for the alarms associated a host - * - * @param host The structure that has the binary tree - * - * @return It returns 1 case it is repeating and 0 otherwise - */ -RRDCALC *alarm_max_last_repeat(RRDHOST *host, char *alarm_name,uint32_t hash) { - RRDCALC findme; - findme.name = alarm_name; - findme.hash = hash; - RRDCALC *rc = (RRDCALC *)avl_search_lock(&host->alarms_idx_name, (avl_t *)&findme); - - return rc; +void rrdcalc_free_unused_rrdcalc_loaded_from_config(RRDCALC *rc) { + if(rc->rrdset) + rrdcalc_unlink_from_rrdset(rc, false); + + rrdcalc_free_internals(rc); + freez(rc); } diff --git a/database/rrdcalc.h b/database/rrdcalc.h index 0dcd7ce69..a25c05cc6 100644 --- a/database/rrdcalc.h +++ b/database/rrdcalc.h @@ -10,55 +10,60 @@ // (defined in their update_every member below) // They increase the overhead of netdata. // -// These calculations are allocated and linked (->next) -// under RRDHOST. -// Then are also linked to RRDSET (of course only when the -// chart is found, via ->rrdset_next and ->rrdset_prev). -// This double-linked list is maintained sorted at all times -// having as RRDSET.calculations the RRDCALC to be processed -// next. - -#define RRDCALC_FLAG_DB_ERROR 0x00000001 -#define RRDCALC_FLAG_DB_NAN 0x00000002 -/* #define RRDCALC_FLAG_DB_STALE 0x00000004 */ -#define RRDCALC_FLAG_CALC_ERROR 0x00000008 -#define RRDCALC_FLAG_WARN_ERROR 0x00000010 -#define RRDCALC_FLAG_CRIT_ERROR 0x00000020 -#define RRDCALC_FLAG_RUNNABLE 0x00000040 -#define RRDCALC_FLAG_DISABLED 0x00000080 -#define RRDCALC_FLAG_SILENCED 0x00000100 -#define RRDCALC_FLAG_RUN_ONCE 0x00000200 -#define RRDCALC_FLAG_NO_CLEAR_NOTIFICATION 0x80000000 - +// These calculations are stored under RRDHOST. +// Then are also linked to RRDSET (of course only when a +// matching chart is found). + +typedef enum { + RRDCALC_FLAG_DB_ERROR = (1 << 0), + RRDCALC_FLAG_DB_NAN = (1 << 1), + // RRDCALC_FLAG_DB_STALE = (1 << 2), + RRDCALC_FLAG_CALC_ERROR = (1 << 3), + RRDCALC_FLAG_WARN_ERROR = (1 << 4), + RRDCALC_FLAG_CRIT_ERROR = (1 << 5), + RRDCALC_FLAG_RUNNABLE = (1 << 6), + RRDCALC_FLAG_DISABLED = (1 << 7), + RRDCALC_FLAG_SILENCED = (1 << 8), + RRDCALC_FLAG_RUN_ONCE = (1 << 9), + RRDCALC_FLAG_FROM_TEMPLATE = (1 << 10), // the rrdcalc has been created from a template +} RRDCALC_FLAGS; + +typedef enum { + // This list uses several other options from RRDR_OPTIONS for db lookups. + // To add an item here, you need to reserve a bit in RRDR_OPTIONS. + RRDCALC_OPTION_NO_CLEAR_NOTIFICATION = 0x80000000, +} RRDCALC_OPTIONS; + +#define RRDCALC_ALL_OPTIONS_EXCLUDING_THE_RRDR_ONES (RRDCALC_OPTION_NO_CLEAR_NOTIFICATION) struct rrdcalc { - avl_t avl; // the index, with key the id - this has to be first! + STRING *key; // the unique key in the host's rrdcalc_root_index + uint32_t id; // the unique id of this alarm uint32_t next_event_id; // the next event id that will be used for this alarm - char *name; // the name of this alarm - uint32_t hash; // the hash of the alarm name uuid_t config_hash_id; // a predictable hash_id based on specific alert configuration - char *exec; // the command to execute when this alarm switches state - char *recipient; // the recipient of the alarm (the first parameter to exec) + STRING *name; // the name of this alarm + STRING *chart; // the chart id this should be linked to - char *classification; // the class that this alarm belongs - char *component; // the component that this alarm refers to - char *type; // type of the alarm + STRING *exec; // the command to execute when this alarm switches state + STRING *recipient; // the recipient of the alarm (the first parameter to exec) - char *chart; // the chart id this should be linked to - uint32_t hash_chart; + STRING *classification; // the class that this alarm belongs + STRING *component; // the component that this alarm refers to + STRING *type; // type of the alarm - char *plugin_match; //the plugin name that should be linked to + STRING *plugin_match; // the plugin name that should be linked to SIMPLE_PATTERN *plugin_pattern; - char *module_match; //the module name that should be linked to + STRING *module_match; // the module name that should be linked to SIMPLE_PATTERN *module_pattern; - char *source; // the source of this alarm - char *units; // the units of the alarm - char *info; // a short description of the alarm + STRING *source; // the source of this alarm + STRING *units; // the units of the alarm + STRING *original_info; // the original info field before any variable replacement + STRING *info; // a short description of the alarm int update_every; // update frequency for the alarm @@ -69,15 +74,13 @@ struct rrdcalc { // ------------------------------------------------------------------------ // database lookup settings - char *dimensions; // the chart dimensions - char *foreachdim; // the group of dimensions that the `foreach` will be applied. - SIMPLE_PATTERN *spdim; // used if and only if there is a simple pattern for the chart. - int foreachcounter; // the number of alarms created with foreachdim, this also works as an id of the - // children + STRING *dimensions; // the chart dimensions + STRING *foreach_dimension; // the group of dimensions that the `foreach` will be applied. + SIMPLE_PATTERN *foreach_dimension_pattern; // used if and only if there is a simple pattern for the chart. RRDR_GROUPING group; // grouping method: average, max, etc. int before; // ending point in time-series int after; // starting point in time-series - uint32_t options; // calculation options + RRDCALC_OPTIONS options; // configuration options // ------------------------------------------------------------------------ // expressions related to the alarm @@ -98,29 +101,29 @@ struct rrdcalc { // ------------------------------------------------------------------------ // notification repeat settings - uint32_t warn_repeat_every; // interval between repeating warning notifications - uint32_t crit_repeat_every; // interval between repeating critical notifications + uint32_t warn_repeat_every; // interval between repeating warning notifications + uint32_t crit_repeat_every; // interval between repeating critical notifications // ------------------------------------------------------------------------ // Labels settings - char *host_labels; // the label read from an alarm file + STRING *host_labels; // the label read from an alarm file SIMPLE_PATTERN *host_labels_pattern; // the simple pattern of labels // ------------------------------------------------------------------------ // runtime information - RRDCALC_STATUS old_status; // the old status of the alarm + RRDCALC_STATUS old_status; // the old status of the alarm RRDCALC_STATUS status; // the current status of the alarm - NETDATA_DOUBLE value; // the current value of the alarm - NETDATA_DOUBLE old_value; // the previous value of the alarm + NETDATA_DOUBLE value; // the current value of the alarm + NETDATA_DOUBLE old_value; // the previous value of the alarm - uint32_t rrdcalc_flags; // check RRDCALC_FLAG_* + RRDCALC_FLAGS run_flags; // check RRDCALC_FLAG_* time_t last_updated; // the last update timestamp of the alarm time_t next_update; // the next update timestamp of the alarm time_t last_status_change; // the timestamp of the last time this alarm changed status - time_t last_repeat; // the last time the alarm got repeated + time_t last_repeat; // the last time the alarm got repeated uint32_t times_repeat; // number of times the alarm got repeated time_t db_after; // the first timestamp evaluated by the db lookup @@ -134,85 +137,105 @@ struct rrdcalc { // ------------------------------------------------------------------------ // variables this alarm exposes to the rest of the alarms - RRDVAR *local; - RRDVAR *family; - RRDVAR *hostid; - RRDVAR *hostname; + const RRDVAR_ACQUIRED *rrdvar_local; + const RRDVAR_ACQUIRED *rrdvar_family; + const RRDVAR_ACQUIRED *rrdvar_host_chart_id; + const RRDVAR_ACQUIRED *rrdvar_host_chart_name; // ------------------------------------------------------------------------ // the chart this alarm it is linked to + size_t labels_version; struct rrdset *rrdset; - // linking of this alarm on its chart - struct rrdcalc *rrdset_next; - struct rrdcalc *rrdset_prev; - struct rrdcalc *next; + struct rrdcalc *prev; }; +#define rrdcalc_name(rc) string2str((rc)->name) +#define rrdcalc_chart_name(rc) string2str((rc)->chart) +#define rrdcalc_exec(rc) string2str((rc)->exec) +#define rrdcalc_recipient(rc) string2str((rc)->recipient) +#define rrdcalc_classification(rc) string2str((rc)->classification) +#define rrdcalc_component(rc) string2str((rc)->component) +#define rrdcalc_type(rc) string2str((rc)->type) +#define rrdcalc_plugin_match(rc) string2str((rc)->plugin_match) +#define rrdcalc_module_match(rc) string2str((rc)->module_match) +#define rrdcalc_source(rc) string2str((rc)->source) +#define rrdcalc_units(rc) string2str((rc)->units) +#define rrdcalc_original_info(rc) string2str((rc)->original_info) +#define rrdcalc_info(rc) string2str((rc)->info) +#define rrdcalc_dimensions(rc) string2str((rc)->dimensions) +#define rrdcalc_foreachdim(rc) string2str((rc)->foreach_dimension) +#define rrdcalc_host_labels(rc) string2str((rc)->host_labels) + +#define foreach_rrdcalc_in_rrdhost_read(host, rc) \ + dfe_start_read((host)->rrdcalc_root_index, rc) \ + +#define foreach_rrdcalc_in_rrdhost_reentrant(host, rc) \ + dfe_start_reentrant((host)->rrdcalc_root_index, rc) + +#define foreach_rrdcalc_in_rrdhost_done(rc) \ + dfe_done(rc) + struct alert_config { - char *alarm; - char *template_key; - char *os; - char *host; - char *on; - char *families; - char *plugin; - char *module; - char *charts; - char *lookup; - char *calc; - char *warn; - char *crit; - char *every; - char *green; - char *red; - char *exec; - char *to; - char *units; - char *info; - char *classification; - char *component; - char *type; - char *delay; - char *options; - char *repeat; - char *host_labels; - - char *p_db_lookup_dimensions; - char *p_db_lookup_method; + STRING *alarm; + STRING *template_key; + STRING *os; + STRING *host; + STRING *on; + STRING *families; + STRING *plugin; + STRING *module; + STRING *charts; + STRING *lookup; + STRING *calc; + STRING *warn; + STRING *crit; + STRING *every; + STRING *green; + STRING *red; + STRING *exec; + STRING *to; + STRING *units; + STRING *info; + STRING *classification; + STRING *component; + STRING *type; + STRING *delay; + STRING *options; + STRING *repeat; + STRING *host_labels; + + STRING *p_db_lookup_dimensions; + STRING *p_db_lookup_method; + uint32_t p_db_lookup_options; int32_t p_db_lookup_after; int32_t p_db_lookup_before; int32_t p_update_every; }; -extern int alarm_isrepeating(RRDHOST *host, uint32_t alarm_id); -extern int alarm_entry_isrepeating(RRDHOST *host, ALARM_ENTRY *ae); -extern RRDCALC *alarm_max_last_repeat(RRDHOST *host, char *alarm_name, uint32_t hash); - #define RRDCALC_HAS_DB_LOOKUP(rc) ((rc)->after) -extern void rrdsetcalc_link_matching(RRDSET *st); -extern void rrdsetcalc_unlink(RRDCALC *rc); -extern RRDCALC *rrdcalc_find(RRDSET *st, const char *name); +void rrdcalc_update_info_using_rrdset_labels(RRDCALC *rc); -extern const char *rrdcalc_status2string(RRDCALC_STATUS status); +void rrdcalc_link_matching_alerts_to_rrdset(RRDSET *st); -extern void rrdcalc_free(RRDCALC *rc); -extern void rrdcalc_unlink_and_free(RRDHOST *host, RRDCALC *rc); +const RRDCALC_ACQUIRED *rrdcalc_from_rrdset_get(RRDSET *st, const char *alert_name); +void rrdcalc_from_rrdset_release(RRDSET *st, const RRDCALC_ACQUIRED *rca); +RRDCALC *rrdcalc_acquired_to_rrdcalc(const RRDCALC_ACQUIRED *rca); -extern int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name); -extern uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id); -extern RRDCALC *rrdcalc_create_from_template(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart); -extern RRDCALC *rrdcalc_create_from_rrdcalc(RRDCALC *rc, RRDHOST *host, const char *name, const char *dimension); -extern void rrdcalc_add_to_host(RRDHOST *host, RRDCALC *rc); -extern void dimension_remove_pipe_comma(char *str); -extern char *alarm_name_with_dim(char *name, size_t namelen, const char *dim, size_t dimlen); +const char *rrdcalc_status2string(RRDCALC_STATUS status); -extern void rrdcalc_labels_unlink(); -extern void rrdcalc_labels_unlink_alarm_from_host(RRDHOST *host); +void rrdcalc_free_unused_rrdcalc_loaded_from_config(RRDCALC *rc); + +uint32_t rrdcalc_get_unique_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id); +void rrdcalc_add_from_rrdcalctemplate(RRDHOST *host, RRDCALCTEMPLATE *rt, RRDSET *st, const char *overwrite_alert_name, const char *overwrite_dimensions); +int rrdcalc_add_from_config(RRDHOST *host, RRDCALC *rc); + +void rrdcalc_delete_alerts_not_matching_host_labels_from_all_hosts(); +void rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(RRDHOST *host); static inline int rrdcalc_isrepeating(RRDCALC *rc) { if (unlikely(rc->warn_repeat_every > 0 || rc->crit_repeat_every > 0)) { @@ -221,4 +244,15 @@ static inline int rrdcalc_isrepeating(RRDCALC *rc) { return 0; } +void rrdcalc_unlink_all_rrdset_alerts(RRDSET *st); +void rrdcalc_delete_all(RRDHOST *host); + +void rrdcalc_rrdhost_index_init(RRDHOST *host); +void rrdcalc_rrdhost_index_destroy(RRDHOST *host); + +#define RRDCALC_VAR_MAX 100 +#define RRDCALC_VAR_FAMILY "$family" +#define RRDCALC_VAR_LABEL "$label:" +#define RRDCALC_VAR_LABEL_LEN (sizeof(RRDCALC_VAR_LABEL)-1) + #endif //NETDATA_RRDCALC_H diff --git a/database/rrdcalctemplate.c b/database/rrdcalctemplate.c index 3f9804b93..87e085c93 100644 --- a/database/rrdcalctemplate.c +++ b/database/rrdcalctemplate.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#define NETDATA_HEALTH_INTERNALS #include "rrd.h" // ---------------------------------------------------------------------------- @@ -11,100 +10,232 @@ * @param rt is the template used to create the chart. * @param st is the chart where the alarm will be attached. */ -void rrdcalctemplate_check_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host) { - if(rt->hash_context != st->hash_context || strcmp(rt->context, st->context) != 0) - return; - if (rt->charts_pattern && !simple_pattern_matches(rt->charts_pattern, st->name)) - return; +static char *rrdcalc_alert_name_with_dimension(const char *name, size_t namelen, const char *dim, size_t dimlen) { + char *newname,*move; - if (rt->family_pattern && !simple_pattern_matches(rt->family_pattern, st->family)) - return; + newname = mallocz(namelen + dimlen + 2); + move = newname; + memcpy(move, name, namelen); + move += namelen; - if (rt->module_pattern && !simple_pattern_matches(rt->module_pattern, st->module_name)) - return; + *move++ = '_'; + memcpy(move, dim, dimlen); + move += dimlen; + *move = '\0'; + + return newname; +} + +bool rrdcalctemplate_check_rrdset_conditions(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host) { + if(rt->context != st->context) + return false; + + if(rt->foreach_dimension_pattern && !rrdset_number_of_dimensions(st)) + return false; + + if (rt->charts_pattern && !simple_pattern_matches(rt->charts_pattern, rrdset_name(st)) && !simple_pattern_matches(rt->charts_pattern, rrdset_id(st))) + return false; + + if (rt->family_pattern && !simple_pattern_matches(rt->family_pattern, rrdset_family(st))) + return false; + + if (rt->module_pattern && !simple_pattern_matches(rt->module_pattern, rrdset_module_name(st))) + return false; + + if (rt->plugin_pattern && !simple_pattern_matches(rt->plugin_pattern, rrdset_plugin_name(st))) + return false; + + if(host->rrdlabels && rt->host_labels_pattern && !rrdlabels_match_simple_pattern_parsed(host->rrdlabels, rt->host_labels_pattern, '=')) + return false; - if (rt->plugin_pattern && !simple_pattern_matches(rt->plugin_pattern, st->plugin_name)) + return true; +} + +void rrdcalctemplate_check_rrddim_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDDIM *rd, RRDHOST *host) { + if (simple_pattern_matches(rt->foreach_dimension_pattern, rrddim_id(rd)) || simple_pattern_matches(rt->foreach_dimension_pattern, rrddim_name(rd))) { + char *overwrite_alert_name = rrdcalc_alert_name_with_dimension( + rrdcalctemplate_name(rt), string_strlen(rt->name), rrddim_name(rd), string_strlen(rd->name)); + rrdcalc_add_from_rrdcalctemplate(host, rt, st, overwrite_alert_name, rrddim_name(rd)); + freez(overwrite_alert_name); + } +} + +void rrdcalctemplate_check_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host) { + if(!rrdcalctemplate_check_rrdset_conditions(rt, st, host)) return; - if(host->host_labels && rt->host_labels_pattern && !rrdlabels_match_simple_pattern_parsed(host->host_labels, rt->host_labels_pattern, '=')) + if(!rt->foreach_dimension_pattern) { + rrdcalc_add_from_rrdcalctemplate(host, rt, st, NULL, NULL); return; + } - RRDCALC *rc = rrdcalc_create_from_template(host, rt, st->id); - if (unlikely(!rc)) - info("Health tried to create alarm from template '%s' on chart '%s' of host '%s', but it failed", rt->name, st->id, host->hostname); -#ifdef NETDATA_INTERNAL_CHECKS - else if (rc->rrdset != st && !rc->foreachdim) //When we have a template with foreadhdim, the child will be added to the index late - error("Health alarm '%s.%s' should be linked to chart '%s', but it is not", rc->chart ? rc->chart : "NOCHART", rc->name, st->id); -#endif + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + rrdcalctemplate_check_rrddim_conditions_and_link(rt, st, rd, host); + } + rrddim_foreach_done(rd); } -void rrdcalctemplate_link_matching(RRDSET *st) { +void rrdcalctemplate_link_matching_templates_to_rrdset(RRDSET *st) { RRDHOST *host = st->rrdhost; - RRDCALCTEMPLATE *rt; - - for(rt = host->templates; rt ; rt = rt->next) - rrdcalctemplate_check_conditions_and_link(rt, st, host); - for(rt = host->alarms_template_with_foreach; rt ; rt = rt->next) + RRDCALCTEMPLATE *rt; + foreach_rrdcalctemplate_read(host, rt) { rrdcalctemplate_check_conditions_and_link(rt, st, host); + } + foreach_rrdcalctemplate_done(rt); } -inline void rrdcalctemplate_free(RRDCALCTEMPLATE *rt) { - if(unlikely(!rt)) return; - +static void rrdcalctemplate_free_internals(RRDCALCTEMPLATE *rt) { expression_free(rt->calculation); expression_free(rt->warning); expression_free(rt->critical); - freez(rt->family_match); + string_freez(rt->family_match); simple_pattern_free(rt->family_pattern); - freez(rt->plugin_match); + string_freez(rt->plugin_match); simple_pattern_free(rt->plugin_pattern); - freez(rt->module_match); + string_freez(rt->module_match); simple_pattern_free(rt->module_pattern); - freez(rt->charts_match); + string_freez(rt->charts_match); simple_pattern_free(rt->charts_pattern); - freez(rt->name); - freez(rt->exec); - freez(rt->recipient); - freez(rt->classification); - freez(rt->component); - freez(rt->type); - freez(rt->context); - freez(rt->source); - freez(rt->units); - freez(rt->info); - freez(rt->dimensions); - freez(rt->foreachdim); - freez(rt->host_labels); - simple_pattern_free(rt->spdim); + string_freez(rt->name); + string_freez(rt->exec); + string_freez(rt->recipient); + string_freez(rt->classification); + string_freez(rt->component); + string_freez(rt->type); + string_freez(rt->context); + string_freez(rt->source); + string_freez(rt->units); + string_freez(rt->info); + string_freez(rt->dimensions); + string_freez(rt->foreach_dimension); + string_freez(rt->host_labels); + simple_pattern_free(rt->foreach_dimension_pattern); simple_pattern_free(rt->host_labels_pattern); - freez(rt); } -inline void rrdcalctemplate_unlink_and_free(RRDHOST *host, RRDCALCTEMPLATE *rt) { +void rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(RRDCALCTEMPLATE *rt) { if(unlikely(!rt)) return; - debug(D_HEALTH, "Health removing template '%s' of host '%s'", rt->name, host->hostname); + rrdcalctemplate_free_internals(rt); + freez(rt); +} +static void rrdcalctemplate_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalctemplate, void *added_bool) { + RRDCALCTEMPLATE *rt = rrdcalctemplate; (void)rt; + + bool *added = added_bool; + *added = true; + + debug(D_HEALTH, "Health configuration adding template '%s'" + ": context '%s'" + ", exec '%s'" + ", recipient '%s'" + ", green " NETDATA_DOUBLE_FORMAT_AUTO + ", red " NETDATA_DOUBLE_FORMAT_AUTO + ", lookup: group %d" + ", after %d" + ", before %d" + ", options %u" + ", dimensions '%s'" + ", for each dimension '%s'" + ", update every %d" + ", calculation '%s'" + ", warning '%s'" + ", critical '%s'" + ", source '%s'" + ", delay up %d" + ", delay down %d" + ", delay max %d" + ", delay_multiplier %f" + ", warn_repeat_every %u" + ", crit_repeat_every %u", + rrdcalctemplate_name(rt), + (rt->context)?string2str(rt->context):"NONE", + (rt->exec)?rrdcalctemplate_exec(rt):"DEFAULT", + (rt->recipient)?rrdcalctemplate_recipient(rt):"DEFAULT", + rt->green, + rt->red, + (int)rt->group, + rt->after, + rt->before, + rt->options, + (rt->dimensions)?rrdcalctemplate_dimensions(rt):"NONE", + (rt->foreach_dimension)?rrdcalctemplate_foreachdim(rt):"NONE", + rt->update_every, + (rt->calculation)?rt->calculation->parsed_as:"NONE", + (rt->warning)?rt->warning->parsed_as:"NONE", + (rt->critical)?rt->critical->parsed_as:"NONE", + rrdcalctemplate_source(rt), + rt->delay_up_duration, + rt->delay_down_duration, + rt->delay_max_duration, + rt->delay_multiplier, + rt->warn_repeat_every, + rt->crit_repeat_every + ); +} + +static void rrdcalctemplate_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalctemplate, void *rrdhost __maybe_unused) { + RRDCALCTEMPLATE *rt = rrdcalctemplate; + rrdcalctemplate_free_internals(rt); +} + +void rrdcalctemplate_index_init(RRDHOST *host) { + if(!host->rrdcalctemplate_root_index) { + host->rrdcalctemplate_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); - if(host->templates == rt) { - host->templates = rt->next; + dictionary_register_insert_callback(host->rrdcalctemplate_root_index, rrdcalctemplate_insert_callback, NULL); + dictionary_register_delete_callback(host->rrdcalctemplate_root_index, rrdcalctemplate_delete_callback, host); } - else { - RRDCALCTEMPLATE *t; - for (t = host->templates; t && t->next != rt; t = t->next ) ; - if(t) { - t->next = rt->next; - rt->next = NULL; - } - else - error("Cannot find RRDCALCTEMPLATE '%s' linked in host '%s'", rt->name, host->hostname); +} + +void rrdcalctemplate_index_destroy(RRDHOST *host) { + dictionary_destroy(host->rrdcalctemplate_root_index); + host->rrdcalctemplate_root_index = NULL; +} + +inline void rrdcalctemplate_delete_all(RRDHOST *host) { + dictionary_flush(host->rrdcalctemplate_root_index); +} + +#define RRDCALCTEMPLATE_MAX_KEY_SIZE 1024 +static size_t rrdcalctemplate_key(char *dst, size_t dst_len, const char *name, const char *family_match) { + return snprintfz(dst, dst_len, "%s/%s", name, (family_match && *family_match)?family_match:"*"); +} + +void rrdcalctemplate_add_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) { + if(unlikely(!rt->context)) { + error("Health configuration for template '%s' does not have a context", rrdcalctemplate_name(rt)); + return; + } + + if(unlikely(!rt->update_every)) { + error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rrdcalctemplate_name(rt)); + return; } - rrdcalctemplate_free(rt); + if(unlikely(!RRDCALCTEMPLATE_HAS_DB_LOOKUP(rt) && !rt->calculation && !rt->warning && !rt->critical)) { + error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rrdcalctemplate_name(rt)); + return; + } + + char key[RRDCALCTEMPLATE_MAX_KEY_SIZE + 1]; + size_t key_len = rrdcalctemplate_key(key, RRDCALCTEMPLATE_MAX_KEY_SIZE, rrdcalctemplate_name(rt), rrdcalctemplate_family_match(rt)); + + bool added = false; + dictionary_set_advanced(host->rrdcalctemplate_root_index, key, (ssize_t)(key_len + 1), rt, sizeof(*rt), &added); + + if(added) + freez(rt); + else { + info("Health configuration template '%s' already exists for host '%s'.", rrdcalctemplate_name(rt), rrdhost_hostname(host)); + rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(rt); + } } diff --git a/database/rrdcalctemplate.h b/database/rrdcalctemplate.h index 51aa33054..6212a42da 100644 --- a/database/rrdcalctemplate.h +++ b/database/rrdcalctemplate.h @@ -9,35 +9,34 @@ // these are to be applied to charts found dynamically // based on their context. struct rrdcalctemplate { - char *name; - uint32_t hash_name; uuid_t config_hash_id; - char *exec; - char *recipient; + STRING *name; - char *classification; - char *component; - char *type; + STRING *exec; + STRING *recipient; - char *context; - uint32_t hash_context; + STRING *classification; + STRING *component; + STRING *type; - char *family_match; + STRING *context; + + STRING *family_match; SIMPLE_PATTERN *family_pattern; - char *plugin_match; + STRING *plugin_match; SIMPLE_PATTERN *plugin_pattern; - char *module_match; + STRING *module_match; SIMPLE_PATTERN *module_pattern; - char *charts_match; + STRING *charts_match; SIMPLE_PATTERN *charts_pattern; - char *source; // the source of this alarm - char *units; // the units of the alarm - char *info; // a short description of the alarm + STRING *source; // the source of this alarm + STRING *units; // the units of the alarm + STRING *info; // a short description of the alarm int update_every; // update frequency for the alarm @@ -48,15 +47,13 @@ struct rrdcalctemplate { // ------------------------------------------------------------------------ // database lookup settings - char *dimensions; // the chart dimensions - char *foreachdim; // the group of dimensions that the lookup will be applied. - SIMPLE_PATTERN *spdim; // used if and only if there is a simple pattern for the chart. - int foreachcounter; // the number of alarms created with foreachdim, this also works as an id of the - // children + STRING *dimensions; // the chart dimensions + STRING *foreach_dimension; // the group of dimensions that the lookup will be applied. + SIMPLE_PATTERN *foreach_dimension_pattern; // used if and only if there is a simple pattern for the chart. RRDR_GROUPING group; // grouping method: average, max, etc. int before; // ending point in time-series int after; // starting point in time-series - uint32_t options; // calculation options + RRDCALC_OPTIONS options; // configuration options // ------------------------------------------------------------------------ // notification delay settings @@ -74,7 +71,7 @@ struct rrdcalctemplate { // ------------------------------------------------------------------------ // Labels settings - char *host_labels; // the label read from an alarm file + STRING *host_labels; // the label read from an alarm file SIMPLE_PATTERN *host_labels_pattern; // the simple pattern of labels // ------------------------------------------------------------------------ @@ -85,13 +82,47 @@ struct rrdcalctemplate { EVAL_EXPRESSION *critical; struct rrdcalctemplate *next; + struct rrdcalctemplate *prev; }; +#define foreach_rrdcalctemplate_read(host, rt) \ + dfe_start_read((host)->rrdcalctemplate_root_index, rt) + +#define foreach_rrdcalctemplate_done(rt) \ + dfe_done(rt) + +#define rrdcalctemplate_name(rt) string2str((rt)->name) +#define rrdcalctemplate_exec(rt) string2str((rt)->exec) +#define rrdcalctemplate_recipient(rt) string2str((rt)->recipient) +#define rrdcalctemplate_classification(rt) string2str((rt)->classification) +#define rrdcalctemplate_component(rt) string2str((rt)->component) +#define rrdcalctemplate_type(rt) string2str((rt)->type) +#define rrdcalctemplate_family_match(rt) string2str((rt)->family_match) +#define rrdcalctemplate_plugin_match(rt) string2str((rt)->plugin_match) +#define rrdcalctemplate_module_match(rt) string2str((rt)->module_match) +#define rrdcalctemplate_charts_match(rt) string2str((rt)->charts_match) +#define rrdcalctemplate_units(rt) string2str((rt)->units) +#define rrdcalctemplate_info(rt) string2str((rt)->info) +#define rrdcalctemplate_source(rt) string2str((rt)->source) +#define rrdcalctemplate_dimensions(rt) string2str((rt)->dimensions) +#define rrdcalctemplate_foreachdim(rt) string2str((rt)->foreach_dimension) +#define rrdcalctemplate_host_labels(rt) string2str((rt)->host_labels) + #define RRDCALCTEMPLATE_HAS_DB_LOOKUP(rt) ((rt)->after) -extern void rrdcalctemplate_link_matching(RRDSET *st); +void rrdcalctemplate_link_matching_templates_to_rrdset(RRDSET *st); + +void rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(RRDCALCTEMPLATE *rt); +void rrdcalctemplate_delete_all(RRDHOST *host); +void rrdcalctemplate_add_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt); + +void rrdcalctemplate_check_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host); + +bool rrdcalctemplate_check_rrdset_conditions(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host); +void rrdcalctemplate_check_rrddim_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDDIM *rd, RRDHOST *host); + + +void rrdcalctemplate_index_init(RRDHOST *host); +void rrdcalctemplate_index_destroy(RRDHOST *host); -extern void rrdcalctemplate_free(RRDCALCTEMPLATE *rt); -extern void rrdcalctemplate_unlink_and_free(RRDHOST *host, RRDCALCTEMPLATE *rt); -extern void rrdcalctemplate_create_alarms(RRDHOST *host, RRDCALCTEMPLATE *rt, RRDSET *st); #endif //NETDATA_RRDCALCTEMPLATE_H diff --git a/database/rrdcontext.c b/database/rrdcontext.c index 24884dbc0..cfa8af3e0 100644 --- a/database/rrdcontext.c +++ b/database/rrdcontext.c @@ -4,17 +4,30 @@ #include "sqlite/sqlite_context.h" #include "aclk/schema-wrappers/context.h" #include "aclk/aclk_contexts_api.h" -#include "aclk/aclk_api.h" - -int rrdcontext_enabled = CONFIG_BOOLEAN_YES; +#include "aclk/aclk.h" +#include "storage_engine.h" #define MESSAGES_PER_BUNDLE_TO_SEND_TO_HUB_PER_HOST 5000 #define FULL_RETENTION_SCAN_DELAY_AFTER_DB_ROTATION_SECS 120 -#define RRDCONTEXT_WORKER_THREAD_HEARTBEAT_SECS 1 +#define RRDCONTEXT_WORKER_THREAD_HEARTBEAT_USEC (1000 * USEC_PER_MS) #define RRDCONTEXT_MINIMUM_ALLOWED_PRIORITY 10 -// #define LOG_TRANSITIONS 1 -// #define LOG_RRDINSTANCES 1 +#define LOG_TRANSITIONS false + +#define WORKER_JOB_HOSTS 1 +#define WORKER_JOB_CHECK 2 +#define WORKER_JOB_SEND 3 +#define WORKER_JOB_DEQUEUE 4 +#define WORKER_JOB_RETENTION 5 +#define WORKER_JOB_QUEUED 6 +#define WORKER_JOB_CLEANUP 7 +#define WORKER_JOB_CLEANUP_DELETE 8 +#define WORKER_JOB_PP_METRIC 9 // post-processing metrics +#define WORKER_JOB_PP_INSTANCE 10 // post-processing instances +#define WORKER_JOB_PP_CONTEXT 11 // post-processing contexts +#define WORKER_JOB_HUB_QUEUE_SIZE 12 +#define WORKER_JOB_PP_QUEUE_SIZE 13 + typedef enum { RRD_FLAG_NONE = 0, @@ -24,46 +37,36 @@ typedef enum { RRD_FLAG_ARCHIVED = (1 << 3), // this object is not currently being collected RRD_FLAG_OWN_LABELS = (1 << 4), // this instance has its own labels - not linked to an RRDSET RRD_FLAG_LIVE_RETENTION = (1 << 5), // we have got live retention from the database - RRD_FLAG_QUEUED = (1 << 6), // this context is currently queued to be dispatched to hub - RRD_FLAG_DONT_PROCESS = (1 << 7), // don't process updates for this object + RRD_FLAG_QUEUED_FOR_HUB = (1 << 6), // this context is currently queued to be dispatched to hub + RRD_FLAG_QUEUED_FOR_PP = (1 << 7), // this context is currently queued to be post-processed RRD_FLAG_HIDDEN = (1 << 8), // don't expose this to the hub or the API + RRD_FLAG_UPDATE_REASON_TRIGGERED = (1 << 9), // the update was triggered by the child object RRD_FLAG_UPDATE_REASON_LOAD_SQL = (1 << 10), // this object has just been loaded from SQL RRD_FLAG_UPDATE_REASON_NEW_OBJECT = (1 << 11), // this object has just been created RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT = (1 << 12), // we received an update on this object RRD_FLAG_UPDATE_REASON_CHANGED_LINKING = (1 << 13), // an instance or a metric switched RRDSET or RRDDIM - RRD_FLAG_UPDATE_REASON_CHANGED_UUID = (1 << 14), // an instance or a metric changed UUID - RRD_FLAG_UPDATE_REASON_CHANGED_NAME = (1 << 15), // an instance or a metric changed name - RRD_FLAG_UPDATE_REASON_CHANGED_UNITS = (1 << 16), // this context or instance changed units - RRD_FLAG_UPDATE_REASON_CHANGED_TITLE = (1 << 17), // this context or instance changed title - RRD_FLAG_UPDATE_REASON_CHANGED_FAMILY = (1 << 18), // the context or the instance changed family - RRD_FLAG_UPDATE_REASON_CHANGED_CHART_TYPE = (1 << 19), // this context or instance changed chart type - RRD_FLAG_UPDATE_REASON_CHANGED_PRIORITY = (1 << 20), // this context or instance changed its priority - RRD_FLAG_UPDATE_REASON_CHANGED_UPDATE_EVERY = (1 << 21), // the instance or the metric changed update frequency - RRD_FLAG_UPDATE_REASON_ZERO_RETENTION = (1 << 22), // this object has not retention - RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T = (1 << 23), // this object changed its oldest time in the db - RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T = (1 << 24), // this object change its latest time in the db - RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED = (1 << 25), // this object has stopped being collected - RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED = (1 << 26), // this object has started being collected - RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD = (1 << 27), // this context belongs to a host that just disconnected - RRD_FLAG_UPDATE_REASON_DB_ROTATION = (1 << 28), // this context changed because of a db rotation - RRD_FLAG_UPDATE_REASON_UNUSED = (1 << 29), // this context is not used anymore - RRD_FLAG_UPDATE_REASON_CHANGED_FLAGS = (1 << 30), // this context is not used anymore + RRD_FLAG_UPDATE_REASON_CHANGED_METADATA = (1 << 14), // this context or instance changed uuid, name, units, title, family, chart type, priority, update every, rrd changed flags + RRD_FLAG_UPDATE_REASON_ZERO_RETENTION = (1 << 15), // this object has no retention + RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T = (1 << 16), // this object changed its oldest time in the db + RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T = (1 << 17), // this object change its latest time in the db + RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED = (1 << 18), // this object has stopped being collected + RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED = (1 << 19), // this object has started being collected + RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD = (1 << 20), // this context belongs to a host that just disconnected + RRD_FLAG_UPDATE_REASON_UNUSED = (1 << 21), // this context is not used anymore + RRD_FLAG_UPDATE_REASON_DB_ROTATION = (1 << 22), // this context changed because of a db rotation + + // action to perform on an object + RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION = (1 << 30), // this object has to update its retention from the db } RRD_FLAGS; #define RRD_FLAG_ALL_UPDATE_REASONS ( \ - RRD_FLAG_UPDATE_REASON_LOAD_SQL \ + RRD_FLAG_UPDATE_REASON_TRIGGERED \ + |RRD_FLAG_UPDATE_REASON_LOAD_SQL \ |RRD_FLAG_UPDATE_REASON_NEW_OBJECT \ |RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT \ |RRD_FLAG_UPDATE_REASON_CHANGED_LINKING \ - |RRD_FLAG_UPDATE_REASON_CHANGED_UUID \ - |RRD_FLAG_UPDATE_REASON_CHANGED_NAME \ - |RRD_FLAG_UPDATE_REASON_CHANGED_UNITS \ - |RRD_FLAG_UPDATE_REASON_CHANGED_TITLE \ - |RRD_FLAG_UPDATE_REASON_CHANGED_FAMILY \ - |RRD_FLAG_UPDATE_REASON_CHANGED_CHART_TYPE \ - |RRD_FLAG_UPDATE_REASON_CHANGED_PRIORITY \ - |RRD_FLAG_UPDATE_REASON_CHANGED_UPDATE_EVERY \ + |RRD_FLAG_UPDATE_REASON_CHANGED_METADATA \ |RRD_FLAG_UPDATE_REASON_ZERO_RETENTION \ |RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T \ |RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T \ @@ -72,60 +75,113 @@ typedef enum { |RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD \ |RRD_FLAG_UPDATE_REASON_DB_ROTATION \ |RRD_FLAG_UPDATE_REASON_UNUSED \ - |RRD_FLAG_UPDATE_REASON_CHANGED_FLAGS \ ) #define RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS ( \ RRD_FLAG_ARCHIVED \ - |RRD_FLAG_DONT_PROCESS \ |RRD_FLAG_HIDDEN \ |RRD_FLAG_ALL_UPDATE_REASONS \ ) +#define RRD_FLAGS_REQUIRED_FOR_DELETIONS ( \ + RRD_FLAG_DELETED \ + |RRD_FLAG_LIVE_RETENTION \ +) + #define RRD_FLAGS_PREVENTING_DELETIONS ( \ - RRD_FLAG_QUEUED \ + RRD_FLAG_QUEUED_FOR_HUB \ |RRD_FLAG_COLLECTED \ - |RRD_FLAG_UPDATE_REASON_LOAD_SQL \ - |RRD_FLAG_UPDATE_REASON_NEW_OBJECT \ - |RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT \ - |RRD_FLAG_UPDATE_REASON_CHANGED_LINKING \ + |RRD_FLAG_QUEUED_FOR_PP \ ) -#define rrd_flag_set_updated(obj, reason) (obj)->flags |= (RRD_FLAG_UPDATED | (reason)) -#define rrd_flag_unset_updated(obj) (obj)->flags &= ~(RRD_FLAG_UPDATED | RRD_FLAG_ALL_UPDATE_REASONS) - -#define rrd_flag_set_collected(obj) do { \ - if(likely( !((obj)->flags & RRD_FLAG_COLLECTED))) \ - (obj)->flags |= (RRD_FLAG_COLLECTED | RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED | RRD_FLAG_UPDATED); \ - if(likely( ((obj)->flags & (RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED)))) \ - (obj)->flags &= ~(RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED); \ - if(unlikely(((obj)->flags & (RRD_FLAG_DELETED | RRD_FLAG_UPDATE_REASON_ZERO_RETENTION)))) \ - (obj)->flags &= ~(RRD_FLAG_DELETED | RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); \ - if(unlikely(((obj)->flags & RRD_FLAG_DONT_PROCESS))) \ - (obj)->flags &= ~RRD_FLAG_DONT_PROCESS; \ -} while(0) - -#define rrd_flag_set_archived(obj) do { \ - if(likely( !((obj)->flags & RRD_FLAG_ARCHIVED))) \ - (obj)->flags |= (RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED | RRD_FLAG_UPDATED); \ - if(likely( ((obj)->flags & (RRD_FLAG_COLLECTED | RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED)))) \ - (obj)->flags &= ~(RRD_FLAG_COLLECTED | RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED); \ - if(unlikely(((obj)->flags & (RRD_FLAG_DELETED | RRD_FLAG_UPDATE_REASON_ZERO_RETENTION)))) \ - (obj)->flags &= ~(RRD_FLAG_DELETED | RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); \ -} while(0) - -#define rrd_flag_set_deleted(obj, reason) do { \ - if(likely( !((obj)->flags & RRD_FLAG_DELETED))) \ - (obj)->flags |= (RRD_FLAG_DELETED | RRD_FLAG_UPDATE_REASON_ZERO_RETENTION | RRD_FLAG_UPDATED | (reason)); \ - if(unlikely(((obj)->flags & RRD_FLAG_ARCHIVED))) \ - (obj)->flags &= ~RRD_FLAG_ARCHIVED; \ - if(likely( ((obj)->flags & RRD_FLAG_COLLECTED))) \ - (obj)->flags &= ~RRD_FLAG_COLLECTED; \ -} while(0) - - -#define rrd_flag_is_collected(obj) ((obj)->flags & RRD_FLAG_COLLECTED) -#define rrd_flag_is_archived(obj) ((obj)->flags & RRD_FLAG_ARCHIVED) +// get all the flags of an object +#define rrd_flags_get(obj) __atomic_load_n(&((obj)->flags), __ATOMIC_SEQ_CST) + +// check if ANY of the given flags (bits) is set +#define rrd_flag_check(obj, flag) (rrd_flags_get(obj) & (flag)) + +// check if ALL the given flags (bits) are set +#define rrd_flag_check_all(obj, flag) (rrd_flag_check(obj, flag) == (flag)) + +// set one or more flags (bits) +#define rrd_flag_set(obj, flag) __atomic_or_fetch(&((obj)->flags), flag, __ATOMIC_SEQ_CST) + +// clear one or more flags (bits) +#define rrd_flag_clear(obj, flag) __atomic_and_fetch(&((obj)->flags), ~(flag), __ATOMIC_SEQ_CST) + +// replace the flags of an object, with the supplied ones +#define rrd_flags_replace(obj, all_flags) __atomic_store_n(&((obj)->flags), all_flags, __ATOMIC_SEQ_CST) + +static inline void +rrd_flag_add_remove_atomic(RRD_FLAGS *flags, RRD_FLAGS check, RRD_FLAGS conditionally_add, RRD_FLAGS always_remove) { + RRD_FLAGS expected, desired; + do { + expected = *flags; + + desired = expected; + desired &= ~(always_remove); + + if(!(expected & check)) + desired |= (check | conditionally_add); + + } while(!__atomic_compare_exchange_n(flags, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)); +} + +#define rrd_flag_set_collected(obj) \ + rrd_flag_add_remove_atomic(&((obj)->flags) \ + /* check this flag */ \ + , RRD_FLAG_COLLECTED \ + \ + /* add these flags together with the above, if the above is not already set */ \ + , RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED | RRD_FLAG_UPDATED \ + \ + /* always remove these flags */ \ + , RRD_FLAG_ARCHIVED \ + | RRD_FLAG_DELETED \ + | RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED \ + | RRD_FLAG_UPDATE_REASON_ZERO_RETENTION \ + | RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD \ + ) + +#define rrd_flag_set_archived(obj) \ + rrd_flag_add_remove_atomic(&((obj)->flags) \ + /* check this flag */ \ + , RRD_FLAG_ARCHIVED \ + \ + /* add these flags together with the above, if the above is not already set */ \ + , RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED | RRD_FLAG_UPDATED \ + \ + /* always remove these flags */ \ + , RRD_FLAG_COLLECTED \ + | RRD_FLAG_DELETED \ + | RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED \ + | RRD_FLAG_UPDATE_REASON_ZERO_RETENTION \ + ) + +#define rrd_flag_set_deleted(obj, reason) \ + rrd_flag_add_remove_atomic(&((obj)->flags) \ + /* check this flag */ \ + , RRD_FLAG_DELETED \ + \ + /* add these flags together with the above, if the above is not already set */ \ + , RRD_FLAG_UPDATE_REASON_ZERO_RETENTION | RRD_FLAG_UPDATED | (reason) \ + \ + /* always remove these flags */ \ + , RRD_FLAG_ARCHIVED \ + | RRD_FLAG_COLLECTED \ + ) + +#define rrd_flag_is_collected(obj) rrd_flag_check(obj, RRD_FLAG_COLLECTED) +#define rrd_flag_is_archived(obj) rrd_flag_check(obj, RRD_FLAG_ARCHIVED) +#define rrd_flag_is_deleted(obj) rrd_flag_check(obj, RRD_FLAG_DELETED) +#define rrd_flag_is_updated(obj) rrd_flag_check(obj, RRD_FLAG_UPDATED) + +// mark an object as updated, providing reasons (additional bits) +#define rrd_flag_set_updated(obj, reason) rrd_flag_set(obj, RRD_FLAG_UPDATED | (reason)) + +// clear an object as being updated, clearing also all the reasons +#define rrd_flag_unset_updated(obj) rrd_flag_clear(obj, RRD_FLAG_UPDATED | RRD_FLAG_ALL_UPDATE_REASONS) + static struct rrdcontext_reason { RRD_FLAGS flag; @@ -133,32 +189,26 @@ static struct rrdcontext_reason { usec_t delay_ut; } rrdcontext_reasons[] = { // context related - { RRD_FLAG_UPDATE_REASON_NEW_OBJECT, "object created", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT, "object updated", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_LOAD_SQL, "loaded from sql", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_TITLE, "changed title", 30 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_UNITS, "changed units", 30 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_FAMILY, "changed family", 30 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_PRIORITY, "changed priority", 30 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_ZERO_RETENTION, "has no retention", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T, "updated first_time_t", 30 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T, "updated last_time_t", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_CHART_TYPE, "changed chart type", 30 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED, "stopped collected", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED, "started collected", 0 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_UNUSED, "unused", 0 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_TRIGGERED, "triggered transition", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_NEW_OBJECT, "object created", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT, "object updated", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_LOAD_SQL, "loaded from sql", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_CHANGED_METADATA, "changed metadata", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_ZERO_RETENTION, "has no retention", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T, "updated first_time_t", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T, "updated last_time_t", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_STOPPED_BEING_COLLECTED, "stopped collected", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_STARTED_BEING_COLLECTED, "started collected", 5 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_UNUSED, "unused", 5 * USEC_PER_SEC }, // not context related - { RRD_FLAG_UPDATE_REASON_CHANGED_UUID, "changed uuid", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_UPDATE_EVERY, "changed updated every",60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_LINKING, "changed rrd link", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_NAME, "changed name", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD, "child disconnected", 30 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_DB_ROTATION, "db rotation", 60 * USEC_PER_SEC }, - { RRD_FLAG_UPDATE_REASON_CHANGED_FLAGS, "changed flags", 60 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_CHANGED_LINKING, "changed rrd link", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD, "child disconnected", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_DB_ROTATION, "db rotation", 65 * USEC_PER_SEC }, + {RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION, "updated retention", 65 * USEC_PER_SEC }, // terminator - { 0, NULL, 0 }, + {0, NULL, 0 }, }; @@ -175,8 +225,6 @@ typedef struct rrdmetric { RRD_FLAGS flags; struct rrdinstance *ri; - - usec_t created_ut; // the time this object was created } RRDMETRIC; typedef struct rrdinstance { @@ -197,10 +245,16 @@ typedef struct rrdinstance { int update_every; // data collection frequency RRDSET *rrdset; // pointer to RRDSET when collected, or NULL - DICTIONARY *rrdlabels; // linked to RRDSET->state->chart_labels or own version + DICTIONARY *rrdlabels; // linked to RRDSET->chart_labels or own version struct rrdcontext *rc; DICTIONARY *rrdmetrics; + + struct { + uint32_t collected_metrics_count; // a temporary variable to detect BEGIN/END without SET + // don't use it for other purposes + // it goes up and then resets to zero, on every iteration + } internal; } RRDINSTANCE; typedef struct rrdcontext { @@ -222,12 +276,20 @@ typedef struct rrdcontext { DICTIONARY *rrdinstances; RRDHOST *rrdhost; + struct { + RRD_FLAGS queued_flags; // the last flags that triggered the post-processing + usec_t queued_ut; // the last time this was queued + usec_t dequeued_ut; // the last time we sent (or deduplicated) this context + size_t executions; // how many times this context has been processed + } pp; + struct { RRD_FLAGS queued_flags; // the last flags that triggered the queueing usec_t queued_ut; // the last time this was queued usec_t delay_calc_ut; // the last time we calculated the scheduled_dispatched_ut usec_t scheduled_dispatch_ut; // the time it was/is scheduled to be sent - usec_t dequeued_ut; // the last time we sent (or deduped) this context + usec_t dequeued_ut; // the last time we sent (or deduplicated) this context + size_t dispatches; // the number of times this has been dispatched to hub } queue; netdata_mutex_t mutex; @@ -236,28 +298,51 @@ typedef struct rrdcontext { // ---------------------------------------------------------------------------- // helper one-liners for RRDMETRIC +static bool rrdmetric_update_retention(RRDMETRIC *rm); + static inline RRDMETRIC *rrdmetric_acquired_value(RRDMETRIC_ACQUIRED *rma) { return dictionary_acquired_item_value((DICTIONARY_ITEM *)rma); } +static inline RRDMETRIC_ACQUIRED *rrdmetric_acquired_dup(RRDMETRIC_ACQUIRED *rma) { + RRDMETRIC *rm = rrdmetric_acquired_value(rma); + return (RRDMETRIC_ACQUIRED *)dictionary_acquired_item_dup(rm->ri->rrdmetrics, (DICTIONARY_ITEM *)rma); +} + static inline void rrdmetric_release(RRDMETRIC_ACQUIRED *rma) { RRDMETRIC *rm = rrdmetric_acquired_value(rma); dictionary_acquired_item_release(rm->ri->rrdmetrics, (DICTIONARY_ITEM *)rma); } -// ---------------------------------------------------------------------------- -// helper one-liners for RRDINSTANCE +const char *rrdmetric_acquired_id(RRDMETRIC_ACQUIRED *rma) { + RRDMETRIC *rm = rrdmetric_acquired_value(rma); + return string2str(rm->id); +} + +const char *rrdmetric_acquired_name(RRDMETRIC_ACQUIRED *rma) { + RRDMETRIC *rm = rrdmetric_acquired_value(rma); + return string2str(rm->name); +} -static inline RRDINSTANCE_ACQUIRED *rrdinstance_dup(RRDINSTANCE_ACQUIRED *ria) { - return (RRDINSTANCE_ACQUIRED *)dictionary_acquired_item_dup((DICTIONARY_ITEM *)ria); +NETDATA_DOUBLE rrdmetric_acquired_last_stored_value(RRDMETRIC_ACQUIRED *rma) { + RRDMETRIC *rm = rrdmetric_acquired_value(rma); + + if(rm->rrddim) + return rm->rrddim->last_stored_value; + + return NAN; } +// ---------------------------------------------------------------------------- +// helper one-liners for RRDINSTANCE + static inline RRDINSTANCE *rrdinstance_acquired_value(RRDINSTANCE_ACQUIRED *ria) { return dictionary_acquired_item_value((DICTIONARY_ITEM *)ria); } -static inline const char *rrdinstance_acquired_name(RRDINSTANCE_ACQUIRED *ria) { - return dictionary_acquired_item_name((DICTIONARY_ITEM *)ria); +static inline RRDINSTANCE_ACQUIRED *rrdinstance_acquired_dup(RRDINSTANCE_ACQUIRED *ria) { + RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + return (RRDINSTANCE_ACQUIRED *)dictionary_acquired_item_dup(ri->rc->rrdinstances, (DICTIONARY_ITEM *)ria); } static inline void rrdinstance_release(RRDINSTANCE_ACQUIRED *ria) { @@ -265,23 +350,42 @@ static inline void rrdinstance_release(RRDINSTANCE_ACQUIRED *ria) { dictionary_acquired_item_release(ri->rc->rrdinstances, (DICTIONARY_ITEM *)ria); } -// ---------------------------------------------------------------------------- -// helper one-liners for RRDCONTEXT +const char *rrdinstance_acquired_id(RRDINSTANCE_ACQUIRED *ria) { + RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + return string2str(ri->id); +} + +const char *rrdinstance_acquired_name(RRDINSTANCE_ACQUIRED *ria) { + RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + return string2str(ri->name); +} -static inline RRDCONTEXT_ACQUIRED *rrdcontext_dup(RRDCONTEXT_ACQUIRED *rca) { - return (RRDCONTEXT_ACQUIRED *)dictionary_acquired_item_dup((DICTIONARY_ITEM *)rca); +DICTIONARY *rrdinstance_acquired_labels(RRDINSTANCE_ACQUIRED *ria) { + RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + return ri->rrdlabels; } -static inline const char *rrdcontext_acquired_name(RRDCONTEXT_ACQUIRED *rca) { - return dictionary_acquired_item_name((DICTIONARY_ITEM *)rca); +DICTIONARY *rrdinstance_acquired_functions(RRDINSTANCE_ACQUIRED *ria) { + RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + if(!ri->rrdset) return NULL; + return ri->rrdset->functions_view; } +// ---------------------------------------------------------------------------- +// helper one-liners for RRDCONTEXT + static inline RRDCONTEXT *rrdcontext_acquired_value(RRDCONTEXT_ACQUIRED *rca) { return dictionary_acquired_item_value((DICTIONARY_ITEM *)rca); } -static inline RRDCONTEXT_ACQUIRED *rrdcontext_acquire(RRDHOST *host, const char *name) { - return (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)host->rrdctx, name); +const char *rrdcontext_acquired_id(RRDCONTEXT_ACQUIRED *rca) { + RRDCONTEXT *rc = rrdcontext_acquired_value(rca); + return string2str(rc->id); +} + +static inline RRDCONTEXT_ACQUIRED *rrdcontext_acquired_dup(RRDCONTEXT_ACQUIRED *rca) { + RRDCONTEXT *rc = rrdcontext_acquired_value(rca); + return (RRDCONTEXT_ACQUIRED *)dictionary_acquired_item_dup((DICTIONARY *)rc->rrdhost->rrdctx, (DICTIONARY_ITEM *)rca); } static inline void rrdcontext_release(RRDCONTEXT_ACQUIRED *rca) { @@ -289,29 +393,40 @@ static inline void rrdcontext_release(RRDCONTEXT_ACQUIRED *rca) { dictionary_acquired_item_release((DICTIONARY *)rc->rrdhost->rrdctx, (DICTIONARY_ITEM *)rca); } -static void rrdcontext_recalculate_context_retention(RRDCONTEXT *rc, RRD_FLAGS reason, int job_id); -static void rrdcontext_recalculate_host_retention(RRDHOST *host, RRD_FLAGS reason, int job_id); +static void rrdcontext_recalculate_context_retention(RRDCONTEXT *rc, RRD_FLAGS reason, bool worker_jobs); +static void rrdcontext_recalculate_host_retention(RRDHOST *host, RRD_FLAGS reason, bool worker_jobs); #define rrdcontext_version_hash(host) rrdcontext_version_hash_with_callback(host, NULL, false, NULL) static uint64_t rrdcontext_version_hash_with_callback(RRDHOST *host, void (*callback)(RRDCONTEXT *, bool, void *), bool snapshot, void *bundle); -void rrdcontext_delete_from_sql_unsafe(RRDCONTEXT *rc); +static void rrdcontext_garbage_collect_single_host(RRDHOST *host, bool worker_jobs); +static void rrdcontext_garbage_collect_for_all_hosts(void); #define rrdcontext_lock(rc) netdata_mutex_lock(&((rc)->mutex)) #define rrdcontext_unlock(rc) netdata_mutex_unlock(&((rc)->mutex)) // ---------------------------------------------------------------------------- -// Updates triggers +// Forward definitions + +static uint64_t rrdcontext_get_next_version(RRDCONTEXT *rc); +static bool check_if_cloud_version_changed_unsafe(RRDCONTEXT *rc, bool sending __maybe_unused); +static void rrdcontext_message_send_unsafe(RRDCONTEXT *rc, bool snapshot __maybe_unused, void *bundle __maybe_unused); -static void rrdmetric_trigger_updates(RRDMETRIC *rm, bool force, bool escalate); -static void rrdinstance_trigger_updates(RRDINSTANCE *ri, bool force, bool escalate); -static void rrdcontext_trigger_updates(RRDCONTEXT *rc, bool force); +static void rrdcontext_delete_from_sql_unsafe(RRDCONTEXT *rc); + +static void rrdcontext_dequeue_from_post_processing(RRDCONTEXT *rc); +static void rrdcontext_queue_for_post_processing(RRDCONTEXT *rc, const char *function, RRD_FLAGS flags); +static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAGS reason, bool worker_jobs); + +static void rrdmetric_trigger_updates(RRDMETRIC *rm, const char *function); +static void rrdinstance_trigger_updates(RRDINSTANCE *ri, const char *function); +static void rrdcontext_trigger_updates(RRDCONTEXT *rc, const char *function); // ---------------------------------------------------------------------------- // visualizing flags static void rrd_flags_to_buffer(RRD_FLAGS flags, BUFFER *wb) { - if(flags & RRD_FLAG_QUEUED) + if(flags & RRD_FLAG_QUEUED_FOR_HUB) buffer_strcat(wb, "QUEUED "); if(flags & RRD_FLAG_DELETED) @@ -332,11 +447,11 @@ static void rrd_flags_to_buffer(RRD_FLAGS flags, BUFFER *wb) { if(flags & RRD_FLAG_LIVE_RETENTION) buffer_strcat(wb, "LIVE_RETENTION "); - if(flags & RRD_FLAG_DONT_PROCESS) - buffer_strcat(wb, "DONT_PROCESS "); - if(flags & RRD_FLAG_HIDDEN) buffer_strcat(wb, "HIDDEN "); + + if(flags & RRD_FLAG_QUEUED_FOR_PP) + buffer_strcat(wb, "PENDING_UPDATES "); } static void rrd_reasons_to_buffer(RRD_FLAGS flags, BUFFER *wb) { @@ -350,118 +465,11 @@ static void rrd_reasons_to_buffer(RRD_FLAGS flags, BUFFER *wb) { } } -// ---------------------------------------------------------------------------- -// logging of all data collected - -#ifdef LOG_TRANSITIONS -static void log_transition(STRING *metric, STRING *instance, STRING *context, RRD_FLAGS flags, const char *msg) { - BUFFER *wb = buffer_create(1000); - - buffer_sprintf(wb, "RRD TRANSITION: context '%s'", string2str(context)); - - if(instance) - buffer_sprintf(wb, ", instance '%s'", string2str(instance)); - - if(metric) - buffer_sprintf(wb, ", metric '%s'", string2str(metric)); - - buffer_sprintf(wb, ", triggered by %s: ", msg); - - rrd_flags_to_buffer(flags, wb); - - buffer_strcat(wb, ", reasons: "); - - rrd_reasons_to_buffer(flags, wb); - - internal_error(true, "%s", buffer_tostring(wb)); - buffer_free(wb); -} -#else -#define log_transition(metric, instance, context, flags, msg) debug_dummy() -#endif - -#ifdef LOG_RRDINSTANCES -static void rrdinstance_log(RRDINSTANCE *ri, const char *msg) { - char uuid[UUID_STR_LEN]; - - uuid_unparse(ri->uuid, uuid); - - BUFFER *wb = buffer_create(1000); - - buffer_sprintf(wb, - "RRDINSTANCE: %s id '%s' (host '%s'), uuid '%s', name '%s', context '%s', title '%s', units '%s', family '%s', priority %zu, chart type '%s', update every %d, rrdset '%s', flags %s%s%s%s%s%s%s%s, first_time_t %ld, last_time_t %ld", - msg, - string2str(ri->id), - ri->rc->rrdhost->hostname, - uuid, - string2str(ri->name), - string2str(ri->rc->id), - string2str(ri->title), - string2str(ri->units), - string2str(ri->family), - ri->priority, - rrdset_type_name(ri->chart_type), - ri->update_every, - ri->rrdset?ri->rrdset->id:"NONE", - ri->flags & RRD_FLAG_DELETED ?"DELETED ":"", - ri->flags & RRD_FLAG_UPDATED ?"UPDATED ":"", - rrd_flag_is_collected(ri) ?"COLLECTED ":"", - rrd_flag_is_archived(ri) ?"ARCHIVED ":"", - ri->flags & RRD_FLAG_OWNLABELS ?"OWNLABELS ":"", - ri->flags & RRD_FLAG_LIVE_RETENTION ?"LIVE ":"", - ri->flags & RRD_FLAG_QUEUED ?"QUEUED ":"", - ri->flags & RRD_FLAG_DONT_TRIGGER ?"BLOCKED ":"", - ri->first_time_t, - ri->last_time_t - ); - - buffer_strcat(wb, ", update reasons: { "); - for(int i = 0, added = 0; rrdcontext_reasons[i].name ;i++) - if(ri->flags & rrdcontext_reasons[i].flag) { - if(added) buffer_strcat(wb, ", "); - buffer_strcat(wb, rrdcontext_reasons[i].name); - added++; - } - buffer_strcat(wb, " }"); - - buffer_strcat(wb, ", labels: { "); - if(ri->rrdlabels) { - if(!rrdlabels_to_buffer(ri->rrdlabels, wb, "", "=", "'", ", ", NULL, NULL, NULL, NULL)) - buffer_strcat(wb, "EMPTY }"); - else - buffer_strcat(wb, " }"); - } - else - buffer_strcat(wb, "NONE }"); - - buffer_strcat(wb, ", metrics: { "); - if(ri->rrdmetrics) { - RRDMETRIC *v; - int i = 0; - dfe_start_read((DICTIONARY *)ri->rrdmetrics, v) { - buffer_sprintf(wb, "%s%s", i?",":"", v_name); - i++; - } - dfe_done(v); - - if(!i) - buffer_strcat(wb, "EMPTY }"); - else - buffer_strcat(wb, " }"); - } - else - buffer_strcat(wb, "NONE }"); - - internal_error(true, "%s", buffer_tostring(wb)); - buffer_free(wb); -} -#else -#define rrdinstance_log(ir, msg) debug_dummy() -#endif - // ---------------------------------------------------------------------------- // RRDMETRIC +// free the contents of RRDMETRIC. +// RRDMETRIC itself is managed by DICTIONARY - no need to free it here. static void rrdmetric_free(RRDMETRIC *rm) { string_freez(rm->id); string_freez(rm->name); @@ -471,77 +479,24 @@ static void rrdmetric_free(RRDMETRIC *rm) { rm->ri = NULL; } -static void rrdmetric_update_retention(RRDMETRIC *rm) { - time_t min_first_time_t = LONG_MAX, max_last_time_t = 0; - - if(rm->rrddim) { - min_first_time_t = rrddim_first_entry_t(rm->rrddim); - max_last_time_t = rrddim_last_entry_t(rm->rrddim); - } -#ifdef ENABLE_DBENGINE - else { - RRDHOST *rrdhost = rm->ri->rc->rrdhost; - for (int tier = 0; tier < storage_tiers; tier++) { - if(!rrdhost->storage_instance[tier]) continue; - - time_t first_time_t, last_time_t; - if (rrdeng_metric_retention_by_uuid(rrdhost->storage_instance[tier], &rm->uuid, &first_time_t, &last_time_t) == 0) { - if (first_time_t < min_first_time_t) - min_first_time_t = first_time_t; - - if (last_time_t > max_last_time_t) - max_last_time_t = last_time_t; - } - } - } -#endif - - if(min_first_time_t == LONG_MAX) - min_first_time_t = 0; - - if(min_first_time_t > max_last_time_t) { - internal_error(true, "RRDMETRIC: retention of '%s' is flipped", string2str(rm->id)); - time_t tmp = min_first_time_t; - min_first_time_t = max_last_time_t; - max_last_time_t = tmp; - } - - // check if retention changed - - if (min_first_time_t != rm->first_time_t) { - rm->first_time_t = min_first_time_t; - rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); - } - - if (max_last_time_t != rm->last_time_t) { - rm->last_time_t = max_last_time_t; - rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); - } - - if(unlikely(!rm->first_time_t && !rm->last_time_t)) - rrd_flag_set_deleted(rm, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); - - rm->flags |= RRD_FLAG_LIVE_RETENTION; -} - // called when this rrdmetric is inserted to the rrdmetrics dictionary of a rrdinstance -static void rrdmetric_insert_callback(const char *id __maybe_unused, void *value, void *data) { +// the constructor of the rrdmetric object +static void rrdmetric_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance) { RRDMETRIC *rm = value; // link it to its parent - rm->ri = data; + rm->ri = rrdinstance; // remove flags that we need to figure out at runtime - rm->flags = rm->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; - - rm->created_ut = now_realtime_usec(); + rm->flags = rm->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics // signal the react callback to do the job rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_NEW_OBJECT); } // called when this rrdmetric is deleted from the rrdmetrics dictionary of a rrdinstance -static void rrdmetric_delete_callback(const char *id __maybe_unused, void *value, void *data __maybe_unused) { +// the destructor of the rrdmetric object +static void rrdmetric_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance __maybe_unused) { RRDMETRIC *rm = value; internal_error(rm->rrddim, "RRDMETRIC: '%s' is freed but there is a RRDDIM linked to it.", string2str(rm->id)); @@ -551,21 +506,49 @@ static void rrdmetric_delete_callback(const char *id __maybe_unused, void *value } // called when the same rrdmetric is inserted again to the rrdmetrics dictionary of a rrdinstance -static void rrdmetric_conflict_callback(const char *id __maybe_unused, void *oldv, void *newv, void *data __maybe_unused) { - RRDMETRIC *rm = oldv; - RRDMETRIC *rm_new = newv; +// while this is called, the dictionary is write locked, but there may be other users of the object +static bool rrdmetric_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *rrdinstance __maybe_unused) { + RRDMETRIC *rm = old_value; + RRDMETRIC *rm_new = new_value; internal_error(rm->id != rm_new->id, "RRDMETRIC: '%s' cannot change id to '%s'", string2str(rm->id), string2str(rm_new->id)); if(uuid_compare(rm->uuid, rm_new->uuid) != 0) { +#ifdef NETDATA_INTERNAL_CHECKS char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN]; uuid_unparse(rm->uuid, uuid1); uuid_unparse(rm_new->uuid, uuid2); - internal_error(true, "RRDMETRIC: '%s' of instance '%s' changed uuid from '%s' to '%s'", string2str(rm->id), string2str(rm->ri->id), uuid1, uuid2); + + time_t old_first_time_t = 0; + time_t old_last_time_t = 0; + if(rrdmetric_update_retention(rm)) { + old_first_time_t = rm->first_time_t; + old_last_time_t = rm->last_time_t; + } + uuid_copy(rm->uuid, rm_new->uuid); - rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_UUID); + + time_t new_first_time_t = 0; + time_t new_last_time_t = 0; + if(rrdmetric_update_retention(rm)) { + new_first_time_t = rm->first_time_t; + new_last_time_t = rm->last_time_t; + } + + internal_error(true, + "RRDMETRIC: '%s' of instance '%s' of host '%s' changed UUID from '%s' (retention %ld to %ld, %ld secs) to '%s' (retention %ld to %ld, %ld secs)" + , string2str(rm->id) + , string2str(rm->ri->id) + , rrdhost_hostname(rm->ri->rc->rrdhost) + , uuid1, old_first_time_t, old_last_time_t, old_last_time_t - old_first_time_t + , uuid2, new_first_time_t, new_last_time_t, new_last_time_t - new_first_time_t + ); +#else + uuid_copy(rm->uuid, rm_new->uuid); +#endif + rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(rm->rrddim && rm_new->rrddim && rm->rrddim != rm_new->rrddim) { @@ -573,12 +556,14 @@ static void rrdmetric_conflict_callback(const char *id __maybe_unused, void *old rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING); } +#ifdef NETDATA_INTERNAL_CHECKS if(rm->rrddim && uuid_compare(rm->uuid, rm->rrddim->metric_uuid) != 0) { char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN]; uuid_unparse(rm->uuid, uuid1); uuid_unparse(rm_new->uuid, uuid2); - internal_error(true, "RRDMETRIC: '%s' is linked to RRDDIM '%s' but they have different UUIDs. RRDMETRIC has '%s', RRDDIM has '%s'", string2str(rm->id), rm->rrddim->id, uuid1, uuid2); + internal_error(true, "RRDMETRIC: '%s' is linked to RRDDIM '%s' but they have different UUIDs. RRDMETRIC has '%s', RRDDIM has '%s'", string2str(rm->id), rrddim_id(rm->rrddim), uuid1, uuid2); } +#endif if(rm->rrddim != rm_new->rrddim) rm->rrddim = rm_new->rrddim; @@ -587,7 +572,7 @@ static void rrdmetric_conflict_callback(const char *id __maybe_unused, void *old STRING *old = rm->name; rm->name = string_dup(rm_new->name); string_freez(old); - rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_NAME); + rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(!rm->first_time_t || (rm_new->first_time_t && rm_new->first_time_t < rm->first_time_t)) { @@ -600,98 +585,74 @@ static void rrdmetric_conflict_callback(const char *id __maybe_unused, void *old rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); } - rm->flags |= (rm_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); + rrd_flag_set(rm, rm_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no needs for atomics on rm_new if(rrd_flag_is_collected(rm) && rrd_flag_is_archived(rm)) rrd_flag_set_collected(rm); - if(rm->flags & RRD_FLAG_UPDATED) - rm->flags |= RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT; + if(rrd_flag_check(rm, RRD_FLAG_UPDATED)) + rrd_flag_set(rm, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT); rrdmetric_free(rm_new); // the react callback will continue from here + return rrd_flag_is_updated(rm); } -static void rrdmetric_react_callback(const char *id __maybe_unused, void *value, void *data __maybe_unused) { +// this is called after the insert or the conflict callbacks, +// but the dictionary is now unlocked +static void rrdmetric_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdinstance __maybe_unused) { RRDMETRIC *rm = value; - - rrdmetric_trigger_updates(rm, false, true); + rrdmetric_trigger_updates(rm, __FUNCTION__ ); } -static void rrdmetrics_create(RRDINSTANCE *ri) { +static void rrdmetrics_create_in_rrdinstance(RRDINSTANCE *ri) { if(unlikely(!ri)) return; if(likely(ri->rrdmetrics)) return; - ri->rrdmetrics = dictionary_create(DICTIONARY_FLAG_DONT_OVERWRITE_VALUE); - dictionary_register_insert_callback(ri->rrdmetrics, rrdmetric_insert_callback, (void *)ri); - dictionary_register_delete_callback(ri->rrdmetrics, rrdmetric_delete_callback, (void *)ri); - dictionary_register_conflict_callback(ri->rrdmetrics, rrdmetric_conflict_callback, (void *)ri); - dictionary_register_react_callback(ri->rrdmetrics, rrdmetric_react_callback, (void *)ri); + ri->rrdmetrics = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + dictionary_register_insert_callback(ri->rrdmetrics, rrdmetric_insert_callback, ri); + dictionary_register_delete_callback(ri->rrdmetrics, rrdmetric_delete_callback, ri); + dictionary_register_conflict_callback(ri->rrdmetrics, rrdmetric_conflict_callback, ri); + dictionary_register_react_callback(ri->rrdmetrics, rrdmetric_react_callback, ri); } -static void rrdmetrics_destroy(RRDINSTANCE *ri) { +static void rrdmetrics_destroy_from_rrdinstance(RRDINSTANCE *ri) { if(unlikely(!ri || !ri->rrdmetrics)) return; dictionary_destroy(ri->rrdmetrics); ri->rrdmetrics = NULL; } -static inline bool rrdmetric_should_be_deleted(RRDMETRIC *rm) { - if(likely(!(rm->flags & RRD_FLAG_DELETED))) - return false; - - if(likely(!(rm->flags & RRD_FLAG_LIVE_RETENTION))) - return false; - - if(unlikely(rm->flags & RRD_FLAGS_PREVENTING_DELETIONS)) - return false; - - if(likely(rm->rrddim)) - return false; - - if((now_realtime_usec() - rm->created_ut) < 600 * USEC_PER_SEC) - return false; - - rrdmetric_update_retention(rm); - if(rm->first_time_t || rm->last_time_t) - return false; - - return true; -} - -static void rrdmetric_trigger_updates(RRDMETRIC *rm, bool force, bool escalate) { - if(likely(!force && !(rm->flags & RRD_FLAG_UPDATED))) return; - - if(unlikely(rrd_flag_is_collected(rm) && !rm->rrddim)) - rrd_flag_set_archived(rm); - - if(unlikely((rm->flags & RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD) && rrd_flag_is_collected(rm))) - rrd_flag_set_archived(rm); - - rrdmetric_update_retention(rm); +// trigger post-processing of the rrdmetric, escalating changes to the rrdinstance it belongs +static void rrdmetric_trigger_updates(RRDMETRIC *rm, const char *function) { + if(unlikely(rrd_flag_is_collected(rm)) && (!rm->rrddim || rrd_flag_check(rm, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD))) + rrd_flag_set_archived(rm); - if(unlikely(escalate && rm->flags & RRD_FLAG_UPDATED && !(rm->ri->flags & RRD_FLAG_DONT_PROCESS))) { - log_transition(rm->id, rm->ri->id, rm->ri->rc->id, rm->flags, "RRDMETRIC"); - rrdinstance_trigger_updates(rm->ri, true, true); + if(rrd_flag_is_updated(rm) || !rrd_flag_check(rm, RRD_FLAG_LIVE_RETENTION)) { + rrd_flag_set_updated(rm->ri, RRD_FLAG_UPDATE_REASON_TRIGGERED); + rrdcontext_queue_for_post_processing(rm->ri->rc, function, rm->flags); } } +// ---------------------------------------------------------------------------- +// RRDMETRIC HOOKS ON RRDDIM + static inline void rrdmetric_from_rrddim(RRDDIM *rd) { if(unlikely(!rd->rrdset)) - fatal("RRDMETRIC: rrddim '%s' does not have a rrdset.", rd->id); + fatal("RRDMETRIC: rrddim '%s' does not have a rrdset.", rrddim_id(rd)); if(unlikely(!rd->rrdset->rrdhost)) - fatal("RRDMETRIC: rrdset '%s' does not have a rrdhost", rd->rrdset->id); + fatal("RRDMETRIC: rrdset '%s' does not have a rrdhost", rrdset_id(rd->rrdset)); if(unlikely(!rd->rrdset->rrdinstance)) - fatal("RRDMETRIC: rrdset '%s' does not have a rrdinstance", rd->rrdset->id); + fatal("RRDMETRIC: rrdset '%s' does not have a rrdinstance", rrdset_id(rd->rrdset)); RRDINSTANCE *ri = rrdinstance_acquired_value(rd->rrdset->rrdinstance); RRDMETRIC trm = { - .id = string_strdupz(rd->id), - .name = string_strdupz(rd->name), - .flags = RRD_FLAG_NONE, + .id = string_dup(rd->id), + .name = string_dup(rd->name), + .flags = RRD_FLAG_NONE, // no need for atomics .rrddim = rd, }; uuid_copy(trm.uuid, rd->metric_uuid); @@ -707,14 +668,18 @@ static inline void rrdmetric_from_rrddim(RRDDIM *rd) { #define rrddim_get_rrdmetric(rd) rrddim_get_rrdmetric_with_trace(rd, __FUNCTION__) static inline RRDMETRIC *rrddim_get_rrdmetric_with_trace(RRDDIM *rd, const char *function) { if(unlikely(!rd->rrdmetric)) { - error("RRDMETRIC: RRDDIM '%s' is not linked to an RRDMETRIC at %s()", rd->id, function); + error("RRDMETRIC: RRDDIM '%s' is not linked to an RRDMETRIC at %s()", rrddim_id(rd), function); return NULL; } RRDMETRIC *rm = rrdmetric_acquired_value(rd->rrdmetric); + if(unlikely(!rm)) { + error("RRDMETRIC: RRDDIM '%s' lost the link to its RRDMETRIC at %s()", rrddim_id(rd), function); + return NULL; + } if(unlikely(rm->rrddim != rd)) - fatal("RRDMETRIC: '%s' is not linked to RRDDIM '%s' at %s()", string2str(rm->id), rd->id, function); + fatal("RRDMETRIC: '%s' is not linked to RRDDIM '%s' at %s()", string2str(rm->id), rrddim_id(rd), function); return rm; } @@ -727,7 +692,7 @@ static inline void rrdmetric_rrddim_is_freed(RRDDIM *rd) { rrd_flag_set_archived(rm); rm->rrddim = NULL; - rrdmetric_trigger_updates(rm, false, true); + rrdmetric_trigger_updates(rm, __FUNCTION__ ); rrdmetric_release(rd->rrdmetric); rd->rrdmetric = NULL; } @@ -736,12 +701,12 @@ static inline void rrdmetric_updated_rrddim_flags(RRDDIM *rd) { RRDMETRIC *rm = rrddim_get_rrdmetric(rd); if(unlikely(!rm)) return; - if(unlikely(rd->flags & (RRDDIM_FLAG_ARCHIVED | RRDDIM_FLAG_OBSOLETE))) { + if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED|RRDDIM_FLAG_OBSOLETE))) { if(unlikely(rrd_flag_is_collected(rm))) rrd_flag_set_archived(rm); } - rrdmetric_trigger_updates(rm, false, true); + rrdmetric_trigger_updates(rm, __FUNCTION__ ); } static inline void rrdmetric_collected_rrddim(RRDDIM *rd) { @@ -751,7 +716,10 @@ static inline void rrdmetric_collected_rrddim(RRDDIM *rd) { if(unlikely(!rrd_flag_is_collected(rm))) rrd_flag_set_collected(rm); - rrdmetric_trigger_updates(rm, false, true); + // we use this variable to detect BEGIN/END without SET + rm->ri->internal.collected_metrics_count++; + + rrdmetric_trigger_updates(rm, __FUNCTION__ ); } // ---------------------------------------------------------------------------- @@ -759,10 +727,10 @@ static inline void rrdmetric_collected_rrddim(RRDDIM *rd) { static void rrdinstance_free(RRDINSTANCE *ri) { - if(ri->flags & RRD_FLAG_OWN_LABELS) + if(rrd_flag_check(ri, RRD_FLAG_OWN_LABELS)) dictionary_destroy(ri->rrdlabels); - rrdmetrics_destroy(ri); + rrdmetrics_destroy_from_rrdinstance(ri); string_freez(ri->id); string_freez(ri->name); string_freez(ri->title); @@ -780,7 +748,7 @@ static void rrdinstance_free(RRDINSTANCE *ri) { ri->rrdset = NULL; } -static void rrdinstance_insert_callback(const char *id __maybe_unused, void *value, void *data) { +static void rrdinstance_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdcontext) { static STRING *ml_anomaly_rates_id = NULL; if(unlikely(!ml_anomaly_rates_id)) @@ -789,64 +757,66 @@ static void rrdinstance_insert_callback(const char *id __maybe_unused, void *val RRDINSTANCE *ri = value; // link it to its parent - ri->rc = data; + ri->rc = rrdcontext; - ri->flags = ri->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; + ri->flags = ri->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics if(!ri->name) ri->name = string_dup(ri->id); - if(ri->rrdset && ri->rrdset->state) { - ri->rrdlabels = ri->rrdset->state->chart_labels; - if(ri->flags & RRD_FLAG_OWN_LABELS) - ri->flags &= ~RRD_FLAG_OWN_LABELS; + if(ri->rrdset) { + ri->rrdlabels = ri->rrdset->rrdlabels; + ri->flags &= ~RRD_FLAG_OWN_LABELS; // no need of atomics at the constructor } else { ri->rrdlabels = rrdlabels_create(); - ri->flags |= RRD_FLAG_OWN_LABELS; + ri->flags |= RRD_FLAG_OWN_LABELS; // no need of atomics at the constructor } if(ri->rrdset) { - if(unlikely((ri->rrdset->flags & RRDSET_FLAG_HIDDEN) || (ri->rrdset->state && ri->rrdset->state->is_ar_chart))) - ri->flags |= RRD_FLAG_HIDDEN; + if(unlikely(rrdset_flag_check(ri->rrdset, RRDSET_FLAG_HIDDEN))) + ri->flags |= RRD_FLAG_HIDDEN; // no need of atomics at the constructor else - ri->flags &= ~RRD_FLAG_HIDDEN; + ri->flags &= ~RRD_FLAG_HIDDEN; // no need of atomics at the constructor } // we need this when loading from SQL if(unlikely(ri->id == ml_anomaly_rates_id)) - ri->flags |= RRD_FLAG_HIDDEN; + ri->flags |= RRD_FLAG_HIDDEN; // no need of atomics at the constructor - rrdmetrics_create(ri); - rrdinstance_log(ri, "INSERT"); + rrdmetrics_create_in_rrdinstance(ri); // signal the react callback to do the job rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_NEW_OBJECT); } -static void rrdinstance_delete_callback(const char *id, void *value, void *data) { - (void)id; - RRDCONTEXT *rc = data; (void)rc; +static void rrdinstance_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdcontext __maybe_unused) { RRDINSTANCE *ri = (RRDINSTANCE *)value; - rrdinstance_log(ri, "DELETE"); - internal_error(ri->rrdset, "RRDINSTANCE: '%s' is freed but there is a RRDSET linked to it.", string2str(ri->id)); rrdinstance_free(ri); } -static void rrdinstance_conflict_callback(const char *id __maybe_unused, void *oldv, void *newv, void *data __maybe_unused) { - RRDINSTANCE *ri = (RRDINSTANCE *)oldv; - RRDINSTANCE *ri_new = (RRDINSTANCE *)newv; +static bool rrdinstance_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *rrdcontext __maybe_unused) { + RRDINSTANCE *ri = (RRDINSTANCE *)old_value; + RRDINSTANCE *ri_new = (RRDINSTANCE *)new_value; internal_error(ri->id != ri_new->id, "RRDINSTANCE: '%s' cannot change id to '%s'", string2str(ri->id), string2str(ri_new->id)); if(uuid_compare(ri->uuid, ri_new->uuid) != 0) { +#ifdef NETDATA_INTERNAL_CHECKS + char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN]; + uuid_unparse(ri->uuid, uuid1); + uuid_unparse(ri_new->uuid, uuid2); + internal_error(true, "RRDINSTANCE: '%s' of host '%s' changed UUID from '%s' to '%s'", + string2str(ri->id), rrdhost_hostname(ri->rc->rrdhost), uuid1, uuid2); +#endif + uuid_copy(ri->uuid, ri_new->uuid); - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_UUID); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(ri->rrdset && ri_new->rrdset && ri->rrdset != ri_new->rrdset) { @@ -854,289 +824,175 @@ static void rrdinstance_conflict_callback(const char *id __maybe_unused, void *o rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING); } - if(ri->rrdset && ri->rrdset->chart_uuid && uuid_compare(ri->uuid, *ri->rrdset->chart_uuid) != 0) { +#ifdef NETDATA_INTERNAL_CHECKS + if(ri->rrdset && uuid_compare(ri->uuid, ri->rrdset->chart_uuid) != 0) { char uuid1[UUID_STR_LEN], uuid2[UUID_STR_LEN]; uuid_unparse(ri->uuid, uuid1); - uuid_unparse(*ri->rrdset->chart_uuid, uuid2); - internal_error(true, "RRDINSTANCE: '%s' is linked to RRDSET '%s' but they have different UUIDs. RRDINSTANCE has '%s', RRDSET has '%s'", string2str(ri->id), ri->rrdset->id, uuid1, uuid2); + uuid_unparse(ri->rrdset->chart_uuid, uuid2); + internal_error(true, "RRDINSTANCE: '%s' is linked to RRDSET '%s' but they have different UUIDs. RRDINSTANCE has '%s', RRDSET has '%s'", string2str(ri->id), rrdset_id(ri->rrdset), uuid1, uuid2); } +#endif if(ri->name != ri_new->name) { STRING *old = ri->name; ri->name = string_dup(ri_new->name); string_freez(old); - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_NAME); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(ri->title != ri_new->title) { STRING *old = ri->title; ri->title = string_dup(ri_new->title); string_freez(old); - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_TITLE); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(ri->units != ri_new->units) { STRING *old = ri->units; ri->units = string_dup(ri_new->units); string_freez(old); - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_UNITS); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(ri->family != ri_new->family) { STRING *old = ri->family; ri->family = string_dup(ri_new->family); string_freez(old); - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FAMILY); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(ri->chart_type != ri_new->chart_type) { ri->chart_type = ri_new->chart_type; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_CHART_TYPE); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(ri->priority != ri_new->priority) { ri->priority = ri_new->priority; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_PRIORITY); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(ri->update_every != ri_new->update_every) { ri->update_every = ri_new->update_every; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_UPDATE_EVERY); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(ri->rrdset != ri_new->rrdset) { ri->rrdset = ri_new->rrdset; - if(ri->rrdset && (ri->flags & RRD_FLAG_OWN_LABELS)) { + if(ri->rrdset && rrd_flag_check(ri, RRD_FLAG_OWN_LABELS)) { DICTIONARY *old = ri->rrdlabels; - ri->rrdlabels = ri->rrdset->state->chart_labels; - ri->flags &= ~RRD_FLAG_OWN_LABELS; + ri->rrdlabels = ri->rrdset->rrdlabels; + rrd_flag_clear(ri, RRD_FLAG_OWN_LABELS); rrdlabels_destroy(old); } - else if(!ri->rrdset && !(ri->flags & RRD_FLAG_OWN_LABELS)) { + else if(!ri->rrdset && !rrd_flag_check(ri, RRD_FLAG_OWN_LABELS)) { ri->rrdlabels = rrdlabels_create(); - ri->flags |= RRD_FLAG_OWN_LABELS; + rrd_flag_set(ri, RRD_FLAG_OWN_LABELS); } } if(ri->rrdset) { - if(unlikely((ri->rrdset->flags & RRDSET_FLAG_HIDDEN) || (ri->rrdset->state && ri->rrdset->state->is_ar_chart))) - ri->flags |= RRD_FLAG_HIDDEN; + if(unlikely(rrdset_flag_check(ri->rrdset, RRDSET_FLAG_HIDDEN))) + rrd_flag_set(ri, RRD_FLAG_HIDDEN); else - ri->flags &= ~RRD_FLAG_HIDDEN; + rrd_flag_clear(ri, RRD_FLAG_HIDDEN); } - ri->flags |= (ri_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); + rrd_flag_set(ri, ri_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no need for atomics on ri_new if(rrd_flag_is_collected(ri) && rrd_flag_is_archived(ri)) rrd_flag_set_collected(ri); - if(ri->flags & RRD_FLAG_UPDATED) - ri->flags |= RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT; - - rrdinstance_log(ri, "CONFLICT"); + if(rrd_flag_is_updated(ri)) + rrd_flag_set(ri, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT); // free the new one rrdinstance_free(ri_new); // the react callback will continue from here + return rrd_flag_is_updated(ri); } -static void rrdinstance_react_callback(const char *id __maybe_unused, void *value, void *data __maybe_unused) { +static void rrdinstance_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdcontext __maybe_unused) { RRDINSTANCE *ri = value; - rrdinstance_trigger_updates(ri, false, true); + rrdinstance_trigger_updates(ri, __FUNCTION__ ); } -void rrdinstances_create(RRDCONTEXT *rc) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - +void rrdinstances_create_in_rrdcontext(RRDCONTEXT *rc) { if(unlikely(!rc || rc->rrdinstances)) return; - rc->rrdinstances = dictionary_create(DICTIONARY_FLAG_DONT_OVERWRITE_VALUE); - dictionary_register_insert_callback(rc->rrdinstances, rrdinstance_insert_callback, (void *)rc); - dictionary_register_delete_callback(rc->rrdinstances, rrdinstance_delete_callback, (void *)rc); - dictionary_register_conflict_callback(rc->rrdinstances, rrdinstance_conflict_callback, (void *)rc); - dictionary_register_react_callback(rc->rrdinstances, rrdinstance_react_callback, (void *)rc); + rc->rrdinstances = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + dictionary_register_insert_callback(rc->rrdinstances, rrdinstance_insert_callback, rc); + dictionary_register_delete_callback(rc->rrdinstances, rrdinstance_delete_callback, rc); + dictionary_register_conflict_callback(rc->rrdinstances, rrdinstance_conflict_callback, rc); + dictionary_register_react_callback(rc->rrdinstances, rrdinstance_react_callback, rc); } -void rrdinstances_destroy(RRDCONTEXT *rc) { +void rrdinstances_destroy_from_rrdcontext(RRDCONTEXT *rc) { if(unlikely(!rc || !rc->rrdinstances)) return; dictionary_destroy(rc->rrdinstances); rc->rrdinstances = NULL; } -static inline bool rrdinstance_should_be_deleted(RRDINSTANCE *ri) { - if(likely(!(ri->flags & RRD_FLAG_DELETED))) - return false; - - if(likely(!(ri->flags & RRD_FLAG_LIVE_RETENTION))) - return false; - - if(unlikely(ri->flags & RRD_FLAGS_PREVENTING_DELETIONS)) - return false; - - if(likely(ri->rrdset)) - return false; - - if(unlikely(dictionary_stats_referenced_items(ri->rrdmetrics) != 0)) - return false; - - if(unlikely(dictionary_stats_entries(ri->rrdmetrics) != 0)) - return false; - - if(ri->first_time_t || ri->last_time_t) - return false; - - return true; -} - -static void rrdinstance_trigger_updates(RRDINSTANCE *ri, bool force, bool escalate) { - if(unlikely(ri->flags & RRD_FLAG_DONT_PROCESS)) return; - if(unlikely(!force && !(ri->flags & RRD_FLAG_UPDATED))) return; +static void rrdinstance_trigger_updates(RRDINSTANCE *ri, const char *function) { + RRDSET *st = ri->rrdset; - if(likely(ri->rrdset)) { - if(unlikely(ri->rrdset->priority != ri->priority)) { - ri->priority = ri->rrdset->priority; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_PRIORITY); + if(likely(st)) { + if(unlikely((unsigned int) st->priority != ri->priority)) { + ri->priority = st->priority; + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } - if(unlikely(ri->rrdset->update_every != ri->update_every)) { - ri->update_every = ri->rrdset->update_every; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_UPDATE_EVERY); + if(unlikely(st->update_every != ri->update_every)) { + ri->update_every = st->update_every; + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } } else if(unlikely(rrd_flag_is_collected(ri))) { + // there is no rrdset, but we have it as collected! + rrd_flag_set_archived(ri); rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LINKING); } - time_t min_first_time_t = LONG_MAX, max_last_time_t = 0; - size_t metrics_active = 0, metrics_deleted = 0; - bool live_retention = true, currently_collected = false; - { - RRDMETRIC *rm; - dfe_start_read((DICTIONARY *)ri->rrdmetrics, rm) { - if(!(rm->flags & RRD_FLAG_LIVE_RETENTION)) - live_retention = false; + if(rrd_flag_is_updated(ri) || !rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION)) { + rrd_flag_set_updated(ri->rc, RRD_FLAG_UPDATE_REASON_TRIGGERED); + rrdcontext_queue_for_post_processing(ri->rc, function, ri->flags); + } +} - if (unlikely((rrdmetric_should_be_deleted(rm)))) { - metrics_deleted++; - rrd_flag_unset_updated(rm); - continue; - } +// ---------------------------------------------------------------------------- +// RRDINSTANCE HOOKS ON RRDSET - if(rm->flags & RRD_FLAG_COLLECTED) - currently_collected = true; +static inline void rrdinstance_from_rrdset(RRDSET *st) { + RRDCONTEXT trc = { + .id = string_dup(st->context), + .title = string_dup(st->title), + .units = string_dup(st->units), + .family = string_dup(st->family), + .priority = st->priority, + .chart_type = st->chart_type, + .flags = RRD_FLAG_NONE, // no need for atomics + .rrdhost = st->rrdhost, + }; - metrics_active++; + RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_set_and_acquire_item((DICTIONARY *)st->rrdhost->rrdctx, string2str(trc.id), &trc, sizeof(trc)); + RRDCONTEXT *rc = rrdcontext_acquired_value(rca); - if (rm->first_time_t && rm->first_time_t < min_first_time_t) - min_first_time_t = rm->first_time_t; - - if (rm->last_time_t && rm->last_time_t > max_last_time_t) - max_last_time_t = rm->last_time_t; - - rrd_flag_unset_updated(rm); - } - dfe_done(rm); - } - - if(live_retention && !(ri->flags & RRD_FLAG_LIVE_RETENTION)) - ri->flags |= RRD_FLAG_LIVE_RETENTION; - else if(!live_retention && (ri->flags & RRD_FLAG_LIVE_RETENTION)) - ri->flags &= ~RRD_FLAG_LIVE_RETENTION; - - if(unlikely(!metrics_active)) { - // no metrics available - - if(ri->first_time_t) { - ri->first_time_t = 0; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); - } - - if(ri->last_time_t) { - ri->last_time_t = 0; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); - } - - rrd_flag_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); - } - else { - // we have active metrics... - - if (unlikely(min_first_time_t == LONG_MAX)) - min_first_time_t = 0; - - if (unlikely(min_first_time_t == 0 || max_last_time_t == 0)) { - if(ri->first_time_t) { - ri->first_time_t = 0; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); - } - - if(ri->last_time_t) { - ri->last_time_t = 0; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); - } - - if(unlikely(live_retention)) - rrd_flag_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); - } - else { - ri->flags &= ~RRD_FLAG_UPDATE_REASON_ZERO_RETENTION; - - if (unlikely(ri->first_time_t != min_first_time_t)) { - ri->first_time_t = min_first_time_t; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); - } - - if (unlikely(ri->last_time_t != max_last_time_t)) { - ri->last_time_t = max_last_time_t; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); - } - - if(likely(currently_collected)) - rrd_flag_set_collected(ri); - else - rrd_flag_set_archived(ri); - } - } - - if(unlikely(escalate && ri->flags & RRD_FLAG_UPDATED && !(ri->rc->flags & RRD_FLAG_DONT_PROCESS))) { - log_transition(NULL, ri->id, ri->rc->id, ri->flags, "RRDINSTANCE"); - rrdcontext_trigger_updates(ri->rc, true); - } -} - -static inline void rrdinstance_from_rrdset(RRDSET *st) { - RRDCONTEXT trc = { - .id = string_strdupz(st->context), - .title = string_strdupz(st->title), - .units = string_strdupz(st->units), - .family = string_strdupz(st->family), - .priority = st->priority, - .chart_type = st->chart_type, - .flags = RRD_FLAG_NONE, - .rrdhost = st->rrdhost, - }; - - RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_set_and_acquire_item((DICTIONARY *)st->rrdhost->rrdctx, string2str(trc.id), &trc, sizeof(trc)); - RRDCONTEXT *rc = rrdcontext_acquired_value(rca); - - RRDINSTANCE tri = { - .id = string_strdupz(st->id), - .name = string_strdupz(st->name), - .units = string_strdupz(st->units), - .family = string_strdupz(st->family), - .title = string_strdupz(st->title), - .chart_type = st->chart_type, - .priority = st->priority, - .update_every = st->update_every, - .flags = RRD_FLAG_DONT_PROCESS, - .rrdset = st, - }; - uuid_copy(tri.uuid, *st->chart_uuid); + RRDINSTANCE tri = { + .id = string_dup(st->id), + .name = string_dup(st->name), + .units = string_dup(st->units), + .family = string_dup(st->family), + .title = string_dup(st->title), + .chart_type = st->chart_type, + .priority = st->priority, + .update_every = st->update_every, + .flags = RRD_FLAG_NONE, // no need for atomics + .rrdset = st, + }; + uuid_copy(tri.uuid, st->chart_uuid); RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_set_and_acquire_item(rc->rrdinstances, string2str(tri.id), &tri, sizeof(tri)); @@ -1157,18 +1013,18 @@ static inline void rrdinstance_from_rrdset(RRDSET *st) { } if(rca_old && ria_old) { - // the chart changed context - RRDCONTEXT *rc_old = rrdcontext_acquired_value(rca_old); + // Oops! The chart changed context! + + // RRDCONTEXT *rc_old = rrdcontext_acquired_value(rca_old); RRDINSTANCE *ri_old = rrdinstance_acquired_value(ria_old); // migrate all dimensions to the new metrics - rrdset_rdlock(st); RRDDIM *rd; rrddim_foreach_read(rd, st) { if (!rd->rrdmetric) continue; RRDMETRIC *rm_old = rrdmetric_acquired_value(rd->rrdmetric); - rm_old->flags = RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION; + rrd_flags_replace(rm_old, RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); rm_old->rrddim = NULL; rm_old->first_time_t = 0; rm_old->last_time_t = 0; @@ -1178,37 +1034,32 @@ static inline void rrdinstance_from_rrdset(RRDSET *st) { rrdmetric_from_rrddim(rd); } - rrdset_unlock(st); + rrddim_foreach_done(rd); // mark the old instance, ready to be deleted - if(!(ri_old->flags & RRD_FLAG_OWN_LABELS)) + if(!rrd_flag_check(ri_old, RRD_FLAG_OWN_LABELS)) ri_old->rrdlabels = rrdlabels_create(); - ri_old->flags = RRD_FLAG_OWN_LABELS|RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION; + rrd_flags_replace(ri_old, RRD_FLAG_OWN_LABELS|RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); ri_old->rrdset = NULL; ri_old->first_time_t = 0; ri_old->last_time_t = 0; - ri_old->flags &= ~RRD_FLAG_DONT_PROCESS; - rc_old->flags &= ~RRD_FLAG_DONT_PROCESS; - - rrdinstance_trigger_updates(ri_old, true, true); - - ri_old->flags |= RRD_FLAG_DONT_PROCESS; + rrdinstance_trigger_updates(ri_old, __FUNCTION__ ); rrdinstance_release(ria_old); /* // trigger updates on the old context - if(!dictionary_stats_entries(rc_old->rrdinstances) && !dictionary_stats_referenced_items(rc_old->rrdinstances)) { + if(!dictionary_entries(rc_old->rrdinstances) && !dictionary_stats_referenced_items(rc_old->rrdinstances)) { rrdcontext_lock(rc_old); rc_old->flags = ((rc_old->flags & RRD_FLAG_QUEUED)?RRD_FLAG_QUEUED:RRD_FLAG_NONE)|RRD_FLAG_DELETED|RRD_FLAG_UPDATED|RRD_FLAG_LIVE_RETENTION|RRD_FLAG_UPDATE_REASON_UNUSED|RRD_FLAG_UPDATE_REASON_ZERO_RETENTION; rc_old->first_time_t = 0; rc_old->last_time_t = 0; rrdcontext_unlock(rc_old); - rrdcontext_trigger_updates(rc_old, true); + rrdcontext_trigger_updates(rc_old, __FUNCTION__ ); } else - rrdcontext_trigger_updates(rc_old, true); + rrdcontext_trigger_updates(rc_old, __FUNCTION__ ); */ rrdcontext_release(rca_old); @@ -1223,14 +1074,18 @@ static inline void rrdinstance_from_rrdset(RRDSET *st) { #define rrdset_get_rrdinstance(st) rrdset_get_rrdinstance_with_trace(st, __FUNCTION__); static inline RRDINSTANCE *rrdset_get_rrdinstance_with_trace(RRDSET *st, const char *function) { if(unlikely(!st->rrdinstance)) { - error("RRDINSTANCE: RRDSET '%s' is not linked to an RRDINSTANCE at %s()", st->id, function); + error("RRDINSTANCE: RRDSET '%s' is not linked to an RRDINSTANCE at %s()", rrdset_id(st), function); return NULL; } RRDINSTANCE *ri = rrdinstance_acquired_value(st->rrdinstance); + if(unlikely(!ri)) { + error("RRDINSTANCE: RRDSET '%s' lost its link to an RRDINSTANCE at %s()", rrdset_id(st), function); + return NULL; + } if(unlikely(ri->rrdset != st)) - fatal("RRDINSTANCE: '%s' is not linked to RRDSET '%s' at %s()", string2str(ri->id), st->id, function); + fatal("RRDINSTANCE: '%s' is not linked to RRDSET '%s' at %s()", string2str(ri->id), rrdset_id(st), function); return ri; } @@ -1241,17 +1096,15 @@ static inline void rrdinstance_rrdset_is_freed(RRDSET *st) { rrd_flag_set_archived(ri); - if(!(ri->flags & RRD_FLAG_OWN_LABELS)) { - ri->flags |= RRD_FLAG_OWN_LABELS; + if(!rrd_flag_check(ri, RRD_FLAG_OWN_LABELS)) { ri->rrdlabels = rrdlabels_create(); - rrdlabels_copy(ri->rrdlabels, st->state->chart_labels); + rrdlabels_copy(ri->rrdlabels, st->rrdlabels); + rrd_flag_set(ri, RRD_FLAG_OWN_LABELS); } ri->rrdset = NULL; - ri->flags &= ~RRD_FLAG_DONT_PROCESS; - rrdinstance_trigger_updates(ri, false, true); - ri->flags |= RRD_FLAG_DONT_PROCESS; + rrdinstance_trigger_updates(ri, __FUNCTION__ ); rrdinstance_release(st->rrdinstance); st->rrdinstance = NULL; @@ -1260,6 +1113,14 @@ static inline void rrdinstance_rrdset_is_freed(RRDSET *st) { st->rrdcontext = NULL; } +static inline void rrdinstance_rrdset_has_updated_retention(RRDSET *st) { + RRDINSTANCE *ri = rrdset_get_rrdinstance(st); + if(unlikely(!ri)) return; + + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION); + rrdinstance_trigger_updates(ri, __FUNCTION__ ); +} + static inline void rrdinstance_updated_rrdset_name(RRDSET *st) { // the chart may not be initialized when this is called if(unlikely(!st->rrdinstance)) return; @@ -1267,28 +1128,32 @@ static inline void rrdinstance_updated_rrdset_name(RRDSET *st) { RRDINSTANCE *ri = rrdset_get_rrdinstance(st); if(unlikely(!ri)) return; - STRING *old = ri->name; - ri->name = string_strdupz(st->name); - - if(ri->name != old) - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_NAME); - - string_freez(old); + if(st->name != ri->name) { + STRING *old = ri->name; + ri->name = string_dup(st->name); + string_freez(old); - rrdinstance_trigger_updates(ri, false, true); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); + rrdinstance_trigger_updates(ri, __FUNCTION__ ); + } } static inline void rrdinstance_updated_rrdset_flags_no_action(RRDINSTANCE *ri, RRDSET *st) { - if(unlikely(st->flags & (RRDSET_FLAG_ARCHIVED | RRDSET_FLAG_OBSOLETE))) - rrd_flag_set_archived(ri); + if(unlikely(ri->rrdset != st)) + fatal("RRDCONTEXT: instance '%s' is not linked to chart '%s' on host '%s'", + string2str(ri->id), rrdset_id(st), rrdhost_hostname(st->rrdhost)); - if(unlikely((st->flags & RRDSET_FLAG_HIDDEN) && !(ri->flags & RRD_FLAG_HIDDEN))) { - ri->flags |= RRD_FLAG_HIDDEN; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FLAGS); - } - else if(unlikely(!(st->flags & RRDSET_FLAG_HIDDEN) && (ri->flags & RRD_FLAG_HIDDEN))) { - ri->flags &= ~RRD_FLAG_HIDDEN; - rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FLAGS); + bool st_is_hidden = rrdset_flag_check(st, RRDSET_FLAG_HIDDEN); + bool ri_is_hidden = rrd_flag_check(ri, RRD_FLAG_HIDDEN); + + if(unlikely(st_is_hidden != ri_is_hidden)) { + if (unlikely(st_is_hidden && !ri_is_hidden)) + rrd_flag_set_updated(ri, RRD_FLAG_HIDDEN | RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); + + else if (unlikely(!st_is_hidden && ri_is_hidden)) { + rrd_flag_clear(ri, RRD_FLAG_HIDDEN); + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); + } } } @@ -1296,11 +1161,12 @@ static inline void rrdinstance_updated_rrdset_flags(RRDSET *st) { RRDINSTANCE *ri = rrdset_get_rrdinstance(st); if(unlikely(!ri)) return; + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED|RRDSET_FLAG_OBSOLETE))) + rrd_flag_set_archived(ri); + rrdinstance_updated_rrdset_flags_no_action(ri, st); - ri->flags &= ~RRD_FLAG_DONT_PROCESS; - rrdinstance_trigger_updates(ri, false, true); - ri->flags |= RRD_FLAG_DONT_PROCESS; + rrdinstance_trigger_updates(ri, __FUNCTION__ ); } static inline void rrdinstance_collected_rrdset(RRDSET *st) { @@ -1309,13 +1175,13 @@ static inline void rrdinstance_collected_rrdset(RRDSET *st) { rrdinstance_updated_rrdset_flags_no_action(ri, st); - if(unlikely(!rrd_flag_is_collected(ri))) + if(unlikely(ri->internal.collected_metrics_count && !rrd_flag_is_collected(ri))) rrd_flag_set_collected(ri); - if(unlikely(ri->flags & RRD_FLAG_DONT_PROCESS)) - ri->flags &= ~RRD_FLAG_DONT_PROCESS; + // we use this variable to detect BEGIN/END without SET + ri->internal.collected_metrics_count = 0; - rrdinstance_trigger_updates(ri, false, true); + rrdinstance_trigger_updates(ri, __FUNCTION__ ); } // ---------------------------------------------------------------------------- @@ -1328,131 +1194,12 @@ static void rrdcontext_freez(RRDCONTEXT *rc) { string_freez(rc->family); } -static uint64_t rrdcontext_get_next_version(RRDCONTEXT *rc) { - time_t now = now_realtime_sec(); - uint64_t version = MAX(rc->version, rc->hub.version); - version = MAX((uint64_t)now, version); - version++; - return version; -} - -static void rrdcontext_message_send_unsafe(RRDCONTEXT *rc, bool snapshot __maybe_unused, void *bundle __maybe_unused) { - - // save it, so that we know the last version we sent to hub - rc->version = rc->hub.version = rrdcontext_get_next_version(rc); - rc->hub.id = string2str(rc->id); - rc->hub.title = string2str(rc->title); - rc->hub.units = string2str(rc->units); - rc->hub.family = string2str(rc->family); - rc->hub.chart_type = rrdset_type_name(rc->chart_type); - rc->hub.priority = rc->priority; - rc->hub.first_time_t = rc->first_time_t; - rc->hub.last_time_t = rrd_flag_is_collected(rc) ? 0 : rc->last_time_t; - rc->hub.deleted = (rc->flags & RRD_FLAG_DELETED) ? true : false; - -#ifdef ENABLE_ACLK - struct context_updated message = { - .id = rc->hub.id, - .version = rc->hub.version, - .title = rc->hub.title, - .units = rc->hub.units, - .family = rc->hub.family, - .chart_type = rc->hub.chart_type, - .priority = rc->hub.priority, - .first_entry = rc->hub.first_time_t, - .last_entry = rc->hub.last_time_t, - .deleted = rc->hub.deleted, - }; - - if(likely(!(rc->flags & RRD_FLAG_HIDDEN))) { - if (snapshot) { - if (!rc->hub.deleted) - contexts_snapshot_add_ctx_update(bundle, &message); - } - else - contexts_updated_add_ctx_update(bundle, &message); - } -#endif - - // store it to SQL - - if(rc->flags & RRD_FLAG_DELETED) { - rrdcontext_delete_from_sql_unsafe(rc); - } - else { - if (ctx_store_context(&rc->rrdhost->host_uuid, &rc->hub) != 0) - error("RRDCONTEXT: failed to save context '%s' version %"PRIu64" to SQL.", rc->hub.id, rc->hub.version); - } -} - -static bool check_if_cloud_version_changed_unsafe(RRDCONTEXT *rc, bool sending __maybe_unused) { - bool id_changed = false, - title_changed = false, - units_changed = false, - family_changed = false, - chart_type_changed = false, - priority_changed = false, - first_time_changed = false, - last_time_changed = false, - deleted_changed = false; - - if(unlikely(string2str(rc->id) != rc->hub.id)) - id_changed = true; - - if(unlikely(string2str(rc->title) != rc->hub.title)) - title_changed = true; - - if(unlikely(string2str(rc->units) != rc->hub.units)) - units_changed = true; - - if(unlikely(string2str(rc->family) != rc->hub.family)) - family_changed = true; - - if(unlikely(rrdset_type_name(rc->chart_type) != rc->hub.chart_type)) - chart_type_changed = true; - - if(unlikely(rc->priority != rc->hub.priority)) - priority_changed = true; - - if(unlikely((uint64_t)rc->first_time_t != rc->hub.first_time_t)) - first_time_changed = true; - - if(unlikely((uint64_t)(rrd_flag_is_collected(rc) ? 0 : rc->last_time_t) != rc->hub.last_time_t)) - last_time_changed = true; - - if(unlikely(((rc->flags & RRD_FLAG_DELETED) ? true : false) != rc->hub.deleted)) - deleted_changed = true; - - if(unlikely(id_changed || title_changed || units_changed || family_changed || chart_type_changed || priority_changed || first_time_changed || last_time_changed || deleted_changed)) { - - internal_error(true, "RRDCONTEXT: %s NEW VERSION '%s'%s, version %"PRIu64", title '%s'%s, units '%s'%s, family '%s'%s, chart type '%s'%s, priority %u%s, first_time_t %ld%s, last_time_t %ld%s, deleted '%s'%s, (queued for %llu ms, expected %llu ms)", - sending?"SENDING":"QUEUE", - string2str(rc->id), id_changed ? " (CHANGED)" : "", - rc->version, - string2str(rc->title), title_changed ? " (CHANGED)" : "", - string2str(rc->units), units_changed ? " (CHANGED)" : "", - string2str(rc->family), family_changed ? " (CHANGED)" : "", - rrdset_type_name(rc->chart_type), chart_type_changed ? " (CHANGED)" : "", - rc->priority, priority_changed ? " (CHANGED)" : "", - rc->first_time_t, first_time_changed ? " (CHANGED)" : "", - rrd_flag_is_collected(rc) ? 0 : rc->last_time_t, last_time_changed ? " (CHANGED)" : "", - (rc->flags & RRD_FLAG_DELETED) ? "true" : "false", deleted_changed ? " (CHANGED)" : "", - sending ? (now_realtime_usec() - rc->queue.queued_ut) / USEC_PER_MS : 0, - sending ? (rc->queue.scheduled_dispatch_ut - rc->queue.queued_ut) / USEC_PER_SEC : 0 - ); - return true; - } - - return false; -} - -static void rrdcontext_insert_callback(const char *id, void *value, void *data) { - (void)id; - RRDHOST *host = (RRDHOST *)data; +static void rrdcontext_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdhost) { + RRDHOST *host = (RRDHOST *)rrdhost; RRDCONTEXT *rc = (RRDCONTEXT *)value; rc->rrdhost = host; - rc->flags = rc->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; + rc->flags = rc->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS; // no need for atomics at constructor if(rc->hub.version) { // we are loading data from the SQL database @@ -1485,11 +1232,11 @@ static void rrdcontext_insert_callback(const char *id, void *value, void *data) rc->version = rc->hub.version; rc->priority = rc->hub.priority; - rc->first_time_t = rc->hub.first_time_t; - rc->last_time_t = rc->hub.last_time_t; + rc->first_time_t = (time_t)rc->hub.first_time_t; + rc->last_time_t = (time_t)rc->hub.last_time_t; if(rc->hub.deleted || !rc->hub.first_time_t) - rrd_flag_set_deleted(rc, 0); + rrd_flag_set_deleted(rc, RRD_FLAG_NONE); else { if (rc->last_time_t == 0) rrd_flag_set_collected(rc); @@ -1497,80 +1244,85 @@ static void rrdcontext_insert_callback(const char *id, void *value, void *data) rrd_flag_set_archived(rc); } - rc->flags |= RRD_FLAG_UPDATE_REASON_LOAD_SQL; + rc->flags |= RRD_FLAG_UPDATE_REASON_LOAD_SQL; // no need for atomics at constructor } else { // we are adding this context now for the first time rc->version = now_realtime_sec(); } - rrdinstances_create(rc); + rrdinstances_create_in_rrdcontext(rc); netdata_mutex_init(&rc->mutex); // signal the react callback to do the job rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_NEW_OBJECT); } -static void rrdcontext_delete_callback(const char *id, void *value, void *data) { - (void)id; - RRDHOST *host = (RRDHOST *)data; - (void)host; +static void rrdcontext_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdhost __maybe_unused) { RRDCONTEXT *rc = (RRDCONTEXT *)value; - rrdinstances_destroy(rc); + rrdinstances_destroy_from_rrdcontext(rc); netdata_mutex_destroy(&rc->mutex); rrdcontext_freez(rc); } -static void rrdcontext_conflict_callback(const char *id, void *oldv, void *newv, void *data) { - (void)id; - RRDHOST *host = (RRDHOST *)data; - (void)host; +static bool rrdcontext_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *rrdhost __maybe_unused) { + RRDCONTEXT *rc = (RRDCONTEXT *)old_value; + RRDCONTEXT *rc_new = (RRDCONTEXT *)new_value; - RRDCONTEXT *rc = (RRDCONTEXT *)oldv; - RRDCONTEXT *rc_new = (RRDCONTEXT *)newv; + //current rc is not archived, new_rc is archived, don't merge + if (!rrd_flag_is_archived(rc) && rrd_flag_is_archived(rc_new)) { + rrdcontext_freez(rc_new); + return false; + } rrdcontext_lock(rc); if(rc->title != rc_new->title) { STRING *old_title = rc->title; - rc->title = string_2way_merge(rc->title, rc_new->title); + if (rrd_flag_is_archived(rc) && !rrd_flag_is_archived(rc_new)) + rc->title = string_dup(rc_new->title); + else + rc->title = string_2way_merge(rc->title, rc_new->title); string_freez(old_title); - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_TITLE); + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(rc->units != rc_new->units) { STRING *old_units = rc->units; rc->units = string_dup(rc_new->units); string_freez(old_units); - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_UNITS); + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(rc->family != rc_new->family) { STRING *old_family = rc->family; - rc->family = string_2way_merge(rc->family, rc_new->family); + if (rrd_flag_is_archived(rc) && !rrd_flag_is_archived(rc_new)) + rc->family = string_dup(rc_new->family); + else + rc->family = string_2way_merge(rc->family, rc_new->family); string_freez(old_family); - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FAMILY); + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(rc->chart_type != rc_new->chart_type) { rc->chart_type = rc_new->chart_type; - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_CHART_TYPE); + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } if(rc->priority != rc_new->priority) { rc->priority = rc_new->priority; - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_PRIORITY); + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); } - rc->flags |= (rc_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); + rrd_flag_set(rc, rc_new->flags & RRD_FLAGS_ALLOWED_EXTERNALLY_ON_NEW_OBJECTS); // no need for atomics on rc_new if(rrd_flag_is_collected(rc) && rrd_flag_is_archived(rc)) rrd_flag_set_collected(rc); - if(rc->flags & RRD_FLAG_UPDATED) - rc->flags |= RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT; + if(rrd_flag_is_updated(rc)) + rrd_flag_set(rc, RRD_FLAG_UPDATE_REASON_UPDATED_OBJECT); rrdcontext_unlock(rc); @@ -1578,310 +1330,284 @@ static void rrdcontext_conflict_callback(const char *id, void *oldv, void *newv, rrdcontext_freez(rc_new); // the react callback will continue from here + return rrd_flag_is_updated(rc); } -static void rrdcontext_react_callback(const char *id __maybe_unused, void *value, void *data __maybe_unused) { +static void rrdcontext_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *rrdhost __maybe_unused) { RRDCONTEXT *rc = (RRDCONTEXT *)value; + rrdcontext_trigger_updates(rc, __FUNCTION__ ); +} - rrdcontext_trigger_updates(rc, false); +static void rrdcontext_trigger_updates(RRDCONTEXT *rc, const char *function) { + if(rrd_flag_is_updated(rc) || !rrd_flag_check(rc, RRD_FLAG_LIVE_RETENTION)) + rrdcontext_queue_for_post_processing(rc, function, rc->flags); } -void rrdhost_create_rrdcontexts(RRDHOST *host) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; +static void rrdcontext_hub_queue_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *nothing __maybe_unused) { + RRDCONTEXT *rc = context; + rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_HUB); + rc->queue.queued_ut = now_realtime_usec(); + rc->queue.queued_flags = rrd_flags_get(rc); +} - if(unlikely(!host)) return; - if(likely(host->rrdctx)) return; +static void rrdcontext_hub_queue_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *nothing __maybe_unused) { + RRDCONTEXT *rc = context; + rrd_flag_clear(rc, RRD_FLAG_QUEUED_FOR_HUB); +} + +static bool rrdcontext_hub_queue_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *new_context __maybe_unused, void *nothing __maybe_unused) { + // context and new_context are the same + // we just need to update the timings + RRDCONTEXT *rc = context; + rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_HUB); + rc->queue.queued_ut = now_realtime_usec(); + rc->queue.queued_flags |= rrd_flags_get(rc); - host->rrdctx = (RRDCONTEXTS *)dictionary_create(DICTIONARY_FLAG_DONT_OVERWRITE_VALUE); - dictionary_register_insert_callback((DICTIONARY *)host->rrdctx, rrdcontext_insert_callback, (void *)host); - dictionary_register_delete_callback((DICTIONARY *)host->rrdctx, rrdcontext_delete_callback, (void *)host); - dictionary_register_conflict_callback((DICTIONARY *)host->rrdctx, rrdcontext_conflict_callback, (void *)host); - dictionary_register_react_callback((DICTIONARY *)host->rrdctx, rrdcontext_react_callback, (void *)host); + return true; +} - host->rrdctx_queue = (RRDCONTEXTS *)dictionary_create(DICTIONARY_FLAG_DONT_OVERWRITE_VALUE | DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE); +static void rrdcontext_post_processing_queue_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *nothing __maybe_unused) { + RRDCONTEXT *rc = context; + rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_PP); + rc->pp.queued_flags = rc->flags; + rc->pp.queued_ut = now_realtime_usec(); } -void rrdhost_destroy_rrdcontexts(RRDHOST *host) { - if(unlikely(!host)) return; - if(unlikely(!host->rrdctx)) return; +static void rrdcontext_post_processing_queue_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *nothing __maybe_unused) { + RRDCONTEXT *rc = context; + rrd_flag_clear(rc, RRD_FLAG_QUEUED_FOR_PP); + rc->pp.dequeued_ut = now_realtime_usec(); +} - if(host->rrdctx_queue) { - dictionary_destroy((DICTIONARY *)host->rrdctx_queue); - host->rrdctx_queue = NULL; +static bool rrdcontext_post_processing_queue_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *context, void *new_context __maybe_unused, void *nothing __maybe_unused) { + RRDCONTEXT *rc = context; + bool changed = false; + + if(!(rc->flags & RRD_FLAG_QUEUED_FOR_PP)) { + rrd_flag_set(rc, RRD_FLAG_QUEUED_FOR_PP); + changed = true; } - dictionary_destroy((DICTIONARY *)host->rrdctx); - host->rrdctx = NULL; + if(rc->pp.queued_flags != rc->flags) { + rc->pp.queued_flags |= rc->flags; + changed = true; + } + + return changed; } -static inline bool rrdcontext_should_be_deleted(RRDCONTEXT *rc) { - if(likely(!(rc->flags & RRD_FLAG_DELETED))) - return false; +void rrdhost_create_rrdcontexts(RRDHOST *host) { + if(unlikely(!host)) return; + if(likely(host->rrdctx)) return; - if(likely(!(rc->flags & RRD_FLAG_LIVE_RETENTION))) - return false; + host->rrdctx = (RRDCONTEXTS *)dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + dictionary_register_insert_callback((DICTIONARY *)host->rrdctx, rrdcontext_insert_callback, host); + dictionary_register_delete_callback((DICTIONARY *)host->rrdctx, rrdcontext_delete_callback, host); + dictionary_register_conflict_callback((DICTIONARY *)host->rrdctx, rrdcontext_conflict_callback, host); + dictionary_register_react_callback((DICTIONARY *)host->rrdctx, rrdcontext_react_callback, host); - if(unlikely(rc->flags & RRD_FLAGS_PREVENTING_DELETIONS)) - return false; + host->rrdctx_hub_queue = (RRDCONTEXTS *)dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_VALUE_LINK_DONT_CLONE); + dictionary_register_insert_callback((DICTIONARY *)host->rrdctx_hub_queue, rrdcontext_hub_queue_insert_callback, NULL); + dictionary_register_delete_callback((DICTIONARY *)host->rrdctx_hub_queue, rrdcontext_hub_queue_delete_callback, NULL); + dictionary_register_conflict_callback((DICTIONARY *)host->rrdctx_hub_queue, rrdcontext_hub_queue_conflict_callback, NULL); - if(unlikely(dictionary_stats_referenced_items(rc->rrdinstances) != 0)) - return false; + host->rrdctx_post_processing_queue = (RRDCONTEXTS *)dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_VALUE_LINK_DONT_CLONE); + dictionary_register_insert_callback((DICTIONARY *)host->rrdctx_post_processing_queue, rrdcontext_post_processing_queue_insert_callback, NULL); + dictionary_register_delete_callback((DICTIONARY *)host->rrdctx_post_processing_queue, rrdcontext_post_processing_queue_delete_callback, NULL); + dictionary_register_conflict_callback((DICTIONARY *)host->rrdctx_post_processing_queue, rrdcontext_post_processing_queue_conflict_callback, NULL); +} - if(unlikely(dictionary_stats_entries(rc->rrdinstances) != 0)) - return false; +void rrdhost_destroy_rrdcontexts(RRDHOST *host) { + if(unlikely(!host)) return; + if(unlikely(!host->rrdctx)) return; - if(unlikely(rc->first_time_t || rc->last_time_t)) - return false; + DICTIONARY *old; - return true; -} + if(host->rrdctx_hub_queue) { + old = (DICTIONARY *)host->rrdctx_hub_queue; + host->rrdctx_hub_queue = NULL; -static void rrdcontext_trigger_updates(RRDCONTEXT *rc, bool force) { - if(unlikely(rc->flags & RRD_FLAG_DONT_PROCESS)) return; - if(unlikely(!force && !(rc->flags & RRD_FLAG_UPDATED))) return; + RRDCONTEXT *rc; + dfe_start_write(old, rc) { + dictionary_del(old, string2str(rc->id)); + } + dfe_done(rc); + dictionary_destroy(old); + } - rrdcontext_lock(rc); + if(host->rrdctx_post_processing_queue) { + old = (DICTIONARY *)host->rrdctx_post_processing_queue; + host->rrdctx_post_processing_queue = NULL; - size_t min_priority = LONG_MAX; - time_t min_first_time_t = LONG_MAX, max_last_time_t = 0; - size_t instances_active = 0, instances_deleted = 0; - bool live_retention = true, currently_collected = false, hidden = true; - { - RRDINSTANCE *ri; - dfe_start_read(rc->rrdinstances, ri) { - if(likely(!(ri->flags & RRD_FLAG_HIDDEN))) - hidden = false; - - if(!(ri->flags & RRD_FLAG_LIVE_RETENTION)) - live_retention = false; - - if (unlikely(rrdinstance_should_be_deleted(ri))) { - instances_deleted++; - rrd_flag_unset_updated(ri); - continue; - } - - if(ri->flags & RRD_FLAG_COLLECTED) - currently_collected = true; - - internal_error(rc->units != ri->units, - "RRDCONTEXT: '%s' rrdinstance '%s' has different units, context '%s', instance '%s'", - string2str(rc->id), string2str(ri->id), - string2str(rc->units), string2str(ri->units)); - - instances_active++; - - if (ri->priority >= RRDCONTEXT_MINIMUM_ALLOWED_PRIORITY && ri->priority < min_priority) - min_priority = ri->priority; - - if (ri->first_time_t && ri->first_time_t < min_first_time_t) - min_first_time_t = ri->first_time_t; - - if (ri->last_time_t && ri->last_time_t > max_last_time_t) - max_last_time_t = ri->last_time_t; - - rrd_flag_unset_updated(ri); - } - dfe_done(ri); - } - - if(hidden && !(rc->flags & RRD_FLAG_HIDDEN)) - rc->flags |= RRD_FLAG_HIDDEN; - else if(!hidden && (rc->flags & RRD_FLAG_HIDDEN)) - rc->flags &= ~RRD_FLAG_HIDDEN; - - if(live_retention && !(rc->flags & RRD_FLAG_LIVE_RETENTION)) - rc->flags |= RRD_FLAG_LIVE_RETENTION; - else if(!live_retention && (rc->flags & RRD_FLAG_LIVE_RETENTION)) - rc->flags &= ~RRD_FLAG_LIVE_RETENTION; - - if(unlikely(!instances_active)) { - // we had some instances, but they are gone now... - - if(rc->first_time_t) { - rc->first_time_t = 0; - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); - } - - if(rc->last_time_t) { - rc->last_time_t = 0; - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); - } - - rrd_flag_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); - } - else { - // we have some active instances... - - if (unlikely(min_first_time_t == LONG_MAX)) - min_first_time_t = 0; - - if (unlikely(min_first_time_t == 0 && max_last_time_t == 0)) { - if(rc->first_time_t) { - rc->first_time_t = 0; - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); - } - - if(rc->last_time_t) { - rc->last_time_t = 0; - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); - } - - rrd_flag_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); - } - else { - rc->flags &= ~RRD_FLAG_UPDATE_REASON_ZERO_RETENTION; - - if (unlikely(rc->first_time_t != min_first_time_t)) { - rc->first_time_t = min_first_time_t; - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); - } - - if (rc->last_time_t != max_last_time_t) { - rc->last_time_t = max_last_time_t; - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); - } - - if(likely(currently_collected)) - rrd_flag_set_collected(rc); - else - rrd_flag_set_archived(rc); - } - - if (min_priority != LONG_MAX && rc->priority != min_priority) { - rc->priority = min_priority; - rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_PRIORITY); - } - } - - if(unlikely(rc->flags & RRD_FLAG_UPDATED)) { - log_transition(NULL, NULL, rc->id, rc->flags, "RRDCONTEXT"); - - if(check_if_cloud_version_changed_unsafe(rc, false)) { - rc->version = rrdcontext_get_next_version(rc); - - if(rc->flags & RRD_FLAG_QUEUED) { - rc->queue.queued_ut = now_realtime_usec(); - rc->queue.queued_flags |= rc->flags; - } - else { - rc->queue.queued_ut = now_realtime_usec(); - rc->queue.queued_flags = rc->flags; - - rc->flags |= RRD_FLAG_QUEUED; - dictionary_set((DICTIONARY *)rc->rrdhost->rrdctx_queue, string2str(rc->id), rc, sizeof(*rc)); - } + RRDCONTEXT *rc; + dfe_start_write(old, rc) { + dictionary_del(old, string2str(rc->id)); } - - rrd_flag_unset_updated(rc); + dfe_done(rc); + dictionary_destroy(old); } - rrdcontext_unlock(rc); + old = (DICTIONARY *)host->rrdctx; + host->rrdctx = NULL; + dictionary_destroy(old); } // ---------------------------------------------------------------------------- // public API void rrdcontext_updated_rrddim(RRDDIM *rd) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdmetric_from_rrddim(rd); } void rrdcontext_removed_rrddim(RRDDIM *rd) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdmetric_rrddim_is_freed(rd); } void rrdcontext_updated_rrddim_algorithm(RRDDIM *rd) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdmetric_updated_rrddim_flags(rd); } void rrdcontext_updated_rrddim_multiplier(RRDDIM *rd) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdmetric_updated_rrddim_flags(rd); } void rrdcontext_updated_rrddim_divisor(RRDDIM *rd) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdmetric_updated_rrddim_flags(rd); } void rrdcontext_updated_rrddim_flags(RRDDIM *rd) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdmetric_updated_rrddim_flags(rd); } void rrdcontext_collected_rrddim(RRDDIM *rd) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdmetric_collected_rrddim(rd); } void rrdcontext_updated_rrdset(RRDSET *st) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdinstance_from_rrdset(st); } void rrdcontext_removed_rrdset(RRDSET *st) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdinstance_rrdset_is_freed(st); } -void rrdcontext_updated_rrdset_name(RRDSET *st) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; +void rrdcontext_updated_retention_rrdset(RRDSET *st) { + rrdinstance_rrdset_has_updated_retention(st); +} +void rrdcontext_updated_rrdset_name(RRDSET *st) { rrdinstance_updated_rrdset_name(st); } void rrdcontext_updated_rrdset_flags(RRDSET *st) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdinstance_updated_rrdset_flags(st); } void rrdcontext_collected_rrdset(RRDSET *st) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - rrdinstance_collected_rrdset(st); } void rrdcontext_host_child_connected(RRDHOST *host) { (void)host; - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; - // no need to do anything here ; } +int rrdcontext_find_dimension_uuid(RRDSET *st, const char *id, uuid_t *store_uuid) { + if(!st->rrdhost) return 1; + if(!st->context) return 2; + + RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)st->rrdhost->rrdctx, string2str(st->context)); + if(!rca) return 3; + + RRDCONTEXT *rc = rrdcontext_acquired_value(rca); + + RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_get_and_acquire_item(rc->rrdinstances, string2str(st->id)); + if(!ria) { + rrdcontext_release(rca); + return 4; + } + + RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + + RRDMETRIC_ACQUIRED *rma = (RRDMETRIC_ACQUIRED *)dictionary_get_and_acquire_item(ri->rrdmetrics, id); + if(!rma) { + rrdinstance_release(ria); + rrdcontext_release(rca); + return 5; + } + + RRDMETRIC *rm = rrdmetric_acquired_value(rma); + + uuid_copy(*store_uuid, rm->uuid); + + rrdmetric_release(rma); + rrdinstance_release(ria); + rrdcontext_release(rca); + return 0; +} + +int rrdcontext_find_chart_uuid(RRDSET *st, uuid_t *store_uuid) { + if(!st->rrdhost) return 1; + if(!st->context) return 2; + + RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)st->rrdhost->rrdctx, string2str(st->context)); + if(!rca) return 3; + + RRDCONTEXT *rc = rrdcontext_acquired_value(rca); + + RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_get_and_acquire_item(rc->rrdinstances, string2str(st->id)); + if(!ria) { + rrdcontext_release(rca); + return 4; + } + + RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + uuid_copy(*store_uuid, ri->uuid); + + rrdinstance_release(ria); + rrdcontext_release(rca); + return 0; +} + void rrdcontext_host_child_disconnected(RRDHOST *host) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; + rrdcontext_recalculate_host_retention(host, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD, false); +} + +static usec_t rrdcontext_next_db_rotation_ut = 0; +void rrdcontext_db_rotation(void) { + // called when the db rotates its database + rrdcontext_next_db_rotation_ut = now_realtime_usec() + FULL_RETENTION_SCAN_DELAY_AFTER_DB_ROTATION_SECS * USEC_PER_SEC; +} + +int rrdcontext_foreach_instance_with_rrdset_in_context(RRDHOST *host, const char *context, int (*callback)(RRDSET *st, void *data), void *data) { + if(unlikely(!host || !context || !*context || !callback)) + return -1; - rrdcontext_recalculate_host_retention(host, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD, -1); + RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)host->rrdctx, context); + if(unlikely(!rca)) return -1; + + RRDCONTEXT *rc = rrdcontext_acquired_value(rca); + if(unlikely(!rc)) return -1; + + int ret = 0; + RRDINSTANCE *ri; + dfe_start_read(rc->rrdinstances, ri) { + if(ri->rrdset) { + int r = callback(ri->rrdset, data); + if(r >= 0) ret += r; + else { + ret = r; + break; + } + } + } + dfe_done(ri); + + rrdcontext_release(rca); + + return ret; } // ---------------------------------------------------------------------------- @@ -1933,7 +1659,7 @@ void rrdcontext_hub_checkpoint_command(void *ptr) { if(rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS)) { info("RRDCONTEXT: received checkpoint command for claim id '%s', node id '%s', while node '%s' has an active context streaming.", - cmd->claim_id, cmd->node_id, host->hostname); + cmd->claim_id, cmd->node_id, rrdhost_hostname(host)); // disable it temporarily, so that our worker will not attempt to send messages in parallel rrdhost_flag_clear(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS); @@ -1943,7 +1669,7 @@ void rrdcontext_hub_checkpoint_command(void *ptr) { if(cmd->version_hash != our_version_hash) { error("RRDCONTEXT: received version hash %"PRIu64" for host '%s', does not match our version hash %"PRIu64". Sending snapshot of all contexts.", - cmd->version_hash, host->hostname, our_version_hash); + cmd->version_hash, rrdhost_hostname(host), our_version_hash); #ifdef ENABLE_ACLK // prepare the snapshot @@ -1952,7 +1678,7 @@ void rrdcontext_hub_checkpoint_command(void *ptr) { contexts_snapshot_t bundle = contexts_snapshot_new(cmd->claim_id, uuid, our_version_hash); // do a deep scan on every metric of the host to make sure all our data are updated - rrdcontext_recalculate_host_retention(host, RRD_FLAG_NONE, -1); + rrdcontext_recalculate_host_retention(host, RRD_FLAG_NONE, false); // calculate version hash and pack all the messages together in one go our_version_hash = rrdcontext_version_hash_with_callback(host, rrdcontext_message_send_unsafe, true, bundle); @@ -1965,11 +1691,11 @@ void rrdcontext_hub_checkpoint_command(void *ptr) { #endif } - internal_error(true, "RRDCONTEXT: host '%s' enabling streaming of contexts", host->hostname); + internal_error(true, "RRDCONTEXT: host '%s' enabling streaming of contexts", rrdhost_hostname(host)); rrdhost_flag_set(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS); char node_str[UUID_STR_LEN]; uuid_unparse_lower(*host->node_id, node_str); - log_access("ACLK REQ [%s (%s)]: STREAM CONTEXTS ENABLED", node_str, host->hostname); + log_access("ACLK REQ [%s (%s)]: STREAM CONTEXTS ENABLED", node_str, rrdhost_hostname(host)); } void rrdcontext_hub_stop_streaming_command(void *ptr) { @@ -1994,12 +1720,12 @@ void rrdcontext_hub_stop_streaming_command(void *ptr) { if(!rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS)) { error("RRDCONTEXT: received stop streaming command for claim id '%s', node id '%s', but node '%s' does not have active context streaming. Ignoring command.", - cmd->claim_id, cmd->node_id, host->hostname); + cmd->claim_id, cmd->node_id, rrdhost_hostname(host)); return; } - internal_error(true, "RRDCONTEXT: host '%s' disabling streaming of contexts", host->hostname); + internal_error(true, "RRDCONTEXT: host '%s' disabling streaming of contexts", rrdhost_hostname(host)); rrdhost_flag_clear(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS); } @@ -2021,7 +1747,8 @@ struct rrdcontext_to_json { RRD_FLAGS combined_flags; }; -static inline int rrdmetric_to_json_callback(const char *id, void *value, void *data) { +static inline int rrdmetric_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *id = dictionary_acquired_item_name(item); struct rrdcontext_to_json * t = data; RRDMETRIC *rm = value; BUFFER *wb = t->wb; @@ -2029,7 +1756,7 @@ static inline int rrdmetric_to_json_callback(const char *id, void *value, void * time_t after = t->after; time_t before = t->before; - if((rm->flags & RRD_FLAG_DELETED) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)) + if(unlikely(rrd_flag_is_deleted(rm) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED))) return 0; if(after && (!rm->last_time_t || after > rm->last_time_t)) @@ -2047,13 +1774,13 @@ static inline int rrdmetric_to_json_callback(const char *id, void *value, void * buffer_strcat(wb, ",\n"); t->combined_first_time_t = MIN(t->combined_first_time_t, rm->first_time_t); t->combined_last_time_t = MAX(t->combined_last_time_t, rm->last_time_t); - t->combined_flags |= rm->flags; + t->combined_flags |= rrd_flags_get(rm); } else { buffer_strcat(wb, "\n"); t->combined_first_time_t = rm->first_time_t; t->combined_last_time_t = rm->last_time_t; - t->combined_flags = rm->flags; + t->combined_flags = rrd_flags_get(rm); } buffer_sprintf(wb, "\t\t\t\t\t\t\"%s\": {", id); @@ -2066,25 +1793,25 @@ static inline int rrdmetric_to_json_callback(const char *id, void *value, void * buffer_sprintf(wb, "\n\t\t\t\t\t\t\t\"name\":\"%s\"" - ",\n\t\t\t\t\t\t\t\"first_time_t\":%ld" - ",\n\t\t\t\t\t\t\t\"last_time_t\":%ld" + ",\n\t\t\t\t\t\t\t\"first_time_t\":%lld" + ",\n\t\t\t\t\t\t\t\"last_time_t\":%lld" ",\n\t\t\t\t\t\t\t\"collected\":%s" , string2str(rm->name) - , rm->first_time_t - , rrd_flag_is_collected(rm) ? t->now : rm->last_time_t - , rm->flags & RRD_FLAG_COLLECTED ? "true" : "false" + , (long long)rm->first_time_t + , rrd_flag_is_collected(rm) ? (long long)t->now : (long long)rm->last_time_t + , rrd_flag_is_collected(rm) ? "true" : "false" ); if(options & RRDCONTEXT_OPTION_SHOW_DELETED) { buffer_sprintf(wb, ",\n\t\t\t\t\t\t\t\"deleted\":%s" - , rm->flags & RRD_FLAG_DELETED ? "true" : "false" + , rrd_flag_is_deleted(rm) ? "true" : "false" ); } if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) { buffer_strcat(wb, ",\n\t\t\t\t\t\t\t\"flags\":\""); - rrd_flags_to_buffer(rm->flags, wb); + rrd_flags_to_buffer(rrd_flags_get(rm), wb); buffer_strcat(wb, "\""); } @@ -2093,7 +1820,9 @@ static inline int rrdmetric_to_json_callback(const char *id, void *value, void * return 1; } -static inline int rrdinstance_to_json_callback(const char *id, void *value, void *data) { +static inline int rrdinstance_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *id = dictionary_acquired_item_name(item); + struct rrdcontext_to_json *t_parent = data; RRDINSTANCE *ri = value; BUFFER *wb = t_parent->wb; @@ -2102,7 +1831,7 @@ static inline int rrdinstance_to_json_callback(const char *id, void *value, void time_t before = t_parent->before; bool has_filter = t_parent->chart_label_key || t_parent->chart_labels_filter || t_parent->chart_dimensions; - if((ri->flags & RRD_FLAG_DELETED) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)) + if(unlikely(rrd_flag_is_deleted(ri) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED))) return 0; if(after && (!ri->last_time_t || after > ri->last_time_t)) @@ -2119,7 +1848,7 @@ static inline int rrdinstance_to_json_callback(const char *id, void *value, void time_t first_time_t = ri->first_time_t; time_t last_time_t = ri->last_time_t; - RRD_FLAGS flags = ri->flags; + RRD_FLAGS flags = rrd_flags_get(ri); BUFFER *wb_metrics = NULL; if(options & RRDCONTEXT_OPTION_SHOW_METRICS || t_parent->chart_dimensions) { @@ -2179,8 +1908,8 @@ static inline int rrdinstance_to_json_callback(const char *id, void *value, void ",\n\t\t\t\t\t\"chart_type\":\"%s\"" ",\n\t\t\t\t\t\"priority\":%u" ",\n\t\t\t\t\t\"update_every\":%d" - ",\n\t\t\t\t\t\"first_time_t\":%ld" - ",\n\t\t\t\t\t\"last_time_t\":%ld" + ",\n\t\t\t\t\t\"first_time_t\":%lld" + ",\n\t\t\t\t\t\"last_time_t\":%lld" ",\n\t\t\t\t\t\"collected\":%s" , string2str(ri->name) , string2str(ri->rc->id) @@ -2190,25 +1919,25 @@ static inline int rrdinstance_to_json_callback(const char *id, void *value, void , rrdset_type_name(ri->chart_type) , ri->priority , ri->update_every - , first_time_t - , (flags & RRD_FLAG_COLLECTED) ? t_parent->now : last_time_t + , (long long)first_time_t + , (flags & RRD_FLAG_COLLECTED) ? (long long)t_parent->now : (long long)last_time_t , (flags & RRD_FLAG_COLLECTED) ? "true" : "false" ); if(options & RRDCONTEXT_OPTION_SHOW_DELETED) { buffer_sprintf(wb, ",\n\t\t\t\t\t\"deleted\":%s" - , (ri->flags & RRD_FLAG_DELETED) ? "true" : "false" + , rrd_flag_is_deleted(ri) ? "true" : "false" ); } if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) { buffer_strcat(wb, ",\n\t\t\t\t\t\"flags\":\""); - rrd_flags_to_buffer(ri->flags, wb); + rrd_flags_to_buffer(rrd_flags_get(ri), wb); buffer_strcat(wb, "\""); } - if(options & RRDCONTEXT_OPTION_SHOW_LABELS && ri->rrdlabels && dictionary_stats_entries(ri->rrdlabels)) { + if(options & RRDCONTEXT_OPTION_SHOW_LABELS && ri->rrdlabels && dictionary_entries(ri->rrdlabels)) { buffer_sprintf(wb, ",\n\t\t\t\t\t\"labels\": {\n"); rrdlabels_to_buffer(ri->rrdlabels, wb, "\t\t\t\t\t\t", ":", "\"", ",\n", NULL, NULL, NULL, NULL); buffer_strcat(wb, "\n\t\t\t\t\t}"); @@ -2227,7 +1956,8 @@ static inline int rrdinstance_to_json_callback(const char *id, void *value, void return 1; } -static inline int rrdcontext_to_json_callback(const char *id, void *value, void *data) { +static inline int rrdcontext_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *id = dictionary_acquired_item_name(item); struct rrdcontext_to_json *t_parent = data; RRDCONTEXT *rc = value; BUFFER *wb = t_parent->wb; @@ -2236,14 +1966,14 @@ static inline int rrdcontext_to_json_callback(const char *id, void *value, void time_t before = t_parent->before; bool has_filter = t_parent->chart_label_key || t_parent->chart_labels_filter || t_parent->chart_dimensions; - if(unlikely((rc->flags & RRD_FLAG_HIDDEN) && !(options & RRDCONTEXT_OPTION_SHOW_HIDDEN))) + if(unlikely(rrd_flag_check(rc, RRD_FLAG_HIDDEN) && !(options & RRDCONTEXT_OPTION_SHOW_HIDDEN))) return 0; - if((rc->flags & RRD_FLAG_DELETED) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)) + if(unlikely(rrd_flag_is_deleted(rc) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED))) return 0; if(options & RRDCONTEXT_OPTION_DEEPSCAN) - rrdcontext_recalculate_context_retention(rc, RRD_FLAG_NONE, -1); + rrdcontext_recalculate_context_retention(rc, RRD_FLAG_NONE, false); if(after && (!rc->last_time_t || after > rc->last_time_t)) return 0; @@ -2253,7 +1983,7 @@ static inline int rrdcontext_to_json_callback(const char *id, void *value, void time_t first_time_t = rc->first_time_t; time_t last_time_t = rc->last_time_t; - RRD_FLAGS flags = rc->flags; + RRD_FLAGS flags = rrd_flags_get(rc); BUFFER *wb_instances = NULL; if((options & (RRDCONTEXT_OPTION_SHOW_LABELS|RRDCONTEXT_OPTION_SHOW_INSTANCES|RRDCONTEXT_OPTION_SHOW_METRICS)) @@ -2304,29 +2034,29 @@ static inline int rrdcontext_to_json_callback(const char *id, void *value, void ",\n\t\t\t\"family\":\"%s\"" ",\n\t\t\t\"chart_type\":\"%s\"" ",\n\t\t\t\"priority\":%u" - ",\n\t\t\t\"first_time_t\":%ld" - ",\n\t\t\t\"last_time_t\":%ld" + ",\n\t\t\t\"first_time_t\":%lld" + ",\n\t\t\t\"last_time_t\":%lld" ",\n\t\t\t\"collected\":%s" , string2str(rc->title) , string2str(rc->units) , string2str(rc->family) , rrdset_type_name(rc->chart_type) , rc->priority - , first_time_t - , (flags & RRD_FLAG_COLLECTED) ? t_parent->now : last_time_t + , (long long)first_time_t + , (flags & RRD_FLAG_COLLECTED) ? (long long)t_parent->now : (long long)last_time_t , (flags & RRD_FLAG_COLLECTED) ? "true" : "false" ); if(options & RRDCONTEXT_OPTION_SHOW_DELETED) { buffer_sprintf(wb, ",\n\t\t\t\"deleted\":%s" - , (rc->flags & RRD_FLAG_DELETED) ? "true" : "false" + , rrd_flag_is_deleted(rc) ? "true" : "false" ); } if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) { buffer_strcat(wb, ",\n\t\t\t\"flags\":\""); - rrd_flags_to_buffer(rc->flags, wb); + rrd_flags_to_buffer(rrd_flags_get(rc), wb); buffer_strcat(wb, "\""); } @@ -2339,14 +2069,29 @@ static inline int rrdcontext_to_json_callback(const char *id, void *value, void ",\n\t\t\t\"last_queued\":%llu" ",\n\t\t\t\"scheduled_dispatch\":%llu" ",\n\t\t\t\"last_dequeued\":%llu" + ",\n\t\t\t\"dispatches\":%zu" ",\n\t\t\t\"hub_version\":%"PRIu64"" ",\n\t\t\t\"version\":%"PRIu64"" , rc->queue.queued_ut / USEC_PER_SEC , rc->queue.scheduled_dispatch_ut / USEC_PER_SEC , rc->queue.dequeued_ut / USEC_PER_SEC + , rc->queue.dispatches , rc->hub.version , rc->version ); + + buffer_strcat(wb, ",\n\t\t\t\"pp_reasons\":\""); + rrd_reasons_to_buffer(rc->pp.queued_flags, wb); + buffer_strcat(wb, "\""); + + buffer_sprintf(wb, + ",\n\t\t\t\"pp_last_queued\":%llu" + ",\n\t\t\t\"pp_last_dequeued\":%llu" + ",\n\t\t\t\"pp_executed\":%zu" + , rc->pp.queued_ut / USEC_PER_SEC + , rc->pp.dequeued_ut / USEC_PER_SEC + , rc->pp.executions + ); } rrdcontext_unlock(rc); @@ -2365,18 +2110,18 @@ static inline int rrdcontext_to_json_callback(const char *id, void *value, void } int rrdcontext_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, const char *context, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions) { + if(!host->rrdctx) { + error("%s(): request for host '%s' that does not have rrdcontexts initialized.", __FUNCTION__, rrdhost_hostname(host)); + return HTTP_RESP_NOT_FOUND; + } + RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)host->rrdctx, context); if(!rca) return HTTP_RESP_NOT_FOUND; RRDCONTEXT *rc = rrdcontext_acquired_value(rca); - if(after != 0 && before != 0) { - long long after_wanted = after; - long long before_wanted = before; - rrdr_relative_window_to_absolute(&after_wanted, &before_wanted); - after = after_wanted; - before = before_wanted; - } + if(after != 0 && before != 0) + rrdr_relative_window_to_absolute(&after, &before); struct rrdcontext_to_json t_contexts = { .wb = wb, @@ -2389,7 +2134,7 @@ int rrdcontext_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, R .written = 0, .now = now_realtime_sec(), }; - rrdcontext_to_json_callback(context, rc, &t_contexts); + rrdcontext_to_json_callback((DICTIONARY_ITEM *)rca, rc, &t_contexts); rrdcontext_release(rca); @@ -2400,25 +2145,25 @@ int rrdcontext_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, R } int rrdcontexts_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions) { + if(!host->rrdctx) { + error("%s(): request for host '%s' that does not have rrdcontexts initialized.", __FUNCTION__, rrdhost_hostname(host)); + return HTTP_RESP_NOT_FOUND; + } + char node_uuid[UUID_STR_LEN] = ""; if(host->node_id) uuid_unparse(*host->node_id, node_uuid); - if(after != 0 && before != 0) { - long long after_wanted = after; - long long before_wanted = before; - rrdr_relative_window_to_absolute(&after_wanted, &before_wanted); - after = after_wanted; - before = before_wanted; - } + if(after != 0 && before != 0) + rrdr_relative_window_to_absolute(&after, &before); buffer_sprintf(wb, "{\n" "\t\"hostname\": \"%s\"" ",\n\t\"machine_guid\": \"%s\"" ",\n\t\"node_id\": \"%s\"" ",\n\t\"claim_id\": \"%s\"" - , host->hostname + , rrdhost_hostname(host) , host->machine_guid , node_uuid , host->aclk_state.claimed_id ? host->aclk_state.claimed_id : "" @@ -2426,7 +2171,7 @@ int rrdcontexts_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, if(options & RRDCONTEXT_OPTION_SHOW_LABELS) { buffer_sprintf(wb, ",\n\t\"host_labels\": {\n"); - rrdlabels_to_buffer(host->host_labels, wb, "\t\t", ":", "\"", ",\n", NULL, NULL, NULL, NULL); + rrdlabels_to_buffer(host->rrdlabels, wb, "\t\t", ":", "\"", ",\n", NULL, NULL, NULL, NULL); buffer_strcat(wb, "\n\t}"); } @@ -2451,108 +2196,1534 @@ int rrdcontexts_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, } // ---------------------------------------------------------------------------- -// load from SQL - -static void rrdinstance_load_clabel(SQL_CLABEL_DATA *sld, void *data) { - RRDINSTANCE *ri = data; - rrdlabels_add(ri->rrdlabels, sld->label_key, sld->label_value, sld->label_source); +// weights API + +static void metric_entry_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct metric_entry *t = value; + t->rca = rrdcontext_acquired_dup(t->rca); + t->ria = rrdinstance_acquired_dup(t->ria); + t->rma = rrdmetric_acquired_dup(t->rma); +} +static void metric_entry_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct metric_entry *t = value; + rrdcontext_release(t->rca); + rrdinstance_release(t->ria); + rrdmetric_release(t->rma); +} +static bool metric_entry_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value __maybe_unused, void *new_value __maybe_unused, void *data __maybe_unused) { + fatal("RRDCONTEXT: %s() detected a conflict on a metric pointer!", __FUNCTION__); + return false; } -static void rrdinstance_load_dimension(SQL_DIMENSION_DATA *sd, void *data) { - RRDINSTANCE *ri = data; - - RRDMETRIC trm = { - .id = string_strdupz(sd->id), - .name = string_strdupz(sd->name), - .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, - }; - uuid_copy(trm.uuid, sd->dim_id); - - dictionary_set(ri->rrdmetrics, string2str(trm.id), &trm, sizeof(trm)); -} +DICTIONARY *rrdcontext_all_metrics_to_dict(RRDHOST *host, SIMPLE_PATTERN *contexts) { + if(!host || !host->rrdctx) + return NULL; -static void rrdinstance_load_chart_callback(SQL_CHART_DATA *sc, void *data) { - RRDHOST *host = data; + DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE); + dictionary_register_insert_callback(dict, metric_entry_insert_callback, NULL); + dictionary_register_delete_callback(dict, metric_entry_delete_callback, NULL); + dictionary_register_conflict_callback(dict, metric_entry_conflict_callback, NULL); - RRDCONTEXT tc = { - .id = string_strdupz(sc->context), - .title = string_strdupz(sc->title), - .units = string_strdupz(sc->units), - .family = string_strdupz(sc->family), - .priority = sc->priority, - .chart_type = sc->chart_type, - .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_DONT_PROCESS | RRD_FLAG_UPDATE_REASON_LOAD_SQL, - .rrdhost = host, - }; + RRDCONTEXT *rc; + dfe_start_reentrant((DICTIONARY *)host->rrdctx, rc) { + if(rrd_flag_is_deleted(rc)) + continue; - RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_set_and_acquire_item((DICTIONARY *)host->rrdctx, string2str(tc.id), &tc, sizeof(tc)); - RRDCONTEXT *rc = rrdcontext_acquired_value(rca); + if(contexts && !simple_pattern_matches(contexts, string2str(rc->id))) + continue; - RRDINSTANCE tri = { - .id = string_strdupz(sc->id), - .name = string_strdupz(sc->name), - .title = string_strdupz(sc->title), - .units = string_strdupz(sc->units), - .family = string_strdupz(sc->family), - .chart_type = sc->chart_type, - .priority = sc->priority, - .update_every = sc->update_every, - .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_DONT_PROCESS | RRD_FLAG_UPDATE_REASON_LOAD_SQL, - }; - uuid_copy(tri.uuid, sc->chart_id); + RRDINSTANCE *ri; + dfe_start_read(rc->rrdinstances, ri) { + if(rrd_flag_is_deleted(ri)) + continue; - RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_set_and_acquire_item(rc->rrdinstances, sc->id, &tri, sizeof(tri)); - RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + RRDMETRIC *rm; + dfe_start_read(ri->rrdmetrics, rm) { + if(rrd_flag_is_deleted(rm)) + continue; - ctx_get_dimension_list(&ri->uuid, rrdinstance_load_dimension, ri); - ctx_get_label_list(&ri->uuid, rrdinstance_load_clabel, ri); - ri->flags &= ~RRD_FLAG_DONT_PROCESS; - rrdinstance_trigger_updates(ri, true, true); + struct metric_entry tmp = { + .rca = (RRDCONTEXT_ACQUIRED *)rc_dfe.item, + .ria = (RRDINSTANCE_ACQUIRED *)ri_dfe.item, + .rma = (RRDMETRIC_ACQUIRED *)rm_dfe.item, + }; - // let the instance be in "don't process" mode - // so that we process it once, when it is collected - ri->flags |= RRD_FLAG_DONT_PROCESS; + char buffer[20 + 1]; + ssize_t len = snprintfz(buffer, 20, "%p", rm); + dictionary_set_advanced(dict, buffer, len + 1, &tmp, sizeof(struct metric_entry), NULL); + } + dfe_done(rm); + } + dfe_done(ri); + } + dfe_done(rc); + + return dict; +} + +// ---------------------------------------------------------------------------- +// query API + +typedef struct query_target_locals { + time_t start_s; + + QUERY_TARGET *qt; + + RRDSET *st; + + const char *hosts; + const char *contexts; + const char *charts; + const char *dimensions; + const char *chart_label_key; + const char *charts_labels_filter; + + long long after; + long long before; + bool match_ids; + bool match_names; + + RRDHOST *host; + RRDCONTEXT_ACQUIRED *rca; + RRDINSTANCE_ACQUIRED *ria; + + size_t metrics_skipped_due_to_not_matching_timeframe; +} QUERY_TARGET_LOCALS; + +static __thread QUERY_TARGET thread_query_target = {}; +void query_target_release(QUERY_TARGET *qt) { + if(unlikely(!qt)) return; + if(unlikely(!qt->used)) return; + + simple_pattern_free(qt->hosts.pattern); + qt->hosts.pattern = NULL; + + simple_pattern_free(qt->contexts.pattern); + qt->contexts.pattern = NULL; + + simple_pattern_free(qt->instances.pattern); + qt->instances.pattern = NULL; + + simple_pattern_free(qt->instances.chart_label_key_pattern); + qt->instances.chart_label_key_pattern = NULL; + + simple_pattern_free(qt->instances.charts_labels_filter_pattern); + qt->instances.charts_labels_filter_pattern = NULL; + + simple_pattern_free(qt->query.pattern); + qt->query.pattern = NULL; + + // release the query + for(size_t i = 0, used = qt->query.used; i < used ;i++) { + string_freez(qt->query.array[i].dimension.id); + qt->query.array[i].dimension.id = NULL; + + string_freez(qt->query.array[i].dimension.name); + qt->query.array[i].dimension.name = NULL; + + string_freez(qt->query.array[i].chart.id); + qt->query.array[i].chart.id = NULL; + + string_freez(qt->query.array[i].chart.name); + qt->query.array[i].chart.name = NULL; + + for(size_t tier = 0; tier < storage_tiers ;tier++) { + if(qt->query.array[i].tiers[tier].db_metric_handle) { + STORAGE_ENGINE *eng = qt->query.array[i].tiers[tier].eng; + eng->api.metric_release(qt->query.array[i].tiers[tier].db_metric_handle); + qt->query.array[i].tiers[tier].db_metric_handle = NULL; + } + } + } + + // release the metrics + for(size_t i = 0, used = qt->metrics.used; i < used ;i++) { + rrdmetric_release(qt->metrics.array[i]); + qt->metrics.array[i] = NULL; + } + + // release the instances + for(size_t i = 0, used = qt->instances.used; i < used ;i++) { + rrdinstance_release(qt->instances.array[i]); + qt->instances.array[i] = NULL; + } + + // release the contexts + for(size_t i = 0, used = qt->contexts.used; i < used ;i++) { + rrdcontext_release(qt->contexts.array[i]); + qt->contexts.array[i] = NULL; + } + + // release the hosts + for(size_t i = 0, used = qt->hosts.used; i < used ;i++) { + qt->hosts.array[i] = NULL; + } + + qt->query.used = 0; + qt->metrics.used = 0; + qt->instances.used = 0; + qt->contexts.used = 0; + qt->hosts.used = 0; + + qt->db.minimum_latest_update_every = 0; + qt->db.first_time_t = 0; + qt->db.last_time_t = 0; + + qt->id[0] = '\0'; + + qt->used = false; +} +void query_target_free(void) { + if(thread_query_target.used) + query_target_release(&thread_query_target); + + freez(thread_query_target.query.array); + thread_query_target.query.array = NULL; + thread_query_target.query.size = 0; + + freez(thread_query_target.metrics.array); + thread_query_target.metrics.array = NULL; + thread_query_target.metrics.size = 0; + + freez(thread_query_target.instances.array); + thread_query_target.instances.array = NULL; + thread_query_target.instances.size = 0; + + freez(thread_query_target.contexts.array); + thread_query_target.contexts.array = NULL; + thread_query_target.contexts.size = 0; + + freez(thread_query_target.hosts.array); + thread_query_target.hosts.array = NULL; + thread_query_target.hosts.size = 0; +} + +static void query_target_add_metric(QUERY_TARGET_LOCALS *qtl, RRDMETRIC_ACQUIRED *rma, RRDINSTANCE *ri, + bool queryable_instance) { + QUERY_TARGET *qt = qtl->qt; + + RRDMETRIC *rm = rrdmetric_acquired_value(rma); + if(rrd_flag_is_deleted(rm)) + return; + + if(qt->metrics.used == qt->metrics.size) { + qt->metrics.size = (qt->metrics.size) ? qt->metrics.size * 2 : 1; + qt->metrics.array = reallocz(qt->metrics.array, qt->metrics.size * sizeof(RRDMETRIC_ACQUIRED *)); + } + qt->metrics.array[qt->metrics.used++] = rrdmetric_acquired_dup(rma); + + if(!queryable_instance) + return; + + time_t common_first_time_t = 0; + time_t common_last_time_t = 0; + time_t common_update_every = 0; + size_t tiers_added = 0; + struct { + STORAGE_ENGINE *eng; + STORAGE_METRIC_HANDLE *db_metric_handle; + time_t db_first_time_t; + time_t db_last_time_t; + time_t db_update_every; + } tier_retention[storage_tiers]; + + for (size_t tier = 0; tier < storage_tiers; tier++) { + STORAGE_ENGINE *eng = qtl->host->db[tier].eng; + tier_retention[tier].eng = eng; + tier_retention[tier].db_update_every = (time_t) (qtl->host->db[tier].tier_grouping * ri->update_every); + + if(rm->rrddim && rm->rrddim->tiers[tier] && rm->rrddim->tiers[tier]->db_metric_handle) + tier_retention[tier].db_metric_handle = eng->api.metric_dup(rm->rrddim->tiers[tier]->db_metric_handle); + else + tier_retention[tier].db_metric_handle = eng->api.metric_get(qtl->host->db[tier].instance, &rm->uuid, NULL); + + if(tier_retention[tier].db_metric_handle) { + tier_retention[tier].db_first_time_t = tier_retention[tier].eng->api.query_ops.oldest_time(tier_retention[tier].db_metric_handle); + tier_retention[tier].db_last_time_t = tier_retention[tier].eng->api.query_ops.latest_time(tier_retention[tier].db_metric_handle); + + if(!common_first_time_t) + common_first_time_t = tier_retention[tier].db_first_time_t; + else if(tier_retention[tier].db_first_time_t) + common_first_time_t = MIN(common_first_time_t, tier_retention[tier].db_first_time_t); + + if(!common_last_time_t) + common_last_time_t = tier_retention[tier].db_last_time_t; + else + common_last_time_t = MAX(common_last_time_t, tier_retention[tier].db_last_time_t); + + if(!common_update_every) + common_update_every = tier_retention[tier].db_update_every; + else if(tier_retention[tier].db_update_every) + common_update_every = MIN(common_update_every, tier_retention[tier].db_update_every); + + tiers_added++; + } + else { + tier_retention[tier].db_first_time_t = 0; + tier_retention[tier].db_last_time_t = 0; + tier_retention[tier].db_update_every = 0; + } + } + + bool release_retention = true; + bool timeframe_matches = + (tiers_added + && (common_first_time_t - common_update_every * 2) <= qt->window.before + && (common_last_time_t + common_update_every * 2) >= qt->window.after + ) ? true : false; + + if(timeframe_matches) { + RRDR_DIMENSION_FLAGS options = RRDR_DIMENSION_DEFAULT; + + if (rrd_flag_check(rm, RRD_FLAG_HIDDEN) + || (rm->rrddim && rrddim_option_check(rm->rrddim, RRDDIM_OPTION_HIDDEN))) { + options |= RRDR_DIMENSION_HIDDEN; + options &= ~RRDR_DIMENSION_SELECTED; + } + + if (qt->query.pattern) { + // we have a dimensions pattern + // lets see if this dimension is selected + + if ((qtl->match_ids && simple_pattern_matches(qt->query.pattern, string2str(rm->id))) + || (qtl->match_names && simple_pattern_matches(qt->query.pattern, string2str(rm->name))) + ) { + // it matches the pattern + options |= (RRDR_DIMENSION_SELECTED | RRDR_DIMENSION_NONZERO); + options &= ~RRDR_DIMENSION_HIDDEN; + } + else { + // it does not match the pattern + options |= RRDR_DIMENSION_HIDDEN; + options &= ~RRDR_DIMENSION_SELECTED; + } + } + else { + // we don't have a dimensions pattern + // so this is a selected dimension + // if it is not hidden + if(!(options & RRDR_DIMENSION_HIDDEN)) + options |= RRDR_DIMENSION_SELECTED; + } + + if((options & RRDR_DIMENSION_HIDDEN) && (options & RRDR_DIMENSION_SELECTED)) + options &= ~RRDR_DIMENSION_HIDDEN; + + if(!(options & RRDR_DIMENSION_HIDDEN) || (qt->request.options & RRDR_OPTION_PERCENTAGE)) { + // we have a non-hidden dimension + // let's add it to the query metrics + + if(ri->rrdset) + ri->rrdset->last_accessed_time = qtl->start_s; + + if (qt->query.used == qt->query.size) { + qt->query.size = (qt->query.size) ? qt->query.size * 2 : 1; + qt->query.array = reallocz(qt->query.array, qt->query.size * sizeof(QUERY_METRIC)); + } + QUERY_METRIC *qm = &qt->query.array[qt->query.used++]; + + qm->dimension.options = options; + + qm->link.host = qtl->host; + qm->link.rca = qtl->rca; + qm->link.ria = qtl->ria; + qm->link.rma = rma; + + qm->chart.id = string_dup(ri->id); + qm->chart.name = string_dup(ri->name); + + qm->dimension.id = string_dup(rm->id); + qm->dimension.name = string_dup(rm->name); + + if (!qt->db.first_time_t || common_first_time_t < qt->db.first_time_t) + qt->db.first_time_t = common_first_time_t; + + if (!qt->db.last_time_t || common_last_time_t > qt->db.last_time_t) + qt->db.last_time_t = common_last_time_t; + + for (size_t tier = 0; tier < storage_tiers; tier++) { + qm->tiers[tier].eng = tier_retention[tier].eng; + qm->tiers[tier].db_metric_handle = tier_retention[tier].db_metric_handle; + qm->tiers[tier].db_first_time_t = tier_retention[tier].db_first_time_t; + qm->tiers[tier].db_last_time_t = tier_retention[tier].db_last_time_t; + qm->tiers[tier].db_update_every = tier_retention[tier].db_update_every; + } + release_retention = false; + } + } + else + qtl->metrics_skipped_due_to_not_matching_timeframe++; + + if(release_retention) { + // cleanup anything we allocated to the retention we will not use + for(size_t tier = 0; tier < storage_tiers ;tier++) { + if (tier_retention[tier].db_metric_handle) + tier_retention[tier].eng->api.metric_release(tier_retention[tier].db_metric_handle); + } + } +} + +static void query_target_add_instance(QUERY_TARGET_LOCALS *qtl, RRDINSTANCE_ACQUIRED *ria, bool queryable_instance) { + QUERY_TARGET *qt = qtl->qt; + + RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + if(rrd_flag_is_deleted(ri)) + return; + + if(qt->instances.used == qt->instances.size) { + qt->instances.size = (qt->instances.size) ? qt->instances.size * 2 : 1; + qt->instances.array = reallocz(qt->instances.array, qt->instances.size * sizeof(RRDINSTANCE_ACQUIRED *)); + } + + qtl->ria = qt->instances.array[qt->instances.used++] = rrdinstance_acquired_dup(ria); + + if(qt->db.minimum_latest_update_every == 0 || ri->update_every < qt->db.minimum_latest_update_every) + qt->db.minimum_latest_update_every = ri->update_every; + + if(queryable_instance) { + if ((qt->instances.chart_label_key_pattern && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, qt->instances.chart_label_key_pattern, ':')) || + (qt->instances.charts_labels_filter_pattern && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, qt->instances.charts_labels_filter_pattern, ':'))) + queryable_instance = false; + } + + size_t added = 0; + + if(unlikely(qt->request.rma)) { + query_target_add_metric(qtl, qt->request.rma, ri, queryable_instance); + added++; + } + else { + RRDMETRIC *rm; + dfe_start_read(ri->rrdmetrics, rm) { + query_target_add_metric(qtl, (RRDMETRIC_ACQUIRED *) rm_dfe.item, ri, queryable_instance); + added++; + } + dfe_done(rm); + } + + if(!added) { + qt->instances.used--; + rrdinstance_release(ria); + } +} + +static void query_target_add_context(QUERY_TARGET_LOCALS *qtl, RRDCONTEXT_ACQUIRED *rca) { + QUERY_TARGET *qt = qtl->qt; + + RRDCONTEXT *rc = rrdcontext_acquired_value(rca); + if(rrd_flag_is_deleted(rc)) + return; + + if(qt->contexts.used == qt->contexts.size) { + qt->contexts.size = (qt->contexts.size) ? qt->contexts.size * 2 : 1; + qt->contexts.array = reallocz(qt->contexts.array, qt->contexts.size * sizeof(RRDCONTEXT_ACQUIRED *)); + } + qtl->rca = qt->contexts.array[qt->contexts.used++] = rrdcontext_acquired_dup(rca); + + size_t added = 0; + if(unlikely(qt->request.ria)) { + query_target_add_instance(qtl, qt->request.ria, true); + added++; + } + else if(unlikely(qtl->st && qtl->st->rrdcontext == rca && qtl->st->rrdinstance)) { + query_target_add_instance(qtl, qtl->st->rrdinstance, true); + added++; + } + else { + RRDINSTANCE *ri; + dfe_start_read(rc->rrdinstances, ri) { + bool queryable_instance = false; + if(!qt->instances.pattern + || (qtl->match_ids && simple_pattern_matches(qt->instances.pattern, string2str(ri->id))) + || (qtl->match_names && simple_pattern_matches(qt->instances.pattern, string2str(ri->name))) + ) + queryable_instance = true; + + query_target_add_instance(qtl, (RRDINSTANCE_ACQUIRED *)ri_dfe.item, queryable_instance); + added++; + } + dfe_done(ri); + } + + if(!added) { + qt->contexts.used--; + rrdcontext_release(rca); + } +} + +static void query_target_add_host(QUERY_TARGET_LOCALS *qtl, RRDHOST *host) { + QUERY_TARGET *qt = qtl->qt; + + if(qt->hosts.used == qt->hosts.size) { + qt->hosts.size = (qt->hosts.size) ? qt->hosts.size * 2 : 1; + qt->hosts.array = reallocz(qt->hosts.array, qt->hosts.size * sizeof(RRDHOST *)); + } + qtl->host = qt->hosts.array[qt->hosts.used++] = host; + + // is the chart given valid? + if(unlikely(qtl->st && (!qtl->st->rrdinstance || !qtl->st->rrdcontext))) { + error("QUERY TARGET: RRDSET '%s' given, because it is not linked to rrdcontext structures. Switching to context query.", rrdset_name(qtl->st)); + + if(!is_valid_sp(qtl->charts)) + qtl->charts = rrdset_name(qtl->st); + + qtl->st = NULL; + } + + size_t added = 0; + if(unlikely(qt->request.rca)) { + query_target_add_context(qtl, qt->request.rca); + added++; + } + else if(unlikely(qtl->st)) { + // single chart data queries + query_target_add_context(qtl, qtl->st->rrdcontext); + added++; + } + else { + // context pattern queries + RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item((DICTIONARY *)qtl->host->rrdctx, qtl->contexts); + if(likely(rca)) { + // we found it! + query_target_add_context(qtl, rca); + rrdcontext_release(rca); + added++; + } + else { + // Probably it is a pattern, we need to search for it... + RRDCONTEXT *rc; + dfe_start_read((DICTIONARY *)qtl->host->rrdctx, rc) { + if(qt->contexts.pattern && !simple_pattern_matches(qt->contexts.pattern, string2str(rc->id))) + continue; + + query_target_add_context(qtl, (RRDCONTEXT_ACQUIRED *)rc_dfe.item); + added++; + } + dfe_done(rc); + } + } + + if(!added) { + qt->hosts.used--; + } +} + +void query_target_generate_name(QUERY_TARGET *qt) { + char options_buffer[100 + 1]; + web_client_api_request_v1_data_options_to_string(options_buffer, 100, qt->request.options); + + char resampling_buffer[20 + 1] = ""; + if(qt->request.resampling_time > 1) + snprintfz(resampling_buffer, 20, "/resampling:%lld", (long long)qt->request.resampling_time); + + char tier_buffer[20 + 1] = ""; + if(qt->request.options & RRDR_OPTION_SELECTED_TIER) + snprintfz(tier_buffer, 20, "/tier:%zu", qt->request.tier); + + if(qt->request.st) + snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "chart://host:%s/instance:%s/dimensions:%s/after:%lld/before:%lld/points:%zu/group:%s%s/options:%s%s%s" + , rrdhost_hostname(qt->request.st->rrdhost) + , rrdset_name(qt->request.st) + , (qt->request.dimensions) ? qt->request.dimensions : "*" + , (long long)qt->request.after + , (long long)qt->request.before + , qt->request.points + , web_client_api_request_v1_data_group_to_string(qt->request.group_method) + , qt->request.group_options?qt->request.group_options:"" + , options_buffer + , resampling_buffer + , tier_buffer + ); + else if(qt->request.host && qt->request.rca && qt->request.ria && qt->request.rma) + snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "metric://host:%s/context:%s/instance:%s/dimension:%s/after:%lld/before:%lld/points:%zu/group:%s%s/options:%s%s%s" + , rrdhost_hostname(qt->request.host) + , rrdcontext_acquired_id(qt->request.rca) + , rrdinstance_acquired_id(qt->request.ria) + , rrdmetric_acquired_id(qt->request.rma) + , (long long)qt->request.after + , (long long)qt->request.before + , qt->request.points + , web_client_api_request_v1_data_group_to_string(qt->request.group_method) + , qt->request.group_options?qt->request.group_options:"" + , options_buffer + , resampling_buffer + , tier_buffer + ); + else + snprintfz(qt->id, MAX_QUERY_TARGET_ID_LENGTH, "context://host:%s/contexts:%s/instances:%s/dimensions:%s/after:%lld/before:%lld/points:%zu/group:%s%s/options:%s%s%s" + , (qt->request.host) ? rrdhost_hostname(qt->request.host) : ((qt->request.hosts) ? qt->request.hosts : "*") + , (qt->request.contexts) ? qt->request.contexts : "*" + , (qt->request.charts) ? qt->request.charts : "*" + , (qt->request.dimensions) ? qt->request.dimensions : "*" + , (long long)qt->request.after + , (long long)qt->request.before + , qt->request.points + , web_client_api_request_v1_data_group_to_string(qt->request.group_method) + , qt->request.group_options?qt->request.group_options:"" + , options_buffer + , resampling_buffer + , tier_buffer + ); + + json_fix_string(qt->id); +} + +QUERY_TARGET *query_target_create(QUERY_TARGET_REQUEST *qtr) { + QUERY_TARGET *qt = &thread_query_target; + + if(qt->used) + fatal("QUERY TARGET: this query target is already used (%zu queries made with this QUERY_TARGET so far).", qt->queries); + + qt->used = true; + qt->queries++; + + // copy the request into query_thread_target + qt->request = *qtr; + + query_target_generate_name(qt); + qt->window.after = qt->request.after; + qt->window.before = qt->request.before; + rrdr_relative_window_to_absolute(&qt->window.after, &qt->window.before); + + // prepare our local variables - we need these across all these functions + QUERY_TARGET_LOCALS qtl = { + .qt = qt, + .start_s = now_realtime_sec(), + .host = qt->request.host, + .st = qt->request.st, + .hosts = qt->request.hosts, + .contexts = qt->request.contexts, + .charts = qt->request.charts, + .dimensions = qt->request.dimensions, + .chart_label_key = qt->request.chart_label_key, + .charts_labels_filter = qt->request.charts_labels_filter, + }; + + qt->db.minimum_latest_update_every = 0; // it will be updated by query_target_add_query() + + // prepare all the patterns + qt->hosts.pattern = is_valid_sp(qtl.hosts) ? simple_pattern_create(qtl.hosts, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL; + qt->contexts.pattern = is_valid_sp(qtl.contexts) ? simple_pattern_create(qtl.contexts, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL; + qt->instances.pattern = is_valid_sp(qtl.charts) ? simple_pattern_create(qtl.charts, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL; + qt->query.pattern = is_valid_sp(qtl.dimensions) ? simple_pattern_create(qtl.dimensions, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL; + qt->instances.chart_label_key_pattern = is_valid_sp(qtl.chart_label_key) ? simple_pattern_create(qtl.chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL; + qt->instances.charts_labels_filter_pattern = is_valid_sp(qtl.charts_labels_filter) ? simple_pattern_create(qtl.charts_labels_filter, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL; + + qtl.match_ids = qt->request.options & RRDR_OPTION_MATCH_IDS; + qtl.match_names = qt->request.options & RRDR_OPTION_MATCH_NAMES; + if(likely(!qtl.match_ids && !qtl.match_names)) + qtl.match_ids = qtl.match_names = true; + + // verify that the chart belongs to the host we are interested + if(qtl.st) { + if (!qtl.host) { + // It is NULL, set it ourselves. + qtl.host = qtl.st->rrdhost; + } + else if (unlikely(qtl.host != qtl.st->rrdhost)) { + // Oops! A different host! + error("QUERY TARGET: RRDSET '%s' given does not belong to host '%s'. Switching query host to '%s'", + rrdset_name(qtl.st), rrdhost_hostname(qtl.host), rrdhost_hostname(qtl.st->rrdhost)); + qtl.host = qtl.st->rrdhost; + } + } + + if(qtl.host) { + // single host query + query_target_add_host(&qtl, qtl.host); + qtl.hosts = rrdhost_hostname(qtl.host); + } + else { + // multi host query + rrd_rdlock(); + rrdhost_foreach_read(qtl.host) { + if(!qt->hosts.pattern || simple_pattern_matches(qt->hosts.pattern, rrdhost_hostname(qtl.host))) + query_target_add_host(&qtl, qtl.host); + } + rrd_unlock(); + } + + // make sure everything is good + if(!qt->query.used || !qt->metrics.used || !qt->instances.used || !qt->contexts.used || !qt->hosts.used) { + internal_error( + true + , "QUERY TARGET: query '%s' does not have all the data required. " + "Matched %u hosts, %u contexts, %u instances, %u dimensions, %u metrics to query, " + "%zu metrics skipped because they don't have data in the desired time-frame. " + "Aborting it." + , qt->id + , qt->hosts.used + , qt->contexts.used + , qt->instances.used + , qt->metrics.used + , qt->query.used + , qtl.metrics_skipped_due_to_not_matching_timeframe + ); + + query_target_release(qt); + return NULL; + } + + if(!query_target_calculate_window(qt)) { + query_target_release(qt); + return NULL; + } + + return qt; +} + + +// ---------------------------------------------------------------------------- +// load from SQL + +static void rrdinstance_load_clabel(SQL_CLABEL_DATA *sld, void *data) { + RRDINSTANCE *ri = data; + rrdlabels_add(ri->rrdlabels, sld->label_key, sld->label_value, sld->label_source); +} + +static void rrdinstance_load_dimension(SQL_DIMENSION_DATA *sd, void *data) { + RRDINSTANCE *ri = data; + + RRDMETRIC trm = { + .id = string_strdupz(sd->id), + .name = string_strdupz(sd->name), + .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomic + }; + if(sd->hidden) trm.flags |= RRD_FLAG_HIDDEN; + + uuid_copy(trm.uuid, sd->dim_id); + + dictionary_set(ri->rrdmetrics, string2str(trm.id), &trm, sizeof(trm)); +} + +static void rrdinstance_load_chart_callback(SQL_CHART_DATA *sc, void *data) { + RRDHOST *host = data; + + RRDCONTEXT tc = { + .id = string_strdupz(sc->context), + .title = string_strdupz(sc->title), + .units = string_strdupz(sc->units), + .family = string_strdupz(sc->family), + .priority = sc->priority, + .chart_type = sc->chart_type, + .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomics + .rrdhost = host, + }; + + RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_set_and_acquire_item((DICTIONARY *)host->rrdctx, string2str(tc.id), &tc, sizeof(tc)); + RRDCONTEXT *rc = rrdcontext_acquired_value(rca); + + RRDINSTANCE tri = { + .id = string_strdupz(sc->id), + .name = string_strdupz(sc->name), + .title = string_strdupz(sc->title), + .units = string_strdupz(sc->units), + .family = string_strdupz(sc->family), + .chart_type = sc->chart_type, + .priority = sc->priority, + .update_every = sc->update_every, + .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomics + }; + uuid_copy(tri.uuid, sc->chart_id); + + RRDINSTANCE_ACQUIRED *ria = (RRDINSTANCE_ACQUIRED *)dictionary_set_and_acquire_item(rc->rrdinstances, sc->id, &tri, sizeof(tri)); + RRDINSTANCE *ri = rrdinstance_acquired_value(ria); + + ctx_get_dimension_list(&ri->uuid, rrdinstance_load_dimension, ri); + ctx_get_label_list(&ri->uuid, rrdinstance_load_clabel, ri); + rrdinstance_trigger_updates(ri, __FUNCTION__ ); + rrdinstance_release(ria); + rrdcontext_release(rca); +} + +static void rrdcontext_load_context_callback(VERSIONED_CONTEXT_DATA *ctx_data, void *data) { + RRDHOST *host = data; + (void)host; + + RRDCONTEXT trc = { + .id = string_strdupz(ctx_data->id), + .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_UPDATE_REASON_LOAD_SQL, // no need for atomics + + // no need to set more data here + // we only need the hub data + + .hub = *ctx_data, + }; + dictionary_set((DICTIONARY *)host->rrdctx, string2str(trc.id), &trc, sizeof(trc)); +} + +void rrdhost_load_rrdcontext_data(RRDHOST *host) { + if(host->rrdctx) return; + + rrdhost_create_rrdcontexts(host); + ctx_get_context_list(&host->host_uuid, rrdcontext_load_context_callback, host); + ctx_get_chart_list(&host->host_uuid, rrdinstance_load_chart_callback, host); + + RRDCONTEXT *rc; + dfe_start_read((DICTIONARY *)host->rrdctx, rc) { + rrdcontext_trigger_updates(rc, __FUNCTION__ ); + } + dfe_done(rc); + + rrdcontext_garbage_collect_single_host(host, false); +} + +// ---------------------------------------------------------------------------- +// version hash calculation + +static uint64_t rrdcontext_version_hash_with_callback( + RRDHOST *host, + void (*callback)(RRDCONTEXT *, bool, void *), + bool snapshot, + void *bundle) { + + if(unlikely(!host || !host->rrdctx)) return 0; + + RRDCONTEXT *rc; + uint64_t hash = 0; + + // loop through all contexts of the host + dfe_start_read((DICTIONARY *)host->rrdctx, rc) { + + rrdcontext_lock(rc); + + if(unlikely(rrd_flag_check(rc, RRD_FLAG_HIDDEN))) { + rrdcontext_unlock(rc); + continue; + } + + if(unlikely(callback)) + callback(rc, snapshot, bundle); + + // skip any deleted contexts + if(unlikely(rrd_flag_is_deleted(rc))) { + rrdcontext_unlock(rc); + continue; + } + + // we use rc->hub.* which has the latest + // metadata we have sent to the hub + + // if a context is currently queued, rc->hub.* does NOT + // reflect the queued changes. rc->hub.* is updated with + // their metadata, after messages are dispatched to hub. + + // when the context is being collected, + // rc->hub.last_time_t is already zero + + hash += rc->hub.version + rc->hub.last_time_t - rc->hub.first_time_t; + + rrdcontext_unlock(rc); + + } + dfe_done(rc); + + return hash; +} + +// ---------------------------------------------------------------------------- +// retention recalculation + +static void rrdcontext_recalculate_context_retention(RRDCONTEXT *rc, RRD_FLAGS reason, bool worker_jobs) { + rrdcontext_post_process_updates(rc, true, reason, worker_jobs); +} + +static void rrdcontext_recalculate_host_retention(RRDHOST *host, RRD_FLAGS reason, bool worker_jobs) { + if(unlikely(!host || !host->rrdctx)) return; + + RRDCONTEXT *rc; + dfe_start_read((DICTIONARY *)host->rrdctx, rc) { + rrdcontext_recalculate_context_retention(rc, reason, worker_jobs); + } + dfe_done(rc); +} + +static void rrdcontext_recalculate_retention_all_hosts(void) { + rrdcontext_next_db_rotation_ut = 0; + rrd_rdlock(); + RRDHOST *host; + rrdhost_foreach_read(host) { + worker_is_busy(WORKER_JOB_RETENTION); + rrdcontext_recalculate_host_retention(host, RRD_FLAG_UPDATE_REASON_DB_ROTATION, true); + } + rrd_unlock(); +} + +// ---------------------------------------------------------------------------- +// garbage collector + +static bool rrdmetric_update_retention(RRDMETRIC *rm) { + time_t min_first_time_t = LONG_MAX, max_last_time_t = 0; + + if(rm->rrddim) { + min_first_time_t = rrddim_first_entry_t(rm->rrddim); + max_last_time_t = rrddim_last_entry_t(rm->rrddim); + } +#ifdef ENABLE_DBENGINE + else if (dbengine_enabled) { + RRDHOST *rrdhost = rm->ri->rc->rrdhost; + for (size_t tier = 0; tier < storage_tiers; tier++) { + if(!rrdhost->db[tier].instance) continue; + + time_t first_time_t, last_time_t; + if (rrdeng_metric_retention_by_uuid(rrdhost->db[tier].instance, &rm->uuid, &first_time_t, &last_time_t) == 0) { + if (first_time_t < min_first_time_t) + min_first_time_t = first_time_t; + + if (last_time_t > max_last_time_t) + max_last_time_t = last_time_t; + } + } + } + else { + // cannot get retention + return false; + } +#endif + + if(min_first_time_t == LONG_MAX) + min_first_time_t = 0; + + if(min_first_time_t > max_last_time_t) { + internal_error(true, "RRDMETRIC: retention of '%s' is flipped", string2str(rm->id)); + time_t tmp = min_first_time_t; + min_first_time_t = max_last_time_t; + max_last_time_t = tmp; + } + + // check if retention changed + + if (min_first_time_t != rm->first_time_t) { + rm->first_time_t = min_first_time_t; + rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); + } + + if (max_last_time_t != rm->last_time_t) { + rm->last_time_t = max_last_time_t; + rrd_flag_set_updated(rm, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); + } + + if(unlikely(!rm->first_time_t && !rm->last_time_t)) + rrd_flag_set_deleted(rm, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); + + rrd_flag_set(rm, RRD_FLAG_LIVE_RETENTION); + + return true; +} + +static inline bool rrdmetric_should_be_deleted(RRDMETRIC *rm) { + if(likely(!rrd_flag_check(rm, RRD_FLAGS_REQUIRED_FOR_DELETIONS))) + return false; + + if(likely(rrd_flag_check(rm, RRD_FLAGS_PREVENTING_DELETIONS))) + return false; + + if(likely(rm->rrddim)) + return false; + + rrdmetric_update_retention(rm); + if(rm->first_time_t || rm->last_time_t) + return false; + + return true; +} + +static inline bool rrdinstance_should_be_deleted(RRDINSTANCE *ri) { + if(likely(!rrd_flag_check(ri, RRD_FLAGS_REQUIRED_FOR_DELETIONS))) + return false; + + if(likely(rrd_flag_check(ri, RRD_FLAGS_PREVENTING_DELETIONS))) + return false; + + if(likely(ri->rrdset)) + return false; + + if(unlikely(dictionary_referenced_items(ri->rrdmetrics) != 0)) + return false; + + if(unlikely(dictionary_entries(ri->rrdmetrics) != 0)) + return false; + + if(ri->first_time_t || ri->last_time_t) + return false; + + return true; +} + +static inline bool rrdcontext_should_be_deleted(RRDCONTEXT *rc) { + if(likely(!rrd_flag_check(rc, RRD_FLAGS_REQUIRED_FOR_DELETIONS))) + return false; + + if(likely(rrd_flag_check(rc, RRD_FLAGS_PREVENTING_DELETIONS))) + return false; + + if(unlikely(dictionary_referenced_items(rc->rrdinstances) != 0)) + return false; + + if(unlikely(dictionary_entries(rc->rrdinstances) != 0)) + return false; + + if(unlikely(rc->first_time_t || rc->last_time_t)) + return false; + + return true; +} + +void rrdcontext_delete_from_sql_unsafe(RRDCONTEXT *rc) { + // we need to refresh the string pointers in rc->hub + // in case the context changed values + rc->hub.id = string2str(rc->id); + rc->hub.title = string2str(rc->title); + rc->hub.units = string2str(rc->units); + rc->hub.family = string2str(rc->family); + + // delete it from SQL + if(ctx_delete_context(&rc->rrdhost->host_uuid, &rc->hub) != 0) + error("RRDCONTEXT: failed to delete context '%s' version %"PRIu64" from SQL.", rc->hub.id, rc->hub.version); +} + +static void rrdcontext_garbage_collect_single_host(RRDHOST *host, bool worker_jobs) { + + internal_error(true, "RRDCONTEXT: garbage collecting context structures of host '%s'", rrdhost_hostname(host)); + + RRDCONTEXT *rc; + dfe_start_reentrant((DICTIONARY *)host->rrdctx, rc) { + if(unlikely(netdata_exit)) break; + + if(worker_jobs) worker_is_busy(WORKER_JOB_CLEANUP); + + rrdcontext_lock(rc); + + RRDINSTANCE *ri; + dfe_start_reentrant(rc->rrdinstances, ri) { + if(unlikely(netdata_exit)) break; + + RRDMETRIC *rm; + dfe_start_write(ri->rrdmetrics, rm) { + if(rrdmetric_should_be_deleted(rm)) { + if(worker_jobs) worker_is_busy(WORKER_JOB_CLEANUP_DELETE); + if(!dictionary_del(ri->rrdmetrics, string2str(rm->id))) + error("RRDCONTEXT: metric '%s' of instance '%s' of context '%s' of host '%s', failed to be deleted from rrdmetrics dictionary.", + string2str(rm->id), + string2str(ri->id), + string2str(rc->id), + rrdhost_hostname(host)); + else + internal_error( + true, + "RRDCONTEXT: metric '%s' of instance '%s' of context '%s' of host '%s', deleted from rrdmetrics dictionary.", + string2str(rm->id), + string2str(ri->id), + string2str(rc->id), + rrdhost_hostname(host)); + } + } + dfe_done(rm); + + if(rrdinstance_should_be_deleted(ri)) { + if(worker_jobs) worker_is_busy(WORKER_JOB_CLEANUP_DELETE); + if(!dictionary_del(rc->rrdinstances, string2str(ri->id))) + error("RRDCONTEXT: instance '%s' of context '%s' of host '%s', failed to be deleted from rrdmetrics dictionary.", + string2str(ri->id), + string2str(rc->id), + rrdhost_hostname(host)); + else + internal_error( + true, + "RRDCONTEXT: instance '%s' of context '%s' of host '%s', deleted from rrdmetrics dictionary.", + string2str(ri->id), + string2str(rc->id), + rrdhost_hostname(host)); + } + } + dfe_done(ri); + + if(unlikely(rrdcontext_should_be_deleted(rc))) { + if(worker_jobs) worker_is_busy(WORKER_JOB_CLEANUP_DELETE); + rrdcontext_dequeue_from_post_processing(rc); + rrdcontext_delete_from_sql_unsafe(rc); + + if(!dictionary_del((DICTIONARY *)host->rrdctx, string2str(rc->id))) + error("RRDCONTEXT: context '%s' of host '%s', failed to be deleted from rrdmetrics dictionary.", + string2str(rc->id), + rrdhost_hostname(host)); + else + internal_error( + true, + "RRDCONTEXT: context '%s' of host '%s', deleted from rrdmetrics dictionary.", + string2str(rc->id), + rrdhost_hostname(host)); + + fprintf(stderr, "RRDCONTEXT: deleted context '%s'", string2str(rc->id)); + } + + // the item is referenced in the dictionary + // so, it is still here to unlock, even if we have deleted it + rrdcontext_unlock(rc); + } + dfe_done(rc); +} + +static void rrdcontext_garbage_collect_for_all_hosts(void) { + rrd_rdlock(); + RRDHOST *host; + rrdhost_foreach_read(host) { + rrdcontext_garbage_collect_single_host(host, true); + } + rrd_unlock(); +} + +// ---------------------------------------------------------------------------- +// post processing + +static void rrdmetric_process_updates(RRDMETRIC *rm, bool force, RRD_FLAGS reason, bool worker_jobs) { + if(reason != RRD_FLAG_NONE) + rrd_flag_set_updated(rm, reason); + + if(!force && !rrd_flag_is_updated(rm) && rrd_flag_check(rm, RRD_FLAG_LIVE_RETENTION) && !rrd_flag_check(rm, RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION)) + return; + + if(worker_jobs) + worker_is_busy(WORKER_JOB_PP_METRIC); + + if(reason & RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD) { + rrd_flag_set_archived(rm); + rrd_flag_set(rm, RRD_FLAG_UPDATE_REASON_DISCONNECTED_CHILD); + } + if(rrd_flag_is_deleted(rm) && (reason & RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION)) + rrd_flag_set_archived(rm); + + rrdmetric_update_retention(rm); + + rrd_flag_unset_updated(rm); +} + +static void rrdinstance_post_process_updates(RRDINSTANCE *ri, bool force, RRD_FLAGS reason, bool worker_jobs) { + if(reason != RRD_FLAG_NONE) + rrd_flag_set_updated(ri, reason); + + if(!force && !rrd_flag_is_updated(ri) && rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION)) + return; + + if(worker_jobs) + worker_is_busy(WORKER_JOB_PP_INSTANCE); + + time_t min_first_time_t = LONG_MAX, max_last_time_t = 0; + size_t metrics_active = 0, metrics_deleted = 0; + bool live_retention = true, currently_collected = false; + if(dictionary_entries(ri->rrdmetrics) > 0) { + RRDMETRIC *rm; + dfe_start_read((DICTIONARY *)ri->rrdmetrics, rm) { + if(unlikely(netdata_exit)) break; + + RRD_FLAGS reason_to_pass = reason; + if(rrd_flag_check(ri, RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION)) + reason_to_pass |= RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION; + + rrdmetric_process_updates(rm, force, reason_to_pass, worker_jobs); + + if(unlikely(!rrd_flag_check(rm, RRD_FLAG_LIVE_RETENTION))) + live_retention = false; + + if (unlikely((rrdmetric_should_be_deleted(rm)))) { + metrics_deleted++; + continue; + } + + if(!currently_collected && rrd_flag_check(rm, RRD_FLAG_COLLECTED) && rm->first_time_t) + currently_collected = true; + + metrics_active++; + + if (rm->first_time_t && rm->first_time_t < min_first_time_t) + min_first_time_t = rm->first_time_t; + + if (rm->last_time_t && rm->last_time_t > max_last_time_t) + max_last_time_t = rm->last_time_t; + } + dfe_done(rm); + } + + if(unlikely(live_retention && !rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION))) + rrd_flag_set(ri, RRD_FLAG_LIVE_RETENTION); + else if(unlikely(!live_retention && rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION))) + rrd_flag_clear(ri, RRD_FLAG_LIVE_RETENTION); + + if(unlikely(!metrics_active)) { + // no metrics available + + if(ri->first_time_t) { + ri->first_time_t = 0; + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); + } + + if(ri->last_time_t) { + ri->last_time_t = 0; + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); + } + + rrd_flag_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); + } + else { + // we have active metrics... + + if (unlikely(min_first_time_t == LONG_MAX)) + min_first_time_t = 0; + + if (unlikely(min_first_time_t == 0 || max_last_time_t == 0)) { + if(ri->first_time_t) { + ri->first_time_t = 0; + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); + } + + if(ri->last_time_t) { + ri->last_time_t = 0; + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); + } + + if(likely(live_retention)) + rrd_flag_set_deleted(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); + } + else { + rrd_flag_clear(ri, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); + + if (unlikely(ri->first_time_t != min_first_time_t)) { + ri->first_time_t = min_first_time_t; + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); + } + + if (unlikely(ri->last_time_t != max_last_time_t)) { + ri->last_time_t = max_last_time_t; + rrd_flag_set_updated(ri, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); + } + + if(likely(currently_collected)) + rrd_flag_set_collected(ri); + else + rrd_flag_set_archived(ri); + } + } + + rrd_flag_unset_updated(ri); +} + +static void rrdcontext_post_process_updates(RRDCONTEXT *rc, bool force, RRD_FLAGS reason, bool worker_jobs) { + if(reason != RRD_FLAG_NONE) + rrd_flag_set_updated(rc, reason); + + if(worker_jobs) + worker_is_busy(WORKER_JOB_PP_CONTEXT); + + size_t min_priority = LONG_MAX; + time_t min_first_time_t = LONG_MAX, max_last_time_t = 0; + size_t instances_active = 0, instances_deleted = 0; + bool live_retention = true, currently_collected = false, hidden = true; + if(dictionary_entries(rc->rrdinstances) > 0) { + RRDINSTANCE *ri; + dfe_start_reentrant(rc->rrdinstances, ri) { + if(unlikely(netdata_exit)) break; + + RRD_FLAGS reason_to_pass = reason; + if(rrd_flag_check(rc, RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION)) + reason_to_pass |= RRD_FLAG_UPDATE_REASON_UPDATE_RETENTION; + + rrdinstance_post_process_updates(ri, force, reason_to_pass, worker_jobs); + + if(unlikely(hidden && !rrd_flag_check(ri, RRD_FLAG_HIDDEN))) + hidden = false; + + if(unlikely(live_retention && !rrd_flag_check(ri, RRD_FLAG_LIVE_RETENTION))) + live_retention = false; + + if (unlikely(rrdinstance_should_be_deleted(ri))) { + instances_deleted++; + continue; + } + + if(unlikely(!currently_collected && rrd_flag_is_collected(ri) && ri->first_time_t)) + currently_collected = true; + + internal_error(rc->units != ri->units, + "RRDCONTEXT: '%s' rrdinstance '%s' has different units, context '%s', instance '%s'", + string2str(rc->id), string2str(ri->id), + string2str(rc->units), string2str(ri->units)); + + instances_active++; + + if (ri->priority >= RRDCONTEXT_MINIMUM_ALLOWED_PRIORITY && ri->priority < min_priority) + min_priority = ri->priority; + + if (ri->first_time_t && ri->first_time_t < min_first_time_t) + min_first_time_t = ri->first_time_t; + + if (ri->last_time_t && ri->last_time_t > max_last_time_t) + max_last_time_t = ri->last_time_t; + } + dfe_done(ri); + } + + { + bool previous_hidden = rrd_flag_check(rc, RRD_FLAG_HIDDEN); + if (hidden != previous_hidden) { + if (hidden && !rrd_flag_check(rc, RRD_FLAG_HIDDEN)) + rrd_flag_set(rc, RRD_FLAG_HIDDEN); + else if (!hidden && rrd_flag_check(rc, RRD_FLAG_HIDDEN)) + rrd_flag_clear(rc, RRD_FLAG_HIDDEN); + } + + bool previous_live_retention = rrd_flag_check(rc, RRD_FLAG_LIVE_RETENTION); + if (live_retention != previous_live_retention) { + if (live_retention && !rrd_flag_check(rc, RRD_FLAG_LIVE_RETENTION)) + rrd_flag_set(rc, RRD_FLAG_LIVE_RETENTION); + else if (!live_retention && rrd_flag_check(rc, RRD_FLAG_LIVE_RETENTION)) + rrd_flag_clear(rc, RRD_FLAG_LIVE_RETENTION); + } + } + + rrdcontext_lock(rc); + rc->pp.executions++; + + if(unlikely(!instances_active)) { + // we had some instances, but they are gone now... + + if(rc->first_time_t) { + rc->first_time_t = 0; + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); + } + + if(rc->last_time_t) { + rc->last_time_t = 0; + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); + } + + rrd_flag_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); + } + else { + // we have some active instances... + + if (unlikely(min_first_time_t == LONG_MAX)) + min_first_time_t = 0; + + if (unlikely(min_first_time_t == 0 && max_last_time_t == 0)) { + if(rc->first_time_t) { + rc->first_time_t = 0; + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); + } + + if(rc->last_time_t) { + rc->last_time_t = 0; + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); + } + + rrd_flag_set_deleted(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); + } + else { + rrd_flag_clear(rc, RRD_FLAG_UPDATE_REASON_ZERO_RETENTION); + + if (unlikely(rc->first_time_t != min_first_time_t)) { + rc->first_time_t = min_first_time_t; + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_FIRST_TIME_T); + } + + if (rc->last_time_t != max_last_time_t) { + rc->last_time_t = max_last_time_t; + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_LAST_TIME_T); + } + + if(likely(currently_collected)) + rrd_flag_set_collected(rc); + else + rrd_flag_set_archived(rc); + } + + if (min_priority != LONG_MAX && rc->priority != min_priority) { + rc->priority = min_priority; + rrd_flag_set_updated(rc, RRD_FLAG_UPDATE_REASON_CHANGED_METADATA); + } + } + + if(unlikely(rrd_flag_is_updated(rc) && rc->rrdhost->rrdctx_hub_queue)) { + if(check_if_cloud_version_changed_unsafe(rc, false)) { + rc->version = rrdcontext_get_next_version(rc); + dictionary_set((DICTIONARY *)rc->rrdhost->rrdctx_hub_queue, + string2str(rc->id), rc, sizeof(*rc)); + } + } + + rrd_flag_unset_updated(rc); + rrdcontext_unlock(rc); +} + +static void rrdcontext_queue_for_post_processing(RRDCONTEXT *rc, const char *function __maybe_unused, RRD_FLAGS flags __maybe_unused) { + if(unlikely(!rc->rrdhost->rrdctx_post_processing_queue)) return; + + if(!rrd_flag_check(rc, RRD_FLAG_QUEUED_FOR_PP)) { + dictionary_set((DICTIONARY *)rc->rrdhost->rrdctx_post_processing_queue, + string2str(rc->id), + rc, + sizeof(*rc)); + +#if(defined(NETDATA_INTERNAL_CHECKS) && defined(LOG_POST_PROCESSING_QUEUE_INSERTIONS)) + { + BUFFER *wb_flags = buffer_create(1000); + rrd_flags_to_buffer(flags, wb_flags); + + BUFFER *wb_reasons = buffer_create(1000); + rrd_reasons_to_buffer(flags, wb_reasons); + + internal_error(true, "RRDCONTEXT: '%s' update triggered by function %s(), due to flags: %s, reasons: %s", + string2str(rc->id), function, + buffer_tostring(wb_flags), + buffer_tostring(wb_reasons)); + + buffer_free(wb_reasons); + buffer_free(wb_flags); + } +#endif + } +} + +static void rrdcontext_dequeue_from_post_processing(RRDCONTEXT *rc) { + if(unlikely(!rc->rrdhost->rrdctx_post_processing_queue)) return; + dictionary_del((DICTIONARY *)rc->rrdhost->rrdctx_post_processing_queue, string2str(rc->id)); +} + +static void rrdcontext_post_process_queued_contexts(RRDHOST *host) { + if(unlikely(!host->rrdctx_post_processing_queue)) return; + + RRDCONTEXT *rc; + dfe_start_reentrant((DICTIONARY *)host->rrdctx_post_processing_queue, rc) { + if(unlikely(netdata_exit)) break; + + rrdcontext_dequeue_from_post_processing(rc); + rrdcontext_post_process_updates(rc, false, RRD_FLAG_NONE, true); + } + dfe_done(rc); +} + +// ---------------------------------------------------------------------------- +// dispatching contexts to cloud + +static uint64_t rrdcontext_get_next_version(RRDCONTEXT *rc) { + time_t now = now_realtime_sec(); + uint64_t version = MAX(rc->version, rc->hub.version); + version = MAX((uint64_t)now, version); + version++; + return version; +} + +static void rrdcontext_message_send_unsafe(RRDCONTEXT *rc, bool snapshot __maybe_unused, void *bundle __maybe_unused) { + + // save it, so that we know the last version we sent to hub + rc->version = rc->hub.version = rrdcontext_get_next_version(rc); + rc->hub.id = string2str(rc->id); + rc->hub.title = string2str(rc->title); + rc->hub.units = string2str(rc->units); + rc->hub.family = string2str(rc->family); + rc->hub.chart_type = rrdset_type_name(rc->chart_type); + rc->hub.priority = rc->priority; + rc->hub.first_time_t = rc->first_time_t; + rc->hub.last_time_t = rrd_flag_is_collected(rc) ? 0 : rc->last_time_t; + rc->hub.deleted = rrd_flag_is_deleted(rc) ? true : false; + +#ifdef ENABLE_ACLK + struct context_updated message = { + .id = rc->hub.id, + .version = rc->hub.version, + .title = rc->hub.title, + .units = rc->hub.units, + .family = rc->hub.family, + .chart_type = rc->hub.chart_type, + .priority = rc->hub.priority, + .first_entry = rc->hub.first_time_t, + .last_entry = rc->hub.last_time_t, + .deleted = rc->hub.deleted, + }; - rrdinstance_release(ria); - rrdcontext_release(rca); + if(likely(!rrd_flag_check(rc, RRD_FLAG_HIDDEN))) { + if (snapshot) { + if (!rc->hub.deleted) + contexts_snapshot_add_ctx_update(bundle, &message); + } + else + contexts_updated_add_ctx_update(bundle, &message); + } +#endif + + // store it to SQL + + if(rrd_flag_is_deleted(rc)) + rrdcontext_delete_from_sql_unsafe(rc); + + else if (ctx_store_context(&rc->rrdhost->host_uuid, &rc->hub) != 0) + error("RRDCONTEXT: failed to save context '%s' version %"PRIu64" to SQL.", rc->hub.id, rc->hub.version); } -static void rrdcontext_load_context_callback(VERSIONED_CONTEXT_DATA *ctx_data, void *data) { - RRDHOST *host = data; - (void)host; +static bool check_if_cloud_version_changed_unsafe(RRDCONTEXT *rc, bool sending __maybe_unused) { + bool id_changed = false, + title_changed = false, + units_changed = false, + family_changed = false, + chart_type_changed = false, + priority_changed = false, + first_time_changed = false, + last_time_changed = false, + deleted_changed = false; - RRDCONTEXT trc = { - .id = string_strdupz(ctx_data->id), - .flags = RRD_FLAG_ARCHIVED | RRD_FLAG_DONT_PROCESS | RRD_FLAG_UPDATE_REASON_LOAD_SQL, + RRD_FLAGS flags = rrd_flags_get(rc); - // no need to set more data here - // we only need the hub data + if(unlikely(string2str(rc->id) != rc->hub.id)) + id_changed = true; - .hub = *ctx_data, - }; - dictionary_set((DICTIONARY *)host->rrdctx, string2str(trc.id), &trc, sizeof(trc)); -} + if(unlikely(string2str(rc->title) != rc->hub.title)) + title_changed = true; -void rrdhost_load_rrdcontext_data(RRDHOST *host) { - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - return; + if(unlikely(string2str(rc->units) != rc->hub.units)) + units_changed = true; - if(host->rrdctx) return; + if(unlikely(string2str(rc->family) != rc->hub.family)) + family_changed = true; - rrdhost_create_rrdcontexts(host); - ctx_get_context_list(&host->host_uuid, rrdcontext_load_context_callback, host); - ctx_get_chart_list(&host->host_uuid, rrdinstance_load_chart_callback, host); + if(unlikely(rrdset_type_name(rc->chart_type) != rc->hub.chart_type)) + chart_type_changed = true; - RRDCONTEXT *rc; - dfe_start_read((DICTIONARY *)host->rrdctx, rc) { - rc->flags &= ~RRD_FLAG_DONT_PROCESS; - rrdcontext_trigger_updates(rc, true); + if(unlikely(rc->priority != rc->hub.priority)) + priority_changed = true; + + if(unlikely((uint64_t)rc->first_time_t != rc->hub.first_time_t)) + first_time_changed = true; + + if(unlikely((uint64_t)((flags & RRD_FLAG_COLLECTED) ? 0 : rc->last_time_t) != rc->hub.last_time_t)) + last_time_changed = true; + + if(unlikely(((flags & RRD_FLAG_DELETED) ? true : false) != rc->hub.deleted)) + deleted_changed = true; + + if(unlikely(id_changed || title_changed || units_changed || family_changed || chart_type_changed || priority_changed || first_time_changed || last_time_changed || deleted_changed)) { + + internal_error(LOG_TRANSITIONS, + "RRDCONTEXT: %s NEW VERSION '%s'%s of host '%s', version %"PRIu64", title '%s'%s, units '%s'%s, family '%s'%s, chart type '%s'%s, priority %u%s, first_time_t %ld%s, last_time_t %ld%s, deleted '%s'%s, (queued for %llu ms, expected %llu ms)", + sending?"SENDING":"QUEUE", + string2str(rc->id), id_changed ? " (CHANGED)" : "", + rrdhost_hostname(rc->rrdhost), + rc->version, + string2str(rc->title), title_changed ? " (CHANGED)" : "", + string2str(rc->units), units_changed ? " (CHANGED)" : "", + string2str(rc->family), family_changed ? " (CHANGED)" : "", + rrdset_type_name(rc->chart_type), chart_type_changed ? " (CHANGED)" : "", + rc->priority, priority_changed ? " (CHANGED)" : "", + rc->first_time_t, first_time_changed ? " (CHANGED)" : "", + (flags & RRD_FLAG_COLLECTED) ? 0 : rc->last_time_t, last_time_changed ? " (CHANGED)" : "", + (flags & RRD_FLAG_DELETED) ? "true" : "false", deleted_changed ? " (CHANGED)" : "", + sending ? (now_realtime_usec() - rc->queue.queued_ut) / USEC_PER_MS : 0, + sending ? (rc->queue.scheduled_dispatch_ut - rc->queue.queued_ut) / USEC_PER_MS : 0 + ); + + return true; } - dfe_done(rc); -} -// ---------------------------------------------------------------------------- -// the worker thread + return false; +} static inline usec_t rrdcontext_calculate_queued_dispatch_time_ut(RRDCONTEXT *rc, usec_t now_ut) { @@ -2581,204 +3752,145 @@ static inline usec_t rrdcontext_calculate_queued_dispatch_time_ut(RRDCONTEXT *rc return dispatch_ut; } -#define WORKER_JOB_HOSTS 1 -#define WORKER_JOB_CHECK 2 -#define WORKER_JOB_SEND 3 -#define WORKER_JOB_DEQUEUE 4 -#define WORKER_JOB_RETENTION 5 -#define WORKER_JOB_QUEUED 6 -#define WORKER_JOB_CLEANUP 7 -#define WORKER_JOB_CLEANUP_DELETE 8 - -static usec_t rrdcontext_next_db_rotation_ut = 0; -void rrdcontext_db_rotation(void) { - // called when the db rotates its database - rrdcontext_next_db_rotation_ut = now_realtime_usec() + FULL_RETENTION_SCAN_DELAY_AFTER_DB_ROTATION_SECS * USEC_PER_SEC; +static void rrdcontext_dequeue_from_hub_queue(RRDCONTEXT *rc) { + dictionary_del((DICTIONARY *)rc->rrdhost->rrdctx_hub_queue, string2str(rc->id)); } -static uint64_t rrdcontext_version_hash_with_callback( - RRDHOST *host, - void (*callback)(RRDCONTEXT *, bool, void *), - bool snapshot, - void *bundle) { - - if(unlikely(!host || !host->rrdctx)) return 0; - - RRDCONTEXT *rc; - uint64_t hash = 0; +static void rrdcontext_dispatch_queued_contexts_to_hub(RRDHOST *host, usec_t now_ut) { - // loop through all contexts of the host - dfe_start_read((DICTIONARY *)host->rrdctx, rc) { + // check if we have received a streaming command for this host + if(!rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS) || !aclk_connected || !host->rrdctx_hub_queue) + return; - rrdcontext_lock(rc); + // check if there are queued items to send + if(!dictionary_entries((DICTIONARY *)host->rrdctx_hub_queue)) + return; - if(unlikely(rc->flags & RRD_FLAG_HIDDEN)) { - rrdcontext_unlock(rc); - continue; - } + if(!host->node_id) + return; - if(unlikely(callback)) - callback(rc, snapshot, bundle); + size_t messages_added = 0; + contexts_updated_t bundle = NULL; - // skip any deleted contexts - if(unlikely(rc->flags & RRD_FLAG_DELETED)) { - rrdcontext_unlock(rc); - continue; - } + RRDCONTEXT *rc; + dfe_start_reentrant((DICTIONARY *)host->rrdctx_hub_queue, rc) { + if(unlikely(netdata_exit)) break; - // we use rc->hub.* which has the latest - // metadata we have sent to the hub + if(unlikely(messages_added >= MESSAGES_PER_BUNDLE_TO_SEND_TO_HUB_PER_HOST)) + break; - // if a context is currently queued, rc->hub.* does NOT - // reflect the queued changes. rc->hub.* is updated with - // their metadata, after messages are dispatched to hub. + worker_is_busy(WORKER_JOB_QUEUED); + usec_t dispatch_ut = rrdcontext_calculate_queued_dispatch_time_ut(rc, now_ut); + char *claim_id = get_agent_claimid(); - // when the context is being collected, - // rc->hub.last_time_t is already zero + if(unlikely(now_ut >= dispatch_ut) && claim_id) { + worker_is_busy(WORKER_JOB_CHECK); - hash += rc->hub.version + rc->hub.last_time_t - rc->hub.first_time_t; + rrdcontext_lock(rc); - rrdcontext_unlock(rc); + if(check_if_cloud_version_changed_unsafe(rc, true)) { + worker_is_busy(WORKER_JOB_SEND); - } - dfe_done(rc); +#ifdef ENABLE_ACLK + if(!bundle) { + // prepare the bundle to send the messages + char uuid[UUID_STR_LEN]; + uuid_unparse_lower(*host->node_id, uuid); - return hash; -} + bundle = contexts_updated_new(claim_id, uuid, 0, now_ut); + } +#endif + // update the hub data of the context, give a new version, pack the message + // and save an update to SQL + rrdcontext_message_send_unsafe(rc, false, bundle); + messages_added++; -static void rrdcontext_recalculate_context_retention(RRDCONTEXT *rc, RRD_FLAGS reason, int job_id) { - RRDINSTANCE *ri; - dfe_start_read(rc->rrdinstances, ri) { - RRDMETRIC *rm; - dfe_start_read(ri->rrdmetrics, rm) { + rc->queue.dispatches++; + rc->queue.dequeued_ut = now_ut; + } + else + rc->version = rc->hub.version; - if(job_id >= 0) - worker_is_busy(job_id); + // remove it from the queue + worker_is_busy(WORKER_JOB_DEQUEUE); + rrdcontext_dequeue_from_hub_queue(rc); - rrd_flag_set_updated(rm, reason); + if(unlikely(rrdcontext_should_be_deleted(rc))) { + // this is a deleted context - delete it forever... - rm->flags &= ~RRD_FLAG_DONT_PROCESS; - rrdmetric_trigger_updates(rm, true, false); - } - dfe_done(rm); + worker_is_busy(WORKER_JOB_CLEANUP_DELETE); - ri->flags &= ~RRD_FLAG_DONT_PROCESS; - rrdinstance_trigger_updates(ri, true, false); - ri->flags |= RRD_FLAG_DONT_PROCESS; - } - dfe_done(ri); + rrdcontext_dequeue_from_post_processing(rc); + rrdcontext_delete_from_sql_unsafe(rc); - rc->flags &= ~RRD_FLAG_DONT_PROCESS; - rrdcontext_trigger_updates(rc, true); -} + STRING *id = string_dup(rc->id); + rrdcontext_unlock(rc); -static void rrdcontext_recalculate_host_retention(RRDHOST *host, RRD_FLAGS reason, int job_id) { - if(unlikely(!host || !host->rrdctx)) return; + // delete it from the master dictionary + if(!dictionary_del((DICTIONARY *)host->rrdctx, string2str(rc->id))) + error("RRDCONTEXT: '%s' of host '%s' failed to be deleted from rrdcontext dictionary.", + string2str(id), rrdhost_hostname(host)); - RRDCONTEXT *rc; - dfe_start_read((DICTIONARY *)host->rrdctx, rc) { - rrdcontext_recalculate_context_retention(rc, reason, job_id); + string_freez(id); + } + else + rrdcontext_unlock(rc); + } + freez(claim_id); } dfe_done(rc); -} - -static void rrdcontext_recalculate_retention(int job_id) { - rrdcontext_next_db_rotation_ut = 0; - rrd_rdlock(); - RRDHOST *host; - rrdhost_foreach_read(host) { - rrdcontext_recalculate_host_retention(host, RRD_FLAG_UPDATE_REASON_DB_ROTATION, job_id); - } - rrd_unlock(); -} - -void rrdcontext_delete_from_sql_unsafe(RRDCONTEXT *rc) { - // we need to refresh the string pointers in rc->hub - // in case the context changed values - rc->hub.id = string2str(rc->id); - rc->hub.title = string2str(rc->title); - rc->hub.units = string2str(rc->units); - rc->hub.family = string2str(rc->family); - - // delete it from SQL - if(ctx_delete_context(&rc->rrdhost->host_uuid, &rc->hub) != 0) - error("RRDCONTEXT: failed to delete context '%s' version %"PRIu64" from SQL.", rc->hub.id, rc->hub.version); -} - -static void rrdcontext_garbage_collect(void) { - rrd_rdlock(); - RRDHOST *host; - rrdhost_foreach_read(host) { - RRDCONTEXT *rc; - dfe_start_write((DICTIONARY *)host->rrdctx, rc) { - worker_is_busy(WORKER_JOB_CLEANUP); - - rrdcontext_lock(rc); - if(unlikely(rrdcontext_should_be_deleted(rc))) { - worker_is_busy(WORKER_JOB_CLEANUP_DELETE); - rrdcontext_delete_from_sql_unsafe(rc); +#ifdef ENABLE_ACLK + if(!netdata_exit && bundle) { + // we have a bundle to send messages - if(dictionary_del_having_write_lock((DICTIONARY *)host->rrdctx, string2str(rc->id)) != 0) - error("RRDCONTEXT: '%s' of host '%s' failed to be deleted from rrdcontext dictionary.", - string2str(rc->id), host->hostname); - } - else { - RRDINSTANCE *ri; - dfe_start_write(rc->rrdinstances, ri) { - if(rrdinstance_should_be_deleted(ri)) { - worker_is_busy(WORKER_JOB_CLEANUP_DELETE); - dictionary_del_having_write_lock(rc->rrdinstances, string2str(ri->id)); - } - else { - RRDMETRIC *rm; - dfe_start_write(ri->rrdmetrics, rm) { - if(rrdmetric_should_be_deleted(rm)) { - worker_is_busy(WORKER_JOB_CLEANUP_DELETE); - dictionary_del_having_write_lock(ri->rrdmetrics, string2str(rm->id)); - } - } - dfe_done(rm); - } - } - dfe_done(ri); - } + // update the version hash + contexts_updated_update_version_hash(bundle, rrdcontext_version_hash(host)); - // the item is referenced in the dictionary - // so, it is still here to unlock, even if we have deleted it - rrdcontext_unlock(rc); - } - dfe_done(rc); + // send it + aclk_send_contexts_updated(bundle); } - rrd_unlock(); + else if(bundle) + contexts_updated_delete(bundle); +#endif + } +// ---------------------------------------------------------------------------- +// worker thread + static void rrdcontext_main_cleanup(void *ptr) { struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; static_thread->enabled = NETDATA_MAIN_THREAD_EXITING; + // custom code + worker_unregister(); + static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; } void *rrdcontext_main(void *ptr) { netdata_thread_cleanup_push(rrdcontext_main_cleanup, ptr); - if(unlikely(rrdcontext_enabled == CONFIG_BOOLEAN_NO)) - goto exit; - worker_register("RRDCONTEXT"); worker_register_job_name(WORKER_JOB_HOSTS, "hosts"); worker_register_job_name(WORKER_JOB_CHECK, "dedup checks"); worker_register_job_name(WORKER_JOB_SEND, "sent contexts"); - worker_register_job_name(WORKER_JOB_DEQUEUE, "deduped contexts"); + worker_register_job_name(WORKER_JOB_DEQUEUE, "deduplicated contexts"); worker_register_job_name(WORKER_JOB_RETENTION, "metrics retention"); worker_register_job_name(WORKER_JOB_QUEUED, "queued contexts"); worker_register_job_name(WORKER_JOB_CLEANUP, "cleanups"); worker_register_job_name(WORKER_JOB_CLEANUP_DELETE, "deletes"); + worker_register_job_name(WORKER_JOB_PP_METRIC, "check metrics"); + worker_register_job_name(WORKER_JOB_PP_INSTANCE, "check instances"); + worker_register_job_name(WORKER_JOB_PP_CONTEXT, "check contexts"); + + worker_register_job_custom_metric(WORKER_JOB_HUB_QUEUE_SIZE, "hub queue size", "contexts", WORKER_METRIC_ABSOLUTE); + worker_register_job_custom_metric(WORKER_JOB_PP_QUEUE_SIZE, "post processing queue size", "contexts", WORKER_METRIC_ABSOLUTE); heartbeat_t hb; heartbeat_init(&hb); - usec_t step = USEC_PER_SEC * RRDCONTEXT_WORKER_THREAD_HEARTBEAT_SECS; + usec_t step = RRDCONTEXT_WORKER_THREAD_HEARTBEAT_USEC; while (!netdata_exit) { worker_is_idle(); @@ -2786,16 +3898,17 @@ void *rrdcontext_main(void *ptr) { if(unlikely(netdata_exit)) break; - if(!aclk_connected) continue; - usec_t now_ut = now_realtime_usec(); if(rrdcontext_next_db_rotation_ut && now_ut > rrdcontext_next_db_rotation_ut) { - rrdcontext_recalculate_retention(WORKER_JOB_RETENTION); - rrdcontext_garbage_collect(); + rrdcontext_recalculate_retention_all_hosts(); + rrdcontext_garbage_collect_for_all_hosts(); rrdcontext_next_db_rotation_ut = 0; } + size_t hub_queued_contexts_for_all_hosts = 0; + size_t pp_queued_contexts_for_all_hosts = 0; + rrd_rdlock(); RRDHOST *host; rrdhost_foreach_read(host) { @@ -2803,106 +3916,23 @@ void *rrdcontext_main(void *ptr) { worker_is_busy(WORKER_JOB_HOSTS); - // check if we have received a streaming command for this host - if(!rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS)) - continue; - - // check if there are queued items to send - if(!dictionary_stats_entries((DICTIONARY *)host->rrdctx_queue)) - continue; - - if(!host->node_id) - continue; - - size_t messages_added = 0; - contexts_updated_t bundle = NULL; - - RRDCONTEXT *rc; - dfe_start_write((DICTIONARY *)host->rrdctx_queue, rc) { - if(unlikely(netdata_exit)) break; - - if(unlikely(messages_added >= MESSAGES_PER_BUNDLE_TO_SEND_TO_HUB_PER_HOST)) - break; - - worker_is_busy(WORKER_JOB_QUEUED); - usec_t dispatch_ut = rrdcontext_calculate_queued_dispatch_time_ut(rc, now_ut); - char *claim_id = get_agent_claimid(); - if(unlikely(now_ut >= dispatch_ut) && claim_id) { - worker_is_busy(WORKER_JOB_CHECK); - - rrdcontext_lock(rc); - - if(check_if_cloud_version_changed_unsafe(rc, true)) { - worker_is_busy(WORKER_JOB_SEND); - -#ifdef ENABLE_ACLK - if(!bundle) { - // prepare the bundle to send the messages - char uuid[UUID_STR_LEN]; - uuid_unparse_lower(*host->node_id, uuid); - - bundle = contexts_updated_new(claim_id, uuid, 0, now_ut); - } -#endif - // update the hub data of the context, give a new version, pack the message - // and save an update to SQL - rrdcontext_message_send_unsafe(rc, false, bundle); - messages_added++; - - rc->queue.dequeued_ut = now_ut; - } - else - rc->version = rc->hub.version; - - // remove the queued flag, so that it can be queued again - rc->flags &= ~RRD_FLAG_QUEUED; - - // remove it from the queue - worker_is_busy(WORKER_JOB_DEQUEUE); - dictionary_del_having_write_lock((DICTIONARY *)host->rrdctx_queue, string2str(rc->id)); - - if(unlikely(rrdcontext_should_be_deleted(rc))) { - // this is a deleted context - delete it forever... - - worker_is_busy(WORKER_JOB_CLEANUP_DELETE); - rrdcontext_delete_from_sql_unsafe(rc); - - STRING *id = string_dup(rc->id); - rrdcontext_unlock(rc); - - // delete it from the master dictionary - if(dictionary_del((DICTIONARY *)host->rrdctx, string2str(rc->id)) != 0) - error("RRDCONTEXT: '%s' of host '%s' failed to be deleted from rrdcontext dictionary.", - string2str(id), host->hostname); - - string_freez(id); - } - else - rrdcontext_unlock(rc); - } - freez(claim_id); + if(host->rrdctx_post_processing_queue) { + pp_queued_contexts_for_all_hosts += + dictionary_entries((DICTIONARY *)host->rrdctx_post_processing_queue); + rrdcontext_post_process_queued_contexts(host); } - dfe_done(rc); -#ifdef ENABLE_ACLK - if(!netdata_exit && bundle) { - // we have a bundle to send messages - - // update the version hash - contexts_updated_update_version_hash(bundle, rrdcontext_version_hash(host)); - - // send it - aclk_send_contexts_updated(bundle); + if(host->rrdctx_hub_queue) { + hub_queued_contexts_for_all_hosts += dictionary_entries((DICTIONARY *)host->rrdctx_hub_queue); + rrdcontext_dispatch_queued_contexts_to_hub(host, now_ut); } - else if(bundle) - contexts_updated_delete(bundle); -#endif } rrd_unlock(); + worker_set_metric(WORKER_JOB_HUB_QUEUE_SIZE, (NETDATA_DOUBLE)hub_queued_contexts_for_all_hosts); + worker_set_metric(WORKER_JOB_PP_QUEUE_SIZE, (NETDATA_DOUBLE)pp_queued_contexts_for_all_hosts); } -exit: netdata_thread_cleanup_pop(1); return NULL; } diff --git a/database/rrdcontext.h b/database/rrdcontext.h index a9e0bd2e3..67e6cf394 100644 --- a/database/rrdcontext.h +++ b/database/rrdcontext.h @@ -8,7 +8,6 @@ typedef struct rrdmetric_acquired RRDMETRIC_ACQUIRED; - // ---------------------------------------------------------------------------- // RRDINSTANCE @@ -24,15 +23,26 @@ typedef struct rrdcontext_acquired RRDCONTEXT_ACQUIRED; #include "rrd.h" +const char *rrdmetric_acquired_id(RRDMETRIC_ACQUIRED *rma); +const char *rrdmetric_acquired_name(RRDMETRIC_ACQUIRED *rma); +NETDATA_DOUBLE rrdmetric_acquired_last_stored_value(RRDMETRIC_ACQUIRED *rma); + +const char *rrdinstance_acquired_id(RRDINSTANCE_ACQUIRED *ria); +const char *rrdinstance_acquired_name(RRDINSTANCE_ACQUIRED *ria); +DICTIONARY *rrdinstance_acquired_labels(RRDINSTANCE_ACQUIRED *ria); +DICTIONARY *rrdinstance_acquired_functions(RRDINSTANCE_ACQUIRED *ria); + // ---------------------------------------------------------------------------- // public API for rrdhost -extern void rrdhost_load_rrdcontext_data(RRDHOST *host); -extern void rrdhost_create_rrdcontexts(RRDHOST *host); -extern void rrdhost_destroy_rrdcontexts(RRDHOST *host); +void rrdhost_load_rrdcontext_data(RRDHOST *host); +void rrdhost_create_rrdcontexts(RRDHOST *host); +void rrdhost_destroy_rrdcontexts(RRDHOST *host); -extern void rrdcontext_host_child_connected(RRDHOST *host); -extern void rrdcontext_host_child_disconnected(RRDHOST *host); +void rrdcontext_host_child_connected(RRDHOST *host); +void rrdcontext_host_child_disconnected(RRDHOST *host); + +int rrdcontext_foreach_instance_with_rrdset_in_context(RRDHOST *host, const char *context, int (*callback)(RRDSET *st, void *data), void *data); typedef enum { RRDCONTEXT_OPTION_NONE = 0, @@ -50,43 +60,191 @@ typedef enum { #define RRDCONTEXT_OPTIONS_ALL (RRDCONTEXT_OPTION_SHOW_METRICS|RRDCONTEXT_OPTION_SHOW_INSTANCES|RRDCONTEXT_OPTION_SHOW_LABELS|RRDCONTEXT_OPTION_SHOW_QUEUED|RRDCONTEXT_OPTION_SHOW_FLAGS|RRDCONTEXT_OPTION_SHOW_DELETED|RRDCONTEXT_OPTION_SHOW_UUIDS|RRDCONTEXT_OPTION_SHOW_HIDDEN) -extern int rrdcontext_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, const char *context, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions); -extern int rrdcontexts_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions); +int rrdcontext_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, const char *context, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions); +int rrdcontexts_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions); + +// ---------------------------------------------------------------------------- +// public API for rrdcontexts + +const char *rrdcontext_acquired_id(RRDCONTEXT_ACQUIRED *rca); // ---------------------------------------------------------------------------- // public API for rrddims -extern void rrdcontext_updated_rrddim(RRDDIM *rd); -extern void rrdcontext_removed_rrddim(RRDDIM *rd); -extern void rrdcontext_updated_rrddim_algorithm(RRDDIM *rd); -extern void rrdcontext_updated_rrddim_multiplier(RRDDIM *rd); -extern void rrdcontext_updated_rrddim_divisor(RRDDIM *rd); -extern void rrdcontext_updated_rrddim_flags(RRDDIM *rd); -extern void rrdcontext_collected_rrddim(RRDDIM *rd); +void rrdcontext_updated_rrddim(RRDDIM *rd); +void rrdcontext_removed_rrddim(RRDDIM *rd); +void rrdcontext_updated_rrddim_algorithm(RRDDIM *rd); +void rrdcontext_updated_rrddim_multiplier(RRDDIM *rd); +void rrdcontext_updated_rrddim_divisor(RRDDIM *rd); +void rrdcontext_updated_rrddim_flags(RRDDIM *rd); +void rrdcontext_collected_rrddim(RRDDIM *rd); +int rrdcontext_find_dimension_uuid(RRDSET *st, const char *id, uuid_t *store_uuid); // ---------------------------------------------------------------------------- // public API for rrdsets -extern void rrdcontext_updated_rrdset(RRDSET *st); -extern void rrdcontext_removed_rrdset(RRDSET *st); -extern void rrdcontext_updated_rrdset_name(RRDSET *st); -extern void rrdcontext_updated_rrdset_flags(RRDSET *st); -extern void rrdcontext_collected_rrdset(RRDSET *st); +void rrdcontext_updated_rrdset(RRDSET *st); +void rrdcontext_removed_rrdset(RRDSET *st); +void rrdcontext_updated_rrdset_name(RRDSET *st); +void rrdcontext_updated_rrdset_flags(RRDSET *st); +void rrdcontext_updated_retention_rrdset(RRDSET *st); +void rrdcontext_collected_rrdset(RRDSET *st); +int rrdcontext_find_chart_uuid(RRDSET *st, uuid_t *store_uuid); // ---------------------------------------------------------------------------- // public API for ACLK -extern void rrdcontext_hub_checkpoint_command(void *cmd); -extern void rrdcontext_hub_stop_streaming_command(void *cmd); +void rrdcontext_hub_checkpoint_command(void *cmd); +void rrdcontext_hub_stop_streaming_command(void *cmd); // ---------------------------------------------------------------------------- // public API for threads -extern int rrdcontext_enabled; +void rrdcontext_db_rotation(void); +void *rrdcontext_main(void *); + +// ---------------------------------------------------------------------------- +// public API for weights + +struct metric_entry { + RRDCONTEXT_ACQUIRED *rca; + RRDINSTANCE_ACQUIRED *ria; + RRDMETRIC_ACQUIRED *rma; +}; + +DICTIONARY *rrdcontext_all_metrics_to_dict(RRDHOST *host, SIMPLE_PATTERN *contexts); -extern void rrdcontext_db_rotation(void); -extern void *rrdcontext_main(void *); +// ---------------------------------------------------------------------------- +// public API for queries + +typedef struct query_metric { + struct query_metric_tier { + struct storage_engine *eng; + STORAGE_METRIC_HANDLE *db_metric_handle; + time_t db_first_time_t; // the oldest timestamp available for this tier + time_t db_last_time_t; // the latest timestamp available for this tier + time_t db_update_every; // latest update every for this tier + } tiers[RRD_STORAGE_TIERS]; + + struct { + RRDHOST *host; + RRDCONTEXT_ACQUIRED *rca; + RRDINSTANCE_ACQUIRED *ria; + RRDMETRIC_ACQUIRED *rma; + } link; + + struct { + STRING *id; + STRING *name; + RRDR_DIMENSION_FLAGS options; + } dimension; + + struct { + STRING *id; + STRING *name; + } chart; + +} QUERY_METRIC; + +#define MAX_QUERY_TARGET_ID_LENGTH 255 + +typedef struct query_target_request { + RRDHOST *host; // the host to be queried (can be NULL, hosts will be used) + RRDCONTEXT_ACQUIRED *rca; // the context to be queried (can be NULL) + RRDINSTANCE_ACQUIRED *ria; // the instance to be queried (can be NULL) + RRDMETRIC_ACQUIRED *rma; // the metric to be queried (can be NULL) + RRDSET *st; // the chart to be queried (NULL, for context queries) + const char *hosts; // hosts simple pattern + const char *contexts; // contexts simple pattern (context queries) + const char *charts; // charts simple pattern (for context queries) + const char *dimensions; // dimensions simple pattern + const char *chart_label_key; // select only the chart having this label key + const char *charts_labels_filter; // select only the charts having this combo of label key:value + time_t after; // the requested timeframe + time_t before; // the requested timeframe + size_t points; // the requested number of points + time_t timeout; // the timeout of the query in seconds + uint32_t format; // DATASOURCE_FORMAT + RRDR_OPTIONS options; + RRDR_GROUPING group_method; + const char *group_options; + time_t resampling_time; + size_t tier; + QUERY_SOURCE query_source; +} QUERY_TARGET_REQUEST; + +typedef struct query_target { + char id[MAX_QUERY_TARGET_ID_LENGTH + 1]; // query identifier (for logging) + QUERY_TARGET_REQUEST request; + + bool used; // when true, this query is currently being used + size_t queries; // how many query we have done so far + + struct { + bool relative; // true when the request made with relative timestamps, true if it was absolute + bool aligned; + time_t after; // the absolute timestamp this query is about + time_t before; // the absolute timestamp this query is about + time_t query_granularity; + size_t points; // the number of points the query will return (maybe different from the request) + size_t group; + RRDR_GROUPING group_method; + const char *group_options; + size_t resampling_group; + NETDATA_DOUBLE resampling_divisor; + RRDR_OPTIONS options; + size_t tier; + } window; + + struct { + time_t first_time_t; // the combined first_time_t of all metrics in the query, across all tiers + time_t last_time_t; // the combined last_time_T of all metrics in the query, across all tiers + time_t minimum_latest_update_every; // the min update every of the metrics in the query + } db; + + struct { + QUERY_METRIC *array; // the metrics to be queried (all of them should be queried, no exceptions) + uint32_t used; // how many items of the array are used + uint32_t size; // the size of the array + SIMPLE_PATTERN *pattern; + } query; + + struct { + RRDMETRIC_ACQUIRED **array; + uint32_t used; // how many items of the array are used + uint32_t size; // the size of the array + } metrics; + + struct { + RRDINSTANCE_ACQUIRED **array; + uint32_t used; // how many items of the array are used + uint32_t size; // the size of the array + SIMPLE_PATTERN *pattern; + SIMPLE_PATTERN *chart_label_key_pattern; + SIMPLE_PATTERN *charts_labels_filter_pattern; + } instances; + + struct { + RRDCONTEXT_ACQUIRED **array; + uint32_t used; // how many items of the array are used + uint32_t size; // the size of the array + SIMPLE_PATTERN *pattern; + } contexts; + + struct { + RRDHOST **array; + uint32_t used; // how many items of the array are used + uint32_t size; // the size of the array + SIMPLE_PATTERN *pattern; + } hosts; + +} QUERY_TARGET; + +void query_target_free(void); +void query_target_release(QUERY_TARGET *qt); + +QUERY_TARGET *query_target_create(QUERY_TARGET_REQUEST *qtr); #endif // NETDATA_RRDCONTEXT_H diff --git a/database/rrddim.c b/database/rrddim.c index 90165a253..1b3d9952c 100644 --- a/database/rrddim.c +++ b/database/rrddim.c @@ -2,281 +2,117 @@ #define NETDATA_RRD_INTERNALS #include "rrd.h" -#ifdef ENABLE_DBENGINE -#include "database/engine/rrdengineapi.h" -#endif #include "storage_engine.h" // ---------------------------------------------------------------------------- // RRDDIM index -int rrddim_compare(void* a, void* b) { - if(((RRDDIM *)a)->hash < ((RRDDIM *)b)->hash) return -1; - else if(((RRDDIM *)a)->hash > ((RRDDIM *)b)->hash) return 1; - else return strcmp(((RRDDIM *)a)->id, ((RRDDIM *)b)->id); -} - -#define rrddim_index_add(st, rd) (RRDDIM *)avl_insert_lock(&((st)->dimensions_index), (avl_t *)(rd)) -#define rrddim_index_del(st,rd ) (RRDDIM *)avl_remove_lock(&((st)->dimensions_index), (avl_t *)(rd)) - -static inline RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) { - RRDDIM tmp = { - .id = id, - .hash = (hash)?hash:simple_hash(id) - }; - return (RRDDIM *)avl_search_lock(&(st->dimensions_index), (avl_t *) &tmp); -} - - -// ---------------------------------------------------------------------------- -// RRDDIM - find a dimension - -inline RRDDIM *rrddim_find(RRDSET *st, const char *id) { - debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", st->name, id); - - return rrddim_index_find(st, id, 0); -} - - -// ---------------------------------------------------------------------------- -// RRDDIM rename a dimension - -inline int rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name) { - if(unlikely(!name || !*name || (rd->name && !strcmp(rd->name, name)))) - return 0; - - debug(D_RRD_CALLS, "rrddim_set_name() from %s.%s to %s.%s", st->name, rd->name, st->name, name); - - if (rd->name) - freez((void *) rd->name); - - rd->name = strdupz(name); - rd->hash_name = simple_hash(rd->name); - - if (!st->state->is_ar_chart) - rrddimvar_rename_all(rd); - - rd->exposed = 0; - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); - - ml_dimension_update_name(st, rd, name); - - return 1; -} - -inline int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm) { - if(unlikely(rd->algorithm == algorithm)) - return 0; - - debug(D_RRD_CALLS, "Updating algorithm of dimension '%s/%s' from %s to %s", st->id, rd->name, rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(algorithm)); - rd->algorithm = algorithm; - rd->exposed = 0; - rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); - rrdcontext_updated_rrddim_algorithm(rd); - return 1; -} - -inline int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, collected_number multiplier) { - if(unlikely(rd->multiplier == multiplier)) - return 0; +struct rrddim_constructor { + RRDSET *st; + const char *id; + const char *name; + collected_number multiplier; + collected_number divisor; + RRD_ALGORITHM algorithm; + RRD_MEMORY_MODE memory_mode; + + enum { + RRDDIM_REACT_NONE = 0, + RRDDIM_REACT_NEW = (1 << 0), + RRDDIM_REACT_UPDATED = (1 << 2), + } react_action; - debug(D_RRD_CALLS, "Updating multiplier of dimension '%s/%s' from " COLLECTED_NUMBER_FORMAT " to " COLLECTED_NUMBER_FORMAT, st->id, rd->name, rd->multiplier, multiplier); - rd->multiplier = multiplier; - rd->exposed = 0; - rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); - rrdcontext_updated_rrddim_multiplier(rd); - return 1; -} - -inline int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, collected_number divisor) { - if(unlikely(rd->divisor == divisor)) - return 0; - - debug(D_RRD_CALLS, "Updating divisor of dimension '%s/%s' from " COLLECTED_NUMBER_FORMAT " to " COLLECTED_NUMBER_FORMAT, st->id, rd->name, rd->divisor, divisor); - rd->divisor = divisor; - rd->exposed = 0; - rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); - rrdcontext_updated_rrddim_divisor(rd); - return 1; -} - -// ---------------------------------------------------------------------------- -// RRDDIM create a dimension - -void rrdcalc_link_to_rrddim(RRDDIM *rd, RRDSET *st, RRDHOST *host) { - RRDCALC *rrdc; - - for (rrdc = host->alarms_with_foreach; rrdc ; rrdc = rrdc->next) { - if (simple_pattern_matches(rrdc->spdim, rd->id) || simple_pattern_matches(rrdc->spdim, rd->name)) { - if (rrdc->hash_chart == st->hash_name || !strcmp(rrdc->chart, st->name) || !strcmp(rrdc->chart, st->id)) { - char *name = alarm_name_with_dim(rrdc->name, strlen(rrdc->name), rd->name, strlen(rd->name)); - if(rrdcalc_exists(host, st->name, name, 0, 0)) { - freez(name); - continue; - } - - netdata_rwlock_wrlock(&host->health_log.alarm_log_rwlock); - RRDCALC *child = rrdcalc_create_from_rrdcalc(rrdc, host, name, rd->name); - netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); - - if (child) { - rrdcalc_add_to_host(host, child); - RRDCALC *rdcmp = (RRDCALC *) avl_insert_lock(&(host)->alarms_idx_health_log,(avl_t *)child); - if (rdcmp != child) { - error("Cannot insert the alarm index ID %s",child->name); - } - } - else { - error("Cannot allocate a new alarm."); - rrdc->foreachcounter--; - } - } - } - } -} - -// Return either -// 0 : Dimension is live -// last collected time : Dimension is not live +}; -#ifdef ENABLE_ACLK -time_t calc_dimension_liveness(RRDDIM *rd, time_t now) -{ - time_t last_updated = rd->last_collected_time.tv_sec; - int live; - if (rd->aclk_live_status == 1) - live = - ((now - last_updated) < - MIN(rrdset_free_obsolete_time, RRDSET_MINIMUM_DIM_OFFLINE_MULTIPLIER * rd->update_every)); - else - live = ((now - last_updated) < RRDSET_MINIMUM_DIM_LIVE_MULTIPLIER * rd->update_every); - return live ? 0 : last_updated; +// isolated call to appear +// separate in statistics +static void *rrddim_alloc_db(size_t entries) { + return callocz(entries, sizeof(storage_number)); } -#endif -RRDDIM *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collected_number multiplier, - collected_number divisor, RRD_ALGORITHM algorithm, RRD_MEMORY_MODE memory_mode) -{ +static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *constructor_data) { + struct rrddim_constructor *ctr = constructor_data; + RRDDIM *rd = rrddim; + RRDSET *st = ctr->st; RRDHOST *host = st->rrdhost; - rrdset_wrlock(st); - RRDDIM *rd = rrddim_find(st, id); - if(unlikely(rd)) { - debug(D_RRD_CALLS, "Cannot create rrd dimension '%s/%s', it already exists.", st->id, name?name:""); - - int rc = rrddim_set_name(st, rd, name); - rc += rrddim_set_algorithm(st, rd, algorithm); - rc += rrddim_set_multiplier(st, rd, multiplier); - rc += rrddim_set_divisor(st, rd, divisor); - - if (rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) { - store_active_dimension(&rd->metric_uuid); - - for(int tier = 0; tier < storage_tiers ;tier++) { - if (rd->tiers[tier]) - rd->tiers[tier]->db_collection_handle = - rd->tiers[tier]->collect_ops.init(rd->tiers[tier]->db_metric_handle); - } - - rrddim_flag_clear(rd, RRDDIM_FLAG_ARCHIVED); - rrddimvar_create(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->last_stored_value, RRDVAR_OPTION_DEFAULT); - rrddimvar_create(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->last_collected_value, RRDVAR_OPTION_DEFAULT); - rrddimvar_create(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->last_collected_time.tv_sec, RRDVAR_OPTION_DEFAULT); + rd->flags = RRDDIM_FLAG_NONE; - rrddim_flag_set(rd, RRDDIM_FLAG_PENDING_FOREACH_ALARM); - rrdset_flag_set(st, RRDSET_FLAG_PENDING_FOREACH_ALARMS); - rrdhost_flag_set(host, RRDHOST_FLAG_PENDING_FOREACH_ALARMS); - } + rd->id = string_strdupz(ctr->id); + rd->name = (ctr->name && *ctr->name)?rrd_string_strdupz(ctr->name):string_dup(rd->id); - if (unlikely(rc)) { - debug(D_METADATALOG, "DIMENSION [%s] metadata updated", rd->id); - (void)sql_store_dimension(&rd->metric_uuid, rd->rrdset->chart_uuid, rd->id, rd->name, rd->multiplier, rd->divisor, - rd->algorithm); -#ifdef ENABLE_ACLK - queue_dimension_to_aclk(rd, calc_dimension_liveness(rd, now_realtime_sec())); -#endif - rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); - } - rrdset_unlock(st); - rrdcontext_updated_rrddim(rd); - return rd; - } - - rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); - - rd = callocz(1, sizeof(RRDDIM)); - rd->id = strdupz(id); - rd->hash = simple_hash(rd->id); - - rd->name = (name && *name)?strdupz(name):strdupz(rd->id); - rd->hash_name = simple_hash(rd->name); - - rd->algorithm = algorithm; - rd->multiplier = multiplier; - rd->divisor = divisor; + rd->algorithm = ctr->algorithm; + rd->multiplier = ctr->multiplier; + rd->divisor = ctr->divisor; if(!rd->divisor) rd->divisor = 1; - rd->entries = st->entries; rd->update_every = st->update_every; + rd->rrdset = st; + if(rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST)) rd->collections_counter = 1; - rd->rrdset = st; - - if(memory_mode == RRD_MEMORY_MODE_MAP || memory_mode == RRD_MEMORY_MODE_SAVE) { - if(!rrddim_memory_load_or_create_map_save(st, rd, memory_mode)) { - info("Failed to use memory mode %s for chart '%s', dimension '%s', falling back to ram", (memory_mode == RRD_MEMORY_MODE_MAP)?"map":"save", st->name, rd->name); - memory_mode = RRD_MEMORY_MODE_RAM; + if(ctr->memory_mode == RRD_MEMORY_MODE_MAP || ctr->memory_mode == RRD_MEMORY_MODE_SAVE) { + if(!rrddim_memory_load_or_create_map_save(st, rd, ctr->memory_mode)) { + info("Failed to use memory mode %s for chart '%s', dimension '%s', falling back to ram", (ctr->memory_mode == RRD_MEMORY_MODE_MAP)?"map":"save", rrdset_name(st), rrddim_name(rd)); + ctr->memory_mode = RRD_MEMORY_MODE_RAM; } } - if(memory_mode == RRD_MEMORY_MODE_RAM) { + if(ctr->memory_mode == RRD_MEMORY_MODE_RAM) { size_t entries = st->entries; if(!entries) entries = 5; rd->db = netdata_mmap(NULL, entries * sizeof(storage_number), MAP_PRIVATE, 1); if(!rd->db) { - info("Failed to use memory mode ram for chart '%s', dimension '%s', falling back to alloc", st->name, rd->name); - memory_mode = RRD_MEMORY_MODE_ALLOC; + info("Failed to use memory mode ram for chart '%s', dimension '%s', falling back to alloc", rrdset_name(st), rrddim_name(rd)); + ctr->memory_mode = RRD_MEMORY_MODE_ALLOC; } else rd->memsize = entries * sizeof(storage_number); } - if(memory_mode == RRD_MEMORY_MODE_ALLOC || memory_mode == RRD_MEMORY_MODE_NONE) { + if(ctr->memory_mode == RRD_MEMORY_MODE_ALLOC || ctr->memory_mode == RRD_MEMORY_MODE_NONE) { size_t entries = st->entries; if(entries < 5) entries = 5; - rd->db = callocz(entries, sizeof(storage_number)); + rd->db = rrddim_alloc_db(entries); rd->memsize = entries * sizeof(storage_number); } - rd->rrd_memory_mode = memory_mode; - -#ifdef ENABLE_ACLK - rd->aclk_live_status = -1; + rd->rrd_memory_mode = ctr->memory_mode; + + if (unlikely(rrdcontext_find_dimension_uuid(st, rrddim_id(rd), &(rd->metric_uuid)))) { + uuid_generate(rd->metric_uuid); + bool found_in_sql = false; (void)found_in_sql; + +// bool found_in_sql = true; +// if(unlikely(sql_find_dimension_uuid(st, rd, &rd->metric_uuid))) { +// found_in_sql = false; +// uuid_generate(rd->metric_uuid); +// } + +#ifdef NETDATA_INTERNAL_CHECKS + char uuid_str[UUID_STR_LEN]; + uuid_unparse_lower(rd->metric_uuid, uuid_str); + error_report("Dimension UUID for host %s chart [%s] dimension [%s] not found in context. It is now set to %s (%s)", + string2str(host->hostname), + string2str(st->name), + string2str(rd->name), + uuid_str, found_in_sql ? "found in sqlite" : "newly generated"); #endif - (void) find_dimension_uuid(st, rd, &(rd->metric_uuid)); + } // initialize the db tiers { size_t initialized = 0; - RRD_MEMORY_MODE wanted_mode = memory_mode; - for(int tier = 0; tier < storage_tiers ; tier++, wanted_mode = RRD_MEMORY_MODE_DBENGINE) { - STORAGE_ENGINE *eng = storage_engine_get(wanted_mode); - if(!eng) continue; - + for(size_t tier = 0; tier < storage_tiers ; tier++) { + STORAGE_ENGINE *eng = host->db[tier].eng; rd->tiers[tier] = callocz(1, sizeof(struct rrddim_tier)); - rd->tiers[tier]->tier_grouping = get_tier_grouping(tier); - rd->tiers[tier]->mode = eng->id; - rd->tiers[tier]->collect_ops = eng->api.collect_ops; - rd->tiers[tier]->query_ops = eng->api.query_ops; - rd->tiers[tier]->db_metric_handle = eng->api.init(rd, host->storage_instance[tier]); + rd->tiers[tier]->tier_grouping = host->db[tier].tier_grouping; + rd->tiers[tier]->collect_ops = &eng->api.collect_ops; + rd->tiers[tier]->query_ops = &eng->api.query_ops; + rd->tiers[tier]->db_metric_handle = eng->api.metric_get_or_create(rd, host->db[tier].instance, rd->rrdset->storage_metrics_groups[tier]); storage_point_unset(rd->tiers[tier]->virtual_point); initialized++; @@ -284,138 +120,114 @@ RRDDIM *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collecte } if(!initialized) - error("Failed to initialize all db tiers for chart '%s', dimension '%s", st->name, rd->name); + error("Failed to initialize all db tiers for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd)); if(!rd->tiers[0]) - error("Failed to initialize the first db tier for chart '%s', dimension '%s", st->name, rd->name); + error("Failed to initialize the first db tier for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd)); } - store_active_dimension(&rd->metric_uuid); - // initialize data collection for all tiers { size_t initialized = 0; - for (int tier = 0; tier < storage_tiers; tier++) { + for (size_t tier = 0; tier < storage_tiers; tier++) { if (rd->tiers[tier]) { - rd->tiers[tier]->db_collection_handle = rd->tiers[tier]->collect_ops.init(rd->tiers[tier]->db_metric_handle); + rd->tiers[tier]->db_collection_handle = rd->tiers[tier]->collect_ops->init(rd->tiers[tier]->db_metric_handle, st->rrdhost->db[tier].tier_grouping * st->update_every); initialized++; } } if(!initialized) - error("Failed to initialize data collection for all db tiers for chart '%s', dimension '%s", st->name, rd->name); + error("Failed to initialize data collection for all db tiers for chart '%s', dimension '%s", rrdset_name(st), rrddim_name(rd)); } - // append this dimension - if(!st->dimensions) - st->dimensions = rd; - else { - RRDDIM *td = st->dimensions; + if(rrdset_number_of_dimensions(st) != 0) { + RRDDIM *td; + dfe_start_write(st->rrddim_root_index, td) { + if(!td) break; + } + dfe_done(td); - if(td->algorithm != rd->algorithm || ABS(td->multiplier) != ABS(rd->multiplier) || ABS(td->divisor) != ABS(rd->divisor)) { + if(td && (td->algorithm != rd->algorithm || ABS(td->multiplier) != ABS(rd->multiplier) || ABS(td->divisor) != ABS(rd->divisor))) { if(!rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) { - #ifdef NETDATA_INTERNAL_CHECKS +#ifdef NETDATA_INTERNAL_CHECKS info("Dimension '%s' added on chart '%s' of host '%s' is not homogeneous to other dimensions already present (algorithm is '%s' vs '%s', multiplier is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ", divisor is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ").", - rd->name, - st->name, - host->hostname, - rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(td->algorithm), - rd->multiplier, td->multiplier, - rd->divisor, td->divisor + rrddim_name(rd), + rrdset_name(st), + rrdhost_hostname(host), + rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(td->algorithm), + rd->multiplier, td->multiplier, + rd->divisor, td->divisor ); - #endif +#endif rrdset_flag_set(st, RRDSET_FLAG_HETEROGENEOUS); } } - - for(; td->next; td = td->next) ; - td->next = rd; - } - - if(host->health_enabled && !st->state->is_ar_chart) { - rrddimvar_create(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->last_stored_value, RRDVAR_OPTION_DEFAULT); - rrddimvar_create(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->last_collected_value, RRDVAR_OPTION_DEFAULT); - rrddimvar_create(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->last_collected_time.tv_sec, RRDVAR_OPTION_DEFAULT); } - if(unlikely(rrddim_index_add(st, rd) != rd)) - error("RRDDIM: INTERNAL ERROR: attempt to index duplicate dimension '%s' on chart '%s'", rd->id, st->id); + rrddim_flag_set(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION); + rrdset_flag_set(rd->rrdset, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION); + rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION); - rrddim_flag_set(rd, RRDDIM_FLAG_PENDING_FOREACH_ALARM); - rrdset_flag_set(st, RRDSET_FLAG_PENDING_FOREACH_ALARMS); - rrdhost_flag_set(host, RRDHOST_FLAG_PENDING_FOREACH_ALARMS); + // let the chart resync + rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); ml_new_dimension(rd); - rrdset_unlock(st); - rrdcontext_updated_rrddim(rd); - return(rd); + ctr->react_action = RRDDIM_REACT_NEW; + + internal_error(false, "RRDDIM: inserted dimension '%s' of chart '%s' of host '%s'", + rrddim_name(rd), rrdset_name(st), rrdhost_hostname(st->rrdhost)); + } -// ---------------------------------------------------------------------------- -// RRDDIM remove / free a dimension +static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *rrdset) { + RRDDIM *rd = rrddim; + RRDSET *st = rrdset; + RRDHOST *host = st->rrdhost; + + internal_error(false, "RRDDIM: deleting dimension '%s' of chart '%s' of host '%s'", + rrddim_name(rd), rrdset_name(st), rrdhost_hostname(host)); -void rrddim_free(RRDSET *st, RRDDIM *rd) -{ rrdcontext_removed_rrddim(rd); - ml_delete_dimension(rd); - - debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name); - if (!rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) { + ml_delete_dimension(rd); - size_t tiers_available = 0, tiers_said_yes = 0; - for(int tier = 0; tier < storage_tiers ;tier++) { - if(rd->tiers[tier]) { - tiers_available++; + debug(D_RRD_CALLS, "rrddim_free() %s.%s", rrdset_name(st), rrddim_name(rd)); - if(rd->tiers[tier]->collect_ops.finalize(rd->tiers[tier]->db_collection_handle)) - tiers_said_yes++; + size_t tiers_available = 0, tiers_said_yes = 0; + for(size_t tier = 0; tier < storage_tiers ;tier++) { + if(rd->tiers[tier] && rd->tiers[tier]->db_collection_handle) { + tiers_available++; - rd->tiers[tier]->db_collection_handle = NULL; - } - } + if(rd->tiers[tier]->collect_ops->finalize(rd->tiers[tier]->db_collection_handle)) + tiers_said_yes++; - if (tiers_available == tiers_said_yes && tiers_said_yes && rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { - /* This metric has no data and no references */ - delete_dimension_uuid(&rd->metric_uuid); + rd->tiers[tier]->db_collection_handle = NULL; } } - if(rd == st->dimensions) - st->dimensions = rd->next; - else { - RRDDIM *i; - for (i = st->dimensions; i && i->next != rd; i = i->next) ; - - if (i && i->next == rd) - i->next = rd->next; - else - error("Request to free dimension '%s.%s' but it is not linked.", st->id, rd->name); + if (tiers_available == tiers_said_yes && tiers_said_yes && rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { + /* This metric has no data and no references */ + metaqueue_delete_dimension_uuid(&rd->metric_uuid); } - rd->next = NULL; - while(rd->variables) - rrddimvar_free(rd->variables); - - if(unlikely(rrddim_index_del(st, rd) != rd)) - error("RRDDIM: INTERNAL ERROR: attempt to remove from index dimension '%s' on chart '%s', removed a different dimension.", rd->id, st->id); + rrddimvar_delete_all(rd); // free(rd->annotations); -//#ifdef ENABLE_ACLK -// if (!netdata_exit) -// aclk_send_dimension_update(rd); -//#endif + //#ifdef ENABLE_ACLK + // if (!netdata_exit) + // aclk_send_dimension_update(rd); + //#endif // this will free MEMORY_MODE_SAVE and MEMORY_MODE_MAP structures rrddim_memory_file_free(rd); - for(int tier = 0; tier < storage_tiers ;tier++) { + for(size_t tier = 0; tier < storage_tiers ;tier++) { if(!rd->tiers[tier]) continue; - STORAGE_ENGINE* eng = storage_engine_get(rd->tiers[tier]->mode); - if(eng) - eng->api.free(rd->tiers[tier]->db_metric_handle); + STORAGE_ENGINE* eng = host->db[tier].eng; + eng->api.metric_release(rd->tiers[tier]->db_metric_handle); freez(rd->tiers[tier]); rd->tiers[tier] = NULL; @@ -423,14 +235,252 @@ void rrddim_free(RRDSET *st, RRDDIM *rd) if(rd->db) { if(rd->rrd_memory_mode == RRD_MEMORY_MODE_RAM) - munmap(rd->db, rd->memsize); + netdata_munmap(rd->db, rd->memsize); else freez(rd->db); } - freez((void *)rd->id); - freez((void *)rd->name); - freez(rd); + string_freez(rd->id); + string_freez(rd->name); +} + +static bool rrddim_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *new_rrddim, void *constructor_data) { + (void)new_rrddim; // it is NULL + + struct rrddim_constructor *ctr = constructor_data; + RRDDIM *rd = rrddim; + RRDSET *st = ctr->st; + + ctr->react_action = RRDDIM_REACT_NONE; + + int rc = rrddim_reset_name(st, rd, ctr->name); + rc += rrddim_set_algorithm(st, rd, ctr->algorithm); + rc += rrddim_set_multiplier(st, rd, ctr->multiplier); + rc += rrddim_set_divisor(st, rd, ctr->divisor); + + for(size_t tier = 0; tier < storage_tiers ;tier++) { + if (rd->tiers[tier] && !rd->tiers[tier]->db_collection_handle) + rd->tiers[tier]->db_collection_handle = + rd->tiers[tier]->collect_ops->init(rd->tiers[tier]->db_metric_handle, st->rrdhost->db[tier].tier_grouping * st->update_every); + } + + if(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) { + rrddim_flag_clear(rd, RRDDIM_FLAG_ARCHIVED); + + rrddim_flag_set(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION); + rrdset_flag_set(rd->rrdset, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION); + rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION); + } + + if(unlikely(rc)) + ctr->react_action = RRDDIM_REACT_UPDATED; + + return ctr->react_action == RRDDIM_REACT_UPDATED; +} + +static void rrddim_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *constructor_data) { + struct rrddim_constructor *ctr = constructor_data; + RRDDIM *rd = rrddim; + RRDSET *st = ctr->st; + + if(ctr->react_action & (RRDDIM_REACT_UPDATED | RRDDIM_REACT_NEW)) { + rrddim_flag_set(rd, RRDDIM_FLAG_METADATA_UPDATE); + rrdset_flag_set(rd->rrdset, RRDSET_FLAG_METADATA_UPDATE); + rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE); + } + + if(ctr->react_action == RRDDIM_REACT_UPDATED) { + // the chart needs to be updated to the parent + rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); + } + + rrdcontext_updated_rrddim(rd); +} + +void rrddim_index_init(RRDSET *st) { + if(!st->rrddim_root_index) { + st->rrddim_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + + dictionary_register_insert_callback(st->rrddim_root_index, rrddim_insert_callback, NULL); + dictionary_register_conflict_callback(st->rrddim_root_index, rrddim_conflict_callback, NULL); + dictionary_register_delete_callback(st->rrddim_root_index, rrddim_delete_callback, st); + dictionary_register_react_callback(st->rrddim_root_index, rrddim_react_callback, st); + } +} + +void rrddim_index_destroy(RRDSET *st) { + dictionary_destroy(st->rrddim_root_index); + st->rrddim_root_index = NULL; +} + +static inline RRDDIM *rrddim_index_find(RRDSET *st, const char *id) { + return dictionary_get(st->rrddim_root_index, id); +} + +// ---------------------------------------------------------------------------- +// RRDDIM - find a dimension + +inline RRDDIM *rrddim_find(RRDSET *st, const char *id) { + debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", rrdset_name(st), id); + + return rrddim_index_find(st, id); +} + +inline RRDDIM_ACQUIRED *rrddim_find_and_acquire(RRDSET *st, const char *id) { + debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", rrdset_name(st), id); + + return (RRDDIM_ACQUIRED *)dictionary_get_and_acquire_item(st->rrddim_root_index, id); +} + +RRDDIM *rrddim_acquired_to_rrddim(RRDDIM_ACQUIRED *rda) { + if(unlikely(!rda)) + return NULL; + + return (RRDDIM *) dictionary_acquired_item_value((const DICTIONARY_ITEM *)rda); +} + +void rrddim_acquired_release(RRDDIM_ACQUIRED *rda) { + if(unlikely(!rda)) + return; + + RRDDIM *rd = rrddim_acquired_to_rrddim(rda); + dictionary_acquired_item_release(rd->rrdset->rrddim_root_index, (const DICTIONARY_ITEM *)rda); +} + +// This will not return dimensions that are archived +RRDDIM *rrddim_find_active(RRDSET *st, const char *id) { + RRDDIM *rd = rrddim_find(st, id); + + if (unlikely(rd && rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED))) + return NULL; + + return rd; +} + +// ---------------------------------------------------------------------------- +// RRDDIM rename a dimension + +inline int rrddim_reset_name(RRDSET *st, RRDDIM *rd, const char *name) { + if(unlikely(!name || !*name || !strcmp(rrddim_name(rd), name))) + return 0; + + debug(D_RRD_CALLS, "rrddim_reset_name() from %s.%s to %s.%s", rrdset_name(st), rrddim_name(rd), rrdset_name(st), name); + + STRING *old = rd->name; + rd->name = rrd_string_strdupz(name); + string_freez(old); + + rrddimvar_rename_all(rd); + + rd->exposed = 0; + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); + + return 1; +} + +inline int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm) { + if(unlikely(rd->algorithm == algorithm)) + return 0; + + debug(D_RRD_CALLS, "Updating algorithm of dimension '%s/%s' from %s to %s", rrdset_id(st), rrddim_name(rd), rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(algorithm)); + rd->algorithm = algorithm; + rd->exposed = 0; + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); + rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); + rrdcontext_updated_rrddim_algorithm(rd); + return 1; +} + +inline int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, collected_number multiplier) { + if(unlikely(rd->multiplier == multiplier)) + return 0; + + debug(D_RRD_CALLS, "Updating multiplier of dimension '%s/%s' from " COLLECTED_NUMBER_FORMAT " to " COLLECTED_NUMBER_FORMAT, rrdset_id(st), rrddim_name(rd), rd->multiplier, multiplier); + rd->multiplier = multiplier; + rd->exposed = 0; + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); + rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); + rrdcontext_updated_rrddim_multiplier(rd); + return 1; +} + +inline int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, collected_number divisor) { + if(unlikely(rd->divisor == divisor)) + return 0; + + debug(D_RRD_CALLS, "Updating divisor of dimension '%s/%s' from " COLLECTED_NUMBER_FORMAT " to " COLLECTED_NUMBER_FORMAT, rrdset_id(st), rrddim_name(rd), rd->divisor, divisor); + rd->divisor = divisor; + rd->exposed = 0; + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); + rrdset_flag_set(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); + rrdcontext_updated_rrddim_divisor(rd); + return 1; +} + +// ---------------------------------------------------------------------------- + +// get the timestamp of the last entry in the round-robin database +time_t rrddim_last_entry_t(RRDDIM *rd) { + time_t latest = rd->tiers[0]->query_ops->latest_time(rd->tiers[0]->db_metric_handle); + + for(size_t tier = 1; tier < storage_tiers ;tier++) { + if(unlikely(!rd->tiers[tier])) continue; + + time_t t = rd->tiers[tier]->query_ops->latest_time(rd->tiers[tier]->db_metric_handle); + if(t > latest) + latest = t; + } + + return latest; +} + +time_t rrddim_first_entry_t_of_tier(RRDDIM *rd, size_t tier) { + if(unlikely(tier > storage_tiers || !rd->tiers[tier])) + return 0; + + return rd->tiers[tier]->query_ops->oldest_time(rd->tiers[tier]->db_metric_handle); +} + +time_t rrddim_first_entry_t(RRDDIM *rd) { + time_t oldest = 0; + + for(size_t tier = 0; tier < storage_tiers ;tier++) { + time_t t = rrddim_first_entry_t_of_tier(rd, tier); + if(t != 0 && (oldest == 0 || t < oldest)) + oldest = t; + } + + return oldest; +} + +RRDDIM *rrddim_add_custom(RRDSET *st + , const char *id + , const char *name + , collected_number multiplier + , collected_number divisor + , RRD_ALGORITHM algorithm + , RRD_MEMORY_MODE memory_mode + ) { + struct rrddim_constructor tmp = { + .st = st, + .id = id, + .name = name, + .multiplier = multiplier, + .divisor = divisor, + .algorithm = algorithm, + .memory_mode = memory_mode, + }; + + RRDDIM *rd = dictionary_set_advanced(st->rrddim_root_index, tmp.id, -1, NULL, sizeof(RRDDIM), &tmp); + return(rd); +} + +// ---------------------------------------------------------------------------- +// RRDDIM remove / free a dimension + +void rrddim_free(RRDSET *st, RRDDIM *rd) { + dictionary_del(st->rrddim_root_index, string2str(rd->id)); } @@ -438,56 +488,59 @@ void rrddim_free(RRDSET *st, RRDDIM *rd) // RRDDIM - set dimension options int rrddim_hide(RRDSET *st, const char *id) { - debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", st->name, id); + debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", rrdset_name(st), id); RRDHOST *host = st->rrdhost; RRDDIM *rd = rrddim_find(st, id); if(unlikely(!rd)) { - error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, st->name, st->id, host->hostname); + error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host)); return 1; } - if (!rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) - (void)sql_set_dimension_option(&rd->metric_uuid, "hidden"); + if (!rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) { + rrddim_flag_set(rd, RRDDIM_FLAG_META_HIDDEN); + metaqueue_dimension_update_flags(rd); + } - rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN); - rrddim_flag_set(rd, RRDDIM_FLAG_META_HIDDEN); + rrddim_option_set(rd, RRDDIM_OPTION_HIDDEN); rrdcontext_updated_rrddim_flags(rd); return 0; } int rrddim_unhide(RRDSET *st, const char *id) { - debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", st->name, id); + debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", rrdset_name(st), id); RRDHOST *host = st->rrdhost; RRDDIM *rd = rrddim_find(st, id); if(unlikely(!rd)) { - error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, st->name, st->id, host->hostname); + error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host)); return 1; } - if (rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) - (void)sql_set_dimension_option(&rd->metric_uuid, NULL); + if (rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN)) { + rrddim_flag_clear(rd, RRDDIM_FLAG_META_HIDDEN); + metaqueue_dimension_update_flags(rd); + } - rrddim_flag_clear(rd, RRDDIM_FLAG_HIDDEN); - rrddim_flag_clear(rd, RRDDIM_FLAG_META_HIDDEN); + rrddim_option_clear(rd, RRDDIM_OPTION_HIDDEN); rrdcontext_updated_rrddim_flags(rd); return 0; } inline void rrddim_is_obsolete(RRDSET *st, RRDDIM *rd) { - debug(D_RRD_CALLS, "rrddim_is_obsolete() for chart %s, dimension %s", st->name, rd->name); + debug(D_RRD_CALLS, "rrddim_is_obsolete() for chart %s, dimension %s", rrdset_name(st), rrddim_name(rd)); if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED))) { - info("Cannot obsolete already archived dimension %s from chart %s", rd->name, st->name); + info("Cannot obsolete already archived dimension %s from chart %s", rrddim_name(rd), rrdset_name(st)); return; } rrddim_flag_set(rd, RRDDIM_FLAG_OBSOLETE); rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS); + rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_PENDING_OBSOLETE_DIMENSIONS); rrdcontext_updated_rrddim_flags(rd); } inline void rrddim_isnot_obsolete(RRDSET *st __maybe_unused, RRDDIM *rd) { - debug(D_RRD_CALLS, "rrddim_isnot_obsolete() for chart %s, dimension %s", st->name, rd->name); + debug(D_RRD_CALLS, "rrddim_isnot_obsolete() for chart %s, dimension %s", rrdset_name(st), rrddim_name(rd)); rrddim_flag_clear(rd, RRDDIM_FLAG_OBSOLETE); rrdcontext_updated_rrddim_flags(rd); @@ -496,30 +549,34 @@ inline void rrddim_isnot_obsolete(RRDSET *st __maybe_unused, RRDDIM *rd) { // ---------------------------------------------------------------------------- // RRDDIM - collect values for a dimension -inline collected_number rrddim_set_by_pointer(RRDSET *st __maybe_unused, RRDDIM *rd, collected_number value) { - debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value); +inline collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value) { + struct timeval now; + now_realtime_timeval(&now); + + return rrddim_timed_set_by_pointer(st, rd, now, value); +} - rrdcontext_collected_rrddim(rd); +collected_number rrddim_timed_set_by_pointer(RRDSET *st __maybe_unused, RRDDIM *rd, struct timeval collected_time, collected_number value) { + debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, rrdset_name(st), rrddim_name(rd), value); - now_realtime_timeval(&rd->last_collected_time); + rd->last_collected_time = collected_time; rd->collected_value = value; rd->updated = 1; - rd->collections_counter++; collected_number v = (value >= 0) ? value : -value; - if(unlikely(v > rd->collected_value_max)) rd->collected_value_max = v; - - // fprintf(stderr, "%s.%s %llu " COLLECTED_NUMBER_FORMAT " dt %0.6f" " rate " NETDATA_DOUBLE_FORMAT "\n", st->name, rd->name, st->usec_since_last_update, value, (float)((double)st->usec_since_last_update / (double)1000000), (NETDATA_DOUBLE)((value - rd->last_collected_value) * (NETDATA_DOUBLE)rd->multiplier / (NETDATA_DOUBLE)rd->divisor * 1000000.0 / (NETDATA_DOUBLE)st->usec_since_last_update)); + if (unlikely(v > rd->collected_value_max)) + rd->collected_value_max = v; return rd->last_collected_value; } + collected_number rrddim_set(RRDSET *st, const char *id, collected_number value) { RRDHOST *host = st->rrdhost; RRDDIM *rd = rrddim_find(st, id); if(unlikely(!rd)) { - error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, st->name, st->id, host->hostname); + error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, rrdset_name(st), rrdset_id(st), rrdhost_hostname(host)); return 0; } @@ -560,7 +617,7 @@ struct rrddim_map_save_v019 { long double last_calculated_value; // ignored long double last_stored_value; // ignored long long collected_value; // ignored - long long last_collected_value; // ignored + long long last_collected_value; // load and save long double collected_volume; // ignored long double stored_volume; // ignored void *next; // ignored @@ -578,22 +635,23 @@ size_t rrddim_memory_file_header_size(void) { } void rrddim_memory_file_update(RRDDIM *rd) { - if(!rd->rd_on_file) return; + if(!rd || !rd->rd_on_file) return; struct rrddim_map_save_v019 *rd_on_file = rd->rd_on_file; rd_on_file->last_collected_time.tv_sec = rd->last_collected_time.tv_sec; rd_on_file->last_collected_time.tv_usec = rd->last_collected_time.tv_usec; + rd_on_file->last_collected_value = rd->last_collected_value; } void rrddim_memory_file_free(RRDDIM *rd) { - if(!rd->rd_on_file) return; + if(!rd || !rd->rd_on_file) return; // needed for memory mode map, to save the latest state rrddim_memory_file_update(rd); struct rrddim_map_save_v019 *rd_on_file = rd->rd_on_file; freez(rd_on_file->cache_filename); - munmap(rd_on_file, rd_on_file->memsize); + netdata_munmap(rd_on_file, rd_on_file->memsize); // remove the pointers from the RRDDIM rd->rd_on_file = NULL; @@ -601,13 +659,13 @@ void rrddim_memory_file_free(RRDDIM *rd) { } const char *rrddim_cache_filename(RRDDIM *rd) { - if(!rd->rd_on_file) return NULL; + if(!rd || !rd->rd_on_file) return NULL; struct rrddim_map_save_v019 *rd_on_file = rd->rd_on_file; return rd_on_file->cache_filename; } void rrddim_memory_file_save(RRDDIM *rd) { - if(!rd->rd_on_file) return; + if(!rd || !rd->rd_on_file) return; rrddim_memory_file_update(rd); @@ -627,7 +685,7 @@ bool rrddim_memory_load_or_create_map_save(RRDSET *st, RRDDIM *rd, RRD_MEMORY_MO char filename[FILENAME_MAX + 1]; char fullfilename[FILENAME_MAX + 1]; - rrdset_strncpyz_name(filename, rd->id, FILENAME_MAX); + rrdset_strncpyz_name(filename, rrddim_id(rd), FILENAME_MAX); snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename); rd_on_file = (struct rrddim_map_save_v019 *)netdata_mmap(fullfilename, size, @@ -646,7 +704,7 @@ bool rrddim_memory_load_or_create_map_save(RRDSET *st, RRDDIM *rd, RRD_MEMORY_MO reset = 1; } else if(rd_on_file->memsize != size) { - error("File %s does not have the desired size, expected %lu but found %lu. Clearing it.", fullfilename, size, rd_on_file->memsize); + error("File %s does not have the desired size, expected %lu but found %lu. Clearing it.", fullfilename, size, (unsigned long int) rd_on_file->memsize); memset(rd_on_file, 0, size); reset = 1; } @@ -662,6 +720,8 @@ bool rrddim_memory_load_or_create_map_save(RRDSET *st, RRDDIM *rd, RRD_MEMORY_MO } if(!reset) { + rd->last_collected_value = rd_on_file->last_collected_value; + if(rd_on_file->algorithm != rd->algorithm) info("File %s does not have the expected algorithm (expected %u '%s', found %u '%s'). Previous values may be wrong.", fullfilename, rd->algorithm, rrd_algorithm_name(rd->algorithm), rd_on_file->algorithm, rrd_algorithm_name(rd_on_file->algorithm)); diff --git a/database/rrddimvar.c b/database/rrddimvar.c index 3c2ed75e5..449ceeb93 100644 --- a/database/rrddimvar.c +++ b/database/rrddimvar.c @@ -1,84 +1,87 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#define NETDATA_HEALTH_INTERNALS #include "rrd.h" +typedef struct rrddimvar { + struct rrddim *rrddim; + + STRING *prefix; + STRING *suffix; + void *value; + + const RRDVAR_ACQUIRED *rrdvar_local_dim_id; + const RRDVAR_ACQUIRED *rrdvar_local_dim_name; + + const RRDVAR_ACQUIRED *rrdvar_family_id; + const RRDVAR_ACQUIRED *rrdvar_family_name; + const RRDVAR_ACQUIRED *rrdvar_family_context_dim_id; + const RRDVAR_ACQUIRED *rrdvar_family_context_dim_name; + + const RRDVAR_ACQUIRED *rrdvar_host_chart_id_dim_id; + const RRDVAR_ACQUIRED *rrdvar_host_chart_id_dim_name; + const RRDVAR_ACQUIRED *rrdvar_host_chart_name_dim_id; + const RRDVAR_ACQUIRED *rrdvar_host_chart_name_dim_name; + + RRDVAR_FLAGS flags:24; + RRDVAR_TYPE type:8; +} RRDDIMVAR; + // ---------------------------------------------------------------------------- // RRDDIMVAR management // DIMENSION VARIABLES #define RRDDIMVAR_ID_MAX 1024 -static inline void rrddimvar_free_variables(RRDDIMVAR *rs) { +static inline void rrddimvar_free_variables_unsafe(RRDDIMVAR *rs) { RRDDIM *rd = rs->rrddim; RRDSET *st = rd->rrdset; RRDHOST *host = st->rrdhost; // CHART VARIABLES FOR THIS DIMENSION - rrdvar_free(host, &st->rrdvar_root_index, rs->var_local_id); - rs->var_local_id = NULL; + if(st->rrdvars) { + rrdvar_release_and_del(st->rrdvars, rs->rrdvar_local_dim_id); + rs->rrdvar_local_dim_id = NULL; - rrdvar_free(host, &st->rrdvar_root_index, rs->var_local_name); - rs->var_local_name = NULL; + rrdvar_release_and_del(st->rrdvars, rs->rrdvar_local_dim_name); + rs->rrdvar_local_dim_name = NULL; + } // FAMILY VARIABLES FOR THIS DIMENSION - rrdvar_free(host, &st->rrdfamily->rrdvar_root_index, rs->var_family_id); - rs->var_family_id = NULL; + if(st->rrdfamily) { + rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_id); + rs->rrdvar_family_id = NULL; - rrdvar_free(host, &st->rrdfamily->rrdvar_root_index, rs->var_family_name); - rs->var_family_name = NULL; + rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_name); + rs->rrdvar_family_name = NULL; - rrdvar_free(host, &st->rrdfamily->rrdvar_root_index, rs->var_family_contextid); - rs->var_family_contextid = NULL; + rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_context_dim_id); + rs->rrdvar_family_context_dim_id = NULL; - rrdvar_free(host, &st->rrdfamily->rrdvar_root_index, rs->var_family_contextname); - rs->var_family_contextname = NULL; + rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_context_dim_name); + rs->rrdvar_family_context_dim_name = NULL; + } // HOST VARIABLES FOR THIS DIMENSION - rrdvar_free(host, &host->rrdvar_root_index, rs->var_host_chartidid); - rs->var_host_chartidid = NULL; - - rrdvar_free(host, &host->rrdvar_root_index, rs->var_host_chartidname); - rs->var_host_chartidname = NULL; - - rrdvar_free(host, &host->rrdvar_root_index, rs->var_host_chartnameid); - rs->var_host_chartnameid = NULL; - - rrdvar_free(host, &host->rrdvar_root_index, rs->var_host_chartnamename); - rs->var_host_chartnamename = NULL; + if(host->rrdvars && host->health_enabled) { + rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_id_dim_id); + rs->rrdvar_host_chart_id_dim_id = NULL; - // KEYS - - freez(rs->key_id); - rs->key_id = NULL; - - freez(rs->key_name); - rs->key_name = NULL; - - freez(rs->key_fullidid); - rs->key_fullidid = NULL; - - freez(rs->key_fullidname); - rs->key_fullidname = NULL; - - freez(rs->key_contextid); - rs->key_contextid = NULL; + rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_id_dim_name); + rs->rrdvar_host_chart_id_dim_name = NULL; - freez(rs->key_contextname); - rs->key_contextname = NULL; + rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_name_dim_id); + rs->rrdvar_host_chart_name_dim_id = NULL; - freez(rs->key_fullnameid); - rs->key_fullnameid = NULL; - - freez(rs->key_fullnamename); - rs->key_fullnamename = NULL; + rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_name_dim_name); + rs->rrdvar_host_chart_name_dim_name = NULL; + } } -static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { - rrddimvar_free_variables(rs); +static inline void rrddimvar_update_variables_unsafe(RRDDIMVAR *rs) { + rrddimvar_free_variables_unsafe(rs); RRDDIM *rd = rs->rrddim; RRDSET *st = rd->rrdset; @@ -88,29 +91,29 @@ static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { // KEYS - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix); - rs->key_id = strdupz(buffer); + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", string2str(rs->prefix), rrddim_id(rd), string2str(rs->suffix)); + STRING *key_dim_id = string_strdupz(buffer); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix); - rs->key_name = strdupz(buffer); + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", string2str(rs->prefix), rrddim_name(rd), string2str(rs->suffix)); + STRING *key_dim_name = string_strdupz(buffer); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_id); - rs->key_fullidid = strdupz(buffer); + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_id(st), string2str(key_dim_id)); + STRING *key_chart_id_dim_id = string_strdupz(buffer); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_name); - rs->key_fullidname = strdupz(buffer); + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_id(st), string2str(key_dim_name)); + STRING *key_chart_id_dim_name = string_strdupz(buffer); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_id); - rs->key_contextid = strdupz(buffer); + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_context(st), string2str(key_dim_id)); + STRING *key_context_dim_id = string_strdupz(buffer); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_name); - rs->key_contextname = strdupz(buffer); + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_context(st), string2str(key_dim_name)); + STRING *key_context_dim_name = string_strdupz(buffer); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_id); - rs->key_fullnameid = strdupz(buffer); + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_name(st), string2str(key_dim_id)); + STRING *key_chart_name_dim_id = string_strdupz(buffer); - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_name); - rs->key_fullnamename = strdupz(buffer); + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_name(st), string2str(key_dim_name)); + STRING *key_chart_name_dim_name = string_strdupz(buffer); // CHART VARIABLES FOR THIS DIMENSION // ----------------------------------- @@ -119,8 +122,10 @@ static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { // - $id // - $name - rs->var_local_id = rrdvar_create_and_index("local", &st->rrdvar_root_index, rs->key_id, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); - rs->var_local_name = rrdvar_create_and_index("local", &st->rrdvar_root_index, rs->key_name, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); + if(st->rrdvars) { + rs->rrdvar_local_dim_id = rrdvar_add_and_acquire("local", st->rrdvars, key_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); + rs->rrdvar_local_dim_name = rrdvar_add_and_acquire("local", st->rrdvars, key_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); + } // FAMILY VARIABLES FOR THIS DIMENSION // ----------------------------------- @@ -131,10 +136,12 @@ static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { // - $chart-context.id // - $chart-context.name - rs->var_family_id = rrdvar_create_and_index("family", &st->rrdfamily->rrdvar_root_index, rs->key_id, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); - rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->rrdvar_root_index, rs->key_name, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); - rs->var_family_contextid = rrdvar_create_and_index("family", &st->rrdfamily->rrdvar_root_index, rs->key_contextid, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); - rs->var_family_contextname = rrdvar_create_and_index("family", &st->rrdfamily->rrdvar_root_index, rs->key_contextname, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); + if(st->rrdfamily) { + rs->rrdvar_family_id = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); + rs->rrdvar_family_name = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); + rs->rrdvar_family_context_dim_id = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_context_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); + rs->rrdvar_family_context_dim_name = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_context_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); + } // HOST VARIABLES FOR THIS DIMENSION // ----------------------------------- @@ -145,73 +152,121 @@ static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { // - $chart-name.id // - $chart-name.name - rs->var_host_chartidid = rrdvar_create_and_index("host", &host->rrdvar_root_index, rs->key_fullidid, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); - rs->var_host_chartidname = rrdvar_create_and_index("host", &host->rrdvar_root_index, rs->key_fullidname, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); - rs->var_host_chartnameid = rrdvar_create_and_index("host", &host->rrdvar_root_index, rs->key_fullnameid, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); - rs->var_host_chartnamename = rrdvar_create_and_index("host", &host->rrdvar_root_index, rs->key_fullnamename, rs->type, RRDVAR_OPTION_DEFAULT, rs->value); + if(host->rrdvars && host->health_enabled) { + rs->rrdvar_host_chart_id_dim_id = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_id_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); + rs->rrdvar_host_chart_id_dim_name = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_id_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); + rs->rrdvar_host_chart_name_dim_id = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_name_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value); + rs->rrdvar_host_chart_name_dim_name = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_name_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value); + } + + // free the keys + + string_freez(key_dim_id); + string_freez(key_dim_name); + string_freez(key_chart_id_dim_id); + string_freez(key_chart_id_dim_name); + string_freez(key_context_dim_id); + string_freez(key_context_dim_name); + string_freez(key_chart_name_dim_id); + string_freez(key_chart_name_dim_name); } -RRDDIMVAR *rrddimvar_create(RRDDIM *rd, RRDVAR_TYPE type, const char *prefix, const char *suffix, void *value, RRDVAR_OPTIONS options) { - RRDSET *st = rd->rrdset; - (void)st; +struct rrddimvar_constructor { + RRDDIM *rrddim; + const char *prefix; + const char *suffix; + void *value; + RRDVAR_FLAGS flags :16; + RRDVAR_TYPE type:8; +}; - debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:""); +static void rrddimvar_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddimvar, void *constructor_data) { + RRDDIMVAR *rs = rrddimvar; + struct rrddimvar_constructor *ctr = constructor_data; - if(!prefix) prefix = ""; - if(!suffix) suffix = ""; + if(!ctr->prefix) ctr->prefix = ""; + if(!ctr->suffix) ctr->suffix = ""; + + rs->prefix = string_strdupz(ctr->prefix); + rs->suffix = string_strdupz(ctr->suffix); + + rs->type = ctr->type; + rs->value = ctr->value; + rs->flags = ctr->flags; + rs->rrddim = ctr->rrddim; + + rrddimvar_update_variables_unsafe(rs); +} + +static bool rrddimvar_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddimvar, void *new_rrddimvar __maybe_unused, void *constructor_data __maybe_unused) { + RRDDIMVAR *rs = rrddimvar; + rrddimvar_update_variables_unsafe(rs); + + return true; +} - RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR)); +static void rrddimvar_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddimvar, void *rrdset __maybe_unused) { + RRDDIMVAR *rs = rrddimvar; + rrddimvar_free_variables_unsafe(rs); + string_freez(rs->prefix); + string_freez(rs->suffix); +} - rs->prefix = strdupz(prefix); - rs->suffix = strdupz(suffix); +void rrddimvar_index_init(RRDSET *st) { + if(!st->rrddimvar_root_index) { + st->rrddimvar_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); - rs->type = type; - rs->value = value; - rs->options = options; - rs->rrddim = rd; + dictionary_register_insert_callback(st->rrddimvar_root_index, rrddimvar_insert_callback, NULL); + dictionary_register_conflict_callback(st->rrddimvar_root_index, rrddimvar_conflict_callback, NULL); + dictionary_register_delete_callback(st->rrddimvar_root_index, rrddimvar_delete_callback, st); + } +} - rs->next = rd->variables; - rd->variables = rs; +void rrddimvar_index_destroy(RRDSET *st) { + dictionary_destroy(st->rrddimvar_root_index); + st->rrddimvar_root_index = NULL; +} - rrddimvar_create_variables(rs); +void rrddimvar_add_and_leave_released(RRDDIM *rd, RRDVAR_TYPE type, const char *prefix, const char *suffix, void *value, RRDVAR_FLAGS flags) { + if(!prefix) prefix = ""; + if(!suffix) suffix = ""; - return rs; + char key[RRDDIMVAR_ID_MAX + 1]; + size_t key_len = snprintfz(key, RRDDIMVAR_ID_MAX, "%s_%s_%s", prefix, rrddim_id(rd), suffix); + + struct rrddimvar_constructor tmp = { + .suffix = suffix, + .prefix = prefix, + .type = type, + .flags = flags, + .value = value, + .rrddim = rd + }; + dictionary_set_advanced(rd->rrdset->rrddimvar_root_index, key, (ssize_t)(key_len + 1), NULL, sizeof(RRDDIMVAR), &tmp); } void rrddimvar_rename_all(RRDDIM *rd) { RRDSET *st = rd->rrdset; - (void)st; - debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); + debug(D_VARIABLES, "RRDDIMVAR rename for chart id '%s' name '%s', dimension id '%s', name '%s'", rrdset_id(st), rrdset_name(st), rrddim_id(rd), rrddim_name(rd)); - RRDDIMVAR *rs, *next = rd->variables; - while((rs = next)) { - next = rs->next; - rrddimvar_create_variables(rs); + RRDDIMVAR *rs; + dfe_start_write(st->rrddimvar_root_index, rs) { + if(unlikely(rs->rrddim == rd)) + rrddimvar_update_variables_unsafe(rs); } + dfe_done(rs); } -void rrddimvar_free(RRDDIMVAR *rs) { - RRDDIM *rd = rs->rrddim; +void rrddimvar_delete_all(RRDDIM *rd) { RRDSET *st = rd->rrdset; - debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix); - rrddimvar_free_variables(rs); + debug(D_VARIABLES, "RRDDIMVAR delete for chart id '%s' name '%s', dimension id '%s', name '%s'", rrdset_id(st), rrdset_name(st), rrddim_id(rd), rrddim_name(rd)); - if(rd->variables == rs) { - debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); - rd->variables = rs->next; - } - else { - debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name); - RRDDIMVAR *t; - for (t = rd->variables; t && t->next != rs; t = t->next) ; - if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->key_name, st->id, rd->id); - else t->next = rs->next; + RRDDIMVAR *rs; + dfe_start_write(st->rrddimvar_root_index, rs) { + if(unlikely(rs->rrddim == rd)) + dictionary_del(st->rrddimvar_root_index, rs_dfe.name); } - - freez(rs->prefix); - freez(rs->suffix); - freez(rs); + dfe_done(rs); } - diff --git a/database/rrddimvar.h b/database/rrddimvar.h index 3494824be..a803ea753 100644 --- a/database/rrddimvar.h +++ b/database/rrddimvar.h @@ -10,47 +10,12 @@ // calculated / processed by the normal data collection process // This means, there will be no speed penalty for using // these variables -struct rrddimvar { - char *prefix; - char *suffix; - - char *key_id; // dimension id - char *key_name; // dimension name - char *key_contextid; // context + dimension id - char *key_contextname; // context + dimension name - char *key_fullidid; // chart type.chart id + dimension id - char *key_fullidname; // chart type.chart id + dimension name - char *key_fullnameid; // chart type.chart name + dimension id - char *key_fullnamename; // chart type.chart name + dimension name - - RRDVAR_TYPE type; - void *value; - - RRDVAR_OPTIONS options; - - RRDVAR *var_local_id; - RRDVAR *var_local_name; - - RRDVAR *var_family_id; - RRDVAR *var_family_name; - RRDVAR *var_family_contextid; - RRDVAR *var_family_contextname; - - RRDVAR *var_host_chartidid; - RRDVAR *var_host_chartidname; - RRDVAR *var_host_chartnameid; - RRDVAR *var_host_chartnamename; - - struct rrddim *rrddim; - - struct rrddimvar *next; -}; - - -extern void rrddimvar_rename_all(RRDDIM *rd); -extern RRDDIMVAR *rrddimvar_create(RRDDIM *rd, RRDVAR_TYPE type, const char *prefix, const char *suffix, void *value, RRDVAR_OPTIONS options); -extern void rrddimvar_free(RRDDIMVAR *rs); +void rrddimvar_rename_all(RRDDIM *rd); +void rrddimvar_add_and_leave_released(RRDDIM *rd, RRDVAR_TYPE type, const char *prefix, const char *suffix, void *value, RRDVAR_FLAGS flags); +void rrddimvar_delete_all(RRDDIM *rd); +void rrddimvar_index_init(RRDSET *st); +void rrddimvar_index_destroy(RRDSET *st); #endif //NETDATA_RRDDIMVAR_H diff --git a/database/rrdfamily.c b/database/rrdfamily.c index 3d91c3788..e7d1536c8 100644 --- a/database/rrdfamily.c +++ b/database/rrdfamily.c @@ -3,59 +3,66 @@ #define NETDATA_RRD_INTERNALS #include "rrd.h" +typedef struct rrdfamily { + STRING *family; + DICTIONARY *rrdvars; +} RRDFAMILY; + // ---------------------------------------------------------------------------- // RRDFAMILY index -int rrdfamily_compare(void *a, void *b) { - if(((RRDFAMILY *)a)->hash_family < ((RRDFAMILY *)b)->hash_family) return -1; - else if(((RRDFAMILY *)a)->hash_family > ((RRDFAMILY *)b)->hash_family) return 1; - else return strcmp(((RRDFAMILY *)a)->family, ((RRDFAMILY *)b)->family); -} +struct rrdfamily_constructor { + const char *family; +}; -#define rrdfamily_index_add(host, rc) (RRDFAMILY *)avl_insert_lock(&((host)->rrdfamily_root_index), (avl_t *)(rc)) -#define rrdfamily_index_del(host, rc) (RRDFAMILY *)avl_remove_lock(&((host)->rrdfamily_root_index), (avl_t *)(rc)) +static void rrdfamily_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdfamily, void *constructor_data) { + RRDFAMILY *rf = rrdfamily; + struct rrdfamily_constructor *ctr = constructor_data; -static RRDFAMILY *rrdfamily_index_find(RRDHOST *host, const char *id, uint32_t hash) { - RRDFAMILY tmp; - tmp.family = id; - tmp.hash_family = (hash)?hash:simple_hash(tmp.family); + rf->family = string_strdupz(ctr->family); + rf->rrdvars = rrdvariables_create(); +} - return (RRDFAMILY *)avl_search_lock(&(host->rrdfamily_root_index), (avl_t *) &tmp); +static void rrdfamily_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdfamily, void *rrdhost __maybe_unused) { + RRDFAMILY *rf = rrdfamily; + string_freez(rf->family); + rrdvariables_destroy(rf->rrdvars); + rf->family = NULL; + rf->rrdvars = NULL; } -RRDFAMILY *rrdfamily_create(RRDHOST *host, const char *id) { - RRDFAMILY *rc = rrdfamily_index_find(host, id, 0); - if(!rc) { - rc = callocz(1, sizeof(RRDFAMILY)); +void rrdfamily_index_init(RRDHOST *host) { + if(!host->rrdfamily_root_index) { + host->rrdfamily_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); - rc->family = strdupz(id); - rc->hash_family = simple_hash(rc->family); + dictionary_register_insert_callback(host->rrdfamily_root_index, rrdfamily_insert_callback, NULL); + dictionary_register_delete_callback(host->rrdfamily_root_index, rrdfamily_delete_callback, host); + } +} - // initialize the variables index - avl_init_lock(&rc->rrdvar_root_index, rrdvar_compare); +void rrdfamily_index_destroy(RRDHOST *host) { + dictionary_destroy(host->rrdfamily_root_index); + host->rrdfamily_root_index = NULL; +} - RRDFAMILY *ret = rrdfamily_index_add(host, rc); - if(ret != rc) - error("RRDFAMILY: INTERNAL ERROR: Expected to INSERT RRDFAMILY '%s' into index, but inserted '%s'.", rc->family, (ret)?ret->family:"NONE"); - } - rc->use_count++; - return rc; +// ---------------------------------------------------------------------------- +// RRDFAMILY management + +const RRDFAMILY_ACQUIRED *rrdfamily_add_and_acquire(RRDHOST *host, const char *id) { + struct rrdfamily_constructor tmp = { + .family = id, + }; + return (const RRDFAMILY_ACQUIRED *)dictionary_set_and_acquire_item_advanced(host->rrdfamily_root_index, id, -1, NULL, sizeof(RRDFAMILY), &tmp); } -void rrdfamily_free(RRDHOST *host, RRDFAMILY *rc) { - rc->use_count--; - if(!rc->use_count) { - RRDFAMILY *ret = rrdfamily_index_del(host, rc); - if(ret != rc) - error("RRDFAMILY: INTERNAL ERROR: Expected to DELETE RRDFAMILY '%s' from index, but deleted '%s'.", rc->family, (ret)?ret->family:"NONE"); - else { - debug(D_RRD_CALLS, "RRDFAMILY: Cleaning up remaining family variables for host '%s', family '%s'", host->hostname, rc->family); - rrdvar_free_remaining_variables(host, &rc->rrdvar_root_index); - - freez((void *) rc->family); - freez(rc); - } - } +void rrdfamily_release(RRDHOST *host, const RRDFAMILY_ACQUIRED *rfa) { + if(unlikely(!rfa)) return; + dictionary_acquired_item_release(host->rrdfamily_root_index, (const DICTIONARY_ITEM *)rfa); } +DICTIONARY *rrdfamily_rrdvars_dict(const RRDFAMILY_ACQUIRED *rfa) { + if(unlikely(!rfa)) return NULL; + RRDFAMILY *rf = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rfa); + return(rf->rrdvars); +} diff --git a/database/rrdfunctions.c b/database/rrdfunctions.c new file mode 100644 index 000000000..fb847a356 --- /dev/null +++ b/database/rrdfunctions.c @@ -0,0 +1,758 @@ +#define NETDATA_RRD_INTERNALS +#include "rrd.h" + +#define MAX_FUNCTION_LENGTH (PLUGINSD_LINE_MAX - 512) // we need some space for the rest of the line + +static unsigned char functions_allowed_chars[256] = { + [0] = '\0', // + [1] = '_', // + [2] = '_', // + [3] = '_', // + [4] = '_', // + [5] = '_', // + [6] = '_', // + [7] = '_', // + [8] = '_', // + [9] = ' ', // Horizontal Tab + [10] = ' ', // Line Feed + [11] = ' ', // Vertical Tab + [12] = ' ', // Form Feed + [13] = ' ', // Carriage Return + [14] = '_', // + [15] = '_', // + [16] = '_', // + [17] = '_', // + [18] = '_', // + [19] = '_', // + [20] = '_', // + [21] = '_', // + [22] = '_', // + [23] = '_', // + [24] = '_', // + [25] = '_', // + [26] = '_', // + [27] = '_', // + [28] = '_', // + [29] = '_', // + [30] = '_', // + [31] = '_', // + [32] = ' ', // SPACE keep + [33] = '_', // ! + [34] = '_', // " + [35] = '_', // # + [36] = '_', // $ + [37] = '_', // % + [38] = '_', // & + [39] = '_', // ' + [40] = '_', // ( + [41] = '_', // ) + [42] = '_', // * + [43] = '_', // + + [44] = ',', // , keep + [45] = '-', // - keep + [46] = '.', // . keep + [47] = '/', // / keep + [48] = '0', // 0 keep + [49] = '1', // 1 keep + [50] = '2', // 2 keep + [51] = '3', // 3 keep + [52] = '4', // 4 keep + [53] = '5', // 5 keep + [54] = '6', // 6 keep + [55] = '7', // 7 keep + [56] = '8', // 8 keep + [57] = '9', // 9 keep + [58] = ':', // : keep + [59] = ':', // ; convert ; to : + [60] = '_', // < + [61] = ':', // = convert = to : + [62] = '_', // > + [63] = '_', // ? + [64] = '_', // @ + [65] = 'A', // A keep + [66] = 'B', // B keep + [67] = 'C', // C keep + [68] = 'D', // D keep + [69] = 'E', // E keep + [70] = 'F', // F keep + [71] = 'G', // G keep + [72] = 'H', // H keep + [73] = 'I', // I keep + [74] = 'J', // J keep + [75] = 'K', // K keep + [76] = 'L', // L keep + [77] = 'M', // M keep + [78] = 'N', // N keep + [79] = 'O', // O keep + [80] = 'P', // P keep + [81] = 'Q', // Q keep + [82] = 'R', // R keep + [83] = 'S', // S keep + [84] = 'T', // T keep + [85] = 'U', // U keep + [86] = 'V', // V keep + [87] = 'W', // W keep + [88] = 'X', // X keep + [89] = 'Y', // Y keep + [90] = 'Z', // Z keep + [91] = '_', // [ + [92] = '/', // backslash convert \ to / + [93] = '_', // ] + [94] = '_', // ^ + [95] = '_', // _ keep + [96] = '_', // ` + [97] = 'a', // a keep + [98] = 'b', // b keep + [99] = 'c', // c keep + [100] = 'd', // d keep + [101] = 'e', // e keep + [102] = 'f', // f keep + [103] = 'g', // g keep + [104] = 'h', // h keep + [105] = 'i', // i keep + [106] = 'j', // j keep + [107] = 'k', // k keep + [108] = 'l', // l keep + [109] = 'm', // m keep + [110] = 'n', // n keep + [111] = 'o', // o keep + [112] = 'p', // p keep + [113] = 'q', // q keep + [114] = 'r', // r keep + [115] = 's', // s keep + [116] = 't', // t keep + [117] = 'u', // u keep + [118] = 'v', // v keep + [119] = 'w', // w keep + [120] = 'x', // x keep + [121] = 'y', // y keep + [122] = 'z', // z keep + [123] = '_', // { + [124] = '_', // | + [125] = '_', // } + [126] = '_', // ~ + [127] = '_', // + [128] = '_', // + [129] = '_', // + [130] = '_', // + [131] = '_', // + [132] = '_', // + [133] = '_', // + [134] = '_', // + [135] = '_', // + [136] = '_', // + [137] = '_', // + [138] = '_', // + [139] = '_', // + [140] = '_', // + [141] = '_', // + [142] = '_', // + [143] = '_', // + [144] = '_', // + [145] = '_', // + [146] = '_', // + [147] = '_', // + [148] = '_', // + [149] = '_', // + [150] = '_', // + [151] = '_', // + [152] = '_', // + [153] = '_', // + [154] = '_', // + [155] = '_', // + [156] = '_', // + [157] = '_', // + [158] = '_', // + [159] = '_', // + [160] = '_', // + [161] = '_', // + [162] = '_', // + [163] = '_', // + [164] = '_', // + [165] = '_', // + [166] = '_', // + [167] = '_', // + [168] = '_', // + [169] = '_', // + [170] = '_', // + [171] = '_', // + [172] = '_', // + [173] = '_', // + [174] = '_', // + [175] = '_', // + [176] = '_', // + [177] = '_', // + [178] = '_', // + [179] = '_', // + [180] = '_', // + [181] = '_', // + [182] = '_', // + [183] = '_', // + [184] = '_', // + [185] = '_', // + [186] = '_', // + [187] = '_', // + [188] = '_', // + [189] = '_', // + [190] = '_', // + [191] = '_', // + [192] = '_', // + [193] = '_', // + [194] = '_', // + [195] = '_', // + [196] = '_', // + [197] = '_', // + [198] = '_', // + [199] = '_', // + [200] = '_', // + [201] = '_', // + [202] = '_', // + [203] = '_', // + [204] = '_', // + [205] = '_', // + [206] = '_', // + [207] = '_', // + [208] = '_', // + [209] = '_', // + [210] = '_', // + [211] = '_', // + [212] = '_', // + [213] = '_', // + [214] = '_', // + [215] = '_', // + [216] = '_', // + [217] = '_', // + [218] = '_', // + [219] = '_', // + [220] = '_', // + [221] = '_', // + [222] = '_', // + [223] = '_', // + [224] = '_', // + [225] = '_', // + [226] = '_', // + [227] = '_', // + [228] = '_', // + [229] = '_', // + [230] = '_', // + [231] = '_', // + [232] = '_', // + [233] = '_', // + [234] = '_', // + [235] = '_', // + [236] = '_', // + [237] = '_', // + [238] = '_', // + [239] = '_', // + [240] = '_', // + [241] = '_', // + [242] = '_', // + [243] = '_', // + [244] = '_', // + [245] = '_', // + [246] = '_', // + [247] = '_', // + [248] = '_', // + [249] = '_', // + [250] = '_', // + [251] = '_', // + [252] = '_', // + [253] = '_', // + [254] = '_', // + [255] = '_' // +}; + +static inline size_t sanitize_function_text(char *dst, const char *src, size_t dst_len) { + return text_sanitize((unsigned char *)dst, (const unsigned char *)src, dst_len, + functions_allowed_chars, true, "", NULL); +} + +// we keep a dictionary per RRDSET with these functions +// the dictionary is created on demand (only when a function is added to an RRDSET) + +typedef enum { + RRD_FUNCTION_LOCAL = (1 << 0), + RRD_FUNCTION_GLOBAL = (1 << 1), + + // this is 8-bit +} RRD_FUNCTION_OPTIONS; + +struct rrd_collector_function { + bool sync; // when true, the function is called synchronously + uint8_t options; // RRD_FUNCTION_OPTIONS + STRING *help; + int timeout; // the default timeout of the function + + int (*function)(BUFFER *wb, int timeout, const char *function, void *collector_data, + function_data_ready_callback callback, void *callback_data); + + void *collector_data; + struct rrd_collector *collector; +}; + +// Each function points to this collector structure +// so that when the collector exits, all of them will +// be invalidated (running == false) +// The last function that is using this collector +// frees the structure too (or when the collector calls +// rrdset_collector_finished()). + +struct rrd_collector { + int32_t refcount; + pid_t tid; + bool running; +}; + +// Each thread that adds RRDSET functions, has to call +// rrdset_collector_started() and rrdset_collector_finished() +// to create the collector structure. + +static __thread struct rrd_collector *thread_rrd_collector = NULL; + +static void rrd_collector_free(struct rrd_collector *rdc) { + int32_t expected = 0; + if(likely(!__atomic_compare_exchange_n(&rdc->refcount, &expected, -1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))) { + // the collector is still referenced by charts. + // leave it hanging there, the last chart will actually free it. + return; + } + + // we can free it now + freez(rdc); +} + +// called once per collector +void rrd_collector_started(void) { + if(likely(thread_rrd_collector)) return; + + thread_rrd_collector = callocz(1, sizeof(struct rrd_collector)); + thread_rrd_collector->tid = gettid(); + thread_rrd_collector->running = true; +} + +// called once per collector +void rrd_collector_finished(void) { + if(!thread_rrd_collector) + return; + + thread_rrd_collector->running = false; + rrd_collector_free(thread_rrd_collector); + thread_rrd_collector = NULL; +} + +static struct rrd_collector *rrd_collector_acquire(void) { + __atomic_add_fetch(&thread_rrd_collector->refcount, 1, __ATOMIC_SEQ_CST); + return thread_rrd_collector; +} + +static void rrd_collector_release(struct rrd_collector *rdc) { + if(unlikely(!rdc)) return; + + int32_t refcount = __atomic_sub_fetch(&rdc->refcount, 1, __ATOMIC_SEQ_CST); + if(refcount == 0 && !rdc->running) + rrd_collector_free(rdc); +} + +static void rrd_functions_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func __maybe_unused, + void *rrdhost __maybe_unused) { + struct rrd_collector_function *rdcf = func; + + if(!thread_rrd_collector) + fatal("RRDSET_COLLECTOR: called %s() for function '%s' without calling rrd_collector_started() first.", + __FUNCTION__, dictionary_acquired_item_name(item)); + + rdcf->collector = rrd_collector_acquire(); +} + +static void rrd_functions_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func __maybe_unused, + void *rrdhost __maybe_unused) { + struct rrd_collector_function *rdcf = func; + rrd_collector_release(rdcf->collector); +} + +static bool rrd_functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func __maybe_unused, + void *new_func __maybe_unused, void *rrdhost __maybe_unused) { + struct rrd_collector_function *rdcf = func; + struct rrd_collector_function *new_rdcf = new_func; + + if(!thread_rrd_collector) + fatal("RRDSET_COLLECTOR: called %s() for function '%s' without calling rrd_collector_started() first.", + __FUNCTION__, dictionary_acquired_item_name(item)); + + bool changed = false; + + if(rdcf->collector != thread_rrd_collector) { + struct rrd_collector *old_rdc = rdcf->collector; + rdcf->collector = rrd_collector_acquire(); + rrd_collector_release(old_rdc); + changed = true; + } + + if(rdcf->function != new_rdcf->function) { + rdcf->function = new_rdcf->function; + changed = true; + } + + if(rdcf->help != new_rdcf->help) { + STRING *old = rdcf->help; + rdcf->help = new_rdcf->help; + string_freez(old); + changed = true; + } + else + string_freez(new_rdcf->help); + + if(rdcf->timeout != new_rdcf->timeout) { + rdcf->timeout = new_rdcf->timeout; + changed = true; + } + + if(rdcf->sync != new_rdcf->sync) { + rdcf->sync = new_rdcf->sync; + changed = true; + } + + if(rdcf->collector_data != new_rdcf->collector_data) { + rdcf->collector_data = new_rdcf->collector_data; + changed = true; + } + + return changed; +} + + +void rrdfunctions_init(RRDHOST *host) { + if(host->functions) return; + + host->functions = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + dictionary_register_insert_callback(host->functions, rrd_functions_insert_callback, host); + dictionary_register_delete_callback(host->functions, rrd_functions_delete_callback, host); + dictionary_register_conflict_callback(host->functions, rrd_functions_conflict_callback, host); +} + +void rrdfunctions_destroy(RRDHOST *host) { + dictionary_destroy(host->functions); +} + +void rrd_collector_add_function(RRDHOST *host, RRDSET *st, const char *name, int timeout, const char *help, + bool sync, function_execute_at_collector function, void *collector_data) { + + // RRDSET *st may be NULL in this function + // to create a GLOBAL function + + if(st && !st->functions_view) + st->functions_view = dictionary_create_view(host->functions); + + char key[PLUGINSD_LINE_MAX + 1]; + sanitize_function_text(key, name, PLUGINSD_LINE_MAX); + + struct rrd_collector_function tmp = { + .sync = sync, + .timeout = timeout, + .options = (st)?RRD_FUNCTION_LOCAL:RRD_FUNCTION_GLOBAL, + .function = function, + .collector_data = collector_data, + .help = string_strdupz(help), + }; + const DICTIONARY_ITEM *item = dictionary_set_and_acquire_item(host->functions, key, &tmp, sizeof(tmp)); + + if(st) + dictionary_view_set(st->functions_view, key, item); + + dictionary_acquired_item_release(host->functions, item); +} + +void rrd_functions_expose_rrdpush(RRDSET *st, BUFFER *wb) { + if(!st->functions_view) + return; + + struct rrd_collector_function *tmp; + dfe_start_read(st->functions_view, tmp) { + buffer_sprintf(wb + , PLUGINSD_KEYWORD_FUNCTION " \"%s\" %d \"%s\"\n" + , tmp_dfe.name + , tmp->timeout + , string2str(tmp->help) + ); + } + dfe_done(tmp); +} + +struct rrd_function_call_wait { + bool free_with_signal; + bool data_are_ready; + netdata_mutex_t mutex; + pthread_cond_t cond; + int code; +}; + +static void rrd_function_call_wait_free(struct rrd_function_call_wait *tmp) { + pthread_cond_destroy(&tmp->cond); + netdata_mutex_destroy(&tmp->mutex); + freez(tmp); +} + +struct { + const char *format; + uint8_t content_type; +} function_formats[] = { + { .format = "application/json", CT_APPLICATION_JSON }, + { .format = "text/plain", CT_TEXT_PLAIN }, + { .format = "application/xml", CT_APPLICATION_XML }, + { .format = "prometheus", CT_PROMETHEUS }, + { .format = "text", CT_TEXT_PLAIN }, + { .format = "txt", CT_TEXT_PLAIN }, + { .format = "json", CT_APPLICATION_JSON }, + { .format = "html", CT_TEXT_HTML }, + { .format = "text/html", CT_TEXT_HTML }, + { .format = "xml", CT_APPLICATION_XML }, + + // terminator + { .format = NULL, CT_TEXT_PLAIN }, +}; + +uint8_t functions_format_to_content_type(const char *format) { + if(format && *format) { + for (int i = 0; function_formats[i].format; i++) + if (strcmp(function_formats[i].format, format) == 0) + return function_formats[i].content_type; + } + + return CT_TEXT_PLAIN; +} + +const char *functions_content_type_to_format(uint8_t content_type) { + for (int i = 0; function_formats[i].format; i++) + if (function_formats[i].content_type == content_type) + return function_formats[i].format; + + return "text/plain"; +} + +int rrd_call_function_error(BUFFER *wb, const char *msg, int code) { + char buffer[PLUGINSD_LINE_MAX]; + json_escape_string(buffer, msg, PLUGINSD_LINE_MAX); + + buffer_flush(wb); + buffer_sprintf(wb, "{\"status\":%d,\"error_message\":\"%s\"}", code, buffer); + wb->contenttype = CT_APPLICATION_JSON; + buffer_no_cacheable(wb); + return code; +} + +static int rrd_call_function_find(RRDHOST *host, BUFFER *wb, const char *name, size_t key_length, struct rrd_collector_function **rdcf) { + char buffer[MAX_FUNCTION_LENGTH + 1]; + + strncpyz(buffer, name, MAX_FUNCTION_LENGTH); + char *s = NULL; + + *rdcf = NULL; + while(!(*rdcf) && buffer[0]) { + *rdcf = dictionary_get(host->functions, buffer); + if(*rdcf) break; + + // if s == NULL, set it to the end of the buffer + // this should happen only the first time + if(unlikely(!s)) + s = &buffer[key_length - 1]; + + // skip a word from the end + while(s >= buffer && !isspace(*s)) *s-- = '\0'; + + // skip all spaces + while(s >= buffer && isspace(*s)) *s-- = '\0'; + } + + buffer_flush(wb); + + if(!(*rdcf)) + return rrd_call_function_error(wb, "No collector is supplying this function on this host at this time.", HTTP_RESP_NOT_FOUND); + + if(!(*rdcf)->collector->running) + return rrd_call_function_error(wb, "The collector that registered this function, is not currently running.", HTTP_RESP_BACKEND_FETCH_FAILED); + + return HTTP_RESP_OK; +} + +static void rrd_call_function_signal_when_ready(BUFFER *temp_wb __maybe_unused, int code, void *callback_data) { + struct rrd_function_call_wait *tmp = callback_data; + bool we_should_free = false; + + netdata_mutex_lock(&tmp->mutex); + + // since we got the mutex, + // the waiting thread is either in pthread_cond_timedwait() + // or gave up and left. + + tmp->code = code; + tmp->data_are_ready = true; + + if(tmp->free_with_signal) + we_should_free = true; + + pthread_cond_signal(&tmp->cond); + + netdata_mutex_unlock(&tmp->mutex); + + if(we_should_free) { + buffer_free(temp_wb); + rrd_function_call_wait_free(tmp); + } +} + +int rrd_call_function_and_wait(RRDHOST *host, BUFFER *wb, int timeout, const char *name) { + int code; + + struct rrd_collector_function *rdcf = NULL; + + char key[PLUGINSD_LINE_MAX + 1]; + size_t key_length = sanitize_function_text(key, name, PLUGINSD_LINE_MAX); + code = rrd_call_function_find(host, wb, key, key_length, &rdcf); + if(code != HTTP_RESP_OK) + return code; + + if(timeout <= 0) + timeout = rdcf->timeout; + + struct timespec tp; + clock_gettime(CLOCK_REALTIME, &tp); + tp.tv_sec += (time_t)timeout; + + if(rdcf->sync) { + code = rdcf->function(wb, timeout, key, rdcf->collector_data, NULL, NULL); + } + else { + struct rrd_function_call_wait *tmp = mallocz(sizeof(struct rrd_function_call_wait)); + tmp->free_with_signal = false; + tmp->data_are_ready = false; + netdata_mutex_init(&tmp->mutex); + pthread_cond_init(&tmp->cond, NULL); + + bool we_should_free = true; + BUFFER *temp_wb = buffer_create(PLUGINSD_LINE_MAX + 1); // we need it because we may give up on it + temp_wb->contenttype = wb->contenttype; + code = rdcf->function(temp_wb, timeout, key, rdcf->collector_data, rrd_call_function_signal_when_ready, tmp); + if (code == HTTP_RESP_OK) { + netdata_mutex_lock(&tmp->mutex); + + int rc = 0; + while (rc == 0 && !tmp->data_are_ready) { + // the mutex is unlocked within pthread_cond_timedwait() + rc = pthread_cond_timedwait(&tmp->cond, &tmp->mutex, &tp); + // the mutex is again ours + } + + if (tmp->data_are_ready) { + // we have a response + buffer_fast_strcat(wb, buffer_tostring(temp_wb), buffer_strlen(temp_wb)); + wb->contenttype = temp_wb->contenttype; + wb->expires = temp_wb->expires; + + if(wb->expires) + buffer_cacheable(wb); + else + buffer_no_cacheable(wb); + + code = tmp->code; + } + else if (rc == ETIMEDOUT) { + // timeout + // we will go away and let the callback free the structure + tmp->free_with_signal = true; + we_should_free = false; + code = rrd_call_function_error(wb, "Timeout while waiting for a response from the collector.", HTTP_RESP_GATEWAY_TIMEOUT); + } + else + code = rrd_call_function_error(wb, "Failed to get the response from the collector.", HTTP_RESP_INTERNAL_SERVER_ERROR); + + netdata_mutex_unlock(&tmp->mutex); + } + else { + buffer_free(temp_wb); + if(!buffer_strlen(wb)) + rrd_call_function_error(wb, "Failed to send request to the collector.", code); + } + + if (we_should_free) + rrd_function_call_wait_free(tmp); + } + + return code; +} + +int rrd_call_function_async(RRDHOST *host, BUFFER *wb, int timeout, const char *name, + rrd_call_function_async_callback callback, void *callback_data) { + int code; + + struct rrd_collector_function *rdcf = NULL; + char key[PLUGINSD_LINE_MAX + 1]; + size_t key_length = sanitize_function_text(key, name, PLUGINSD_LINE_MAX); + code = rrd_call_function_find(host, wb, key, key_length, &rdcf); + if(code != HTTP_RESP_OK) + return code; + + if(timeout <= 0) + timeout = rdcf->timeout; + + code = rdcf->function(wb, timeout, key, rdcf->collector_data, callback, callback_data); + + if(code != HTTP_RESP_OK) { + if (!buffer_strlen(wb)) + rrd_call_function_error(wb, "Failed to send request to the collector.", code); + } + + return code; +} + +static void functions2json(DICTIONARY *functions, BUFFER *wb, const char *ident, const char *kq, const char *sq) { + struct rrd_collector_function *t; + dfe_start_read(functions, t) { + if(!t->collector->running) continue; + + if(t_dfe.counter) + buffer_strcat(wb, ",\n"); + + buffer_sprintf(wb, "%s%s%s%s: {", ident, kq, t_dfe.name, kq); + buffer_sprintf(wb, "\n\t%s%shelp%s: %s%s%s", ident, kq, kq, sq, string2str(t->help), sq); + buffer_sprintf(wb, ",\n\t%s%stimeout%s: %d", ident, kq, kq, t->timeout); + buffer_sprintf(wb, ",\n\t%s%soptions%s: \"%s%s\"", ident, kq, kq + , (t->options & RRD_FUNCTION_LOCAL)?"LOCAL ":"" + , (t->options & RRD_FUNCTION_GLOBAL)?"GLOBAL ":"" + ); + buffer_sprintf(wb, "\n%s}", ident); + } + dfe_done(t); + buffer_strcat(wb, "\n"); +} + +void chart_functions2json(RRDSET *st, BUFFER *wb, int tabs, const char *kq, const char *sq) { + if(!st || !st->functions_view) return; + + char ident[tabs + 1]; + ident[tabs] = '\0'; + while(tabs) ident[--tabs] = '\t'; + + functions2json(st->functions_view, wb, ident, kq, sq); +} + +void host_functions2json(RRDHOST *host, BUFFER *wb, int tabs, const char *kq, const char *sq) { + if(!host || !host->functions) return; + + char ident[tabs + 1]; + ident[tabs] = '\0'; + while(tabs) ident[--tabs] = '\t'; + + functions2json(host->functions, wb, ident, kq, sq); +} + +void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst) { + if(!rrdset_functions_view || !dst) return; + + struct rrd_collector_function *t; + dfe_start_read(rrdset_functions_view, t) { + if(!t->collector->running) continue; + + dictionary_set(dst, t_dfe.name, NULL, 0); + } + dfe_done(t); +} diff --git a/database/rrdfunctions.h b/database/rrdfunctions.h new file mode 100644 index 000000000..f031ec34d --- /dev/null +++ b/database/rrdfunctions.h @@ -0,0 +1,35 @@ +#ifndef NETDATA_RRDFUNCTIONS_H +#define NETDATA_RRDFUNCTIONS_H 1 + +#include "rrd.h" + +void rrdfunctions_init(RRDHOST *host); +void rrdfunctions_destroy(RRDHOST *host); + +void rrd_collector_started(void); +void rrd_collector_finished(void); + +typedef void (*function_data_ready_callback)(BUFFER *wb, int code, void *callback_data); + +typedef int (*function_execute_at_collector)(BUFFER *wb, int timeout, const char *function, void *collector_data, + function_data_ready_callback callback, void *callback_data); + +void rrd_collector_add_function(RRDHOST *host, RRDSET *st, const char *name, int timeout, const char *help, + bool sync, function_execute_at_collector function, void *collector_data); + +int rrd_call_function_and_wait(RRDHOST *host, BUFFER *wb, int timeout, const char *name); + +typedef void (*rrd_call_function_async_callback)(BUFFER *wb, int code, void *callback_data); +int rrd_call_function_async(RRDHOST *host, BUFFER *wb, int timeout, const char *name, rrd_call_function_async_callback, void *callback_data); + +void rrd_functions_expose_rrdpush(RRDSET *st, BUFFER *wb); + +void chart_functions2json(RRDSET *st, BUFFER *wb, int tabs, const char *kq, const char *sq); +void chart_functions_to_dict(DICTIONARY *rrdset_functions_view, DICTIONARY *dst); +void host_functions2json(RRDHOST *host, BUFFER *wb, int tabs, const char *kq, const char *sq); + +uint8_t functions_format_to_content_type(const char *format); +const char *functions_content_type_to_format(uint8_t content_type); +int rrd_call_function_error(BUFFER *wb, const char *msg, int code); + +#endif // NETDATA_RRDFUNCTIONS_H diff --git a/database/rrdhost.c b/database/rrdhost.c index 7f4bd95ba..5ba13d47b 100644 --- a/database/rrdhost.c +++ b/database/rrdhost.c @@ -3,21 +3,21 @@ #define NETDATA_RRD_INTERNALS #include "rrd.h" -int storage_tiers = 1; -int storage_tiers_grouping_iterations[RRD_STORAGE_TIERS] = { 1, 60, 60, 60, 60 }; +bool dbengine_enabled = false; // will become true if and when dbengine is initialized +size_t storage_tiers = 3; +size_t storage_tiers_grouping_iterations[RRD_STORAGE_TIERS] = { 1, 60, 60, 60, 60 }; RRD_BACKFILL storage_tiers_backfill[RRD_STORAGE_TIERS] = { RRD_BACKFILL_NEW, RRD_BACKFILL_NEW, RRD_BACKFILL_NEW, RRD_BACKFILL_NEW, RRD_BACKFILL_NEW }; #if RRD_STORAGE_TIERS != 5 #error RRD_STORAGE_TIERS is not 5 - you need to update the grouping iterations per tier #endif -int get_tier_grouping(int tier) { +size_t get_tier_grouping(size_t tier) { if(unlikely(tier >= storage_tiers)) tier = storage_tiers - 1; - if(unlikely(tier < 0)) tier = 0; - int grouping = 1; + size_t grouping = 1; // first tier is always 1 iteration of whatever update every the chart has - for(int i = 1; i <= tier ;i++) + for(size_t i = 1; i <= tier ;i++) grouping *= storage_tiers_grouping_iterations[i]; return grouping; @@ -32,7 +32,7 @@ time_t rrdhost_free_orphan_time = 3600; bool is_storage_engine_shared(STORAGE_INSTANCE *engine) { #ifdef ENABLE_DBENGINE - for(int tier = 0; tier < storage_tiers ;tier++) { + for(size_t tier = 0; tier < storage_tiers ;tier++) { if (engine == (STORAGE_INSTANCE *)multidb_ctx[tier]) return true; } @@ -43,107 +43,144 @@ bool is_storage_engine_shared(STORAGE_INSTANCE *engine) { // ---------------------------------------------------------------------------- -// RRDHOST index +// RRDHOST indexes management -int rrdhost_compare(void* a, void* b) { - if(((RRDHOST *)a)->hash_machine_guid < ((RRDHOST *)b)->hash_machine_guid) return -1; - else if(((RRDHOST *)a)->hash_machine_guid > ((RRDHOST *)b)->hash_machine_guid) return 1; - else return strcmp(((RRDHOST *)a)->machine_guid, ((RRDHOST *)b)->machine_guid); +DICTIONARY *rrdhost_root_index = NULL; +static DICTIONARY *rrdhost_root_index_hostname = NULL; + +static inline void rrdhost_init() { + if(unlikely(!rrdhost_root_index)) { + rrdhost_root_index = dictionary_create( + DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE); + } + + if(unlikely(!rrdhost_root_index_hostname)) { + rrdhost_root_index_hostname = dictionary_create( + DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE); + } } -avl_tree_lock rrdhost_root_index = { - .avl_tree = { NULL, rrdhost_compare }, - .rwlock = AVL_LOCK_INITIALIZER -}; +// ---------------------------------------------------------------------------- +// RRDHOST index by UUID + +inline long rrdhost_hosts_available(void) { + return dictionary_entries(rrdhost_root_index); +} -RRDHOST *rrdhost_find_by_guid(const char *guid, uint32_t hash) { - debug(D_RRDHOST, "Searching in index for host with guid '%s'", guid); +inline RRDHOST *rrdhost_find_by_guid(const char *guid) { + return dictionary_get(rrdhost_root_index, guid); +} - RRDHOST tmp; - strncpyz(tmp.machine_guid, guid, GUID_LEN); - tmp.hash_machine_guid = (hash)?hash:simple_hash(tmp.machine_guid); +static inline RRDHOST *rrdhost_index_add_by_guid(RRDHOST *host) { + RRDHOST *ret_machine_guid = dictionary_set(rrdhost_root_index, host->machine_guid, host, sizeof(RRDHOST)); + if(ret_machine_guid == host) + rrdhost_option_set(host, RRDHOST_OPTION_INDEXED_MACHINE_GUID); + else { + rrdhost_option_clear(host, RRDHOST_OPTION_INDEXED_MACHINE_GUID); + error("RRDHOST: %s() host with machine guid '%s' is already indexed", __FUNCTION__, host->machine_guid); + } - return (RRDHOST *)avl_search_lock(&(rrdhost_root_index), (avl_t *) &tmp); + return host; +} + +static void rrdhost_index_del_by_guid(RRDHOST *host) { + if(rrdhost_option_check(host, RRDHOST_OPTION_INDEXED_MACHINE_GUID)) { + if(!dictionary_del(rrdhost_root_index, host->machine_guid)) + error("RRDHOST: %s() failed to delete machine guid '%s' from index", __FUNCTION__, host->machine_guid); + + rrdhost_option_clear(host, RRDHOST_OPTION_INDEXED_MACHINE_GUID); + } } -RRDHOST *rrdhost_find_by_hostname(const char *hostname, uint32_t hash) { +// ---------------------------------------------------------------------------- +// RRDHOST index by hostname + +inline RRDHOST *rrdhost_find_by_hostname(const char *hostname) { if(unlikely(!strcmp(hostname, "localhost"))) return localhost; - if(unlikely(!hash)) hash = simple_hash(hostname); + return dictionary_get(rrdhost_root_index_hostname, hostname); +} - rrd_rdlock(); - RRDHOST *host; - rrdhost_foreach_read(host) { - if(unlikely((hash == host->hash_hostname && !strcmp(hostname, host->hostname)))) { - rrd_unlock(); - return host; - } +static inline RRDHOST *rrdhost_index_add_hostname(RRDHOST *host) { + if(!host->hostname) return host; + + RRDHOST *ret_hostname = dictionary_set(rrdhost_root_index_hostname, rrdhost_hostname(host), host, sizeof(RRDHOST)); + if(ret_hostname == host) + rrdhost_option_set(host, RRDHOST_OPTION_INDEXED_HOSTNAME); + else { + rrdhost_option_clear(host, RRDHOST_OPTION_INDEXED_HOSTNAME); + error("RRDHOST: %s() host with hostname '%s' is already indexed", __FUNCTION__, rrdhost_hostname(host)); } - rrd_unlock(); - return NULL; + return host; } -#define rrdhost_index_add(rrdhost) (RRDHOST *)avl_insert_lock(&(rrdhost_root_index), (avl_t *)(rrdhost)) -#define rrdhost_index_del(rrdhost) (RRDHOST *)avl_remove_lock(&(rrdhost_root_index), (avl_t *)(rrdhost)) +static inline void rrdhost_index_del_hostname(RRDHOST *host) { + if(unlikely(!host->hostname)) return; + + if(rrdhost_option_check(host, RRDHOST_OPTION_INDEXED_HOSTNAME)) { + if(!dictionary_del(rrdhost_root_index_hostname, rrdhost_hostname(host))) + error("RRDHOST: %s() failed to delete hostname '%s' from index", __FUNCTION__, rrdhost_hostname(host)); + rrdhost_option_clear(host, RRDHOST_OPTION_INDEXED_HOSTNAME); + } +} // ---------------------------------------------------------------------------- // RRDHOST - internal helpers static inline void rrdhost_init_tags(RRDHOST *host, const char *tags) { - if(host->tags && tags && !strcmp(host->tags, tags)) + if(host->tags && tags && !strcmp(rrdhost_tags(host), tags)) return; - void *old = (void *)host->tags; - host->tags = (tags && *tags)?strdupz(tags):NULL; - freez(old); + STRING *old = host->tags; + host->tags = string_strdupz((tags && *tags)?tags:NULL); + string_freez(old); } static inline void rrdhost_init_hostname(RRDHOST *host, const char *hostname) { - if(host->hostname && hostname && !strcmp(host->hostname, hostname)) + if(unlikely(hostname && !*hostname)) hostname = NULL; + + if(host->hostname && hostname && !strcmp(rrdhost_hostname(host), hostname)) return; - void *old = host->hostname; - host->hostname = strdupz(hostname?hostname:"localhost"); - host->hash_hostname = simple_hash(host->hostname); - freez(old); + rrdhost_index_del_hostname(host); + + STRING *old = host->hostname; + host->hostname = string_strdupz(hostname?hostname:"localhost"); + string_freez(old); + + rrdhost_index_add_hostname(host); } static inline void rrdhost_init_os(RRDHOST *host, const char *os) { - if(host->os && os && !strcmp(host->os, os)) + if(host->os && os && !strcmp(rrdhost_os(host), os)) return; - void *old = (void *)host->os; - host->os = strdupz(os?os:"unknown"); - freez(old); + STRING *old = host->os; + host->os = string_strdupz(os?os:"unknown"); + string_freez(old); } static inline void rrdhost_init_timezone(RRDHOST *host, const char *timezone, const char *abbrev_timezone, int32_t utc_offset) { - if (host->timezone && timezone && !strcmp(host->timezone, timezone) && host->abbrev_timezone && abbrev_timezone && - !strcmp(host->abbrev_timezone, abbrev_timezone) && host->utc_offset == utc_offset) + if (host->timezone && timezone && !strcmp(rrdhost_timezone(host), timezone) && host->abbrev_timezone && abbrev_timezone && + !strcmp(rrdhost_abbrev_timezone(host), abbrev_timezone) && host->utc_offset == utc_offset) return; - void *old = (void *)host->timezone; - host->timezone = strdupz((timezone && *timezone)?timezone:"unknown"); - freez(old); + STRING *old = host->timezone; + host->timezone = string_strdupz((timezone && *timezone)?timezone:"unknown"); + string_freez(old); old = (void *)host->abbrev_timezone; - host->abbrev_timezone = strdupz((abbrev_timezone && *abbrev_timezone) ? abbrev_timezone : "UTC"); - freez(old); + host->abbrev_timezone = string_strdupz((abbrev_timezone && *abbrev_timezone) ? abbrev_timezone : "UTC"); + string_freez(old); host->utc_offset = utc_offset; } -static inline void rrdhost_init_machine_guid(RRDHOST *host, const char *machine_guid) { - strncpy(host->machine_guid, machine_guid, GUID_LEN); - host->machine_guid[GUID_LEN] = '\0'; - host->hash_machine_guid = simple_hash(host->machine_guid); -} - -void set_host_properties(RRDHOST *host, int update_every, RRD_MEMORY_MODE memory_mode, const char *hostname, - const char *registry_hostname, const char *guid, const char *os, const char *tags, +void set_host_properties(RRDHOST *host, int update_every, RRD_MEMORY_MODE memory_mode, + const char *registry_hostname, const char *os, const char *tags, const char *tzone, const char *abbrev_tzone, int32_t utc_offset, const char *program_name, const char *program_version) { @@ -151,23 +188,48 @@ void set_host_properties(RRDHOST *host, int update_every, RRD_MEMORY_MODE memory host->rrd_update_every = update_every; host->rrd_memory_mode = memory_mode; - rrdhost_init_hostname(host, hostname); - - rrdhost_init_machine_guid(host, guid); - rrdhost_init_os(host, os); rrdhost_init_timezone(host, tzone, abbrev_tzone, utc_offset); rrdhost_init_tags(host, tags); - host->program_name = strdupz((program_name && *program_name) ? program_name : "unknown"); - host->program_version = strdupz((program_version && *program_version) ? program_version : "unknown"); - - host->registry_hostname = strdupz((registry_hostname && *registry_hostname) ? registry_hostname : host->hostname); + host->program_name = string_strdupz((program_name && *program_name) ? program_name : "unknown"); + host->program_version = string_strdupz((program_version && *program_version) ? program_version : "unknown"); + host->registry_hostname = string_strdupz((registry_hostname && *registry_hostname) ? registry_hostname : rrdhost_hostname(host)); } // ---------------------------------------------------------------------------- // RRDHOST - add a host +static void rrdhost_initialize_rrdpush_sender(RRDHOST *host, + unsigned int rrdpush_enabled, + char *rrdpush_destination, + char *rrdpush_api_key, + char *rrdpush_send_charts_matching +) { + if(rrdhost_flag_check(host, RRDHOST_FLAG_RRDPUSH_SENDER_INITIALIZED)) return; + + if(rrdpush_enabled && rrdpush_destination && *rrdpush_destination && rrdpush_api_key && *rrdpush_api_key) { + rrdhost_flag_set(host, RRDHOST_FLAG_RRDPUSH_SENDER_INITIALIZED); + + sender_init(host); + +#ifdef ENABLE_HTTPS + host->sender->ssl.conn = NULL; + host->sender->ssl.flags = NETDATA_SSL_START; +#endif + + host->rrdpush_send_destination = strdupz(rrdpush_destination); + rrdpush_destinations_init(host); + + host->rrdpush_send_api_key = strdupz(rrdpush_api_key); + host->rrdpush_send_charts_matching = simple_pattern_create(rrdpush_send_charts_matching, NULL, SIMPLE_PATTERN_EXACT); + + rrdhost_option_set(host, RRDHOST_OPTION_SENDER_ENABLED); + } + else + rrdhost_option_clear(host, RRDHOST_OPTION_SENDER_ENABLED); +} + RRDHOST *rrdhost_create(const char *hostname, const char *registry_hostname, const char *guid, @@ -186,188 +248,136 @@ RRDHOST *rrdhost_create(const char *hostname, char *rrdpush_destination, char *rrdpush_api_key, char *rrdpush_send_charts_matching, + bool rrdpush_enable_replication, + time_t rrdpush_seconds_to_replicate, + time_t rrdpush_replication_step, struct rrdhost_system_info *system_info, int is_localhost, bool archived ) { debug(D_RRDHOST, "Host '%s': adding with guid '%s'", hostname, guid); + rrd_check_wrlock(); + + if(memory_mode == RRD_MEMORY_MODE_DBENGINE && !dbengine_enabled) { + error("memory mode 'dbengine' is not enabled, but host '%s' is configured for it. Falling back to 'alloc'", hostname); + memory_mode = RRD_MEMORY_MODE_ALLOC; + } + #ifdef ENABLE_DBENGINE int is_legacy = (memory_mode == RRD_MEMORY_MODE_DBENGINE) && is_legacy_child(guid); #else - int is_legacy = 1; +int is_legacy = 1; #endif - rrd_check_wrlock(); int is_in_multihost = (memory_mode == RRD_MEMORY_MODE_DBENGINE && !is_legacy); RRDHOST *host = callocz(1, sizeof(RRDHOST)); - set_host_properties(host, (update_every > 0)?update_every:1, memory_mode, hostname, registry_hostname, guid, os, + strncpyz(host->machine_guid, guid, GUID_LEN + 1); + + set_host_properties(host, (update_every > 0)?update_every:1, memory_mode, registry_hostname, os, tags, timezone, abbrev_timezone, utc_offset, program_name, program_version); + rrdhost_init_hostname(host, hostname); + host->rrd_history_entries = align_entries_to_pagesize(memory_mode, entries); host->health_enabled = ((memory_mode == RRD_MEMORY_MODE_NONE)) ? 0 : health_enabled; - sender_init(host); - netdata_mutex_init(&host->receiver_lock); - - host->rrdpush_send_enabled = (rrdpush_enabled && rrdpush_destination && *rrdpush_destination && rrdpush_api_key && *rrdpush_api_key) ? 1 : 0; - host->rrdpush_send_destination = (host->rrdpush_send_enabled)?strdupz(rrdpush_destination):NULL; - if (host->rrdpush_send_destination) - host->destinations = destinations_init(host->rrdpush_send_destination); - host->rrdpush_send_api_key = (host->rrdpush_send_enabled)?strdupz(rrdpush_api_key):NULL; - host->rrdpush_send_charts_matching = simple_pattern_create(rrdpush_send_charts_matching, NULL, SIMPLE_PATTERN_EXACT); + if (likely(!archived)) { + rrdfunctions_init(host); + host->rrdlabels = rrdlabels_create(); + rrdhost_initialize_rrdpush_sender( + host, rrdpush_enabled, rrdpush_destination, rrdpush_api_key, rrdpush_send_charts_matching); + } - host->rrdpush_sender_pipe[0] = -1; - host->rrdpush_sender_pipe[1] = -1; - host->rrdpush_sender_socket = -1; + if(rrdpush_enable_replication) + rrdhost_option_set(host, RRDHOST_OPTION_REPLICATION); + else + rrdhost_option_clear(host, RRDHOST_OPTION_REPLICATION); + + host->rrdpush_seconds_to_replicate = rrdpush_seconds_to_replicate; + host->rrdpush_replication_step = rrdpush_replication_step; + + switch(memory_mode) { + default: + case RRD_MEMORY_MODE_ALLOC: + case RRD_MEMORY_MODE_MAP: + case RRD_MEMORY_MODE_SAVE: + case RRD_MEMORY_MODE_RAM: + if(host->rrdpush_seconds_to_replicate > host->rrd_history_entries * host->rrd_update_every) + host->rrdpush_seconds_to_replicate = host->rrd_history_entries * host->rrd_update_every; + break; - //host->stream_version = STREAMING_PROTOCOL_CURRENT_VERSION; Unused? -#ifdef ENABLE_HTTPS - host->ssl.conn = NULL; - host->ssl.flags = NETDATA_SSL_START; - host->stream_ssl.conn = NULL; - host->stream_ssl.flags = NETDATA_SSL_START; -#endif + case RRD_MEMORY_MODE_DBENGINE: + break; + } netdata_rwlock_init(&host->rrdhost_rwlock); - host->host_labels = rrdlabels_create(); - netdata_mutex_init(&host->aclk_state_lock); + netdata_mutex_init(&host->receiver_lock); host->system_info = system_info; - avl_init_lock(&(host->rrdset_root_index), rrdset_compare); - avl_init_lock(&(host->rrdset_root_index_name), rrdset_compare_name); - avl_init_lock(&(host->rrdfamily_root_index), rrdfamily_compare); - avl_init_lock(&(host->rrdvar_root_index), rrdvar_compare); + rrdset_index_init(host); if(config_get_boolean(CONFIG_SECTION_DB, "delete obsolete charts files", 1)) - rrdhost_flag_set(host, RRDHOST_FLAG_DELETE_OBSOLETE_CHARTS); + rrdhost_option_set(host, RRDHOST_OPTION_DELETE_OBSOLETE_CHARTS); if(config_get_boolean(CONFIG_SECTION_DB, "delete orphan hosts files", 1) && !is_localhost) - rrdhost_flag_set(host, RRDHOST_FLAG_DELETE_ORPHAN_HOST); - - host->health_default_warn_repeat_every = config_get_duration(CONFIG_SECTION_HEALTH, "default repeat warning", "never"); - host->health_default_crit_repeat_every = config_get_duration(CONFIG_SECTION_HEALTH, "default repeat critical", "never"); - avl_init_lock(&(host->alarms_idx_health_log), alarm_compare_id); - avl_init_lock(&(host->alarms_idx_name), alarm_compare_name); - - // ------------------------------------------------------------------------ - // initialize health variables - - host->health_log.next_log_id = 1; - host->health_log.next_alarm_id = 1; - host->health_log.max = 1000; - host->health_log.next_log_id = (uint32_t)now_realtime_sec(); - host->health_log.next_alarm_id = 0; - - long n = config_get_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", host->health_log.max); - if(n < 10) { - error("Host '%s': health configuration has invalid max log entries %ld. Using default %u", host->hostname, n, host->health_log.max); - config_set_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", (long)host->health_log.max); - } - else - host->health_log.max = (unsigned int)n; - - netdata_rwlock_init(&host->health_log.alarm_log_rwlock); + rrdhost_option_set(host, RRDHOST_OPTION_DELETE_ORPHAN_HOST); char filename[FILENAME_MAX + 1]; - if(is_localhost) { - host->cache_dir = strdupz(netdata_configured_cache_dir); host->varlib_dir = strdupz(netdata_configured_varlib_dir); - } else { // this is not localhost - append our GUID to localhost path if (is_in_multihost) { // don't append to cache dir in multihost host->cache_dir = strdupz(netdata_configured_cache_dir); - } else { + } + else { snprintfz(filename, FILENAME_MAX, "%s/%s", netdata_configured_cache_dir, host->machine_guid); host->cache_dir = strdupz(filename); } - if((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || ( - host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_legacy))) { + if((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || + (host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_legacy))) { int r = mkdir(host->cache_dir, 0775); if(r != 0 && errno != EEXIST) - error("Host '%s': cannot create directory '%s'", host->hostname, host->cache_dir); + error("Host '%s': cannot create directory '%s'", rrdhost_hostname(host), host->cache_dir); } snprintfz(filename, FILENAME_MAX, "%s/%s", netdata_configured_varlib_dir, host->machine_guid); host->varlib_dir = strdupz(filename); - - if(host->health_enabled) { - int r = mkdir(host->varlib_dir, 0775); - if(r != 0 && errno != EEXIST) - error("Host '%s': cannot create directory '%s'", host->hostname, host->varlib_dir); - } - - } - - if(host->health_enabled) { - snprintfz(filename, FILENAME_MAX, "%s/health", host->varlib_dir); - int r = mkdir(filename, 0775); - if(r != 0 && errno != EEXIST) - error("Host '%s': cannot create directory '%s'", host->hostname, filename); - } - - snprintfz(filename, FILENAME_MAX, "%s/health/health-log.db", host->varlib_dir); - host->health_log_filename = strdupz(filename); - - snprintfz(filename, FILENAME_MAX, "%s/alarm-notify.sh", netdata_configured_primary_plugins_dir); - host->health_default_exec = strdupz(config_get(CONFIG_SECTION_HEALTH, "script to execute on alarm", filename)); - host->health_default_recipient = strdupz("root"); - - - // ------------------------------------------------------------------------ - // load health configuration - - if(host->health_enabled) { - rrdhost_wrlock(host); - health_readdir(host, health_user_config_dir(), health_stock_config_dir(), NULL); - rrdhost_unlock(host); } - RRDHOST *t = rrdhost_index_add(host); + // this is also needed for custom host variables - not only health + if(!host->rrdvars) + host->rrdvars = rrdvariables_create(); + RRDHOST *t = rrdhost_index_add_by_guid(host); if(t != host) { - error("Host '%s': cannot add host with machine guid '%s' to index. It already exists as host '%s' with machine guid '%s'.", host->hostname, host->machine_guid, t->hostname, t->machine_guid); + error("Host '%s': cannot add host with machine guid '%s' to index. It already exists as host '%s' with machine guid '%s'.", rrdhost_hostname(host), host->machine_guid, rrdhost_hostname(t), t->machine_guid); rrdhost_free(host, 1); return NULL; } if (likely(!uuid_parse(host->machine_guid, host->host_uuid))) { - int rc; - if (!archived) { - rc = sql_store_host_info(host); - if (unlikely(rc)) - error_report("Failed to store machine GUID to the database"); - } + if(!archived) + metaqueue_host_update_info(host->machine_guid); sql_load_node_id(host); - if (host->health_enabled) { - if (!file_is_migrated(host->health_log_filename)) { - rc = sql_create_health_log_table(host); - if (unlikely(rc)) { - error_report("Failed to create health log table in the database"); - health_alarm_log_load(host); - health_alarm_log_open(host); - } - else { - health_alarm_log_load(host); - add_migrated_file(host->health_log_filename, 0); - } - } else { - sql_create_health_log_table(host); - sql_health_alarm_log_load(host); - } - } } else error_report("Host machine GUID %s is not valid", host->machine_guid); + rrdfamily_index_init(host); + rrdcalctemplate_index_init(host); + rrdcalc_rrdhost_index_init(host); + + if (health_enabled) + health_thread_spawn(host); + if (host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { #ifdef ENABLE_DBENGINE char dbenginepath[FILENAME_MAX + 1]; @@ -376,14 +386,18 @@ RRDHOST *rrdhost_create(const char *hostname, snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine", host->cache_dir); ret = mkdir(dbenginepath, 0775); if (ret != 0 && errno != EEXIST) - error("Host '%s': cannot create directory '%s'", host->hostname, dbenginepath); + error("Host '%s': cannot create directory '%s'", rrdhost_hostname(host), dbenginepath); else ret = 0; // succeed if (is_legacy) { // initialize legacy dbengine instance as needed + host->db[0].mode = RRD_MEMORY_MODE_DBENGINE; + host->db[0].eng = storage_engine_get(host->db[0].mode); + host->db[0].tier_grouping = get_tier_grouping(0); + ret = rrdeng_init( host, - (struct rrdengine_instance **)&host->storage_instance[0], + (struct rrdengine_instance **)&host->db[0].instance, dbenginepath, default_rrdeng_page_cache_mb, default_rrdeng_disk_quota_mb, @@ -392,18 +406,26 @@ RRDHOST *rrdhost_create(const char *hostname, if(ret == 0) { // assign the rest of the shared storage instances to it // to allow them collect its metrics too - for(int tier = 1; tier < storage_tiers ; tier++) - host->storage_instance[tier] = (STORAGE_INSTANCE *)multidb_ctx[tier]; + for(size_t tier = 1; tier < storage_tiers ; tier++) { + host->db[tier].mode = RRD_MEMORY_MODE_DBENGINE; + host->db[tier].eng = storage_engine_get(host->db[tier].mode); + host->db[tier].instance = (STORAGE_INSTANCE *) multidb_ctx[tier]; + host->db[tier].tier_grouping = get_tier_grouping(tier); + } } } else { - for(int tier = 0; tier < storage_tiers ; tier++) - host->storage_instance[tier] = (STORAGE_INSTANCE *)multidb_ctx[tier]; + for(size_t tier = 0; tier < storage_tiers ; tier++) { + host->db[tier].mode = RRD_MEMORY_MODE_DBENGINE; + host->db[tier].eng = storage_engine_get(host->db[tier].mode); + host->db[tier].instance = (STORAGE_INSTANCE *)multidb_ctx[tier]; + host->db[tier].tier_grouping = get_tier_grouping(tier); + } } if (ret) { // check legacy or multihost initialization success error( "Host '%s': cannot initialize host with machine guid '%s'. Failed to initialize DB engine at '%s'.", - host->hostname, host->machine_guid, host->cache_dir); + rrdhost_hostname(host), host->machine_guid, host->cache_dir); rrdhost_free(host, 1); host = NULL; //rrd_hosts_available++; //TODO: maybe we want this? @@ -416,27 +438,29 @@ RRDHOST *rrdhost_create(const char *hostname, #endif } else { + host->db[0].mode = host->rrd_memory_mode; + host->db[0].eng = storage_engine_get(host->db[0].mode); + host->db[0].instance = NULL; + host->db[0].tier_grouping = get_tier_grouping(0); + #ifdef ENABLE_DBENGINE // the first tier is reserved for the non-dbengine modes - for(int tier = 1; tier < storage_tiers ; tier++) - host->storage_instance[tier] = (STORAGE_INSTANCE *)multidb_ctx[tier]; + for(size_t tier = 1; tier < storage_tiers ; tier++) { + host->db[tier].mode = RRD_MEMORY_MODE_DBENGINE; + host->db[tier].eng = storage_engine_get(host->db[tier].mode); + host->db[tier].instance = (STORAGE_INSTANCE *) multidb_ctx[tier]; + host->db[tier].tier_grouping = get_tier_grouping(tier); + } #endif } // ------------------------------------------------------------------------ // link it and add it to the index - if(is_localhost) { - host->next = localhost; - localhost = host; - } - else { - if(localhost) { - host->next = localhost->next; - localhost->next = host; - } - else localhost = host; - } + if(is_localhost) + DOUBLE_LINKED_LIST_PREPEND_UNSAFE(localhost, host, prev, next); + else + DOUBLE_LINKED_LIST_APPEND_UNSAFE(localhost, host, prev, next); // ------------------------------------------------------------------------ // init new ML host and update system_info to let upstreams know @@ -466,28 +490,29 @@ RRDHOST *rrdhost_create(const char *hostname, ", health_log '%s'" ", alarms default handler '%s'" ", alarms default recipient '%s'" - , host->hostname - , host->registry_hostname + , rrdhost_hostname(host) + , rrdhost_registry_hostname(host) , host->machine_guid - , host->os - , host->timezone - , (host->tags)?host->tags:"" - , host->program_name - , host->program_version + , rrdhost_os(host) + , rrdhost_timezone(host) + , rrdhost_tags(host) + , rrdhost_program_name(host) + , rrdhost_program_version(host) , host->rrd_update_every , rrd_memory_mode_name(host->rrd_memory_mode) , host->rrd_history_entries - , host->rrdpush_send_enabled?"enabled":"disabled" + , rrdhost_has_rrdpush_sender_enabled(host)?"enabled":"disabled" , host->rrdpush_send_destination?host->rrdpush_send_destination:"" , host->rrdpush_send_api_key?host->rrdpush_send_api_key:"" , host->health_enabled?"enabled":"disabled" , host->cache_dir , host->varlib_dir , host->health_log_filename - , host->health_default_exec - , host->health_default_recipient + , string2str(host->health_default_exec) + , string2str(host->health_default_recipient) ); - sql_store_host_system_info(&host->host_uuid, system_info); + if(!archived) + metaqueue_host_update_system_info(host); rrd_hosts_available++; @@ -496,6 +521,8 @@ RRDHOST *rrdhost_create(const char *hostname, ml_new_host(host); else rrdhost_flag_set(host, RRDHOST_FLAG_ARCHIVED); + + return host; } @@ -518,113 +545,97 @@ void rrdhost_update(RRDHOST *host , char *rrdpush_destination , char *rrdpush_api_key , char *rrdpush_send_charts_matching + , bool rrdpush_enable_replication + , time_t rrdpush_seconds_to_replicate + , time_t rrdpush_replication_step , struct rrdhost_system_info *system_info ) { UNUSED(guid); - UNUSED(rrdpush_enabled); - UNUSED(rrdpush_destination); - UNUSED(rrdpush_api_key); - UNUSED(rrdpush_send_charts_matching); host->health_enabled = (mode == RRD_MEMORY_MODE_NONE) ? 0 : health_enabled; - //host->stream_version = STREAMING_PROTOCOL_CURRENT_VERSION; Unused? rrdhost_system_info_free(host->system_info); host->system_info = system_info; - sql_store_host_system_info(&host->host_uuid, system_info); + metaqueue_host_update_system_info(host); rrdhost_init_os(host, os); rrdhost_init_timezone(host, timezone, abbrev_timezone, utc_offset); - freez(host->registry_hostname); - host->registry_hostname = strdupz((registry_hostname && *registry_hostname)?registry_hostname:hostname); + string_freez(host->registry_hostname); + host->registry_hostname = string_strdupz((registry_hostname && *registry_hostname)?registry_hostname:hostname); - if(strcmp(host->hostname, hostname) != 0) { - info("Host '%s' has been renamed to '%s'. If this is not intentional it may mean multiple hosts are using the same machine_guid.", host->hostname, hostname); - char *t = host->hostname; - host->hostname = strdupz(hostname); - host->hash_hostname = simple_hash(host->hostname); - freez(t); + if(strcmp(rrdhost_hostname(host), hostname) != 0) { + info("Host '%s' has been renamed to '%s'. If this is not intentional it may mean multiple hosts are using the same machine_guid.", rrdhost_hostname(host), hostname); + rrdhost_init_hostname(host, hostname); } - if(strcmp(host->program_name, program_name) != 0) { - info("Host '%s' switched program name from '%s' to '%s'", host->hostname, host->program_name, program_name); - char *t = host->program_name; - host->program_name = strdupz(program_name); - freez(t); + if(strcmp(rrdhost_program_name(host), program_name) != 0) { + info("Host '%s' switched program name from '%s' to '%s'", rrdhost_hostname(host), rrdhost_program_name(host), program_name); + STRING *t = host->program_name; + host->program_name = string_strdupz(program_name); + string_freez(t); } - if(strcmp(host->program_version, program_version) != 0) { - info("Host '%s' switched program version from '%s' to '%s'", host->hostname, host->program_version, program_version); - char *t = host->program_version; - host->program_version = strdupz(program_version); - freez(t); + if(strcmp(rrdhost_program_version(host), program_version) != 0) { + info("Host '%s' switched program version from '%s' to '%s'", rrdhost_hostname(host), rrdhost_program_version(host), program_version); + STRING *t = host->program_version; + host->program_version = string_strdupz(program_version); + string_freez(t); } if(host->rrd_update_every != update_every) - error("Host '%s' has an update frequency of %d seconds, but the wanted one is %d seconds. Restart netdata here to apply the new settings.", host->hostname, host->rrd_update_every, update_every); - - if(host->rrd_history_entries < history) - error("Host '%s' has history of %ld entries, but the wanted one is %ld entries. Restart netdata here to apply the new settings.", host->hostname, host->rrd_history_entries, history); + error("Host '%s' has an update frequency of %d seconds, but the wanted one is %d seconds. Restart netdata here to apply the new settings.", rrdhost_hostname(host), host->rrd_update_every, update_every); if(host->rrd_memory_mode != mode) - error("Host '%s' has memory mode '%s', but the wanted one is '%s'. Restart netdata here to apply the new settings.", host->hostname, rrd_memory_mode_name(host->rrd_memory_mode), rrd_memory_mode_name(mode)); + error("Host '%s' has memory mode '%s', but the wanted one is '%s'. Restart netdata here to apply the new settings.", rrdhost_hostname(host), rrd_memory_mode_name(host->rrd_memory_mode), rrd_memory_mode_name(mode)); + + else if(host->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE && host->rrd_history_entries < history) + error("Host '%s' has history of %ld entries, but the wanted one is %ld entries. Restart netdata here to apply the new settings.", rrdhost_hostname(host), host->rrd_history_entries, history); // update host tags rrdhost_init_tags(host, tags); + if(!host->rrdvars) + host->rrdvars = rrdvariables_create(); + if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED)) { rrdhost_flag_clear(host, RRDHOST_FLAG_ARCHIVED); - host->rrdpush_send_enabled = (rrdpush_enabled && rrdpush_destination && *rrdpush_destination && rrdpush_api_key && *rrdpush_api_key) ? 1 : 0; - host->rrdpush_send_destination = (host->rrdpush_send_enabled)?strdupz(rrdpush_destination):NULL; - if (host->rrdpush_send_destination) - host->destinations = destinations_init(host->rrdpush_send_destination); - host->rrdpush_send_api_key = (host->rrdpush_send_enabled)?strdupz(rrdpush_api_key):NULL; - host->rrdpush_send_charts_matching = simple_pattern_create(rrdpush_send_charts_matching, NULL, SIMPLE_PATTERN_EXACT); + rrdfunctions_init(host); - if(host->health_enabled) { - int r; - char filename[FILENAME_MAX + 1]; + if(!host->rrdlabels) + host->rrdlabels = rrdlabels_create(); + + if (!host->rrdset_root_index) + rrdset_index_init(host); + + rrdhost_initialize_rrdpush_sender(host, + rrdpush_enabled, + rrdpush_destination, + rrdpush_api_key, + rrdpush_send_charts_matching); + + rrdfamily_index_init(host); + rrdcalctemplate_index_init(host); + rrdcalc_rrdhost_index_init(host); + + if(rrdpush_enable_replication) + rrdhost_option_set(host, RRDHOST_OPTION_REPLICATION); + else + rrdhost_option_clear(host, RRDHOST_OPTION_REPLICATION); + + host->rrdpush_seconds_to_replicate = rrdpush_seconds_to_replicate; + host->rrdpush_replication_step = rrdpush_replication_step; - if (host != localhost) { - r = mkdir(host->varlib_dir, 0775); - if (r != 0 && errno != EEXIST) - error("Host '%s': cannot create directory '%s'", host->hostname, host->varlib_dir); - } - snprintfz(filename, FILENAME_MAX, "%s/health", host->varlib_dir); - r = mkdir(filename, 0775); - if(r != 0 && errno != EEXIST) - error("Host '%s': cannot create directory '%s'", host->hostname, filename); - - rrdhost_wrlock(host); - health_readdir(host, health_user_config_dir(), health_stock_config_dir(), NULL); - rrdhost_unlock(host); - - if (!file_is_migrated(host->health_log_filename)) { - int rc = sql_create_health_log_table(host); - if (unlikely(rc)) { - error_report("Failed to create health log table in the database"); - - health_alarm_log_load(host); - health_alarm_log_open(host); - } else { - health_alarm_log_load(host); - add_migrated_file(host->health_log_filename, 0); - } - } else { - sql_create_health_log_table(host); - sql_health_alarm_log_load(host); - } - } rrd_hosts_available++; ml_new_host(host); rrdhost_load_rrdcontext_data(host); - info("Host %s is not in archived mode anymore", host->hostname); + info("Host %s is not in archived mode anymore", rrdhost_hostname(host)); } - return; + if (health_enabled) + health_thread_spawn(host); } RRDHOST *rrdhost_find_or_create( @@ -646,17 +657,20 @@ RRDHOST *rrdhost_find_or_create( , char *rrdpush_destination , char *rrdpush_api_key , char *rrdpush_send_charts_matching + , bool rrdpush_enable_replication + , time_t rrdpush_seconds_to_replicate + , time_t rrdpush_replication_step , struct rrdhost_system_info *system_info , bool archived ) { debug(D_RRDHOST, "Searching for host '%s' with guid '%s'", hostname, guid); rrd_wrlock(); - RRDHOST *host = rrdhost_find_by_guid(guid, 0); - if (unlikely(host && RRD_MEMORY_MODE_DBENGINE != mode && rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))) { + RRDHOST *host = rrdhost_find_by_guid(guid); + if (unlikely(host && host->rrd_memory_mode != mode && rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))) { /* If a legacy memory mode instantiates all dbengine state must be discarded to avoid inconsistencies */ error("Archived host '%s' has memory mode '%s', but the wanted one is '%s'. Discarding archived state.", - host->hostname, rrd_memory_mode_name(host->rrd_memory_mode), rrd_memory_mode_name(mode)); + rrdhost_hostname(host), rrd_memory_mode_name(host->rrd_memory_mode), rrd_memory_mode_name(mode)); rrdhost_free(host, 1); host = NULL; } @@ -680,6 +694,9 @@ RRDHOST *rrdhost_find_or_create( , rrdpush_destination , rrdpush_api_key , rrdpush_send_charts_matching + , rrdpush_enable_replication + , rrdpush_seconds_to_replicate + , rrdpush_replication_step , system_info , 0 , archived @@ -705,6 +722,9 @@ RRDHOST *rrdhost_find_or_create( , rrdpush_destination , rrdpush_api_key , rrdpush_send_charts_matching + , rrdpush_enable_replication + , rrdpush_seconds_to_replicate + , rrdpush_replication_step , system_info); } if (host) { @@ -721,6 +741,8 @@ RRDHOST *rrdhost_find_or_create( inline int rrdhost_should_be_removed(RRDHOST *host, RRDHOST *protected_host, time_t now) { if(host != protected_host && host != localhost + && rrdhost_receiver_replicating_charts(host) == 0 + && rrdhost_sender_replicating_charts(host) == 0 && rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN) && !rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED) && !host->receiver @@ -731,50 +753,10 @@ inline int rrdhost_should_be_removed(RRDHOST *host, RRDHOST *protected_host, tim return 0; } -void rrdhost_cleanup_orphan_hosts_nolock(RRDHOST *protected_host) { - time_t now = now_realtime_sec(); - - RRDHOST *host; - -restart_after_removal: - rrdhost_foreach_write(host) { - if(rrdhost_should_be_removed(host, protected_host, now)) { - info("Host '%s' with machine guid '%s' is obsolete - cleaning up.", host->hostname, host->machine_guid); - - if (rrdhost_flag_check(host, RRDHOST_FLAG_DELETE_ORPHAN_HOST) -#ifdef ENABLE_DBENGINE - /* don't delete multi-host DB host files */ - && !(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_storage_engine_shared(host->storage_instance[0])) -#endif - ) - rrdhost_delete_charts(host); - else - rrdhost_save_charts(host); - - rrdhost_free(host, 0); - goto restart_after_removal; - } - } -} - // ---------------------------------------------------------------------------- // RRDHOST global / startup initialization -int rrd_init(char *hostname, struct rrdhost_system_info *system_info) { - - if (unlikely(sql_init_database(DB_CHECK_NONE, system_info ? 0 : 1))) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - fatal("Failed to initialize SQLite"); - info("Skipping SQLITE metadata initialization since memory mode is not dbengine"); - } - - if (unlikely(sql_init_context_database(system_info ? 0 : 1))) { - error_report("Failed to initialize context metadata database"); - } - - if (unlikely(!system_info)) - goto unittest; - +void dbengine_init(char *hostname) { #ifdef ENABLE_DBENGINE storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", storage_tiers); if(storage_tiers < 1) { @@ -807,14 +789,15 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info) { else rrdeng_page_descr_use_malloc(); - int created_tiers = 0; + size_t created_tiers = 0; char dbenginepath[FILENAME_MAX + 1]; char dbengineconfig[200 + 1]; - for(int tier = 0; tier < storage_tiers ;tier++) { + int divisor = 1; + for(size_t tier = 0; tier < storage_tiers ;tier++) { if(tier == 0) snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine", netdata_configured_cache_dir); else - snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine-tier%d", netdata_configured_cache_dir, tier); + snprintfz(dbenginepath, FILENAME_MAX, "%s/dbengine-tier%zu", netdata_configured_cache_dir, tier); int ret = mkdir(dbenginepath, 0775); if (ret != 0 && errno != EEXIST) { @@ -822,27 +805,30 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info) { break; } - int page_cache_mb = default_rrdeng_page_cache_mb; - int disk_space_mb = default_multidb_disk_quota_mb; - int grouping_iterations = storage_tiers_grouping_iterations[tier]; + if(tier > 0) + divisor *= 2; + + int page_cache_mb = default_rrdeng_page_cache_mb / divisor; + int disk_space_mb = default_multidb_disk_quota_mb / divisor; + size_t grouping_iterations = storage_tiers_grouping_iterations[tier]; RRD_BACKFILL backfill = storage_tiers_backfill[tier]; if(tier > 0) { - snprintfz(dbengineconfig, 200, "dbengine tier %d page cache size MB", tier); + snprintfz(dbengineconfig, 200, "dbengine tier %zu page cache size MB", tier); page_cache_mb = config_get_number(CONFIG_SECTION_DB, dbengineconfig, page_cache_mb); - snprintfz(dbengineconfig, 200, "dbengine tier %d multihost disk space MB", tier); + snprintfz(dbengineconfig, 200, "dbengine tier %zu multihost disk space MB", tier); disk_space_mb = config_get_number(CONFIG_SECTION_DB, dbengineconfig, disk_space_mb); - snprintfz(dbengineconfig, 200, "dbengine tier %d update every iterations", tier); + snprintfz(dbengineconfig, 200, "dbengine tier %zu update every iterations", tier); grouping_iterations = config_get_number(CONFIG_SECTION_DB, dbengineconfig, grouping_iterations); if(grouping_iterations < 2) { grouping_iterations = 2; config_set_number(CONFIG_SECTION_DB, dbengineconfig, grouping_iterations); - error("DBENGINE on '%s': 'dbegnine tier %d update every iterations' cannot be less than 2. Assuming 2.", hostname, tier); + error("DBENGINE on '%s': 'dbegnine tier %zu update every iterations' cannot be less than 2. Assuming 2.", hostname, tier); } - snprintfz(dbengineconfig, 200, "dbengine tier %d backfill", tier); + snprintfz(dbengineconfig, 200, "dbengine tier %zu backfill", tier); const char *bf = config_get(CONFIG_SECTION_DB, dbengineconfig, backfill == RRD_BACKFILL_NEW ? "new" : backfill == RRD_BACKFILL_FULL ? "full" : "none"); if(strcmp(bf, "new") == 0) backfill = RRD_BACKFILL_NEW; else if(strcmp(bf, "full") == 0) backfill = RRD_BACKFILL_FULL; @@ -859,14 +845,14 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info) { if(tier > 0 && get_tier_grouping(tier) > 65535) { storage_tiers_grouping_iterations[tier] = 1; - error("DBENGINE on '%s': dbengine tier %d gives aggregation of more than 65535 points of tier 0. Disabling tiers above %d", hostname, tier, tier); + error("DBENGINE on '%s': dbengine tier %zu gives aggregation of more than 65535 points of tier 0. Disabling tiers above %zu", hostname, tier, tier); break; } - - internal_error(true, "DBENGINE tier %d grouping iterations is set to %d", tier, storage_tiers_grouping_iterations[tier]); + + internal_error(true, "DBENGINE tier %zu grouping iterations is set to %zu", tier, storage_tiers_grouping_iterations[tier]); ret = rrdeng_init(NULL, NULL, dbenginepath, page_cache_mb, disk_space_mb, tier); if(ret != 0) { - error("DBENGINE on '%s': Failed to initialize multi-host database tier %d on path '%s'", + error("DBENGINE on '%s': Failed to initialize multi-host database tier %zu on path '%s'", hostname, tier, dbenginepath); break; } @@ -875,13 +861,14 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info) { } if(created_tiers && created_tiers < storage_tiers) { - error("DBENGINE on '%s': Managed to create %d tiers instead of %d. Continuing with %d available.", + error("DBENGINE on '%s': Managed to create %zu tiers instead of %zu. Continuing with %zu available.", hostname, created_tiers, storage_tiers, created_tiers); storage_tiers = created_tiers; } else if(!created_tiers) fatal("DBENGINE on '%s', failed to initialize databases at '%s'.", hostname, netdata_configured_cache_dir); + dbengine_enabled = true; #else storage_tiers = config_get_number(CONFIG_SECTION_DB, "storage tiers", 1); if(storage_tiers != 1) { @@ -889,12 +876,54 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info) { storage_tiers = 1; config_set_number(CONFIG_SECTION_DB, "storage tiers", storage_tiers); } + dbengine_enabled = false; #endif +} - health_init(); - rrdpush_init(); +int rrd_init(char *hostname, struct rrdhost_system_info *system_info) { + rrdhost_init(); -unittest: + if (unlikely(sql_init_database(DB_CHECK_NONE, system_info ? 0 : 1))) { + if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) + fatal("Failed to initialize SQLite"); + info("Skipping SQLITE metadata initialization since memory mode is not dbengine"); + } + + if (unlikely(sql_init_context_database(system_info ? 0 : 1))) { + error_report("Failed to initialize context metadata database"); + } + + if (unlikely(strcmp(hostname, "unittest") == 0)) { + dbengine_enabled = true; + } + else { + health_init(); + rrdpush_init(); + + if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE || rrdpush_receiver_needs_dbengine()) { + info("Initializing dbengine..."); + dbengine_init(hostname); + } + else { + info("Not initializing dbengine..."); + storage_tiers = 1; + } + + if (!dbengine_enabled) { + if (storage_tiers > 1) { + error("dbengine is not enabled, but %zu tiers have been requested. Resetting tiers to 1", + storage_tiers); + storage_tiers = 1; + } + + if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { + error("dbengine is not enabled, but it has been given as the default db mode. Resetting db mode to alloc"); + default_rrd_memory_mode = RRD_MEMORY_MODE_ALLOC; + } + } + } + + metadata_sync_init(); debug(D_RRDHOST, "Initializing localhost with hostname '%s'", hostname); rrd_wrlock(); localhost = rrdhost_create( @@ -916,6 +945,9 @@ unittest: , default_rrdpush_destination , default_rrdpush_api_key , default_rrdpush_send_charts_matching + , default_rrdpush_enable_replication + , default_rrdpush_seconds_to_replicate + , default_rrdpush_replication_step , system_info , 1 , 0 @@ -940,19 +972,19 @@ unittest: // there are only used when NETDATA_INTERNAL_CHECKS is set void __rrdhost_check_rdlock(RRDHOST *host, const char *file, const char *function, const unsigned long line) { - debug(D_RRDHOST, "Checking read lock on host '%s'", host->hostname); + debug(D_RRDHOST, "Checking read lock on host '%s'", rrdhost_hostname(host)); int ret = netdata_rwlock_trywrlock(&host->rrdhost_rwlock); if(ret == 0) - fatal("RRDHOST '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file); + fatal("RRDHOST '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", rrdhost_hostname(host), function, line, file); } void __rrdhost_check_wrlock(RRDHOST *host, const char *file, const char *function, const unsigned long line) { - debug(D_RRDHOST, "Checking write lock on host '%s'", host->hostname); + debug(D_RRDHOST, "Checking write lock on host '%s'", rrdhost_hostname(host)); int ret = netdata_rwlock_tryrdlock(&host->rrdhost_rwlock); if(ret == 0) - fatal("RRDHOST '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file); + fatal("RRDHOST '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", rrdhost_hostname(host), function, line, file); } void __rrd_check_rdlock(const char *file, const char *function, const unsigned long line) { @@ -975,8 +1007,6 @@ void __rrd_check_wrlock(const char *file, const char *function, const unsigned l // RRDHOST - free void rrdhost_system_info_free(struct rrdhost_system_info *system_info) { - info("SYSTEM_INFO: free %p", system_info); - if(likely(system_info)) { freez(system_info->cloud_provider_type); freez(system_info->cloud_instance_type); @@ -1016,18 +1046,21 @@ void destroy_receiver_state(struct receiver_state *rpt); void stop_streaming_sender(RRDHOST *host) { + rrdhost_option_clear(host, RRDHOST_OPTION_SENDER_ENABLED); + if (unlikely(!host->sender)) return; rrdpush_sender_thread_stop(host); // stop a possibly running thread cbuffer_free(host->sender->buffer); - buffer_free(host->sender->build); #ifdef ENABLE_COMPRESSION if (host->sender->compressor) host->sender->compressor->destroy(&host->sender->compressor); #endif + replication_cleanup_sender(host->sender); freez(host->sender); host->sender = NULL; + rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_INITIALIZED); } void stop_streaming_receiver(RRDHOST *host) @@ -1051,7 +1084,7 @@ void rrdhost_free(RRDHOST *host, bool force) { if(!host) return; if (netdata_exit || force) - info("Freeing all memory for host '%s'...", host->hostname); + info("Freeing all memory for host '%s'...", rrdhost_hostname(host)); rrd_check_wrlock(); // make sure the RRDs are write locked @@ -1061,65 +1094,53 @@ void rrdhost_free(RRDHOST *host, bool force) { // ------------------------------------------------------------------------ // clean up streaming + stop_streaming_sender(host); if (netdata_exit || force) stop_streaming_receiver(host); + + // ------------------------------------------------------------------------ + // clean up alarms + + rrdcalc_delete_all(host); + + rrdhost_wrlock(host); // lock this RRDHOST + // ------------------------------------------------------------------------ // release its children resources #ifdef ENABLE_DBENGINE - for(int tier = 0; tier < storage_tiers ;tier++) { - if(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && - host->storage_instance[tier] && - !is_storage_engine_shared(host->storage_instance[tier])) - rrdeng_prepare_exit((struct rrdengine_instance *)host->storage_instance[tier]); + for(size_t tier = 0; tier < storage_tiers ;tier++) { + if(host->db[tier].mode == RRD_MEMORY_MODE_DBENGINE + && host->db[tier].instance + && !is_storage_engine_shared(host->db[tier].instance)) + rrdeng_prepare_exit((struct rrdengine_instance *)host->db[tier].instance); } #endif - while(host->rrdset_root) - rrdset_free(host->rrdset_root); + // delete all the RRDSETs of the host + rrdset_index_destroy(host); + rrdcalc_rrdhost_index_destroy(host); + rrdcalctemplate_index_destroy(host); freez(host->exporting_flags); - while(host->alarms) - rrdcalc_unlink_and_free(host, host->alarms); - - RRDCALC *rc,*nc; - for(rc = host->alarms_with_foreach; rc ; rc = nc) { - nc = rc->next; - rrdcalc_free(rc); - } - host->alarms_with_foreach = NULL; - - while(host->templates) - rrdcalctemplate_unlink_and_free(host, host->templates); - - RRDCALCTEMPLATE *rt,*next; - for(rt = host->alarms_template_with_foreach; rt ; rt = next) { - next = rt->next; - rrdcalctemplate_free(rt); - } - host->alarms_template_with_foreach = NULL; - - debug(D_RRD_CALLS, "RRDHOST: Cleaning up remaining host variables for host '%s'", host->hostname); - rrdvar_free_remaining_variables(host, &host->rrdvar_root_index); - health_alarm_log_free(host); #ifdef ENABLE_DBENGINE - for(int tier = 0; tier < storage_tiers ;tier++) { - if(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && - host->storage_instance[tier] && - !is_storage_engine_shared(host->storage_instance[tier])) - rrdeng_exit((struct rrdengine_instance *)host->storage_instance[tier]); + for(size_t tier = 0; tier < storage_tiers ;tier++) { + if(host->db[tier].mode == RRD_MEMORY_MODE_DBENGINE + && host->db[tier].instance + && !is_storage_engine_shared(host->db[tier].instance)) + rrdeng_exit((struct rrdengine_instance *)host->db[tier].instance); } #endif if (!netdata_exit && !force) { - info("Setting archive mode for host '%s'...", host->hostname); + info("Setting archive mode for host '%s'...", rrdhost_hostname(host)); rrdhost_flag_set(host, RRDHOST_FLAG_ARCHIVED); rrdhost_unlock(host); return; @@ -1143,24 +1164,13 @@ void rrdhost_free(RRDHOST *host, bool force) { // ------------------------------------------------------------------------ // remove it from the indexes - if(rrdhost_index_del(host) != host) - error("RRDHOST '%s' removed from index, deleted the wrong entry.", host->hostname); + rrdhost_index_del_hostname(host); + rrdhost_index_del_by_guid(host); // ------------------------------------------------------------------------ // unlink it from the host - if(host == localhost) { - localhost = host->next; - } - else { - // find the previous one - RRDHOST *h; - for(h = localhost; h && h->next != host ; h = h->next) ; - - // bypass it - if(h) h->next = host->next; - else error("Request to free RRDHOST '%s': cannot find it", host->hostname); - } + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(localhost, host, prev, next); // ------------------------------------------------------------------------ // free it @@ -1168,37 +1178,36 @@ void rrdhost_free(RRDHOST *host, bool force) { pthread_mutex_destroy(&host->aclk_state_lock); freez(host->aclk_state.claimed_id); freez(host->aclk_state.prev_claimed_id); - freez((void *)host->tags); - rrdlabels_destroy(host->host_labels); - freez((void *)host->os); - freez((void *)host->timezone); - freez((void *)host->abbrev_timezone); - freez(host->program_version); - freez(host->program_name); + string_freez(host->tags); + rrdlabels_destroy(host->rrdlabels); + string_freez(host->os); + string_freez(host->timezone); + string_freez(host->abbrev_timezone); + string_freez(host->program_name); + string_freez(host->program_version); rrdhost_system_info_free(host->system_info); freez(host->cache_dir); freez(host->varlib_dir); freez(host->rrdpush_send_api_key); freez(host->rrdpush_send_destination); - struct rrdpush_destinations *tmp_destination; - while (host->destinations) { - tmp_destination = host->destinations->next; - freez(host->destinations); - host->destinations = tmp_destination; - } - freez(host->health_default_exec); - freez(host->health_default_recipient); + rrdpush_destinations_free(host); + string_freez(host->health_default_exec); + string_freez(host->health_default_recipient); freez(host->health_log_filename); - freez(host->hostname); - freez(host->registry_hostname); + string_freez(host->registry_hostname); simple_pattern_free(host->rrdpush_send_charts_matching); rrdhost_unlock(host); netdata_rwlock_destroy(&host->health_log.alarm_log_rwlock); netdata_rwlock_destroy(&host->rrdhost_rwlock); freez(host->node_id); + rrdfamily_index_destroy(host); + rrdfunctions_destroy(host); + rrdvariables_destroy(host->rrdvars); + rrdhost_destroy_rrdcontexts(host); + string_freez(host->hostname); freez(host); #ifdef ENABLE_ACLK if (wc) @@ -1209,9 +1218,14 @@ void rrdhost_free(RRDHOST *host, bool force) { void rrdhost_free_all(void) { rrd_wrlock(); + /* Make sure child-hosts are released before the localhost. */ - while(localhost->next) rrdhost_free(localhost->next, 1); - rrdhost_free(localhost, 1); + while(localhost && localhost->next) + rrdhost_free(localhost->next, 1); + + if(localhost) + rrdhost_free(localhost, 1); + rrd_unlock(); } @@ -1221,25 +1235,20 @@ void rrdhost_free_all(void) { void rrdhost_save_charts(RRDHOST *host) { if(!host) return; - info("Saving/Closing database of host '%s'...", host->hostname); + info("Saving/Closing database of host '%s'...", rrdhost_hostname(host)); RRDSET *st; // we get a write lock // to ensure only one thread is saving the database - rrdhost_wrlock(host); - rrdset_foreach_write(st, host) { - rrdset_rdlock(st); rrdset_save(st); - rrdset_unlock(st); } - - rrdhost_unlock(host); + rrdset_foreach_done(st); } static void rrdhost_load_auto_labels(void) { - DICTIONARY *labels = localhost->host_labels; + DICTIONARY *labels = localhost->rrdlabels; if (localhost->system_info->cloud_provider_type) rrdlabels_add(labels, "_cloud_provider_type", localhost->system_info->cloud_provider_type, RRDLABEL_SRC_AUTO); @@ -1301,13 +1310,31 @@ static void rrdhost_load_auto_labels(void) { add_aclk_host_labels(); + health_add_host_labels(); + rrdlabels_add( - labels, "_is_parent", (localhost->next || configured_as_parent()) ? "true" : "false", RRDLABEL_SRC_AUTO); + labels, "_is_parent", (localhost->senders_count > 0) ? "true" : "false", RRDLABEL_SRC_AUTO); if (localhost->rrdpush_send_destination) rrdlabels_add(labels, "_streams_to", localhost->rrdpush_send_destination, RRDLABEL_SRC_AUTO); } +void rrdhost_set_is_parent_label(int count) { + DICTIONARY *labels = localhost->rrdlabels; + + if (count == 0 || count == 1) { + rrdlabels_add( + labels, "_is_parent", (count) ? "true" : "false", RRDLABEL_SRC_AUTO); + + //queue a node info +#ifdef ENABLE_ACLK + if (netdata_cloud_setting) { + aclk_queue_node_info(localhost); + } +#endif + } +} + static void rrdhost_load_config_labels(void) { int status = config_load(NULL, 1, CONFIG_SECTION_HOST_LABEL); if(!status) { @@ -1320,7 +1347,7 @@ static void rrdhost_load_config_labels(void) { config_section_wrlock(co); struct config_option *cv; for(cv = co->values; cv ; cv = cv->next) { - rrdlabels_add(localhost->host_labels, cv->name, cv->value, RRDLABEL_SRC_CONFIG); + rrdlabels_add(localhost->rrdlabels, cv->name, cv->value, RRDLABEL_SRC_CONFIG); cv->flags |= CONFIG_VALUE_USED; } config_section_unlock(co); @@ -1339,41 +1366,37 @@ static void rrdhost_load_kubernetes_labels(void) { debug(D_RRDHOST, "Attempting to fetch external labels via %s", label_script); pid_t pid; - FILE *fp = mypopen(label_script, &pid); - if(!fp) return; + FILE *fp_child_input; + FILE *fp_child_output = netdata_popen(label_script, &pid, &fp_child_input); + if(!fp_child_output) return; char buffer[1000 + 1]; - while (fgets(buffer, 1000, fp) != NULL) - rrdlabels_add_pair(localhost->host_labels, buffer, RRDLABEL_SRC_AUTO|RRDLABEL_SRC_K8S); + while (fgets(buffer, 1000, fp_child_output) != NULL) + rrdlabels_add_pair(localhost->rrdlabels, buffer, RRDLABEL_SRC_AUTO|RRDLABEL_SRC_K8S); // Non-zero exit code means that all the script output is error messages. We've shown already any message that didn't include a ':' // Here we'll inform with an ERROR that the script failed, show whatever (if anything) was added to the list of labels, free the memory and set the return to null - int rc = mypclose(fp, pid); + int rc = netdata_pclose(fp_child_input, fp_child_output, pid); if(rc) error("%s exited abnormally. Failed to get kubernetes labels.", label_script); } void reload_host_labels(void) { - if(!localhost->host_labels) - localhost->host_labels = rrdlabels_create(); + if(!localhost->rrdlabels) + localhost->rrdlabels = rrdlabels_create(); - rrdlabels_unmark_all(localhost->host_labels); + rrdlabels_unmark_all(localhost->rrdlabels); // priority is important here rrdhost_load_config_labels(); rrdhost_load_kubernetes_labels(); rrdhost_load_auto_labels(); - rrdlabels_remove_all_unmarked(localhost->host_labels); - sql_store_host_labels(localhost); + rrdlabels_remove_all_unmarked(localhost->rrdlabels); + metaqueue_store_host_labels(localhost->machine_guid); health_label_log_save(localhost); -/* TODO-GAPS - fix this so that it looks properly at the state and version of the sender - if(localhost->rrdpush_send_enabled && localhost->rrdpush_sender_buffer){ - localhost->labels.labels_flag |= RRDHOST_FLAG_STREAM_LABELS_UPDATE; - rrdpush_send_labels(localhost); - } -*/ + rrdpush_send_host_labels(localhost); health_reload(); } @@ -1383,23 +1406,18 @@ void reload_host_labels(void) { void rrdhost_delete_charts(RRDHOST *host) { if(!host) return; - info("Deleting database of host '%s'...", host->hostname); + info("Deleting database of host '%s'...", rrdhost_hostname(host)); RRDSET *st; // we get a write lock // to ensure only one thread is saving the database - rrdhost_wrlock(host); - rrdset_foreach_write(st, host) { - rrdset_rdlock(st); - rrdset_delete_files(st); - rrdset_unlock(st); + rrdset_delete_files(st); } + rrdset_foreach_done(st); recursively_delete_dir(host->cache_dir, "left over host"); - - rrdhost_unlock(host); } // ---------------------------------------------------------------------------- @@ -1408,29 +1426,26 @@ void rrdhost_delete_charts(RRDHOST *host) { void rrdhost_cleanup_charts(RRDHOST *host) { if(!host) return; - info("Cleaning up database of host '%s'...", host->hostname); + info("Cleaning up database of host '%s'...", rrdhost_hostname(host)); RRDSET *st; - uint32_t rrdhost_delete_obsolete_charts = rrdhost_flag_check(host, RRDHOST_FLAG_DELETE_OBSOLETE_CHARTS); + uint32_t rrdhost_delete_obsolete_charts = rrdhost_option_check(host, RRDHOST_OPTION_DELETE_OBSOLETE_CHARTS); // we get a write lock // to ensure only one thread is saving the database - rrdhost_wrlock(host); - rrdset_foreach_write(st, host) { - rrdset_rdlock(st); if(rrdhost_delete_obsolete_charts && rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)) rrdset_delete_files(st); + else if(rrdhost_delete_obsolete_charts && rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS)) rrdset_delete_obsolete_dimensions(st); + else rrdset_save(st); - rrdset_unlock(st); } - - rrdhost_unlock(host); + rrdset_foreach_done(st); } @@ -1459,11 +1474,9 @@ void rrdhost_cleanup_all(void) { RRDHOST *host; rrdhost_foreach_read(host) { - if (host != localhost && rrdhost_flag_check(host, RRDHOST_FLAG_DELETE_ORPHAN_HOST) && !host->receiver -#ifdef ENABLE_DBENGINE + if (host != localhost && rrdhost_option_check(host, RRDHOST_OPTION_DELETE_ORPHAN_HOST) && !host->receiver /* don't delete multi-host DB host files */ - && !(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_storage_engine_shared(host->storage_instance[0])) -#endif + && !(host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && is_storage_engine_shared(host->db[0].instance)) ) rrdhost_delete_charts(host); else @@ -1474,157 +1487,6 @@ void rrdhost_cleanup_all(void) { } -// ---------------------------------------------------------------------------- -// RRDHOST - save or delete all the host charts from disk - -void rrdhost_cleanup_obsolete_charts(RRDHOST *host) { - time_t now = now_realtime_sec(); - - RRDSET *st; - - uint32_t rrdhost_delete_obsolete_charts = rrdhost_flag_check(host, RRDHOST_FLAG_DELETE_OBSOLETE_CHARTS); - -restart_after_removal: - rrdset_foreach_write(st, host) { - if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) - && st->last_accessed_time + rrdset_free_obsolete_time < now - && st->last_updated.tv_sec + rrdset_free_obsolete_time < now - && st->last_collected_time.tv_sec + rrdset_free_obsolete_time < now - )) { - st->rrdhost->obsolete_charts_count--; -#ifdef ENABLE_DBENGINE - if(st->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { - RRDDIM *rd, *last; - - rrdset_flag_set(st, RRDSET_FLAG_ARCHIVED); - while (st->variables) rrdsetvar_free(st->variables); - while (st->alarms) rrdsetcalc_unlink(st->alarms); - rrdset_wrlock(st); - for (rd = st->dimensions, last = NULL ; likely(rd) ; ) { - if (rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) { - last = rd; - rd = rd->next; - continue; - } - - if (rrddim_flag_check(rd, RRDDIM_FLAG_ACLK)) { - last = rd; - rd = rd->next; - continue; - } - rrddim_flag_set(rd, RRDDIM_FLAG_ARCHIVED); - while (rd->variables) - rrddimvar_free(rd->variables); - - if (rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) { - rrddim_flag_clear(rd, RRDDIM_FLAG_OBSOLETE); - - /* only a collector can mark a chart as obsolete, so we must remove the reference */ - - size_t tiers_available = 0, tiers_said_yes = 0; - for(int tier = 0; tier < storage_tiers ;tier++) { - if(rd->tiers[tier]) { - tiers_available++; - - if(rd->tiers[tier]->collect_ops.finalize(rd->tiers[tier]->db_collection_handle)) - tiers_said_yes++; - - rd->tiers[tier]->db_collection_handle = NULL; - } - } - - if (tiers_available == tiers_said_yes && tiers_said_yes) { - /* This metric has no data and no references */ - delete_dimension_uuid(&rd->metric_uuid); - rrddim_free(st, rd); - if (unlikely(!last)) { - rd = st->dimensions; - } - else { - rd = last->next; - } - continue; - } -#ifdef ENABLE_ACLK - else - queue_dimension_to_aclk(rd, rd->last_collected_time.tv_sec); -#endif - } - last = rd; - rd = rd->next; - } - rrdset_unlock(st); - - debug(D_RRD_CALLS, "RRDSET: Cleaning up remaining chart variables for host '%s', chart '%s'", host->hostname, st->id); - rrdvar_free_remaining_variables(host, &st->rrdvar_root_index); - - rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); - - if (st->dimensions) { - /* If the chart still has dimensions don't delete it from the metadata log */ - continue; - } - } -#endif - rrdset_rdlock(st); - - if(rrdhost_delete_obsolete_charts) - rrdset_delete_files(st); - else - rrdset_save(st); - - rrdset_unlock(st); - - rrdset_free(st); - goto restart_after_removal; - } -#ifdef ENABLE_ACLK - else - sql_check_chart_liveness(st); -#endif - } -} - -void rrdset_check_obsoletion(RRDHOST *host) -{ - RRDSET *st; - time_t last_entry_t; - rrdset_foreach_read(st, host) { - last_entry_t = rrdset_last_entry_t(st); - if (last_entry_t && last_entry_t < host->senders_connect_time) { - rrdset_is_obsolete(st); - } - } -} - -void rrd_cleanup_obsolete_charts() -{ - rrd_rdlock(); - - RRDHOST *host; - rrdhost_foreach_read(host) - { - if (host->obsolete_charts_count) { - rrdhost_wrlock(host); - rrdhost_cleanup_obsolete_charts(host); - rrdhost_unlock(host); - } - - if ( host != localhost && - host->trigger_chart_obsoletion_check && - ((host->senders_last_chart_command && - host->senders_last_chart_command + host->health_delay_up_to < now_realtime_sec()) - || (host->senders_connect_time + 300 < now_realtime_sec())) ) { - rrdhost_rdlock(host); - rrdset_check_obsoletion(host); - rrdhost_unlock(host); - host->trigger_chart_obsoletion_check = 0; - } - } - - rrd_unlock(); -} - // ---------------------------------------------------------------------------- // RRDHOST - set system info from environment variables // system_info fields must be heap allocated or NULL @@ -1761,57 +1623,18 @@ int rrdhost_set_system_info_variable(struct rrdhost_system_info *system_info, ch return res; } -/** - * Alarm Compare ID - * - * Callback function used with the binary trees to compare the id of RRDCALC - * - * @param a a pointer to the RRDCAL item to insert,compare or update the binary tree - * @param b the pointer to the binary tree. - * - * @return It returns 0 case the values are equal, 1 case a is bigger than b and -1 case a is smaller than b. - */ -int alarm_compare_id(void *a, void *b) { - register uint32_t hash1 = ((RRDCALC *)a)->id; - register uint32_t hash2 = ((RRDCALC *)b)->id; - - if(hash1 < hash2) return -1; - else if(hash1 > hash2) return 1; - - return 0; -} - -/** - * Alarm Compare NAME - * - * Callback function used with the binary trees to compare the name of RRDCALC - * - * @param a a pointer to the RRDCAL item to insert,compare or update the binary tree - * @param b the pointer to the binary tree. - * - * @return It returns 0 case the values are equal, 1 case a is bigger than b and -1 case a is smaller than b. - */ -int alarm_compare_name(void *a, void *b) { - RRDCALC *in1 = (RRDCALC *)a; - RRDCALC *in2 = (RRDCALC *)b; - - if(in1->hash < in2->hash) return -1; - else if(in1->hash > in2->hash) return 1; - - return strcmp(in1->name,in2->name); -} - // Added for gap-filling, if this proves to be a bottleneck in large-scale systems then we will need to cache // the last entry times as the metric updates, but let's see if it is a problem first. time_t rrdhost_last_entry_t(RRDHOST *h) { - rrdhost_rdlock(h); RRDSET *st; time_t result = 0; + rrdset_foreach_read(st, h) { time_t st_last = rrdset_last_entry_t(st); + if (st_last > result) result = st_last; } - rrdhost_unlock(h); + rrdset_foreach_done(st); return result; } diff --git a/database/rrdlabels.c b/database/rrdlabels.c index 5198cb4aa..743499ab5 100644 --- a/database/rrdlabels.c +++ b/database/rrdlabels.c @@ -369,12 +369,15 @@ __attribute__((constructor)) void initialize_labels_keys_char_map(void) { } -static size_t rrdlabels_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, unsigned char *char_map, bool utf, const char *empty) { +size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, unsigned char *char_map, bool utf, const char *empty, size_t *multibyte_length) { if(unlikely(!dst_size)) return 0; + if(unlikely(!src || !*src)) { strncpyz((char *)dst, empty, dst_size); dst[dst_size - 1] = '\0'; - return strlen((char *)dst); + size_t len = strlen((char *)dst); + if(multibyte_length) *multibyte_length = len; + return len; } unsigned char *d = dst; @@ -385,7 +388,9 @@ static size_t rrdlabels_sanitize(unsigned char *dst, const unsigned char *src, s // copy while converting, but keep only one white space // we start wil last_is_space = 1 to skip leading spaces int last_is_space = 1; + size_t mblen = 0; + while(*src && d < end) { unsigned char c = *src; @@ -446,28 +451,34 @@ static size_t rrdlabels_sanitize(unsigned char *dst, const unsigned char *src, s *d = '\0'; // check if dst is all underscores and empty it if it is - d = dst; - while(*d == '_') d++; - if(unlikely(*d == '\0')) { - *dst = '\0'; - mblen = 0; + if(*dst == '_') { + unsigned char *t = dst; + while (*t == '_') t++; + if (unlikely(*t == '\0')) { + *dst = '\0'; + mblen = 0; + } } if(unlikely(*dst == '\0')) { strncpyz((char *)dst, empty, dst_size); dst[dst_size - 1] = '\0'; - return strlen((char *)dst); + mblen = strlen((char *)dst); + if(multibyte_length) *multibyte_length = mblen; + return mblen; } - return mblen; + if(multibyte_length) *multibyte_length = mblen; + + return d - dst; } static inline size_t rrdlabels_sanitize_name(char *dst, const char *src, size_t dst_size) { - return rrdlabels_sanitize((unsigned char *)dst, (const unsigned char *)src, dst_size, label_names_char_map, 0, ""); + return text_sanitize((unsigned char *)dst, (const unsigned char *)src, dst_size, label_names_char_map, 0, "", NULL); } static inline size_t rrdlabels_sanitize_value(char *dst, const char *src, size_t dst_size) { - return rrdlabels_sanitize((unsigned char *)dst, (const unsigned char *)src, dst_size, label_values_char_map, 1, "[none]"); + return text_sanitize((unsigned char *)dst, (const unsigned char *)src, dst_size, label_values_char_map, 1, "[none]", NULL); } // ---------------------------------------------------------------------------- @@ -478,9 +489,7 @@ typedef struct rrdlabel { RRDLABEL_SRC label_source; } RRDLABEL; -static void rrdlabel_insert_callback(const char *name, void *value, void *data) { - (void)name; - DICTIONARY *dict = (DICTIONARY *)data; (void)dict; +static void rrdlabel_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *dict_ptr __maybe_unused) { RRDLABEL *lb = (RRDLABEL *)value; // label_value is already allocated by the STRING @@ -488,42 +497,43 @@ static void rrdlabel_insert_callback(const char *name, void *value, void *data) lb->label_source &= ~RRDLABEL_FLAG_OLD; } -static void rrdlabel_delete_callback(const char *name, void *value, void *data) { - (void)name; - DICTIONARY *dict = (DICTIONARY *)data; (void)dict; +static void rrdlabel_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *dict_ptr __maybe_unused) { RRDLABEL *lb = (RRDLABEL *)value; string_freez(lb->label_value); lb->label_value = NULL; } -static void rrdlabel_conflict_callback(const char *name, void *oldvalue, void *newvalue, void *data) { - (void)name; - DICTIONARY *dict = (DICTIONARY *)data; (void)dict; +static bool rrdlabel_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *oldvalue, void *newvalue, void *dict_ptr __maybe_unused) { RRDLABEL *lbold = (RRDLABEL *)oldvalue; RRDLABEL *lbnew = (RRDLABEL *)newvalue; - if(lbold->label_value == lbnew->label_value || strcmp(string2str(lbold->label_value), string2str(lbnew->label_value)) == 0) { + if(lbold->label_value == lbnew->label_value) { // they are the same + lbold->label_source |= lbnew->label_source; lbold->label_source |= RRDLABEL_FLAG_OLD; lbold->label_source &= ~RRDLABEL_FLAG_NEW; // free the new one string_freez(lbnew->label_value); + + return false; } - else { - // they are different - string_freez(lbold->label_value); - lbold->label_value = lbnew->label_value; - lbold->label_source = lbnew->label_source; - lbold->label_source |= RRDLABEL_FLAG_NEW; - lbold->label_source &= ~RRDLABEL_FLAG_OLD; - } + + // they are different + + string_freez(lbold->label_value); + lbold->label_value = lbnew->label_value; + lbold->label_source = lbnew->label_source; + lbold->label_source |= RRDLABEL_FLAG_NEW; + lbold->label_source &= ~RRDLABEL_FLAG_OLD; + + return true; } DICTIONARY *rrdlabels_create(void) { - DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_DONT_OVERWRITE_VALUE); + DICTIONARY *dict = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); dictionary_register_insert_callback(dict, rrdlabel_insert_callback, dict); dictionary_register_delete_callback(dict, rrdlabel_delete_callback, dict); dictionary_register_conflict_callback(dict, rrdlabel_conflict_callback, dict); @@ -538,6 +548,9 @@ void rrdlabels_destroy(DICTIONARY *labels_dict) { dictionary_destroy(labels_dict); } +void rrdlabels_flush(DICTIONARY *labels_dict) { + dictionary_flush(labels_dict); +} // ---------------------------------------------------------------------------- // rrdlabels_add() @@ -620,10 +633,12 @@ void rrdlabels_add_pair(DICTIONARY *dict, const char *string, RRDLABEL_SRC ls) { } // ---------------------------------------------------------------------------- -// rrdlabels_get_to_buffer_or_null() +// rrdlabels_get_value_to_buffer_or_null() void rrdlabels_get_value_to_buffer_or_null(DICTIONARY *labels, BUFFER *wb, const char *key, const char *quote, const char *null) { - DICTIONARY_ITEM *acquired_item = dictionary_get_and_acquire_item(labels, key); + if(!labels) return; + + const DICTIONARY_ITEM *acquired_item = dictionary_get_and_acquire_item(labels, key); RRDLABEL *lb = dictionary_acquired_item_value(acquired_item); if(lb && lb->label_value) @@ -634,15 +649,23 @@ void rrdlabels_get_value_to_buffer_or_null(DICTIONARY *labels, BUFFER *wb, const dictionary_acquired_item_release(labels, acquired_item); } +// ---------------------------------------------------------------------------- +// rrdlabels_get_value_to_char_or_null() + +void rrdlabels_get_value_to_char_or_null(DICTIONARY *labels, char **value, const char *key) { + const DICTIONARY_ITEM *acquired_item = dictionary_get_and_acquire_item(labels, key); + RRDLABEL *lb = dictionary_acquired_item_value(acquired_item); + + *value = (lb && lb->label_value) ? strdupz(string2str(lb->label_value)) : NULL; + + dictionary_acquired_item_release(labels, acquired_item); +} // ---------------------------------------------------------------------------- // rrdlabels_unmark_all() // remove labels RRDLABEL_FLAG_OLD and RRDLABEL_FLAG_NEW from all dictionary items -static int remove_flags_old_new(const char *name, void *value, void *data) { - (void)name; - (void)data; - +static int remove_flags_old_new(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { RRDLABEL *lb = (RRDLABEL *)value; if(lb->label_source & RRDLABEL_FLAG_OLD) lb->label_source &= ~RRDLABEL_FLAG_OLD; @@ -660,12 +683,13 @@ void rrdlabels_unmark_all(DICTIONARY *labels) { // rrdlabels_remove_all_unmarked() // remove dictionary items that are neither old, nor new -static int remove_not_old_not_new_callback(const char *name, void *value, void *data) { +static int remove_not_old_not_new_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *name = dictionary_acquired_item_name(item); DICTIONARY *dict = (DICTIONARY *)data; RRDLABEL *lb = (RRDLABEL *)value; if(!(lb->label_source & (RRDLABEL_FLAG_OLD | RRDLABEL_FLAG_NEW | RRDLABEL_FLAG_PERMANENT))) { - dictionary_del_having_write_lock(dict, name); + dictionary_del(dict, name); return 1; } @@ -685,7 +709,8 @@ struct labels_walkthrough { void *data; }; -static int labels_walkthrough_callback(const char *name, void *value, void *data) { +static int labels_walkthrough_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *name = dictionary_acquired_item_name(item); struct labels_walkthrough *d = (struct labels_walkthrough *)data; RRDLABEL *lb = (RRDLABEL *)value; @@ -717,7 +742,8 @@ int rrdlabels_sorted_walkthrough_read(DICTIONARY *labels, int (*callback)(const // rrdlabels_migrate_to_these() // migrate an existing label list to a new list, INPLACE -static int copy_label_to_dictionary_callback(const char *name, void *value, void *data) { +static int copy_label_to_dictionary_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *name = dictionary_acquired_item_name(item); DICTIONARY *dst = (DICTIONARY *)data; RRDLABEL *lb = (RRDLABEL *)value; labels_add_already_sanitized(dst, name, string2str(lb->label_value), lb->label_source); @@ -754,7 +780,8 @@ struct simple_pattern_match_name_value { char equal; }; -static int simple_pattern_match_name_only_callback(const char *name, void *value, void *data) { +static int simple_pattern_match_name_only_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *name = dictionary_acquired_item_name(item); struct simple_pattern_match_name_value *t = (struct simple_pattern_match_name_value *)data; (void)value; @@ -764,7 +791,8 @@ static int simple_pattern_match_name_only_callback(const char *name, void *value return 0; } -static int simple_pattern_match_name_and_value_callback(const char *name, void *value, void *data) { +static int simple_pattern_match_name_and_value_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *name = dictionary_acquired_item_name(item); struct simple_pattern_match_name_value *t = (struct simple_pattern_match_name_value *)data; RRDLABEL *lb = (RRDLABEL *)value; @@ -830,7 +858,9 @@ bool rrdlabels_match_simple_pattern(DICTIONARY *labels, const char *simple_patte // ---------------------------------------------------------------------------- // Log all labels -static int rrdlabels_log_label_to_buffer_callback(const char *name, void *value, void *data) { +static int rrdlabels_log_label_to_buffer_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *name = dictionary_acquired_item_name(item); + BUFFER *wb = (BUFFER *)data; RRDLABEL *lb = (RRDLABEL *)value; @@ -880,7 +910,8 @@ struct labels_to_buffer { size_t count; }; -static int label_to_buffer_callback(const char *name, void *value, void *data) { +static int label_to_buffer_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *name = dictionary_acquired_item_name(item); struct labels_to_buffer *t = (struct labels_to_buffer *)data; RRDLABEL *lb = (RRDLABEL *)value; @@ -926,23 +957,17 @@ int rrdlabels_to_buffer(DICTIONARY *labels, BUFFER *wb, const char *before_each, return dictionary_walkthrough_read(labels, label_to_buffer_callback, (void *)&tmp); } -static int chart_label_store_to_sql_callback(const char *name, const char *value, RRDLABEL_SRC ls, void *data) { - RRDSET *st = (RRDSET *)data; - sql_store_chart_label(st->chart_uuid, (int)ls, (char *)name, (char *)value); - return 1; -} - void rrdset_update_rrdlabels(RRDSET *st, DICTIONARY *new_rrdlabels) { - if(!st->state->chart_labels) - st->state->chart_labels = rrdlabels_create(); + if(!st->rrdlabels) + st->rrdlabels = rrdlabels_create(); if (new_rrdlabels) - rrdlabels_migrate_to_these(st->state->chart_labels, new_rrdlabels); + rrdlabels_migrate_to_these(st->rrdlabels, new_rrdlabels); - // TODO - we should also cleanup sqlite from old new_rrdlabels that have been removed - rrdlabels_walkthrough_read(st->state->chart_labels, chart_label_store_to_sql_callback, st); + metaqueue_chart_labels(st); } + // ---------------------------------------------------------------------------- // rrdlabels unit test @@ -1107,12 +1132,14 @@ int rrdlabels_unittest_simple_pattern() { int rrdlabels_unittest_sanitize_value(const char *src, const char *expected) { char buf[RRDLABELS_MAX_VALUE_LENGTH + 1]; - size_t mblen = rrdlabels_sanitize_value(buf, src, RRDLABELS_MAX_VALUE_LENGTH); + size_t len = rrdlabels_sanitize_value(buf, src, RRDLABELS_MAX_VALUE_LENGTH); + size_t expected_len = strlen(expected); int err = 0; if(strcmp(buf, expected) != 0) err = 1; + if(len != expected_len) err = 1; - fprintf(stderr, "%s(%s): %s, expected '%s', got '%s', mblen = %zu, bytes = %zu\n", __FUNCTION__, src, (err==1)?"FAILED":"OK", expected, buf, mblen, strlen(buf)); + fprintf(stderr, "%s(%s): %s, expected '%s', got '%s', expected bytes = %zu, got bytes = %zu\n", __FUNCTION__, src, (err==1)?"FAILED":"OK", expected, buf, expected_len, strlen(buf)); return err; } diff --git a/database/rrdset.c b/database/rrdset.c index 9693ee211..6eb3c7105 100644 --- a/database/rrdset.c +++ b/database/rrdset.c @@ -3,97 +3,452 @@ #define NETDATA_RRD_INTERNALS #include "rrd.h" #include +#include "storage_engine.h" -void __rrdset_check_rdlock(RRDSET *st, const char *file, const char *function, const unsigned long line) { - debug(D_RRD_CALLS, "Checking read lock on chart '%s'", st->id); +// ---------------------------------------------------------------------------- +// RRDSET name index - int ret = netdata_rwlock_trywrlock(&st->rrdset_rwlock); - if(ret == 0) - fatal("RRDSET '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", st->id, function, line, file); +static void rrdset_name_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdset, void *rrdhost __maybe_unused) { + RRDSET *st = rrdset; + rrdset_flag_set(st, RRDSET_FLAG_INDEXED_NAME); +} +static void rrdset_name_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdset, void *rrdhost __maybe_unused) { + RRDSET *st = rrdset; + rrdset_flag_clear(st, RRDSET_FLAG_INDEXED_NAME); } -void __rrdset_check_wrlock(RRDSET *st, const char *file, const char *function, const unsigned long line) { - debug(D_RRD_CALLS, "Checking write lock on chart '%s'", st->id); +static inline void rrdset_index_add_name(RRDHOST *host, RRDSET *st) { + if(!st->name) return; + dictionary_set(host->rrdset_root_index_name, rrdset_name(st), st, sizeof(RRDSET)); +} - int ret = netdata_rwlock_tryrdlock(&st->rrdset_rwlock); - if(ret == 0) - fatal("RRDSET '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", st->id, function, line, file); +static inline void rrdset_index_del_name(RRDHOST *host, RRDSET *st) { + if(rrdset_flag_check(st, RRDSET_FLAG_INDEXED_NAME)) + dictionary_del(host->rrdset_root_index_name, rrdset_name(st)); } +static inline RRDSET *rrdset_index_find_name(RRDHOST *host, const char *name) { + return dictionary_get(host->rrdset_root_index_name, name); +} // ---------------------------------------------------------------------------- // RRDSET index -int rrdset_compare(void* a, void* b) { - if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1; - else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1; - else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id); +static inline void rrdset_update_permanent_labels(RRDSET *st) { + if(!st->rrdlabels) return; + + rrdlabels_add(st->rrdlabels, "_collect_plugin", rrdset_plugin_name(st), RRDLABEL_SRC_AUTO| RRDLABEL_FLAG_PERMANENT); + rrdlabels_add(st->rrdlabels, "_collect_module", rrdset_module_name(st), RRDLABEL_SRC_AUTO| RRDLABEL_FLAG_PERMANENT); } -static RRDSET *rrdset_index_find(RRDHOST *host, const char *id, uint32_t hash) { - RRDSET tmp; - strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX); - tmp.hash = (hash)?hash:simple_hash(tmp.id); +static STRING *rrdset_fix_name(RRDHOST *host, const char *chart_full_id, const char *type, const char *current_name, const char *name) { + if(!name || !*name) return NULL; + + char full_name[RRD_ID_LENGTH_MAX + 1]; + char sanitized_name[CONFIG_MAX_VALUE + 1]; + char new_name[CONFIG_MAX_VALUE + 1]; + + snprintfz(full_name, RRD_ID_LENGTH_MAX, "%s.%s", type, name); + rrdset_strncpyz_name(sanitized_name, full_name, CONFIG_MAX_VALUE); + strncpyz(new_name, sanitized_name, CONFIG_MAX_VALUE); + + if(rrdset_index_find_name(host, new_name)) { + debug(D_RRD_CALLS, "RRDSET: chart name '%s' on host '%s' already exists.", new_name, rrdhost_hostname(host)); + if(!strcmp(chart_full_id, full_name) && (!current_name || !*current_name)) { + unsigned i = 1; - return (RRDSET *)avl_search_lock(&(host->rrdset_root_index), (avl_t *) &tmp); + do { + snprintfz(new_name, CONFIG_MAX_VALUE, "%s_%u", sanitized_name, i); + i++; + } while (rrdset_index_find_name(host, new_name)); + + info("RRDSET: using name '%s' for chart '%s' on host '%s'.", new_name, full_name, rrdhost_hostname(host)); + } + else + return NULL; + } + + return string_strdupz(new_name); } -// ---------------------------------------------------------------------------- -// RRDSET name index +struct rrdset_constructor { + RRDHOST *host; + const char *type; + const char *id; + const char *name; + const char *family; + const char *context; + const char *title; + const char *units; + const char *plugin; + const char *module; + long priority; + int update_every; + RRDSET_TYPE chart_type; + RRD_MEMORY_MODE memory_mode; + long history_entries; + + enum { + RRDSET_REACT_NONE = 0, + RRDSET_REACT_NEW = (1 << 0), + RRDSET_REACT_UPDATED = (1 << 1), + RRDSET_REACT_PLUGIN_UPDATED = (1 << 2), + RRDSET_REACT_MODULE_UPDATED = (1 << 3), + RRDSET_REACT_CHART_ACTIVATED = (1 << 4), + } react_action; +}; + +// the constructor - the dictionary is write locked while this runs +static void rrdset_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdset, void *constructor_data) { + struct rrdset_constructor *ctr = constructor_data; + RRDHOST *host = ctr->host; + RRDSET *st = rrdset; + + const char *chart_full_id = dictionary_acquired_item_name(item); + + st->id = string_strdupz(chart_full_id); + + st->name = rrdset_fix_name(host, chart_full_id, ctr->type, NULL, ctr->name); + if(!st->name) + st->name = rrdset_fix_name(host, chart_full_id, ctr->type, NULL, ctr->id); + rrdset_index_add_name(host, st); + + st->parts.id = string_strdupz(ctr->id); + st->parts.type = string_strdupz(ctr->type); + st->parts.name = string_strdupz(ctr->name); + + st->family = (ctr->family && *ctr->family) ? rrd_string_strdupz(ctr->family) : rrd_string_strdupz(ctr->type); + st->context = (ctr->context && *ctr->context) ? rrd_string_strdupz(ctr->context) : rrd_string_strdupz(chart_full_id); + + st->units = rrd_string_strdupz(ctr->units); + st->title = rrd_string_strdupz(ctr->title); + st->plugin_name = rrd_string_strdupz(ctr->plugin); + st->module_name = rrd_string_strdupz(ctr->module); + st->priority = ctr->priority; + + st->cache_dir = rrdset_cache_dir(host, chart_full_id); + st->entries = (ctr->memory_mode != RRD_MEMORY_MODE_DBENGINE) ? align_entries_to_pagesize(ctr->memory_mode, ctr->history_entries) : 5; + st->update_every = ctr->update_every; + st->rrd_memory_mode = ctr->memory_mode; + + st->chart_type = ctr->chart_type; + st->gap_when_lost_iterations_above = (int) (gap_when_lost_iterations_above + 2); + st->rrdhost = host; + + st->flags = RRDSET_FLAG_SYNC_CLOCK + | RRDSET_FLAG_INDEXED_ID + | RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED + | RRDSET_FLAG_SENDER_REPLICATION_FINISHED + ; + + netdata_rwlock_init(&st->alerts.rwlock); + + if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { + if(!rrdset_memory_load_or_create_map_save(st, st->rrd_memory_mode)) { + info("Failed to use db mode %s for chart '%s', falling back to ram mode.", (st->rrd_memory_mode == RRD_MEMORY_MODE_MAP)?"map":"save", rrdset_name(st)); + st->rrd_memory_mode = RRD_MEMORY_MODE_RAM; + } + } + + // initialize the db tiers + { + for(size_t tier = 0; tier < storage_tiers ; tier++) { + STORAGE_ENGINE *eng = st->rrdhost->db[tier].eng; + if(!eng) continue; + + st->storage_metrics_groups[tier] = eng->api.collect_ops.metrics_group_get(host->db[tier].instance, &st->chart_uuid); + } + } + + rrddim_index_init(st); + + // chart variables - we need this for data collection to work (collector given chart variables) - not only health + rrdsetvar_index_init(st); + + if (host->health_enabled) { + st->rrdfamily = rrdfamily_add_and_acquire(host, rrdset_family(st)); + st->rrdvars = rrdvariables_create(); + rrddimvar_index_init(st); + } + + st->rrdlabels = rrdlabels_create(); + rrdset_update_permanent_labels(st); + + st->green = NAN; + st->red = NAN; + + ctr->react_action = RRDSET_REACT_NEW; +} + +// the destructor - the dictionary is write locked while this runs +static void rrdset_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdset, void *rrdhost) { + RRDHOST *host = rrdhost; + RRDSET *st = rrdset; + + rrdset_flag_clear(st, RRDSET_FLAG_INDEXED_ID); + + // cleanup storage engines + { + for(size_t tier = 0; tier < storage_tiers ; tier++) { + STORAGE_ENGINE *eng = st->rrdhost->db[tier].eng; + if(!eng) continue; + + eng->api.collect_ops.metrics_group_release(host->db[tier].instance, st->storage_metrics_groups[tier]); + } + } + + // remove it from the name index + rrdset_index_del_name(host, st); + + // release the collector info + dictionary_destroy(st->functions_view); + + rrdcalc_unlink_all_rrdset_alerts(st); + + // ------------------------------------------------------------------------ + // the order of destruction is important here + + // 1. delete RRDDIMVAR index - this will speed up the destruction of RRDDIMs + // because each dimension loops to find its own variables in this index. + // There are no references to the items on this index from the dimensions. + // To find their own, they have to walk-through the dictionary. + rrddimvar_index_destroy(st); // destroy the rrddimvar index + + // 2. delete RRDSETVAR index + rrdsetvar_index_destroy(st); // destroy the rrdsetvar index + + // 3. delete RRDVAR index after the above, to avoid triggering its garbage collector (they have references on this) + rrdvariables_destroy(st->rrdvars); // free all variables and destroy the rrdvar dictionary + + // 4. delete RRDFAMILY - this has to be last, because RRDDIMVAR and RRDSETVAR need the reference counter + rrdfamily_release(host, st->rrdfamily); // release the acquired rrdfamily -- has to be after all variables + + // 5. delete RRDDIMs, now their variables are not existing, so this is fast + rrddim_index_destroy(st); // free all the dimensions and destroy the dimensions index + + // 6. this has to be after the dimensions are freed, but before labels are freed (contexts need the labels) + rrdcontext_removed_rrdset(st); // let contexts know + + // 7. destroy the chart labels + rrdlabels_destroy(st->rrdlabels); // destroy the labels, after letting the contexts know + + rrdset_memory_file_free(st); // remove files of db mode save and map + + // ------------------------------------------------------------------------ + // free it + + netdata_rwlock_destroy(&st->alerts.rwlock); + + string_freez(st->id); + string_freez(st->name); + string_freez(st->parts.id); + string_freez(st->parts.type); + string_freez(st->parts.name); + string_freez(st->family); + string_freez(st->title); + string_freez(st->units); + string_freez(st->context); + string_freez(st->plugin_name); + string_freez(st->module_name); + + freez(st->exporting_flags); + freez(st->cache_dir); +} + +// the item to be inserted, is already in the dictionary +// this callback deals with the situation, migrating the existing object to the new values +// the dictionary is write locked while this runs +static bool rrdset_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdset, void *new_rrdset, void *constructor_data) { + (void)new_rrdset; // it is NULL + + struct rrdset_constructor *ctr = constructor_data; + RRDSET *st = rrdset; + + rrdset_isnot_obsolete(st); + + ctr->react_action = RRDSET_REACT_NONE; + + if (rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED)) { + rrdset_flag_clear(st, RRDSET_FLAG_ARCHIVED); + ctr->react_action |= RRDSET_REACT_CHART_ACTIVATED; + } + + if (rrdset_reset_name(st, (ctr->name && *ctr->name) ? ctr->name : ctr->id) == 2) + ctr->react_action |= RRDSET_REACT_UPDATED; + + if (unlikely(st->priority != ctr->priority)) { + st->priority = ctr->priority; + ctr->react_action |= RRDSET_REACT_UPDATED; + } -#define rrdset_from_avlname(avlname_ptr) ((RRDSET *)((avlname_ptr) - offsetof(RRDSET, avlname))) + if (unlikely(st->update_every != ctr->update_every)) { + rrdset_set_update_every(st, ctr->update_every); + ctr->react_action |= RRDSET_REACT_UPDATED; + } + + if(ctr->plugin && *ctr->plugin) { + STRING *old_plugin = st->plugin_name; + st->plugin_name = rrd_string_strdupz(ctr->plugin); + if (old_plugin != st->plugin_name) + ctr->react_action |= RRDSET_REACT_PLUGIN_UPDATED; + string_freez(old_plugin); + } -int rrdset_compare_name(void* a, void* b) { - RRDSET *A = rrdset_from_avlname(a); - RRDSET *B = rrdset_from_avlname(b); + if(ctr->module && *ctr->module) { + STRING *old_module = st->module_name; + st->module_name = rrd_string_strdupz(ctr->module); + if (old_module != st->module_name) + ctr->react_action |= RRDSET_REACT_MODULE_UPDATED; + string_freez(old_module); + } - // fprintf(stderr, "COMPARING: %s with %s\n", A->name, B->name); + if(ctr->title && *ctr->title) { + STRING *old_title = st->title; + st->title = rrd_string_strdupz(ctr->title); + if(old_title != st->title) + ctr->react_action |= RRDSET_REACT_UPDATED; + string_freez(old_title); + } - if(A->hash_name < B->hash_name) return -1; - else if(A->hash_name > B->hash_name) return 1; - else return strcmp(A->name, B->name); + if(ctr->units && *ctr->units) { + STRING *old_units = st->units; + st->units = rrd_string_strdupz(ctr->units); + if(old_units != st->units) + ctr->react_action |= RRDSET_REACT_UPDATED; + string_freez(old_units); + } + + if(ctr->family && *ctr->family) { + STRING *old_family = st->family; + st->family = rrd_string_strdupz(ctr->family); + if(old_family != st->family) + ctr->react_action |= RRDSET_REACT_UPDATED; + string_freez(old_family); + + // TODO - we should rename RRDFAMILY variables + } + + if(ctr->context && *ctr->context) { + STRING *old_context = st->context; + st->context = rrd_string_strdupz(ctr->context); + if(old_context != st->context) + ctr->react_action |= RRDSET_REACT_UPDATED; + string_freez(old_context); + } + + if(st->chart_type != ctr->chart_type) { + st->chart_type = ctr->chart_type; + ctr->react_action |= RRDSET_REACT_UPDATED; + } + + rrdset_update_permanent_labels(st); + + rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); + + return ctr->react_action != RRDSET_REACT_NONE; } -RRDSET *rrdset_index_add_name(RRDHOST *host, RRDSET *st) { - void *result; - // fprintf(stderr, "ADDING: %s (name: %s)\n", st->id, st->name); - result = avl_insert_lock(&host->rrdset_root_index_name, (avl_t *) (&st->avlname)); - if(result) return rrdset_from_avlname(result); - return NULL; +// this is called after all insertions/conflicts, with the dictionary unlocked, with a reference to RRDSET +// so, any actions requiring locks on other objects, should be placed here +static void rrdset_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdset, void *constructor_data) { + struct rrdset_constructor *ctr = constructor_data; + RRDSET *st = rrdset; + RRDHOST *host = st->rrdhost; + + st->last_accessed_time = now_realtime_sec(); + + if(host->health_enabled && (ctr->react_action & (RRDSET_REACT_NEW | RRDSET_REACT_CHART_ACTIVATED))) { + rrdset_flag_set(st, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION); + rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION); + } + + if(ctr->react_action & (RRDSET_REACT_NEW | RRDSET_REACT_PLUGIN_UPDATED | RRDSET_REACT_MODULE_UPDATED)) { + if (ctr->react_action & RRDSET_REACT_NEW) { + if(unlikely(rrdcontext_find_chart_uuid(st, &st->chart_uuid))) { + uuid_generate(st->chart_uuid); + bool found_in_sql = false; (void)found_in_sql; + +// bool found_in_sql = true; +// if(unlikely(sql_find_chart_uuid(host, st, &st->chart_uuid))) { +// uuid_generate(st->chart_uuid); +// found_in_sql = false; +// } + +#ifdef NETDATA_INTERNAL_CHECKS + char uuid_str[UUID_STR_LEN]; + uuid_unparse_lower(st->chart_uuid, uuid_str); + error_report("Chart UUID for host %s chart [%s] not found in context. It is now set to %s (%s)", + string2str(host->hostname), + string2str(st->name), uuid_str, found_in_sql ? "found in sqlite" : "newly generated"); +#endif + } + } + rrdset_flag_set(st, RRDSET_FLAG_METADATA_UPDATE); + rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_METADATA_UPDATE); + } + + rrdcontext_updated_rrdset(st); } -RRDSET *rrdset_index_del_name(RRDHOST *host, RRDSET *st) { - void *result; - // fprintf(stderr, "DELETING: %s (name: %s)\n", st->id, st->name); - result = (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index_name), (avl_t *)(&st->avlname)); - if(result) return rrdset_from_avlname(result); - return NULL; +void rrdset_index_init(RRDHOST *host) { + if(!host->rrdset_root_index) { + host->rrdset_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + + dictionary_register_insert_callback(host->rrdset_root_index, rrdset_insert_callback, NULL); + dictionary_register_conflict_callback(host->rrdset_root_index, rrdset_conflict_callback, NULL); + dictionary_register_react_callback(host->rrdset_root_index, rrdset_react_callback, NULL); + dictionary_register_delete_callback(host->rrdset_root_index, rrdset_delete_callback, host); + } + + if(!host->rrdset_root_index_name) { + host->rrdset_root_index_name = dictionary_create( + DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE); + + dictionary_register_insert_callback(host->rrdset_root_index_name, rrdset_name_insert_callback, host); + dictionary_register_delete_callback(host->rrdset_root_index_name, rrdset_name_delete_callback, host); + } } +void rrdset_index_destroy(RRDHOST *host) { + // destroy the name index first + dictionary_destroy(host->rrdset_root_index_name); + host->rrdset_root_index_name = NULL; -// ---------------------------------------------------------------------------- -// RRDSET - find charts + // destroy the id index last + dictionary_destroy(host->rrdset_root_index); + host->rrdset_root_index = NULL; +} -static inline RRDSET *rrdset_index_find_name(RRDHOST *host, const char *name, uint32_t hash) { - void *result = NULL; - RRDSET tmp; - tmp.name = name; - tmp.hash_name = (hash)?hash:simple_hash(tmp.name); +static inline RRDSET *rrdset_index_add(RRDHOST *host, const char *id, struct rrdset_constructor *st_ctr) { + return dictionary_set_advanced(host->rrdset_root_index, id, -1, NULL, sizeof(RRDSET), st_ctr); +} - result = avl_search_lock(&host->rrdset_root_index_name, (avl_t *) (&(tmp.avlname))); - if(result) return rrdset_from_avlname(result); +static inline void rrdset_index_del(RRDHOST *host, RRDSET *st) { + if(rrdset_flag_check(st, RRDSET_FLAG_INDEXED_ID)) + dictionary_del(host->rrdset_root_index, rrdset_id(st)); +} - return NULL; +static RRDSET *rrdset_index_find(RRDHOST *host, const char *id) { + // TODO - the name index should have an acquired dictionary item, not just a pointer to RRDSET + return dictionary_get(host->rrdset_root_index, id); } +// ---------------------------------------------------------------------------- +// RRDSET - find charts + inline RRDSET *rrdset_find(RRDHOST *host, const char *id) { - debug(D_RRD_CALLS, "rrdset_find() for chart '%s' in host '%s'", id, host->hostname); - RRDSET *st = rrdset_index_find(host, id, 0); + debug(D_RRD_CALLS, "rrdset_find() for chart '%s' in host '%s'", id, rrdhost_hostname(host)); + RRDSET *st = rrdset_index_find(host, id); + + if(st) + st->last_accessed_time = now_realtime_sec(); + return(st); } inline RRDSET *rrdset_find_bytype(RRDHOST *host, const char *type, const char *id) { - debug(D_RRD_CALLS, "rrdset_find_bytype() for chart '%s.%s' in host '%s'", type, id, host->hostname); + debug(D_RRD_CALLS, "rrdset_find_bytype() for chart '%s.%s' in host '%s'", type, id, rrdhost_hostname(host)); char buf[RRD_ID_LENGTH_MAX + 1]; strncpyz(buf, type, RRD_ID_LENGTH_MAX - 1); @@ -105,8 +460,8 @@ inline RRDSET *rrdset_find_bytype(RRDHOST *host, const char *type, const char *i } inline RRDSET *rrdset_find_byname(RRDHOST *host, const char *name) { - debug(D_RRD_CALLS, "rrdset_find_byname() for chart '%s' in host '%s'", name, host->hostname); - RRDSET *st = rrdset_index_find_name(host, name, 0); + debug(D_RRD_CALLS, "rrdset_find_byname() for chart '%s' in host '%s'", name, rrdhost_hostname(host)); + RRDSET *st = rrdset_index_find_name(host, name); return(st); } @@ -128,57 +483,32 @@ char *rrdset_strncpyz_name(char *to, const char *from, size_t length) { return to; } -int rrdset_set_name(RRDSET *st, const char *name) { - if(unlikely(st->name && !strcmp(st->name, name))) +int rrdset_reset_name(RRDSET *st, const char *name) { + if(unlikely(!strcmp(rrdset_name(st), name))) return 1; RRDHOST *host = st->rrdhost; - debug(D_RRD_CALLS, "rrdset_set_name() old: '%s', new: '%s'", st->name?st->name:"", name); + debug(D_RRD_CALLS, "rrdset_reset_name() old: '%s', new: '%s'", rrdset_name(st), name); - char full_name[RRD_ID_LENGTH_MAX + 1]; - char sanitized_name[CONFIG_MAX_VALUE + 1]; - char new_name[CONFIG_MAX_VALUE + 1]; - - snprintfz(full_name, RRD_ID_LENGTH_MAX, "%s.%s", st->type, name); - rrdset_strncpyz_name(sanitized_name, full_name, CONFIG_MAX_VALUE); - strncpyz(new_name, sanitized_name, CONFIG_MAX_VALUE); - - if(rrdset_index_find_name(host, new_name, 0)) { - debug(D_RRD_CALLS, "RRDSET: chart name '%s' on host '%s' already exists.", new_name, host->hostname); - if(!strcmp(st->id, full_name) && !st->name) { - unsigned i = 1; - - do { - snprintfz(new_name, CONFIG_MAX_VALUE, "%s_%u", sanitized_name, i); - i++; - } while (rrdset_index_find_name(host, new_name, 0)); - - info("RRDSET: using name '%s' for chart '%s' on host '%s'.", new_name, full_name, host->hostname); - } else { - return 0; - } - } + STRING *name_string = rrdset_fix_name(host, rrdset_id(st), rrdset_parts_type(st), string2str(st->name), name); + if(!name_string) return 0; if(st->name) { rrdset_index_del_name(host, st); - st->name = strdupz(new_name); - st->hash_name = simple_hash(st->name); + string_freez(st->name); + st->name = name_string; rrdsetvar_rename_all(st); } - else { - st->name = strdupz(new_name); - st->hash_name = simple_hash(st->name); - } + else + st->name = name_string; - rrdset_wrlock(st); RRDDIM *rd; - rrddim_foreach_write(rd, st) + rrddim_foreach_read(rd, st) rrddimvar_rename_all(rd); - rrdset_unlock(st); + rrddim_foreach_done(rd); - if(unlikely(rrdset_index_add_name(host, st) != st)) - error("RRDSET: INTERNAL ERROR: attempted to index duplicate chart name '%s'", st->name); + rrdset_index_add_name(host, st); rrdset_flag_clear(st, RRDSET_FLAG_EXPORTING_SEND); rrdset_flag_clear(st, RRDSET_FLAG_EXPORTING_IGNORE); @@ -190,15 +520,65 @@ int rrdset_set_name(RRDSET *st, const char *name) { return 2; } +// get the timestamp of the last entry in the round-robin database +time_t rrdset_last_entry_t(RRDSET *st) { + RRDDIM *rd; + time_t last_entry_t = 0; + + rrddim_foreach_read(rd, st) { + time_t t = rrddim_last_entry_t(rd); + if(t > last_entry_t) last_entry_t = t; + } + rrddim_foreach_done(rd); + + return last_entry_t; +} + +// get the timestamp of first entry in the round-robin database +time_t rrdset_first_entry_t(RRDSET *st) { + RRDDIM *rd; + time_t first_entry_t = LONG_MAX; + + rrddim_foreach_read(rd, st) { + time_t t = rrddim_first_entry_t(rd); + if(t < first_entry_t) + first_entry_t = t; + } + rrddim_foreach_done(rd); + + if (unlikely(LONG_MAX == first_entry_t)) return 0; + return first_entry_t; +} + +time_t rrdset_first_entry_t_of_tier(RRDSET *st, size_t tier) { + if(unlikely(tier > storage_tiers)) + return 0; + + RRDDIM *rd; + time_t first_entry_t = LONG_MAX; + + rrddim_foreach_read(rd, st) { + time_t t = rrddim_first_entry_t_of_tier(rd, tier); + if(t && t < first_entry_t) + first_entry_t = t; + } + rrddim_foreach_done(rd); + + if (unlikely(LONG_MAX == first_entry_t)) return 0; + return first_entry_t; +} + inline void rrdset_is_obsolete(RRDSET *st) { if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED))) { - info("Cannot obsolete already archived chart %s", st->name); + info("Cannot obsolete already archived chart %s", rrdset_name(st)); return; } if(unlikely(!(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)))) { rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE); - st->rrdhost->obsolete_charts_count++; + rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_PENDING_OBSOLETE_CHARTS); + + st->last_accessed_time = now_realtime_sec(); rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); @@ -212,7 +592,7 @@ inline void rrdset_is_obsolete(RRDSET *st) { inline void rrdset_isnot_obsolete(RRDSET *st) { if(unlikely((rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)))) { rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); - st->rrdhost->obsolete_charts_count--; + st->last_accessed_time = now_realtime_sec(); rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); @@ -230,38 +610,52 @@ inline void rrdset_update_heterogeneous_flag(RRDSET *st) { rrdset_flag_clear(st, RRDSET_FLAG_HOMOGENEOUS_CHECK); - RRD_ALGORITHM algorithm = st->dimensions->algorithm; - collected_number multiplier = ABS(st->dimensions->multiplier); - collected_number divisor = ABS(st->dimensions->divisor); + bool init = false, is_heterogeneous = false; + RRD_ALGORITHM algorithm; + collected_number multiplier; + collected_number divisor; rrddim_foreach_read(rd, st) { + if(!init) { + algorithm = rd->algorithm; + multiplier = rd->multiplier; + divisor = ABS(rd->divisor); + init = true; + continue; + } + if(algorithm != rd->algorithm || multiplier != ABS(rd->multiplier) || divisor != ABS(rd->divisor)) { if(!rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) { #ifdef NETDATA_INTERNAL_CHECKS info("Dimension '%s' added on chart '%s' of host '%s' is not homogeneous to other dimensions already present (algorithm is '%s' vs '%s', multiplier is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ", divisor is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ").", - rd->name, - st->name, - host->hostname, - rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(algorithm), - rd->multiplier, multiplier, - rd->divisor, divisor + rrddim_name(rd), + rrdset_name(st), + rrdhost_hostname(host), + rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(algorithm), + rd->multiplier, multiplier, + rd->divisor, divisor ); #endif rrdset_flag_set(st, RRDSET_FLAG_HETEROGENEOUS); } - return; + + is_heterogeneous = true; + break; } } + rrddim_foreach_done(rd); - rrdset_flag_clear(st, RRDSET_FLAG_HETEROGENEOUS); - rrdcontext_updated_rrdset_flags(st); + if(!is_heterogeneous) { + rrdset_flag_clear(st, RRDSET_FLAG_HETEROGENEOUS); + rrdcontext_updated_rrdset_flags(st); + } } // ---------------------------------------------------------------------------- // RRDSET - reset a chart void rrdset_reset(RRDSET *st) { - debug(D_RRD_CALLS, "rrdset_reset() %s", st->name); + debug(D_RRD_CALLS, "rrdset_reset() %s", rrdset_name(st)); st->last_collected_time.tv_sec = 0; st->last_collected_time.tv_usec = 0; @@ -270,7 +664,6 @@ void rrdset_reset(RRDSET *st) { st->current_entry = 0; st->counter = 0; st->counter_done = 0; - st->rrddim_page_alignment = 0; RRDDIM *rd; rrddim_foreach_read(rd, st) { @@ -279,12 +672,13 @@ void rrdset_reset(RRDSET *st) { rd->collections_counter = 0; if(!rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) { - for(int tier = 0; tier < storage_tiers ;tier++) { + for(size_t tier = 0; tier < storage_tiers ;tier++) { if(rd->tiers[tier]) - rd->tiers[tier]->collect_ops.flush(rd->tiers[tier]->db_collection_handle); + rd->tiers[tier]->collect_ops->flush(rd->tiers[tier]->db_collection_handle); } } } + rrddim_foreach_done(rd); } // ---------------------------------------------------------------------------- @@ -336,101 +730,22 @@ static inline void last_updated_time_align(RRDSET *st) { void rrdset_free(RRDSET *st) { if(unlikely(!st)) return; - - RRDHOST *host = st->rrdhost; - - rrdhost_check_wrlock(host); // make sure we have a write lock on the host - rrdset_wrlock(st); // lock this RRDSET - // info("Removing chart '%s' ('%s')", st->id, st->name); - - // ------------------------------------------------------------------------ - // remove it from the indexes - - if(unlikely(rrdset_index_del(host, st) != st)) - error("RRDSET: INTERNAL ERROR: attempt to remove from index chart '%s', removed a different chart.", st->id); - - rrdset_index_del_name(host, st); - - // ------------------------------------------------------------------------ - // free its children structures - - freez(st->exporting_flags); - - while(st->variables) rrdsetvar_free(st->variables); -// while(st->alarms) rrdsetcalc_unlink(st->alarms); - /* We must free all connected alarms here in case this has been an ephemeral chart whose alarm was - * created by a template. This leads to an effective memory leak, which cannot be detected since the - * alarms will still be connected to the host, and freed during shutdown. */ - while(st->alarms) rrdcalc_unlink_and_free(st->rrdhost, st->alarms); - while(st->dimensions) rrddim_free(st, st->dimensions); - - rrdfamily_free(host, st->rrdfamily); - - debug(D_RRD_CALLS, "RRDSET: Cleaning up remaining chart variables for host '%s', chart '%s'", host->hostname, st->id); - rrdvar_free_remaining_variables(host, &st->rrdvar_root_index); - - // ------------------------------------------------------------------------ - // unlink it from the host - - if(st == host->rrdset_root) { - host->rrdset_root = st->next; - } - else { - // find the previous one - RRDSET *s; - for(s = host->rrdset_root; s && s->next != st ; s = s->next) ; - - // bypass it - if(s) s->next = st->next; - else error("Request to free RRDSET '%s': cannot find it under host '%s'", st->id, host->hostname); - } - - rrdset_unlock(st); - - // this has to be after the dimensions are freed - rrdcontext_removed_rrdset(st); - - // ------------------------------------------------------------------------ - // free it - - netdata_rwlock_destroy(&st->rrdset_rwlock); - - // free directly allocated members - freez((void *)st->name); - freez(st->type); - freez(st->family); - freez(st->title); - freez(st->units); - freez(st->context); - freez(st->cache_dir); - freez(st->plugin_name); - freez(st->module_name); - freez(st->state->old_title); - freez(st->state->old_units); - freez(st->state->old_context); - rrdlabels_destroy(st->state->chart_labels); - freez(st->state); - freez(st->chart_uuid); - - rrdset_memory_file_free(st); - freez(st); + rrdset_index_del(st->rrdhost, st); } void rrdset_save(RRDSET *st) { - rrdset_check_rdlock(st); - rrdset_memory_file_save(st); RRDDIM *rd; rrddim_foreach_read(rd, st) rrddim_memory_file_save(rd); + rrddim_foreach_done(rd); } void rrdset_delete_files(RRDSET *st) { RRDDIM *rd; - rrdset_check_rdlock(st); - info("Deleting chart '%s' ('%s') from disk...", st->id, st->name); + info("Deleting chart '%s' ('%s') from disk...", rrdset_id(st), rrdset_name(st)); if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { const char *cache_filename = rrdset_cache_filename(st); @@ -440,7 +755,7 @@ void rrdset_delete_files(RRDSET *st) { error("Cannot delete chart header file '%s'", cache_filename); } else - error("Cannot find the cache filename of chart '%s'", st->id); + error("Cannot find the cache filename of chart '%s'", rrdset_id(st)); } rrddim_foreach_read(rd, st) { @@ -451,6 +766,7 @@ void rrdset_delete_files(RRDSET *st) { if(unlikely(unlink(cache_filename) == -1)) error("Cannot delete dimension file '%s'", cache_filename); } + rrddim_foreach_done(rd); recursively_delete_dir(st->cache_dir, "left-over chart"); } @@ -458,9 +774,7 @@ void rrdset_delete_files(RRDSET *st) { void rrdset_delete_obsolete_dimensions(RRDSET *st) { RRDDIM *rd; - rrdset_check_rdlock(st); - - info("Deleting dimensions of chart '%s' ('%s') from disk...", st->id, st->name); + info("Deleting dimensions of chart '%s' ('%s') from disk...", rrdset_id(st), rrdset_name(st)); rrddim_foreach_read(rd, st) { if(rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) { @@ -471,30 +785,12 @@ void rrdset_delete_obsolete_dimensions(RRDSET *st) { error("Cannot delete dimension file '%s'", cache_filename); } } + rrddim_foreach_done(rd); } // ---------------------------------------------------------------------------- // RRDSET - create a chart -static inline RRDSET *rrdset_find_on_create(RRDHOST *host, const char *fullid) { - RRDSET *st = rrdset_find(host, fullid); - if(unlikely(st)) { - rrdset_isnot_obsolete(st); - debug(D_RRD_CALLS, "RRDSET '%s', already exists.", fullid); - return st; - } - - return NULL; -} - -static inline void rrdset_update_permanent_labels(RRDSET *st) { - if(!st->state || !st->state->chart_labels) return; - - rrdlabels_add(st->state->chart_labels, "_collect_plugin", st->plugin_name, RRDLABEL_SRC_AUTO| RRDLABEL_FLAG_PERMANENT); - rrdlabels_add(st->state->chart_labels, "_collect_module", st->module_name, RRDLABEL_SRC_AUTO| RRDLABEL_FLAG_PERMANENT); - rrdlabels_add(st->state->chart_labels, "_instance_family", st->family, RRDLABEL_SRC_AUTO| RRDLABEL_FLAG_PERMANENT); -} - RRDSET *rrdset_create_custom( RRDHOST *host , const char *type @@ -505,343 +801,88 @@ RRDSET *rrdset_create_custom( , const char *title , const char *units , const char *plugin - , const char *module - , long priority - , int update_every - , RRDSET_TYPE chart_type - , RRD_MEMORY_MODE memory_mode - , long history_entries -) { - if(!type || !type[0]) { - fatal("Cannot create rrd stats without a type: id '%s', name '%s', family '%s', context '%s', title '%s', units '%s', plugin '%s', module '%s'." - , (id && *id)?id:"" - , (name && *name)?name:"" - , (family && *family)?family:"" - , (context && *context)?context:"" - , (title && *title)?title:"" - , (units && *units)?units:"" - , (plugin && *plugin)?plugin:"" - , (module && *module)?module:"" - ); - return NULL; - } - - if(!id || !id[0]) { - fatal("Cannot create rrd stats without an id: type '%s', name '%s', family '%s', context '%s', title '%s', units '%s', plugin '%s', module '%s'." - , type - , (name && *name)?name:"" - , (family && *family)?family:"" - , (context && *context)?context:"" - , (title && *title)?title:"" - , (units && *units)?units:"" - , (plugin && *plugin)?plugin:"" - , (module && *module)?module:"" - ); - return NULL; - } - - if (host != localhost) { - host->senders_last_chart_command = now_realtime_sec(); - } - - // ------------------------------------------------------------------------ - // check if it already exists - - char fullid[RRD_ID_LENGTH_MAX + 1]; - snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id); - - int changed_from_archived_to_active = 0; - RRDSET *st = rrdset_find_on_create(host, fullid); - if (st) { - int mark_rebuild = 0; - if (rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED)) { - rrdset_flag_clear(st, RRDSET_FLAG_ARCHIVED); - changed_from_archived_to_active = 1; - mark_rebuild |= META_CHART_ACTIVATED; - } - char *old_plugin = NULL, *old_module = NULL, *old_title = NULL, *old_context = NULL, - *old_title_v = NULL, *old_context_v = NULL, *old_units_v = NULL, *old_units = NULL; - int rc; - - if(unlikely(name)) - rc = rrdset_set_name(st, name); - else - rc = rrdset_set_name(st, id); - - if (rc == 2) - mark_rebuild |= META_CHART_UPDATED; - - if (unlikely(st->priority != priority)) { - st->priority = priority; - mark_rebuild |= META_CHART_UPDATED; - } - if (unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && st->update_every != update_every)) { - st->update_every = update_every; - mark_rebuild |= META_CHART_UPDATED; - } - - if (plugin && st->plugin_name) { - if (unlikely(strcmp(plugin, st->plugin_name))) { - old_plugin = st->plugin_name; - st->plugin_name = strdupz(plugin); - mark_rebuild |= META_PLUGIN_UPDATED; - } - } else { - if (plugin != st->plugin_name) { // one is NULL? - old_plugin = st->plugin_name; - st->plugin_name = plugin ? strdupz(plugin) : NULL; - mark_rebuild |= META_PLUGIN_UPDATED; - } - } - - if (module && st->module_name) { - if (unlikely(strcmp(module, st->module_name))) { - old_module = st->module_name; - st->module_name = strdupz(module); - mark_rebuild |= META_MODULE_UPDATED; - } - } else { - if (module != st->module_name) { - if (st->module_name && *st->module_name) { - old_module = st->module_name; - st->module_name = module ? strdupz(module) : NULL; - mark_rebuild |= META_MODULE_UPDATED; - } - } - } - - if (unlikely(title && st->state->old_title && strcmp(st->state->old_title, title))) { - char *new_title = strdupz(title); - old_title_v = st->state->old_title; - st->state->old_title = strdupz(title); - json_fix_string(new_title); - old_title = st->title; - st->title = new_title; - mark_rebuild |= META_CHART_UPDATED; - } - - if (unlikely(units && st->state->old_units && strcmp(st->state->old_units, units))) { - char *new_units = strdupz(units); - old_units_v = st->state->old_units; - st->state->old_units = strdupz(units); - json_fix_string(new_units); - old_units= st->units; - st->units = new_units; - mark_rebuild |= META_CHART_UPDATED; - } - - - if (st->chart_type != chart_type) { - st->chart_type = chart_type; - mark_rebuild |= META_CHART_UPDATED; - } - - if (unlikely(context && st->state->old_context && strcmp(st->state->old_context, context))) { - char *new_context = strdupz(context); - old_context_v = st->state->old_context; - st->state->old_context = strdupz(context); - json_fix_string(new_context); - old_context = st->context; - st->context = new_context; - st->hash_context = simple_hash(st->context); - mark_rebuild |= META_CHART_UPDATED; - } - - if (mark_rebuild) { - rrdset_flag_clear(st, RRDSET_FLAG_ACLK); - freez(old_plugin); - freez(old_module); - freez(old_title); - freez(old_units); - freez(old_context); - freez(old_title_v); - freez(old_units_v); - freez(old_context_v); - if (mark_rebuild != META_CHART_ACTIVATED) { - info("Collector updated metadata for chart %s", st->id); - sched_yield(); - } - } - if (mark_rebuild & (META_CHART_UPDATED | META_PLUGIN_UPDATED | META_MODULE_UPDATED)) { - debug(D_METADATALOG, "CHART [%s] metadata updated", st->id); - int rc = update_chart_metadata(st->chart_uuid, st, id, name); - if (unlikely(rc)) - error_report("Failed to update chart metadata in the database"); - - if (!changed_from_archived_to_active) { - rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); - } - } - /* Fall-through during switch from archived to active so that the host lock is taken and health is linked */ - if (!changed_from_archived_to_active) { - rrdset_update_permanent_labels(st); - rrdcontext_updated_rrdset(st); - return st; - } - } - - rrdhost_wrlock(host); - - st = rrdset_find_on_create(host, fullid); - if(st) { - if (changed_from_archived_to_active) { - rrdset_flag_clear(st, RRDSET_FLAG_ARCHIVED); - rrdsetvar_create(st, "last_collected_t", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, RRDVAR_OPTION_DEFAULT); - rrdsetvar_create(st, "collected_total_raw", RRDVAR_TYPE_TOTAL, &st->last_collected_total, RRDVAR_OPTION_DEFAULT); - rrdsetvar_create(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, RRDVAR_OPTION_DEFAULT); - rrdsetvar_create(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, RRDVAR_OPTION_DEFAULT); - rrdsetvar_create(st, "update_every", RRDVAR_TYPE_INT, &st->update_every, RRDVAR_OPTION_DEFAULT); - rrdsetcalc_link_matching(st); - rrdcalctemplate_link_matching(st); - } - rrdhost_unlock(host); - rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); - rrdcontext_updated_rrdset(st); - return st; - } - - // ------------------------------------------------------------------------ - // get the options from the config, we need to create it - - long entries = 5; - if (memory_mode != RRD_MEMORY_MODE_DBENGINE) - entries = align_entries_to_pagesize(memory_mode, history_entries); - - char *cache_dir = rrdset_cache_dir(host, fullid); - - // ------------------------------------------------------------------------ - // load it or allocate it - - debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id); - - st = callocz(1, sizeof(RRDSET)); - st->state = callocz(1, sizeof(*st->state)); - - strcpy(st->id, fullid); - st->hash = simple_hash(st->id); - - st->rrdhost = host; - st->cache_dir = cache_dir; - st->entries = entries; - st->update_every = update_every; - - if(memory_mode == RRD_MEMORY_MODE_SAVE || memory_mode == RRD_MEMORY_MODE_MAP) { - if(!rrdset_memory_load_or_create_map_save(st, memory_mode)) { - info("Failed to use memory mode %s for chart '%s', falling back to ram", (memory_mode == RRD_MEMORY_MODE_MAP)?"map":"save", st->name); - memory_mode = RRD_MEMORY_MODE_RAM; - } - } - st->rrd_memory_mode = memory_mode; - - st->plugin_name = plugin?strdupz(plugin):NULL; - st->module_name = module?strdupz(module):NULL; - st->chart_type = chart_type; - st->type = strdupz(type); - st->family = family ? strdupz(family) : strdupz(st->type); - json_fix_string(st->family); - - st->state->is_ar_chart = strcmp(st->id, ML_ANOMALY_RATES_CHART_ID) == 0; - - st->units = units ? strdupz(units) : strdupz(""); - st->state->old_units = strdupz(st->units); - json_fix_string(st->units); - - st->context = context ? strdupz(context) : strdupz(st->id); - st->state->old_context = strdupz(st->context); - json_fix_string(st->context); - st->hash_context = simple_hash(st->context); - - st->priority = priority; - - rrdset_flag_set(st, RRDSET_FLAG_SYNC_CLOCK); - - st->green = NAN; - st->red = NAN; - - st->gap_when_lost_iterations_above = (int) (gap_when_lost_iterations_above + 2); - - avl_init_lock(&st->dimensions_index, rrddim_compare); - avl_init_lock(&st->rrdvar_root_index, rrdvar_compare); - - netdata_rwlock_init(&st->rrdset_rwlock); - st->state->chart_labels = rrdlabels_create(); - rrdset_update_permanent_labels(st); - - if(name && *name && rrdset_set_name(st, name)) - // we did set the name - ; - else - // could not use the name, use the id - rrdset_set_name(st, id); - - st->title = strdupz(title); - st->state->old_title = strdupz(st->title); - json_fix_string(st->title); - - st->rrdfamily = rrdfamily_create(host, st->family); + , const char *module + , long priority + , int update_every + , RRDSET_TYPE chart_type + , RRD_MEMORY_MODE memory_mode + , long history_entries +) { + if (host != localhost) + host->senders_last_chart_command = now_realtime_sec(); - st->next = host->rrdset_root; - host->rrdset_root = st; + if(!type || !type[0]) + fatal("Cannot create rrd stats without a type: id '%s', name '%s', family '%s', context '%s', title '%s', units '%s', plugin '%s', module '%s'." + , (id && *id)?id:"" + , (name && *name)?name:"" + , (family && *family)?family:"" + , (context && *context)?context:"" + , (title && *title)?title:"" + , (units && *units)?units:"" + , (plugin && *plugin)?plugin:"" + , (module && *module)?module:"" + ); - if(host->health_enabled) { - rrdsetvar_create(st, "last_collected_t", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, RRDVAR_OPTION_DEFAULT); - rrdsetvar_create(st, "collected_total_raw", RRDVAR_TYPE_TOTAL, &st->last_collected_total, RRDVAR_OPTION_DEFAULT); - rrdsetvar_create(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, RRDVAR_OPTION_DEFAULT); - rrdsetvar_create(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, RRDVAR_OPTION_DEFAULT); - rrdsetvar_create(st, "update_every", RRDVAR_TYPE_INT, &st->update_every, RRDVAR_OPTION_DEFAULT); - } + if(!id || !id[0]) + fatal("Cannot create rrd stats without an id: type '%s', name '%s', family '%s', context '%s', title '%s', units '%s', plugin '%s', module '%s'." + , type + , (name && *name)?name:"" + , (family && *family)?family:"" + , (context && *context)?context:"" + , (title && *title)?title:"" + , (units && *units)?units:"" + , (plugin && *plugin)?plugin:"" + , (module && *module)?module:"" + ); - if(unlikely(rrdset_index_add(host, st) != st)) - error("RRDSET: INTERNAL ERROR: attempt to index duplicate chart '%s'", st->id); + // ------------------------------------------------------------------------ + // check if it already exists - rrdsetcalc_link_matching(st); - rrdcalctemplate_link_matching(st); + char full_id[RRD_ID_LENGTH_MAX + 1]; + snprintfz(full_id, RRD_ID_LENGTH_MAX, "%s.%s", type, id); - st->chart_uuid = find_chart_uuid(host, type, id, name); - if (unlikely(!st->chart_uuid)) - st->chart_uuid = create_chart_uuid(st, id, name); - else - update_chart_metadata(st->chart_uuid, st, id, name); + // ------------------------------------------------------------------------ + // allocate it - store_active_chart(st->chart_uuid); - compute_chart_hash(st); + debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id); - rrdhost_unlock(host); - rrdcontext_updated_rrdset(st); + struct rrdset_constructor tmp = { + .host = host, + .type = type, + .id = id, + .name = name, + .family = family, + .context = context, + .title = title, + .units = units, + .plugin = plugin, + .module = module, + .priority = priority, + .update_every = update_every, + .chart_type = chart_type, + .memory_mode = memory_mode, + .history_entries = history_entries, + }; + + RRDSET *st = rrdset_index_add(host, full_id, &tmp); return(st); } - // ---------------------------------------------------------------------------- // RRDSET - data collection iteration control -inline void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds) { - if(unlikely(!st->last_collected_time.tv_sec || !microseconds || (rrdset_flag_check(st, RRDSET_FLAG_SYNC_CLOCK)))) { - // call the full next_usec() function - rrdset_next_usec(st, microseconds); - return; - } - - st->usec_since_last_update = microseconds; -} - -inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) { - struct timeval now; - now_realtime_timeval(&now); - +void rrdset_timed_next(RRDSET *st, struct timeval now, usec_t duration_since_last_update) { #ifdef NETDATA_INTERNAL_CHECKS char *discard_reason = NULL; - usec_t discarded = microseconds; + usec_t discarded = duration_since_last_update; #endif if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_SYNC_CLOCK))) { // the chart needs to be re-synced to current time rrdset_flag_clear(st, RRDSET_FLAG_SYNC_CLOCK); - // discard the microseconds supplied - microseconds = 0; + // discard the duration supplied + duration_since_last_update = 0; #ifdef NETDATA_INTERNAL_CHECKS if(!discard_reason) discard_reason = "SYNC CLOCK FLAG"; @@ -850,14 +891,14 @@ inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) { if(unlikely(!st->last_collected_time.tv_sec)) { // the first entry - microseconds = st->update_every * USEC_PER_SEC; + duration_since_last_update = st->update_every * USEC_PER_SEC; #ifdef NETDATA_INTERNAL_CHECKS if(!discard_reason) discard_reason = "FIRST DATA COLLECTION"; #endif } - else if(unlikely(!microseconds)) { + else if(unlikely(!duration_since_last_update)) { // no dt given by the plugin - microseconds = dt_usec(&now, &st->last_collected_time); + duration_since_last_update = dt_usec(&now, &st->last_collected_time); #ifdef NETDATA_INTERNAL_CHECKS if(!discard_reason) discard_reason = "NO USEC GIVEN BY COLLECTOR"; #endif @@ -870,7 +911,13 @@ inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) { // oops! the database is in the future #ifdef NETDATA_INTERNAL_CHECKS info("RRD database for chart '%s' on host '%s' is %0.5" NETDATA_DOUBLE_MODIFIER - " secs in the future (counter #%zu, update #%zu). Adjusting it to current time.", st->id, st->rrdhost->hostname, (NETDATA_DOUBLE)-since_last_usec / USEC_PER_SEC, st->counter, st->counter_done); + " secs in the future (counter #%zu, update #%zu). Adjusting it to current time." + , rrdset_id(st) + , rrdhost_hostname(st->rrdhost) + , (NETDATA_DOUBLE)-since_last_usec / USEC_PER_SEC + , st->counter + , st->counter_done + ); #endif st->last_collected_time.tv_sec = now.tv_sec - st->update_every; @@ -881,7 +928,7 @@ inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) { st->last_updated.tv_usec = now.tv_usec; last_updated_time_align(st); - microseconds = st->update_every * USEC_PER_SEC; + duration_since_last_update = st->update_every * USEC_PER_SEC; #ifdef NETDATA_INTERNAL_CHECKS if(!discard_reason) discard_reason = "COLLECTION TIME IN FUTURE"; #endif @@ -890,24 +937,24 @@ inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) { // oops! the database is too far behind #ifdef NETDATA_INTERNAL_CHECKS info("RRD database for chart '%s' on host '%s' is %0.5" NETDATA_DOUBLE_MODIFIER - " secs in the past (counter #%zu, update #%zu). Adjusting it to current time.", st->id, st->rrdhost->hostname, (NETDATA_DOUBLE)since_last_usec / USEC_PER_SEC, st->counter, st->counter_done); + " secs in the past (counter #%zu, update #%zu). Adjusting it to current time.", rrdset_id(st), rrdhost_hostname(st->rrdhost), (NETDATA_DOUBLE)since_last_usec / USEC_PER_SEC, st->counter, st->counter_done); #endif - microseconds = (usec_t)since_last_usec; + duration_since_last_update = (usec_t)since_last_usec; #ifdef NETDATA_INTERNAL_CHECKS if(!discard_reason) discard_reason = "COLLECTION TIME TOO FAR IN THE PAST"; #endif } #ifdef NETDATA_INTERNAL_CHECKS - if(since_last_usec > 0 && (susec_t)microseconds < since_last_usec) { + if(since_last_usec > 0 && (susec_t) duration_since_last_update < since_last_usec) { static __thread susec_t min_delta = USEC_PER_SEC * 3600, permanent_min_delta = 0; static __thread time_t last_t = 0; // the first time initialize it so that it will make the check later if(last_t == 0) last_t = now.tv_sec + 60; - susec_t delta = since_last_usec - (susec_t)microseconds; + susec_t delta = since_last_usec - (susec_t) duration_since_last_update; if(delta < min_delta) min_delta = delta; if(now.tv_sec >= last_t + 60) { @@ -924,31 +971,49 @@ inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) { #endif } - #ifdef NETDATA_INTERNAL_CHECKS - debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds); - rrdset_debug(st, "NEXT: %llu microseconds", microseconds); + debug(D_RRD_CALLS, "rrdset_timed_next() for chart %s with duration since last update %llu usec", rrdset_name(st), duration_since_last_update); + rrdset_debug(st, "NEXT: %llu microseconds", duration_since_last_update); - if(discarded && discarded != microseconds) - info("host '%s', chart '%s': discarded data collection time of %llu usec, replaced with %llu usec, reason: '%s'", st->rrdhost->hostname, st->id, discarded, microseconds, discard_reason?discard_reason:"UNDEFINED"); + internal_error(discarded && discarded != duration_since_last_update, + "host '%s', chart '%s': discarded data collection time of %llu usec, " + "replaced with %llu usec, reason: '%s'" + , rrdhost_hostname(st->rrdhost) + , rrdset_id(st) + , discarded + , duration_since_last_update + , discard_reason?discard_reason:"UNDEFINED" + ); - #endif + st->usec_since_last_update = duration_since_last_update; +} + +inline void rrdset_next_usec_unfiltered(RRDSET *st, usec_t duration_since_last_update) { + if(unlikely(!st->last_collected_time.tv_sec || !duration_since_last_update || (rrdset_flag_check(st, RRDSET_FLAG_SYNC_CLOCK)))) { + // call the full next_usec() function + rrdset_next_usec(st, duration_since_last_update); + return; + } - st->usec_since_last_update = microseconds; + st->usec_since_last_update = duration_since_last_update; } +inline void rrdset_next_usec(RRDSET *st, usec_t duration_since_last_update) { + struct timeval now; + + now_realtime_timeval(&now); + rrdset_timed_next(st, now, duration_since_last_update); +} // ---------------------------------------------------------------------------- // RRDSET - process the collected values for all dimensions of a chart -static inline usec_t rrdset_init_last_collected_time(RRDSET *st) { - now_realtime_timeval(&st->last_collected_time); +static inline usec_t rrdset_init_last_collected_time(RRDSET *st, struct timeval now) { + st->last_collected_time = now; last_collected_time_align(st); usec_t last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec; - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "initialized last collected time to %0.3" NETDATA_DOUBLE_MODIFIER, (NETDATA_DOUBLE)last_collect_ut / USEC_PER_SEC); - #endif return last_collect_ut; } @@ -959,9 +1024,7 @@ static inline usec_t rrdset_update_last_collected_time(RRDSET *st) { st->last_collected_time.tv_sec = (time_t) (ut / USEC_PER_SEC); st->last_collected_time.tv_usec = (suseconds_t) (ut % USEC_PER_SEC); - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "updated last collected time to %0.3" NETDATA_DOUBLE_MODIFIER, (NETDATA_DOUBLE)last_collect_ut / USEC_PER_SEC); - #endif return last_collect_ut; } @@ -978,22 +1041,51 @@ static inline usec_t rrdset_init_last_updated_time(RRDSET *st) { usec_t last_updated_ut = st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec; - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "initialized last updated time to %0.3" NETDATA_DOUBLE_MODIFIER, (NETDATA_DOUBLE)last_updated_ut / USEC_PER_SEC); - #endif return last_updated_ut; } +static __thread size_t rrdset_done_statistics_points_stored_per_tier[RRD_STORAGE_TIERS]; + static inline time_t tier_next_point_time(RRDDIM *rd, struct rrddim_tier *t, time_t now) { time_t loop = (time_t)rd->update_every * (time_t)t->tier_grouping; return now + loop - ((now + loop) % loop); } -void store_metric_at_tier(RRDDIM *rd, struct rrddim_tier *t, STORAGE_POINT sp, usec_t now_ut) { +void store_metric_at_tier(RRDDIM *rd, size_t tier, struct rrddim_tier *t, STORAGE_POINT sp, usec_t now_ut __maybe_unused) { if (unlikely(!t->next_point_time)) t->next_point_time = tier_next_point_time(rd, t, sp.end_time); + if(unlikely(sp.start_time > t->next_point_time)) { + if (likely(!storage_point_is_unset(t->virtual_point))) { + + t->collect_ops->store_metric( + t->db_collection_handle, + t->next_point_time * USEC_PER_SEC, + t->virtual_point.sum, + t->virtual_point.min, + t->virtual_point.max, + t->virtual_point.count, + t->virtual_point.anomaly_count, + t->virtual_point.flags); + } + else { + t->collect_ops->store_metric( + t->db_collection_handle, + t->next_point_time * USEC_PER_SEC, + NAN, + NAN, + NAN, + 0, + 0, SN_FLAG_NONE); + } + + rrdset_done_statistics_points_stored_per_tier[tier]++; + t->virtual_point.count = 0; // make the point unset + t->next_point_time = tier_next_point_time(rd, t, sp.end_time); + } + // merge the dates into our virtual point if (unlikely(sp.start_time < t->virtual_point.start_time)) t->virtual_point.start_time = sp.start_time; @@ -1019,72 +1111,105 @@ void store_metric_at_tier(RRDDIM *rd, struct rrddim_tier *t, STORAGE_POINT sp, u t->virtual_point = sp; } } - - if(unlikely(sp.end_time >= t->next_point_time)) { - if (likely(!storage_point_is_unset(t->virtual_point))) { - - t->collect_ops.store_metric( - t->db_collection_handle, - now_ut, - t->virtual_point.sum, - t->virtual_point.min, - t->virtual_point.max, - t->virtual_point.count, - t->virtual_point.anomaly_count, - t->virtual_point.flags); - } - else { - t->collect_ops.store_metric( - t->db_collection_handle, - now_ut, - NAN, - NAN, - NAN, - 0, - 0, SN_FLAG_NONE); +} +#ifdef NETDATA_LOG_COLLECTION_ERRORS +void rrddim_store_metric_with_trace(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n, SN_FLAGS flags, const char *function) { +#else // !NETDATA_LOG_COLLECTION_ERRORS +void rrddim_store_metric(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n, SN_FLAGS flags) { +#endif // !NETDATA_LOG_COLLECTION_ERRORS +#ifdef NETDATA_LOG_COLLECTION_ERRORS + rd->rrddim_store_metric_count++; + + if(likely(rd->rrddim_store_metric_count > 1)) { + usec_t expected = rd->rrddim_store_metric_last_ut + rd->update_every * USEC_PER_SEC; + + if(point_end_time_ut != rd->rrddim_store_metric_last_ut) { + internal_error(true, + "%s COLLECTION: 'host:%s/chart:%s/dim:%s' granularity %d, collection %zu, expected to store at tier 0 a value at %llu, but it gave %llu [%s%llu usec] (called from %s(), previously by %s())", + (point_end_time_ut < rd->rrddim_store_metric_last_ut) ? "**PAST**" : "GAP", + rrdhost_hostname(rd->rrdset->rrdhost), rrdset_id(rd->rrdset), rrddim_id(rd), + rd->update_every, + rd->rrddim_store_metric_count, + expected, point_end_time_ut, + (point_end_time_ut < rd->rrddim_store_metric_last_ut)?"by -" : "gap ", + expected - point_end_time_ut, + function, + rd->rrddim_store_metric_last_caller?rd->rrddim_store_metric_last_caller:"none"); } - - t->virtual_point.count = 0; - t->next_point_time = tier_next_point_time(rd, t, sp.end_time); } -} -static void store_metric(RRDDIM *rd, usec_t point_end_time_ut, NETDATA_DOUBLE n, SN_FLAGS flags) { + rd->rrddim_store_metric_last_ut = point_end_time_ut; + rd->rrddim_store_metric_last_caller = function; +#endif // NETDATA_LOG_COLLECTION_ERRORS // store the metric on tier 0 - rd->tiers[0]->collect_ops.store_metric(rd->tiers[0]->db_collection_handle, point_end_time_ut, n, 0, 0, 1, 0, flags); - - for(int tier = 1; tier < storage_tiers ;tier++) { + rd->tiers[0]->collect_ops->store_metric(rd->tiers[0]->db_collection_handle, point_end_time_ut, n, 0, 0, 1, 0, flags); + rrdset_done_statistics_points_stored_per_tier[0]++; + + time_t now = (time_t)(point_end_time_ut / USEC_PER_SEC); + + STORAGE_POINT sp = { + .start_time = now - rd->update_every, + .end_time = now, + .min = n, + .max = n, + .sum = n, + .count = 1, + .anomaly_count = (flags & SN_FLAG_NOT_ANOMALOUS) ? 0 : 1, + .flags = flags + }; + + for(size_t tier = 1; tier < storage_tiers ;tier++) { if(unlikely(!rd->tiers[tier])) continue; struct rrddim_tier *t = rd->tiers[tier]; - time_t now = (time_t)(point_end_time_ut / USEC_PER_SEC); - - if(!t->last_collected_ut) { + if(!rrddim_option_check(rd, RRDDIM_OPTION_BACKFILLED_HIGH_TIERS)) { // we have not collected this tier before // let's fill any gap that may exist rrdr_fill_tier_gap_from_smaller_tiers(rd, tier, now); + rrddim_option_set(rd, RRDDIM_OPTION_BACKFILLED_HIGH_TIERS); } - STORAGE_POINT sp = { - .start_time = now - rd->update_every, - .end_time = now, - .min = n, - .max = n, - .sum = n, - .count = 1, - .anomaly_count = (flags & SN_FLAG_NOT_ANOMALOUS) ? 0 : 1, - .flags = flags - }; + store_metric_at_tier(rd, tier, t, sp, point_end_time_ut); + } +} + +void store_metric_collection_completed() { + global_statistics_rrdset_done_chart_collection_completed(rrdset_done_statistics_points_stored_per_tier); +} + +// caching of dimensions rrdset_done() and rrdset_done_interpolate() loop through +struct rda_item { + const DICTIONARY_ITEM *item; + RRDDIM *rd; +}; + +static __thread struct rda_item *thread_rda = NULL; +static __thread size_t thread_rda_entries = 0; - t->last_collected_ut = point_end_time_ut; - store_metric_at_tier(rd, t, sp, point_end_time_ut); +struct rda_item *rrdset_thread_rda(size_t *dimensions) { + + if(unlikely(!thread_rda || (*dimensions) > thread_rda_entries)) { + freez(thread_rda); + thread_rda = mallocz((*dimensions) * sizeof(struct rda_item)); + thread_rda_entries = *dimensions; } + + *dimensions = thread_rda_entries; + return thread_rda; +} + +void rrdset_thread_rda_free(void) { + freez(thread_rda); + thread_rda = NULL; + thread_rda_entries = 0; } static inline size_t rrdset_done_interpolate( RRDSET *st + , struct rda_item *rda_base + , size_t rda_slots , usec_t update_every_ut , usec_t last_stored_ut , usec_t next_store_ut @@ -1113,17 +1238,26 @@ static inline size_t rrdset_done_interpolate( for( ; next_store_ut <= now_collect_ut ; last_collect_ut = next_store_ut, next_store_ut += update_every_ut, iterations-- ) { - #ifdef NETDATA_INTERNAL_CHECKS - if(iterations < 0) { error("INTERNAL CHECK: %s: iterations calculation wrapped! first_ut = %llu, last_stored_ut = %llu, next_store_ut = %llu, now_collect_ut = %llu", st->name, first_ut, last_stored_ut, next_store_ut, now_collect_ut); } + internal_error(iterations < 0, + "RRDSET: '%s': iterations calculation wrapped! " + "first_ut = %llu, last_stored_ut = %llu, next_store_ut = %llu, now_collect_ut = %llu" + , rrdset_id(st) + , first_ut + , last_stored_ut + , next_store_ut + , now_collect_ut + ); + rrdset_debug(st, "last_stored_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (last updated time)", (NETDATA_DOUBLE)last_stored_ut/USEC_PER_SEC); rrdset_debug(st, "next_store_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (next interpolation point)", (NETDATA_DOUBLE)next_store_ut/USEC_PER_SEC); - #endif last_ut = next_store_ut; - rrddim_foreach_read(rd, st) { - if (rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) - continue; + struct rda_item *rda; + size_t dim_id; + for(dim_id = 0, rda = rda_base ; dim_id < rda_slots ; ++dim_id, ++rda) { + rd = rda->rd; + if(unlikely(!rd)) continue; NETDATA_DOUBLE new_value; @@ -1135,18 +1269,16 @@ static inline size_t rrdset_done_interpolate( / (NETDATA_DOUBLE)(now_collect_ut - last_collect_ut) ); - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: CALC2 INC " NETDATA_DOUBLE_FORMAT " = " NETDATA_DOUBLE_FORMAT " * (%llu - %llu)" " / (%llu - %llu)" - , rd->name + , rrddim_name(rd) , new_value , rd->calculated_value , next_store_ut, last_collect_ut , now_collect_ut, last_collect_ut ); - #endif rd->calculated_value -= new_value; new_value += rd->last_calculated_value; @@ -1155,12 +1287,10 @@ static inline size_t rrdset_done_interpolate( if(unlikely(next_store_ut - last_stored_ut < update_every_ut)) { - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: COLLECTION POINT IS SHORT " NETDATA_DOUBLE_FORMAT " - EXTRAPOLATING", - rd->name + rrddim_name(rd) , (NETDATA_DOUBLE)(next_store_ut - last_stored_ut) ); - #endif new_value = new_value * (NETDATA_DOUBLE)(st->update_every * USEC_PER_SEC) / (NETDATA_DOUBLE)(next_store_ut - last_stored_ut); } @@ -1189,24 +1319,23 @@ static inline size_t rrdset_done_interpolate( + rd->last_calculated_value ); - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: CALC2 DEF " NETDATA_DOUBLE_FORMAT " = (((" "(" NETDATA_DOUBLE_FORMAT " - " NETDATA_DOUBLE_FORMAT ")" " * %llu" - " / %llu) + " NETDATA_DOUBLE_FORMAT, rd->name + " / %llu) + " NETDATA_DOUBLE_FORMAT, rrddim_name(rd) , new_value , rd->calculated_value, rd->last_calculated_value , (next_store_ut - first_ut) , (now_collect_ut - first_ut), rd->last_calculated_value ); - #endif } break; } if(unlikely(!store_this_entry)) { (void) ml_is_anomalous(rd, 0, false); - store_metric(rd, next_store_ut, NAN, SN_FLAG_NONE); + rrddim_store_metric(rd, next_store_ut, NAN, SN_FLAG_NONE); + rrdcontext_collected_rrddim(rd); continue; } @@ -1218,17 +1347,17 @@ static inline size_t rrdset_done_interpolate( dim_storage_flags &= ~((storage_number)SN_FLAG_NOT_ANOMALOUS); } - store_metric(rd, next_store_ut, new_value, dim_storage_flags); + rrddim_store_metric(rd, next_store_ut, new_value, dim_storage_flags); + rrdcontext_collected_rrddim(rd); rd->last_stored_value = new_value; } else { (void) ml_is_anomalous(rd, 0, false); - #ifdef NETDATA_INTERNAL_CHECKS - rrdset_debug(st, "%s: STORE[%ld] = NON EXISTING ", rd->name, current_entry); - #endif + rrdset_debug(st, "%s: STORE[%ld] = NON EXISTING ", rrddim_name(rd), current_entry); - store_metric(rd, next_store_ut, NAN, SN_FLAG_NONE); + rrddim_store_metric(rd, next_store_ut, NAN, SN_FLAG_NONE); + rrdcontext_collected_rrddim(rd); rd->last_stored_value = NAN; } @@ -1274,11 +1403,10 @@ static inline void rrdset_done_fill_the_gap(RRDSET *st) { rd->db[current_entry] = pack_storage_number(NAN, SN_FLAG_NONE); current_entry = ((current_entry + 1) >= entries) ? 0 : current_entry + 1; - #ifdef NETDATA_INTERNAL_CHECKS - rrdset_debug(st, "%s: STORE[%ld] = NON EXISTING (FILLED THE GAP)", rd->name, current_entry); - #endif + rrdset_debug(st, "%s: STORE[%ld] = NON EXISTING (FILLED THE GAP)", rrddim_name(rd), current_entry); } } + rrddim_foreach_done(rd); if(c > 0) { c--; @@ -1292,10 +1420,19 @@ static inline void rrdset_done_fill_the_gap(RRDSET *st) { } void rrdset_done(RRDSET *st) { + struct timeval now; + + now_realtime_timeval(&now); + rrdset_timed_done(st, now, /* pending_rrdset_next = */ st->counter_done != 0); +} + +void rrdset_timed_done(RRDSET *st, struct timeval now, bool pending_rrdset_next) { if(unlikely(netdata_exit)) return; - debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name); - rrdcontext_collected_rrdset(st); + if (pending_rrdset_next) + rrdset_next(st); + + debug(D_RRD_CALLS, "rrdset_done() for chart '%s'", rrdset_name(st)); RRDDIM *rd; @@ -1312,44 +1449,29 @@ void rrdset_done(RRDSET *st) { netdata_thread_disable_cancelability(); - // a read lock is OK here - rrdset_rdlock(st); - -#ifdef ENABLE_ACLK - if (likely(!st->state->is_ar_chart)) { - if (unlikely(!rrdset_flag_check(st, RRDSET_FLAG_ACLK))) { - if (likely(st->dimensions && st->counter_done && !queue_chart_to_aclk(st))) { - rrdset_flag_set(st, RRDSET_FLAG_ACLK); - } - } - } -#endif - if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE))) { - error("Chart '%s' has the OBSOLETE flag set, but it is collected.", st->id); + error("Chart '%s' has the OBSOLETE flag set, but it is collected.", rrdset_id(st)); rrdset_isnot_obsolete(st); } // check if the chart has a long time to be updated if(unlikely(st->usec_since_last_update > st->entries * update_every_ut && st->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE && st->rrd_memory_mode != RRD_MEMORY_MODE_NONE)) { - info("host '%s', chart %s: took too long to be updated (counter #%zu, update #%zu, %0.3" NETDATA_DOUBLE_MODIFIER - " secs). Resetting it.", st->rrdhost->hostname, st->name, st->counter, st->counter_done, (NETDATA_DOUBLE)st->usec_since_last_update / USEC_PER_SEC); + info("host '%s', chart '%s': took too long to be updated (counter #%zu, update #%zu, %0.3" NETDATA_DOUBLE_MODIFIER + " secs). Resetting it.", rrdhost_hostname(st->rrdhost), rrdset_id(st), st->counter, st->counter_done, (NETDATA_DOUBLE)st->usec_since_last_update / USEC_PER_SEC); rrdset_reset(st); st->usec_since_last_update = update_every_ut; store_this_entry = 0; first_entry = 1; } - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "microseconds since last update: %llu", st->usec_since_last_update); - #endif // set last_collected_time if(unlikely(!st->last_collected_time.tv_sec)) { // it is the first entry // set the last_collected_time to now - last_collect_ut = rrdset_init_last_collected_time(st) - update_every_ut; + last_collect_ut = rrdset_init_last_collected_time(st, now) - update_every_ut; // the first entry should not be stored store_this_entry = 0; @@ -1380,9 +1502,9 @@ void rrdset_done(RRDSET *st) { if(unlikely(dt_usec(&st->last_collected_time, &st->last_updated) > st->entries * update_every_ut && st->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE)) { info( - "%s: too old data (last updated at %"PRId64".%"PRId64", last collected at %"PRId64".%"PRId64"). " + "'%s': too old data (last updated at %"PRId64".%"PRId64", last collected at %"PRId64".%"PRId64"). " "Resetting it. Will not store the next entry.", - st->name, + rrdset_id(st), (int64_t)st->last_updated.tv_sec, (int64_t)st->last_updated.tv_usec, (int64_t)st->last_collected_time.tv_sec, @@ -1402,9 +1524,9 @@ void rrdset_done(RRDSET *st) { if(unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE && dt_usec(&st->last_collected_time, &st->last_updated) > (RRDENG_BLOCK_SIZE / sizeof(storage_number)) * update_every_ut)) { info( - "%s: too old data (last updated at %" PRId64 ".%" PRId64 ", last collected at %" PRId64 ".%" PRId64 "). " + "'%s': too old data (last updated at %" PRId64 ".%" PRId64 ", last collected at %" PRId64 ".%" PRId64 "). " "Resetting it. Will not store the next entry.", - st->name, + rrdset_id(st), (int64_t)st->last_updated.tv_sec, (int64_t)st->last_updated.tv_usec, (int64_t)st->last_collected_time.tv_sec, @@ -1449,79 +1571,108 @@ void rrdset_done(RRDSET *st) { store_this_entry = 1; last_collect_ut = next_store_ut - update_every_ut; - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "Fixed first entry."); - #endif } else { store_this_entry = 0; - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "Will not store the next entry."); - #endif } } after_first_database_work: st->counter_done++; - if(unlikely(st->rrdhost->rrdpush_send_enabled)) + if(unlikely(rrdhost_has_rrdpush_sender_enabled(st->rrdhost))) rrdset_done_push(st); - if (unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) { - goto after_second_database_work; - } - #ifdef NETDATA_INTERNAL_CHECKS - rrdset_debug(st, "last_collect_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (last collection time)", (NETDATA_DOUBLE)last_collect_ut/USEC_PER_SEC); - rrdset_debug(st, "now_collect_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (current collection time)", (NETDATA_DOUBLE)now_collect_ut/USEC_PER_SEC); - rrdset_debug(st, "last_stored_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (last updated time)", (NETDATA_DOUBLE)last_stored_ut/USEC_PER_SEC); - rrdset_debug(st, "next_store_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (next interpolation point)", (NETDATA_DOUBLE)next_store_ut/USEC_PER_SEC); - #endif + uint32_t has_reset_value = 0; + + size_t rda_slots = dictionary_entries(st->rrddim_root_index); + struct rda_item *rda_base = rrdset_thread_rda(&rda_slots); - // calculate totals and count the dimensions - int dimensions = 0; - st->collected_total = 0; + size_t dim_id; + size_t dimensions = 0; + struct rda_item *rda = rda_base; + total_number collected_total = 0; + total_number last_collected_total = 0; rrddim_foreach_read(rd, st) { - if (rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) + if(rd_dfe.counter >= rda_slots) + break; + + rda = &rda_base[dimensions++]; + + if(rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) { + rda->item = NULL; + rda->rd = NULL; continue; + } + + // store the dimension in the array + rda->item = dictionary_acquired_item_dup(st->rrddim_root_index, rd_dfe.item); + rda->rd = dictionary_acquired_item_value(rda->item); + + // calculate totals + if(likely(rd->updated)) { + // if the new is smaller than the old (an overflow, or reset), set the old equal to the new + // to reset the calculation (it will give zero as the calculation for this second) + if(unlikely(rd->algorithm == RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL && rd->last_collected_value > rd->collected_value)) { + debug(D_RRD_STATS, "'%s' / '%s': RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT + , rrdset_id(st) + , rrddim_name(rd) + , rd->last_collected_value + , rd->collected_value + ); + + if(!(rrddim_option_check(rd, RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS))) + has_reset_value = 1; + + rd->last_collected_value = rd->collected_value; + } - dimensions++; + last_collected_total += rd->last_collected_value; + collected_total += rd->collected_value; - if(likely(rd->updated)) - st->collected_total += rd->collected_value; + if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))) { + error("Dimension %s in chart '%s' has the OBSOLETE flag set, but it is collected.", rrddim_name(rd), rrdset_id(st)); + rrddim_isnot_obsolete(st, rd); + } + } } + rrddim_foreach_done(rd); + rda_slots = dimensions; - uint32_t has_reset_value = 0; + if (unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) + goto after_second_database_work; + + rrdset_debug(st, "last_collect_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (last collection time)", (NETDATA_DOUBLE)last_collect_ut/USEC_PER_SEC); + rrdset_debug(st, "now_collect_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (current collection time)", (NETDATA_DOUBLE)now_collect_ut/USEC_PER_SEC); + rrdset_debug(st, "last_stored_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (last updated time)", (NETDATA_DOUBLE)last_stored_ut/USEC_PER_SEC); + rrdset_debug(st, "next_store_ut = %0.3" NETDATA_DOUBLE_MODIFIER " (next interpolation point)", (NETDATA_DOUBLE)next_store_ut/USEC_PER_SEC); // process all dimensions to calculate their values // based on the collected figures only // at this stage we do not interpolate anything - rrddim_foreach_read(rd, st) { - if (rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) - continue; + for(dim_id = 0, rda = rda_base ; dim_id < rda_slots ; ++dim_id, ++rda) { + rd = rda->rd; + if(unlikely(!rd)) continue; if(unlikely(!rd->updated)) { rd->calculated_value = 0; continue; } - if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))) { - error("Dimension %s in chart '%s' has the OBSOLETE flag set, but it is collected.", rd->name, st->id); - rrddim_isnot_obsolete(st, rd); - } - - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: START " " last_collected_value = " COLLECTED_NUMBER_FORMAT " collected_value = " COLLECTED_NUMBER_FORMAT " last_calculated_value = " NETDATA_DOUBLE_FORMAT - " calculated_value = " NETDATA_DOUBLE_FORMAT, rd->name - , rd->last_collected_value - , rd->collected_value - , rd->last_calculated_value - , rd->calculated_value + " calculated_value = " NETDATA_DOUBLE_FORMAT + , rrddim_name(rd) + , rd->last_collected_value + , rd->collected_value + , rd->last_calculated_value + , rd->calculated_value ); - #endif switch(rd->algorithm) { case RRD_ALGORITHM_ABSOLUTE: @@ -1529,22 +1680,20 @@ after_first_database_work: * (NETDATA_DOUBLE)rd->multiplier / (NETDATA_DOUBLE)rd->divisor; - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: CALC ABS/ABS-NO-IN " NETDATA_DOUBLE_FORMAT " = " COLLECTED_NUMBER_FORMAT " * " NETDATA_DOUBLE_FORMAT - " / " NETDATA_DOUBLE_FORMAT, rd->name + " / " NETDATA_DOUBLE_FORMAT + , rrddim_name(rd) , rd->calculated_value , rd->collected_value , (NETDATA_DOUBLE)rd->multiplier , (NETDATA_DOUBLE)rd->divisor ); - #endif - break; case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL: - if(unlikely(!st->collected_total)) + if(unlikely(!collected_total)) rd->calculated_value = 0; else // the percentage of the current value @@ -1552,19 +1701,16 @@ after_first_database_work: rd->calculated_value = (NETDATA_DOUBLE)100 * (NETDATA_DOUBLE)rd->collected_value - / (NETDATA_DOUBLE)st->collected_total; + / (NETDATA_DOUBLE)collected_total; - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: CALC PCENT-ROW " NETDATA_DOUBLE_FORMAT " = 100" " * " COLLECTED_NUMBER_FORMAT " / " COLLECTED_NUMBER_FORMAT - , rd->name + , rrddim_name(rd) , rd->calculated_value , rd->collected_value - , st->collected_total + , collected_total ); - #endif - break; case RRD_ALGORITHM_INCREMENTAL: @@ -1578,12 +1724,13 @@ after_first_database_work: // It is imperative to set the comparison to uint64_t since type collected_number is signed and // produces wrong results as far as incremental counters are concerned. if(unlikely((uint64_t)rd->last_collected_value > (uint64_t)rd->collected_value)) { - debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT - , st->name, rd->name + debug(D_RRD_STATS, "'%s' / '%s': RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT + , rrdset_id(st) + , rrddim_name(rd) , rd->last_collected_value , rd->collected_value); - if(!(rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS))) + if(!(rrddim_option_check(rd, RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS))) has_reset_value = 1; uint64_t last = (uint64_t)rd->last_collected_value; @@ -1622,19 +1769,17 @@ after_first_database_work: / (NETDATA_DOUBLE) rd->divisor; } - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: CALC INC PRE " NETDATA_DOUBLE_FORMAT " = (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")" " * " NETDATA_DOUBLE_FORMAT - " / " NETDATA_DOUBLE_FORMAT, rd->name + " / " NETDATA_DOUBLE_FORMAT + , rrddim_name(rd) , rd->calculated_value , rd->collected_value, rd->last_collected_value , (NETDATA_DOUBLE)rd->multiplier , (NETDATA_DOUBLE)rd->divisor ); - #endif - break; case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL: @@ -1643,42 +1788,24 @@ after_first_database_work: continue; } - // if the new is smaller than the old (an overflow, or reset), set the old equal to the new - // to reset the calculation (it will give zero as the calculation for this second) - if(unlikely(rd->last_collected_value > rd->collected_value)) { - debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT - , st->name, rd->name - , rd->last_collected_value - , rd->collected_value - ); - - if(!(rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS))) - has_reset_value = 1; - - rd->last_collected_value = rd->collected_value; - } - // the percentage of the current increment // over the increment of all dimensions together - if(unlikely(st->collected_total == st->last_collected_total)) + if(unlikely(collected_total == last_collected_total)) rd->calculated_value = 0; else rd->calculated_value = (NETDATA_DOUBLE)100 * (NETDATA_DOUBLE)(rd->collected_value - rd->last_collected_value) - / (NETDATA_DOUBLE)(st->collected_total - st->last_collected_total); + / (NETDATA_DOUBLE)(collected_total - last_collected_total); - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: CALC PCENT-DIFF " NETDATA_DOUBLE_FORMAT " = 100" " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")" " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")" - , rd->name + , rrddim_name(rd) , rd->calculated_value , rd->collected_value, rd->last_collected_value - , st->collected_total, st->last_collected_total + , collected_total, last_collected_total ); - #endif - break; default: @@ -1686,43 +1813,41 @@ after_first_database_work: // it gets noticed when we add new types rd->calculated_value = 0; - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: CALC " NETDATA_DOUBLE_FORMAT " = 0" - , rd->name + , rrddim_name(rd) , rd->calculated_value ); - #endif - break; } - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: PHASE2 " " last_collected_value = " COLLECTED_NUMBER_FORMAT " collected_value = " COLLECTED_NUMBER_FORMAT " last_calculated_value = " NETDATA_DOUBLE_FORMAT - " calculated_value = " NETDATA_DOUBLE_FORMAT, rd->name - , rd->last_collected_value - , rd->collected_value - , rd->last_calculated_value - , rd->calculated_value + " calculated_value = " NETDATA_DOUBLE_FORMAT + , rrddim_name(rd) + , rd->last_collected_value + , rd->collected_value + , rd->last_calculated_value + , rd->calculated_value ); - #endif - } // at this point we have all the calculated values ready // it is now time to interpolate values on a second boundary -#ifdef NETDATA_INTERNAL_CHECKS - if(unlikely(now_collect_ut < next_store_ut && st->counter_done > 1)) { - // this is collected in the same interpolation point - rrdset_debug(st, "THIS IS IN THE SAME INTERPOLATION POINT"); - info("INTERNAL CHECK: host '%s', chart '%s' collection %zu is in the same interpolation point: short by %llu microseconds", st->rrdhost->hostname, st->name, st->counter_done, next_store_ut - now_collect_ut); - } -#endif - - rrdset_done_interpolate(st +// #ifdef NETDATA_INTERNAL_CHECKS +// if(unlikely(now_collect_ut < next_store_ut && st->counter_done > 1)) { +// // this is collected in the same interpolation point +// rrdset_debug(st, "THIS IS IN THE SAME INTERPOLATION POINT"); +// info("INTERNAL CHECK: host '%s', chart '%s' collection %zu is in the same interpolation point: short by %llu microseconds", st->rrdhost->hostname, rrdset_name(st), st->counter_done, next_store_ut - now_collect_ut); +// } +// #endif + + rrdset_done_interpolate( + st + , rda_base + , rda_slots , update_every_ut , last_stored_ut , next_store_ut @@ -1733,54 +1858,41 @@ after_first_database_work: ); after_second_database_work: - st->last_collected_total = st->collected_total; - -#ifdef ENABLE_ACLK - time_t mark = now_realtime_sec(); -#endif - rrddim_foreach_read(rd, st) { - if (rrddim_flag_check(rd, RRDDIM_FLAG_ARCHIVED)) - continue; + for(dim_id = 0, rda = rda_base ; dim_id < rda_slots ; ++dim_id, ++rda) { + rd = rda->rd; + if(unlikely(!rd)) continue; -#ifdef ENABLE_ACLK - if (likely(!st->state->is_ar_chart)) { - if (!rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN) && likely(rrdset_flag_check(st, RRDSET_FLAG_ACLK))) - queue_dimension_to_aclk(rd, calc_dimension_liveness(rd, mark)); - } -#endif if(unlikely(!rd->updated)) continue; - #ifdef NETDATA_INTERNAL_CHECKS - rrdset_debug(st, "%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", rd->name, rd->last_collected_value, rd->collected_value); - #endif + rrdset_debug(st, "%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", rrddim_name(rd), rd->last_collected_value, rd->collected_value); rd->last_collected_value = rd->collected_value; switch(rd->algorithm) { case RRD_ALGORITHM_INCREMENTAL: if(unlikely(!first_entry)) { - #ifdef NETDATA_INTERNAL_CHECKS - rrdset_debug(st, "%s: setting last_calculated_value (old: " NETDATA_DOUBLE_FORMAT - ") to last_calculated_value (new: " NETDATA_DOUBLE_FORMAT ")", rd->name, rd->last_calculated_value + rd->calculated_value, rd->calculated_value); - #endif + rrdset_debug(st, "%s: setting last_calculated_value (old: " NETDATA_DOUBLE_FORMAT ") to " + "last_calculated_value (new: " NETDATA_DOUBLE_FORMAT ")" + , rrddim_name(rd) + , rd->last_calculated_value + rd->calculated_value + , rd->calculated_value); rd->last_calculated_value += rd->calculated_value; } else { - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "THIS IS THE FIRST POINT"); - #endif } break; case RRD_ALGORITHM_ABSOLUTE: case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL: case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL: - #ifdef NETDATA_INTERNAL_CHECKS - rrdset_debug(st, "%s: setting last_calculated_value (old: " NETDATA_DOUBLE_FORMAT - ") to last_calculated_value (new: " NETDATA_DOUBLE_FORMAT ")", rd->name, rd->last_calculated_value, rd->calculated_value); - #endif + rrdset_debug(st, "%s: setting last_calculated_value (old: " NETDATA_DOUBLE_FORMAT ") to " + "last_calculated_value (new: " NETDATA_DOUBLE_FORMAT ")" + , rrddim_name(rd) + , rd->last_calculated_value + , rd->calculated_value); rd->last_calculated_value = rd->calculated_value; break; @@ -1790,19 +1902,17 @@ after_second_database_work: rd->collected_value = 0; rd->updated = 0; - #ifdef NETDATA_INTERNAL_CHECKS rrdset_debug(st, "%s: END " " last_collected_value = " COLLECTED_NUMBER_FORMAT " collected_value = " COLLECTED_NUMBER_FORMAT " last_calculated_value = " NETDATA_DOUBLE_FORMAT - " calculated_value = " NETDATA_DOUBLE_FORMAT, rd->name - , rd->last_collected_value - , rd->collected_value - , rd->last_calculated_value - , rd->calculated_value + " calculated_value = " NETDATA_DOUBLE_FORMAT + , rrddim_name(rd) + , rd->last_collected_value + , rd->collected_value + , rd->last_calculated_value + , rd->calculated_value ); - #endif - } // ALL DONE ABOUT THE DATA UPDATE @@ -1812,99 +1922,54 @@ after_second_database_work: // update the memory mapped files with the latest values rrdset_memory_file_update(st); - rrddim_foreach_read(rd, st) { + + for(dim_id = 0, rda = rda_base; dim_id < rda_slots ; ++dim_id, ++rda) { + rd = rda->rd; + if(unlikely(!rd)) continue; rrddim_memory_file_update(rd); } } - // find if there are any obsolete dimensions - if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS))) { - rrddim_foreach_read(rd, st) - if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))) - break; - - if(unlikely(rd)) { - time_t now = now_realtime_sec(); - - RRDDIM *last; - // there is a dimension to free - // upgrade our read lock to a write lock - rrdset_unlock(st); - rrdset_wrlock(st); - - for( rd = st->dimensions, last = NULL ; likely(rd) ; ) { - if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE) && !rrddim_flag_check(rd, RRDDIM_FLAG_ACLK) - && (rd->last_collected_time.tv_sec + rrdset_free_obsolete_time < now))) { - info("Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id); - - const char *cache_filename = rrddim_cache_filename(rd); - if(cache_filename) { - info("Deleting dimension file '%s'.", cache_filename); - if (unlikely(unlink(cache_filename) == -1)) - error("Cannot delete dimension file '%s'", cache_filename); - } - -#ifdef ENABLE_DBENGINE - if (rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) { - rrddim_flag_set(rd, RRDDIM_FLAG_ARCHIVED); - while(rd->variables) - rrddimvar_free(rd->variables); - - rrddim_flag_clear(rd, RRDDIM_FLAG_OBSOLETE); - /* only a collector can mark a chart as obsolete, so we must remove the reference */ - - size_t tiers_available = 0, tiers_said_yes = 0; - for(int tier = 0; tier < storage_tiers ;tier++) { - if(rd->tiers[tier]) { - tiers_available++; - - if(rd->tiers[tier]->collect_ops.finalize(rd->tiers[tier]->db_collection_handle)) - tiers_said_yes++; - - rd->tiers[tier]->db_collection_handle = NULL; - } - } - - if (tiers_available == tiers_said_yes && tiers_said_yes) { - /* This metric has no data and no references */ - delete_dimension_uuid(&rd->metric_uuid); - } else { - /* Do not delete this dimension */ -#ifdef ENABLE_ACLK - queue_dimension_to_aclk(rd, calc_dimension_liveness(rd, mark)); -#endif - last = rd; - rd = rd->next; - continue; - } - } -#endif - if(unlikely(!last)) { - rrddim_free(st, rd); - rd = st->dimensions; - continue; - } - else { - rrddim_free(st, rd); - rd = last->next; - continue; - } - } + for(dim_id = 0, rda = rda_base; dim_id < rda_slots ; ++dim_id, ++rda) { + rd = rda->rd; + if(unlikely(!rd)) continue; - last = rd; - rd = rd->next; - } - } - else { - rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE_DIMENSIONS); - } + dictionary_acquired_item_release(st->rrddim_root_index, rda->item); + rda->item = NULL; + rda->rd = NULL; } - rrdset_unlock(st); + rrdcontext_collected_rrdset(st); netdata_thread_enable_cancelability(); + + store_metric_collection_completed(); } +time_t rrdset_set_update_every(RRDSET *st, time_t update_every) { + + internal_error(true, "RRDSET '%s' switching update every from %d to %d", + rrdset_id(st), (int)st->update_every, (int)update_every); + + time_t prev_update_every = st->update_every; + st->update_every = update_every; + + // switch update every to the storage engine + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + for (size_t tier = 0; tier < storage_tiers; tier++) { + if (rd->tiers[tier] && rd->tiers[tier]->db_collection_handle) + rd->tiers[tier]->collect_ops->change_collection_frequency(rd->tiers[tier]->db_collection_handle, (int)(st->rrdhost->db[tier].tier_grouping * st->update_every)); + } + + assert(rd->update_every == prev_update_every && + "chart's update every differs from the update every of its dimensions"); + rd->update_every = st->update_every; + } + rrddim_foreach_done(rd); + + return prev_update_every; +} // ---------------------------------------------------------------------------- // compatibility layer for RRDSET files v019 @@ -1966,8 +2031,8 @@ struct rrdset_map_save_v019 { usec_t usec_since_last_update; // NEEDS TO BE UPDATED - maintained on load struct timeval last_updated; // NEEDS TO BE UPDATED - check to reset all - fixed on load struct timeval last_collected_time; // ignored - long long collected_total; // NEEDS TO BE UPDATED - maintained on load - long long last_collected_total; // NEEDS TO BE UPDATED - maintained on load + long long collected_total; // ignored + long long last_collected_total; // ignored void *rrdfamily; // ignored void *rrdhost; // ignored void *next; // ignored @@ -1991,8 +2056,6 @@ void rrdset_memory_file_update(RRDSET *st) { st_on_file->usec_since_last_update = st->usec_since_last_update; st_on_file->last_updated.tv_sec = st->last_updated.tv_sec; st_on_file->last_updated.tv_usec = st->last_updated.tv_usec; - st_on_file->collected_total = st->collected_total; - st_on_file->last_collected_total = st->last_collected_total; } const char *rrdset_cache_filename(RRDSET *st) { @@ -2008,7 +2071,7 @@ void rrdset_memory_file_free(RRDSET *st) { rrdset_memory_file_update(st); struct rrdset_map_save_v019 *st_on_file = st->st_on_file; - munmap(st_on_file, st_on_file->memsize); + netdata_munmap(st_on_file, st_on_file->memsize); // remove the pointers from the RRDDIM st->st_on_file = NULL; @@ -2047,8 +2110,8 @@ bool rrdset_memory_load_or_create_map_save(RRDSET *st, RRD_MEMORY_MODE memory_mo info("Initializing file '%s'.", fullfilename); memset(st_on_file, 0, size); } - else if(strncmp(st_on_file->id, st->id, RRD_ID_LENGTH_MAX_V019) != 0) { - error("File '%s' contents are not for chart '%s'. Clearing it.", fullfilename, st->id); + else if(strncmp(st_on_file->id, rrdset_id(st), RRD_ID_LENGTH_MAX_V019) != 0) { + error("File '%s' contents are not for chart '%s'. Clearing it.", fullfilename, rrdset_id(st)); memset(st_on_file, 0, size); } else if(st_on_file->memsize != size || st_on_file->entries != st->entries) { @@ -2084,8 +2147,6 @@ bool rrdset_memory_load_or_create_map_save(RRDSET *st, RRD_MEMORY_MODE memory_mo st->usec_since_last_update = st_on_file->usec_since_last_update; st->last_updated.tv_sec = st_on_file->last_updated.tv_sec; st->last_updated.tv_usec = st_on_file->last_updated.tv_usec; - st->collected_total = st_on_file->collected_total; - st->last_collected_total = st_on_file->last_collected_total; // link it to st st->st_on_file = st_on_file; @@ -2094,7 +2155,7 @@ bool rrdset_memory_load_or_create_map_save(RRDSET *st, RRD_MEMORY_MODE memory_mo memset(st_on_file, 0, size); // set the values we need - strncpyz(st_on_file->id, st->id, RRD_ID_LENGTH_MAX_V019 + 1); + strncpyz(st_on_file->id, rrdset_id(st), RRD_ID_LENGTH_MAX_V019 + 1); strcpy(st_on_file->cache_filename, fullfilename); strcpy(st_on_file->magic, RRDSET_MAGIC_V019); st_on_file->memsize = size; diff --git a/database/rrdsetvar.c b/database/rrdsetvar.c index e520764a2..22cf8a1f0 100644 --- a/database/rrdsetvar.c +++ b/database/rrdsetvar.c @@ -1,190 +1,293 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#define NETDATA_HEALTH_INTERNALS #include "rrd.h" -// ---------------------------------------------------------------------------- -// RRDSETVAR management -// CHART VARIABLES +typedef struct rrdsetvar { + STRING *name; // variable name + void *value; // we need this to maintain the allocation for custom chart variables -static inline void rrdsetvar_free_variables(RRDSETVAR *rs) { - RRDSET *st = rs->rrdset; + const RRDVAR_ACQUIRED *rrdvar_local; + const RRDVAR_ACQUIRED *rrdvar_family_chart_id; + const RRDVAR_ACQUIRED *rrdvar_family_chart_name; + const RRDVAR_ACQUIRED *rrdvar_host_chart_id; + const RRDVAR_ACQUIRED *rrdvar_host_chart_name; + + RRDVAR_FLAGS flags:24; + RRDVAR_TYPE type:8; +} RRDSETVAR; + +// should only be called while the rrdsetvar dict is write locked +// otherwise, 2+ threads may be setting the same variables at the same time +static inline void rrdsetvar_free_rrdvars_unsafe(RRDSET *st, RRDSETVAR *rs) { RRDHOST *host = st->rrdhost; // ------------------------------------------------------------------------ // CHART - rrdvar_free(host, &st->rrdvar_root_index, rs->var_local); - rs->var_local = NULL; + + if(st->rrdvars) { + rrdvar_release_and_del(st->rrdvars, rs->rrdvar_local); + rs->rrdvar_local = NULL; + } // ------------------------------------------------------------------------ // FAMILY - rrdvar_free(host, &st->rrdfamily->rrdvar_root_index, rs->var_family); - rs->var_family = NULL; - rrdvar_free(host, &st->rrdfamily->rrdvar_root_index, rs->var_family_name); - rs->var_family_name = NULL; + if(st->rrdfamily) { + rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_chart_id); + rs->rrdvar_family_chart_id = NULL; + + rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_chart_name); + rs->rrdvar_family_chart_name = NULL; + } // ------------------------------------------------------------------------ // HOST - rrdvar_free(host, &host->rrdvar_root_index, rs->var_host); - rs->var_host = NULL; - rrdvar_free(host, &host->rrdvar_root_index, rs->var_host_name); - rs->var_host_name = NULL; + if(host->rrdvars && host->health_enabled) { + rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_id); + rs->rrdvar_host_chart_id = NULL; - // ------------------------------------------------------------------------ - // KEYS - freez(rs->key_fullid); - rs->key_fullid = NULL; - - freez(rs->key_fullname); - rs->key_fullname = NULL; + rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_name); + rs->rrdvar_host_chart_name = NULL; + } } -static inline void rrdsetvar_create_variables(RRDSETVAR *rs) { - RRDSET *st = rs->rrdset; +// should only be called while the rrdsetvar dict is write locked +// otherwise, 2+ threads may be setting the same variables at the same time +static inline void rrdsetvar_update_rrdvars_unsafe(RRDSET *st, RRDSETVAR *rs) { RRDHOST *host = st->rrdhost; - RRDVAR_OPTIONS options = rs->options; - if(rs->options & RRDVAR_OPTION_ALLOCATED) - options &= ~ RRDVAR_OPTION_ALLOCATED; + RRDVAR_FLAGS options = rs->flags; + options &= ~RRDVAR_OPTIONS_REMOVED_WHEN_PROPAGATING_TO_RRDVAR; // ------------------------------------------------------------------------ // free the old ones (if any) - rrdsetvar_free_variables(rs); + rrdsetvar_free_rrdvars_unsafe(st, rs); // ------------------------------------------------------------------------ // KEYS char buffer[RRDVAR_MAX_LENGTH + 1]; - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rs->variable); - rs->key_fullid = strdupz(buffer); + snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_id(st), string2str(rs->name)); + STRING *key_chart_id = string_strdupz(buffer); - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable); - rs->key_fullname = strdupz(buffer); + snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_name(st), string2str(rs->name)); + STRING *key_chart_name = string_strdupz(buffer); // ------------------------------------------------------------------------ // CHART - rs->var_local = rrdvar_create_and_index("local", &st->rrdvar_root_index, rs->variable, rs->type, options, rs->value); + + if(st->rrdvars) { + rs->rrdvar_local = rrdvar_add_and_acquire("local", st->rrdvars, rs->name, rs->type, options, rs->value); + } // ------------------------------------------------------------------------ // FAMILY - rs->var_family = rrdvar_create_and_index("family", &st->rrdfamily->rrdvar_root_index, rs->key_fullid, rs->type, options, rs->value); - rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->rrdvar_root_index, rs->key_fullname, rs->type, options, rs->value); + + if(st->rrdfamily) { + rs->rrdvar_family_chart_id = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_chart_id, rs->type, options, rs->value); + rs->rrdvar_family_chart_name = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_chart_name, rs->type, options, rs->value); + } // ------------------------------------------------------------------------ // HOST - rs->var_host = rrdvar_create_and_index("host", &host->rrdvar_root_index, rs->key_fullid, rs->type, options, rs->value); - rs->var_host_name = rrdvar_create_and_index("host", &host->rrdvar_root_index, rs->key_fullname, rs->type, options, rs->value); -} -RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, RRDVAR_TYPE type, void *value, RRDVAR_OPTIONS options) { - debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable); - RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR)); + if(host->rrdvars && host->health_enabled) { + rs->rrdvar_host_chart_id = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_id, rs->type, options, rs->value); + rs->rrdvar_host_chart_name = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_name, rs->type, options, rs->value); + } - rs->variable = strdupz(variable); - rs->hash = simple_hash(rs->variable); - rs->type = type; - rs->value = value; - rs->options = options; - rs->rrdset = st; + // free the keys + string_freez(key_chart_id); + string_freez(key_chart_name); +} - rs->next = st->variables; - st->variables = rs; +static void rrdsetvar_free_value_unsafe(RRDSETVAR *rs) { + if(rs->flags & RRDVAR_FLAG_ALLOCATED) { + void *old = rs->value; + rs->value = NULL; + rs->flags &= ~RRDVAR_FLAG_ALLOCATED; + freez(old); + } +} - rrdsetvar_create_variables(rs); +static void rrdsetvar_set_value_unsafe(RRDSETVAR *rs, void *new_value) { + rrdsetvar_free_value_unsafe(rs); - return rs; + if(new_value) + rs->value = new_value; + else { + NETDATA_DOUBLE *n = mallocz(sizeof(NETDATA_DOUBLE)); + *n = NAN; + rs->value = n; + rs->flags |= RRDVAR_FLAG_ALLOCATED; + } } -void rrdsetvar_rename_all(RRDSET *st) { - debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name); +struct rrdsetvar_constructor { + RRDSET *rrdset; + const char *name; + void *value; + RRDVAR_FLAGS flags :16; + RRDVAR_TYPE type:8; +}; - RRDSETVAR *rs; - for(rs = st->variables; rs ; rs = rs->next) - rrdsetvar_create_variables(rs); +static void rrdsetvar_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdsetvar, void *constructor_data) { + RRDSETVAR *rs = rrdsetvar; + struct rrdsetvar_constructor *ctr = constructor_data; + + ctr->flags &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS; - rrdsetcalc_link_matching(st); + rs->name = string_strdupz(ctr->name); + rs->type = ctr->type; + rs->flags = ctr->flags; + rrdsetvar_set_value_unsafe(rs, ctr->value); + + // create the rrdvariables while we are having a write lock to the dictionary + rrdsetvar_update_rrdvars_unsafe(ctr->rrdset, rs); } -void rrdsetvar_free(RRDSETVAR *rs) { - RRDSET *st = rs->rrdset; - debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable); +static bool rrdsetvar_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdsetvar, void *new_rrdsetvar __maybe_unused, void *constructor_data) { + RRDSETVAR *rs = rrdsetvar; + struct rrdsetvar_constructor *ctr = constructor_data; - if(st->variables == rs) { - st->variables = rs->next; - } - else { - RRDSETVAR *t; - for (t = st->variables; t && t->next != rs; t = t->next); - if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->key_fullname, st->id); - else t->next = rs->next; + ctr->flags &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS; + + RRDVAR_FLAGS options = rs->flags; + options &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS; + + if(((ctr->value == NULL && rs->value != NULL && rs->flags & RRDVAR_FLAG_ALLOCATED) || (rs->value == ctr->value)) + && ctr->flags == options && rs->type == ctr->type) { + // don't reset it - everything is the same, or as it should... + return false; } - rrdsetvar_free_variables(rs); + internal_error(true, "RRDSETVAR: resetting variable '%s' of chart '%s' of host '%s', options from 0x%x to 0x%x, type from %d to %d", + string2str(rs->name), rrdset_id(ctr->rrdset), rrdhost_hostname(ctr->rrdset->rrdhost), + options, ctr->flags, rs->type, ctr->type); - freez(rs->variable); + rrdsetvar_free_value_unsafe(rs); // we are going to change the options, so free it before setting it + rs->flags = ctr->flags; + rs->type = ctr->type; + rrdsetvar_set_value_unsafe(rs, ctr->value); - if(rs->options & RRDVAR_OPTION_ALLOCATED) - freez(rs->value); + // recreate the rrdvariables while we are having a write lock to the dictionary + rrdsetvar_update_rrdvars_unsafe(ctr->rrdset, rs); + return true; +} + +static void rrdsetvar_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdsetvar, void *rrdset __maybe_unused) { + RRDSET *st = rrdset; + RRDSETVAR *rs = rrdsetvar; - freez(rs); + rrdsetvar_free_rrdvars_unsafe(st, rs); + rrdsetvar_free_value_unsafe(rs); + string_freez(rs->name); + rs->name = NULL; } -// -------------------------------------------------------------------------------------------------------------------- -// custom chart variables +void rrdsetvar_index_init(RRDSET *st) { + if(!st->rrdsetvar_root_index) { + st->rrdsetvar_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); -RRDSETVAR *rrdsetvar_custom_chart_variable_create(RRDSET *st, const char *name) { - RRDHOST *host = st->rrdhost; + dictionary_register_insert_callback(st->rrdsetvar_root_index, rrdsetvar_insert_callback, NULL); + dictionary_register_conflict_callback(st->rrdsetvar_root_index, rrdsetvar_conflict_callback, NULL); + dictionary_register_delete_callback(st->rrdsetvar_root_index, rrdsetvar_delete_callback, st); + } +} - char *n = strdupz(name); - rrdvar_fix_name(n); - uint32_t hash = simple_hash(n); +void rrdsetvar_index_destroy(RRDSET *st) { + dictionary_destroy(st->rrdsetvar_root_index); + st->rrdsetvar_root_index = NULL; +} - rrdset_wrlock(st); +const RRDSETVAR_ACQUIRED *rrdsetvar_add_and_acquire(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags) { + struct rrdsetvar_constructor tmp = { + .name = name, + .type = type, + .value = value, + .flags = flags, + .rrdset = st, + }; + + const RRDSETVAR_ACQUIRED *rsa = (const RRDSETVAR_ACQUIRED *)dictionary_set_and_acquire_item_advanced(st->rrdsetvar_root_index, name, -1, NULL, sizeof(RRDSETVAR), &tmp); + return rsa; +} + +void rrdsetvar_add_and_leave_released(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags) { + const RRDSETVAR_ACQUIRED *rsa = rrdsetvar_add_and_acquire(st, name, type, value, flags); + dictionary_acquired_item_release(st->rrdsetvar_root_index, (const DICTIONARY_ITEM *)rsa); +} + +void rrdsetvar_rename_all(RRDSET *st) { + debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", rrdset_id(st), rrdset_name(st)); - // find it RRDSETVAR *rs; - for(rs = st->variables; rs ; rs = rs->next) { - if(hash == rs->hash && strcmp(n, rs->variable) == 0) { - rrdset_unlock(st); - if(rs->options & RRDVAR_OPTION_CUSTOM_CHART_VAR) { - freez(n); - return rs; - } - else { - error("RRDSETVAR: custom variable '%s' on chart '%s' of host '%s', conflicts with an internal chart variable", n, st->id, host->hostname); - freez(n); - return NULL; - } - } + dfe_start_write(st->rrdsetvar_root_index, rs) { + // should only be called while the rrdsetvar dict is write locked + rrdsetvar_update_rrdvars_unsafe(st, rs); } + dfe_done(rs); - // not found, allocate one + rrdcalc_link_matching_alerts_to_rrdset(st); +} - NETDATA_DOUBLE *v = mallocz(sizeof(NETDATA_DOUBLE)); - *v = NAN; +void rrdsetvar_release_and_delete_all(RRDSET *st) { + RRDSETVAR *rs; + dfe_start_write(st->rrdsetvar_root_index, rs) { + dictionary_del_advanced(st->rrdsetvar_root_index, string2str(rs->name), (ssize_t)string_strlen(rs->name) + 1); + } + dfe_done(rs); +} - rs = rrdsetvar_create(st, n, RRDVAR_TYPE_CALCULATED, v, RRDVAR_OPTION_ALLOCATED|RRDVAR_OPTION_CUSTOM_CHART_VAR); - rrdset_unlock(st); +void rrdsetvar_release(DICTIONARY *dict, const RRDSETVAR_ACQUIRED *rsa) { + dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rsa); +} + +// -------------------------------------------------------------------------------------------------------------------- +// custom chart variables - freez(n); +const RRDSETVAR_ACQUIRED *rrdsetvar_custom_chart_variable_add_and_acquire(RRDSET *st, const char *name) { + STRING *name_string = rrdvar_name_to_string(name); + const RRDSETVAR_ACQUIRED *rs = rrdsetvar_add_and_acquire(st, string2str(name_string), RRDVAR_TYPE_CALCULATED, NULL, RRDVAR_FLAG_CUSTOM_CHART_VAR); + string_freez(name_string); return rs; } -void rrdsetvar_custom_chart_variable_set(RRDSETVAR *rs, NETDATA_DOUBLE value) { - if(rs->type != RRDVAR_TYPE_CALCULATED || !(rs->options & RRDVAR_OPTION_CUSTOM_CHART_VAR) || !(rs->options & RRDVAR_OPTION_ALLOCATED)) { +void rrdsetvar_custom_chart_variable_set(RRDSET *st, const RRDSETVAR_ACQUIRED *rsa, NETDATA_DOUBLE value) { + if(!rsa) return; + + RRDSETVAR *rs = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rsa); + + if(rs->type != RRDVAR_TYPE_CALCULATED || !(rs->flags & RRDVAR_FLAG_CUSTOM_CHART_VAR) || !(rs->flags & RRDVAR_FLAG_ALLOCATED)) { error("RRDSETVAR: requested to set variable '%s' of chart '%s' on host '%s' to value " NETDATA_DOUBLE_FORMAT - " but the variable is not a custom chart one.", rs->variable, rs->rrdset->id, rs->rrdset->rrdhost->hostname, value); + " but the variable is not a custom chart one (it has options 0x%x, value pointer %p). Ignoring request.", string2str(rs->name), rrdset_id(st), rrdhost_hostname(st->rrdhost), value, (uint32_t)rs->flags, rs->value); } else { NETDATA_DOUBLE *v = rs->value; if(*v != value) { *v = value; + rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES); + } + } +} + +void rrdsetvar_print_to_streaming_custom_chart_variables(RRDSET *st, BUFFER *wb) { + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES); - // mark the chart to be sent upstream - rrdset_flag_clear(rs->rrdset, RRDSET_FLAG_UPSTREAM_EXPOSED); + // send the chart local custom variables + RRDSETVAR *rs; + dfe_start_read(st->rrdsetvar_root_index, rs) { + if(unlikely(rs->type == RRDVAR_TYPE_CALCULATED && rs->flags & RRDVAR_FLAG_CUSTOM_CHART_VAR)) { + NETDATA_DOUBLE *value = (NETDATA_DOUBLE *) rs->value; + + buffer_sprintf(wb + , "VARIABLE CHART %s = " NETDATA_DOUBLE_FORMAT "\n" + , string2str(rs->name) + , *value + ); } } + dfe_done(rs); } diff --git a/database/rrdsetvar.h b/database/rrdsetvar.h index 37f4da959..0c2e66712 100644 --- a/database/rrdsetvar.h +++ b/database/rrdsetvar.h @@ -11,34 +11,20 @@ // This means, there will be no speed penalty for using // these variables -struct rrdsetvar { - char *variable; // variable name - uint32_t hash; // variable name hash +void rrdsetvar_index_init(RRDSET *st); +void rrdsetvar_index_destroy(RRDSET *st); +void rrdsetvar_release_and_delete_all(RRDSET *st); - char *key_fullid; // chart type.chart id.variable - char *key_fullname; // chart type.chart name.variable +#define rrdsetvar_custom_chart_variable_release(st, rsa) rrdsetvar_release((st)->rrdsetvar_root_index, rsa) +void rrdsetvar_release(DICTIONARY *dict, const RRDSETVAR_ACQUIRED *rsa); - RRDVAR_TYPE type; - void *value; +const RRDSETVAR_ACQUIRED *rrdsetvar_custom_chart_variable_add_and_acquire(RRDSET *st, const char *name); +void rrdsetvar_custom_chart_variable_set(RRDSET *st, const RRDSETVAR_ACQUIRED *rsa, NETDATA_DOUBLE value); - RRDVAR_OPTIONS options; +void rrdsetvar_rename_all(RRDSET *st); +const RRDSETVAR_ACQUIRED *rrdsetvar_add_and_acquire(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags); +void rrdsetvar_add_and_leave_released(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags); - RRDVAR *var_local; - RRDVAR *var_family; - RRDVAR *var_host; - RRDVAR *var_family_name; - RRDVAR *var_host_name; - - struct rrdset *rrdset; - - struct rrdsetvar *next; -}; - -extern RRDSETVAR *rrdsetvar_custom_chart_variable_create(RRDSET *st, const char *name); -extern void rrdsetvar_custom_chart_variable_set(RRDSETVAR *rv, NETDATA_DOUBLE value); - -extern void rrdsetvar_rename_all(RRDSET *st); -extern RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, RRDVAR_TYPE type, void *value, RRDVAR_OPTIONS options); -extern void rrdsetvar_free(RRDSETVAR *rs); +void rrdsetvar_print_to_streaming_custom_chart_variables(RRDSET *st, BUFFER *wb); #endif //NETDATA_RRDSETVAR_H diff --git a/database/rrdvar.c b/database/rrdvar.c index d4dda1079..28be4f6a1 100644 --- a/database/rrdvar.c +++ b/database/rrdvar.c @@ -1,8 +1,19 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#define NETDATA_HEALTH_INTERNALS #include "rrd.h" +// the variables as stored in the variables indexes +// there are 3 indexes: +// 1. at each chart (RRDSET.rrdvar_root_index) +// 2. at each context (RRDFAMILY.rrdvar_root_index) +// 3. at each host (RRDHOST.rrdvar_root_index) +typedef struct rrdvar { + STRING *name; + void *value; + RRDVAR_FLAGS flags:24; + RRDVAR_TYPE type:8; +} RRDVAR; + // ---------------------------------------------------------------------------- // RRDVAR management @@ -20,168 +31,153 @@ inline int rrdvar_fix_name(char *variable) { return fixed; } -int rrdvar_compare(void* a, void* b) { - if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1; - else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1; - else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name); +inline STRING *rrdvar_name_to_string(const char *name) { + char *variable = strdupz(name); + rrdvar_fix_name(variable); + STRING *name_string = string_strdupz(variable); + freez(variable); + return name_string; } -static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) { - RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl_t *)(rv)); - if(ret != rv) - debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name); +struct rrdvar_constructor { + STRING *name; + void *value; + RRDVAR_FLAGS options:16; + RRDVAR_TYPE type:8; - return ret; -} + enum { + RRDVAR_REACT_NONE = 0, + RRDVAR_REACT_NEW = (1 << 0), + } react_action; +}; + +static void rrdvar_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdvar, void *constructor_data) { + RRDVAR *rv = rrdvar; + struct rrdvar_constructor *ctr = constructor_data; -static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) { - RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl_t *)(rv)); - if(!ret) - error("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name); + ctr->options &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS; - return ret; + rv->name = string_dup(ctr->name); + rv->type = ctr->type; + rv->flags = ctr->options; + + if(!ctr->value) { + NETDATA_DOUBLE *v = mallocz(sizeof(NETDATA_DOUBLE)); + *v = NAN; + rv->value = v; + rv->flags |= RRDVAR_FLAG_ALLOCATED; + } + else + rv->value = ctr->value; + + ctr->react_action = RRDVAR_REACT_NEW; } -static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) { - RRDVAR tmp; - tmp.name = (char *)name; - tmp.hash = (hash)?hash:simple_hash(tmp.name); +static void rrdvar_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdvar, void *nothing __maybe_unused) { + RRDVAR *rv = rrdvar; - return (RRDVAR *)avl_search_lock(tree, (avl_t *)&tmp); + if(rv->flags & RRDVAR_FLAG_ALLOCATED) + freez(rv->value); + + string_freez(rv->name); + rv->name = NULL; } -inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) { - (void)host; +DICTIONARY *rrdvariables_create(void) { + DICTIONARY *dict = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); - if(!rv) return; + dictionary_register_insert_callback(dict, rrdvar_insert_callback, NULL); + dictionary_register_delete_callback(dict, rrdvar_delete_callback, NULL); - if(tree) { - debug(D_VARIABLES, "Deleting variable '%s'", rv->name); - if(unlikely(!rrdvar_index_del(tree, rv))) - error("RRDVAR: Attempted to delete variable '%s' from host '%s', but it is not found.", rv->name, host->hostname); - } + return dict; +} - if(rv->options & RRDVAR_OPTION_ALLOCATED) - freez(rv->value); +void rrdvariables_destroy(DICTIONARY *dict) { + dictionary_destroy(dict); +} - freez(rv->name); - freez(rv); +static inline const RRDVAR_ACQUIRED *rrdvar_get_and_acquire(DICTIONARY *dict, STRING *name) { + return (const RRDVAR_ACQUIRED *)dictionary_get_and_acquire_item_advanced(dict, string2str(name), (ssize_t)string_strlen(name) + 1); } -inline RRDVAR *rrdvar_create_and_index(const char *scope __maybe_unused, avl_tree_lock *tree, const char *name, - RRDVAR_TYPE type, RRDVAR_OPTIONS options, void *value) { - char *variable = strdupz(name); - rrdvar_fix_name(variable); - uint32_t hash = simple_hash(variable); - - RRDVAR *rv = rrdvar_index_find(tree, variable, hash); - if(unlikely(!rv)) { - debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", variable, scope); - - rv = callocz(1, sizeof(RRDVAR)); - rv->name = variable; - rv->hash = hash; - rv->type = type; - rv->options = options; - rv->value = value; - rv->last_updated = now_realtime_sec(); - - RRDVAR *ret = rrdvar_index_add(tree, rv); - if(unlikely(ret != rv)) { - debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", variable, scope); - freez(rv); - freez(variable); - rv = NULL; - } - else - debug(D_VARIABLES, "Variable '%s' created in scope '%s'", variable, scope); - } - else { - debug(D_VARIABLES, "Variable '%s' is already found in scope '%s'.", variable, scope); +inline void rrdvar_release_and_del(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva) { + if(unlikely(!dict || !rva)) return; - // already exists - freez(variable); + RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); - // this is important - // it must return NULL - not the existing variable - or double-free will happen - rv = NULL; - } + dictionary_del_advanced(dict, string2str(rv->name), (ssize_t)string_strlen(rv->name) + 1); - return rv; + dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva); } -void rrdvar_free_remaining_variables(RRDHOST *host, avl_tree_lock *tree_lock) { - // This is not bullet proof - avl should support some means to destroy it - // with a callback for each item already in the index +inline const RRDVAR_ACQUIRED *rrdvar_add_and_acquire(const char *scope __maybe_unused, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value) { + if(unlikely(!dict || !name)) return NULL; - RRDVAR *rv, *last = NULL; - while((rv = (RRDVAR *)tree_lock->avl_tree.root)) { - if(unlikely(rv == last)) { - error("RRDVAR: INTERNAL ERROR: Cannot cleanup tree of RRDVARs"); - break; - } - last = rv; - rrdvar_free(host, tree_lock, rv); - } + struct rrdvar_constructor tmp = { + .name = name, + .value = value, + .type = type, + .options = options, + .react_action = RRDVAR_REACT_NONE, + }; + return (const RRDVAR_ACQUIRED *)dictionary_set_and_acquire_item_advanced(dict, string2str(name), (ssize_t)string_strlen(name) + 1, NULL, sizeof(RRDVAR), &tmp); +} + +void rrdvar_delete_all(DICTIONARY *dict) { + dictionary_flush(dict); } + // ---------------------------------------------------------------------------- // CUSTOM HOST VARIABLES -inline int rrdvar_callback_for_all_host_variables(RRDHOST *host, int (*callback)(void * /*rrdvar*/, void * /*data*/), void *data) { - return avl_traverse_lock(&host->rrdvar_root_index, callback, data); +inline int rrdvar_walkthrough_read(DICTIONARY *dict, int (*callback)(const DICTIONARY_ITEM *item, void *rrdvar, void *data), void *data) { + if(unlikely(!dict)) return 0; // when health is not enabled + return dictionary_walkthrough_read(dict, callback, data); } -static RRDVAR *rrdvar_custom_variable_create(const char *scope, avl_tree_lock *tree_lock, const char *name) { - NETDATA_DOUBLE *v = callocz(1, sizeof(NETDATA_DOUBLE)); - *v = NAN; - - RRDVAR *rv = rrdvar_create_and_index(scope, tree_lock, name, RRDVAR_TYPE_CALCULATED, RRDVAR_OPTION_CUSTOM_HOST_VAR|RRDVAR_OPTION_ALLOCATED, v); - if(unlikely(!rv)) { - freez(v); - debug(D_VARIABLES, "Requested variable '%s' already exists - possibly 2 plugins are updating it at the same time.", name); +const RRDVAR_ACQUIRED *rrdvar_custom_host_variable_add_and_acquire(RRDHOST *host, const char *name) { + DICTIONARY *dict = host->rrdvars; + if(unlikely(!dict)) return NULL; // when health is not enabled - char *variable = strdupz(name); - rrdvar_fix_name(variable); - uint32_t hash = simple_hash(variable); + STRING *name_string = rrdvar_name_to_string(name); - // find the existing one to return it - rv = rrdvar_index_find(tree_lock, variable, hash); + const RRDVAR_ACQUIRED *rva = rrdvar_add_and_acquire("host", dict, name_string, RRDVAR_TYPE_CALCULATED, RRDVAR_FLAG_CUSTOM_HOST_VAR, NULL); - freez(variable); - } - - return rv; + string_freez(name_string); + return rva; } -RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name) { - return rrdvar_custom_variable_create("host", &host->rrdvar_root_index, name); -} +void rrdvar_custom_host_variable_set(RRDHOST *host, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value) { + if(unlikely(!host->rrdvars || !rva)) return; // when health is not enabled -void rrdvar_custom_host_variable_set(RRDHOST *host, RRDVAR *rv, NETDATA_DOUBLE value) { - if(rv->type != RRDVAR_TYPE_CALCULATED || !(rv->options & RRDVAR_OPTION_CUSTOM_HOST_VAR) || !(rv->options & RRDVAR_OPTION_ALLOCATED)) - error("requested to set variable '%s' to value " NETDATA_DOUBLE_FORMAT " but the variable is not a custom one.", rv->name, value); + if(rrdvar_type(rva) != RRDVAR_TYPE_CALCULATED || !(rrdvar_flags(rva) & (RRDVAR_FLAG_CUSTOM_HOST_VAR | RRDVAR_FLAG_ALLOCATED))) + error("requested to set variable '%s' to value " NETDATA_DOUBLE_FORMAT " but the variable is not a custom one.", rrdvar_name(rva), value); else { + RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); NETDATA_DOUBLE *v = rv->value; if(*v != value) { *v = value; - rv->last_updated = now_realtime_sec(); - // if the host is streaming, send this variable upstream immediately - rrdpush_sender_send_this_host_variable_now(host, rv); + rrdpush_sender_send_this_host_variable_now(host, rva); } } } -int foreach_host_variable_callback(RRDHOST *host, int (*callback)(RRDVAR * /*rv*/, void * /*data*/), void *data) { - return avl_traverse_lock(&host->rrdvar_root_index, (int (*)(void *, void *))callback, data); +void rrdvar_release(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva) { + if(unlikely(!dict || !rva)) return; // when health is not enabled + dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva); } // ---------------------------------------------------------------------------- // RRDVAR lookup -NETDATA_DOUBLE rrdvar2number(RRDVAR *rv) { +NETDATA_DOUBLE rrdvar2number(const RRDVAR_ACQUIRED *rva) { + if(unlikely(!rva)) return NAN; + + RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); + switch(rv->type) { case RRDVAR_TYPE_CALCULATED: { NETDATA_DOUBLE *n = (NETDATA_DOUBLE *)rv->value; @@ -190,17 +186,17 @@ NETDATA_DOUBLE rrdvar2number(RRDVAR *rv) { case RRDVAR_TYPE_TIME_T: { time_t *n = (time_t *)rv->value; - return *n; + return (NETDATA_DOUBLE)*n; } case RRDVAR_TYPE_COLLECTED: { collected_number *n = (collected_number *)rv->value; - return *n; + return (NETDATA_DOUBLE)*n; } case RRDVAR_TYPE_TOTAL: { total_number *n = (total_number *)rv->value; - return *n; + return (NETDATA_DOUBLE)*n; } case RRDVAR_TYPE_INT: { @@ -214,28 +210,31 @@ NETDATA_DOUBLE rrdvar2number(RRDVAR *rv) { } } -int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, NETDATA_DOUBLE *result) { +int health_variable_lookup(STRING *variable, RRDCALC *rc, NETDATA_DOUBLE *result) { RRDSET *st = rc->rrdset; if(!st) return 0; RRDHOST *host = st->rrdhost; - RRDVAR *rv; + const RRDVAR_ACQUIRED *rva; - rv = rrdvar_index_find(&st->rrdvar_root_index, variable, hash); - if(rv) { - *result = rrdvar2number(rv); + rva = rrdvar_get_and_acquire(st->rrdvars, variable); + if(rva) { + *result = rrdvar2number(rva); + dictionary_acquired_item_release(st->rrdvars, (const DICTIONARY_ITEM *)rva); return 1; } - rv = rrdvar_index_find(&st->rrdfamily->rrdvar_root_index, variable, hash); - if(rv) { - *result = rrdvar2number(rv); + rva = rrdvar_get_and_acquire(rrdfamily_rrdvars_dict(st->rrdfamily), variable); + if(rva) { + *result = rrdvar2number(rva); + dictionary_acquired_item_release(rrdfamily_rrdvars_dict(st->rrdfamily), (const DICTIONARY_ITEM *)rva); return 1; } - rv = rrdvar_index_find(&host->rrdvar_root_index, variable, hash); - if(rv) { - *result = rrdvar2number(rv); + rva = rrdvar_get_and_acquire(host->rrdvars, variable); + if(rva) { + *result = rrdvar2number(rva); + dictionary_acquired_item_release(host->rrdvars, (const DICTIONARY_ITEM *)rva); return 1; } @@ -248,19 +247,19 @@ int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, NET struct variable2json_helper { BUFFER *buf; size_t counter; - RRDVAR_OPTIONS options; + RRDVAR_FLAGS options; }; -static int single_variable2json(void *entry, void *data) { - struct variable2json_helper *helper = (struct variable2json_helper *)data; - RRDVAR *rv = (RRDVAR *)entry; - NETDATA_DOUBLE value = rrdvar2number(rv); +static int single_variable2json_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry __maybe_unused, void *helper_data) { + struct variable2json_helper *helper = (struct variable2json_helper *)helper_data; + const RRDVAR_ACQUIRED *rva = (const RRDVAR_ACQUIRED *)item; + NETDATA_DOUBLE value = rrdvar2number(rva); - if (helper->options == RRDVAR_OPTION_DEFAULT || rv->options & helper->options) { + if (helper->options == RRDVAR_FLAG_NONE || rrdvar_flags(rva) & helper->options) { if(unlikely(isnan(value) || isinf(value))) - buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name); + buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rrdvar_name(rva)); else - buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5" NETDATA_DOUBLE_MODIFIER, helper->counter?",":"", rv->name, (NETDATA_DOUBLE)value); + buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5" NETDATA_DOUBLE_MODIFIER, helper->counter?",":"", rrdvar_name(rva), (NETDATA_DOUBLE)value); helper->counter++; } @@ -272,11 +271,10 @@ void health_api_v1_chart_custom_variables2json(RRDSET *st, BUFFER *buf) { struct variable2json_helper helper = { .buf = buf, .counter = 0, - .options = RRDVAR_OPTION_CUSTOM_CHART_VAR - }; + .options = RRDVAR_FLAG_CUSTOM_CHART_VAR}; buffer_sprintf(buf, "{"); - avl_traverse_lock(&st->rrdvar_root_index, single_variable2json, (void *)&helper); + rrdvar_walkthrough_read(st->rrdvars, single_variable2json_callback, &helper); buffer_strcat(buf, "\n\t\t\t}"); } @@ -286,20 +284,34 @@ void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) { struct variable2json_helper helper = { .buf = buf, .counter = 0, - .options = RRDVAR_OPTION_DEFAULT - }; + .options = RRDVAR_FLAG_NONE}; - buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", st->id, st->name, st->context); - avl_traverse_lock(&st->rrdvar_root_index, single_variable2json, (void *)&helper); + buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", rrdset_id(st), rrdset_name(st), rrdset_context(st)); + rrdvar_walkthrough_read(st->rrdvars, single_variable2json_callback, &helper); - buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family); + buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", rrdset_family(st)); helper.counter = 0; - avl_traverse_lock(&st->rrdfamily->rrdvar_root_index, single_variable2json, (void *)&helper); + rrdvar_walkthrough_read(rrdfamily_rrdvars_dict(st->rrdfamily), single_variable2json_callback, &helper); - buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", host->hostname); + buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", rrdhost_hostname(host)); helper.counter = 0; - avl_traverse_lock(&host->rrdvar_root_index, single_variable2json, (void *)&helper); + rrdvar_walkthrough_read(host->rrdvars, single_variable2json_callback, &helper); buffer_strcat(buf, "\n\t}\n}\n"); } +// ---------------------------------------------------------------------------- +// RRDVAR private members examination + +const char *rrdvar_name(const RRDVAR_ACQUIRED *rva) { + return dictionary_acquired_item_name((const DICTIONARY_ITEM *)rva); +} + +RRDVAR_FLAGS rrdvar_flags(const RRDVAR_ACQUIRED *rva) { + RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); + return rv->flags; +} +RRDVAR_TYPE rrdvar_type(const RRDVAR_ACQUIRED *rva) { + RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva); + return rv->type; +} diff --git a/database/rrdvar.h b/database/rrdvar.h index 9074edcdb..a511c732d 100644 --- a/database/rrdvar.h +++ b/database/rrdvar.h @@ -5,62 +5,67 @@ #include "libnetdata/libnetdata.h" -extern int rrdvar_compare(void *a, void *b); - typedef enum rrdvar_type { RRDVAR_TYPE_CALCULATED = 1, RRDVAR_TYPE_TIME_T = 2, RRDVAR_TYPE_COLLECTED = 3, RRDVAR_TYPE_TOTAL = 4, RRDVAR_TYPE_INT = 5 + + // this is 8 bit + // to increase it you have to set change the bitfield in + // rrdvar, rrdsetvar, rrddimvar } RRDVAR_TYPE; typedef enum rrdvar_options { - RRDVAR_OPTION_DEFAULT = 0, - RRDVAR_OPTION_ALLOCATED = (1 << 0), // the value ptr is allocated (not a reference) - RRDVAR_OPTION_CUSTOM_HOST_VAR = (1 << 1), // this is a custom host variable, not associated with a dimension - RRDVAR_OPTION_CUSTOM_CHART_VAR = (1 << 2), // this is a custom chart variable, not associated with a dimension - RRDVAR_OPTION_RRDCALC_LOCAL_VAR = (1 << 3), // this is a an alarm variable, attached to a chart - RRDVAR_OPTION_RRDCALC_FAMILY_VAR = (1 << 4), // this is a an alarm variable, attached to a family - RRDVAR_OPTION_RRDCALC_HOST_CHARTID_VAR = (1 << 5), // this is a an alarm variable, attached to the host, using the chart id - RRDVAR_OPTION_RRDCALC_HOST_CHARTNAME_VAR = (1 << 6), // this is a an alarm variable, attached to the host, using the chart name -} RRDVAR_OPTIONS; - -// the variables as stored in the variables indexes -// there are 3 indexes: -// 1. at each chart (RRDSET.rrdvar_root_index) -// 2. at each context (RRDFAMILY.rrdvar_root_index) -// 3. at each host (RRDHOST.rrdvar_root_index) -struct rrdvar { - avl_t avl; - - char *name; - uint32_t hash; - - RRDVAR_TYPE type; - RRDVAR_OPTIONS options; - - void *value; - - time_t last_updated; -}; + RRDVAR_FLAG_NONE = 0, + RRDVAR_FLAG_ALLOCATED = (1 << 0), // the value ptr is allocated (not a reference) + RRDVAR_FLAG_CUSTOM_HOST_VAR = (1 << 1), // this is a custom host variable, not associated with a dimension + RRDVAR_FLAG_CUSTOM_CHART_VAR = (1 << 2), // this is a custom chart variable, not associated with a dimension + RRDVAR_FLAG_RRDCALC_LOCAL_VAR = (1 << 3), // this is a an alarm variable, attached to a chart + RRDVAR_FLAG_RRDCALC_FAMILY_VAR = (1 << 4), // this is a an alarm variable, attached to a family + RRDVAR_FLAG_RRDCALC_HOST_CHARTID_VAR = (1 << 5), // this is a an alarm variable, attached to the host, using the chart id + RRDVAR_FLAG_RRDCALC_HOST_CHARTNAME_VAR = (1 << 6), // this is a an alarm variable, attached to the host, using the chart name + + // this is 24 bit + // to increase it you have to set change the bitfield in + // rrdvar, rrdsetvar, rrddimvar +} RRDVAR_FLAGS; + +#define RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS \ + (RRDVAR_FLAG_ALLOCATED) + +#define RRDVAR_OPTIONS_REMOVED_WHEN_PROPAGATING_TO_RRDVAR \ + (RRDVAR_FLAG_ALLOCATED) #define RRDVAR_MAX_LENGTH 1024 -extern int rrdvar_fix_name(char *variable); +int rrdvar_fix_name(char *variable); #include "rrd.h" -extern RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name); -extern void rrdvar_custom_host_variable_set(RRDHOST *host, RRDVAR *rv, NETDATA_DOUBLE value); -extern int foreach_host_variable_callback(RRDHOST *host, int (*callback)(RRDVAR *rv, void *data), void *data); -extern void rrdvar_free_remaining_variables(RRDHOST *host, avl_tree_lock *tree_lock); +STRING *rrdvar_name_to_string(const char *name); + +const RRDVAR_ACQUIRED *rrdvar_custom_host_variable_add_and_acquire(RRDHOST *host, const char *name); +void rrdvar_custom_host_variable_set(RRDHOST *host, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value); + +int rrdvar_walkthrough_read(DICTIONARY *dict, int (*callback)(const DICTIONARY_ITEM *item, void *rrdvar, void *data), void *data); + +#define rrdvar_custom_host_variable_release(host, rva) rrdvar_release((host)->rrdvars, rva) +void rrdvar_release(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva); + +NETDATA_DOUBLE rrdvar2number(const RRDVAR_ACQUIRED *rva); + +const RRDVAR_ACQUIRED *rrdvar_add_and_acquire(const char *scope, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value); +void rrdvar_release_and_del(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva); -extern int rrdvar_callback_for_all_host_variables(RRDHOST *host, int (*callback)(void *rrdvar, void *data), void *data); +DICTIONARY *rrdvariables_create(void); +void rrdvariables_destroy(DICTIONARY *dict); -extern NETDATA_DOUBLE rrdvar2number(RRDVAR *rv); +void rrdvar_delete_all(DICTIONARY *dict); -extern RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, RRDVAR_TYPE type, RRDVAR_OPTIONS options, void *value); -extern void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv); +const char *rrdvar_name(const RRDVAR_ACQUIRED *rva); +RRDVAR_FLAGS rrdvar_flags(const RRDVAR_ACQUIRED *rva); +RRDVAR_TYPE rrdvar_type(const RRDVAR_ACQUIRED *rva); #endif //NETDATA_RRDVAR_H diff --git a/database/sqlite/sqlite_aclk.c b/database/sqlite/sqlite_aclk.c index 43b341097..7e3a9b2eb 100644 --- a/database/sqlite/sqlite_aclk.c +++ b/database/sqlite/sqlite_aclk.c @@ -3,59 +3,20 @@ #include "sqlite_functions.h" #include "sqlite_aclk.h" -#include "sqlite_aclk_chart.h" #include "sqlite_aclk_node.h" -#ifdef ENABLE_ACLK -#include "../../aclk/aclk.h" -#endif - void sanity_check(void) { // make sure the compiler will stop on misconfigurations BUILD_BUG_ON(WORKER_UTILIZATION_MAX_JOB_TYPES < ACLK_MAX_ENUMERATIONS_DEFINED); } const char *aclk_sync_config[] = { - "CREATE TABLE IF NOT EXISTS dimension_delete (dimension_id blob, dimension_name text, chart_type_id text, " - "dim_id blob, chart_id blob, host_id blob, date_created);", - - "CREATE INDEX IF NOT EXISTS ind_h1 ON dimension_delete (host_id);", - - "CREATE TRIGGER IF NOT EXISTS tr_dim_del AFTER DELETE ON dimension BEGIN INSERT INTO dimension_delete " - "(dimension_id, dimension_name, chart_type_id, dim_id, chart_id, host_id, date_created)" - " select old.id, old.name, c.type||\".\"||c.id, old.dim_id, old.chart_id, c.host_id, unixepoch() FROM" - " chart c WHERE c.chart_id = old.chart_id; END;", - - "DELETE FROM dimension_delete WHERE host_id NOT IN" - " (SELECT host_id FROM host) OR unixepoch() - date_created > 604800;", NULL, }; uv_mutex_t aclk_async_lock; struct aclk_database_worker_config *aclk_thread_head = NULL; -int retention_running = 0; - -#ifdef ENABLE_ACLK -static void stop_retention_run() -{ - uv_mutex_lock(&aclk_async_lock); - retention_running = 0; - uv_mutex_unlock(&aclk_async_lock); -} - -static int request_retention_run() -{ - int rc = 0; - uv_mutex_lock(&aclk_async_lock); - if (unlikely(retention_running)) - rc = 1; - else - retention_running = 1; - uv_mutex_unlock(&aclk_async_lock); - return rc; -} -#endif int claimed() { @@ -197,25 +158,6 @@ struct aclk_database_cmd aclk_database_deq_cmd(struct aclk_database_worker_confi return ret; } -int aclk_worker_enq_cmd(char *node_id, struct aclk_database_cmd *cmd) -{ - if (unlikely(!node_id || !cmd)) - return 0; - - uv_mutex_lock(&aclk_async_lock); - struct aclk_database_worker_config *wc = aclk_thread_head; - - while (wc) { - if (!strcmp(wc->node_id, node_id)) - break; - wc = wc->next; - } - uv_mutex_unlock(&aclk_async_lock); - if (wc) - aclk_database_enq_cmd(wc, cmd); - return (wc == NULL); -} - struct aclk_database_worker_config *find_inactive_wc_by_node_id(char *node_id) { if (unlikely(!node_id)) @@ -237,15 +179,14 @@ struct aclk_database_worker_config *find_inactive_wc_by_node_id(char *node_id) void aclk_sync_exit_all() { rrd_rdlock(); - RRDHOST *host = localhost; - while(host) { + RRDHOST *host; + rrdhost_foreach_read(host) { struct aclk_database_worker_config *wc = host->dbsync_worker; if (wc) { wc->is_shutting_down = 1; (void) aclk_database_deq_cmd(wc); uv_cond_signal(&wc->cmd_cond); } - host = host->next; } rrd_unlock(); @@ -304,23 +245,26 @@ static int create_host_callback(void *data, int argc, char **argv, char **column , (const char *) (argv[IDX_PROGRAM_VERSION] ? argv[IDX_PROGRAM_VERSION] : "unknown") , argv[3] ? str2i(argv[IDX_UPDATE_EVERY]) : 1 , argv[13] ? str2i(argv[IDX_ENTRIES]) : 0 - , RRD_MEMORY_MODE_DBENGINE + , default_rrd_memory_mode , 0 // health , 0 // rrdpush enabled , NULL //destination , NULL // api key , NULL // send charts matching + , false // rrdpush_enable_replication + , 0 // rrdpush_seconds_to_replicate + , 0 // rrdpush_replication_step , system_info , 1 ); if (likely(host)) - host->host_labels = sql_load_host_labels((uuid_t *)argv[IDX_HOST_ID]); + host->rrdlabels = sql_load_host_labels((uuid_t *)argv[IDX_HOST_ID]); #ifdef NETDATA_INTERNAL_CHECKS char node_str[UUID_STR_LEN] = ""; if (likely(host->node_id)) uuid_unparse_lower(*host->node_id, node_str); - internal_error(true, "Adding archived host \"%s\" with GUID \"%s\" node id = \"%s\"", host->hostname, host->machine_guid, node_str); + internal_error(true, "Adding archived host \"%s\" with GUID \"%s\" node id = \"%s\"", rrdhost_hostname(host), host->machine_guid, node_str); #endif return 0; } @@ -335,7 +279,7 @@ int aclk_start_sync_thread(void *data, int argc, char **argv, char **column) uuid_unparse_lower(*((uuid_t *) argv[0]), uuid_str); - RRDHOST *host = rrdhost_find_by_guid(uuid_str, 0); + RRDHOST *host = rrdhost_find_by_guid(uuid_str); if (host == localhost) return 0; @@ -361,7 +305,7 @@ void sql_aclk_sync_init(void) for (int i = 0; aclk_sync_config[i]; i++) { debug(D_ACLK_SYNC, "Executing %s", aclk_sync_config[i]); - rc = sqlite3_exec(db_meta, aclk_sync_config[i], 0, 0, &err_msg); + rc = sqlite3_exec_monitored(db_meta, aclk_sync_config[i], 0, 0, &err_msg); if (rc != SQLITE_OK) { error_report("SQLite error aclk sync initialization setup, rc = %d (%s)", rc, err_msg); error_report("SQLite failed statement %s", aclk_sync_config[i]); @@ -372,18 +316,16 @@ void sql_aclk_sync_init(void) info("SQLite aclk sync initialization completed"); fatal_assert(0 == uv_mutex_init(&aclk_async_lock)); - if (likely(rrdcontext_enabled == CONFIG_BOOLEAN_YES)) { - rc = sqlite3_exec(db_meta, "SELECT host_id, hostname, registry_hostname, update_every, os, " - "timezone, tags, hops, memory_mode, abbrev_timezone, utc_offset, program_name, " - "program_version, entries, health_enabled FROM host WHERE hops >0;", - create_host_callback, NULL, &err_msg); - if (rc != SQLITE_OK) { - error_report("SQLite error when loading archived hosts, rc = %d (%s)", rc, err_msg); - sqlite3_free(err_msg); - } + rc = sqlite3_exec_monitored(db_meta, "SELECT host_id, hostname, registry_hostname, update_every, os, " + "timezone, tags, hops, memory_mode, abbrev_timezone, utc_offset, program_name, " + "program_version, entries, health_enabled FROM host WHERE hops >0;", + create_host_callback, NULL, &err_msg); + if (rc != SQLITE_OK) { + error_report("SQLite error when loading archived hosts, rc = %d (%s)", rc, err_msg); + sqlite3_free(err_msg); } - rc = sqlite3_exec(db_meta, "SELECT ni.host_id, ni.node_id FROM host h, node_instance ni WHERE " + rc = sqlite3_exec_monitored(db_meta, "SELECT ni.host_id, ni.node_id FROM host h, node_instance ni WHERE " "h.host_id = ni.host_id AND ni.node_id IS NOT NULL;", aclk_start_sync_thread, NULL, &err_msg); if (rc != SQLITE_OK) { error_report("SQLite error when starting ACLK sync threads, rc = %d (%s)", rc, err_msg); @@ -423,30 +365,6 @@ static void timer_cb(uv_timer_t* handle) } if (aclk_connected) { - if (wc->rotation_after && wc->rotation_after < now) { - cmd.opcode = ACLK_DATABASE_UPD_RETENTION; - if (!aclk_database_enq_cmd_noblock(wc, &cmd)) - wc->rotation_after += ACLK_DATABASE_ROTATION_INTERVAL; - } - - if (wc->chart_updates && !wc->chart_pending && wc->chart_payload_count) { - cmd.opcode = ACLK_DATABASE_PUSH_CHART; - cmd.count = ACLK_MAX_CHART_BATCH; - cmd.param1 = ACLK_MAX_CHART_BATCH_COUNT; - if (!aclk_database_enq_cmd_noblock(wc, &cmd)) { - if (wc->retry_count) - info("Queued chart/dimension payload command %s, retry count = %u", wc->host_guid, wc->retry_count); - wc->chart_pending = 1; - wc->retry_count = 0; - } else { - wc->retry_count++; - if (wc->retry_count % 100 == 0) - error_report("Failed to queue chart/dimension payload command %s, retry count = %u", - wc->host_guid, - wc->retry_count); - } - } - if (wc->alert_updates && !wc->pause_alert_updates) { cmd.opcode = ACLK_DATABASE_PUSH_ALERT; cmd.count = ACLK_MAX_ALERT_UPDATES; @@ -456,52 +374,12 @@ static void timer_cb(uv_timer_t* handle) #endif } - -#ifdef ENABLE_ACLK -void after_send_retention(uv_work_t *req, int status) -{ - struct aclk_database_worker_config *wc = req->data; - (void)status; - stop_retention_run(); - wc->retention_running = 0; - - struct aclk_database_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = ACLK_DATABASE_DIM_DELETION; - if (aclk_database_enq_cmd_noblock(wc, &cmd)) - info("Failed to queue a dimension deletion message"); - - cmd.opcode = ACLK_DATABASE_NODE_INFO; - if (aclk_database_enq_cmd_noblock(wc, &cmd)) - info("Failed to queue a node update info message"); -} - - -static void send_retention(uv_work_t *req) -{ - struct aclk_database_worker_config *wc = req->data; - - if (unlikely(wc->is_shutting_down)) - return; - - aclk_update_retention(wc); -} -#endif - #define MAX_CMD_BATCH_SIZE (256) void aclk_database_worker(void *arg) { worker_register("ACLKSYNC"); worker_register_job_name(ACLK_DATABASE_NOOP, "noop"); - worker_register_job_name(ACLK_DATABASE_ADD_CHART, "chart add"); - worker_register_job_name(ACLK_DATABASE_ADD_DIMENSION, "dimension add"); - worker_register_job_name(ACLK_DATABASE_PUSH_CHART, "chart push"); - worker_register_job_name(ACLK_DATABASE_PUSH_CHART_CONFIG, "chart conf push"); - worker_register_job_name(ACLK_DATABASE_RESET_CHART, "chart reset"); - worker_register_job_name(ACLK_DATABASE_CHART_ACK, "chart ack"); - worker_register_job_name(ACLK_DATABASE_UPD_RETENTION, "retention check"); - worker_register_job_name(ACLK_DATABASE_DIM_DELETION, "dimension delete"); worker_register_job_name(ACLK_DATABASE_ORPHAN_HOST, "node orphan"); worker_register_job_name(ACLK_DATABASE_ALARM_HEALTH_LOG, "alert log"); worker_register_job_name(ACLK_DATABASE_CLEANUP, "cleanup"); @@ -526,7 +404,7 @@ void aclk_database_worker(void *arg) char threadname[NETDATA_THREAD_NAME_MAX+1]; if (wc->host) - snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "AS_%s", wc->host->hostname); + snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "AS_%s", rrdhost_hostname(wc->host)); else { snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "AS_%s", wc->uuid_str); threadname[11] = '\0'; @@ -556,23 +434,13 @@ void aclk_database_worker(void *arg) timer_req.data = wc; fatal_assert(0 == uv_timer_start(&timer_req, timer_cb, TIMER_PERIOD_MS, TIMER_PERIOD_MS)); -// wc->retry_count = 0; wc->node_info_send = 1; -// aclk_add_worker_thread(wc); - info("Starting ACLK sync thread for host %s -- scratch area %lu bytes", wc->host_guid, sizeof(*wc)); + info("Starting ACLK sync thread for host %s -- scratch area %lu bytes", wc->host_guid, (unsigned long int) sizeof(*wc)); memset(&cmd, 0, sizeof(cmd)); -#ifdef ENABLE_ACLK - uv_work_t retention_work; - sql_get_last_chart_sequence(wc); - wc->chart_payload_count = sql_get_pending_count(wc); - if (!wc->chart_payload_count) - info("%s: No pending charts and dimensions detected during startup", wc->host_guid); -#endif wc->startup_time = now_realtime_sec(); wc->cleanup_after = wc->startup_time + ACLK_DATABASE_CLEANUP_FIRST; - wc->rotation_after = wc->startup_time + ACLK_DATABASE_ROTATION_DELAY; debug(D_ACLK_SYNC,"Node %s reports pending message count = %u", wc->node_id, wc->chart_payload_count); @@ -604,6 +472,13 @@ void aclk_database_worker(void *arg) // MAINTENANCE case ACLK_DATABASE_CLEANUP: debug(D_ACLK_SYNC, "Database cleanup for %s", wc->host_guid); + + if (wc->startup_time + ACLK_DATABASE_CLEANUP_FIRST + 2 < now_realtime_sec() && claimed() && aclk_connected) { + cmd.opcode = ACLK_DATABASE_NODE_INFO; + cmd.completion = NULL; + (void) aclk_database_enq_cmd_noblock(wc, &cmd); + } + sql_maint_aclk_sync_database(wc, cmd); if (wc->host == localhost) sql_check_aclk_table_list(wc); @@ -614,33 +489,6 @@ void aclk_database_worker(void *arg) sql_delete_aclk_table_list(wc, cmd); break; -// CHART / DIMENSION OPERATIONS -#ifdef ENABLE_ACLK - case ACLK_DATABASE_ADD_CHART: - debug(D_ACLK_SYNC, "Adding chart event for %s", wc->host_guid); - aclk_add_chart_event(wc, cmd); - break; - case ACLK_DATABASE_ADD_DIMENSION: - debug(D_ACLK_SYNC, "Adding dimension event for %s", wc->host_guid); - aclk_add_dimension_event(wc, cmd); - break; - case ACLK_DATABASE_PUSH_CHART: - debug(D_ACLK_SYNC, "Pushing chart info to the cloud for node %s", wc->host_guid); - aclk_send_chart_event(wc, cmd); - break; - case ACLK_DATABASE_PUSH_CHART_CONFIG: - debug(D_ACLK_SYNC, "Pushing chart config info to the cloud for node %s", wc->host_guid); - aclk_send_chart_config(wc, cmd); - break; - case ACLK_DATABASE_CHART_ACK: - debug(D_ACLK_SYNC, "ACK chart SEQ for %s to %"PRIu64, wc->uuid_str, (uint64_t) cmd.param1); - aclk_receive_chart_ack(wc, cmd); - break; - case ACLK_DATABASE_RESET_CHART: - debug(D_ACLK_SYNC, "RESET chart SEQ for %s to %"PRIu64, wc->uuid_str, (uint64_t) cmd.param1); - aclk_receive_chart_reset(wc, cmd); - break; -#endif // ALERTS case ACLK_DATABASE_PUSH_ALERT_CONFIG: debug(D_ACLK_SYNC,"Pushing chart config info to the cloud for %s", wc->host_guid); @@ -673,27 +521,6 @@ void aclk_database_worker(void *arg) sql_build_node_collectors(wc); break; #ifdef ENABLE_ACLK - case ACLK_DATABASE_DIM_DELETION: - debug(D_ACLK_SYNC,"Sending dimension deletion information %s", wc->uuid_str); - aclk_process_dimension_deletion(wc, cmd); - break; - case ACLK_DATABASE_UPD_RETENTION: - if (unlikely(wc->retention_running)) - break; - - if (unlikely(request_retention_run())) { - wc->rotation_after = now_realtime_sec() + ACLK_DATABASE_RETENTION_RETRY; - break; - } - - debug(D_ACLK_SYNC,"Sending retention info for %s", wc->uuid_str); - retention_work.data = wc; - wc->retention_running = 1; - if (unlikely(uv_queue_work(loop, &retention_work, send_retention, after_send_retention))) { - wc->retention_running = 0; - stop_retention_run(); - } - break; // NODE_INSTANCE DETECTION case ACLK_DATABASE_ORPHAN_HOST: @@ -705,14 +532,14 @@ void aclk_database_worker(void *arg) case ACLK_DATABASE_TIMER: if (unlikely(localhost && !wc->host && !wc->is_orphan)) { if (claimed()) { - wc->host = rrdhost_find_by_guid(wc->host_guid, 0); + wc->host = rrdhost_find_by_guid(wc->host_guid); if (wc->host) { - info("HOST %s (%s) detected as active", wc->host->hostname, wc->host_guid); - snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "AS_%s", wc->host->hostname); + info("HOST %s (%s) detected as active", rrdhost_hostname(wc->host), wc->host_guid); + snprintfz(threadname, NETDATA_THREAD_NAME_MAX, "AS_%s", rrdhost_hostname(wc->host)); uv_thread_set_name_np(wc->thread, threadname); wc->host->dbsync_worker = wc; if (unlikely(!wc->hostname)) - wc->hostname = strdupz(wc->host->hostname); + wc->hostname = strdupz(rrdhost_hostname(wc->host)); aclk_del_worker_thread(wc); wc->node_info_send = 1; } @@ -803,30 +630,6 @@ void sql_create_aclk_table(RRDHOST *host, uuid_t *host_uuid, uuid_t *node_id) BUFFER *sql = buffer_create(ACLK_SYNC_QUERY_SIZE); - buffer_sprintf(sql, TABLE_ACLK_CHART, uuid_str); - db_execute(buffer_tostring(sql)); - buffer_flush(sql); - - buffer_sprintf(sql, TABLE_ACLK_CHART_PAYLOAD, uuid_str); - db_execute(buffer_tostring(sql)); - buffer_flush(sql); - - buffer_sprintf(sql, TABLE_ACLK_CHART_LATEST, uuid_str); - db_execute(buffer_tostring(sql)); - buffer_flush(sql); - - buffer_sprintf(sql, INDEX_ACLK_CHART, uuid_str, uuid_str); - db_execute(buffer_tostring(sql)); - buffer_flush(sql); - - buffer_sprintf(sql, INDEX_ACLK_CHART_LATEST, uuid_str, uuid_str); - db_execute(buffer_tostring(sql)); - buffer_flush(sql); - - buffer_sprintf(sql, TRIGGER_ACLK_CHART_PAYLOAD, uuid_str, uuid_str, uuid_str); - db_execute(buffer_tostring(sql)); - buffer_flush(sql); - buffer_sprintf(sql, TABLE_ACLK_ALERT, uuid_str); db_execute(buffer_tostring(sql)); buffer_flush(sql); @@ -844,16 +647,14 @@ void sql_create_aclk_table(RRDHOST *host, uuid_t *host_uuid, uuid_t *node_id) uuid_unparse_lower(*node_id, wc->node_id); if (likely(host)) { host->dbsync_worker = (void *)wc; - wc->hostname = strdupz(host->hostname); + wc->hostname = strdupz(rrdhost_hostname(host)); } else wc->hostname = get_hostname_by_node_id(wc->node_id); wc->host = host; strcpy(wc->uuid_str, uuid_str); strcpy(wc->host_guid, host_guid); - wc->chart_updates = 0; wc->alert_updates = 0; - wc->retry_count = 0; aclk_database_init_cmd_queue(wc); aclk_add_worker_thread(wc); fatal_assert(0 == uv_thread_create(&(wc->thread), aclk_database_worker, wc)); @@ -873,31 +674,9 @@ void sql_maint_aclk_sync_database(struct aclk_database_worker_config *wc, struct BUFFER *sql = buffer_create(ACLK_SYNC_QUERY_SIZE); - buffer_sprintf(sql,"DELETE FROM aclk_chart_%s WHERE date_submitted IS NOT NULL AND " - "CAST(date_updated AS INT) < unixepoch()-%d;", wc->uuid_str, ACLK_DELETE_ACK_INTERNAL); - db_execute(buffer_tostring(sql)); - buffer_flush(sql); - - buffer_sprintf(sql,"DELETE FROM aclk_chart_payload_%s WHERE unique_id NOT IN " - "(SELECT unique_id FROM aclk_chart_%s) AND unique_id NOT IN (SELECT unique_id FROM aclk_chart_latest_%s);", - wc->uuid_str, wc->uuid_str, wc->uuid_str); - db_execute(buffer_tostring(sql)); - buffer_flush(sql); - buffer_sprintf(sql,"DELETE FROM aclk_alert_%s WHERE date_submitted IS NOT NULL AND " "CAST(date_cloud_ack AS INT) < unixepoch()-%d;", wc->uuid_str, ACLK_DELETE_ACK_ALERTS_INTERNAL); db_execute(buffer_tostring(sql)); - buffer_flush(sql); - - buffer_sprintf(sql,"UPDATE aclk_chart_%s SET status = NULL, date_submitted=unixepoch() WHERE " - "date_submitted IS NULL AND CAST(date_created AS INT) < unixepoch()-%d;", wc->uuid_str, ACLK_AUTO_MARK_SUBMIT_INTERVAL); - db_execute(buffer_tostring(sql)); - buffer_flush(sql); - - buffer_sprintf(sql,"UPDATE aclk_chart_%s SET date_updated = unixepoch() WHERE date_updated IS NULL" - " AND date_submitted IS NOT NULL AND CAST(date_submitted AS INT) < unixepoch()-%d;", - wc->uuid_str, ACLK_AUTO_MARK_UPDATED_INTERVAL); - db_execute(buffer_tostring(sql)); buffer_free(sql); return; @@ -927,7 +706,7 @@ static int is_host_available(uuid_t *host_id) error_report("Failed to bind host_id parameter to select node instance information"); goto failed; } - rc = sqlite3_step(res); + rc = sqlite3_step_monitored(res); failed: if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) @@ -980,7 +759,7 @@ void sql_delete_aclk_table_list(struct aclk_database_worker_config *wc, struct a } buffer_flush(sql); - while (sqlite3_step(res) == SQLITE_ROW) + while (sqlite3_step_monitored(res) == SQLITE_ROW) buffer_strcat(sql, (char *) sqlite3_column_text(res, 0)); rc = sqlite3_finalize(res); @@ -1016,42 +795,10 @@ void sql_check_aclk_table_list(struct aclk_database_worker_config *wc) { char *err_msg = NULL; debug(D_ACLK_SYNC,"Cleaning tables for nodes that do not exist"); - int rc = sqlite3_exec(db_meta, SQL_SELECT_ACLK_ACTIVE_LIST, sql_check_aclk_table, (void *) wc, &err_msg); + int rc = sqlite3_exec_monitored(db_meta, SQL_SELECT_ACLK_ACTIVE_LIST, sql_check_aclk_table, (void *) wc, &err_msg); if (rc != SQLITE_OK) { error_report("Query failed when trying to check for obsolete ACLK sync tables, %s", err_msg); sqlite3_free(err_msg); } - db_execute("DELETE FROM dimension_delete WHERE host_id NOT IN (SELECT host_id FROM host) " - " OR unixepoch() - date_created > 604800;"); - return; -} - -void aclk_data_rotated(void) -{ -#ifdef ENABLE_ACLK - - if (!aclk_connected) - return; - - time_t next_rotation_time = now_realtime_sec()+ACLK_DATABASE_ROTATION_DELAY; - rrd_rdlock(); - RRDHOST *this_host = localhost; - while (this_host) { - struct aclk_database_worker_config *wc = this_host->dbsync_worker; - if (wc) - wc->rotation_after = next_rotation_time; - this_host = this_host->next; - } - rrd_unlock(); - - struct aclk_database_worker_config *tmp = aclk_thread_head; - - uv_mutex_lock(&aclk_async_lock); - while (tmp) { - tmp->rotation_after = next_rotation_time; - tmp = tmp->next; - } - uv_mutex_unlock(&aclk_async_lock); -#endif return; } diff --git a/database/sqlite/sqlite_aclk.h b/database/sqlite/sqlite_aclk.h index b73f422e1..06d5d0270 100644 --- a/database/sqlite/sqlite_aclk.h +++ b/database/sqlite/sqlite_aclk.h @@ -5,8 +5,6 @@ #include "sqlite3.h" -// TODO: To be added -#include "../../aclk/schema-wrappers/chart_stream.h" #ifndef ACLK_MAX_CHART_BATCH #define ACLK_MAX_CHART_BATCH (200) @@ -15,15 +13,9 @@ #define ACLK_MAX_CHART_BATCH_COUNT (10) #endif #define ACLK_MAX_ALERT_UPDATES (5) -#define ACLK_DATABASE_CLEANUP_FIRST (60) -#define ACLK_DATABASE_ROTATION_DELAY (180) -#define ACLK_DATABASE_RETENTION_RETRY (60) +#define ACLK_DATABASE_CLEANUP_FIRST (1200) #define ACLK_DATABASE_CLEANUP_INTERVAL (3600) -#define ACLK_DATABASE_ROTATION_INTERVAL (3600) -#define ACLK_DELETE_ACK_INTERNAL (600) #define ACLK_DELETE_ACK_ALERTS_INTERNAL (86400) -#define ACLK_AUTO_MARK_SUBMIT_INTERVAL (3600) -#define ACLK_AUTO_MARK_UPDATED_INTERVAL (1800) #define ACLK_SYNC_QUERY_SIZE 512 struct aclk_completion { @@ -74,57 +66,14 @@ static inline void uuid_unparse_lower_fix(uuid_t *uuid, char *out) out[23] = '_'; } -static inline char *get_str_from_uuid(uuid_t *uuid) -{ - char uuid_str[GUID_LEN + 1]; - if (unlikely(!uuid)) { - uuid_t zero_uuid; - uuid_clear(zero_uuid); - uuid_unparse_lower(zero_uuid, uuid_str); - } - else - uuid_unparse_lower(*uuid, uuid_str); - return strdupz(uuid_str); -} - -#define TABLE_ACLK_CHART "CREATE TABLE IF NOT EXISTS aclk_chart_%s (sequence_id INTEGER PRIMARY KEY, " \ - "date_created, date_updated, date_submitted, status, uuid, type, unique_id, " \ - "update_count default 1, unique(uuid, status));" - -#define TABLE_ACLK_CHART_PAYLOAD "CREATE TABLE IF NOT EXISTS aclk_chart_payload_%s (unique_id BLOB PRIMARY KEY, " \ - "uuid, claim_id, type, date_created, payload);" - -#define TABLE_ACLK_CHART_LATEST "CREATE TABLE IF NOT EXISTS aclk_chart_latest_%s (uuid BLOB PRIMARY KEY, " \ - "unique_id, date_submitted);" - -#define TRIGGER_ACLK_CHART_PAYLOAD "CREATE TRIGGER IF NOT EXISTS aclk_tr_chart_payload_%s " \ - "after insert on aclk_chart_payload_%s " \ - "begin insert into aclk_chart_%s (uuid, unique_id, type, status, date_created) values " \ - " (new.uuid, new.unique_id, new.type, 'pending', unixepoch()) on conflict(uuid, status) " \ - " do update set unique_id = new.unique_id, update_count = update_count + 1; " \ - "end;" - #define TABLE_ACLK_ALERT "CREATE TABLE IF NOT EXISTS aclk_alert_%s (sequence_id INTEGER PRIMARY KEY, " \ - "alert_unique_id, date_created, date_submitted, date_cloud_ack, " \ + "alert_unique_id, date_created, date_submitted, date_cloud_ack, filtered_alert_unique_id NOT NULL, " \ "unique(alert_unique_id));" -#define INDEX_ACLK_CHART "CREATE INDEX IF NOT EXISTS aclk_chart_index_%s ON aclk_chart_%s (unique_id);" - -#define INDEX_ACLK_CHART_LATEST "CREATE INDEX IF NOT EXISTS aclk_chart_latest_index_%s ON aclk_chart_latest_%s (unique_id);" - #define INDEX_ACLK_ALERT "CREATE INDEX IF NOT EXISTS aclk_alert_index_%s ON aclk_alert_%s (alert_unique_id);" - enum aclk_database_opcode { ACLK_DATABASE_NOOP = 0, - ACLK_DATABASE_ADD_CHART, - ACLK_DATABASE_ADD_DIMENSION, - ACLK_DATABASE_PUSH_CHART, - ACLK_DATABASE_PUSH_CHART_CONFIG, - ACLK_DATABASE_RESET_CHART, - ACLK_DATABASE_CHART_ACK, - ACLK_DATABASE_UPD_RETENTION, - ACLK_DATABASE_DIM_DELETION, ACLK_DATABASE_ORPHAN_HOST, ACLK_DATABASE_ALARM_HEALTH_LOG, ACLK_DATABASE_CLEANUP, @@ -142,20 +91,11 @@ enum aclk_database_opcode { ACLK_MAX_ENUMERATIONS_DEFINED }; -struct aclk_chart_payload_t { - long sequence_id; - long last_sequence_id; - char *payload; - struct aclk_chart_payload_t *next; -}; - - struct aclk_database_cmd { enum aclk_database_opcode opcode; void *data; void *data_param; int count; - uint64_t param1; struct aclk_completion *completion; }; @@ -172,12 +112,8 @@ struct aclk_database_worker_config { char node_id[GUID_LEN + 1]; char host_guid[GUID_LEN + 1]; char *hostname; // hostname to avoid constant lookups - uint64_t chart_sequence_id; // last chart_sequence_id - time_t chart_timestamp; // last chart timestamp time_t cleanup_after; // Start a cleanup after this timestamp time_t startup_time; // When the sync thread started - time_t rotation_after; - uint64_t batch_id; // batch id to use uint64_t alerts_batch_id; // batch id for alerts to use uint64_t alerts_start_seq_id; // cloud has asked to start streaming from uint64_t alert_sequence_id; // last alert sequence_id @@ -193,15 +129,9 @@ struct aclk_database_worker_config { uv_cond_t cmd_cond; volatile unsigned queue_size; struct aclk_database_cmdqueue cmd_queue; - uint32_t retry_count; - int chart_updates; int alert_updates; - time_t batch_created; int node_info_send; time_t node_collectors_send; - int chart_pending; - int chart_reset_count; - int retention_running; volatile unsigned is_shutting_down; volatile unsigned is_orphan; struct aclk_database_worker_config *next; @@ -216,23 +146,25 @@ static inline RRDHOST *find_host_by_node_id(char *node_id) if (uuid_parse(node_id, node_uuid)) return NULL; - RRDHOST *host = localhost; - while(host) { - if (host->node_id && !(uuid_compare(*host->node_id, node_uuid))) - return host; - host = host->next; + rrd_rdlock(); + RRDHOST *host, *ret = NULL; + rrdhost_foreach_read(host) { + if (host->node_id && !(uuid_compare(*host->node_id, node_uuid))) { + ret = host; + break; + } } - return NULL; + rrd_unlock(); + + return ret; } extern sqlite3 *db_meta; -extern int aclk_database_enq_cmd_noblock(struct aclk_database_worker_config *wc, struct aclk_database_cmd *cmd); -extern void aclk_database_enq_cmd(struct aclk_database_worker_config *wc, struct aclk_database_cmd *cmd); -extern void sql_create_aclk_table(RRDHOST *host, uuid_t *host_uuid, uuid_t *node_id); -int aclk_worker_enq_cmd(char *node_id, struct aclk_database_cmd *cmd); -void aclk_data_rotated(void); +int aclk_database_enq_cmd_noblock(struct aclk_database_worker_config *wc, struct aclk_database_cmd *cmd); +void aclk_database_enq_cmd(struct aclk_database_worker_config *wc, struct aclk_database_cmd *cmd); +void sql_create_aclk_table(RRDHOST *host, uuid_t *host_uuid, uuid_t *node_id); void sql_aclk_sync_init(void); void sql_check_aclk_table_list(struct aclk_database_worker_config *wc); void sql_delete_aclk_table_list(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); diff --git a/database/sqlite/sqlite_aclk_alert.c b/database/sqlite/sqlite_aclk_alert.c index ea1cc9fea..47663a8d1 100644 --- a/database/sqlite/sqlite_aclk_alert.c +++ b/database/sqlite/sqlite_aclk_alert.c @@ -24,7 +24,7 @@ time_t removed_when(uint32_t alarm_id, uint32_t before_unique_id, uint32_t after return 0; } - rc = sqlite3_step(res); + rc = sqlite3_step_monitored(res); if (likely(rc == SQLITE_ROW)) { when = (time_t) sqlite3_column_int64(res, 0); } @@ -36,7 +36,14 @@ time_t removed_when(uint32_t alarm_id, uint32_t before_unique_id, uint32_t after return when; } -#define MAX_REMOVED_PERIOD 900 +void update_filtered(ALARM_ENTRY *ae, uint32_t unique_id, char *uuid_str) { + char sql[ACLK_SYNC_QUERY_SIZE]; + snprintfz(sql, ACLK_SYNC_QUERY_SIZE-1, "UPDATE aclk_alert_%s SET filtered_alert_unique_id = %u where filtered_alert_unique_id = %u", uuid_str, ae->unique_id, unique_id); + sqlite3_exec_monitored(db_meta, sql, 0, 0, NULL); + ae->flags |= HEALTH_ENTRY_FLAG_ACLK_QUEUED; +} + +#define MAX_REMOVED_PERIOD 86400 //decide if some events should be sent or not int should_send_to_cloud(RRDHOST *host, ALARM_ENTRY *ae) { @@ -56,12 +63,13 @@ int should_send_to_cloud(RRDHOST *host, ALARM_ENTRY *ae) uuid_t config_hash_id; RRDCALC_STATUS status; uint32_t unique_id; - + //get the previous sent event of this alarm_id + //base the search on the last filtered event snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "select hl.new_status, hl.config_hash_id, hl.unique_id from health_log_%s hl, aclk_alert_%s aa \ - where hl.unique_id = aa.alert_unique_id \ - and hl.alarm_id = %u and hl.unique_id <> %u \ - order by alarm_event_id desc LIMIT 1;", uuid_str, uuid_str, ae->alarm_id, ae->unique_id); + where hl.unique_id = aa.filtered_alert_unique_id \ + and hl.alarm_id = %u \ + order by alarm_event_id desc LIMIT 1;", uuid_str, uuid_str, ae->alarm_id); rc = sqlite3_prepare_v2(db_meta, sql, -1, &res, 0); if (rc != SQLITE_OK) { @@ -70,7 +78,7 @@ int should_send_to_cloud(RRDHOST *host, ALARM_ENTRY *ae) return send; } - rc = sqlite3_step(res); + rc = sqlite3_step_monitored(res); if (likely(rc == SQLITE_ROW)) { status = (RRDCALC_STATUS) sqlite3_column_int(res, 0); if (sqlite3_column_type(res, 1) != SQLITE_NULL) @@ -93,8 +101,9 @@ int should_send_to_cloud(RRDHOST *host, ALARM_ENTRY *ae) } //same status, same config - if (ae->new_status == RRDCALC_STATUS_CLEAR) { + if (ae->new_status == RRDCALC_STATUS_CLEAR || ae->new_status == RRDCALC_STATUS_UNDEFINED) { send = 0; + update_filtered(ae, unique_id, uuid_str); goto done; } @@ -107,6 +116,7 @@ int should_send_to_cloud(RRDHOST *host, ALARM_ENTRY *ae) goto done; } else { send = 0; + update_filtered(ae, unique_id, uuid_str); goto done; } } @@ -130,6 +140,8 @@ int sql_queue_alarm_to_aclk(RRDHOST *host, ALARM_ENTRY *ae, int skip_filter) return 0; } + CHECK_SQLITE_CONNECTION(db_meta); + if (!skip_filter) { if (!should_send_to_cloud(host, ae)) { return 0; @@ -137,9 +149,6 @@ int sql_queue_alarm_to_aclk(RRDHOST *host, ALARM_ENTRY *ae, int skip_filter) } int rc = 0; - - CHECK_SQLITE_CONNECTION(db_meta); - sqlite3_stmt *res_alert = NULL; char uuid_str[GUID_LEN + 1]; uuid_unparse_lower_fix(&host->host_uuid, uuid_str); @@ -148,8 +157,8 @@ int sql_queue_alarm_to_aclk(RRDHOST *host, ALARM_ENTRY *ae, int skip_filter) buffer_sprintf( sql, - "INSERT INTO aclk_alert_%s (alert_unique_id, date_created) " - "VALUES (@alert_unique_id, unixepoch()) on conflict (alert_unique_id) do nothing; ", + "INSERT INTO aclk_alert_%s (alert_unique_id, date_created, filtered_alert_unique_id) " + "VALUES (@alert_unique_id, unixepoch(), @alert_unique_id) on conflict (alert_unique_id) do nothing; ", uuid_str); rc = sqlite3_prepare_v2(db_meta, buffer_tostring(sql), -1, &res_alert, 0); @@ -220,7 +229,7 @@ void aclk_push_alert_event(struct aclk_database_worker_config *wc, struct aclk_d int rc; if (unlikely(!wc->alert_updates)) { - log_access("ACLK STA [%s (%s)]: Ignoring alert push event, updates have been turned off for this node.", wc->node_id, wc->host ? wc->host->hostname : "N/A"); + log_access("ACLK STA [%s (%s)]: Ignoring alert push event, updates have been turned off for this node.", wc->node_id, wc->host ? rrdhost_hostname(wc->host) : "N/A"); return; } @@ -280,7 +289,7 @@ void aclk_push_alert_event(struct aclk_database_worker_config *wc, struct aclk_d static __thread uint64_t log_first_sequence_id = 0; static __thread uint64_t log_last_sequence_id = 0; - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { struct alarm_log_entry alarm_log; char old_value_string[100 + 1]; char new_value_string[100 + 1]; @@ -300,9 +309,9 @@ void aclk_push_alert_event(struct aclk_database_worker_config *wc, struct aclk_d alarm_log.config_hash = strdupz((char *)uuid_str); alarm_log.utc_offset = wc->host->utc_offset; - alarm_log.timezone = strdupz((char *)wc->host->abbrev_timezone); + alarm_log.timezone = strdupz(rrdhost_abbrev_timezone(wc->host)); alarm_log.exec_path = sqlite3_column_bytes(res, 14) > 0 ? strdupz((char *)sqlite3_column_text(res, 14)) : - strdupz((char *)wc->host->health_default_exec); + strdupz((char *)string2str(wc->host->health_default_exec)); alarm_log.conf_source = strdupz((char *)sqlite3_column_text(res, 16)); char *edit_command = sqlite3_column_bytes(res, 16) > 0 ? @@ -374,7 +383,7 @@ void aclk_push_alert_event(struct aclk_database_worker_config *wc, struct aclk_d log_access( "ACLK RES [%s (%s)]: ALERTS SENT from %" PRIu64 " to %" PRIu64 " batch=%" PRIu64, wc->node_id, - wc->host ? wc->host->hostname : "N/A", + wc->host ? rrdhost_hostname(wc->host) : "N/A", log_first_sequence_id, log_last_sequence_id, wc->alerts_batch_id); @@ -401,8 +410,8 @@ void sql_queue_existing_alerts_to_aclk(RRDHOST *host) BUFFER *sql = buffer_create(1024); buffer_sprintf(sql,"delete from aclk_alert_%s; " \ - "insert into aclk_alert_%s (alert_unique_id, date_created) " \ - "select unique_id alert_unique_id, unixepoch() from health_log_%s " \ + "insert into aclk_alert_%s (alert_unique_id, date_created, filtered_alert_unique_id) " \ + "select unique_id alert_unique_id, unixepoch(), unique_id alert_unique_id from health_log_%s " \ "where new_status <> 0 and new_status <> -2 and config_hash_id is not null and updated_by_id = 0 " \ "order by unique_id asc on conflict (alert_unique_id) do nothing;", uuid_str, uuid_str, uuid_str); @@ -424,9 +433,7 @@ void aclk_send_alarm_health_log(char *node_id) struct aclk_database_worker_config *wc = find_inactive_wc_by_node_id(node_id); if (likely(!wc)) { - rrd_rdlock(); RRDHOST *host = find_host_by_node_id(node_id); - rrd_unlock(); if (likely(host)) wc = (struct aclk_database_worker_config *)host->dbsync_worker; } @@ -460,9 +467,7 @@ void aclk_push_alarm_health_log(struct aclk_database_worker_config *wc, struct a RRDHOST *host = wc->host; if (unlikely(!host)) { - rrd_rdlock(); host = find_host_by_node_id(wc->node_id); - rrd_unlock(); if (unlikely(!host)) { log_access( @@ -500,7 +505,7 @@ void aclk_push_alarm_health_log(struct aclk_database_worker_config *wc, struct a last_timestamp.tv_sec = 0; last_timestamp.tv_usec = 0; - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { first_sequence = sqlite3_column_bytes(res, 0) > 0 ? (uint64_t) sqlite3_column_int64(res, 0) : 0; if (sqlite3_column_bytes(res, 1) > 0) { first_timestamp.tv_sec = sqlite3_column_int64(res, 1); @@ -536,8 +541,6 @@ void aclk_push_alarm_health_log(struct aclk_database_worker_config *wc, struct a freez(claim_id); buffer_free(sql); - - aclk_alert_reloaded = 1; #endif return; @@ -554,7 +557,7 @@ void aclk_send_alarm_configuration(char *config_hash) return; } - log_access("ACLK REQ [%s (%s)]: Request to send alert config %s.", wc->node_id, wc->host ? wc->host->hostname : "N/A", config_hash); + log_access("ACLK REQ [%s (%s)]: Request to send alert config %s.", wc->node_id, wc->host ? rrdhost_hostname(wc->host) : "N/A", config_hash); struct aclk_database_cmd cmd; memset(&cmd, 0, sizeof(cmd)); @@ -603,7 +606,7 @@ int aclk_push_alert_config_event(struct aclk_database_worker_config *wc, struct struct provide_alarm_configuration p_alarm_config; p_alarm_config.cfg_hash = NULL; - if (sqlite3_step(res) == SQLITE_ROW) { + if (sqlite3_step_monitored(res) == SQLITE_ROW) { alarm_config.alarm = sqlite3_column_bytes(res, 0) > 0 ? strdupz((char *)sqlite3_column_text(res, 0)) : NULL; alarm_config.tmpl = sqlite3_column_bytes(res, 1) > 0 ? strdupz((char *)sqlite3_column_text(res, 1)) : NULL; @@ -664,14 +667,14 @@ int aclk_push_alert_config_event(struct aclk_database_worker_config *wc, struct } if (likely(p_alarm_config.cfg_hash)) { - log_access("ACLK RES [%s (%s)]: Sent alert config %s.", wc->node_id, wc->host ? wc->host->hostname : "N/A", config_hash); + log_access("ACLK RES [%s (%s)]: Sent alert config %s.", wc->node_id, wc->host ? rrdhost_hostname(wc->host) : "N/A", config_hash); aclk_send_provide_alarm_cfg(&p_alarm_config); freez((char *) cmd.data_param); freez(p_alarm_config.cfg_hash); destroy_aclk_alarm_configuration(&alarm_config); } else - log_access("ACLK STA [%s (%s)]: Alert config for %s not found.", wc->node_id, wc->host ? wc->host->hostname : "N/A", config_hash); + log_access("ACLK STA [%s (%s)]: Alert config for %s not found.", wc->node_id, wc->host ? rrdhost_hostname(wc->host) : "N/A", config_hash); bind_fail: rc = sqlite3_finalize(res); @@ -697,9 +700,7 @@ void aclk_start_alert_streaming(char *node_id, uint64_t batch_id, uint64_t start return; struct aclk_database_worker_config *wc = NULL; - rrd_rdlock(); RRDHOST *host = find_host_by_node_id(node_id); - rrd_unlock(); if (likely(host)) { wc = (struct aclk_database_worker_config *)host->dbsync_worker ? (struct aclk_database_worker_config *)host->dbsync_worker : @@ -716,7 +717,7 @@ void aclk_start_alert_streaming(char *node_id, uint64_t batch_id, uint64_t start wc = (struct aclk_database_worker_config *)find_inactive_wc_by_node_id(node_id); if (likely(wc)) { - log_access("ACLK REQ [%s (%s)]: ALERTS STREAM from %"PRIu64" batch=%"PRIu64, node_id, wc->host ? wc->host->hostname : "N/A", start_seq_id, batch_id); + log_access("ACLK REQ [%s (%s)]: ALERTS STREAM from %"PRIu64" batch=%"PRIu64, node_id, wc->host ? rrdhost_hostname(wc->host) : "N/A", start_seq_id, batch_id); __sync_synchronize(); wc->alerts_batch_id = batch_id; wc->alerts_start_seq_id = start_seq_id; @@ -736,15 +737,15 @@ void sql_process_queue_removed_alerts_to_aclk(struct aclk_database_worker_config BUFFER *sql = buffer_create(1024); - buffer_sprintf(sql,"insert into aclk_alert_%s (alert_unique_id, date_created) " \ - "select unique_id alert_unique_id, unixepoch() from health_log_%s " \ + buffer_sprintf(sql,"insert into aclk_alert_%s (alert_unique_id, date_created, filtered_alert_unique_id) " \ + "select unique_id alert_unique_id, unixepoch(), unique_id alert_unique_id from health_log_%s " \ "where new_status = -2 and updated_by_id = 0 and unique_id not in " \ "(select alert_unique_id from aclk_alert_%s) order by unique_id asc " \ "on conflict (alert_unique_id) do nothing;", wc->uuid_str, wc->uuid_str, wc->uuid_str); db_execute(buffer_tostring(sql)); - log_access("ACLK STA [%s (%s)]: QUEUED REMOVED ALERTS", wc->node_id, wc->host ? wc->host->hostname : "N/A"); + log_access("ACLK STA [%s (%s)]: QUEUED REMOVED ALERTS", wc->node_id, wc->host ? rrdhost_hostname(wc->host) : "N/A"); buffer_free(sql); @@ -780,17 +781,15 @@ void aclk_process_send_alarm_snapshot(char *node_id, char *claim_id, uint64_t sn return; struct aclk_database_worker_config *wc = NULL; - rrd_rdlock(); RRDHOST *host = find_host_by_node_id(node_id); if (likely(host)) wc = (struct aclk_database_worker_config *)host->dbsync_worker; - rrd_unlock(); if (likely(wc)) { log_access( "IN [%s (%s)]: Request to send alerts snapshot, snapshot_id %" PRIu64 " and ack_sequence_id %" PRIu64, wc->node_id, - wc->host ? wc->host->hostname : "N/A", + wc->host ? rrdhost_hostname(wc->host) : "N/A", snapshot_id, sequence_id); if (wc->alerts_snapshot_id == snapshot_id) @@ -831,13 +830,13 @@ void aclk_mark_alert_cloud_ack(char *uuid_str, uint64_t alerts_ack_sequence_id) #ifdef ENABLE_ACLK void health_alarm_entry2proto_nolock(struct alarm_log_entry *alarm_log, ALARM_ENTRY *ae, RRDHOST *host) { - char *edit_command = ae->source ? health_edit_command_from_source(ae->source) : strdupz("UNKNOWN=0=UNKNOWN"); + char *edit_command = ae->source ? health_edit_command_from_source(ae_source(ae)) : strdupz("UNKNOWN=0=UNKNOWN"); char config_hash_id[GUID_LEN + 1]; uuid_unparse_lower(ae->config_hash_id, config_hash_id); - alarm_log->chart = strdupz((char *)ae->chart); - alarm_log->name = strdupz((char *)ae->name); - alarm_log->family = strdupz((char *)ae->family); + alarm_log->chart = strdupz(ae_chart_name(ae)); + alarm_log->name = strdupz(ae_name(ae)); + alarm_log->family = strdupz(ae_family(ae)); alarm_log->batch_id = 0; alarm_log->sequence_id = 0; @@ -846,9 +845,9 @@ void health_alarm_entry2proto_nolock(struct alarm_log_entry *alarm_log, ALARM_EN alarm_log->config_hash = strdupz((char *)config_hash_id); alarm_log->utc_offset = host->utc_offset; - alarm_log->timezone = strdupz((char *)host->abbrev_timezone); - alarm_log->exec_path = ae->exec ? strdupz((char *)ae->exec) : strdupz((char *)host->health_default_exec); - alarm_log->conf_source = ae->source ? strdupz((char *)ae->source) : strdupz((char *)""); + alarm_log->timezone = strdupz(rrdhost_abbrev_timezone(host)); + alarm_log->exec_path = ae->exec ? strdupz(ae_exec(ae)) : strdupz((char *)string2str(host->health_default_exec)); + alarm_log->conf_source = ae->source ? strdupz(ae_source(ae)) : strdupz((char *)""); alarm_log->command = strdupz((char *)edit_command); @@ -861,31 +860,31 @@ void health_alarm_entry2proto_nolock(struct alarm_log_entry *alarm_log, ALARM_EN alarm_log->last_repeat = (time_t)ae->last_repeat; alarm_log->silenced = - ((ae->flags & HEALTH_ENTRY_FLAG_SILENCED) || (ae->recipient && !strncmp((char *)ae->recipient, "silent", 6))) ? + ((ae->flags & HEALTH_ENTRY_FLAG_SILENCED) || (ae->recipient && !strncmp(ae_recipient(ae), "silent", 6))) ? 1 : 0; - alarm_log->value_string = strdupz(ae->new_value_string); - alarm_log->old_value_string = strdupz(ae->old_value_string); + alarm_log->value_string = strdupz(ae_new_value_string(ae)); + alarm_log->old_value_string = strdupz(ae_old_value_string(ae)); alarm_log->value = (!isnan(ae->new_value)) ? (NETDATA_DOUBLE)ae->new_value : 0; alarm_log->old_value = (!isnan(ae->old_value)) ? (NETDATA_DOUBLE)ae->old_value : 0; alarm_log->updated = (ae->flags & HEALTH_ENTRY_FLAG_UPDATED) ? 1 : 0; - alarm_log->rendered_info = ae->info ? strdupz(ae->info) : strdupz((char *)""); - alarm_log->chart_context = ae->chart_context ? strdupz(ae->chart_context) : strdupz((char *)""); + alarm_log->rendered_info = strdupz(ae_info(ae)); + alarm_log->chart_context = strdupz(ae_chart_context(ae)); freez(edit_command); } #endif #ifdef ENABLE_ACLK -static int have_recent_alarm(RRDHOST *host, uint32_t alarm_id, time_t mark) +static int have_recent_alarm(RRDHOST *host, uint32_t alarm_id, uint32_t mark) { ALARM_ENTRY *ae = host->health_log.alarms; while (ae) { - if (ae->alarm_id == alarm_id && ae->unique_id > mark && + if (ae->alarm_id == alarm_id && ae->unique_id >mark && (ae->new_status != RRDCALC_STATUS_WARNING && ae->new_status != RRDCALC_STATUS_CRITICAL)) return 1; ae = ae->next; @@ -905,7 +904,7 @@ void aclk_push_alert_snapshot_event(struct aclk_database_worker_config *wc, stru UNUSED(cmd); // we perhaps we don't need this for snapshots if (unlikely(!wc->alert_updates)) { - log_access("ACLK STA [%s (%s)]: Ignoring alert snapshot event, updates have been turned off for this node.", wc->node_id, wc->host ? wc->host->hostname : "N/A"); + log_access("ACLK STA [%s (%s)]: Ignoring alert snapshot event, updates have been turned off for this node.", wc->node_id, wc->host ? rrdhost_hostname(wc->host) : "N/A"); return; } @@ -921,7 +920,7 @@ void aclk_push_alert_snapshot_event(struct aclk_database_worker_config *wc, stru if (unlikely(!claim_id)) return; - log_access("ACLK REQ [%s (%s)]: Sending alerts snapshot, snapshot_id %" PRIu64, wc->node_id, wc->host ? wc->host->hostname : "N/A", wc->alerts_snapshot_id); + log_access("ACLK REQ [%s (%s)]: Sending alerts snapshot, snapshot_id %" PRIu64, wc->node_id, wc->host ? rrdhost_hostname(wc->host) : "N/A", wc->alerts_snapshot_id); aclk_mark_alert_cloud_ack(wc->uuid_str, wc->alerts_ack_sequence_id); @@ -1025,11 +1024,11 @@ void sql_aclk_alert_clean_dead_entries(RRDHOST *host) BUFFER *sql = buffer_create(1024); - buffer_sprintf(sql,"delete from aclk_alert_%s where alert_unique_id not in " + buffer_sprintf(sql,"delete from aclk_alert_%s where filtered_alert_unique_id not in " " (select unique_id from health_log_%s); ", uuid_str, uuid_str); char *err_msg = NULL; - int rc = sqlite3_exec(db_meta, buffer_tostring(sql), NULL, NULL, &err_msg); + int rc = sqlite3_exec_monitored(db_meta, buffer_tostring(sql), NULL, NULL, &err_msg); if (rc != SQLITE_OK) { error_report("Failed when trying to clean stale ACLK alert entries from aclk_alert_%s, error message \"%s""", uuid_str, err_msg); @@ -1064,7 +1063,7 @@ int get_proto_alert_status(RRDHOST *host, struct proto_alert_status *proto_alert return 1; } - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { proto_alert_status->pending_min_sequence_id = sqlite3_column_bytes(res, 0) > 0 ? (uint64_t) sqlite3_column_int64(res, 0) : 0; proto_alert_status->pending_max_sequence_id = sqlite3_column_bytes(res, 1) > 0 ? (uint64_t) sqlite3_column_int64(res, 1) : 0; proto_alert_status->last_acked_sequence_id = sqlite3_column_bytes(res, 2) > 0 ? (uint64_t) sqlite3_column_int64(res, 2) : 0; diff --git a/database/sqlite/sqlite_aclk_alert.h b/database/sqlite/sqlite_aclk_alert.h index 0181b4842..88a939e87 100644 --- a/database/sqlite/sqlite_aclk_alert.h +++ b/database/sqlite/sqlite_aclk_alert.h @@ -26,6 +26,6 @@ void sql_process_queue_removed_alerts_to_aclk(struct aclk_database_worker_config void aclk_push_alert_snapshot_event(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); void aclk_process_send_alarm_snapshot(char *node_id, char *claim_id, uint64_t snapshot_id, uint64_t sequence_id); int get_proto_alert_status(RRDHOST *host, struct proto_alert_status *proto_alert_status); -extern int sql_queue_alarm_to_aclk(RRDHOST *host, ALARM_ENTRY *ae, int skip_filter); +int sql_queue_alarm_to_aclk(RRDHOST *host, ALARM_ENTRY *ae, int skip_filter); #endif //NETDATA_SQLITE_ACLK_ALERT_H diff --git a/database/sqlite/sqlite_aclk_chart.c b/database/sqlite/sqlite_aclk_chart.c deleted file mode 100644 index c1db60c49..000000000 --- a/database/sqlite/sqlite_aclk_chart.c +++ /dev/null @@ -1,1311 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "sqlite_functions.h" -#include "sqlite_aclk_chart.h" - -#ifdef ENABLE_ACLK -#include "../../aclk/aclk_charts_api.h" -#include "../../aclk/aclk.h" - -static inline int -sql_queue_chart_payload(struct aclk_database_worker_config *wc, void *data, enum aclk_database_opcode opcode) -{ - int rc; - if (unlikely(!wc)) - return 1; - - struct aclk_database_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = opcode; - cmd.data = data; - rc = aclk_database_enq_cmd_noblock(wc, &cmd); - return rc; -} - -static time_t payload_sent(char *uuid_str, uuid_t *uuid, void *payload, size_t payload_size) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - time_t send_status = 0; - - if (unlikely(!res)) { - char sql[ACLK_SYNC_QUERY_SIZE]; - snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "SELECT acl.date_submitted FROM aclk_chart_latest_%s acl, aclk_chart_payload_%s acp " - "WHERE acl.unique_id = acp.unique_id AND acl.uuid = @uuid AND acp.payload = @payload;", - uuid_str, uuid_str); - rc = prepare_statement(db_meta, sql, &res); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement to check payload data on %s", sql); - return 0; - } - } - - rc = sqlite3_bind_blob(res, 1, uuid, sizeof(*uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_blob(res, 2, payload, payload_size, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - while (sqlite3_step(res) == SQLITE_ROW) { - send_status = (time_t) sqlite3_column_int64(res, 0); - } - -bind_fail: - if (unlikely(sqlite3_reset(res) != SQLITE_OK)) - error_report("Failed to reset statement in check payload, rc = %d", rc); - return send_status; -} - -static int aclk_add_chart_payload( - struct aclk_database_worker_config *wc, - uuid_t *uuid, - char *claim_id, - ACLK_PAYLOAD_TYPE payload_type, - void *payload, - size_t payload_size, - time_t *send_status, - int check_sent) -{ - static __thread sqlite3_stmt *res_chart = NULL; - int rc; - time_t date_submitted; - - if (unlikely(!payload)) - return 0; - - if (check_sent) { - date_submitted = payload_sent(wc->uuid_str, uuid, payload, payload_size); - if (send_status) - *send_status = date_submitted; - if (date_submitted) - return 0; - } - - if (unlikely(!res_chart)) { - char sql[ACLK_SYNC_QUERY_SIZE]; - snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, - "INSERT INTO aclk_chart_payload_%s (unique_id, uuid, claim_id, date_created, type, payload) " \ - "VALUES (@unique_id, @uuid, @claim_id, unixepoch(), @type, @payload);", wc->uuid_str); - rc = prepare_statement(db_meta, sql, &res_chart); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement to store chart payload data"); - return 1; - } - } - - uuid_t unique_uuid; - uuid_generate(unique_uuid); - - uuid_t claim_uuid; - if (uuid_parse(claim_id, claim_uuid)) - return 1; - - rc = sqlite3_bind_blob(res_chart, 1, &unique_uuid, sizeof(unique_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_blob(res_chart, 2, uuid, sizeof(*uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_blob(res_chart, 3, &claim_uuid, sizeof(claim_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res_chart, 4, payload_type); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_blob(res_chart, 5, payload, payload_size, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = execute_insert(res_chart); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed store chart payload event, rc = %d", rc); - else { - wc->chart_payload_count++; - time_t now = now_realtime_sec(); - if (wc->rotation_after > now && wc->rotation_after < now + ACLK_DATABASE_ROTATION_DELAY) - wc->rotation_after = now + ACLK_DATABASE_ROTATION_DELAY; - } - -bind_fail: - if (unlikely(sqlite3_reset(res_chart) != SQLITE_OK)) - error_report("Failed to reset statement in store chart payload, rc = %d", rc); - return (rc != SQLITE_DONE); -} - -int aclk_add_chart_event(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd) -{ - int rc = 0; - CHECK_SQLITE_CONNECTION(db_meta); - - char *claim_id = get_agent_claimid(); - - RRDSET *st = cmd.data; - - if (likely(claim_id)) { - struct chart_instance_updated chart_payload; - memset(&chart_payload, 0, sizeof(chart_payload)); - chart_payload.config_hash = get_str_from_uuid(&st->state->hash_id); - chart_payload.update_every = st->update_every; - chart_payload.memory_mode = st->rrd_memory_mode; - chart_payload.name = (char *)st->name; - chart_payload.node_id = wc->node_id; - chart_payload.claim_id = claim_id; - chart_payload.id = strdupz(st->id); - - chart_payload.chart_labels = rrdlabels_create(); - rrdlabels_copy(chart_payload.chart_labels, st->state->chart_labels); - - size_t size; - char *payload = generate_chart_instance_updated(&size, &chart_payload); - if (likely(payload)) - rc = aclk_add_chart_payload(wc, st->chart_uuid, claim_id, ACLK_PAYLOAD_CHART, (void *) payload, size, NULL, 1); - freez(payload); - chart_instance_updated_destroy(&chart_payload); - } - return rc; -} - -static inline int aclk_upd_dimension_event(struct aclk_database_worker_config *wc, char *claim_id, uuid_t *dim_uuid, - const char *dim_id, const char *dim_name, const char *chart_type_id, time_t first_time, time_t last_time, - time_t *send_status) -{ - int rc = 0; - size_t size; - - if (unlikely(!dim_uuid || !dim_id || !dim_name || !chart_type_id)) - return 0; - - struct chart_dimension_updated dim_payload; - memset(&dim_payload, 0, sizeof(dim_payload)); - -#ifdef NETDATA_INTERNAL_CHECKS - if (!first_time) - info("Host %s (node %s) deleting dimension id=[%s] name=[%s] chart=[%s]", - wc->host_guid, wc->node_id, dim_id, dim_name, chart_type_id); - if (last_time) - info("Host %s (node %s) stopped collecting dimension id=[%s] name=[%s] chart=[%s] %ld seconds ago at %ld", - wc->host_guid, wc->node_id, dim_id, dim_name, chart_type_id, now_realtime_sec() - last_time, last_time); -#endif - - dim_payload.node_id = wc->node_id; - dim_payload.claim_id = claim_id; - dim_payload.name = dim_name; - dim_payload.id = dim_id; - dim_payload.chart_id = chart_type_id; - dim_payload.created_at.tv_sec = first_time; - dim_payload.last_timestamp.tv_sec = last_time; - char *payload = generate_chart_dimension_updated(&size, &dim_payload); - if (likely(payload)) - rc = aclk_add_chart_payload(wc, dim_uuid, claim_id, ACLK_PAYLOAD_DIMENSION, (void *)payload, size, send_status, 1); - freez(payload); - return rc; -} - -void aclk_process_dimension_deletion(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd) -{ - int rc = 0; - sqlite3_stmt *res = NULL; - - if (!aclk_connected) - return; - - if (unlikely(!db_meta)) - return; - - uuid_t host_id; - if (uuid_parse(wc->host_guid, host_id)) - return; - - char *claim_id = get_agent_claimid(); - if (!claim_id) - return; - - rc = sqlite3_prepare_v2( - db_meta, - "DELETE FROM dimension_delete where host_id = @host_id " - "RETURNING dimension_id, dimension_name, chart_type_id, dim_id LIMIT 10;", - -1, - &res, - 0); - - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement when trying to delete dimension deletes"); - freez(claim_id); - return; - } - - rc = sqlite3_bind_blob(res, 1, &host_id, sizeof(host_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - unsigned count = 0; - while (sqlite3_step(res) == SQLITE_ROW) { - (void) aclk_upd_dimension_event( - wc, - claim_id, - (uuid_t *)sqlite3_column_text(res, 3), - (const char *)sqlite3_column_text(res, 0), - (const char *)sqlite3_column_text(res, 1), - (const char *)sqlite3_column_text(res, 2), - 0, - 0, - NULL); - count++; - } - - if (count) { - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = ACLK_DATABASE_DIM_DELETION; - if (aclk_database_enq_cmd_noblock(wc, &cmd)) - info("Failed to queue a dimension deletion message"); - } - -bind_fail: - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize statement when adding dimension deletion events, rc = %d", rc); - freez(claim_id); - return; -} - -int aclk_add_dimension_event(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd) -{ - int rc = 1; - CHECK_SQLITE_CONNECTION(db_meta); - - struct aclk_chart_dimension_data *aclk_cd_data = cmd.data; - - char *claim_id = get_agent_claimid(); - if (!claim_id) - goto cleanup; - - rc = aclk_add_chart_payload(wc, &aclk_cd_data->uuid, claim_id, ACLK_PAYLOAD_DIMENSION, - (void *) aclk_cd_data->payload, aclk_cd_data->payload_size, NULL, aclk_cd_data->check_payload); - - freez(claim_id); -cleanup: - freez(aclk_cd_data->payload); - freez(aclk_cd_data); - return rc; -} - -void aclk_send_chart_event(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd) -{ - int rc; - - wc->chart_pending = 0; - if (unlikely(!wc->chart_updates)) { - log_access( - "ACLK STA [%s (%s)]: Ignoring chart push event, updates have been turned off for this node.", - wc->node_id, - wc->host ? wc->host->hostname : "N/A"); - return; - } - - char *claim_id = get_agent_claimid(); - if (unlikely(!claim_id)) - return; - - uuid_t claim_uuid; - if (uuid_parse(claim_id, claim_uuid)) - return; - - int limit = cmd.count > 0 ? cmd.count : 1; - - uint64_t first_sequence; - uint64_t last_sequence; - time_t last_timestamp = 0; - - char sql[ACLK_SYNC_QUERY_SIZE]; - static __thread sqlite3_stmt *res = NULL; - - if (unlikely(!res)) { - snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1,"SELECT ac.sequence_id, acp.payload, ac.date_created, ac.type, ac.uuid " \ - "FROM aclk_chart_%s ac, aclk_chart_payload_%s acp " \ - "WHERE ac.date_submitted IS NULL AND ac.unique_id = acp.unique_id AND ac.update_count > 0 " \ - "AND acp.claim_id = @claim_id ORDER BY ac.sequence_id ASC LIMIT %d;", wc->uuid_str, wc->uuid_str, limit); - rc = prepare_statement(db_meta, sql, &res); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement when trying to send a chart update via ACLK"); - freez(claim_id); - return; - } - } - - rc = sqlite3_bind_blob(res, 1, claim_uuid, sizeof(claim_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - char **payload_list = callocz(limit + 1, sizeof(char *)); - size_t *payload_list_size = callocz(limit + 1, sizeof(size_t)); - size_t *payload_list_max_size = callocz(limit + 1, sizeof(size_t)); - struct aclk_message_position *position_list = callocz(limit + 1, sizeof(*position_list)); - int *is_dim = callocz(limit + 1, sizeof(*is_dim)); - - int loop = cmd.param1; - - uint64_t start_sequence_id = wc->chart_sequence_id; - - while (loop > 0) { - uint64_t previous_sequence_id = wc->chart_sequence_id; - int count = 0; - first_sequence = 0; - last_sequence = 0; - while (count < limit && sqlite3_step(res) == SQLITE_ROW) { - size_t payload_size = sqlite3_column_bytes(res, 1); - if (payload_list_max_size[count] < payload_size) { - freez(payload_list[count]); - payload_list_max_size[count] = payload_size; - payload_list[count] = mallocz(payload_size); - } - payload_list_size[count] = payload_size; - memcpy(payload_list[count], sqlite3_column_blob(res, 1), payload_size); - position_list[count].sequence_id = (uint64_t)sqlite3_column_int64(res, 0); - position_list[count].previous_sequence_id = previous_sequence_id; - position_list[count].seq_id_creation_time.tv_sec = sqlite3_column_int64(res, 2); - position_list[count].seq_id_creation_time.tv_usec = 0; - if (!first_sequence) - first_sequence = position_list[count].sequence_id; - last_sequence = position_list[count].sequence_id; - last_timestamp = position_list[count].seq_id_creation_time.tv_sec; - previous_sequence_id = last_sequence; - is_dim[count] = sqlite3_column_int(res, 3) > 0; - count++; - if (wc->chart_payload_count) - wc->chart_payload_count--; - } - freez(payload_list[count]); - payload_list_max_size[count] = 0; - payload_list[count] = NULL; - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement when pushing chart events, rc = %d", rc); - - if (likely(first_sequence)) { - - db_lock(); - snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "UPDATE aclk_chart_%s SET status = NULL, date_submitted=unixepoch() " - "WHERE date_submitted IS NULL AND sequence_id BETWEEN %" PRIu64 " AND %" PRIu64 ";", - wc->uuid_str, first_sequence, last_sequence); - db_execute(sql); - snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "INSERT OR REPLACE INTO aclk_chart_latest_%s (uuid, unique_id, date_submitted) " - " SELECT uuid, unique_id, date_submitted FROM aclk_chart_%s s " - " WHERE date_submitted IS NOT NULL AND sequence_id BETWEEN %" PRIu64 " AND %" PRIu64 - " ;", - wc->uuid_str, wc->uuid_str, first_sequence, last_sequence); - db_execute(sql); - db_unlock(); - - aclk_chart_inst_and_dim_update(payload_list, payload_list_size, is_dim, position_list, wc->batch_id); - log_access( - "ACLK RES [%s (%s)]: CHARTS SENT from %" PRIu64 " to %" PRIu64 " batch=%" PRIu64, - wc->node_id, - wc->hostname ? wc->hostname : "N/A", - first_sequence, - last_sequence, - wc->batch_id); - wc->chart_sequence_id = last_sequence; - wc->chart_timestamp = last_timestamp; - } else - break; - --loop; - } - - if (start_sequence_id != wc->chart_sequence_id) { - time_t now = now_realtime_sec(); - if (wc->rotation_after > now && wc->rotation_after < now + ACLK_DATABASE_ROTATION_DELAY) - wc->rotation_after = now + ACLK_DATABASE_ROTATION_DELAY; - } else { - wc->chart_payload_count = sql_get_pending_count(wc); - if (!wc->chart_payload_count) - log_access( - "ACLK STA [%s (%s)]: Sync of charts and dimensions done in %ld seconds.", - wc->node_id, - wc->hostname ? wc->hostname : "N/A", - now_realtime_sec() - wc->startup_time); - } - - for (int i = 0; i <= limit; ++i) - freez(payload_list[i]); - - freez(payload_list); - freez(payload_list_size); - freez(payload_list_max_size); - freez(position_list); - freez(is_dim); - -bind_fail: - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement when pushing chart events, rc = %d", rc); - - freez(claim_id); - return; -} - -// Push one chart config to the cloud -int aclk_send_chart_config(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd) -{ - UNUSED(wc); - - CHECK_SQLITE_CONNECTION(db_meta); - - sqlite3_stmt *res = NULL; - int rc = 0; - - char *hash_id = (char *) cmd.data_param; - - uuid_t hash_uuid; - rc = uuid_parse(hash_id, hash_uuid); - - if (unlikely(rc)) { - freez((char *) cmd.data_param); - return 1; - } - - BUFFER *sql = buffer_create(1024); - buffer_sprintf(sql, "SELECT type, family, context, title, priority, plugin, module, unit, chart_type " \ - "FROM chart_hash WHERE hash_id = @hash_id;"); - - rc = sqlite3_prepare_v2(db_meta, buffer_tostring(sql), -1, &res, 0); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement when trying to fetch a chart hash configuration"); - goto fail; - } - - rc = sqlite3_bind_blob(res, 1, &hash_uuid , sizeof(hash_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - struct chart_config_updated chart_config; - chart_config.config_hash = NULL; - - while (sqlite3_step(res) == SQLITE_ROW) { - chart_config.type = strdupz((char *)sqlite3_column_text(res, 0)); - chart_config.family = strdupz((char *)sqlite3_column_text(res, 1)); - chart_config.context = strdupz((char *)sqlite3_column_text(res, 2)); - chart_config.title = strdupz((char *)sqlite3_column_text(res, 3)); - chart_config.priority = sqlite3_column_int64(res, 4); - chart_config.plugin = strdupz((char *)sqlite3_column_text(res, 5)); - chart_config.module = sqlite3_column_bytes(res, 6) > 0 ? strdupz((char *)sqlite3_column_text(res, 6)) : NULL; - chart_config.chart_type = (RRDSET_TYPE) sqlite3_column_int(res,8); - chart_config.units = strdupz((char *)sqlite3_column_text(res, 7)); - chart_config.config_hash = strdupz(hash_id); - } - - if (likely(chart_config.config_hash)) { - log_access( - "ACLK REQ [%s (%s)]: Sending chart config for %s.", - wc->node_id, - wc->host ? wc->host->hostname : "N/A", - hash_id); - aclk_chart_config_updated(&chart_config, 1); - destroy_chart_config_updated(&chart_config); - } else - log_access( - "ACLK STA [%s (%s)]: Chart config for %s not found.", - wc->node_id, - wc->host ? wc->host->hostname : "N/A", - hash_id); - -bind_fail: - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement when pushing chart config hash, rc = %d", rc); -fail: - freez((char *)cmd.data_param); - buffer_free(sql); - return rc; -} - -void aclk_receive_chart_ack(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd) -{ - int rc; - sqlite3_stmt *res = NULL; - - char sql[ACLK_SYNC_QUERY_SIZE]; - - snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1,"UPDATE aclk_chart_%s SET date_updated=unixepoch() WHERE sequence_id <= @sequence_id " - "AND date_submitted IS NOT NULL AND date_updated IS NULL;", wc->uuid_str); - - rc = sqlite3_prepare_v2(db_meta, sql, -1, &res, 0); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement to ack chart sequence ids"); - return; - } - - rc = sqlite3_bind_int64(res, 1, (uint64_t) cmd.param1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = execute_insert(res); - if (rc != SQLITE_DONE) - error_report("Failed to ACK sequence id, rc = %d", rc); - else - log_access( - "ACLK STA [%s (%s)]: CHARTS ACKNOWLEDGED IN THE DATABASE UP TO %" PRIu64, - wc->node_id, - wc->host ? wc->host->hostname : "N/A", - cmd.param1); - -bind_fail: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize statement to ACK older sequence ids, rc = %d", rc); - return; -} - -void aclk_receive_chart_reset(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd) -{ - BUFFER *sql = buffer_create(1024); - buffer_sprintf( - sql, - "UPDATE aclk_chart_%s SET status = NULL, date_submitted = NULL WHERE sequence_id >= %" PRIu64 ";", - wc->uuid_str, - cmd.param1); - db_execute(buffer_tostring(sql)); - if (cmd.param1 == 1) { - buffer_flush(sql); - log_access("ACLK REQ [%s (%s)]: Received chart full resync.", wc->node_id, wc->hostname ? wc->hostname: "N/A"); - buffer_sprintf(sql, "DELETE FROM aclk_chart_payload_%s; DELETE FROM aclk_chart_%s; " \ - "DELETE FROM aclk_chart_latest_%s;", wc->uuid_str, wc->uuid_str, wc->uuid_str); - db_lock(); - - db_execute("BEGIN TRANSACTION;"); - db_execute(buffer_tostring(sql)); - db_execute("COMMIT TRANSACTION;"); - - db_unlock(); - wc->chart_sequence_id = 0; - wc->chart_timestamp = 0; - wc->chart_payload_count = 0; - - RRDHOST *host = wc->host; - if (likely(host)) { - rrdhost_rdlock(host); - RRDSET *st; - rrdset_foreach_read(st, host) - { - rrdset_rdlock(st); - rrdset_flag_clear(st, RRDSET_FLAG_ACLK); - RRDDIM *rd; - rrddim_foreach_read(rd, st) - { - rrddim_flag_clear(rd, RRDDIM_FLAG_ACLK); - rd->aclk_live_status = (rd->aclk_live_status == 0); - } - rrdset_unlock(st); - } - rrdhost_unlock(host); - } else - error_report("ACLK synchronization thread for %s is not linked to HOST", wc->host_guid); - } else { - log_access( - "ACLK STA [%s (%s)]: RESTARTING CHART SYNC FROM SEQUENCE %" PRIu64, - wc->node_id, - wc->hostname ? wc->hostname : "N/A", - cmd.param1); - wc->chart_payload_count = sql_get_pending_count(wc); - sql_get_last_chart_sequence(wc); - } - buffer_free(sql); - wc->chart_updates = 1; - return; -} - -// -// Functions called directly from ACLK threads and will queue commands -// -void aclk_get_chart_config(char **hash_id) -{ - struct aclk_database_worker_config *wc = (struct aclk_database_worker_config *)localhost->dbsync_worker; - - if (unlikely(!wc || !hash_id)) - return; - - struct aclk_database_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = ACLK_DATABASE_PUSH_CHART_CONFIG; - for (int i = 0; hash_id[i]; ++i) { - // TODO: Verify that we have a valid hash_id - log_access( - "ACLK REQ [%s (%s)]: Request %d for chart config with hash %s received.", - wc->node_id, - wc->host ? wc->host->hostname : "N/A", - i, - hash_id[i]); - cmd.data_param = (void *)strdupz(hash_id[i]); - aclk_database_enq_cmd(wc, &cmd); - } - return; -} - -// Send a command to a node_id -// Need to discover the thread that will handle the request -// if thread not in active hosts, then try to find in the queue -static void aclk_submit_param_command(char *node_id, enum aclk_database_opcode aclk_command, uint64_t param) -{ - if (unlikely(!node_id)) - return; - - struct aclk_database_worker_config *wc = NULL; - struct aclk_database_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = aclk_command; - cmd.param1 = param; - - rrd_rdlock(); - RRDHOST *host = find_host_by_node_id(node_id); - if (likely(host)) - wc = (struct aclk_database_worker_config *)host->dbsync_worker; - rrd_unlock(); - if (wc) - aclk_database_enq_cmd(wc, &cmd); - else { - if (aclk_worker_enq_cmd(node_id, &cmd)) - log_access("ACLK STA [%s (N/A)]: ACLK synchronization thread is not active.", node_id); - } - return; -} - -void aclk_ack_chart_sequence_id(char *node_id, uint64_t last_sequence_id) -{ - if (unlikely(!node_id)) - return; - - char *hostname = get_hostname_by_node_id(node_id); - log_access("ACLK REQ [%s (%s)]: CHARTS ACKNOWLEDGED upto %" PRIu64, node_id, hostname ? hostname : "N/A", - last_sequence_id); - freez(hostname); - aclk_submit_param_command(node_id, ACLK_DATABASE_CHART_ACK, last_sequence_id); - return; -} - -// Start streaming charts / dimensions for node_id -void aclk_start_streaming(char *node_id, uint64_t sequence_id, time_t created_at, uint64_t batch_id) -{ - UNUSED(created_at); - if (unlikely(!node_id)) - return; - - uuid_t node_uuid; - if (uuid_parse(node_id, node_uuid)) { - log_access("ACLK REQ [%s (N/A)]: CHARTS STREAM ignored, invalid node id", node_id); - return; - } - - struct aclk_database_worker_config *wc = find_inactive_wc_by_node_id(node_id); - rrd_rdlock(); - RRDHOST *host = localhost; - while(host) { - if (wc || (host->node_id && !(uuid_compare(*host->node_id, node_uuid)))) { - rrd_unlock(); - if (!wc) - wc = (struct aclk_database_worker_config *)host->dbsync_worker ? - (struct aclk_database_worker_config *)host->dbsync_worker : - (struct aclk_database_worker_config *)find_inactive_wc_by_node_id(node_id); - if (likely(wc)) { - wc->chart_reset_count++; - __sync_synchronize(); - wc->chart_updates = 0; - wc->batch_id = batch_id; - __sync_synchronize(); - wc->batch_created = now_realtime_sec(); - log_access( - "ACLK REQ [%s (%s)]: CHARTS STREAM from %"PRIu64" (LOCAL %"PRIu64") t=%ld resets=%d" , - wc->node_id, - wc->hostname ? wc->hostname : "N/A", - sequence_id + 1, - wc->chart_sequence_id, - wc->chart_timestamp, - wc->chart_reset_count); - if (sequence_id > wc->chart_sequence_id || wc->chart_reset_count > 10) { - log_access( - "ACLK RES [%s (%s)]: CHARTS FULL RESYNC REQUEST " - "remote_seq=%" PRIu64 " local_seq=%" PRIu64 " resets=%d ", - wc->node_id, - wc->hostname ? wc->hostname : "N/A", - sequence_id, - wc->chart_sequence_id, - wc->chart_reset_count); - - chart_reset_t chart_reset; - chart_reset.claim_id = get_agent_claimid(); - if (chart_reset.claim_id) { - chart_reset.node_id = node_id; - chart_reset.reason = SEQ_ID_NOT_EXISTS; - aclk_chart_reset(chart_reset); - freez(chart_reset.claim_id); - wc->chart_reset_count = -1; - } - } else { - struct aclk_database_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - // TODO: handle timestamp - if (sequence_id < wc->chart_sequence_id || - !sequence_id) { // || created_at != wc->chart_timestamp) { - log_access( - "ACLK REQ [%s (%s)]: CHART RESET from %" PRIu64 " t=%ld batch=%" PRIu64, - wc->node_id, - wc->hostname ? wc->hostname : "N/A", - sequence_id + 1, - wc->chart_timestamp, - wc->batch_id); - cmd.opcode = ACLK_DATABASE_RESET_CHART; - cmd.param1 = sequence_id + 1; - cmd.completion = NULL; - aclk_database_enq_cmd(wc, &cmd); - } else { - wc->chart_reset_count = 0; - wc->chart_updates = 1; - } - } - } else { - log_access("ACLK STA [%s (%s)]: ACLK synchronization thread is not active.", node_id, wc->hostname ? wc->hostname : "N/A"); - } - return; - } - host = host->next; - } - rrd_unlock(); - return; -} - -#define SQL_SELECT_HOST_MEMORY_MODE "SELECT memory_mode FROM chart WHERE host_id = @host_id LIMIT 1;" - -static RRD_MEMORY_MODE sql_get_host_memory_mode(uuid_t *host_id) -{ - int rc; - - RRD_MEMORY_MODE memory_mode = RRD_MEMORY_MODE_RAM; - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_meta, SQL_SELECT_HOST_MEMORY_MODE, -1, &res, 0); - - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to read host memory mode"); - return memory_mode; - } - - rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host parameter to fetch host memory mode"); - goto failed; - } - - while (sqlite3_step(res) == SQLITE_ROW) { - memory_mode = (RRD_MEMORY_MODE)sqlite3_column_int(res, 0); - } - -failed: - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when reading host memory mode"); - return memory_mode; -} - -#define SELECT_HOST_DIMENSION_LIST \ - "SELECT d.dim_id, c.update_every, c.type||'.'||c.id, d.id, d.name FROM chart c, dimension d " \ - "WHERE d.chart_id = c.chart_id AND c.host_id = @host_id ORDER BY c.update_every ASC;" - -#define SELECT_HOST_CHART_LIST \ - "SELECT distinct h.host_id, c.update_every, c.type||'.'||c.id FROM chart c, host h " \ - "WHERE c.host_id = h.host_id AND c.host_id = @host_id ORDER BY c.update_every ASC;" - -void aclk_update_retention(struct aclk_database_worker_config *wc) -{ - int rc; - - if (!aclk_connected) - return; - - if (wc->host && rrdhost_flag_check(wc->host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS)) { - internal_error(true, "Skipping aclk_update_retention for host %s because context streaming is enabled", wc->host->hostname); - return; - } - - char *claim_id = get_agent_claimid(); - if (unlikely(!claim_id)) - return; - - sqlite3_stmt *res = NULL; - RRD_MEMORY_MODE memory_mode; - - uuid_t host_uuid; - rc = uuid_parse(wc->host_guid, host_uuid); - if (unlikely(rc)) { - freez(claim_id); - return; - } - - if (wc->host) - memory_mode = wc->host->rrd_memory_mode; - else - memory_mode = sql_get_host_memory_mode(&host_uuid); - - if (memory_mode == RRD_MEMORY_MODE_DBENGINE) - rc = sqlite3_prepare_v2(db_meta, SELECT_HOST_DIMENSION_LIST, -1, &res, 0); - else - rc = sqlite3_prepare_v2(db_meta, SELECT_HOST_CHART_LIST, -1, &res, 0); - - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch host dimensions"); - freez(claim_id); - return; - } - - rc = sqlite3_bind_blob(res, 1, &host_uuid, sizeof(host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host parameter to fetch host dimensions"); - goto failed; - } - - time_t start_time = LONG_MAX; - time_t first_entry_t; - time_t last_entry_t; - uint32_t update_every = 0; - uint32_t dimension_update_count = 0; - uint32_t total_checked = 0; - uint32_t total_deleted= 0; - uint32_t total_stopped= 0; - time_t send_status; - - struct retention_updated rotate_data; - - memset(&rotate_data, 0, sizeof(rotate_data)); - - int max_intervals = 32; - - rotate_data.interval_duration_count = 0; - rotate_data.interval_durations = callocz(max_intervals, sizeof(*rotate_data.interval_durations)); - - now_realtime_timeval(&rotate_data.rotation_timestamp); - rotate_data.memory_mode = memory_mode; - rotate_data.claim_id = claim_id; - rotate_data.node_id = strdupz(wc->node_id); - - time_t now = now_realtime_sec(); - while (sqlite3_step(res) == SQLITE_ROW && dimension_update_count < ACLK_MAX_DIMENSION_CLEANUP) { - if (unlikely(netdata_exit)) - break; - if (!update_every || update_every != (uint32_t)sqlite3_column_int(res, 1)) { - if (update_every) { - debug(D_ACLK_SYNC, "Update %s for %u oldest time = %ld", wc->host_guid, update_every, start_time); - if (start_time == LONG_MAX) - rotate_data.interval_durations[rotate_data.interval_duration_count].retention = 0; - else - rotate_data.interval_durations[rotate_data.interval_duration_count].retention = - rotate_data.rotation_timestamp.tv_sec - start_time; - rotate_data.interval_duration_count++; - } - update_every = (uint32_t)sqlite3_column_int(res, 1); - rotate_data.interval_durations[rotate_data.interval_duration_count].update_every = update_every; - start_time = LONG_MAX; - } -#ifdef ENABLE_DBENGINE - if (memory_mode == RRD_MEMORY_MODE_DBENGINE) - rc = - rrdeng_metric_latest_time_by_uuid((uuid_t *)sqlite3_column_blob(res, 0), &first_entry_t, &last_entry_t, 0); - else -#endif - { - if (wc->host) { - RRDSET *st = NULL; - rc = (st = rrdset_find(wc->host, (const char *)sqlite3_column_text(res, 2))) ? 0 : 1; - if (!rc) { - first_entry_t = rrdset_first_entry_t(st); - last_entry_t = rrdset_last_entry_t(st); - } - } else { - rc = 0; - first_entry_t = rotate_data.rotation_timestamp.tv_sec; - } - } - - if (likely(!rc && first_entry_t)) - start_time = MIN(start_time, first_entry_t); - - if (memory_mode == RRD_MEMORY_MODE_DBENGINE && wc->chart_updates && (dimension_update_count < ACLK_MAX_DIMENSION_CLEANUP)) { - int live = ((now - last_entry_t) < (RRDSET_MINIMUM_DIM_LIVE_MULTIPLIER * update_every)); - if (rc) { - first_entry_t = 0; - last_entry_t = 0; - live = 0; - } - if (!wc->host || !first_entry_t) { - if (!first_entry_t) { - delete_dimension_uuid((uuid_t *)sqlite3_column_blob(res, 0)); - total_deleted++; - dimension_update_count++; - } - else { - (void)aclk_upd_dimension_event( - wc, - claim_id, - (uuid_t *)sqlite3_column_blob(res, 0), - (const char *)(const char *)sqlite3_column_text(res, 3), - (const char *)(const char *)sqlite3_column_text(res, 4), - (const char *)(const char *)sqlite3_column_text(res, 2), - first_entry_t, - live ? 0 : last_entry_t, - &send_status); - - if (!send_status) { - if (last_entry_t) - total_stopped++; - dimension_update_count++; - } - } - } - } - total_checked++; - } - if (update_every) { - debug(D_ACLK_SYNC, "Update %s for %u oldest time = %ld", wc->host_guid, update_every, start_time); - if (start_time == LONG_MAX) - rotate_data.interval_durations[rotate_data.interval_duration_count].retention = 0; - else - rotate_data.interval_durations[rotate_data.interval_duration_count].retention = - rotate_data.rotation_timestamp.tv_sec - start_time; - rotate_data.interval_duration_count++; - } - - if (dimension_update_count < ACLK_MAX_DIMENSION_CLEANUP && !netdata_exit) - log_access("ACLK STA [%s (%s)]: UPDATES %d RETENTION MESSAGE SENT. CHECKED %u DIMENSIONS. %u DELETED, %u STOPPED COLLECTING", - wc->node_id, wc->hostname ? wc->hostname : "N/A", wc->chart_updates, total_checked, total_deleted, total_stopped); - else - log_access("ACLK STA [%s (%s)]: UPDATES %d RETENTION MESSAGE NOT SENT. CHECKED %u DIMENSIONS. %u DELETED, %u STOPPED COLLECTING", - wc->node_id, wc->hostname ? wc->hostname : "N/A", wc->chart_updates, total_checked, total_deleted, total_stopped); - -#ifdef NETDATA_INTERNAL_CHECKS - info("Retention update for %s (chart updates = %d)", wc->host_guid, wc->chart_updates); - for (int i = 0; i < rotate_data.interval_duration_count; ++i) - info( - "Update for host %s (node %s) for %u Retention = %u", - wc->host_guid, - wc->node_id, - rotate_data.interval_durations[i].update_every, - rotate_data.interval_durations[i].retention); -#endif - if (dimension_update_count < ACLK_MAX_DIMENSION_CLEANUP && !netdata_exit) - aclk_retention_updated(&rotate_data); - freez(rotate_data.node_id); - freez(rotate_data.interval_durations); - -failed: - freez(claim_id); - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when reading host dimensions"); - return; -} - -uint32_t sql_get_pending_count(struct aclk_database_worker_config *wc) -{ - char sql[ACLK_SYNC_QUERY_SIZE]; - static __thread sqlite3_stmt *res = NULL; - - snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "SELECT count(1) FROM aclk_chart_%s ac WHERE ac.date_submitted IS NULL;", wc->uuid_str); - - int rc; - uint32_t chart_payload_count = 0; - if (unlikely(!res)) { - rc = prepare_statement(db_meta, sql, &res); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement to count pending messages"); - return 0; - } - } - while (sqlite3_step(res) == SQLITE_ROW) - chart_payload_count = (uint32_t) sqlite3_column_int(res, 0); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement when fetching pending messages, rc = %d", rc); - - return chart_payload_count; -} - -void sql_get_last_chart_sequence(struct aclk_database_worker_config *wc) -{ - char sql[ACLK_SYNC_QUERY_SIZE]; - - snprintfz(sql,ACLK_SYNC_QUERY_SIZE-1, "SELECT ac.sequence_id, ac.date_created FROM aclk_chart_%s ac " \ - "WHERE ac.date_submitted IS NOT NULL ORDER BY ac.sequence_id DESC LIMIT 1;", wc->uuid_str); - - int rc; - sqlite3_stmt *res = NULL; - rc = sqlite3_prepare_v2(db_meta, sql, -1, &res, 0); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement to find last chart sequence id"); - return; - } - - wc->chart_sequence_id = 0; - wc->chart_timestamp = 0; - while (sqlite3_step(res) == SQLITE_ROW) { - wc->chart_sequence_id = (uint64_t)sqlite3_column_int64(res, 0); - wc->chart_timestamp = (time_t)sqlite3_column_int64(res, 1); - } - - debug(D_ACLK_SYNC, "Node %s reports last sequence_id=%" PRIu64, wc->node_id, wc->chart_sequence_id); - - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement when fetching chart sequence info, rc = %d", rc); - - return; -} - -void queue_dimension_to_aclk(RRDDIM *rd, time_t last_updated) -{ - RRDHOST *host = rd->rrdset->rrdhost; - if (likely(rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS))) - return; - - int live = !last_updated; - - if (likely(rd->aclk_live_status == live)) - return; - - time_t created_at = rd->tiers[0]->query_ops.oldest_time(rd->tiers[0]->db_metric_handle); - - if (unlikely(!created_at && rd->updated)) - created_at = rd->last_collected_time.tv_sec; - - rd->aclk_live_status = live; - - struct aclk_database_worker_config *wc = rd->rrdset->rrdhost->dbsync_worker; - if (unlikely(!wc)) - return; - - char *claim_id = get_agent_claimid(); - if (unlikely(!claim_id)) - return; - - struct chart_dimension_updated dim_payload; - memset(&dim_payload, 0, sizeof(dim_payload)); - dim_payload.node_id = wc->node_id; - dim_payload.claim_id = claim_id; - dim_payload.name = rd->name; - dim_payload.id = rd->id; - dim_payload.chart_id = rd->rrdset->id; - dim_payload.created_at.tv_sec = created_at; - dim_payload.last_timestamp.tv_sec = last_updated; - - size_t size = 0; - char *payload = generate_chart_dimension_updated(&size, &dim_payload); - - freez(claim_id); - if (unlikely(!payload)) - return; - - struct aclk_chart_dimension_data *aclk_cd_data = mallocz(sizeof(*aclk_cd_data)); - uuid_copy(aclk_cd_data->uuid, rd->metric_uuid); - aclk_cd_data->payload = payload; - aclk_cd_data->payload_size = size; - aclk_cd_data->check_payload = 1; - - struct aclk_database_cmd cmd; - memset(&cmd, 0, sizeof(cmd)); - - cmd.opcode = ACLK_DATABASE_ADD_DIMENSION; - cmd.data = aclk_cd_data; - int rc = aclk_database_enq_cmd_noblock(wc, &cmd); - - if (unlikely(rc)) { - freez(aclk_cd_data->payload); - freez(aclk_cd_data); - rd->aclk_live_status = !live; - } - return; -} - -void aclk_send_dimension_update(RRDDIM *rd) -{ - char *claim_id = get_agent_claimid(); - if (unlikely(!claim_id)) - return; - - time_t first_entry_t = rrddim_first_entry_t(rd); - time_t last_entry_t = rrddim_last_entry_t(rd); - - time_t now = now_realtime_sec(); - int live = ((now - rd->last_collected_time.tv_sec) < (RRDSET_MINIMUM_DIM_LIVE_MULTIPLIER * rd->update_every)); - - if (!live || rd->aclk_live_status != live || !first_entry_t) { - (void)aclk_upd_dimension_event( - rd->rrdset->rrdhost->dbsync_worker, - claim_id, - &rd->metric_uuid, - rd->id, - rd->name, - rd->rrdset->id, - first_entry_t, - live ? 0 : last_entry_t, - NULL); - - if (!first_entry_t) - debug( - D_ACLK_SYNC, - "%s: Update dimension chart=%s dim=%s live=%d (%ld, %ld)", - rd->rrdset->rrdhost->hostname, - rd->rrdset->name, - rd->name, - live, - first_entry_t, - last_entry_t); - else - debug( - D_ACLK_SYNC, - "%s: Update dimension chart=%s dim=%s live=%d (%ld, %ld) collected %ld seconds ago", - rd->rrdset->rrdhost->hostname, - rd->rrdset->name, - rd->name, - live, - first_entry_t, - last_entry_t, - now - last_entry_t); - rd->aclk_live_status = live; - } - - freez(claim_id); - return; -} - -#define SQL_SEQ_NULL(result, n) sqlite3_column_type(result, n) == SQLITE_NULL ? 0 : sqlite3_column_int64(result, n) - -struct aclk_chart_sync_stats *aclk_get_chart_sync_stats(RRDHOST *host) -{ - struct aclk_chart_sync_stats *aclk_statistics = NULL; - - struct aclk_database_worker_config *wc = NULL; - wc = (struct aclk_database_worker_config *)host->dbsync_worker; - if (!wc) - return NULL; - - aclk_statistics = callocz(1, sizeof(struct aclk_chart_sync_stats)); - - aclk_statistics->updates = wc->chart_updates; - aclk_statistics->batch_id = wc->batch_id; - - char host_uuid_fixed[GUID_LEN + 1]; - - strncpy(host_uuid_fixed, host->machine_guid, GUID_LEN); - host_uuid_fixed[GUID_LEN] = 0; - - host_uuid_fixed[8] = '_'; - host_uuid_fixed[13] = '_'; - host_uuid_fixed[18] = '_'; - host_uuid_fixed[23] = '_'; - - sqlite3_stmt *res = NULL; - BUFFER *sql = buffer_create(1024); - buffer_sprintf(sql, "SELECT min(sequence_id), max(sequence_id), 0 FROM aclk_chart_%s;", host_uuid_fixed); - buffer_sprintf(sql, "SELECT min(sequence_id), max(sequence_id), 0 FROM aclk_chart_%s WHERE date_submitted IS NULL;", host_uuid_fixed); - buffer_sprintf(sql, "SELECT min(sequence_id), max(sequence_id), 0 FROM aclk_chart_%s WHERE date_submitted IS NOT NULL;", host_uuid_fixed); - buffer_sprintf(sql, "SELECT min(sequence_id), max(sequence_id), 0 FROM aclk_chart_%s WHERE date_updated IS NOT NULL;", host_uuid_fixed); - buffer_sprintf(sql, "SELECT max(date_created), max(date_submitted), max(date_updated), 0 FROM aclk_chart_%s;", host_uuid_fixed); - - int rc = sqlite3_prepare_v2(db_meta, buffer_tostring(sql), -1, &res, 0); - if (rc != SQLITE_OK) { - buffer_free(sql); - freez(aclk_statistics); - return NULL; - } - - rc = sqlite3_step(res); - if (rc == SQLITE_ROW) { - aclk_statistics->min_seqid = SQL_SEQ_NULL(res, 0); - aclk_statistics->max_seqid = SQL_SEQ_NULL(res, 1); - } - - rc = sqlite3_step(res); - if (rc == SQLITE_ROW) { - aclk_statistics->min_seqid_pend = SQL_SEQ_NULL(res, 0); - aclk_statistics->max_seqid_pend = SQL_SEQ_NULL(res, 1); - } - - rc = sqlite3_step(res); - if (rc == SQLITE_ROW) { - aclk_statistics->min_seqid_sent = SQL_SEQ_NULL(res, 0); - aclk_statistics->max_seqid_sent = SQL_SEQ_NULL(res, 1); - } - - rc = sqlite3_step(res); - if (rc == SQLITE_ROW) { - aclk_statistics->min_seqid_ack = SQL_SEQ_NULL(res, 0); - aclk_statistics->max_seqid_ack = SQL_SEQ_NULL(res, 1); - } - - rc = sqlite3_step(res); - if (rc == SQLITE_ROW) { - aclk_statistics->min_seqid_ack = SQL_SEQ_NULL(res, 0); - aclk_statistics->max_seqid_ack = SQL_SEQ_NULL(res, 1); - } - - rc = sqlite3_step(res); - if (rc == SQLITE_ROW) { - aclk_statistics->max_date_created = (time_t) SQL_SEQ_NULL(res, 0); - aclk_statistics->max_date_submitted = (time_t) SQL_SEQ_NULL(res, 1); - aclk_statistics->max_date_ack = (time_t) SQL_SEQ_NULL(res, 2); - } - - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize statement when fetching aclk sync statistics, rc = %d", rc); - - buffer_free(sql); - return aclk_statistics; -} - -void sql_check_chart_liveness(RRDSET *st) { - RRDDIM *rd; - - if (unlikely(st->state->is_ar_chart)) - return; - - rrdset_rdlock(st); - - if (unlikely(!rrdset_flag_check(st, RRDSET_FLAG_ACLK))) { - rrdset_unlock(st); - return; - } - - if (unlikely(!rrdset_flag_check(st, RRDSET_FLAG_ACLK))) { - if (likely(st->dimensions && st->counter_done && !queue_chart_to_aclk(st))) { - debug(D_ACLK_SYNC,"Check chart liveness [%s] submit chart definition", st->name); - rrdset_flag_set(st, RRDSET_FLAG_ACLK); - } - } - else - debug(D_ACLK_SYNC,"Check chart liveness [%s] chart definition already submitted", st->name); - time_t mark = now_realtime_sec(); - - debug(D_ACLK_SYNC,"Check chart liveness [%s] scanning dimensions", st->name); - rrddim_foreach_read(rd, st) { - if (!rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) - queue_dimension_to_aclk(rd, calc_dimension_liveness(rd, mark)); - } - rrdset_unlock(st); -} - -// ST is read locked -int queue_chart_to_aclk(RRDSET *st) -{ - RRDHOST *host = st->rrdhost; - - if (likely(rrdhost_flag_check(host, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS))) - return 0; - - return sql_queue_chart_payload((struct aclk_database_worker_config *) st->rrdhost->dbsync_worker, - st, ACLK_DATABASE_ADD_CHART); -} - -#endif //ENABLE_ACLK diff --git a/database/sqlite/sqlite_aclk_chart.h b/database/sqlite/sqlite_aclk_chart.h deleted file mode 100644 index 84325bf6c..000000000 --- a/database/sqlite/sqlite_aclk_chart.h +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_SQLITE_ACLK_CHART_H -#define NETDATA_SQLITE_ACLK_CHART_H - - -typedef enum payload_type { - ACLK_PAYLOAD_CHART, - ACLK_PAYLOAD_DIMENSION, - ACLK_PAYLOAD_DIMENSION_ROTATED -} ACLK_PAYLOAD_TYPE; - -extern sqlite3 *db_meta; - -#ifndef RRDSET_MINIMUM_DIM_LIVE_MULTIPLIER -#define RRDSET_MINIMUM_DIM_LIVE_MULTIPLIER (3) -#endif - -#ifndef RRDSET_MINIMUM_DIM_OFFLINE_MULTIPLIER -#define RRDSET_MINIMUM_DIM_OFFLINE_MULTIPLIER (30) -#endif - -#ifndef ACLK_MAX_DIMENSION_CLEANUP -#define ACLK_MAX_DIMENSION_CLEANUP (500) -#endif - -struct aclk_chart_dimension_data { - uuid_t uuid; - char *payload; - size_t payload_size; - uint8_t check_payload; -}; - -struct aclk_chart_sync_stats { - int updates; - uint64_t batch_id; - uint64_t min_seqid; - uint64_t max_seqid; - uint64_t min_seqid_pend; - uint64_t max_seqid_pend; - uint64_t min_seqid_sent; - uint64_t max_seqid_sent; - uint64_t min_seqid_ack; - uint64_t max_seqid_ack; - time_t max_date_created; - time_t max_date_submitted; - time_t max_date_ack; -}; - -extern int queue_chart_to_aclk(RRDSET *st); -extern void queue_dimension_to_aclk(RRDDIM *rd, time_t last_updated); -extern void sql_create_aclk_table(RRDHOST *host, uuid_t *host_uuid, uuid_t *node_id); -int aclk_add_chart_event(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); -int aclk_add_dimension_event(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); -int aclk_send_chart_config(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); -void aclk_ack_chart_sequence_id(char *node_id, uint64_t last_sequence_id); -void aclk_get_chart_config(char **hash_id_list); -void aclk_send_chart_event(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); -void aclk_start_streaming(char *node_id, uint64_t seq_id, time_t created_at, uint64_t batch_id); -void sql_chart_deduplicate(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); -void sql_check_rotation_state(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); -void sql_get_last_chart_sequence(struct aclk_database_worker_config *wc); -void aclk_receive_chart_reset(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); -void aclk_receive_chart_ack(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); -void aclk_process_dimension_deletion(struct aclk_database_worker_config *wc, struct aclk_database_cmd cmd); -uint32_t sql_get_pending_count(struct aclk_database_worker_config *wc); -void aclk_send_dimension_update(RRDDIM *rd); -struct aclk_chart_sync_stats *aclk_get_chart_sync_stats(RRDHOST *host); -void sql_check_chart_liveness(RRDSET *st); -void aclk_update_retention(struct aclk_database_worker_config *wc); -#endif //NETDATA_SQLITE_ACLK_CHART_H diff --git a/database/sqlite/sqlite_aclk_node.c b/database/sqlite/sqlite_aclk_node.c index 3d11f83aa..afe774997 100644 --- a/database/sqlite/sqlite_aclk_node.c +++ b/database/sqlite/sqlite_aclk_node.c @@ -3,27 +3,25 @@ #include "sqlite_functions.h" #include "sqlite_aclk_node.h" -#ifdef ENABLE_ACLK -#include "../../aclk/aclk_charts_api.h" -#endif +#include "../../aclk/aclk_contexts_api.h" +#include "../../aclk/aclk_capas.h" #ifdef ENABLE_ACLK DICTIONARY *collectors_from_charts(RRDHOST *host, DICTIONARY *dict) { RRDSET *st; char name[500]; - rrdhost_rdlock(host); rrdset_foreach_read(st, host) { if (rrdset_is_available_for_viewers(st)) { struct collector_info col = { - .plugin = st->plugin_name ? st->plugin_name : "", - .module = st->module_name ? st->module_name : "" + .plugin = rrdset_plugin_name(st), + .module = rrdset_module_name(st) }; snprintfz(name, 499, "%s:%s", col.plugin, col.module); dictionary_set(dict, name, &col, sizeof(struct collector_info)); } } - rrdhost_unlock(host); + rrdset_foreach_done(st); return dict; } @@ -36,7 +34,7 @@ void sql_build_node_collectors(struct aclk_database_worker_config *wc) return; struct update_node_collectors upd_node_collectors; - DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED); upd_node_collectors.node_id = wc->node_id; upd_node_collectors.claim_id = get_agent_claimid(); @@ -47,7 +45,7 @@ void sql_build_node_collectors(struct aclk_database_worker_config *wc) dictionary_destroy(dict); freez(upd_node_collectors.claim_id); - log_access("ACLK RES [%s (%s)]: NODE COLLECTORS SENT", wc->node_id, wc->host->hostname); + log_access("ACLK RES [%s (%s)]: NODE COLLECTORS SENT", wc->node_id, rrdhost_hostname(wc->host)); #else UNUSED(wc); #endif @@ -74,14 +72,7 @@ void sql_build_node_info(struct aclk_database_worker_config *wc, struct aclk_dat node_info.ml_info.ml_capable = ml_capable(localhost); node_info.ml_info.ml_enabled = ml_enabled(wc->host); - struct capability instance_caps[] = { - { .name = "proto", .version = 1, .enabled = 1 }, - { .name = "ml", .version = ml_capable(localhost), .enabled = ml_enabled(wc->host) }, - { .name = "mc", .version = enable_metric_correlations ? metric_correlations_version : 0, .enabled = enable_metric_correlations }, - { .name = "ctx", .version = 1, .enabled = rrdcontext_enabled}, - { .name = NULL, .version = 0, .enabled = 0 } - }; - node_info.node_instance_capabilities = instance_caps; + node_info.node_instance_capabilities = aclk_get_node_instance_capas(wc->host); now_realtime_timeval(&node_info.updated_at); @@ -89,13 +80,12 @@ void sql_build_node_info(struct aclk_database_worker_config *wc, struct aclk_dat char *host_version = NULL; if (host != localhost) { netdata_mutex_lock(&host->receiver_lock); - host_version = - strdupz(host->receiver && host->receiver->program_version ? host->receiver->program_version : "unknown"); + host_version = strdupz(host->receiver && host->receiver->program_version ? host->receiver->program_version : "unknown"); netdata_mutex_unlock(&host->receiver_lock); } - node_info.data.name = host->hostname; - node_info.data.os = (char *) host->os; + node_info.data.name = rrdhost_hostname(host); + node_info.data.os = rrdhost_os(host); node_info.data.os_name = host->system_info->host_os_name; node_info.data.os_version = host->system_info->host_os_version; node_info.data.kernel_name = host->system_info->kernel_name; @@ -106,8 +96,8 @@ void sql_build_node_info(struct aclk_database_worker_config *wc, struct aclk_dat node_info.data.memory = host->system_info->host_ram_total ? host->system_info->host_ram_total : "0"; node_info.data.disk_space = host->system_info->host_disk_space ? host->system_info->host_disk_space : "0"; node_info.data.version = host_version ? host_version : VERSION; - node_info.data.release_channel = (char *) get_release_channel(); - node_info.data.timezone = (char *) host->abbrev_timezone; + node_info.data.release_channel = get_release_channel(); + node_info.data.timezone = rrdhost_abbrev_timezone(host); node_info.data.virtualization_type = host->system_info->virtualization ? host->system_info->virtualization : "unknown"; node_info.data.container_type = host->system_info->container ? host->system_info->container : "unknown"; node_info.data.custom_info = config_get(CONFIG_SECTION_WEB, "custom dashboard_info.js", ""); @@ -123,13 +113,14 @@ void sql_build_node_info(struct aclk_database_worker_config *wc, struct aclk_dat node_info.data.ml_info.ml_capable = host->system_info->ml_capable; node_info.data.ml_info.ml_enabled = host->system_info->ml_enabled; - node_info.data.host_labels_ptr = host->host_labels; + node_info.data.host_labels_ptr = host->rrdlabels; aclk_update_node_info(&node_info); - log_access("ACLK RES [%s (%s)]: NODE INFO SENT for guid [%s] (%s)", wc->node_id, wc->host->hostname, wc->host_guid, wc->host == localhost ? "parent" : "child"); + log_access("ACLK RES [%s (%s)]: NODE INFO SENT for guid [%s] (%s)", wc->node_id, rrdhost_hostname(wc->host), wc->host_guid, wc->host == localhost ? "parent" : "child"); rrd_unlock(); freez(node_info.claim_id); + freez(node_info.node_instance_capabilities); freez(host_version); wc->node_collectors_send = now_realtime_sec(); diff --git a/database/sqlite/sqlite_context.c b/database/sqlite/sqlite_context.c index 901ab0031..9c7a61c6e 100644 --- a/database/sqlite/sqlite_context.c +++ b/database/sqlite/sqlite_context.c @@ -21,7 +21,6 @@ const char *database_context_cleanup[] = { }; sqlite3 *db_context_meta = NULL; - /* * Initialize the SQLite database * Return 0 on success @@ -125,22 +124,24 @@ void sql_close_context_database(void) // Fetching data // #define CTX_GET_CHART_LIST "SELECT c.chart_id, c.type||'.'||c.id, c.name, c.context, c.title, c.unit, c.priority, " \ - "c.update_every, c.chart_type, c.family FROM meta.chart c WHERE c.host_id = @host_id; " + "c.update_every, c.chart_type, c.family FROM meta.chart c WHERE c.host_id = @host_id and c.chart_id is not null; " void ctx_get_chart_list(uuid_t *host_uuid, void (*dict_cb)(SQL_CHART_DATA *, void *), void *data) { int rc; - sqlite3_stmt *res = NULL; + static __thread sqlite3_stmt *res = NULL; if (unlikely(!host_uuid)) { internal_error(true, "Requesting context chart list without host_id"); return; } - rc = sqlite3_prepare_v2(db_context_meta, CTX_GET_CHART_LIST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch chart list"); - return; + if (unlikely(!res)) { + rc = prepare_statement(db_context_meta, CTX_GET_CHART_LIST, &res); + if (rc != SQLITE_OK) { + error_report("Failed to prepare statement to fetch chart list"); + return; + } } rc = sqlite3_bind_blob(res, 1, host_uuid, sizeof(*host_uuid), SQLITE_STATIC); @@ -150,7 +151,7 @@ void ctx_get_chart_list(uuid_t *host_uuid, void (*dict_cb)(SQL_CHART_DATA *, voi } SQL_CHART_DATA chart_data = { 0 }; - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { uuid_copy(chart_data.chart_id, *((uuid_t *)sqlite3_column_blob(res, 0))); chart_data.id = (char *) sqlite3_column_text(res, 1); chart_data.name = (char *) sqlite3_column_text(res, 2); @@ -165,22 +166,25 @@ void ctx_get_chart_list(uuid_t *host_uuid, void (*dict_cb)(SQL_CHART_DATA *, voi } skip_load: - rc = sqlite3_finalize(res); + rc = sqlite3_reset(res); if (rc != SQLITE_OK) - error_report("Failed to finalize statement that fetches chart label data, rc = %d", rc); + error_report("Failed to reset statement that fetches chart label data, rc = %d", rc); } // Dimension list -#define CTX_GET_DIMENSION_LIST "SELECT d.dim_id, d.id, d.name FROM meta.dimension d WHERE d.chart_id = @id;" +#define CTX_GET_DIMENSION_LIST "SELECT d.dim_id, d.id, d.name, CASE WHEN INSTR(d.options,\"hidden\") > 0 THEN 1 ELSE 0 END " \ + "FROM meta.dimension d WHERE d.chart_id = @id and d.dim_id is not null ORDER BY d.rowid ASC;" void ctx_get_dimension_list(uuid_t *chart_uuid, void (*dict_cb)(SQL_DIMENSION_DATA *, void *), void *data) { int rc; - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_context_meta, CTX_GET_DIMENSION_LIST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch chart dimension data"); - return; + static __thread sqlite3_stmt *res = NULL; + + if (unlikely(!res)) { + rc = prepare_statement(db_context_meta, CTX_GET_DIMENSION_LIST, &res); + if (rc != SQLITE_OK) { + error_report("Failed to prepare statement to fetch chart dimension data"); + return; + } } rc = sqlite3_bind_blob(res, 1, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); @@ -191,17 +195,18 @@ void ctx_get_dimension_list(uuid_t *chart_uuid, void (*dict_cb)(SQL_DIMENSION_DA SQL_DIMENSION_DATA dimension_data; - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { uuid_copy(dimension_data.dim_id, *((uuid_t *)sqlite3_column_blob(res, 0))); dimension_data.id = (char *) sqlite3_column_text(res, 1); dimension_data.name = (char *) sqlite3_column_text(res, 2); + dimension_data.hidden = sqlite3_column_int(res, 3); dict_cb(&dimension_data, data); } failed: - rc = sqlite3_finalize(res); + rc = sqlite3_reset(res); if (rc != SQLITE_OK) - error_report("Failed to finalize statement that fetches the chart dimension list, rc = %d", rc); + error_report("Failed to reset statement that fetches the chart dimension list, rc = %d", rc); } // LABEL LIST @@ -209,12 +214,14 @@ failed: void ctx_get_label_list(uuid_t *chart_uuid, void (*dict_cb)(SQL_CLABEL_DATA *, void *), void *data) { int rc; - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_context_meta, CTX_GET_LABEL_LIST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch chart lanbels"); - return; + static __thread sqlite3_stmt *res = NULL; + + if (unlikely(!res)) { + rc = prepare_statement(db_context_meta, CTX_GET_LABEL_LIST, &res); + if (rc != SQLITE_OK) { + error_report("Failed to prepare statement to fetch chart labels"); + return; + } } rc = sqlite3_bind_blob(res, 1, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); @@ -225,7 +232,7 @@ void ctx_get_label_list(uuid_t *chart_uuid, void (*dict_cb)(SQL_CLABEL_DATA *, v SQL_CLABEL_DATA label_data; - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { label_data.label_key = (char *) sqlite3_column_text(res, 0); label_data.label_value = (char *) sqlite3_column_text(res, 1); label_data.label_source = sqlite3_column_int(res, 2); @@ -233,9 +240,9 @@ void ctx_get_label_list(uuid_t *chart_uuid, void (*dict_cb)(SQL_CLABEL_DATA *, v } failed: - rc = sqlite3_finalize(res); + rc = sqlite3_reset(res); if (rc != SQLITE_OK) - error_report("Failed to finalize statement that fetches chart label data, rc = %d", rc); + error_report("Failed to reset statement that fetches chart label data, rc = %d", rc); return; } @@ -250,12 +257,14 @@ void ctx_get_context_list(uuid_t *host_uuid, void (*dict_cb)(VERSIONED_CONTEXT_D return; int rc; - sqlite3_stmt *res = NULL; - - rc = sqlite3_prepare_v2(db_context_meta, CTX_GET_CONTEXT_LIST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch stored context list"); - return; + static __thread sqlite3_stmt *res = NULL; + + if (unlikely(!res)) { + rc = prepare_statement(db_context_meta, CTX_GET_CONTEXT_LIST, &res); + if (rc != SQLITE_OK) { + error_report("Failed to prepare statement to fetch stored context list"); + return; + } } VERSIONED_CONTEXT_DATA context_data = {0}; @@ -267,7 +276,7 @@ void ctx_get_context_list(uuid_t *host_uuid, void (*dict_cb)(VERSIONED_CONTEXT_D goto failed; } - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { context_data.id = (char *) sqlite3_column_text(res, 0); context_data.version = sqlite3_column_int64(res, 1); context_data.title = (char *) sqlite3_column_text(res, 2); @@ -282,9 +291,9 @@ void ctx_get_context_list(uuid_t *host_uuid, void (*dict_cb)(VERSIONED_CONTEXT_D } failed: - rc = sqlite3_finalize(res); + rc = sqlite3_reset(res); if (rc != SQLITE_OK) - error_report("Failed to finalize statement that fetches stored context versioned data, rc = %d", rc); + error_report("Failed to reset statement that fetches stored context versioned data, rc = %d", rc); } @@ -437,6 +446,13 @@ skip_delete: return (rc_stored != SQLITE_DONE); } +int sql_context_cache_stats(int op) +{ + int count, dummy; + sqlite3_db_status(db_context_meta, op, &count, &dummy, 0); + return count; +} + // // TESTING FUNCTIONS // diff --git a/database/sqlite/sqlite_context.h b/database/sqlite/sqlite_context.h index 12937fffd..2e52b9bf8 100644 --- a/database/sqlite/sqlite_context.h +++ b/database/sqlite/sqlite_context.h @@ -6,6 +6,7 @@ #include "daemon/common.h" #include "sqlite3.h" +int sql_context_cache_stats(int op); typedef struct ctx_chart { uuid_t chart_id; const char *id; @@ -23,6 +24,7 @@ typedef struct ctx_dimension { uuid_t dim_id; char *id; char *name; + bool hidden; } SQL_DIMENSION_DATA; typedef struct ctx_label { @@ -50,19 +52,19 @@ typedef struct versioned_context_data { } VERSIONED_CONTEXT_DATA; -extern void ctx_get_context_list(uuid_t *host_uuid, void (*dict_cb)(VERSIONED_CONTEXT_DATA *, void *), void *data); +void ctx_get_context_list(uuid_t *host_uuid, void (*dict_cb)(VERSIONED_CONTEXT_DATA *, void *), void *data); -extern void ctx_get_chart_list(uuid_t *host_uuid, void (*dict_cb)(SQL_CHART_DATA *, void *), void *data); -extern void ctx_get_label_list(uuid_t *chart_uuid, void (*dict_cb)(SQL_CLABEL_DATA *, void *), void *data); -extern void ctx_get_dimension_list(uuid_t *chart_uuid, void (*dict_cb)(SQL_DIMENSION_DATA *, void *), void *data); +void ctx_get_chart_list(uuid_t *host_uuid, void (*dict_cb)(SQL_CHART_DATA *, void *), void *data); +void ctx_get_label_list(uuid_t *chart_uuid, void (*dict_cb)(SQL_CLABEL_DATA *, void *), void *data); +void ctx_get_dimension_list(uuid_t *chart_uuid, void (*dict_cb)(SQL_DIMENSION_DATA *, void *), void *data); -extern int ctx_store_context(uuid_t *host_uuid, VERSIONED_CONTEXT_DATA *context_data); +int ctx_store_context(uuid_t *host_uuid, VERSIONED_CONTEXT_DATA *context_data); #define ctx_update_context(host_uuid, context_data) ctx_store_context(host_uuid, context_data) -extern int ctx_delete_context(uuid_t *host_id, VERSIONED_CONTEXT_DATA *context_data); +int ctx_delete_context(uuid_t *host_id, VERSIONED_CONTEXT_DATA *context_data); -extern int sql_init_context_database(int memory); -extern void sql_close_context_database(void); -extern int ctx_unittest(void); +int sql_init_context_database(int memory); +void sql_close_context_database(void); +int ctx_unittest(void); #endif //NETDATA_SQLITE_CONTEXT_H diff --git a/database/sqlite/sqlite_db_migration.c b/database/sqlite/sqlite_db_migration.c index bd4743364..8b1d01594 100644 --- a/database/sqlite/sqlite_db_migration.c +++ b/database/sqlite/sqlite_db_migration.c @@ -21,7 +21,7 @@ static int table_exists_in_database(const char *table) snprintf(sql, 127, "select 1 from sqlite_schema where type = 'table' and name = '%s';", table); - int rc = sqlite3_exec(db_meta, sql, return_int_cb, (void *) &exists, &err_msg); + int rc = sqlite3_exec_monitored(db_meta, sql, return_int_cb, (void *) &exists, &err_msg); if (rc != SQLITE_OK) { info("Error checking table existence; %s", err_msg); sqlite3_free(err_msg); @@ -39,7 +39,7 @@ static int column_exists_in_table(const char *table, const char *column) snprintf(sql, 127, "SELECT 1 FROM pragma_table_info('%s') where name = '%s';", table, column); - int rc = sqlite3_exec(db_meta, sql, return_int_cb, (void *) &exists, &err_msg); + int rc = sqlite3_exec_monitored(db_meta, sql, return_int_cb, (void *) &exists, &err_msg); if (rc != SQLITE_OK) { info("Error checking column existence; %s", err_msg); sqlite3_free(err_msg); @@ -64,6 +64,22 @@ const char *database_migrate_v2_v3[] = { NULL }; +const char *database_migrate_v4_v5[] = { + "DROP TABLE IF EXISTS chart_active;", + "DROP TABLE IF EXISTS dimension_active;", + "DROP TABLE IF EXISTS chart_hash;", + "DROP TABLE IF EXISTS chart_hash_map;", + "DROP VIEW IF EXISTS v_chart_hash;", + NULL +}; + +const char *database_migrate_v5_v6[] = { + "DROP TRIGGER IF EXISTS tr_dim_del;", + "DROP TABLE IF EXISTS dimension_delete;", + NULL +}; + + static int do_migration_v1_v2(sqlite3 *database, const char *name) { UNUSED(name); @@ -100,11 +116,11 @@ static int do_migration_v3_v4(sqlite3 *database, const char *name) return 1; } - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { char *table = strdupz((char *) sqlite3_column_text(res, 0)); if (!column_exists_in_table(table, "chart_context")) { snprintfz(sql, 255, "ALTER TABLE %s ADD chart_context text", table); - sqlite3_exec(database, sql, 0, 0, NULL); + sqlite3_exec_monitored(database, sql, 0, 0, NULL); } freez(table); } @@ -116,6 +132,57 @@ static int do_migration_v3_v4(sqlite3 *database, const char *name) return 0; } +static int do_migration_v4_v5(sqlite3 *database, const char *name) +{ + UNUSED(name); + info("Running \"%s\" database migration", name); + + return init_database_batch(database, DB_CHECK_NONE, 0, &database_migrate_v4_v5[0]); +} + +static int do_migration_v5_v6(sqlite3 *database, const char *name) +{ + UNUSED(name); + info("Running \"%s\" database migration", name); + + return init_database_batch(database, DB_CHECK_NONE, 0, &database_migrate_v5_v6[0]); +} + +static int do_migration_v6_v7(sqlite3 *database, const char *name) +{ + UNUSED(name); + info("Running \"%s\" database migration", name); + + char sql[256]; + + int rc; + sqlite3_stmt *res = NULL; + snprintfz(sql, 255, "SELECT name FROM sqlite_schema WHERE type ='table' AND name LIKE 'aclk_alert_%%';"); + rc = sqlite3_prepare_v2(database, sql, -1, &res, 0); + if (rc != SQLITE_OK) { + error_report("Failed to prepare statement to alter aclk_alert tables"); + return 1; + } + + while (sqlite3_step_monitored(res) == SQLITE_ROW) { + char *table = strdupz((char *) sqlite3_column_text(res, 0)); + if (!column_exists_in_table(table, "filtered_alert_unique_id")) { + snprintfz(sql, 255, "ALTER TABLE %s ADD filtered_alert_unique_id", table); + sqlite3_exec_monitored(database, sql, 0, 0, NULL); + snprintfz(sql, 255, "UPDATE %s SET filtered_alert_unique_id = alert_unique_id", table); + sqlite3_exec_monitored(database, sql, 0, 0, NULL); + } + freez(table); + } + + rc = sqlite3_finalize(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to finalize statement when altering aclk_alert tables, rc = %d", rc); + + return 0; +} + + static int do_migration_noop(sqlite3 *database, const char *name) { UNUSED(database); @@ -135,7 +202,7 @@ static int migrate_database(sqlite3 *database, int target_version, char *db_name int user_version = 0; char *err_msg = NULL; - int rc = sqlite3_exec(database, "PRAGMA user_version;", return_int_cb, (void *) &user_version, &err_msg); + int rc = sqlite3_exec_monitored(database, "PRAGMA user_version;", return_int_cb, (void *) &user_version, &err_msg); if (rc != SQLITE_OK) { info("Error checking the %s database version; %s", db_name, err_msg); sqlite3_free(err_msg); @@ -163,6 +230,9 @@ DATABASE_FUNC_MIGRATION_LIST migration_action[] = { {.name = "v1 to v2", .func = do_migration_v1_v2}, {.name = "v2 to v3", .func = do_migration_v2_v3}, {.name = "v3 to v4", .func = do_migration_v3_v4}, + {.name = "v4 to v5", .func = do_migration_v4_v5}, + {.name = "v5 to v6", .func = do_migration_v5_v6}, + {.name = "v6 to v7", .func = do_migration_v6_v7}, // the terminator of this array {.name = NULL, .func = NULL} }; diff --git a/database/sqlite/sqlite_functions.c b/database/sqlite/sqlite_functions.c index f46450afa..eeb3c3822 100644 --- a/database/sqlite/sqlite_functions.c +++ b/database/sqlite/sqlite_functions.c @@ -3,7 +3,7 @@ #include "sqlite_functions.h" #include "sqlite_db_migration.h" -#define DB_METADATA_VERSION 4 +#define DB_METADATA_VERSION 7 const char *database_config[] = { "CREATE TABLE IF NOT EXISTS host(host_id BLOB PRIMARY KEY, hostname TEXT NOT NULL, " @@ -21,14 +21,10 @@ const char *database_config[] = { "CREATE TABLE IF NOT EXISTS dimension(dim_id blob PRIMARY KEY, chart_id blob, id text, name text, " "multiplier int, divisor int , algorithm int, options text);", - "DROP TABLE IF EXISTS chart_active;", - "DROP TABLE IF EXISTS dimension_active;", - - "CREATE TABLE IF NOT EXISTS chart_active(chart_id blob PRIMARY KEY, date_created int);", - "CREATE TABLE IF NOT EXISTS dimension_active(dim_id blob primary key, date_created int);", "CREATE TABLE IF NOT EXISTS metadata_migration(filename text, file_size, date_created int);", "CREATE INDEX IF NOT EXISTS ind_d1 on dimension (chart_id, id, name);", "CREATE INDEX IF NOT EXISTS ind_c1 on chart (host_id, id, type, name);", + "CREATE INDEX IF NOT EXISTS ind_c2 on chart (host_id, context);", "CREATE TABLE IF NOT EXISTS chart_label(chart_id blob, source_type int, label_key text, " "label_value text, date_created int, PRIMARY KEY (chart_id, label_key));", "CREATE TABLE IF NOT EXISTS node_instance (host_id blob PRIMARY KEY, claim_id, node_id, date_created);", @@ -45,39 +41,20 @@ const char *database_config[] = { "CREATE TABLE IF NOT EXISTS host_label(host_id blob, source_type int, label_key text NOT NULL, " "label_value text NOT NULL, date_created INT, PRIMARY KEY (host_id, label_key));", - "CREATE TABLE IF NOT EXISTS chart_hash_map(chart_id blob , hash_id blob, UNIQUE (chart_id, hash_id));", - - "CREATE TABLE IF NOT EXISTS chart_hash(hash_id blob PRIMARY KEY,type text, id text, name text, " - "family text, context text, title text, unit text, plugin text, " - "module text, priority integer, chart_type, last_used);", - - "CREATE VIEW IF NOT EXISTS v_chart_hash as SELECT ch.*, chm.chart_id FROM chart_hash ch, chart_hash_map chm " - "WHERE ch.hash_id = chm.hash_id;", - "CREATE TRIGGER IF NOT EXISTS ins_host AFTER INSERT ON host BEGIN INSERT INTO node_instance (host_id, date_created)" " SELECT new.host_id, unixepoch() WHERE new.host_id NOT IN (SELECT host_id FROM node_instance); END;", - "CREATE TRIGGER IF NOT EXISTS tr_v_chart_hash INSTEAD OF INSERT on v_chart_hash BEGIN " - "INSERT INTO chart_hash (hash_id, type, id, name, family, context, title, unit, plugin, " - "module, priority, chart_type, last_used) " - "values (new.hash_id, new.type, new.id, new.name, new.family, new.context, new.title, new.unit, new.plugin, " - "new.module, new.priority, new.chart_type, unixepoch()) " - "ON CONFLICT (hash_id) DO UPDATE SET last_used = unixepoch(); " - "INSERT INTO chart_hash_map (chart_id, hash_id) values (new.chart_id, new.hash_id) " - "on conflict (chart_id, hash_id) do nothing; END; ", - NULL }; const char *database_cleanup[] = { - "delete from chart where chart_id not in (select chart_id from dimension);", - "delete from host where host_id not in (select host_id from chart);", - "delete from chart_label where chart_id not in (select chart_id from chart);", - "DELETE FROM chart_hash_map WHERE chart_id NOT IN (SELECT chart_id FROM chart);", - "DELETE FROM chart_hash WHERE hash_id NOT IN (SELECT hash_id FROM chart_hash_map);", + "DELETE FROM chart WHERE chart_id NOT IN (SELECT chart_id FROM dimension);", + "DELETE FROM host WHERE host_id NOT IN (SELECT host_id FROM chart);", + "DELETE FROM chart_label WHERE chart_id NOT IN (SELECT chart_id FROM chart);", "DELETE FROM node_instance WHERE host_id NOT IN (SELECT host_id FROM host);", "DELETE FROM host_info WHERE host_id NOT IN (SELECT host_id FROM host);", "DELETE FROM host_label WHERE host_id NOT IN (SELECT host_id FROM host);", + "DROP TRIGGER IF EXISTS tr_dim_del;", NULL }; @@ -86,13 +63,49 @@ sqlite3 *db_meta = NULL; #define MAX_PREPARED_STATEMENTS (32) pthread_key_t key_pool[MAX_PREPARED_STATEMENTS]; -static uv_mutex_t sqlite_transaction_lock; +SQLITE_API int sqlite3_exec_monitored( + sqlite3 *db, /* An open database */ + const char *sql, /* SQL to be evaluated */ + int (*callback)(void*,int,char**,char**), /* Callback function */ + void *data, /* 1st argument to callback */ + char **errmsg /* Error msg written here */ +) { + int rc = sqlite3_exec(db, sql, callback, data, errmsg); + global_statistics_sqlite3_query_completed(rc == SQLITE_OK, rc == SQLITE_BUSY, rc == SQLITE_LOCKED); + return rc; +} + +SQLITE_API int sqlite3_step_monitored(sqlite3_stmt *stmt) { + int rc; + int cnt = 0; + + while (cnt++ < SQL_MAX_RETRY) { + rc = sqlite3_step(stmt); + switch (rc) { + case SQLITE_DONE: + global_statistics_sqlite3_query_completed(1, 0, 0); + break; + case SQLITE_ROW: + global_statistics_sqlite3_row_completed(); + break; + case SQLITE_BUSY: + case SQLITE_LOCKED: + global_statistics_sqlite3_query_completed(rc == SQLITE_DONE, rc == SQLITE_BUSY, rc == SQLITE_LOCKED); + usleep(SQLITE_INSERT_DELAY * USEC_PER_MS); + continue; + default: + break; + } + break; + } + return rc; +} int execute_insert(sqlite3_stmt *res) { int rc; int cnt = 0; - while ((rc = sqlite3_step(res)) != SQLITE_DONE && ++cnt < SQL_MAX_RETRY && likely(!netdata_exit)) { + while ((rc = sqlite3_step_monitored(res)) != SQLITE_DONE && ++cnt < SQL_MAX_RETRY && likely(!netdata_exit)) { if (likely(rc == SQLITE_BUSY || rc == SQLITE_LOCKED)) { usleep(SQLITE_INSERT_DELAY * USEC_PER_MS); error_report("Failed to insert/update, rc = %d -- attempt %d", rc, cnt); @@ -134,14 +147,14 @@ static void add_stmt_to_list(sqlite3_stmt *res) static void release_statement(void *statement) { int rc; -#ifdef NETDATA_INTERNAL_CHECKS +#ifdef NETDATA_DEV_MODE info("Thread %d: Cleaning prepared statement on %p", gettid(), statement); #endif if (unlikely(rc = sqlite3_finalize((sqlite3_stmt *) statement) != SQLITE_OK)) error_report("Failed to finalize statement, rc = %d", rc); } -int prepare_statement(sqlite3 *database, char *query, sqlite3_stmt **statement) +int prepare_statement(sqlite3 *database, const char *query, sqlite3_stmt **statement) { static __thread uint32_t keys_used = 0; @@ -155,7 +168,7 @@ int prepare_statement(sqlite3 *database, char *query, sqlite3_stmt **statement) if (likely(rc == SQLITE_OK)) { if (likely(key)) { ret = pthread_setspecific(*key, *statement); -#ifdef NETDATA_INTERNAL_CHECKS +#ifdef NETDATA_DEV_MODE info("Thread %d: Using key %u on statement %p", gettid(), keys_used, *statement); #endif } @@ -165,88 +178,6 @@ int prepare_statement(sqlite3 *database, char *query, sqlite3_stmt **statement) return rc; } -/* - * Store a chart or dimension UUID in chart_active or dimension_active - * The statement that will be prepared determines that - */ - -static int store_active_uuid_object(sqlite3_stmt **res, char *statement, uuid_t *uuid) -{ - int rc; - - // Check if we should need to prepare the statement - if (!*res) { - rc = prepare_statement(db_meta, statement, res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store active object, rc = %d", rc); - return rc; - } - } - - rc = sqlite3_bind_blob(*res, 1, uuid, sizeof(*uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to bind input parameter to store active object, rc = %d", rc); - else - rc = execute_insert(*res); - return rc; -} - -/* - * Marks a chart with UUID as active - * Input: UUID - */ -void store_active_chart(uuid_t *chart_uuid) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return; - } - - if (unlikely(!chart_uuid)) - return; - - rc = store_active_uuid_object(&res, SQL_STORE_ACTIVE_CHART, chart_uuid); - if (rc != SQLITE_DONE) - error_report("Failed to store active chart, rc = %d", rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize statement in store active chart, rc = %d", rc); - return; -} - -/* - * Marks a dimension with UUID as active - * Input: UUID - */ -void store_active_dimension(uuid_t *dimension_uuid) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return; - } - - if (unlikely(!dimension_uuid)) - return; - - rc = store_active_uuid_object(&res, SQL_STORE_ACTIVE_DIMENSION, dimension_uuid); - if (rc != SQLITE_DONE) - error_report("Failed to store active dimension, rc = %d", rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize statement in store active dimension, rc = %d", rc); - return; -} - static int check_table_integrity_cb(void *data, int argc, char **argv, char **column) { int *status = data; @@ -273,7 +204,7 @@ static int check_table_integrity(char *table) strcpy(wstr,"PRAGMA integrity_check;"); } - int rc = sqlite3_exec(db_meta, wstr, check_table_integrity_cb, (void *) &status, &err_msg); + int rc = sqlite3_exec_monitored(db_meta, wstr, check_table_integrity_cb, (void *) &status, &err_msg); if (rc != SQLITE_OK) { error_report("SQLite error during database integrity check for %s, rc = %d (%s)", table ? table : "the entire database", rc, err_msg); @@ -306,14 +237,13 @@ static void rebuild_chart() info("Rebuilding chart table"); for (int i = 0; rebuild_chart_commands[i]; i++) { info("Executing %s", rebuild_chart_commands[i]); - rc = sqlite3_exec(db_meta, rebuild_chart_commands[i], 0, 0, &err_msg); + rc = sqlite3_exec_monitored(db_meta, rebuild_chart_commands[i], 0, 0, &err_msg); if (rc != SQLITE_OK) { error_report("SQLite error during database setup, rc = %d (%s)", rc, err_msg); error_report("SQLite failed statement %s", rebuild_chart_commands[i]); sqlite3_free(err_msg); } } - return; } const char *rebuild_dimension_commands[] = { @@ -339,14 +269,13 @@ void rebuild_dimension() info("Rebuilding dimension table"); for (int i = 0; rebuild_dimension_commands[i]; i++) { info("Executing %s", rebuild_dimension_commands[i]); - rc = sqlite3_exec(db_meta, rebuild_dimension_commands[i], 0, 0, &err_msg); + rc = sqlite3_exec_monitored(db_meta, rebuild_dimension_commands[i], 0, 0, &err_msg); if (rc != SQLITE_OK) { error_report("SQLite error during database setup, rc = %d (%s)", rc, err_msg); error_report("SQLite failed statement %s", rebuild_dimension_commands[i]); sqlite3_free(err_msg); } } - return; } static int attempt_database_fix() @@ -366,7 +295,7 @@ int init_database_batch(sqlite3 *database, int rebuild, int init_type, const cha char *err_msg = NULL; for (int i = 0; batch[i]; i++) { debug(D_METADATALOG, "Executing %s", batch[i]); - rc = sqlite3_exec(database, batch[i], 0, 0, &err_msg); + rc = sqlite3_exec_monitored(database, batch[i], 0, 0, &err_msg); if (rc != SQLITE_OK) { error_report("SQLite error during database %s, rc = %d (%s)", init_type ? "cleanup" : "setup", rc, err_msg); error_report("SQLite failed statement %s", batch[i]); @@ -384,6 +313,24 @@ int init_database_batch(sqlite3 *database, int rebuild, int init_type, const cha return 0; } +static void sqlite_uuid_parse(sqlite3_context *context, int argc, sqlite3_value **argv) +{ + uuid_t uuid; + + if ( argc != 1 ){ + sqlite3_result_null(context); + return ; + } + int rc = uuid_parse((const char *) sqlite3_value_text(argv[0]), uuid); + if (rc == -1) { + sqlite3_result_null(context); + return ; + } + + sqlite3_result_blob(context, &uuid, sizeof(uuid_t), SQLITE_TRANSIENT); +} + + /* * Initialize the SQLite database * Return 0 on success @@ -437,7 +384,7 @@ int sql_init_database(db_check_action_type_t rebuild, int memory) if (rebuild & DB_CHECK_RECLAIM_SPACE) { if (!(rebuild & DB_CHECK_CONT)) info("Reclaiming space of %s", sqlite_database); - rc = sqlite3_exec(db_meta, "VACUUM;", 0, 0, &err_msg); + rc = sqlite3_exec_monitored(db_meta, "VACUUM;", 0, 0, &err_msg); if (rc != SQLITE_OK) { error_report("Failed to execute VACUUM rc = %d (%s)", rc, err_msg); sqlite3_free(err_msg); @@ -497,12 +444,14 @@ int sql_init_database(db_check_action_type_t rebuild, int memory) if (init_database_batch(db_meta, rebuild, 0, &database_cleanup[0])) return 1; - fatal_assert(0 == uv_mutex_init(&sqlite_transaction_lock)); info("SQLite database initialization completed"); for (int i = 0; i < MAX_PREPARED_STATEMENTS; i++) (void)pthread_key_create(&key_pool[i], release_statement); + rc = sqlite3_create_function(db_meta, "u2h", 1, SQLITE_ANY | SQLITE_DETERMINISTIC, 0, sqlite_uuid_parse, 0, 0); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to register internal u2h function"); return 0; } @@ -523,233 +472,9 @@ void sql_close_database(void) rc = sqlite3_close_v2(db_meta); if (unlikely(rc != SQLITE_OK)) error_report("Error %d while closing the SQLite database, %s", rc, sqlite3_errstr(rc)); - return; -} - -#define FIND_UUID_TYPE "select 1 from host where host_id = @uuid union select 2 from chart where chart_id = @uuid union select 3 from dimension where dim_id = @uuid;" - -int find_uuid_type(uuid_t *uuid) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - int uuid_type = 3; - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, FIND_UUID_TYPE, &res); - if (rc != SQLITE_OK) { - error_report("Failed to bind prepare statement to find UUID type in the database"); - return 0; - } - } - - rc = sqlite3_bind_blob(res, 1, uuid, sizeof(*uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_step(res); - if (likely(rc == SQLITE_ROW)) - uuid_type = sqlite3_column_int(res, 0); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement during find uuid type, rc = %d", rc); - - return uuid_type; - -bind_fail: - return 0; -} - -int find_dimension_uuid(RRDSET *st, RRDDIM *rd, uuid_t *store_uuid) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - int status = 1; - - if (unlikely(!db_meta) && default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return 1; - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, SQL_FIND_DIMENSION_UUID, &res); - if (rc != SQLITE_OK) { - error_report("Failed to bind prepare statement to lookup dimension UUID in the database"); - return 1; - } - } - - rc = sqlite3_bind_blob(res, 1, st->chart_uuid, sizeof(*st->chart_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 2, rd->id, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 3, rd->name, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_step(res); - if (likely(rc == SQLITE_ROW)) { - uuid_copy(*store_uuid, *((uuid_t *) sqlite3_column_blob(res, 0))); - status = 0; - } - else { - uuid_generate(*store_uuid); - status = sql_store_dimension(store_uuid, st->chart_uuid, rd->id, rd->name, rd->multiplier, rd->divisor, rd->algorithm); - if (unlikely(status)) - error_report("Failed to store dimension metadata in the database"); - } - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement find dimension uuid, rc = %d", rc); - return status; - -bind_fail: - error_report("Failed to bind input parameter to perform dimension UUID database lookup, rc = %d", rc); - return 1; -} - -#define DELETE_DIMENSION_UUID "delete from dimension where dim_id = @uuid;" - -void delete_dimension_uuid(uuid_t *dimension_uuid) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - -#ifdef NETDATA_INTERNAL_CHECKS - char uuid_str[GUID_LEN + 1]; - uuid_unparse_lower(*dimension_uuid, uuid_str); - debug(D_METADATALOG,"Deleting dimension uuid %s", uuid_str); -#endif - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, DELETE_DIMENSION_UUID, &res); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement to delete a dimension uuid"); - return; - } - } - - rc = sqlite3_bind_blob(res, 1, dimension_uuid, sizeof(*dimension_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_step(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to delete dimension uuid, rc = %d", rc); - -bind_fail: - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement when deleting dimension UUID, rc = %d", rc); - return; -} - -/* - * Do a database lookup to find the UUID of a chart - * - */ -uuid_t *find_chart_uuid(RRDHOST *host, const char *type, const char *id, const char *name) -{ - static __thread sqlite3_stmt *res = NULL; - uuid_t *uuid = NULL; - int rc; - - if (unlikely(!db_meta) && default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return NULL; - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, SQL_FIND_CHART_UUID, &res); - if (rc != SQLITE_OK) { - error_report("Failed to prepare statement to lookup chart UUID in the database"); - return NULL; - } - } - - rc = sqlite3_bind_blob(res, 1, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 2, type, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 3, id, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 4, name ? name : id, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_step(res); - if (likely(rc == SQLITE_ROW)) { - uuid = mallocz(sizeof(uuid_t)); - uuid_copy(*uuid, sqlite3_column_blob(res, 0)); - } - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement when searching for a chart UUID, rc = %d", rc); - -#ifdef NETDATA_INTERNAL_CHECKS - char uuid_str[GUID_LEN + 1]; - if (likely(uuid)) { - uuid_unparse_lower(*uuid, uuid_str); - debug(D_METADATALOG, "Found UUID %s for chart %s.%s", uuid_str, type, name ? name : id); - } - else - debug(D_METADATALOG, "UUID not found for chart %s.%s", type, name ? name : id); -#endif - return uuid; - -bind_fail: - error_report("Failed to bind input parameter to perform chart UUID database lookup, rc = %d", rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement when searching for a chart UUID, rc = %d", rc); - return NULL; -} - -int update_chart_metadata(uuid_t *chart_uuid, RRDSET *st, const char *id, const char *name) -{ - int rc; - - if (unlikely(!db_meta) && default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return 0; - - rc = sql_store_chart( - chart_uuid, &st->rrdhost->host_uuid, st->type, id, name, st->family, st->context, st->title, st->units, st->plugin_name, - st->module_name, st->priority, st->update_every, st->chart_type, st->rrd_memory_mode, st->entries); - - return rc; -} - -uuid_t *create_chart_uuid(RRDSET *st, const char *id, const char *name) -{ - uuid_t *uuid = NULL; - int rc; - - uuid = mallocz(sizeof(uuid_t)); - uuid_generate(*uuid); - -#ifdef NETDATA_INTERNAL_CHECKS - char uuid_str[GUID_LEN + 1]; - uuid_unparse_lower(*uuid, uuid_str); - debug(D_METADATALOG,"Generating uuid [%s] for chart %s under host %s", uuid_str, st->id, st->rrdhost->hostname); -#endif - - rc = update_chart_metadata(uuid, st, id, name); - - if (unlikely(rc)) - error_report("Failed to store chart metadata in the database"); - - return uuid; } -static int exec_statement_with_uuid(const char *sql, uuid_t *uuid) +int exec_statement_with_uuid(const char *sql, uuid_t *uuid) { int rc, result = 1; sqlite3_stmt *res = NULL; @@ -763,7 +488,7 @@ static int exec_statement_with_uuid(const char *sql, uuid_t *uuid) rc = sqlite3_bind_blob(res, 1, uuid, sizeof(*uuid), SQLITE_STATIC); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind host parameter to %s, rc = %d", sql, rc); - goto failed; + goto skip; } rc = execute_insert(res); @@ -772,7 +497,7 @@ static int exec_statement_with_uuid(const char *sql, uuid_t *uuid) else error_report("Failed to execute %s, rc = %d", sql, rc); -failed: +skip: rc = sqlite3_finalize(res); if (unlikely(rc != SQLITE_OK)) error_report("Failed to finalize statement %s, rc = %d", sql, rc); @@ -780,456 +505,24 @@ failed: } -// Migrate all hosts with hops zero to this host_uuid -void migrate_localhost(uuid_t *host_uuid) -{ - int rc; - - rc = exec_statement_with_uuid("UPDATE chart SET host_id = @host_id WHERE host_id in (SELECT host_id FROM host where host_id <> @host_id and hops = 0); ", host_uuid); - if (!rc) - rc = exec_statement_with_uuid("DELETE FROM host WHERE hops = 0 AND host_id <> @host_id; ", host_uuid); - if (!rc) - db_execute("DELETE FROM node_instance WHERE host_id NOT IN (SELECT host_id FROM host);"); -} +// +// Support for archived charts (TO BE REMOVED) +// +#define SELECT_DIMENSION "select d.id, d.name from dimension d where d.chart_id = @chart_uuid;" -int sql_store_host( - uuid_t *host_uuid, const char *hostname, const char *registry_hostname, int update_every, const char *os, - const char *tzone, const char *tags, int hops) +static void sql_rrdim2json(sqlite3_stmt *res_dim, uuid_t *chart_uuid, BUFFER *wb, size_t *dimensions_count) { - static __thread sqlite3_stmt *res = NULL; int rc; - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return 0; - error_report("Database has not been initialized"); - return 1; - } - - if (unlikely((!res))) { - rc = prepare_statement(db_meta, SQL_STORE_HOST, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store host, rc = %d", rc); - return 1; - } - } - - rc = sqlite3_bind_blob(res, 1, host_uuid, sizeof(*host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 2, hostname, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 3, registry_hostname, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 4, update_every); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 5, os, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 6, tzone, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 7, tags, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 8, hops); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - int store_rc = sqlite3_step(res); - if (unlikely(store_rc != SQLITE_DONE)) - error_report("Failed to store host %s, rc = %d", hostname, rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement to store host %s, rc = %d", hostname, rc); - - return !(store_rc == SQLITE_DONE); -bind_fail: - error_report("Failed to bind parameter to store host %s, rc = %d", hostname, rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement to store host %s, rc = %d", hostname, rc); - return 1; -} - -// -// Store host and host system info information in the database -#define SQL_STORE_HOST_INFO "INSERT OR REPLACE INTO host " \ - "(host_id, hostname, registry_hostname, update_every, os, timezone," \ - "tags, hops, memory_mode, abbrev_timezone, utc_offset, program_name, program_version," \ - "entries, health_enabled) " \ - "values (@host_id, @hostname, @registry_hostname, @update_every, @os, @timezone, @tags, @hops, @memory_mode, " \ - "@abbrev_timezone, @utc_offset, @program_name, @program_version, " \ - "@entries, @health_enabled);" - -int sql_store_host_info(RRDHOST *host) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return 0; - error_report("Database has not been initialized"); - return 1; - } - - if (unlikely((!res))) { - rc = prepare_statement(db_meta, SQL_STORE_HOST_INFO, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store host, rc = %d", rc); - return 1; - } - } - - rc = sqlite3_bind_blob(res, 1, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, 2, host->hostname, 0); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, 3, host->registry_hostname, 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 4, host->rrd_update_every); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, 5, host->os, 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, 6, host->timezone, 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, 7, host->tags, 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 8, host->system_info ? host->system_info->hops : 0); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 9, host->rrd_memory_mode); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, 10, host->abbrev_timezone, 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 11, host->utc_offset); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, 12, host->program_name, 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = bind_text_null(res, 13, host->program_version, 1); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int64(res, 14, host->rrd_history_entries); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 15, host->health_enabled); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - int store_rc = sqlite3_step(res); - if (unlikely(store_rc != SQLITE_DONE)) - error_report("Failed to store host %s, rc = %d", host->hostname, rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement to store host %s, rc = %d", host->hostname, rc); - - return !(store_rc == SQLITE_DONE); -bind_fail: - error_report("Failed to bind parameter to store host %s, rc = %d", host->hostname, rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement to store host %s, rc = %d", host->hostname, rc); - return 1; -} - -/* - * Store a chart in the database - */ - -int sql_store_chart( - uuid_t *chart_uuid, uuid_t *host_uuid, const char *type, const char *id, const char *name, const char *family, - const char *context, const char *title, const char *units, const char *plugin, const char *module, long priority, - int update_every, int chart_type, int memory_mode, long history_entries) -{ - static __thread sqlite3_stmt *res = NULL; - int rc, param = 0; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return 0; - error_report("Database has not been initialized"); - return 1; - } - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, SQL_STORE_CHART, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store chart, rc = %d", rc); - return 1; - } - } - - param++; - rc = sqlite3_bind_blob(res, 1, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_blob(res, 2, host_uuid, sizeof(*host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 3, type, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 4, id, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - if (name && *name) - rc = sqlite3_bind_text(res, 5, name, -1, SQLITE_STATIC); - else - rc = sqlite3_bind_null(res, 5); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 6, family, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 7, context, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 8, title, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 9, units, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 10, plugin, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 11, module, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_int(res, 12, priority); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_int(res, 13, update_every); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_int(res, 14, chart_type); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_int(res, 15, memory_mode); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_int(res, 16, history_entries); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to store chart, rc = %d", rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in chart store function, rc = %d", rc); - - return 0; - -bind_fail: - error_report("Failed to bind parameter %d to store chart, rc = %d", param, rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in chart store function, rc = %d", rc); - return 1; -} - -/* - * Store a dimension - */ -int sql_store_dimension( - uuid_t *dim_uuid, uuid_t *chart_uuid, const char *id, const char *name, collected_number multiplier, - collected_number divisor, int algorithm) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return 0; - error_report("Database has not been initialized"); - return 1; - } - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, SQL_STORE_DIMENSION, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store dimension, rc = %d", rc); - return 1; - } - } - - rc = sqlite3_bind_blob(res, 1, dim_uuid, sizeof(*dim_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_blob(res, 2, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 3, id, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_text(res, 4, name, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 5, multiplier); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 6, divisor); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = sqlite3_bind_int(res, 7, algorithm); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to store dimension, rc = %d", rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in store dimension, rc = %d", rc); - return 0; - -bind_fail: - error_report("Failed to bind parameter to store dimension, rc = %d", rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in store dimension, rc = %d", rc); - return 1; -} - -/* - * Store set option for a dimension - */ -int sql_set_dimension_option(uuid_t *dim_uuid, char *option) -{ - sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return 0; - error_report("Database has not been initialized"); - return 1; - } - - rc = sqlite3_prepare_v2(db_meta, "UPDATE dimension SET options = @options WHERE dim_id = @dim_id", -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to update dimension options"); - return 0; - }; - - rc = sqlite3_bind_blob(res, 2, dim_uuid, sizeof(*dim_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - if (!option || !strcmp(option,"unhide")) - rc = sqlite3_bind_null(res, 1); - else - rc = sqlite3_bind_text(res, 1, option, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to update dimension option, rc = %d", rc); - -bind_fail: - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize statement in update dimension options, rc = %d", rc); - return 0; -} - - -// -// Support for archived charts -// -#define SELECT_DIMENSION "select d.id, d.name from dimension d where d.chart_id = @chart_uuid;" - -void sql_rrdim2json(sqlite3_stmt *res_dim, uuid_t *chart_uuid, BUFFER *wb, size_t *dimensions_count) -{ - int rc; - - rc = sqlite3_bind_blob(res_dim, 1, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); - if (rc != SQLITE_OK) - return; + rc = sqlite3_bind_blob(res_dim, 1, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); + if (rc != SQLITE_OK) + return; int dimensions = 0; buffer_sprintf(wb, "\t\t\t\"dimensions\": {\n"); - while (sqlite3_step(res_dim) == SQLITE_ROW) { + while (sqlite3_step_monitored(res_dim) == SQLITE_ROW) { if (dimensions) buffer_strcat(wb, ",\n\t\t\t\t\""); else @@ -1291,11 +584,11 @@ void sql_rrdset2json(RRDHOST *host, BUFFER *wb) ",\n\t\"memory_mode\": \"%s\"" ",\n\t\"custom_info\": \"%s\"" ",\n\t\"charts\": {" - , host->hostname - , host->program_version + , rrdhost_hostname(host) + , rrdhost_program_version(host) , get_release_channel() - , host->os - , host->timezone + , rrdhost_os(host) + , rrdhost_timezone(host) , host->rrd_update_every , host->rrd_history_entries , rrd_memory_mode_name(host->rrd_memory_mode) @@ -1305,7 +598,7 @@ void sql_rrdset2json(RRDHOST *host, BUFFER *wb) size_t c = 0; size_t dimensions = 0; - while (sqlite3_step(res_chart) == SQLITE_ROW) { + while (sqlite3_step_monitored(res_chart) == SQLITE_ROW) { char id[512]; sprintf(id, "%s.%s", sqlite3_column_text(res_chart, 3), sqlite3_column_text(res_chart, 1)); RRDSET *st = rrdset_find(host, id); @@ -1386,7 +679,7 @@ void sql_rrdset2json(RRDHOST *host, BUFFER *wb) "\n\t\t\t\"hostname\": \"%s\"" "\n\t\t}" , (found > 0) ? "," : "" - , h->hostname + , rrdhost_hostname(h) ); found++; @@ -1400,7 +693,7 @@ void sql_rrdset2json(RRDHOST *host, BUFFER *wb) , "\n\t\t{" "\n\t\t\t\"hostname\": \"%s\"" "\n\t\t}" - , host->hostname + , rrdhost_hostname(host) ); } @@ -1414,95 +707,6 @@ failed: rc = sqlite3_finalize(res_chart); if (unlikely(rc != SQLITE_OK)) error_report("Failed to finalize the prepared statement when reading archived charts"); - - return; -} - -void free_temporary_host(RRDHOST *host) -{ - if (host) { - freez(host->hostname); - freez((char *)host->os); - freez((char *)host->tags); - freez((char *)host->timezone); - freez(host->program_name); - freez(host->program_version); - freez(host->registry_hostname); - freez(host->system_info); - freez(host); - } -} - -#define SELECT_HOST "select host_id, registry_hostname, update_every, os, timezone, tags from host where hostname = @hostname order by rowid desc;" -#define SELECT_HOST_BY_UUID "select h.host_id, h.registry_hostname, h.update_every, h.os, h.timezone, h.tags from host h, node_instance ni " \ - "where (ni.host_id = @host_id or ni.node_id = @host_id) AND ni.host_id = h.host_id;" - -RRDHOST *sql_create_host_by_uuid(char *hostname) -{ - int rc; - RRDHOST *host = NULL; - uuid_t host_uuid; - - sqlite3_stmt *res = NULL; - - rc = uuid_parse(hostname, host_uuid); - if (!rc) { - rc = sqlite3_prepare_v2(db_meta, SELECT_HOST_BY_UUID, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch host by uuid"); - return NULL; - } - rc = sqlite3_bind_blob(res, 1, &host_uuid, sizeof(host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host_id parameter to fetch host information"); - goto failed; - } - } - else { - rc = sqlite3_prepare_v2(db_meta, SELECT_HOST, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch host by hostname"); - return NULL; - } - rc = sqlite3_bind_text(res, 1, hostname, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind hostname parameter to fetch host information"); - goto failed; - } - } - - rc = sqlite3_step(res); - if (unlikely(rc != SQLITE_ROW)) { - error_report("Failed to find hostname %s", hostname); - goto failed; - } - - char uuid_str[GUID_LEN + 1]; - uuid_unparse_lower(*((uuid_t *) sqlite3_column_blob(res, 0)), uuid_str); - - host = callocz(1, sizeof(RRDHOST)); - - set_host_properties(host, sqlite3_column_int(res, 2), RRD_MEMORY_MODE_DBENGINE, hostname, - (char *) sqlite3_column_text(res, 1), (const char *) uuid_str, - (char *) sqlite3_column_text(res, 3), (char *) sqlite3_column_text(res, 5), - (char *) sqlite3_column_text(res, 4), NULL, 0, NULL, NULL); - - uuid_copy(host->host_uuid, *((uuid_t *) sqlite3_column_blob(res, 0))); - - host->system_info = callocz(1, sizeof(*host->system_info));; - rrdhost_flag_set(host, RRDHOST_FLAG_ARCHIVED); - -#ifdef ENABLE_DBENGINE - for(int tier = 0; tier < storage_tiers ; tier++) - host->storage_instance[tier] = (STORAGE_INSTANCE *)multidb_ctx[tier]; -#endif - -failed: - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when reading host information"); - - return host; } void db_execute(const char *cmd) @@ -1511,582 +715,85 @@ void db_execute(const char *cmd) int cnt = 0; while (cnt < SQL_MAX_RETRY) { char *err_msg; - rc = sqlite3_exec(db_meta, cmd, 0, 0, &err_msg); + rc = sqlite3_exec_monitored(db_meta, cmd, 0, 0, &err_msg); if (rc != SQLITE_OK) { error_report("Failed to execute '%s', rc = %d (%s) -- attempt %d", cmd, rc, err_msg, cnt); sqlite3_free(err_msg); if (likely(rc == SQLITE_BUSY || rc == SQLITE_LOCKED)) { usleep(SQLITE_INSERT_DELAY * USEC_PER_MS); } - else break; + else + break; } else break; - ++cnt; - } - return; -} - -void db_lock(void) -{ - uv_mutex_lock(&sqlite_transaction_lock); - return; -} - -void db_unlock(void) -{ - uv_mutex_unlock(&sqlite_transaction_lock); - return; -} - - -#define SELECT_MIGRATED_FILE "select 1 from metadata_migration where filename = @path;" - -int file_is_migrated(char *path) -{ - sqlite3_stmt *res = NULL; - int rc; - - rc = sqlite3_prepare_v2(db_meta, SELECT_MIGRATED_FILE, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch host"); - return 0; - } - - rc = sqlite3_bind_text(res, 1, path, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind filename parameter to check migration"); - return 0; - } - - rc = sqlite3_step(res); - - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when checking if metadata file is migrated"); - - return (rc == SQLITE_ROW); -} - -#define STORE_MIGRATED_FILE "insert or replace into metadata_migration (filename, file_size, date_created) " \ - "values (@file, @size, unixepoch());" - -void add_migrated_file(char *path, uint64_t file_size) -{ - sqlite3_stmt *res = NULL; - int rc; - - rc = sqlite3_prepare_v2(db_meta, STORE_MIGRATED_FILE, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch host"); - return; - } - - rc = sqlite3_bind_text(res, 1, path, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind filename parameter to store migration information"); - return; - } - - rc = sqlite3_bind_int64(res, 2, file_size); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind size parameter to store migration information"); - return; - } - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to store migrated file, rc = %d", rc); - - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when checking if metadata file is migrated"); - - return; -} - -static int sql_store_label(sqlite3_stmt *res, uuid_t *uuid, int source_type, const char *label, const char *value) -{ - int rc; - - rc = sqlite3_bind_blob(res, 1, uuid, sizeof(*uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind UUID parameter to store label information"); - goto skip_store; - } - - rc = sqlite3_bind_int(res, 2, source_type); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind type parameter to store label information"); - goto skip_store; - } - - rc = sqlite3_bind_text(res, 3, label, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind label parameter to store label information"); - goto skip_store; - } - - rc = sqlite3_bind_text(res, 4, value, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind value parameter to store label information"); - goto skip_store; - } - - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to store label entry, rc = %d", rc); - -skip_store: - if (unlikely(sqlite3_reset(res) != SQLITE_OK)) - error_report("Failed to reset the prepared statement when storing label information"); - - return rc != SQLITE_DONE; -} - -#define SQL_INS_CHART_LABEL "insert or replace into chart_label " \ - "(chart_id, source_type, label_key, label_value, date_created) " \ - "values (@chart, @source, @label, @value, unixepoch());" - -void sql_store_chart_label(uuid_t *chart_uuid, int source_type, char *label, char *value) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return; - } - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, SQL_INS_CHART_LABEL, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement store chart labels"); - return; - } - } - - sql_store_label(res, chart_uuid, source_type, label, value); - - return; -} - -#define SQL_INS_HOST_LABEL "INSERT OR REPLACE INTO host_label " \ - "(host_id, source_type, label_key, label_value, date_created) " \ - "values (@chart, @source, @label, @value, unixepoch());" - -static void sql_store_host_label(uuid_t *host_uuid, int source_type, const char *label, const char *value) -{ - static __thread sqlite3_stmt *res = NULL; - int rc; - - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return; - } - - if (unlikely(!res)) { - rc = prepare_statement(db_meta, SQL_INS_HOST_LABEL, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement store chart labels"); - return; - } - } - - (void) sql_store_label(res, host_uuid, source_type, label, value); -} - -int find_dimension_first_last_t(char *machine_guid, char *chart_id, char *dim_id, - uuid_t *uuid, time_t *first_entry_t, time_t *last_entry_t, uuid_t *rrdeng_uuid, int tier) -{ -#ifdef ENABLE_DBENGINE - int rc; - uuid_t legacy_uuid; - uuid_t multihost_legacy_uuid; - time_t dim_first_entry_t, dim_last_entry_t; - - rc = rrdeng_metric_latest_time_by_uuid(uuid, &dim_first_entry_t, &dim_last_entry_t, tier); - if (unlikely(rc)) { - rrdeng_generate_legacy_uuid(dim_id, chart_id, &legacy_uuid); - rc = rrdeng_metric_latest_time_by_uuid(&legacy_uuid, &dim_first_entry_t, &dim_last_entry_t, tier); - if (likely(rc)) { - rrdeng_convert_legacy_uuid_to_multihost(machine_guid, &legacy_uuid, &multihost_legacy_uuid); - rc = rrdeng_metric_latest_time_by_uuid(&multihost_legacy_uuid, &dim_first_entry_t, &dim_last_entry_t, tier); - if (likely(!rc)) - uuid_copy(*rrdeng_uuid, multihost_legacy_uuid); - } - else - uuid_copy(*rrdeng_uuid, legacy_uuid); - } - else - uuid_copy(*rrdeng_uuid, *uuid); - - if (likely(!rc)) { - *first_entry_t = MIN(*first_entry_t, dim_first_entry_t); - *last_entry_t = MAX(*last_entry_t, dim_last_entry_t); - } - return rc; -#else - UNUSED(machine_guid); - UNUSED(chart_id); - UNUSED(dim_id); - UNUSED(uuid); - UNUSED(first_entry_t); - UNUSED(last_entry_t); - UNUSED(rrdeng_uuid); - return 1; -#endif -} -#include "../storage_engine.h" -#ifdef ENABLE_DBENGINE -static RRDDIM *create_rrdim_entry(ONEWAYALLOC *owa, RRDSET *st, char *id, char *name, uuid_t *metric_uuid) -{ - STORAGE_ENGINE *eng = storage_engine_get(RRD_MEMORY_MODE_DBENGINE); - - if (unlikely(!eng)) - return NULL; - - RRDDIM *rd = onewayalloc_callocz(owa, 1, sizeof(*rd)); - rd->rrdset = st; - rd->update_every = st->update_every; - rd->last_stored_value = NAN; - rrddim_flag_set(rd, RRDDIM_FLAG_NONE); - - uuid_copy(rd->metric_uuid, *metric_uuid); - rd->id = onewayalloc_strdupz(owa, id); - rd->name = onewayalloc_strdupz(owa, name); - - for(int tier = 0; tier < storage_tiers ;tier++) { - rd->tiers[tier] = onewayalloc_callocz(owa, 1, sizeof(*rd->tiers[tier])); - rd->rrd_memory_mode = RRD_MEMORY_MODE_DBENGINE; - rd->tiers[tier]->tier_grouping = get_tier_grouping(tier); - rd->tiers[tier]->mode = RRD_MEMORY_MODE_DBENGINE; - rd->tiers[tier]->query_ops.init = rrdeng_load_metric_init; - rd->tiers[tier]->query_ops.next_metric = rrdeng_load_metric_next; - rd->tiers[tier]->query_ops.is_finished = rrdeng_load_metric_is_finished; - rd->tiers[tier]->query_ops.finalize = rrdeng_load_metric_finalize; - rd->tiers[tier]->query_ops.latest_time = rrdeng_metric_latest_time; - rd->tiers[tier]->query_ops.oldest_time = rrdeng_metric_oldest_time; - rd->tiers[tier]->db_metric_handle = eng->api.init(rd, st->rrdhost->storage_instance[tier]); - } - - return rd; -} -#endif - -#define SELECT_CHART_CONTEXT "select d.dim_id, d.id, d.name, c.id, c.type, c.name, c.update_every, c.chart_id, " \ - "c.context, CASE WHEN d.options = 'hidden' THEN 1 else 0 END from chart c, " \ - "dimension d, host h " \ - "where d.chart_id = c.chart_id and c.host_id = h.host_id and c.host_id = @host_id and c.context = @context " \ - "order by c.chart_id asc, c.type||c.id desc;" - -#define SELECT_CHART_SINGLE "select d.dim_id, d.id, d.name, c.id, c.type, c.name, c.update_every, c.chart_id, " \ - "c.context, CASE WHEN d.options = 'hidden' THEN 1 else 0 END from chart c, " \ - "dimension d, host h " \ - "where d.chart_id = c.chart_id and c.host_id = h.host_id and c.host_id = @host_id and c.type||'.'||c.id = @chart " \ - "order by c.chart_id asc, c.type||'.'||c.id desc;" - -void sql_build_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list, RRDHOST *host, char *context, char *chart) -{ -#ifdef ENABLE_DBENGINE - int rc; - - if (unlikely(!param_list) || host->rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return; - - if (unlikely(!(*param_list))) { - *param_list = onewayalloc_mallocz(owa, sizeof(struct context_param)); - (*param_list)->first_entry_t = LONG_MAX; - (*param_list)->last_entry_t = 0; - (*param_list)->rd = NULL; - (*param_list)->flags = CONTEXT_FLAGS_ARCHIVE; - if (chart) - (*param_list)->flags |= CONTEXT_FLAGS_CHART; - else - (*param_list)->flags |= CONTEXT_FLAGS_CONTEXT; - } - - sqlite3_stmt *res = NULL; - - if (context) - rc = sqlite3_prepare_v2(db_meta, SELECT_CHART_CONTEXT, -1, &res, 0); - else - rc = sqlite3_prepare_v2(db_meta, SELECT_CHART_SINGLE, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to fetch host archived charts"); - return; - } - - rc = sqlite3_bind_blob(res, 1, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host parameter to fetch archived charts"); - goto failed; - } - - if (context) - rc = sqlite3_bind_text(res, 2, context, -1, SQLITE_STATIC); - else - rc = sqlite3_bind_text(res, 2, chart, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host parameter to fetch archived charts"); - goto failed; - } - - RRDSET *st = NULL; - char machine_guid[GUID_LEN + 1]; - uuid_unparse_lower(host->host_uuid, machine_guid); - uuid_t rrdeng_uuid; - uuid_t chart_id; - - while (sqlite3_step(res) == SQLITE_ROW) { - char id[512]; - sprintf(id, "%s.%s", sqlite3_column_text(res, 3), sqlite3_column_text(res, 1)); - - if (!st || uuid_compare(*(uuid_t *)sqlite3_column_blob(res, 7), chart_id)) { - if (unlikely(st && !st->counter)) { - onewayalloc_freez(owa, st->context); - onewayalloc_freez(owa, (char *) st->name); - onewayalloc_freez(owa, st); - } - st = onewayalloc_callocz(owa, 1, sizeof(*st)); - char n[RRD_ID_LENGTH_MAX + 1]; - - snprintfz( - n, RRD_ID_LENGTH_MAX, "%s.%s", (char *)sqlite3_column_text(res, 4), - (char *)sqlite3_column_text(res, 3)); - st->name = onewayalloc_strdupz(owa, n); - st->update_every = sqlite3_column_int(res, 6); - st->counter = 0; - if (chart) { - st->context = onewayalloc_strdupz(owa, (char *)sqlite3_column_text(res, 8)); - strncpyz(st->id, chart, RRD_ID_LENGTH_MAX); - } - uuid_copy(chart_id, *(uuid_t *)sqlite3_column_blob(res, 7)); - st->last_entry_t = 0; - st->rrdhost = host; - } - - if (unlikely(find_dimension_first_last_t(machine_guid, (char *)st->name, (char *)sqlite3_column_text(res, 1), - (uuid_t *)sqlite3_column_blob(res, 0), &(*param_list)->first_entry_t, &(*param_list)->last_entry_t, - &rrdeng_uuid, 0))) - continue; - st->counter++; - st->last_entry_t = MAX(st->last_entry_t, (*param_list)->last_entry_t); - - RRDDIM *rd = create_rrdim_entry(owa, st, (char *)sqlite3_column_text(res, 1), (char *)sqlite3_column_text(res, 2), &rrdeng_uuid); - if (unlikely(!rd)) - continue; - if (sqlite3_column_int(res, 9) == 1) - rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN); - rd->next = (*param_list)->rd; - (*param_list)->rd = rd; - } - if (st) { - if (!st->counter) { - onewayalloc_freez(owa,st->context); - onewayalloc_freez(owa,(char *)st->name); - onewayalloc_freez(owa,st); - } - else - if (!st->context && context) - st->context = onewayalloc_strdupz(owa,context); + ++cnt; } - -failed: - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when reading archived charts"); -#else - UNUSED(param_list); - UNUSED(host); - UNUSED(context); - UNUSED(chart); -#endif - return; } +#define SELECT_MIGRATED_FILE "select 1 from metadata_migration where filename = @path;" -/* - * Store a chart hash in the database - */ - -#define SQL_STORE_CHART_HASH "insert into v_chart_hash (hash_id, type, id, " \ - "name, family, context, title, unit, plugin, module, priority, chart_type, last_used, chart_id) " \ - "values (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11, ?12, unixepoch(), ?13);" - -int sql_store_chart_hash( - uuid_t *hash_id, uuid_t *chart_id, const char *type, const char *id, const char *name, const char *family, - const char *context, const char *title, const char *units, const char *plugin, const char *module, long priority, - RRDSET_TYPE chart_type) +int file_is_migrated(char *path) { - static __thread sqlite3_stmt *res = NULL; - int rc, param = 0; + sqlite3_stmt *res = NULL; + int rc; - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) - return 0; - error_report("Database has not been initialized"); - return 1; + rc = sqlite3_prepare_v2(db_meta, SELECT_MIGRATED_FILE, -1, &res, 0); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to prepare statement to fetch host"); + return 0; } - if (unlikely(!res)) { - rc = prepare_statement(db_meta, SQL_STORE_CHART_HASH, &res); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store chart, rc = %d", rc); - return 1; - } + rc = sqlite3_bind_text(res, 1, path, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to bind filename parameter to check migration"); + return 0; } - param++; - rc = sqlite3_bind_blob(res, 1, hash_id, sizeof(*hash_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 2, type, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 3, id, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - if (name && *name) - rc = sqlite3_bind_text(res, 4, name, -1, SQLITE_STATIC); - else - rc = sqlite3_bind_null(res, 4); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 5, family, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; - - param++; - rc = sqlite3_bind_text(res, 6, context, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + rc = sqlite3_step_monitored(res); - param++; - rc = sqlite3_bind_text(res, 7, title, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) + error_report("Failed to finalize the prepared statement when checking if metadata file is migrated"); - param++; - rc = sqlite3_bind_text(res, 8, units, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + return (rc == SQLITE_ROW); +} - param++; - rc = sqlite3_bind_text(res, 9, plugin, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; +#define STORE_MIGRATED_FILE "insert or replace into metadata_migration (filename, file_size, date_created) " \ + "values (@file, @size, unixepoch());" - param++; - rc = sqlite3_bind_text(res, 10, module, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; +void add_migrated_file(char *path, uint64_t file_size) +{ + sqlite3_stmt *res = NULL; + int rc; - param++; - rc = sqlite3_bind_int(res, 11, (int) priority); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + rc = sqlite3_prepare_v2(db_meta, STORE_MIGRATED_FILE, -1, &res, 0); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to prepare statement to fetch host"); + return; + } - param++; - rc = sqlite3_bind_int(res, 12, chart_type); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + rc = sqlite3_bind_text(res, 1, path, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to bind filename parameter to store migration information"); + return; + } - param++; - rc = sqlite3_bind_blob(res, 13, chart_id, sizeof(*chart_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) - goto bind_fail; + rc = sqlite3_bind_int64(res, 2, (sqlite_int64) file_size); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to bind size parameter to store migration information"); + return; + } rc = execute_insert(res); if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to store chart hash_id, rc = %d", rc); - - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in chart hash_id store function, rc = %d", rc); - - return 0; + error_report("Failed to store migrated file, rc = %d", rc); - bind_fail: - error_report("Failed to bind parameter %d to store chart hash_id, rc = %d", param, rc); - rc = sqlite3_reset(res); - if (unlikely(rc != SQLITE_OK)) - error_report("Failed to reset statement in chart hash_id store function, rc = %d", rc); - return 1; + if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) + error_report("Failed to finalize the prepared statement when checking if metadata file is migrated"); } -/* - chart hashes are used for cloud communication. - if cloud is disabled or openssl is not available (which will prevent cloud connectivity) - skip hash calculations -*/ -void compute_chart_hash(RRDSET *st) -{ -#if !defined DISABLE_CLOUD && defined ENABLE_HTTPS - EVP_MD_CTX *evpctx; - unsigned char hash_value[EVP_MAX_MD_SIZE]; - unsigned int hash_len; - char priority_str[32]; - - if (rrdhost_flag_check(st->rrdhost, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS)) { - internal_error(true, "Skipping compute_chart_hash for host %s because context streaming is enabled", st->rrdhost->hostname); - return; - } - sprintf(priority_str, "%ld", st->priority); - - evpctx = EVP_MD_CTX_create(); - EVP_DigestInit_ex(evpctx, EVP_sha256(), NULL); - //EVP_DigestUpdate(evpctx, st->type, strlen(st->type)); - EVP_DigestUpdate(evpctx, st->id, strlen(st->id)); - EVP_DigestUpdate(evpctx, st->name, strlen(st->name)); - EVP_DigestUpdate(evpctx, st->family, strlen(st->family)); - EVP_DigestUpdate(evpctx, st->context, strlen(st->context)); - EVP_DigestUpdate(evpctx, st->title, strlen(st->title)); - EVP_DigestUpdate(evpctx, st->units, strlen(st->units)); - EVP_DigestUpdate(evpctx, st->plugin_name, strlen(st->plugin_name)); - if (st->module_name) - EVP_DigestUpdate(evpctx, st->module_name, strlen(st->module_name)); -// EVP_DigestUpdate(evpctx, priority_str, strlen(priority_str)); - EVP_DigestUpdate(evpctx, &st->priority, sizeof(st->priority)); - EVP_DigestUpdate(evpctx, &st->chart_type, sizeof(st->chart_type)); - EVP_DigestFinal_ex(evpctx, hash_value, &hash_len); - EVP_MD_CTX_destroy(evpctx); - fatal_assert(hash_len > sizeof(uuid_t)); - - char uuid_str[GUID_LEN + 1]; - uuid_unparse_lower(*((uuid_t *) &hash_value), uuid_str); - //info("Calculating HASH %s for chart %s", uuid_str, st->name); - uuid_copy(st->state->hash_id, *((uuid_t *) &hash_value)); - - (void)sql_store_chart_hash( - (uuid_t *)&hash_value, - st->chart_uuid, - st->type, - st->id, - st->name, - st->family, - st->context, - st->title, - st->units, - st->plugin_name, - st->module_name, - st->priority, - st->chart_type); -#else - UNUSED(st); -#endif - return; -} #define SQL_STORE_CLAIM_ID "insert into node_instance " \ "(host_id, claim_id, date_created) values (@host_id, @claim_id, unixepoch()) " \ @@ -2156,7 +863,6 @@ static inline void set_host_node_id(RRDHOST *host, uuid_t *node_id) sql_create_aclk_table(host, &host->host_uuid, node_id); else uuid_unparse_lower(*node_id, wc->node_id); - return; } #define SQL_UPDATE_NODE_ID "update node_instance set node_id = @node_id where host_id = @host_id;" @@ -2199,7 +905,7 @@ int update_node_id(uuid_t *host_id, uuid_t *node_id) char host_guid[GUID_LEN + 1]; uuid_unparse_lower(*host_id, host_guid); rrd_wrlock(); - host = rrdhost_find_by_guid(host_guid, 0); + host = rrdhost_find_by_guid(host_guid); if (likely(host)) set_host_node_id(host, node_id); rrd_unlock(); @@ -2242,7 +948,7 @@ char *get_hostname_by_node_id(char *node) goto failed; } - rc = sqlite3_step(res); + rc = sqlite3_step_monitored(res); if (likely(rc == SQLITE_ROW)) hostname = strdupz((char *)sqlite3_column_text(res, 0)); @@ -2280,7 +986,7 @@ int get_host_id(uuid_t *node_id, uuid_t *host_id) goto failed; } - rc = sqlite3_step(res); + rc = sqlite3_step_monitored(res); if (likely(rc == SQLITE_ROW && host_id)) uuid_copy(*host_id, *((uuid_t *) sqlite3_column_blob(res, 0))); @@ -2316,7 +1022,7 @@ int get_node_id(uuid_t *host_id, uuid_t *node_id) goto failed; } - rc = sqlite3_step(res); + rc = sqlite3_step_monitored(res); if (likely(rc == SQLITE_ROW && node_id)) uuid_copy(*node_id, *((uuid_t *) sqlite3_column_blob(res, 0))); @@ -2375,9 +1081,9 @@ failed: #define SQL_GET_NODE_INSTANCE_LIST "select ni.node_id, ni.host_id, h.hostname " \ "from node_instance ni, host h where ni.host_id = h.host_id;" -struct node_instance_list *get_node_list(void) +struct node_instance_list *get_node_list(void) { - struct node_instance_list *node_list = NULL; + struct node_instance_list *node_list = NULL; sqlite3_stmt *res = NULL; int rc; @@ -2395,7 +1101,7 @@ struct node_instance_list *get_node_list(void) int row = 0; char host_guid[37]; - while (sqlite3_step(res) == SQLITE_ROW) + while (sqlite3_step_monitored(res) == SQLITE_ROW) row++; if (sqlite3_reset(res) != SQLITE_OK) { @@ -2405,8 +1111,9 @@ struct node_instance_list *get_node_list(void) node_list = callocz(row + 1, sizeof(*node_list)); int max_rows = row; row = 0; + // TODO: Check to remove lock rrd_rdlock(); - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { if (sqlite3_column_bytes(res, 0) == sizeof(uuid_t)) uuid_copy(node_list[row].node_id, *((uuid_t *)sqlite3_column_blob(res, 0))); if (sqlite3_column_bytes(res, 1) == sizeof(uuid_t)) { @@ -2414,7 +1121,7 @@ struct node_instance_list *get_node_list(void) uuid_copy(node_list[row].host_id, *host_id); node_list[row].queryable = 1; uuid_unparse_lower(*host_id, host_guid); - RRDHOST *host = rrdhost_find_by_guid(host_guid, 0); + RRDHOST *host = rrdhost_find_by_guid(host_guid); node_list[row].live = host && (host == localhost || host->receiver) ? 1 : 0; node_list[row].hops = (host && host->system_info) ? host->system_info->hops : uuid_compare(*host_id, localhost->host_uuid) ? 1 : 0; @@ -2461,7 +1168,7 @@ void sql_load_node_id(RRDHOST *host) goto failed; } - rc = sqlite3_step(res); + rc = sqlite3_step_monitored(res); if (likely(rc == SQLITE_ROW)) { if (likely(sqlite3_column_bytes(res, 0) == sizeof(uuid_t))) set_host_node_id(host, (uuid_t *)sqlite3_column_blob(res, 0)); @@ -2472,8 +1179,6 @@ void sql_load_node_id(RRDHOST *host) failed: if (unlikely(sqlite3_reset(res) != SQLITE_OK)) error_report("Failed to reset the prepared statement when loading node instance information"); - - return; }; @@ -2494,213 +1199,179 @@ void sql_build_host_system_info(uuid_t *host_id, struct rrdhost_system_info *sys rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind host parameter host information"); - goto skip_loading; + goto skip; } - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { rrdhost_set_system_info_variable(system_info, (char *) sqlite3_column_text(res, 0), (char *) sqlite3_column_text(res, 1)); } -skip_loading: +skip: if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) error_report("Failed to finalize the prepared statement when reading host information"); - return; } +#define SELECT_HOST_LABELS "SELECT label_key, label_value, source_type FROM host_label WHERE host_id = @host_id " \ + "AND label_key IS NOT NULL AND label_value IS NOT NULL;" -#define SQL_INS_HOST_SYSTEM_INFO "INSERT OR REPLACE INTO host_info " \ - "(host_id, system_key, system_value, date_created) " \ - "VALUES (@host, @key, @value, unixepoch());" - -void sql_store_host_system_info_key_value(uuid_t *host_id, const char *name, const char *value) +DICTIONARY *sql_load_host_labels(uuid_t *host_id) { - sqlite3_stmt *res = NULL; int rc; - if (unlikely(!db_meta)) { - if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("Database has not been initialized"); - return; - } + DICTIONARY *labels = NULL; + sqlite3_stmt *res = NULL; - rc = sqlite3_prepare_v2(db_meta, SQL_INS_HOST_SYSTEM_INFO, -1, &res, 0); + rc = sqlite3_prepare_v2(db_meta, SELECT_HOST_LABELS, -1, &res, 0); if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to store system info"); - return; + error_report("Failed to prepare statement to read host information"); + return NULL; } rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host parameter to store system information"); - goto skip_store; + error_report("Failed to bind host parameter host information"); + goto skip; } - rc = sqlite3_bind_text(res, 2, name, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind label parameter to store name information"); - goto skip_store; - } + labels = rrdlabels_create(); - rc = sqlite3_bind_text(res, 3, value, -1, SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind value parameter to store value information"); - goto skip_store; + while (sqlite3_step_monitored(res) == SQLITE_ROW) { + rrdlabels_add( + labels, + (const char *)sqlite3_column_text(res, 0), + (const char *)sqlite3_column_text(res, 1), + sqlite3_column_int(res, 2)); } - rc = execute_insert(res); - if (unlikely(rc != SQLITE_DONE)) - error_report("Failed to store host system info, rc = %d", rc); - -skip_store: +skip: if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when storing host system information"); - - return; + error_report("Failed to finalize the prepared statement when reading host information"); + return labels; } - -void sql_store_host_system_info(uuid_t *host_id, const struct rrdhost_system_info *system_info) +// Utils +int bind_text_null(sqlite3_stmt *res, int position, const char *text, bool can_be_null) { - if (unlikely(!system_info)) - return; - - if (system_info->container_os_name) - sql_store_host_system_info_key_value(host_id, "NETDATA_CONTAINER_OS_NAME", system_info->container_os_name); - - if (system_info->container_os_id) - sql_store_host_system_info_key_value(host_id, "NETDATA_CONTAINER_OS_ID", system_info->container_os_id); - - if (system_info->container_os_id_like) - sql_store_host_system_info_key_value(host_id, "NETDATA_CONTAINER_OS_ID_LIKE", system_info->container_os_id_like); - - if (system_info->container_os_version) - sql_store_host_system_info_key_value(host_id, "NETDATA_CONTAINER_OS_VERSION", system_info->container_os_version); - - if (system_info->container_os_version_id) - sql_store_host_system_info_key_value(host_id, "NETDATA_CONTAINER_OS_VERSION_ID", system_info->container_os_version_id); - - if (system_info->host_os_detection) - sql_store_host_system_info_key_value(host_id, "NETDATA_CONTAINER_OS_DETECTION", system_info->host_os_detection); - - if (system_info->host_os_name) - sql_store_host_system_info_key_value(host_id, "NETDATA_HOST_OS_NAME", system_info->host_os_name); - - if (system_info->host_os_id) - sql_store_host_system_info_key_value(host_id, "NETDATA_HOST_OS_ID", system_info->host_os_id); - - if (system_info->host_os_id_like) - sql_store_host_system_info_key_value(host_id, "NETDATA_HOST_OS_ID_LIKE", system_info->host_os_id_like); - - if (system_info->host_os_version) - sql_store_host_system_info_key_value(host_id, "NETDATA_HOST_OS_VERSION", system_info->host_os_version); - - if (system_info->host_os_version_id) - sql_store_host_system_info_key_value(host_id, "NETDATA_HOST_OS_VERSION_ID", system_info->host_os_version_id); - - if (system_info->host_os_detection) - sql_store_host_system_info_key_value(host_id, "NETDATA_HOST_OS_DETECTION", system_info->host_os_detection); + if (likely(text)) + return sqlite3_bind_text(res, position, text, -1, SQLITE_STATIC); + if (!can_be_null) + return 1; + return sqlite3_bind_null(res, position); +} - if (system_info->kernel_name) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_KERNEL_NAME", system_info->kernel_name); +int sql_metadata_cache_stats(int op) +{ + int count, dummy; + sqlite3_db_status(db_meta, op, &count, &dummy, 0); + return count; +} - if (system_info->host_cores) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT", system_info->host_cores); +#define SQL_FIND_CHART_UUID \ + "SELECT chart_id FROM chart WHERE host_id = @host AND type=@type AND id=@id AND (name IS NULL OR name=@name) AND chart_id IS NOT NULL;" - if (system_info->host_cpu_freq) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_CPU_FREQ", system_info->host_cpu_freq); +#define SQL_FIND_DIMENSION_UUID \ + "SELECT dim_id FROM dimension WHERE chart_id=@chart AND id=@id AND name=@name AND LENGTH(dim_id)=16;" - if (system_info->host_ram_total) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_TOTAL_RAM", system_info->host_ram_total); - if (system_info->host_disk_space) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_TOTAL_DISK_SIZE", system_info->host_disk_space); +//Do a database lookup to find the UUID of a chart +//If found store it in store_uuid and return 0 +int sql_find_chart_uuid(RRDHOST *host, RRDSET *st, uuid_t *store_uuid) +{ + static __thread sqlite3_stmt *res = NULL; + int rc; - if (system_info->kernel_version) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_KERNEL_VERSION", system_info->kernel_version); + const char *name = string2str(st->parts.name); - if (system_info->architecture) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_ARCHITECTURE", system_info->architecture); + if (unlikely(!db_meta) && default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) + return 1; - if (system_info->virtualization) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_VIRTUALIZATION", system_info->virtualization); + if (unlikely(!res)) { + rc = prepare_statement(db_meta, SQL_FIND_CHART_UUID, &res); + if (rc != SQLITE_OK) { + error_report("Failed to prepare statement to lookup chart UUID in the database"); + return 1; + } + } - if (system_info->virt_detection) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_VIRT_DETECTION", system_info->virt_detection); + rc = sqlite3_bind_blob(res, 1, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; - if (system_info->container) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_CONTAINER", system_info->container); + rc = sqlite3_bind_text(res, 2, string2str(st->parts.type), -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; - if (system_info->container_detection) - sql_store_host_system_info_key_value(host_id, "NETDATA_SYSTEM_CONTAINER_DETECTION", system_info->container_detection); + rc = sqlite3_bind_text(res, 3, string2str(st->parts.id), -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; - if (system_info->is_k8s_node) - sql_store_host_system_info_key_value(host_id, "NETDATA_HOST_IS_K8S_NODE", system_info->is_k8s_node); + rc = sqlite3_bind_text(res, 4, name && *name ? name : string2str(st->parts.id), -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; - return; -} + int status = 1; + rc = sqlite3_step_monitored(res); + if (likely(rc == SQLITE_ROW)) { + uuid_copy(*store_uuid, sqlite3_column_blob(res, 0)); + status = 0; + } -static int save_host_label_callback(const char *name, const char *value, RRDLABEL_SRC label_source, void *data) -{ - RRDHOST *host = (RRDHOST *)data; - sql_store_host_label(&host->host_uuid, (int)label_source & ~(RRDLABEL_FLAG_INTERNAL), name, value); - return 0; -} + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement when searching for a chart UUID, rc = %d", rc); -#define SQL_DELETE_HOST_LABELS "DELETE FROM host_label WHERE host_id = @uuid;" -void sql_store_host_labels(RRDHOST *host) -{ - int rc = exec_statement_with_uuid(SQL_DELETE_HOST_LABELS, &host->host_uuid); - if (rc != SQLITE_OK) - error_report("Failed to remove old host labels for host %s", host->hostname); + return status; - rrdlabels_walkthrough_read(host->host_labels, save_host_label_callback, host); +bind_fail: + error_report("Failed to bind input parameter to perform chart UUID database lookup, rc = %d", rc); + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement when searching for a chart UUID, rc = %d", rc); + return 1; } -#define SELECT_HOST_LABELS "SELECT label_key, label_value, source_type FROM host_label WHERE host_id = @host_id " \ - "AND label_key IS NOT NULL AND label_value IS NOT NULL;" - -DICTIONARY *sql_load_host_labels(uuid_t *host_id) +int sql_find_dimension_uuid(RRDSET *st, RRDDIM *rd, uuid_t *store_uuid) { + static __thread sqlite3_stmt *res = NULL; int rc; + int status = 1; - DICTIONARY *labels = NULL; - sqlite3_stmt *res = NULL; + if (unlikely(!db_meta) && default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) + return 1; - rc = sqlite3_prepare_v2(db_meta, SELECT_HOST_LABELS, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to prepare statement to read host information"); - return NULL; + if (unlikely(!res)) { + rc = prepare_statement(db_meta, SQL_FIND_DIMENSION_UUID, &res); + if (rc != SQLITE_OK) { + error_report("Failed to bind prepare statement to lookup dimension UUID in the database"); + return 1; + } } - rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); - if (unlikely(rc != SQLITE_OK)) { - error_report("Failed to bind host parameter host information"); - goto skip_loading; - } + rc = sqlite3_bind_blob(res, 1, st->chart_uuid, sizeof(*st->chart_uuid), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; - labels = rrdlabels_create(); + rc = sqlite3_bind_text(res, 2, rrddim_id(rd), -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; - while (sqlite3_step(res) == SQLITE_ROW) { - rrdlabels_add( - labels, - (const char *)sqlite3_column_text(res, 0), - (const char *)sqlite3_column_text(res, 1), - sqlite3_column_int(res, 2)); + rc = sqlite3_bind_text(res, 3, rrddim_name(rd), -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_step_monitored(res); + if (likely(rc == SQLITE_ROW)) { + uuid_copy(*store_uuid, *((uuid_t *) sqlite3_column_blob(res, 0))); + status = 0; } -skip_loading: - if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("Failed to finalize the prepared statement when reading host information"); - return labels; -} + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement find dimension uuid, rc = %d", rc); + return status; -// Utils -int bind_text_null(sqlite3_stmt *res, int position, const char *text, bool can_be_null) -{ - if (likely(text)) - return sqlite3_bind_text(res, position, text, -1, SQLITE_STATIC); - if (!can_be_null) - return 1; - return sqlite3_bind_null(res, position); +bind_fail: + error_report("Failed to bind input parameter to perform dimension UUID database lookup, rc = %d", rc); + return 1; } diff --git a/database/sqlite/sqlite_functions.h b/database/sqlite/sqlite_functions.h index e6808aa81..5731d5c9e 100644 --- a/database/sqlite/sqlite_functions.h +++ b/database/sqlite/sqlite_functions.h @@ -25,29 +25,7 @@ typedef enum db_check_action_type { } db_check_action_type_t; #define SQL_MAX_RETRY (100) -#define SQLITE_INSERT_DELAY (50) // Insert delay in case of lock - -#define SQL_STORE_HOST "insert or replace into host (host_id,hostname,registry_hostname,update_every,os,timezone,tags, hops) " \ - "values (?1,?2,?3,?4,?5,?6,?7,?8);" - -#define SQL_STORE_CHART "insert or replace into chart (chart_id, host_id, type, id, " \ - "name, family, context, title, unit, plugin, module, priority, update_every , chart_type , memory_mode , " \ - "history_entries) values (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,?14,?15,?16);" - -#define SQL_FIND_CHART_UUID \ - "select chart_id from chart where host_id = @host and type=@type and id=@id and (name is null or name=@name);" - -#define SQL_STORE_ACTIVE_CHART \ - "insert or replace into chart_active (chart_id, date_created) values (@id, unixepoch());" - -#define SQL_STORE_DIMENSION \ - "INSERT OR REPLACE into dimension (dim_id, chart_id, id, name, multiplier, divisor , algorithm) values (?0001,?0002,?0003,?0004,?0005,?0006,?0007);" - -#define SQL_FIND_DIMENSION_UUID \ - "select dim_id from dimension where chart_id=@chart and id=@id and name=@name and length(dim_id)=16;" - -#define SQL_STORE_ACTIVE_DIMENSION \ - "insert or replace into dimension_active (dim_id, date_created) values (@id, unixepoch());" +#define SQLITE_INSERT_DELAY (10) // Insert delay in case of lock #define CHECK_SQLITE_CONNECTION(db_meta) \ if (unlikely(!db_meta)) { \ @@ -58,59 +36,51 @@ typedef enum db_check_action_type { return 1; \ } -extern int sql_init_database(db_check_action_type_t rebuild, int memory); -extern void sql_close_database(void); -extern int bind_text_null(sqlite3_stmt *res, int position, const char *text, bool can_be_null); -extern int sql_store_host(uuid_t *guid, const char *hostname, const char *registry_hostname, int update_every, const char *os, - const char *timezone, const char *tags, int hops); - -extern int sql_store_host_info(RRDHOST *host); - -extern int sql_store_chart( - uuid_t *chart_uuid, uuid_t *host_uuid, const char *type, const char *id, const char *name, const char *family, - const char *context, const char *title, const char *units, const char *plugin, const char *module, long priority, - int update_every, int chart_type, int memory_mode, long history_entries); -extern int sql_store_dimension(uuid_t *dim_uuid, uuid_t *chart_uuid, const char *id, const char *name, collected_number multiplier, - collected_number divisor, int algorithm); - -extern int find_dimension_uuid(RRDSET *st, RRDDIM *rd, uuid_t *store_uuid); -extern void store_active_dimension(uuid_t *dimension_uuid); - -extern uuid_t *find_chart_uuid(RRDHOST *host, const char *type, const char *id, const char *name); -extern uuid_t *create_chart_uuid(RRDSET *st, const char *id, const char *name); -extern int update_chart_metadata(uuid_t *chart_uuid, RRDSET *st, const char *id, const char *name); -extern void store_active_chart(uuid_t *dimension_uuid); - -extern int find_uuid_type(uuid_t *uuid); - -extern void sql_rrdset2json(RRDHOST *host, BUFFER *wb); - -extern RRDHOST *sql_create_host_by_uuid(char *guid); -extern int prepare_statement(sqlite3 *database, char *query, sqlite3_stmt **statement); -extern int execute_insert(sqlite3_stmt *res); -extern void db_execute(const char *cmd); -extern int file_is_migrated(char *path); -extern void add_migrated_file(char *path, uint64_t file_size); -extern void db_unlock(void); -extern void db_lock(void); -extern void delete_dimension_uuid(uuid_t *dimension_uuid); -extern void sql_store_chart_label(uuid_t *chart_uuid, int source_type, char *label, char *value); -extern void sql_build_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list, RRDHOST *host, char *context, char *chart); -extern void store_claim_id(uuid_t *host_id, uuid_t *claim_id); -extern int update_node_id(uuid_t *host_id, uuid_t *node_id); -extern int get_node_id(uuid_t *host_id, uuid_t *node_id); -extern int get_host_id(uuid_t *node_id, uuid_t *host_id); -extern void invalidate_node_instances(uuid_t *host_id, uuid_t *claim_id); -extern struct node_instance_list *get_node_list(void); -extern void sql_load_node_id(RRDHOST *host); -extern void compute_chart_hash(RRDSET *st); -extern int sql_set_dimension_option(uuid_t *dim_uuid, char *option); -char *get_hostname_by_node_id(char *node_id); -void free_temporary_host(RRDHOST *host); +SQLITE_API int sqlite3_step_monitored(sqlite3_stmt *stmt); +SQLITE_API int sqlite3_exec_monitored( + sqlite3 *db, /* An open database */ + const char *sql, /* SQL to be evaluated */ + int (*callback)(void*,int,char**,char**), /* Callback function */ + void *data, /* 1st argument to callback */ + char **errmsg /* Error msg written here */ + ); + +// Initialization and shutdown int init_database_batch(sqlite3 *database, int rebuild, int init_type, const char *batch[]); -void migrate_localhost(uuid_t *host_uuid); -extern void sql_store_host_system_info(uuid_t *host_id, const struct rrdhost_system_info *system_info); -extern void sql_build_host_system_info(uuid_t *host_id, struct rrdhost_system_info *system_info); -void sql_store_host_labels(RRDHOST *host); +int sql_init_database(db_check_action_type_t rebuild, int memory); +void sql_close_database(void); + +// Helpers +int bind_text_null(sqlite3_stmt *res, int position, const char *text, bool can_be_null); +int prepare_statement(sqlite3 *database, const char *query, sqlite3_stmt **statement); +int execute_insert(sqlite3_stmt *res); +int file_is_migrated(char *path); +int exec_statement_with_uuid(const char *sql, uuid_t *uuid); +void add_migrated_file(char *path, uint64_t file_size); +void db_execute(const char *cmd); + +// Look up functions +int get_node_id(uuid_t *host_id, uuid_t *node_id); +int get_host_id(uuid_t *node_id, uuid_t *host_id); +struct node_instance_list *get_node_list(void); +void sql_load_node_id(RRDHOST *host); +char *get_hostname_by_node_id(char *node_id); +int sql_find_chart_uuid(RRDHOST *host, RRDSET *st, uuid_t *store_uuid); +int sql_find_dimension_uuid(RRDSET *st, RRDDIM *rd, uuid_t *store_uuid); + +// Help build archived hosts in memory when agent starts +void sql_build_host_system_info(uuid_t *host_id, struct rrdhost_system_info *system_info); DICTIONARY *sql_load_host_labels(uuid_t *host_id); + +// For queries: To be removed when context queries are implemented +void sql_rrdset2json(RRDHOST *host, BUFFER *wb); + +// TODO: move to metadata +int update_node_id(uuid_t *host_id, uuid_t *node_id); + +void invalidate_node_instances(uuid_t *host_id, uuid_t *claim_id); + +// Provide statistics +int sql_metadata_cache_stats(int op); + #endif //NETDATA_SQLITE_FUNCTIONS_H diff --git a/database/sqlite/sqlite_health.c b/database/sqlite/sqlite_health.c index 8e59cad1e..c189305b8 100644 --- a/database/sqlite/sqlite_health.c +++ b/database/sqlite/sqlite_health.c @@ -4,6 +4,7 @@ #include "sqlite_functions.h" #define MAX_HEALTH_SQL_SIZE 2048 +#define sqlite3_bind_string_or_null(res,key,param) ((key) ? sqlite3_bind_text(res, param, string2str(key), -1, SQLITE_STATIC) : sqlite3_bind_null(res, param)) /* Health related SQL queries Creates a health log table in sqlite, one per host guid @@ -15,7 +16,7 @@ int sql_create_health_log_table(RRDHOST *host) { if (unlikely(!db_meta)) { if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("HEALTH [%s]: Database has not been initialized", host->hostname); + error_report("HEALTH [%s]: Database has not been initialized", rrdhost_hostname(host)); return 1; } @@ -24,9 +25,9 @@ int sql_create_health_log_table(RRDHOST *host) { snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_CREATE_HEALTH_LOG_TABLE(uuid_str)); - rc = sqlite3_exec(db_meta, command, 0, 0, &err_msg); + rc = sqlite3_exec_monitored(db_meta, command, 0, 0, &err_msg); if (rc != SQLITE_OK) { - error_report("HEALTH [%s]: SQLite error during creation of health log table, rc = %d (%s)", host->hostname, rc, err_msg); + error_report("HEALTH [%s]: SQLite error during creation of health log table, rc = %d (%s)", rrdhost_hostname(host), rc, err_msg); sqlite3_free(err_msg); return 1; } @@ -49,7 +50,7 @@ void sql_health_alarm_log_update(RRDHOST *host, ALARM_ENTRY *ae) { if (unlikely(!db_meta)) { if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("HEALTH [%s]: Database has not been initialized", host->hostname); + error_report("HEALTH [%s]: Database has not been initialized", rrdhost_hostname(host)); return; } @@ -60,7 +61,7 @@ void sql_health_alarm_log_update(RRDHOST *host, ALARM_ENTRY *ae) { rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0); if (unlikely(rc != SQLITE_OK)) { - error_report("HEALTH [%s]: Failed to prepare statement for SQL_UPDATE_HEALTH_LOG", host->hostname); + error_report("HEALTH [%s]: Failed to prepare statement for SQL_UPDATE_HEALTH_LOG", rrdhost_hostname(host)); return; } @@ -96,12 +97,12 @@ void sql_health_alarm_log_update(RRDHOST *host, ALARM_ENTRY *ae) { rc = execute_insert(res); if (unlikely(rc != SQLITE_DONE)) { - error_report("HEALTH [%s]: Failed to update health log, rc = %d", host->hostname, rc); + error_report("HEALTH [%s]: Failed to update health log, rc = %d", rrdhost_hostname(host), rc); } failed: if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("HEALTH [%s]: Failed to finalize the prepared statement for updating health log.", host->hostname); + error_report("HEALTH [%s]: Failed to finalize the prepared statement for updating health log.", rrdhost_hostname(host)); return; } @@ -122,7 +123,7 @@ void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae) { if (unlikely(!db_meta)) { if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("HEALTH [%s]: Database has not been initialized", host->hostname); + error_report("HEALTH [%s]: Database has not been initialized", rrdhost_hostname(host)); return; } @@ -133,11 +134,11 @@ void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae) { rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0); if (unlikely(rc != SQLITE_OK)) { - error_report("HEALTH [%s]: Failed to prepare statement for SQL_INSERT_HEALTH_LOG", host->hostname); + error_report("HEALTH [%s]: Failed to prepare statement for SQL_INSERT_HEALTH_LOG", rrdhost_hostname(host)); return; } - rc = sqlite3_bind_text(res, 1, host->hostname, -1, SQLITE_STATIC); + rc = sqlite3_bind_text(res, 1, rrdhost_hostname(host), -1, SQLITE_STATIC); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind hostname parameter for SQL_INSERT_HEALTH_LOG"); goto failed; @@ -215,49 +216,49 @@ void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae) { goto failed; } - rc = sqlite3_bind_text(res, 14, ae->name, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->name, 14); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind name parameter for SQL_INSERT_HEALTH_LOG"); goto failed; } - rc = sqlite3_bind_text(res, 15, ae->chart, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->chart, 15); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind chart parameter for SQL_INSERT_HEALTH_LOG"); goto failed; } - rc = sqlite3_bind_text(res, 16, ae->family, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->family, 16); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind family parameter for SQL_INSERT_HEALTH_LOG"); goto failed; } - rc = sqlite3_bind_text(res, 17, ae->exec, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->exec, 17); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind exec parameter for SQL_INSERT_HEALTH_LOG"); goto failed; } - rc = sqlite3_bind_text(res, 18, ae->recipient, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->recipient, 18); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind recipient parameter for SQL_INSERT_HEALTH_LOG"); goto failed; } - rc = sqlite3_bind_text(res, 19, ae->source, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->source, 19); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind source parameter for SQL_INSERT_HEALTH_LOG"); goto failed; } - rc = sqlite3_bind_text(res, 20, ae->units, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->units, 20); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind host_id parameter to store node instance information"); goto failed; } - rc = sqlite3_bind_text(res, 21, ae->info, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->info, 21); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind info parameter for SQL_INSERT_HEALTH_LOG"); goto failed; @@ -305,25 +306,25 @@ void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae) { goto failed; } - rc = sqlite3_bind_text(res, 29, ae->classification, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->classification, 29); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind classification parameter for SQL_INSERT_HEALTH_LOG"); goto failed; } - rc = sqlite3_bind_text(res, 30, ae->component, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->component, 30); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind component parameter for SQL_INSERT_HEALTH_LOG"); goto failed; } - rc = sqlite3_bind_text(res, 31, ae->type, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->type, 31); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind type parameter for SQL_INSERT_HEALTH_LOG"); goto failed; } - rc = sqlite3_bind_text(res, 32, ae->chart_context, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, ae->chart_context, 32); if (unlikely(rc != SQLITE_OK)) { error_report("Failed to bind chart_context parameter for SQL_INSERT_HEALTH_LOG"); goto failed; @@ -331,7 +332,7 @@ void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae) { rc = execute_insert(res); if (unlikely(rc != SQLITE_DONE)) { - error_report("HEALTH [%s]: Failed to execute SQL_INSERT_HEALTH_LOG, rc = %d", host->hostname, rc); + error_report("HEALTH [%s]: Failed to execute SQL_INSERT_HEALTH_LOG, rc = %d", rrdhost_hostname(host), rc); goto failed; } @@ -340,7 +341,7 @@ void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae) { failed: if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) - error_report("HEALTH [%s]: Failed to finalize the prepared statement for inserting to health log.", host->hostname); + error_report("HEALTH [%s]: Failed to finalize the prepared statement for inserting to health log.", rrdhost_hostname(host)); return; } @@ -381,7 +382,7 @@ void sql_health_alarm_log_cleanup(RRDHOST *host) { char uuid_str[GUID_LEN + 1]; uuid_unparse_lower_fix(&host->host_uuid, uuid_str); - snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_CLEANUP_HEALTH_LOG(uuid_str, uuid_str, host->health_log_entries_written - rotate_every)); + snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_CLEANUP_HEALTH_LOG(uuid_str, uuid_str, (unsigned long int) (host->health_log_entries_written - rotate_every))); rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0); if (unlikely(rc != SQLITE_OK)) { @@ -389,7 +390,7 @@ void sql_health_alarm_log_cleanup(RRDHOST *host) { return; } - rc = sqlite3_step(res); + rc = sqlite3_step_monitored(res); if (unlikely(rc != SQLITE_DONE)) error_report("Failed to cleanup health log table, rc = %d", rc); @@ -428,7 +429,7 @@ void sql_health_alarm_log_count(RRDHOST *host) { return; } - rc = sqlite3_step(res); + rc = sqlite3_step_monitored(res); if (likely(rc == SQLITE_ROW)) host->health_log_entries_written = (size_t) sqlite3_column_int64(res, 0); @@ -436,7 +437,7 @@ void sql_health_alarm_log_count(RRDHOST *host) { if (unlikely(rc != SQLITE_OK)) error_report("Failed to finalize the prepared statement to count health log entries from db"); - info("HEALTH [%s]: Table health_log_%s, contains %lu entries.", host->hostname, uuid_str, host->health_log_entries_written); + info("HEALTH [%s]: Table health_log_%s, contains %lu entries.", rrdhost_hostname(host), uuid_str, (unsigned long int) host->health_log_entries_written); } #define SQL_INJECT_REMOVED(guid, guid2) "insert into health_log_%s (hostname, unique_id, alarm_id, alarm_event_id, config_hash_id, updated_by_id, updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, " \ @@ -556,7 +557,7 @@ uint32_t sql_get_max_unique_id (char *uuid_str) return 0; } - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { max_unique_id = (uint32_t) sqlite3_column_int64(res, 0); } @@ -584,7 +585,7 @@ void sql_check_removed_alerts_state(char *uuid_str) return; } - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { status = (RRDCALC_STATUS) sqlite3_column_int(res, 0); unique_id = (uint32_t) sqlite3_column_int64(res, 1); alarm_id = (uint32_t) sqlite3_column_int64(res, 2); @@ -607,7 +608,7 @@ void sql_check_removed_alerts_state(char *uuid_str) #define SQL_LOAD_HEALTH_LOG(guid,limit) "SELECT hostname, unique_id, alarm_id, alarm_event_id, config_hash_id, updated_by_id, updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, name, chart, family, exec, recipient, source, units, info, exec_code, new_status, old_status, delay, new_value, old_value, last_repeat, class, component, type, chart_context FROM (SELECT hostname, unique_id, alarm_id, alarm_event_id, config_hash_id, updated_by_id, updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, name, chart, family, exec, recipient, source, units, info, exec_code, new_status, old_status, delay, new_value, old_value, last_repeat, class, component, type, chart_context FROM health_log_%s order by unique_id desc limit %u) order by unique_id asc;", guid, limit void sql_health_alarm_log_load(RRDHOST *host) { sqlite3_stmt *res = NULL; - int rc; + int ret; ssize_t errored = 0, loaded = 0; char command[MAX_HEALTH_SQL_SIZE + 1]; @@ -615,7 +616,7 @@ void sql_health_alarm_log_load(RRDHOST *host) { if (unlikely(!db_meta)) { if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) - error_report("HEALTH [%s]: Database has not been initialized", host->hostname); + error_report("HEALTH [%s]: Database has not been initialized", rrdhost_hostname(host)); return; } @@ -626,47 +627,55 @@ void sql_health_alarm_log_load(RRDHOST *host) { snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_LOAD_HEALTH_LOG(uuid_str, host->health_log.max)); - rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0); - if (unlikely(rc != SQLITE_OK)) { - error_report("HEALTH [%s]: Failed to prepare sql statement to load health log.", host->hostname); + ret = sqlite3_prepare_v2(db_meta, command, -1, &res, 0); + if (unlikely(ret != SQLITE_OK)) { + error_report("HEALTH [%s]: Failed to prepare sql statement to load health log.", rrdhost_hostname(host)); return; } + DICTIONARY *all_rrdcalcs = dictionary_create( + DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE); + RRDCALC *rc; + foreach_rrdcalc_in_rrdhost_read(host, rc) { + dictionary_set(all_rrdcalcs, rrdcalc_name(rc), rc, sizeof(*rc)); + } + foreach_rrdcalc_in_rrdhost_done(rc); + netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock); - while (sqlite3_step(res) == SQLITE_ROW) { + while (sqlite3_step_monitored(res) == SQLITE_ROW) { ALARM_ENTRY *ae = NULL; // check that we have valid ids uint32_t unique_id = (uint32_t) sqlite3_column_int64(res, 1); if(!unique_id) { - error_report("HEALTH [%s]: Got invalid unique id. Ignoring it.", host->hostname); + error_report("HEALTH [%s]: Got invalid unique id. Ignoring it.", rrdhost_hostname(host)); errored++; continue; } uint32_t alarm_id = (uint32_t) sqlite3_column_int64(res, 2); if(!alarm_id) { - error_report("HEALTH [%s]: Got invalid alarm id. Ignoring it.", host->hostname); + error_report("HEALTH [%s]: Got invalid alarm id. Ignoring it.", rrdhost_hostname(host)); errored++; continue; } //need name, chart and family if (sqlite3_column_type(res, 13) == SQLITE_NULL) { - error_report("HEALTH [%s]: Got null name field. Ignoring it.", host->hostname); + error_report("HEALTH [%s]: Got null name field. Ignoring it.", rrdhost_hostname(host)); errored++; continue; } if (sqlite3_column_type(res, 14) == SQLITE_NULL) { - error_report("HEALTH [%s]: Got null chart field. Ignoring it.", host->hostname); + error_report("HEALTH [%s]: Got null chart field. Ignoring it.", rrdhost_hostname(host)); errored++; continue; } if (sqlite3_column_type(res, 15) == SQLITE_NULL) { - error_report("HEALTH [%s]: Got null family field. Ignoring it.", host->hostname); + error_report("HEALTH [%s]: Got null family field. Ignoring it.", rrdhost_hostname(host)); errored++; continue; } @@ -675,18 +684,7 @@ void sql_health_alarm_log_load(RRDHOST *host) { time_t last_repeat = 0; last_repeat = (time_t)sqlite3_column_int64(res, 27); - RRDCALC *rc = alarm_max_last_repeat(host, (char *) sqlite3_column_text(res, 14), simple_hash((char *) sqlite3_column_text(res, 14))); - if (!rc) { - for(rc = host->alarms; rc ; rc = rc->next) { - RRDCALC *rdcmp = (RRDCALC *) avl_insert_lock(&(host)->alarms_idx_name, (avl_t *)rc); - if(rdcmp != rc) { - error("Cannot insert the alarm index ID using log %s", rc->name); - } - } - - rc = alarm_max_last_repeat(host, (char *) sqlite3_column_text(res, 14), simple_hash((char *) sqlite3_column_text(res, 14))); - } - + rc = dictionary_get(all_rrdcalcs, (char *) sqlite3_column_text(res, 14)); if(unlikely(rc)) { if (rrdcalc_isrepeating(rc)) { rc->last_repeat = last_repeat; @@ -719,36 +717,32 @@ void sql_health_alarm_log_load(RRDHOST *host) { ae->exec_run_timestamp = (time_t) sqlite3_column_int64(res, 11); ae->delay_up_to_timestamp = (time_t) sqlite3_column_int64(res, 12); - ae->name = strdupz((char *) sqlite3_column_text(res, 13)); - ae->hash_name = simple_hash(ae->name); - - ae->chart = strdupz((char *) sqlite3_column_text(res, 14)); - ae->hash_chart = simple_hash(ae->chart); - - ae->family = strdupz((char *) sqlite3_column_text(res, 15)); + ae->name = string_strdupz((char *) sqlite3_column_text(res, 13)); + ae->chart = string_strdupz((char *) sqlite3_column_text(res, 14)); + ae->family = string_strdupz((char *) sqlite3_column_text(res, 15)); if (sqlite3_column_type(res, 16) != SQLITE_NULL) - ae->exec = strdupz((char *) sqlite3_column_text(res, 16)); + ae->exec = string_strdupz((char *) sqlite3_column_text(res, 16)); else ae->exec = NULL; if (sqlite3_column_type(res, 17) != SQLITE_NULL) - ae->recipient = strdupz((char *) sqlite3_column_text(res, 17)); + ae->recipient = string_strdupz((char *) sqlite3_column_text(res, 17)); else ae->recipient = NULL; if (sqlite3_column_type(res, 18) != SQLITE_NULL) - ae->source = strdupz((char *) sqlite3_column_text(res, 18)); + ae->source = string_strdupz((char *) sqlite3_column_text(res, 18)); else ae->source = NULL; if (sqlite3_column_type(res, 19) != SQLITE_NULL) - ae->units = strdupz((char *) sqlite3_column_text(res, 19)); + ae->units = string_strdupz((char *) sqlite3_column_text(res, 19)); else ae->units = NULL; if (sqlite3_column_type(res, 20) != SQLITE_NULL) - ae->info = strdupz((char *) sqlite3_column_text(res, 20)); + ae->info = string_strdupz((char *) sqlite3_column_text(res, 20)); else ae->info = NULL; @@ -763,30 +757,30 @@ void sql_health_alarm_log_load(RRDHOST *host) { ae->last_repeat = last_repeat; if (sqlite3_column_type(res, 28) != SQLITE_NULL) - ae->classification = strdupz((char *) sqlite3_column_text(res, 28)); + ae->classification = string_strdupz((char *) sqlite3_column_text(res, 28)); else ae->classification = NULL; if (sqlite3_column_type(res, 29) != SQLITE_NULL) - ae->component = strdupz((char *) sqlite3_column_text(res, 29)); + ae->component = string_strdupz((char *) sqlite3_column_text(res, 29)); else ae->component = NULL; if (sqlite3_column_type(res, 30) != SQLITE_NULL) - ae->type = strdupz((char *) sqlite3_column_text(res, 30)); + ae->type = string_strdupz((char *) sqlite3_column_text(res, 30)); else ae->type = NULL; if (sqlite3_column_type(res, 31) != SQLITE_NULL) - ae->chart_context = strdupz((char *) sqlite3_column_text(res, 31)); + ae->chart_context = string_strdupz((char *) sqlite3_column_text(res, 31)); else ae->chart_context = NULL; char value_string[100 + 1]; - freez(ae->old_value_string); - freez(ae->new_value_string); - ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1)); - ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1)); + string_freez(ae->old_value_string); + string_freez(ae->new_value_string); + ae->old_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae_units(ae), -1)); + ae->new_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae_units(ae), -1)); ae->next = host->health_log.alarms; host->health_log.alarms = ae; @@ -802,6 +796,9 @@ void sql_health_alarm_log_load(RRDHOST *host) { netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); + dictionary_destroy(all_rrdcalcs); + all_rrdcalcs = NULL; + if(!host->health_max_unique_id) host->health_max_unique_id = (uint32_t)now_realtime_sec(); if(!host->health_max_alarm_id) host->health_max_alarm_id = (uint32_t)now_realtime_sec(); @@ -809,10 +806,10 @@ void sql_health_alarm_log_load(RRDHOST *host) { if (unlikely(!host->health_log.next_alarm_id || host->health_log.next_alarm_id <= host->health_max_alarm_id)) host->health_log.next_alarm_id = host->health_max_alarm_id + 1; - info("HEALTH [%s]: Table health_log_%s, loaded %zd alarm entries, errors in %zd entries.", host->hostname, uuid_str, loaded, errored); + log_health("[%s]: Table health_log_%s, loaded %zd alarm entries, errors in %zd entries.", rrdhost_hostname(host), uuid_str, loaded, errored); - rc = sqlite3_finalize(res); - if (unlikely(rc != SQLITE_OK)) + ret = sqlite3_finalize(res); + if (unlikely(ret != SQLITE_OK)) error_report("Failed to finalize the health log read statement"); sql_health_alarm_log_count(host); @@ -849,159 +846,153 @@ int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg) } param++; - rc = sqlite3_bind_blob(res, 1, hash_id, sizeof(*hash_id), SQLITE_STATIC); + rc = sqlite3_bind_blob(res, param, hash_id, sizeof(*hash_id), SQLITE_STATIC); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - if (cfg->alarm && *cfg->alarm) - rc = sqlite3_bind_text(res, 2, cfg->alarm, -1, SQLITE_STATIC); - else - rc = sqlite3_bind_null(res, 2); + rc = sqlite3_bind_string_or_null(res, cfg->alarm, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - if (cfg->template_key && *cfg->template_key) - rc = sqlite3_bind_text(res, 3, cfg->template_key, -1, SQLITE_STATIC); - else - rc = sqlite3_bind_null(res, 3); + rc = sqlite3_bind_string_or_null(res, cfg->template_key, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 4, cfg->on, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->on, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 5, cfg->classification, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->classification, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 6, cfg->component, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->component, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 7, cfg->type, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->type, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 8, cfg->os, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->os, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 9, cfg->host, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->host, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 10, cfg->lookup, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->lookup, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 11, cfg->every, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->every, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 12, cfg->units, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->units, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 13, cfg->calc, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->calc, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 14, cfg->families, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->families, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 15, cfg->plugin, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->plugin, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 16, cfg->module, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->module, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 17, cfg->charts, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->charts, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 18, cfg->green, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->green, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 19, cfg->red, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->red, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 20, cfg->warn, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->warn, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 21, cfg->crit, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->crit, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 22, cfg->exec, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->exec, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 23, cfg->to, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->to, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 24, cfg->info, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->info, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 25, cfg->delay, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->delay, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 26, cfg->options, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->options, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 27, cfg->repeat, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->repeat, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 28, cfg->host_labels, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->host_labels, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; if (cfg->p_db_lookup_after) { param++; - rc = sqlite3_bind_text(res, 29, cfg->p_db_lookup_dimensions, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->p_db_lookup_dimensions, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; param++; - rc = sqlite3_bind_text(res, 30, cfg->p_db_lookup_method, -1, SQLITE_STATIC); + rc = sqlite3_bind_string_or_null(res, cfg->p_db_lookup_method, param); if (unlikely(rc != SQLITE_OK)) goto bind_fail; @@ -1071,7 +1062,7 @@ int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg) skip hash calculations */ #if !defined DISABLE_CLOUD && defined ENABLE_HTTPS -#define DIGEST_ALERT_CONFIG_VAL(v) ((v) ? EVP_DigestUpdate(evpctx, (v), strlen((v))) : EVP_DigestUpdate(evpctx, "", 1)) +#define DIGEST_ALERT_CONFIG_VAL(v) ((v) ? EVP_DigestUpdate(evpctx, (string2str(v)), string_strlen((v))) : EVP_DigestUpdate(evpctx, "", 1)) #endif int alert_hash_and_store_config( uuid_t hash_id, diff --git a/database/sqlite/sqlite_health.h b/database/sqlite/sqlite_health.h index ef837894a..87060dacc 100644 --- a/database/sqlite/sqlite_health.h +++ b/database/sqlite/sqlite_health.h @@ -6,12 +6,12 @@ #include "sqlite3.h" extern sqlite3 *db_meta; -extern void sql_health_alarm_log_load(RRDHOST *host); -extern int sql_create_health_log_table(RRDHOST *host); -extern void sql_health_alarm_log_update(RRDHOST *host, ALARM_ENTRY *ae); -extern void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae); -extern void sql_health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae); -extern void sql_health_alarm_log_cleanup(RRDHOST *host); -extern int alert_hash_and_store_config(uuid_t hash_id, struct alert_config *cfg, int store_hash); -extern void sql_aclk_alert_clean_dead_entries(RRDHOST *host); +void sql_health_alarm_log_load(RRDHOST *host); +int sql_create_health_log_table(RRDHOST *host); +void sql_health_alarm_log_update(RRDHOST *host, ALARM_ENTRY *ae); +void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae); +void sql_health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae); +void sql_health_alarm_log_cleanup(RRDHOST *host); +int alert_hash_and_store_config(uuid_t hash_id, struct alert_config *cfg, int store_hash); +void sql_aclk_alert_clean_dead_entries(RRDHOST *host); #endif //NETDATA_SQLITE_HEALTH_H diff --git a/database/sqlite/sqlite_metadata.c b/database/sqlite/sqlite_metadata.c new file mode 100644 index 000000000..4eb212152 --- /dev/null +++ b/database/sqlite/sqlite_metadata.c @@ -0,0 +1,1580 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "sqlite_metadata.h" + +// SQL statements + +#define SQL_STORE_CLAIM_ID "insert into node_instance " \ + "(host_id, claim_id, date_created) values (@host_id, @claim_id, unixepoch()) " \ + "on conflict(host_id) do update set claim_id = excluded.claim_id;" + +#define SQL_DELETE_HOST_LABELS "DELETE FROM host_label WHERE host_id = @uuid;" + +#define STORE_HOST_LABEL \ + "INSERT OR REPLACE INTO host_label (host_id, source_type, label_key, label_value, date_created) VALUES " + +#define STORE_CHART_LABEL \ + "INSERT OR REPLACE INTO chart_label (chart_id, source_type, label_key, label_value, date_created) VALUES " + +#define STORE_HOST_OR_CHART_LABEL_VALUE "(u2h('%s'), %d,'%s','%s', unixepoch())" + +#define DELETE_DIMENSION_UUID "DELETE FROM dimension WHERE dim_id = @uuid;" + +#define SQL_STORE_HOST_INFO "INSERT OR REPLACE INTO host " \ + "(host_id, hostname, registry_hostname, update_every, os, timezone," \ + "tags, hops, memory_mode, abbrev_timezone, utc_offset, program_name, program_version," \ + "entries, health_enabled) " \ + "values (@host_id, @hostname, @registry_hostname, @update_every, @os, @timezone, @tags, @hops, @memory_mode, " \ + "@abbrev_timezone, @utc_offset, @program_name, @program_version, " \ + "@entries, @health_enabled);" + +#define SQL_STORE_CHART "insert or replace into chart (chart_id, host_id, type, id, " \ + "name, family, context, title, unit, plugin, module, priority, update_every , chart_type , memory_mode , " \ + "history_entries) values (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,?14,?15,?16);" + +#define SQL_STORE_DIMENSION "INSERT OR REPLACE INTO dimension (dim_id, chart_id, id, name, multiplier, divisor , algorithm, options) " \ + "VALUES (@dim_id, @chart_id, @id, @name, @multiplier, @divisor, @algorithm, @options);" + +#define SELECT_DIMENSION_LIST "SELECT dim_id, rowid FROM dimension WHERE rowid > @row_id" + +#define STORE_HOST_INFO "INSERT OR REPLACE INTO host_info (host_id, system_key, system_value, date_created) VALUES " +#define STORE_HOST_INFO_VALUES "(u2h('%s'), '%s','%s', unixepoch())" + +#define MIGRATE_LOCALHOST_TO_NEW_MACHINE_GUID \ + "UPDATE chart SET host_id = @host_id WHERE host_id in (SELECT host_id FROM host where host_id <> @host_id and hops = 0);" +#define DELETE_NON_EXISTING_LOCALHOST "DELETE FROM host WHERE hops = 0 AND host_id <> @host_id;" +#define DELETE_MISSING_NODE_INSTANCES "DELETE FROM node_instance WHERE host_id NOT IN (SELECT host_id FROM host);" + +#define METADATA_CMD_Q_MAX_SIZE (1024) // Max queue size; callers will block until there is room +#define METADATA_MAINTENANCE_FIRST_CHECK (1800) // Maintenance first run after agent startup in seconds +#define METADATA_MAINTENANCE_RETRY (60) // Retry run if already running or last run did actual work +#define METADATA_MAINTENANCE_INTERVAL (3600) // Repeat maintenance after latest successful + +#define METADATA_HOST_CHECK_FIRST_CHECK (5) // First check for pending metadata +#define METADATA_HOST_CHECK_INTERVAL (30) // Repeat check for pending metadata +#define METADATA_HOST_CHECK_IMMEDIATE (5) // Repeat immediate run because we have more metadata to write + +#define MAX_METADATA_CLEANUP (500) // Maximum metadata write operations (e.g deletes before retrying) +#define METADATA_MAX_BATCH_SIZE (512) // Maximum commands to execute before running the event loop +#define METADATA_MAX_TRANSACTION_BATCH (128) // Maximum commands to add in a transaction + +enum metadata_opcode { + METADATA_DATABASE_NOOP = 0, + METADATA_DATABASE_TIMER, + METADATA_ADD_CHART, + METADATA_ADD_CHART_LABEL, + METADATA_ADD_DIMENSION, + METADATA_DEL_DIMENSION, + METADATA_ADD_DIMENSION_OPTION, + METADATA_ADD_HOST_SYSTEM_INFO, + METADATA_ADD_HOST_INFO, + METADATA_STORE_CLAIM_ID, + METADATA_STORE_HOST_LABELS, + METADATA_STORE_BUFFER, + + METADATA_SKIP_TRANSACTION, // Dummy -- OPCODES less than this one can be in a tranasction + + METADATA_SCAN_HOSTS, + METADATA_MAINTENANCE, + METADATA_SYNC_SHUTDOWN, + METADATA_UNITTEST, + // leave this last + // we need it to check for worker utilization + METADATA_MAX_ENUMERATIONS_DEFINED +}; + +#define MAX_PARAM_LIST (2) +struct metadata_cmd { + enum metadata_opcode opcode; + struct completion *completion; + const void *param[MAX_PARAM_LIST]; +}; + +struct metadata_database_cmdqueue { + unsigned head, tail; + struct metadata_cmd cmd_array[METADATA_CMD_Q_MAX_SIZE]; +}; + +typedef enum { + METADATA_FLAG_CLEANUP = (1 << 0), // Cleanup is running + METADATA_FLAG_SCANNING_HOSTS = (1 << 1), // Scanning of hosts in worker thread + METADATA_FLAG_SHUTDOWN = (1 << 2), // Shutting down +} METADATA_FLAG; + +#define METADATA_WORKER_BUSY (METADATA_FLAG_CLEANUP | METADATA_FLAG_SCANNING_HOSTS) + +struct metadata_wc { + uv_thread_t thread; + time_t check_metadata_after; + time_t check_hosts_after; + volatile unsigned queue_size; + uv_loop_t *loop; + uv_async_t async; + METADATA_FLAG flags; + uint64_t row_id; + uv_timer_t timer_req; + struct completion init_complete; + /* FIFO command queue */ + uv_mutex_t cmd_mutex; + uv_cond_t cmd_cond; + struct metadata_database_cmdqueue cmd_queue; +}; + +#define metadata_flag_check(target_flags, flag) (__atomic_load_n(&((target_flags)->flags), __ATOMIC_SEQ_CST) & (flag)) +#define metadata_flag_set(target_flags, flag) __atomic_or_fetch(&((target_flags)->flags), (flag), __ATOMIC_SEQ_CST) +#define metadata_flag_clear(target_flags, flag) __atomic_and_fetch(&((target_flags)->flags), ~(flag), __ATOMIC_SEQ_CST) + +// +// For unittest +// +struct thread_unittest { + int join; + unsigned added; + unsigned processed; + unsigned *done; +}; + + +// Metadata functions + +struct query_build { + BUFFER *sql; + int count; + char uuid_str[UUID_STR_LEN]; +}; + +static int host_label_store_to_sql_callback(const char *name, const char *value, RRDLABEL_SRC ls, void *data) { + struct query_build *lb = data; + if (unlikely(!lb->count)) + buffer_sprintf(lb->sql, STORE_HOST_LABEL); + else + buffer_strcat(lb->sql, ", "); + buffer_sprintf(lb->sql, STORE_HOST_OR_CHART_LABEL_VALUE, lb->uuid_str, (int)ls & ~(RRDLABEL_FLAG_INTERNAL), name, value); + lb->count++; + return 1; +} + +static int chart_label_store_to_sql_callback(const char *name, const char *value, RRDLABEL_SRC ls, void *data) { + struct query_build *lb = data; + if (unlikely(!lb->count)) + buffer_sprintf(lb->sql, STORE_CHART_LABEL); + else + buffer_strcat(lb->sql, ", "); + buffer_sprintf(lb->sql, STORE_HOST_OR_CHART_LABEL_VALUE, lb->uuid_str, ls, name, value); + lb->count++; + return 1; +} + +static void check_and_update_chart_labels(RRDSET *st, BUFFER *work_buffer) +{ + size_t old_version = st->rrdlabels_last_saved_version; + size_t new_version = dictionary_version(st->rrdlabels); + + if(new_version != old_version) { + buffer_flush(work_buffer); + struct query_build tmp = {.sql = work_buffer, .count = 0}; + uuid_unparse_lower(st->chart_uuid, tmp.uuid_str); + rrdlabels_walkthrough_read(st->rrdlabels, chart_label_store_to_sql_callback, &tmp); + st->rrdlabels_last_saved_version = new_version; + db_execute(buffer_tostring(work_buffer)); + } +} + +// Migrate all hosts with hops zero to this host_uuid +void migrate_localhost(uuid_t *host_uuid) +{ + int rc; + + rc = exec_statement_with_uuid(MIGRATE_LOCALHOST_TO_NEW_MACHINE_GUID, host_uuid); + if (!rc) + rc = exec_statement_with_uuid(DELETE_NON_EXISTING_LOCALHOST, host_uuid); + if (!rc) + db_execute(DELETE_MISSING_NODE_INSTANCES); + +} + +static void store_claim_id(uuid_t *host_id, uuid_t *claim_id) +{ + sqlite3_stmt *res = NULL; + int rc; + + if (unlikely(!db_meta)) { + if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) + error_report("Database has not been initialized"); + return; + } + + rc = sqlite3_prepare_v2(db_meta, SQL_STORE_CLAIM_ID, -1, &res, 0); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to prepare statement store chart labels"); + return; + } + + rc = sqlite3_bind_blob(res, 1, host_id, sizeof(*host_id), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to bind host_id parameter to store node instance information"); + goto failed; + } + + if (claim_id) + rc = sqlite3_bind_blob(res, 2, claim_id, sizeof(*claim_id), SQLITE_STATIC); + else + rc = sqlite3_bind_null(res, 2); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to bind claim_id parameter to store node instance information"); + goto failed; + } + + rc = execute_insert(res); + if (unlikely(rc != SQLITE_DONE)) + error_report("Failed to store node instance information, rc = %d", rc); + +failed: + if (unlikely(sqlite3_finalize(res) != SQLITE_OK)) + error_report("Failed to finalize the prepared statement when storing node instance information"); +} + +static void delete_dimension_uuid(uuid_t *dimension_uuid) +{ + static __thread sqlite3_stmt *res = NULL; + int rc; + + if (unlikely(!res)) { + rc = prepare_statement(db_meta, DELETE_DIMENSION_UUID, &res); + if (rc != SQLITE_OK) { + error_report("Failed to prepare statement to delete a dimension uuid"); + return; + } + } + + rc = sqlite3_bind_blob(res, 1, dimension_uuid, sizeof(*dimension_uuid), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto skip_execution; + + rc = sqlite3_step_monitored(res); + if (unlikely(rc != SQLITE_DONE)) + error_report("Failed to delete dimension uuid, rc = %d", rc); + +skip_execution: + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement when deleting dimension UUID, rc = %d", rc); +} + +// +// Store host and host system info information in the database +static int sql_store_host_info(RRDHOST *host) +{ + static __thread sqlite3_stmt *res = NULL; + int rc, param = 0; + + if (unlikely(!db_meta)) { + if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) + return 0; + error_report("Database has not been initialized"); + return 1; + } + + if (unlikely((!res))) { + rc = prepare_statement(db_meta, SQL_STORE_HOST_INFO, &res); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to prepare statement to store host, rc = %d", rc); + return 1; + } + } + + rc = sqlite3_bind_blob(res, ++param, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = bind_text_null(res, ++param, rrdhost_hostname(host), 0); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = bind_text_null(res, ++param, rrdhost_registry_hostname(host), 1); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, host->rrd_update_every); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = bind_text_null(res, ++param, rrdhost_os(host), 1); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = bind_text_null(res, ++param, rrdhost_timezone(host), 1); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = bind_text_null(res, ++param, rrdhost_tags(host), 1); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, host->system_info ? host->system_info->hops : 0); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, host->rrd_memory_mode); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = bind_text_null(res, ++param, rrdhost_abbrev_timezone(host), 1); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, host->utc_offset); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = bind_text_null(res, ++param, rrdhost_program_name(host), 1); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = bind_text_null(res, ++param, rrdhost_program_version(host), 1); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int64(res, ++param, host->rrd_history_entries); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, (int ) host->health_enabled); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + int store_rc = sqlite3_step_monitored(res); + if (unlikely(store_rc != SQLITE_DONE)) + error_report("Failed to store host %s, rc = %d", rrdhost_hostname(host), rc); + + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement to store host %s, rc = %d", rrdhost_hostname(host), rc); + + return !(store_rc == SQLITE_DONE); +bind_fail: + error_report("Failed to bind %d parameter to store host %s, rc = %d", param, rrdhost_hostname(host), rc); + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement to store host %s, rc = %d", rrdhost_hostname(host), rc); + return 1; +} + +static void sql_store_host_system_info_key_value(const char *name, const char *value, void *data) +{ + struct query_build *lb = data; + + if (unlikely(!value)) + return; + + if (unlikely(!lb->count)) + buffer_sprintf( + lb->sql, STORE_HOST_INFO); + else + buffer_strcat(lb->sql, ", "); + buffer_sprintf(lb->sql, STORE_HOST_INFO_VALUES, lb->uuid_str, name, value); + lb->count++; +} + +static BUFFER *sql_store_host_system_info(RRDHOST *host) +{ + struct rrdhost_system_info *system_info = host->system_info; + + if (unlikely(!system_info)) + return NULL; + + BUFFER *work_buffer = buffer_create(1024); + + struct query_build key_data = {.sql = work_buffer, .count = 0}; + uuid_unparse_lower(host->host_uuid, key_data.uuid_str); + + sql_store_host_system_info_key_value("NETDATA_CONTAINER_OS_NAME", system_info->container_os_name, &key_data); + sql_store_host_system_info_key_value("NETDATA_CONTAINER_OS_ID", system_info->container_os_id, &key_data); + sql_store_host_system_info_key_value("NETDATA_CONTAINER_OS_ID_LIKE", system_info->container_os_id_like, &key_data); + sql_store_host_system_info_key_value("NETDATA_CONTAINER_OS_VERSION", system_info->container_os_version, &key_data); + sql_store_host_system_info_key_value("NETDATA_CONTAINER_OS_VERSION_ID", system_info->container_os_version_id, &key_data); + sql_store_host_system_info_key_value("NETDATA_CONTAINER_OS_DETECTION", system_info->host_os_detection, &key_data); + sql_store_host_system_info_key_value("NETDATA_HOST_OS_NAME", system_info->host_os_name, &key_data); + sql_store_host_system_info_key_value("NETDATA_HOST_OS_ID", system_info->host_os_id, &key_data); + sql_store_host_system_info_key_value("NETDATA_HOST_OS_ID_LIKE", system_info->host_os_id_like, &key_data); + sql_store_host_system_info_key_value("NETDATA_HOST_OS_VERSION", system_info->host_os_version, &key_data); + sql_store_host_system_info_key_value("NETDATA_HOST_OS_VERSION_ID", system_info->host_os_version_id, &key_data); + sql_store_host_system_info_key_value("NETDATA_HOST_OS_DETECTION", system_info->host_os_detection, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_KERNEL_NAME", system_info->kernel_name, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_CPU_LOGICAL_CPU_COUNT", system_info->host_cores, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_CPU_FREQ", system_info->host_cpu_freq, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_TOTAL_RAM", system_info->host_ram_total, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_TOTAL_DISK_SIZE", system_info->host_disk_space, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_KERNEL_VERSION", system_info->kernel_version, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_ARCHITECTURE", system_info->architecture, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_VIRTUALIZATION", system_info->virtualization, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_VIRT_DETECTION", system_info->virt_detection, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_CONTAINER", system_info->container, &key_data); + sql_store_host_system_info_key_value("NETDATA_SYSTEM_CONTAINER_DETECTION", system_info->container_detection, &key_data); + sql_store_host_system_info_key_value("NETDATA_HOST_IS_K8S_NODE", system_info->is_k8s_node, &key_data); + + return work_buffer; +} + + +/* + * Store set option for a dimension + */ +static int sql_set_dimension_option(uuid_t *dim_uuid, char *option) +{ + sqlite3_stmt *res = NULL; + int rc; + + if (unlikely(!db_meta)) { + if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) + return 0; + error_report("Database has not been initialized"); + return 1; + } + + rc = sqlite3_prepare_v2(db_meta, "UPDATE dimension SET options = @options WHERE dim_id = @dim_id", -1, &res, 0); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to prepare statement to update dimension options"); + return 0; + }; + + rc = sqlite3_bind_blob(res, 2, dim_uuid, sizeof(*dim_uuid), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + if (!option || !strcmp(option,"unhide")) + rc = sqlite3_bind_null(res, 1); + else + rc = sqlite3_bind_text(res, 1, option, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = execute_insert(res); + if (unlikely(rc != SQLITE_DONE)) + error_report("Failed to update dimension option, rc = %d", rc); + +bind_fail: + rc = sqlite3_finalize(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to finalize statement in update dimension options, rc = %d", rc); + return 0; +} + +/* + * Store a chart in the database + */ + +static int sql_store_chart( + uuid_t *chart_uuid, uuid_t *host_uuid, const char *type, const char *id, const char *name, const char *family, + const char *context, const char *title, const char *units, const char *plugin, const char *module, long priority, + int update_every, int chart_type, int memory_mode, long history_entries) +{ + static __thread sqlite3_stmt *res = NULL; + int rc, param = 0; + + if (unlikely(!db_meta)) { + if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) + return 0; + error_report("Database has not been initialized"); + return 1; + } + + if (unlikely(!res)) { + rc = prepare_statement(db_meta, SQL_STORE_CHART, &res); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to prepare statement to store chart, rc = %d", rc); + return 1; + } + } + + param++; + rc = sqlite3_bind_blob(res, 1, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_blob(res, 2, host_uuid, sizeof(*host_uuid), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_text(res, 3, type, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_text(res, 4, id, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + if (name && *name) + rc = sqlite3_bind_text(res, 5, name, -1, SQLITE_STATIC); + else + rc = sqlite3_bind_null(res, 5); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_text(res, 6, family, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_text(res, 7, context, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_text(res, 8, title, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_text(res, 9, units, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_text(res, 10, plugin, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_text(res, 11, module, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_int(res, 12, (int) priority); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_int(res, 13, update_every); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_int(res, 14, chart_type); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_int(res, 15, memory_mode); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + param++; + rc = sqlite3_bind_int(res, 16, (int) history_entries); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = execute_insert(res); + if (unlikely(rc != SQLITE_DONE)) + error_report("Failed to store chart, rc = %d", rc); + + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement in chart store function, rc = %d", rc); + + return 0; + +bind_fail: + error_report("Failed to bind parameter %d to store chart, rc = %d", param, rc); + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement in chart store function, rc = %d", rc); + return 1; +} + +/* + * Store a dimension + */ +static int sql_store_dimension( + uuid_t *dim_uuid, uuid_t *chart_uuid, const char *id, const char *name, collected_number multiplier, + collected_number divisor, int algorithm, bool hidden) +{ + static __thread sqlite3_stmt *res = NULL; + int rc, param = 0; + + if (unlikely(!db_meta)) { + if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE) + return 0; + error_report("Database has not been initialized"); + return 1; + } + + if (unlikely(!res)) { + rc = prepare_statement(db_meta, SQL_STORE_DIMENSION, &res); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to prepare statement to store dimension, rc = %d", rc); + return 1; + } + } + + rc = sqlite3_bind_blob(res, ++param, dim_uuid, sizeof(*dim_uuid), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_blob(res, ++param, chart_uuid, sizeof(*chart_uuid), SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_text(res, ++param, id, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_text(res, ++param, name, -1, SQLITE_STATIC); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, (int) multiplier); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, (int ) divisor); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = sqlite3_bind_int(res, ++param, algorithm); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + if (hidden) + rc = sqlite3_bind_text(res, ++param, "hidden", -1, SQLITE_STATIC); + else + rc = sqlite3_bind_null(res, ++param); + if (unlikely(rc != SQLITE_OK)) + goto bind_fail; + + rc = execute_insert(res); + if (unlikely(rc != SQLITE_DONE)) + error_report("Failed to store dimension, rc = %d", rc); + + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement in store dimension, rc = %d", rc); + return 0; + +bind_fail: + error_report("Failed to bind parameter %d to store dimension, rc = %d", param, rc); + rc = sqlite3_reset(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to reset statement in store dimension, rc = %d", rc); + return 1; +} + +static bool dimension_can_be_deleted(uuid_t *dim_uuid) +{ +#ifdef ENABLE_DBENGINE + bool no_retention = true; + for (size_t tier = 0; tier < storage_tiers; tier++) { + if (!multidb_ctx[tier]) + continue; + time_t first_time_t = 0, last_time_t = 0; + if (rrdeng_metric_retention_by_uuid((void *) multidb_ctx[tier], dim_uuid, &first_time_t, &last_time_t) == 0) { + if (first_time_t > 0) { + no_retention = false; + break; + } + } + } + return no_retention; +#else + return false; +#endif +} + +static void check_dimension_metadata(struct metadata_wc *wc) +{ + int rc; + sqlite3_stmt *res = NULL; + + rc = sqlite3_prepare_v2(db_meta, SELECT_DIMENSION_LIST, -1, &res, 0); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to prepare statement to fetch host dimensions"); + return; + } + + rc = sqlite3_bind_int64(res, 1, (sqlite3_int64) wc->row_id); + if (unlikely(rc != SQLITE_OK)) { + error_report("Failed to row parameter"); + goto skip_run; + } + + uint32_t total_checked = 0; + uint32_t total_deleted= 0; + uint64_t last_row_id = wc->row_id; + + info("METADATA: Checking dimensions starting after row %"PRIu64, wc->row_id); + + while (sqlite3_step_monitored(res) == SQLITE_ROW && total_deleted < MAX_METADATA_CLEANUP) { + if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) + break; + + last_row_id = sqlite3_column_int64(res, 1); + rc = dimension_can_be_deleted((uuid_t *)sqlite3_column_blob(res, 0)); + if (rc == true) { + delete_dimension_uuid((uuid_t *)sqlite3_column_blob(res, 0)); + total_deleted++; + } + total_checked++; + } + wc->row_id = last_row_id; + time_t now = now_realtime_sec(); + if (total_deleted > 0) { + wc->check_metadata_after = now + METADATA_MAINTENANCE_RETRY; + } else + wc->row_id = 0; + info("METADATA: Checked %u, deleted %u -- will resume after row %"PRIu64" in %lld seconds", total_checked, total_deleted, wc->row_id, + (long long)(wc->check_metadata_after - now)); + +skip_run: + rc = sqlite3_finalize(res); + if (unlikely(rc != SQLITE_OK)) + error_report("Failed to finalize the prepared statement when reading dimensions"); +} + + +// +// EVENT LOOP STARTS HERE +// +static uv_mutex_t metadata_async_lock; + +static void metadata_init_cmd_queue(struct metadata_wc *wc) +{ + wc->cmd_queue.head = wc->cmd_queue.tail = 0; + wc->queue_size = 0; + fatal_assert(0 == uv_cond_init(&wc->cmd_cond)); + fatal_assert(0 == uv_mutex_init(&wc->cmd_mutex)); +} + +int metadata_enq_cmd_noblock(struct metadata_wc *wc, struct metadata_cmd *cmd) +{ + unsigned queue_size; + + /* wait for free space in queue */ + uv_mutex_lock(&wc->cmd_mutex); + + if (cmd->opcode == METADATA_SYNC_SHUTDOWN) { + metadata_flag_set(wc, METADATA_FLAG_SHUTDOWN); + uv_mutex_unlock(&wc->cmd_mutex); + return 0; + } + + if (unlikely((queue_size = wc->queue_size) == METADATA_CMD_Q_MAX_SIZE || + metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) { + uv_mutex_unlock(&wc->cmd_mutex); + return 1; + } + + fatal_assert(queue_size < METADATA_CMD_Q_MAX_SIZE); + /* enqueue command */ + wc->cmd_queue.cmd_array[wc->cmd_queue.tail] = *cmd; + wc->cmd_queue.tail = wc->cmd_queue.tail != METADATA_CMD_Q_MAX_SIZE - 1 ? + wc->cmd_queue.tail + 1 : 0; + wc->queue_size = queue_size + 1; + uv_mutex_unlock(&wc->cmd_mutex); + return 0; +} + +static void metadata_enq_cmd(struct metadata_wc *wc, struct metadata_cmd *cmd) +{ + unsigned queue_size; + + /* wait for free space in queue */ + uv_mutex_lock(&wc->cmd_mutex); + if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) { + uv_mutex_unlock(&wc->cmd_mutex); + (void) uv_async_send(&wc->async); + return; + } + + if (cmd->opcode == METADATA_SYNC_SHUTDOWN) { + metadata_flag_set(wc, METADATA_FLAG_SHUTDOWN); + uv_mutex_unlock(&wc->cmd_mutex); + (void) uv_async_send(&wc->async); + return; + } + + while ((queue_size = wc->queue_size) == METADATA_CMD_Q_MAX_SIZE) { + if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) { + uv_mutex_unlock(&wc->cmd_mutex); + return; + } + uv_cond_wait(&wc->cmd_cond, &wc->cmd_mutex); + } + fatal_assert(queue_size < METADATA_CMD_Q_MAX_SIZE); + /* enqueue command */ + wc->cmd_queue.cmd_array[wc->cmd_queue.tail] = *cmd; + wc->cmd_queue.tail = wc->cmd_queue.tail != METADATA_CMD_Q_MAX_SIZE - 1 ? + wc->cmd_queue.tail + 1 : 0; + wc->queue_size = queue_size + 1; + uv_mutex_unlock(&wc->cmd_mutex); + + /* wake up event loop */ + (void) uv_async_send(&wc->async); +} + +static struct metadata_cmd metadata_deq_cmd(struct metadata_wc *wc, enum metadata_opcode *next_opcode) +{ + struct metadata_cmd ret; + unsigned queue_size; + + uv_mutex_lock(&wc->cmd_mutex); + queue_size = wc->queue_size; + if (queue_size == 0) { + memset(&ret, 0, sizeof(ret)); + ret.opcode = METADATA_DATABASE_NOOP; + ret.completion = NULL; + *next_opcode = METADATA_DATABASE_NOOP; + } else { + /* dequeue command */ + ret = wc->cmd_queue.cmd_array[wc->cmd_queue.head]; + + if (queue_size == 1) { + wc->cmd_queue.head = wc->cmd_queue.tail = 0; + } else { + wc->cmd_queue.head = wc->cmd_queue.head != METADATA_CMD_Q_MAX_SIZE - 1 ? + wc->cmd_queue.head + 1 : 0; + } + wc->queue_size = queue_size - 1; + if (wc->queue_size > 0) + *next_opcode = wc->cmd_queue.cmd_array[wc->cmd_queue.head].opcode; + else + *next_opcode = METADATA_DATABASE_NOOP; + /* wake up producers */ + uv_cond_signal(&wc->cmd_cond); + } + uv_mutex_unlock(&wc->cmd_mutex); + + return ret; +} + +static void async_cb(uv_async_t *handle) +{ + uv_stop(handle->loop); + uv_update_time(handle->loop); +} + +#define TIMER_INITIAL_PERIOD_MS (1000) +#define TIMER_REPEAT_PERIOD_MS (1000) + +static void timer_cb(uv_timer_t* handle) +{ + uv_stop(handle->loop); + uv_update_time(handle->loop); + + struct metadata_wc *wc = handle->data; + struct metadata_cmd cmd; + memset(&cmd, 0, sizeof(cmd)); + + time_t now = now_realtime_sec(); + + if (wc->check_metadata_after && wc->check_metadata_after < now) { + cmd.opcode = METADATA_MAINTENANCE; + if (!metadata_enq_cmd_noblock(wc, &cmd)) + wc->check_metadata_after = now + METADATA_MAINTENANCE_INTERVAL; + } + + if (wc->check_hosts_after && wc->check_hosts_after < now) { + cmd.opcode = METADATA_SCAN_HOSTS; + if (!metadata_enq_cmd_noblock(wc, &cmd)) + wc->check_hosts_after = now + METADATA_HOST_CHECK_INTERVAL; + } +} + +static void after_metadata_cleanup(uv_work_t *req, int status) +{ + UNUSED(status); + + struct metadata_wc *wc = req->data; + metadata_flag_clear(wc, METADATA_FLAG_CLEANUP); +} +static void start_metadata_cleanup(uv_work_t *req) +{ + struct metadata_wc *wc = req->data; + check_dimension_metadata(wc); +} + +struct scan_metadata_payload { + uv_work_t request; + struct metadata_wc *wc; + struct completion *completion; + uint32_t max_count; +}; + +// Callback after scan of hosts is done +static void after_metadata_hosts(uv_work_t *req, int status __maybe_unused) +{ + struct scan_metadata_payload *data = req->data; + struct metadata_wc *wc = data->wc; + + metadata_flag_clear(wc, METADATA_FLAG_SCANNING_HOSTS); + internal_error(true, "METADATA: scanning hosts complete"); + if (unlikely(data->completion)) { + completion_mark_complete(data->completion); + internal_error(true, "METADATA: Sending completion done"); + } + freez(data); +} + +static bool metadata_scan_host(RRDHOST *host, uint32_t max_count) { + RRDSET *st; + int rc; + + bool more_to_do = false; + uint32_t scan_count = 1; + BUFFER *work_buffer = buffer_create(1024); + + rrdset_foreach_reentrant(st, host) { + if (scan_count == max_count) { + more_to_do = true; + break; + } + if(rrdset_flag_check(st, RRDSET_FLAG_METADATA_UPDATE)) { + rrdset_flag_clear(st, RRDSET_FLAG_METADATA_UPDATE); + scan_count++; + + check_and_update_chart_labels(st, work_buffer); + + rc = sql_store_chart( + &st->chart_uuid, + &st->rrdhost->host_uuid, + string2str(st->parts.type), + string2str(st->parts.id), + string2str(st->parts.name), + rrdset_family(st), + rrdset_context(st), + rrdset_title(st), + rrdset_units(st), + rrdset_plugin_name(st), + rrdset_module_name(st), + st->priority, + st->update_every, + st->chart_type, + st->rrd_memory_mode, + st->entries); + if (unlikely(rc)) + internal_error(true, "METADATA: Failed to store chart metadata %s", string2str(st->id)); + } + + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if(rrddim_flag_check(rd, RRDDIM_FLAG_METADATA_UPDATE)) { + rrddim_flag_clear(rd, RRDDIM_FLAG_METADATA_UPDATE); + + rc = sql_store_dimension( + &rd->metric_uuid, + &rd->rrdset->chart_uuid, + string2str(rd->id), + string2str(rd->name), + rd->multiplier, + rd->divisor, + rd->algorithm, + rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN)); + + if (unlikely(rc)) + error_report("METADATA: Failed to store dimension %s", string2str(rd->id)); + } + } + rrddim_foreach_done(rd); + } + rrdset_foreach_done(st); + + buffer_free(work_buffer); + return more_to_do; +} + +// Worker thread to scan hosts for pending metadata to store +static void start_metadata_hosts(uv_work_t *req __maybe_unused) +{ + RRDHOST *host; + + struct scan_metadata_payload *data = req->data; + struct metadata_wc *wc = data->wc; + + bool run_again = false; + dfe_start_reentrant(rrdhost_root_index, host) { + if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED) || !rrdhost_flag_check(host, RRDHOST_FLAG_METADATA_UPDATE)) + continue; + internal_error(true, "METADATA: Scanning host %s", rrdhost_hostname(host)); + rrdhost_flag_clear(host,RRDHOST_FLAG_METADATA_UPDATE); + if (unlikely(metadata_scan_host(host, data->max_count))) { + run_again = true; + rrdhost_flag_set(host,RRDHOST_FLAG_METADATA_UPDATE); + internal_error(true,"METADATA: Rescheduling host %s to run; more charts to store", rrdhost_hostname(host)); + } + } + dfe_done(host); + if (unlikely(run_again)) + wc->check_hosts_after = now_realtime_sec() + METADATA_HOST_CHECK_IMMEDIATE; + else + wc->check_hosts_after = now_realtime_sec() + METADATA_HOST_CHECK_INTERVAL; +} + +static void metadata_event_loop(void *arg) +{ + worker_register("METASYNC"); + worker_register_job_name(METADATA_DATABASE_NOOP, "noop"); + worker_register_job_name(METADATA_DATABASE_TIMER, "timer"); + worker_register_job_name(METADATA_ADD_CHART, "add chart"); + worker_register_job_name(METADATA_ADD_CHART_LABEL, "add chart label"); + worker_register_job_name(METADATA_ADD_DIMENSION, "add dimension"); + worker_register_job_name(METADATA_DEL_DIMENSION, "delete dimension"); + worker_register_job_name(METADATA_ADD_DIMENSION_OPTION, "dimension option"); + worker_register_job_name(METADATA_ADD_HOST_SYSTEM_INFO, "host system info"); + worker_register_job_name(METADATA_ADD_HOST_INFO, "host info"); + worker_register_job_name(METADATA_STORE_CLAIM_ID, "add claim id"); + worker_register_job_name(METADATA_STORE_HOST_LABELS, "host labels"); + worker_register_job_name(METADATA_MAINTENANCE, "maintenance"); + + + int ret; + uv_loop_t *loop; + unsigned cmd_batch_size; + struct metadata_wc *wc = arg; + enum metadata_opcode opcode, next_opcode; + uv_work_t metadata_cleanup_worker; + + uv_thread_set_name_np(wc->thread, "METASYNC"); + loop = wc->loop = mallocz(sizeof(uv_loop_t)); + ret = uv_loop_init(loop); + if (ret) { + error("uv_loop_init(): %s", uv_strerror(ret)); + goto error_after_loop_init; + } + loop->data = wc; + + ret = uv_async_init(wc->loop, &wc->async, async_cb); + if (ret) { + error("uv_async_init(): %s", uv_strerror(ret)); + goto error_after_async_init; + } + wc->async.data = wc; + + ret = uv_timer_init(loop, &wc->timer_req); + if (ret) { + error("uv_timer_init(): %s", uv_strerror(ret)); + goto error_after_timer_init; + } + wc->timer_req.data = wc; + fatal_assert(0 == uv_timer_start(&wc->timer_req, timer_cb, TIMER_INITIAL_PERIOD_MS, TIMER_REPEAT_PERIOD_MS)); + + info("Starting metadata sync thread with %d entries command queue", METADATA_CMD_Q_MAX_SIZE); + + struct metadata_cmd cmd; + memset(&cmd, 0, sizeof(cmd)); + metadata_flag_clear(wc, METADATA_FLAG_CLEANUP); + metadata_flag_clear(wc, METADATA_FLAG_SCANNING_HOSTS); + + wc->check_metadata_after = now_realtime_sec() + METADATA_MAINTENANCE_FIRST_CHECK; + wc->check_hosts_after = now_realtime_sec() + METADATA_HOST_CHECK_FIRST_CHECK; + + int shutdown = 0; + int in_transaction = 0; + int commands_in_transaction = 0; + // This can be used in the event loop for all opcodes (not workers) + BUFFER *work_buffer = buffer_create(1024); + wc->row_id = 0; + completion_mark_complete(&wc->init_complete); + + while (shutdown == 0 || (wc->flags & METADATA_WORKER_BUSY)) { + RRDDIM *rd = NULL; + RRDSET *st = NULL; + RRDHOST *host = NULL; + DICTIONARY_ITEM *dict_item = NULL; + BUFFER *buffer = NULL; + uuid_t *uuid; + int rc; + + worker_is_idle(); + uv_run(loop, UV_RUN_DEFAULT); + + /* wait for commands */ + cmd_batch_size = 0; + do { + if (unlikely(cmd_batch_size >= METADATA_MAX_BATCH_SIZE)) + break; + + cmd = metadata_deq_cmd(wc, &next_opcode); + opcode = cmd.opcode; + + if (unlikely(opcode == METADATA_DATABASE_NOOP && metadata_flag_check(wc, METADATA_FLAG_SHUTDOWN))) { + shutdown = 1; + continue; + } + + ++cmd_batch_size; + + // If we are not in transaction and this command is the same with the next ; start a transaction + if (!in_transaction && opcode < METADATA_SKIP_TRANSACTION && opcode == next_opcode) { + if (opcode != METADATA_DATABASE_NOOP) { + in_transaction = 1; + db_execute("BEGIN TRANSACTION;"); + } + } + + if (likely(in_transaction)) { + commands_in_transaction++; + } + + if (likely(opcode != METADATA_DATABASE_NOOP)) + worker_is_busy(opcode); + + switch (opcode) { + case METADATA_DATABASE_NOOP: + case METADATA_DATABASE_TIMER: + break; + case METADATA_ADD_CHART: + dict_item = (DICTIONARY_ITEM * ) cmd.param[0]; + st = (RRDSET *) dictionary_acquired_item_value(dict_item); + + rc = sql_store_chart( + &st->chart_uuid, + &st->rrdhost->host_uuid, + string2str(st->parts.type), + string2str(st->parts.id), + string2str(st->parts.name), + rrdset_family(st), + rrdset_context(st), + rrdset_title(st), + rrdset_units(st), + rrdset_plugin_name(st), + rrdset_module_name(st), + st->priority, + st->update_every, + st->chart_type, + st->rrd_memory_mode, + st->entries); + + if (unlikely(rc)) + error_report("Failed to store chart %s", rrdset_id(st)); + + dictionary_acquired_item_release(st->rrdhost->rrdset_root_index, dict_item); + break; + case METADATA_ADD_CHART_LABEL: + dict_item = (DICTIONARY_ITEM * ) cmd.param[0]; + st = (RRDSET *) dictionary_acquired_item_value(dict_item); + check_and_update_chart_labels(st, work_buffer); + dictionary_acquired_item_release(st->rrdhost->rrdset_root_index, dict_item); + break; + case METADATA_ADD_DIMENSION: + dict_item = (DICTIONARY_ITEM * ) cmd.param[0]; + rd = (RRDDIM *) dictionary_acquired_item_value(dict_item); + + rc = sql_store_dimension( + &rd->metric_uuid, + &rd->rrdset->chart_uuid, + string2str(rd->id), + string2str(rd->name), + rd->multiplier, + rd->divisor, + rd->algorithm, + rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN)); + + if (unlikely(rc)) + error_report("Failed to store dimension %s", rrddim_id(rd)); + + dictionary_acquired_item_release(rd->rrdset->rrddim_root_index, dict_item); + break; + case METADATA_DEL_DIMENSION: + uuid = (uuid_t *) cmd.param[0]; + if (likely(dimension_can_be_deleted(uuid))) + delete_dimension_uuid(uuid); + freez(uuid); + break; + case METADATA_ADD_DIMENSION_OPTION: + dict_item = (DICTIONARY_ITEM * ) cmd.param[0]; + rd = (RRDDIM *) dictionary_acquired_item_value(dict_item); + rc = sql_set_dimension_option( + &rd->metric_uuid, rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN) ? "hidden" : NULL); + if (unlikely(rc)) + error_report("Failed to store dimension option for %s", string2str(rd->id)); + dictionary_acquired_item_release(rd->rrdset->rrddim_root_index, dict_item); + break; + case METADATA_ADD_HOST_SYSTEM_INFO: + buffer = (BUFFER *) cmd.param[0]; + db_execute(buffer_tostring(buffer)); + buffer_free(buffer); + break; + case METADATA_ADD_HOST_INFO: + dict_item = (DICTIONARY_ITEM * ) cmd.param[0]; + host = (RRDHOST *) dictionary_acquired_item_value(dict_item); + rc = sql_store_host_info(host); + if (unlikely(rc)) + error_report("Failed to store host info in the database for %s", string2str(host->hostname)); + dictionary_acquired_item_release(rrdhost_root_index, dict_item); + break; + case METADATA_STORE_CLAIM_ID: + store_claim_id((uuid_t *) cmd.param[0], (uuid_t *) cmd.param[1]); + freez((void *) cmd.param[0]); + freez((void *) cmd.param[1]); + break; + case METADATA_STORE_HOST_LABELS: + dict_item = (DICTIONARY_ITEM * ) cmd.param[0]; + host = (RRDHOST *) dictionary_acquired_item_value(dict_item); + rc = exec_statement_with_uuid(SQL_DELETE_HOST_LABELS, &host->host_uuid); + + if (likely(rc == SQLITE_OK)) { + buffer_flush(work_buffer); + struct query_build tmp = {.sql = work_buffer, .count = 0}; + uuid_unparse_lower(host->host_uuid, tmp.uuid_str); + rrdlabels_walkthrough_read(host->rrdlabels, host_label_store_to_sql_callback, &tmp); + db_execute(buffer_tostring(work_buffer)); + } + + dictionary_acquired_item_release(rrdhost_root_index, dict_item); + break; + + case METADATA_SCAN_HOSTS: + if (unlikely(metadata_flag_check(wc, METADATA_FLAG_SCANNING_HOSTS))) + break; + + struct scan_metadata_payload *data = mallocz(sizeof(*data)); + data->request.data = data; + data->wc = wc; + data->completion = cmd.completion; // Completion by the worker + + if (unlikely(cmd.completion)) { + data->max_count = 0; // 0 will process all pending updates + cmd.completion = NULL; // Do not complete after launching worker (worker will do) + } + else + data->max_count = 1000; + + metadata_flag_set(wc, METADATA_FLAG_SCANNING_HOSTS); + if (unlikely( + uv_queue_work(loop,&data->request, + start_metadata_hosts, + after_metadata_hosts))) { + // Failed to launch worker -- let the event loop handle completion + cmd.completion = data->completion; + freez(data); + metadata_flag_clear(wc, METADATA_FLAG_SCANNING_HOSTS); + } + break; + case METADATA_STORE_BUFFER: + buffer = (BUFFER *) cmd.param[0]; + db_execute(buffer_tostring(buffer)); + buffer_free(buffer); + break; + case METADATA_MAINTENANCE: + if (unlikely(metadata_flag_check(wc, METADATA_FLAG_CLEANUP))) + break; + + metadata_cleanup_worker.data = wc; + metadata_flag_set(wc, METADATA_FLAG_CLEANUP); + if (unlikely( + uv_queue_work(loop, &metadata_cleanup_worker, start_metadata_cleanup, after_metadata_cleanup))) { + metadata_flag_clear(wc, METADATA_FLAG_CLEANUP); + } + break; + case METADATA_UNITTEST:; + struct thread_unittest *tu = (struct thread_unittest *) cmd.param[0]; + sleep_usec(1000); // processing takes 1ms + __atomic_fetch_add(&tu->processed, 1, __ATOMIC_SEQ_CST); + break; + default: + break; + } + if (in_transaction && (commands_in_transaction >= METADATA_MAX_TRANSACTION_BATCH || opcode != next_opcode)) { + in_transaction = 0; + db_execute("COMMIT TRANSACTION;"); + commands_in_transaction = 0; + } + + if (cmd.completion) + completion_mark_complete(cmd.completion); + } while (opcode != METADATA_DATABASE_NOOP); + } + + if (!uv_timer_stop(&wc->timer_req)) + uv_close((uv_handle_t *)&wc->timer_req, NULL); + + /* + * uv_async_send after uv_close does not seem to crash in linux at the moment, + * it is however undocumented behaviour we need to be aware if this becomes + * an issue in the future. + */ + uv_close((uv_handle_t *)&wc->async, NULL); + uv_run(loop, UV_RUN_DEFAULT); + + uv_cond_destroy(&wc->cmd_cond); + /* uv_mutex_destroy(&wc->cmd_mutex); */ + //fatal_assert(0 == uv_loop_close(loop)); + int rc; + + do { + rc = uv_loop_close(loop); + } while (rc != UV_EBUSY); + + freez(loop); + worker_unregister(); + + buffer_free(work_buffer); + info("METADATA: Shutting down event loop"); + completion_mark_complete(&wc->init_complete); + return; + +error_after_timer_init: + uv_close((uv_handle_t *)&wc->async, NULL); +error_after_async_init: + fatal_assert(0 == uv_loop_close(loop)); +error_after_loop_init: + freez(loop); + worker_unregister(); +} + +struct metadata_wc metasync_worker = {.loop = NULL}; + +void metadata_sync_shutdown(void) +{ + completion_init(&metasync_worker.init_complete); + + struct metadata_cmd cmd; + memset(&cmd, 0, sizeof(cmd)); + info("METADATA: Sending a shutdown command"); + cmd.opcode = METADATA_SYNC_SHUTDOWN; + metadata_enq_cmd(&metasync_worker, &cmd); + + /* wait for metadata thread to shut down */ + info("METADATA: Waiting for shutdown ACK"); + completion_wait_for(&metasync_worker.init_complete); + completion_destroy(&metasync_worker.init_complete); + info("METADATA: Shutdown complete"); +} + +void metadata_sync_shutdown_prepare(void) +{ + struct metadata_cmd cmd; + memset(&cmd, 0, sizeof(cmd)); + + struct completion compl; + completion_init(&compl); + + info("METADATA: Sending a scan host command"); + uint32_t max_wait_iterations = 2000; + while (unlikely(metadata_flag_check(&metasync_worker, METADATA_FLAG_SCANNING_HOSTS)) && max_wait_iterations--) { + if (max_wait_iterations == 1999) + info("METADATA: Current worker is running; waiting to finish"); + sleep_usec(1000); + } + + cmd.opcode = METADATA_SCAN_HOSTS; + cmd.completion = &compl; + metadata_enq_cmd(&metasync_worker, &cmd); + + info("METADATA: Waiting for host scan completion"); + completion_wait_for(&compl); + completion_destroy(&compl); + info("METADATA: Host scan complete; can continue with shutdown"); +} + +// ------------------------------------------------------------- +// Init function called on agent startup + +void metadata_sync_init(void) +{ + struct metadata_wc *wc = &metasync_worker; + + fatal_assert(0 == uv_mutex_init(&metadata_async_lock)); + + memset(wc, 0, sizeof(*wc)); + metadata_init_cmd_queue(wc); + completion_init(&wc->init_complete); + + fatal_assert(0 == uv_thread_create(&(wc->thread), metadata_event_loop, wc)); + + completion_wait_for(&wc->init_complete); + completion_destroy(&wc->init_complete); + + info("SQLite metadata sync initialization complete"); +} + + +// Helpers + +static inline void queue_metadata_cmd(enum metadata_opcode opcode, const void *param0, const void *param1) +{ + struct metadata_cmd cmd; + cmd.opcode = opcode; + cmd.param[0] = param0; + cmd.param[1] = param1; + cmd.completion = NULL; + metadata_enq_cmd(&metasync_worker, &cmd); + +} + +// Public +void metaqueue_chart_update(RRDSET *st) +{ + const DICTIONARY_ITEM *acquired_st = dictionary_get_and_acquire_item(st->rrdhost->rrdset_root_index, string2str(st->id)); + queue_metadata_cmd(METADATA_ADD_CHART, acquired_st, NULL); +} + +// +// RD may not be collected, so we may store it needlessly +void metaqueue_dimension_update(RRDDIM *rd) +{ + const DICTIONARY_ITEM *acquired_rd = + dictionary_get_and_acquire_item(rd->rrdset->rrddim_root_index, string2str(rd->id)); + + if (unlikely(rrdset_flag_check(rd->rrdset, RRDSET_FLAG_METADATA_UPDATE))) { + metaqueue_chart_update(rd->rrdset); + rrdset_flag_clear(rd->rrdset, RRDSET_FLAG_METADATA_UPDATE); + } + + queue_metadata_cmd(METADATA_ADD_DIMENSION, acquired_rd, NULL); +} + +void metaqueue_dimension_update_flags(RRDDIM *rd) +{ + const DICTIONARY_ITEM *acquired_rd = + dictionary_get_and_acquire_item(rd->rrdset->rrddim_root_index, string2str(rd->id)); + queue_metadata_cmd(METADATA_ADD_DIMENSION_OPTION, acquired_rd, NULL); +} + +void metaqueue_host_update_system_info(RRDHOST *host) +{ + BUFFER *work_buffer = sql_store_host_system_info(host); + + if (unlikely(!work_buffer)) + return; + + queue_metadata_cmd(METADATA_ADD_HOST_SYSTEM_INFO, work_buffer, NULL); +} + +void metaqueue_host_update_info(const char *machine_guid) +{ + const DICTIONARY_ITEM *acquired_host = dictionary_get_and_acquire_item(rrdhost_root_index, machine_guid); + queue_metadata_cmd(METADATA_ADD_HOST_INFO, acquired_host, NULL); +} + +void metaqueue_delete_dimension_uuid(uuid_t *uuid) +{ + if (unlikely(!metasync_worker.loop)) + return; + uuid_t *use_uuid = mallocz(sizeof(*uuid)); + uuid_copy(*use_uuid, *uuid); + queue_metadata_cmd(METADATA_DEL_DIMENSION, use_uuid, NULL); +} + +void metaqueue_store_claim_id(uuid_t *host_uuid, uuid_t *claim_uuid) +{ + if (unlikely(!host_uuid)) + return; + + uuid_t *local_host_uuid = mallocz(sizeof(*host_uuid)); + uuid_t *local_claim_uuid = NULL; + + uuid_copy(*local_host_uuid, *host_uuid); + if (likely(claim_uuid)) { + local_claim_uuid = mallocz(sizeof(*claim_uuid)); + uuid_copy(*local_claim_uuid, *claim_uuid); + } + queue_metadata_cmd(METADATA_STORE_CLAIM_ID, local_host_uuid, local_claim_uuid); +} + +void metaqueue_store_host_labels(const char *machine_guid) +{ + const DICTIONARY_ITEM *acquired_host = dictionary_get_and_acquire_item(rrdhost_root_index, machine_guid); + queue_metadata_cmd(METADATA_STORE_HOST_LABELS, acquired_host, NULL); +} + +void metaqueue_buffer(BUFFER *buffer) +{ + queue_metadata_cmd(METADATA_STORE_BUFFER, buffer, NULL); +} + +void metaqueue_chart_labels(RRDSET *st) +{ + const DICTIONARY_ITEM *acquired_st = dictionary_get_and_acquire_item(st->rrdhost->rrdset_root_index, string2str(st->id)); + queue_metadata_cmd(METADATA_ADD_CHART_LABEL, acquired_st, NULL); +} + + +// +// unitests +// + +static void *unittest_queue_metadata(void *arg) { + struct thread_unittest *tu = arg; + + struct metadata_cmd cmd; + cmd.opcode = METADATA_UNITTEST; + cmd.param[0] = tu; + cmd.param[1] = NULL; + cmd.completion = NULL; + metadata_enq_cmd(&metasync_worker, &cmd); + + do { + __atomic_fetch_add(&tu->added, 1, __ATOMIC_SEQ_CST); + metadata_enq_cmd(&metasync_worker, &cmd); + sleep_usec(10000); + } while (!__atomic_load_n(&tu->join, __ATOMIC_RELAXED)); + return arg; +} + +static void *metadata_unittest_threads(void) +{ + + unsigned done; + + struct thread_unittest tu = { + .join = 0, + .added = 0, + .processed = 0, + .done = &done, + }; + + // Queue messages / Time it + time_t seconds_to_run = 5; + int threads_to_create = 4; + fprintf( + stderr, + "\nChecking metadata queue using %d threads for %lld seconds...\n", + threads_to_create, + (long long)seconds_to_run); + + netdata_thread_t threads[threads_to_create]; + tu.join = 0; + for (int i = 0; i < threads_to_create; i++) { + char buf[100 + 1]; + snprintf(buf, 100, "meta%d", i); + netdata_thread_create( + &threads[i], + buf, + NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, + unittest_queue_metadata, + &tu); + } + uv_async_send(&metasync_worker.async); + sleep_usec(seconds_to_run * USEC_PER_SEC); + + __atomic_store_n(&tu.join, 1, __ATOMIC_RELAXED); + for (int i = 0; i < threads_to_create; i++) { + void *retval; + netdata_thread_join(threads[i], &retval); + } +// uv_async_send(&metasync_worker.async); + sleep_usec(5 * USEC_PER_SEC); + + fprintf(stderr, "Added %u elements, processed %u\n", tu.added, tu.processed); + + return 0; +} + +int metadata_unittest(void) +{ + metadata_sync_init(); + + // Queue items for a specific period of time + metadata_unittest_threads(); + + fprintf(stderr, "Items still in queue %u\n", metasync_worker.queue_size); + metadata_sync_shutdown(); + + return 0; +} diff --git a/database/sqlite/sqlite_metadata.h b/database/sqlite/sqlite_metadata.h new file mode 100644 index 000000000..9293facf8 --- /dev/null +++ b/database/sqlite/sqlite_metadata.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_SQLITE_METADATA_H +#define NETDATA_SQLITE_METADATA_H + +#include "sqlite3.h" +#include "sqlite_functions.h" + +// To initialize and shutdown +void metadata_sync_init(void); +void metadata_sync_shutdown(void); +void metadata_sync_shutdown_prepare(void); + +void metaqueue_dimension_update(RRDDIM *rd); +void metaqueue_chart_update(RRDSET *st); +void metaqueue_dimension_update_flags(RRDDIM *rd); +void metaqueue_host_update_system_info(RRDHOST *host); +void metaqueue_host_update_info(const char *machine_guid); +void metaqueue_delete_dimension_uuid(uuid_t *uuid); +void metaqueue_store_claim_id(uuid_t *host_uuid, uuid_t *claim_uuid); +void metaqueue_store_host_labels(const char *machine_guid); +void metaqueue_chart_labels(RRDSET *st); +void migrate_localhost(uuid_t *host_uuid); +void metaqueue_buffer(BUFFER *buffer); + +// UNIT TEST +int metadata_unittest(void); +#endif //NETDATA_SQLITE_METADATA_H diff --git a/database/storage_engine.c b/database/storage_engine.c index 76597acd5..edf017db4 100644 --- a/database/storage_engine.c +++ b/database/storage_engine.c @@ -10,7 +10,10 @@ .init = rrddim_collect_init,\ .store_metric = rrddim_collect_store_metric,\ .flush = rrddim_store_metric_flush,\ - .finalize = rrddim_collect_finalize\ + .finalize = rrddim_collect_finalize, \ + .change_collection_frequency = rrddim_store_metric_change_collection_frequency, \ + .metrics_group_get = rrddim_metrics_group_get, \ + .metrics_group_release = rrddim_metrics_group_release, \ } #define im_query_ops { \ @@ -27,8 +30,10 @@ static STORAGE_ENGINE engines[] = { .id = RRD_MEMORY_MODE_NONE, .name = RRD_MEMORY_MODE_NONE_NAME, .api = { - .init = rrddim_metric_init, - .free = rrddim_metric_free, + .metric_get = rrddim_metric_get, + .metric_get_or_create = rrddim_metric_get_or_create, + .metric_dup = rrddim_metric_dup, + .metric_release = rrddim_metric_release, .collect_ops = im_collect_ops, .query_ops = im_query_ops } @@ -37,8 +42,10 @@ static STORAGE_ENGINE engines[] = { .id = RRD_MEMORY_MODE_RAM, .name = RRD_MEMORY_MODE_RAM_NAME, .api = { - .init = rrddim_metric_init, - .free = rrddim_metric_free, + .metric_get = rrddim_metric_get, + .metric_get_or_create = rrddim_metric_get_or_create, + .metric_dup = rrddim_metric_dup, + .metric_release = rrddim_metric_release, .collect_ops = im_collect_ops, .query_ops = im_query_ops } @@ -47,8 +54,10 @@ static STORAGE_ENGINE engines[] = { .id = RRD_MEMORY_MODE_MAP, .name = RRD_MEMORY_MODE_MAP_NAME, .api = { - .init = rrddim_metric_init, - .free = rrddim_metric_free, + .metric_get = rrddim_metric_get, + .metric_get_or_create = rrddim_metric_get_or_create, + .metric_dup = rrddim_metric_dup, + .metric_release = rrddim_metric_release, .collect_ops = im_collect_ops, .query_ops = im_query_ops } @@ -57,8 +66,10 @@ static STORAGE_ENGINE engines[] = { .id = RRD_MEMORY_MODE_SAVE, .name = RRD_MEMORY_MODE_SAVE_NAME, .api = { - .init = rrddim_metric_init, - .free = rrddim_metric_free, + .metric_get = rrddim_metric_get, + .metric_get_or_create = rrddim_metric_get_or_create, + .metric_dup = rrddim_metric_dup, + .metric_release = rrddim_metric_release, .collect_ops = im_collect_ops, .query_ops = im_query_ops } @@ -67,8 +78,10 @@ static STORAGE_ENGINE engines[] = { .id = RRD_MEMORY_MODE_ALLOC, .name = RRD_MEMORY_MODE_ALLOC_NAME, .api = { - .init = rrddim_metric_init, - .free = rrddim_metric_free, + .metric_get = rrddim_metric_get, + .metric_get_or_create = rrddim_metric_get_or_create, + .metric_dup = rrddim_metric_dup, + .metric_release = rrddim_metric_release, .collect_ops = im_collect_ops, .query_ops = im_query_ops } @@ -78,13 +91,18 @@ static STORAGE_ENGINE engines[] = { .id = RRD_MEMORY_MODE_DBENGINE, .name = RRD_MEMORY_MODE_DBENGINE_NAME, .api = { - .init = rrdeng_metric_init, - .free = rrdeng_metric_free, + .metric_get = rrdeng_metric_get, + .metric_get_or_create = rrdeng_metric_get_or_create, + .metric_dup = rrdeng_metric_dup, + .metric_release = rrdeng_metric_release, .collect_ops = { .init = rrdeng_store_metric_init, .store_metric = rrdeng_store_metric_next, .flush = rrdeng_store_metric_flush_current_page, - .finalize = rrdeng_store_metric_finalize + .finalize = rrdeng_store_metric_finalize, + .change_collection_frequency = rrdeng_store_metric_change_collection_frequency, + .metrics_group_get = rrdeng_metrics_group_get, + .metrics_group_release = rrdeng_metrics_group_release, }, .query_ops = { .init = rrdeng_load_metric_init, diff --git a/database/storage_engine.h b/database/storage_engine.h index 3ed515e0a..b7fb7383a 100644 --- a/database/storage_engine.h +++ b/database/storage_engine.h @@ -5,28 +5,8 @@ #include "rrd.h" -typedef struct storage_engine STORAGE_ENGINE; - -// ------------------------------------------------------------------------ -// function pointers for all APIs provided by a storge engine -typedef struct storage_engine_api { - STORAGE_METRIC_HANDLE *(*init)(RRDDIM *rd, STORAGE_INSTANCE *instance); - void (*free)(STORAGE_METRIC_HANDLE *); - struct rrddim_collect_ops collect_ops; - struct rrddim_query_ops query_ops; -} STORAGE_ENGINE_API; - -struct storage_engine { - RRD_MEMORY_MODE id; - const char* name; - STORAGE_ENGINE_API api; -}; - -extern STORAGE_ENGINE* storage_engine_get(RRD_MEMORY_MODE mmode); -extern STORAGE_ENGINE* storage_engine_find(const char* name); - // Iterator over existing engines -extern STORAGE_ENGINE* storage_engine_foreach_init(); -extern STORAGE_ENGINE* storage_engine_foreach_next(STORAGE_ENGINE* it); +STORAGE_ENGINE* storage_engine_foreach_init(); +STORAGE_ENGINE* storage_engine_foreach_next(STORAGE_ENGINE* it); #endif diff --git a/docs/Demo-Sites.md b/docs/Demo-Sites.md index 80c98df3d..5c4d1018f 100644 --- a/docs/Demo-Sites.md +++ b/docs/Demo-Sites.md @@ -6,7 +6,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/docs/Demo-Sites. # Demo sites -You can also view live demos of Netdata at **[https://www.netdata.cloud](https://www.netdata.cloud/#live-demo)**. +You can also view live demos of Netdata at **https://app.netdata.cloud/spaces/netdata-demo** | Location | Netdata demo URL | 60 mins reqs | VM donated by | | :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| :------------------------------------------------- | diff --git a/docs/collect/system-metrics.md b/docs/collect/system-metrics.md index 623302894..ecd8dad70 100644 --- a/docs/collect/system-metrics.md +++ b/docs/collect/system-metrics.md @@ -37,14 +37,8 @@ can find all system collectors in our [supported collectors list](/collectors/CO Netdata is also capable of monitoring Windows systems. The [WMI collector](https://learn.netdata.cloud/docs/agent/collectors/go.d.plugin/modules/wmi) integrates with [windows_exporter](https://github.com/prometheus-community/windows_exporter), a small Go-based binary that you can run -on Windows systems. The WMI collector then gathers metrics from an endpoint created by windows_exporter. - -First, [download windows_exporter](https://github.com/prometheus-community/windows_exporter#installation) and run it -with the following collectors enabled, changing `0.14.0` to the version you downloaded. - -```powershell -windows_exporter-0.14.0-amd64.exe --collectors.enabled="cpu,memory,net,logical_disk,os,system,logon" -``` +on Windows systems. The WMI collector then gathers metrics from an endpoint created by windows_exporter, for more +details see [the requirements](https://learn.netdata.cloud/docs/agent/collectors/go.d.plugin/modules/wmi#requirements). Next, [configure the WMI collector](https://learn.netdata.cloud/docs/agent/collectors/go.d.plugin/modules/wmi#configuration) to point to the URL diff --git a/docs/dashboard/interact-charts.mdx b/docs/dashboard/interact-charts.mdx index 5633bd080..fe0f05e3f 100644 --- a/docs/dashboard/interact-charts.mdx +++ b/docs/dashboard/interact-charts.mdx @@ -117,6 +117,63 @@ the resolution to `resolution 1 sec` to see the exact values. Many of the above interactions can also be triggered using the icons on the bottom-right corner of every chart. They are, respectively, `Pan Left`, `Reset`, `Pan Right`, `Zoom In`, and `Zoom Out`. +## Chart label filtering + +The chart label filtering feature supports grouping by and filtering each chart based on labels (key/value pairs) applicable to the context and provides fine-grain capability on slicing the data and metrics. + +All metrics collected get "tagged" with labels and values, thus providing a powerful way of slicing and visualizing all metrics related to the infrastructure. + +The chart label filtering is currently enabled on: + +- All charts on the **Overview** tab +- Custom dashboards + +![Chart filtering on Overview tab chart](https://user-images.githubusercontent.com/88642300/193084084-01074495-c826-4519-a09f-d210f7e3e6be.png) +![Chart filtering on Custom dashboard](https://user-images.githubusercontent.com/88642300/193084172-358dfded-c318-4d9f-b6e2-46a8fc33030b.png) + +The top panel on each chart displays the various filters and grouping options selected on the specific chart. These filters are specific for each chart and need to be manually configured on each chart. + +Additionally, the charts can be saved to a custom dashboard, new or existing, with the selected filters from the overview screen. + +![Chart filtering saved on custom dashboard](https://user-images.githubusercontent.com/88642300/193084225-1b65984e-566c-4815-8bc1-a2781d3564bd.png) + +## Custom labels for Collectors + +In addition to the default labels associated with a collector and metrics context (you can identify them by seeing which ones have an underscore as a prefix), there is now a new feature enabled to create custom labels. These custom labels may be needed to group your jobs or instances into various categories. + +These custom labels can be configured within your go.d plugins by simply associating a label key/value pair, as in the following eaxmple. + +```conf +jobs: + - name: example_1 + someOption: someValue + labels: + label1: value1 + label2: value2 + - name: example_2 + someOption: someValue + labels: + label3: value3 + label4: value4 +``` + +For instance, you may be running multiple Postgres database instances within an infrastructure. Some of these may be associated with testing environments, some with staging and some with production environments. You can now associate each Postgres job / instance with a custom label. The “group by” and filtering options will then allow you to associate individual jobs by specific labels. + +```conf +jobs: + - name: local + dsn: 'postgres://postgres:postgres@127.0.0.1:5432/postgres' + collect_databases_matching: '*' + labels: + instance_type: production + ``` + ![Group by individual job labels one](https://user-images.githubusercontent.com/88642300/193084580-49df500a-ddfb-45bb-a209-3c7a904ee9e0.png) + ![group by individual job labels two](https://user-images.githubusercontent.com/88642300/193084624-6d9848d0-9400-4e34-9cd4-78e50c784cc0.png) + +### Future Work + +We already have [configurable host labels](https://learn.netdata.cloud/guides/using-host-labels) as well, which currently can’t be used to filter or group your metrics. We intend to provide the same capabilities described here with host labels, among other capabilities on other areas of the app as well + ## What's next? We recommend you read up on the differences between [chart dimensions, contexts, and diff --git a/docs/get-started.mdx b/docs/get-started.mdx index ec36c6a2a..892baa0ce 100644 --- a/docs/get-started.mdx +++ b/docs/get-started.mdx @@ -6,8 +6,8 @@ sidebar_label: "Get started" custom_edit_url: https://github.com/netdata/netdata/edit/master/docs/get-started.mdx --- -import { OneLineInstallWget } from '../src/components/OneLineInstall/' -import { Install, InstallBox } from '../src/components/Install/' +import { OneLineInstallWget } from '@site/src/components/OneLineInstall/' +import { Install, InstallBox } from '@site/src/components/Install/' # Get started with Netdata diff --git a/docs/guides/configure/performance.md b/docs/guides/configure/performance.md index f83634168..cb52a1141 100644 --- a/docs/guides/configure/performance.md +++ b/docs/guides/configure/performance.md @@ -8,8 +8,9 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/docs/guides/conf # How to optimize the Netdata Agent's performance We designed the Netdata Agent to be incredibly lightweight, even when it's collecting a few thousand dimensions every -second and visualizing that data into hundreds of charts. The Agent itself should never use more than 1% of a single CPU -core, roughly 100 MiB of RAM, and minimal disk I/O to collect, store, and visualize all this data. +second and visualizing that data into hundreds of charts. When properly configured for a production node, the Agent +itself should never use more than 1% of a single CPU core, roughly 50-100 MiB of RAM, and minimal disk I/O to collect, +store, and visualize all this data. We take this scalability seriously. We have one user [running Netdata](https://github.com/netdata/netdata/issues/1323#issuecomment-266427841) on a system with 144 cores and 288 @@ -21,6 +22,11 @@ only 512 MiB of RAM, or an IoT device like a [Raspberry Pi](/docs/guides/monitor cases, reducing Netdata's footprint beyond its already diminutive size can pay big dividends, giving your services more horsepower while still monitoring the health and the performance of the node, OS, hardware, and applications. +The default settings of the Netdata Agent are not optimized for performance, but for a simple standalone setup. We want +the first install to give you something you can run without any configuration. Most of the settings and options are +enabled, since we want you to experience the full thing. + + ## Prerequisites - A node running the Netdata Agent. @@ -146,18 +152,39 @@ calculator](/docs/store/change-metrics-storage.md#calculate-the-system-resources All the settings are found in the `[global]` section of `netdata.conf`: ```conf -[global] +[db] memory mode = dbengine page cache size = 32 dbengine multihost disk space = 256 ``` +To save even more memory, you can disable the dbengine and reduce retention to just 30 minutes, as shown below: + +```conf +[db] + storage tiers = 1 + mode = alloc + retention = 1800 +``` + Metric retention is not important in certain use cases, such as: - Data collection nodes stream collected metrics collected to a centralization point. - Data collection nodes export their metrics to another time series DB, or are scraped by Prometheus - Netdata installed only during incidents, to get richer information. -In such cases, you may not want to use the dbengine at all and instead opt for memory mode `memory mode = ram` or `memory mode = none`. +In such cases, you may not want to use the dbengine at all and instead opt for memory mode +`memory mode = alloc` or `memory mode = none`. + +## Disable machine learning + +Automated anomaly detection may be a powerful tool, but we recommend it to only be enabled on Netdata parents +that sit outside your production infrastructure, or if you have cpu and memory to spare. You can disable ML +with the following: +```conf +[ml] + enabled = no +``` + ## Run Netdata behind Nginx A dedicated web server like Nginx provides far more robustness than the Agent's internal [web server](/web/README.md). @@ -220,6 +247,19 @@ If you installation is working correctly, and you're not actively auditing Netda access log = none ``` +## Disable health checks + +If you are streaming metrics to parent nodes, we recommend you run your health checks on the parent, for all the metrics collected +by the children nodes. This saves resources on the children and makes it easier to configure or disable alerts and agent notifications. + +The parents by default run health checks for each child, as long as it is connected (the details are in `stream.conf`). +On the child nodes you should add to `netdata.conf` the following: + +```conf +[health] + enabled = no +``` + ## What's next? We hope this guide helped you better understand how to optimize the performance of the Netdata Agent. diff --git a/docs/guides/export/export-netdata-metrics-graphite.md b/docs/guides/export/export-netdata-metrics-graphite.md index 007c30f8b..dd742e454 100644 --- a/docs/guides/export/export-netdata-metrics-graphite.md +++ b/docs/guides/export/export-netdata-metrics-graphite.md @@ -3,7 +3,7 @@ title: Export and visualize Netdata metrics in Graphite description: "Use Netdata to collect and export thousands of metrics to Graphite for long-term storage or further analysis." image: /img/seo/guides/export/export-netdata-metrics-graphite.png --> -import { OneLineInstallWget } from '../../src/components/OneLineInstall/' +import { OneLineInstallWget } from '@site/src/components/OneLineInstall/' # Export and visualize Netdata metrics in Graphite diff --git a/docs/guides/monitor/lamp-stack.md b/docs/guides/monitor/lamp-stack.md index 595d64400..29b35e142 100644 --- a/docs/guides/monitor/lamp-stack.md +++ b/docs/guides/monitor/lamp-stack.md @@ -7,7 +7,7 @@ author_title: "Editorial Director, Technical & Educational Resources" author_img: "/img/authors/joel-hans.jpg" custom_edit_url: https://github.com/netdata/netdata/edit/master/docs/guides/monitor/lamp-stack.md --> -import { OneLineInstallWget } from '../../src/components/OneLineInstall/' +import { OneLineInstallWget } from '@site/src/components/OneLineInstall/' # LAMP stack monitoring (Linux, Apache, MySQL, PHP) with Netdata diff --git a/docs/guides/monitor/pi-hole-raspberry-pi.md b/docs/guides/monitor/pi-hole-raspberry-pi.md index 721ba2a8f..1246d8ba1 100644 --- a/docs/guides/monitor/pi-hole-raspberry-pi.md +++ b/docs/guides/monitor/pi-hole-raspberry-pi.md @@ -4,7 +4,7 @@ description: "Monitor Pi-hole metrics, plus Raspberry Pi system metrics, in minu image: /img/seo/guides/monitor/netdata-pi-hole-raspberry-pi.png custom_edit_url: https://github.com/netdata/netdata/edit/master/docs/guides/monitor/pi-hole-raspberry-pi.md --> -import { OneLineInstallWget } from '../../src/components/OneLineInstall/' +import { OneLineInstallWget } from '@site/src/components/OneLineInstall/' # Monitor Pi-hole (and a Raspberry Pi) with Netdata diff --git a/docs/guides/python-collector.md b/docs/guides/python-collector.md index f93c724bf..920b9b9ef 100644 --- a/docs/guides/python-collector.md +++ b/docs/guides/python-collector.md @@ -424,8 +424,8 @@ configuration in [YAML](https://www.tutorialspoint.com/yaml/yaml_basics.htm) for run. This enables you to define different "ways" to fetch data from a particular data source so that the collector has more chances to work out-of-the-box. For example, if the data source supports both `HTTP` and `linux socket`, you can define 2 jobs named `local`, with each using a different method. -- Check the `postgresql` collector configuration file on - [GitHub](https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/postgres/postgres.conf) to get a +- Check the `example` collector configuration file on + [GitHub](https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/example/example.conf) to get a sense of the structure. ```yaml @@ -455,8 +455,8 @@ function takes 2 arguments, one with the name of the configuration field and one find the configuration field. This allows you to define sane defaults for your collector. Moreover, when creating the configuration file, create a large comment section that describes the configuration -variables and inform the user about the defaults. For example, take a look at the `postgresql` collector on -[GitHub](https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/postgres/postgres.conf). +variables and inform the user about the defaults. For example, take a look at the `example` collector on +[GitHub](https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/example/example.conf). You can read more about the configuration file on the [`python.d.plugin` documentation](https://learn.netdata.cloud/docs/agent/collectors/python.d.plugin). @@ -465,7 +465,7 @@ documentation](https://learn.netdata.cloud/docs/agent/collectors/python.d.plugin Find the source code for the above examples on [GitHub](https://github.com/papajohn-uop/netdata). -Now we you ready to start developing our Netdata python Collector and share it with the rest of the Netdata community. +Now you are ready to start developing our Netdata python Collector and share it with the rest of the Netdata community. - If you need help while developing your collector, join our [Netdata Community](https://community.netdata.cloud/c/agent-development/9) to chat about it. diff --git a/docs/guides/step-by-step/step-00.md b/docs/guides/step-by-step/step-00.md index ab68fe741..9f0fecac8 100644 --- a/docs/guides/step-by-step/step-00.md +++ b/docs/guides/step-by-step/step-00.md @@ -3,7 +3,7 @@ title: "The step-by-step Netdata guide" date: 2020-03-31 custom_edit_url: https://github.com/netdata/netdata/edit/master/docs/guides/step-by-step/step-00.md --> -import { OneLineInstallWget, OneLineInstallCurl } from '../../src/components/OneLineInstall/' +import { OneLineInstallWget, OneLineInstallCurl } from '@site/src/components/OneLineInstall/' # The step-by-step Netdata guide diff --git a/docs/guides/step-by-step/step-09.md b/docs/guides/step-by-step/step-09.md index 6333dfe69..8aacd7514 100644 --- a/docs/guides/step-by-step/step-09.md +++ b/docs/guides/step-by-step/step-09.md @@ -36,10 +36,10 @@ guide, your Netdata agent is already using the database engine. Let's look at your `netdata.conf` file again. Under the `[global]` section, you'll find three connected options. ```conf -[global] - # memory mode = dbengine - # page cache size = 32 - # dbengine disk space = 256 +[db] + # mode = dbengine + # dbengine page cache size MB = 32 + # dbengine disk space MB = 256 ``` The `memory mode` option is set, by default, to `dbengine`. `page cache size` determines the amount of RAM, in MiB, that @@ -56,10 +56,10 @@ space` based on your needs. The calculator gives an accurate estimate based on h metrics your Agent collects, and more. ```conf -[global] - memory mode = dbengine - page cache size = 64 - dbengine disk space = 512 +[db] + mode = dbengine + dbengine page cache size MB = 64 + dbengine disk space MB = 512 ``` After you've made your changes, restart Netdata using `sudo systemctl restart netdata`, or the [appropriate diff --git a/docs/guides/troubleshoot/troubleshooting-agent-with-cloud-connection.md b/docs/guides/troubleshoot/troubleshooting-agent-with-cloud-connection.md index ee214c814..3bb5ace66 100644 --- a/docs/guides/troubleshoot/troubleshooting-agent-with-cloud-connection.md +++ b/docs/guides/troubleshoot/troubleshooting-agent-with-cloud-connection.md @@ -65,7 +65,7 @@ With the introduction of our new architecture, Agents running versions lower tha ### Verify that your IP is whitelisted from Netdata Cloud -Most of the nodes change IPs dynamically. It is possible that your current IP has been restricted from accessing `app.netdata.cloud` due to security concerns. +Most of the nodes change IPs dynamically. It is possible that your current IP has been restricted from accessing `api.netdata.cloud` due to security concerns. To verify this: @@ -75,7 +75,7 @@ To verify this: sudo netdatacli aclk-state | grep "Banned By Cloud" ``` - The output will contain a line indicating if the IP is banned from `app.netdata.cloud`: + The output will contain a line indicating if the IP is banned from `api.netdata.cloud`: ```bash Banned By Cloud: yes @@ -97,13 +97,13 @@ To verify this: 2. If you can reach external IPs, then check your domain resolution. ```bash - host app.netdata.cloud + host api.netdata.cloud ``` The expected output should be something like this: ```bash - app.netdata.cloud is an alias for main-ingress-545609a41fcaf5d6.elb.us-east-1.amazonaws.com. + api.netdata.cloud is an alias for main-ingress-545609a41fcaf5d6.elb.us-east-1.amazonaws.com. main-ingress-545609a41fcaf5d6.elb.us-east-1.amazonaws.com has address 54.198.178.11 main-ingress-545609a41fcaf5d6.elb.us-east-1.amazonaws.com has address 44.207.131.212 main-ingress-545609a41fcaf5d6.elb.us-east-1.amazonaws.com has address 44.196.50.41 @@ -111,7 +111,7 @@ To verify this: :::info - There will be cases in which the firewall restricts network access. In those cases, you need to whitelist the `app.netdata.cloud` domain to be able to see your nodes in Netdata Cloud. + There will be cases in which the firewall restricts network access. In those cases, you need to whitelist `api.netdata.cloud` and `mqtt.netdata.cloud` domains to be able to see your nodes in Netdata Cloud. If you can't whitelist domains in your firewall, you can whitelist the IPs that the above command will produce, but keep in mind that they can change without any notice. ::: diff --git a/docs/netdata-for-IoT.md b/docs/netdata-for-IoT.md index 35d770537..8d5bb21ba 100644 --- a/docs/netdata-for-IoT.md +++ b/docs/netdata-for-IoT.md @@ -25,7 +25,7 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/docs/netdata-for --- -Netdata is a **very efficient** server performance monitoring solution. When running in server hardware, it can collect +Netdata is a [very efficient](/docs/guides/configure/performance.md) server performance monitoring solution. When running in server hardware, it can collect thousands of system and application metrics **per second** with just 1% CPU utilization of a single core. Its web server responds to most data requests in about **half a millisecond** making its web dashboards spontaneous, amazingly fast! diff --git a/docs/store/change-metrics-storage.md b/docs/store/change-metrics-storage.md index 437b45fc2..c4b77d9af 100644 --- a/docs/store/change-metrics-storage.md +++ b/docs/store/change-metrics-storage.md @@ -51,10 +51,8 @@ the accuracy of the values you enter below, changes in the compression ratio, an ::: -Download -the [calculator](https://docs.google.com/spreadsheets/d/e/2PACX-1vTYMhUU90aOnIQ7qF6iIk6tXps57wmY9lxS6qDXznNJrzCKMDzxU3zkgh8Uv0xj_XqwFl3U6aHDZ6ag/pub?output=xlsx) -to optimize the data retention to your preferences. Utilize the "Front" spreadsheet. Experiment with the variables which -are padded with yellow to come up with the best settings for your use case. +Visit the [Netdata Storage Calculator](https://netdata-storage-calculator.herokuapp.com/) app to customize +data retention according to your preferences. ## Edit `netdata.conf` with recommended database engine settings diff --git a/exporting/check_filters.c b/exporting/check_filters.c index 726fd02a1..009a010b3 100644 --- a/exporting/check_filters.c +++ b/exporting/check_filters.c @@ -25,7 +25,7 @@ int rrdhost_is_exportable(struct instance *instance, RRDHOST *host) RRDHOST_FLAGS *flags = &host->exporting_flags[instance->index]; if (unlikely((*flags & (RRDHOST_FLAG_EXPORTING_SEND | RRDHOST_FLAG_EXPORTING_DONT_SEND)) == 0)) { - char *host_name = (host == localhost) ? "localhost" : host->hostname; + const char *host_name = (host == localhost) ? "localhost" : rrdhost_hostname(host); if (!instance->config.hosts_pattern || simple_pattern_matches(instance->config.hosts_pattern, host_name)) { *flags |= RRDHOST_FLAG_EXPORTING_SEND; @@ -55,10 +55,6 @@ int rrdset_is_exportable(struct instance *instance, RRDSET *st) RRDHOST *host = st->rrdhost; #endif - // Do not export anomaly rates charts. - if (st->state && st->state->is_ar_chart) - return 0; - if (st->exporting_flags == NULL) st->exporting_flags = callocz(instance->engine->instance_num, sizeof(size_t)); @@ -69,22 +65,22 @@ int rrdset_is_exportable(struct instance *instance, RRDSET *st) if(unlikely(!(*flags & RRDSET_FLAG_EXPORTING_SEND))) { // we have not checked this chart - if(simple_pattern_matches(instance->config.charts_pattern, st->id) || simple_pattern_matches(instance->config.charts_pattern, st->name)) + if(simple_pattern_matches(instance->config.charts_pattern, rrdset_id(st)) || simple_pattern_matches(instance->config.charts_pattern, rrdset_name(st))) *flags |= RRDSET_FLAG_EXPORTING_SEND; else { *flags |= RRDSET_FLAG_EXPORTING_IGNORE; - debug(D_EXPORTING, "EXPORTING: not sending chart '%s' of host '%s', because it is disabled for exporting.", st->id, host->hostname); + debug(D_EXPORTING, "EXPORTING: not sending chart '%s' of host '%s', because it is disabled for exporting.", rrdset_id(st), rrdhost_hostname(host)); return 0; } } if(unlikely(!rrdset_is_available_for_exporting_and_alarms(st))) { - debug(D_EXPORTING, "EXPORTING: not sending chart '%s' of host '%s', because it is not available for exporting.", st->id, host->hostname); + debug(D_EXPORTING, "EXPORTING: not sending chart '%s' of host '%s', because it is not available for exporting.", rrdset_id(st), rrdhost_hostname(host)); return 0; } if(unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_NONE && !(EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED))) { - debug(D_EXPORTING, "EXPORTING: not sending chart '%s' of host '%s' because its memory mode is '%s' and the exporting engine requires database access.", st->id, host->hostname, rrd_memory_mode_name(host->rrd_memory_mode)); + debug(D_EXPORTING, "EXPORTING: not sending chart '%s' of host '%s' because its memory mode is '%s' and the exporting engine requires database access.", rrdset_id(st), rrdhost_hostname(host), rrd_memory_mode_name(host->rrd_memory_mode)); return 0; } diff --git a/exporting/exporting_engine.c b/exporting/exporting_engine.c index faace86d9..fd16d982b 100644 --- a/exporting/exporting_engine.c +++ b/exporting/exporting_engine.c @@ -7,7 +7,7 @@ static struct engine *engine = NULL; void analytics_exporting_connectors_ssl(BUFFER *b) { #ifdef ENABLE_HTTPS - if (netdata_exporting_ctx) { + if (netdata_ssl_exporting_ctx) { for (struct instance *instance = engine->instance_root; instance; instance = instance->next) { struct simple_connector_data *connector_specific_data = instance->connector_specific_data; if (connector_specific_data->flags == NETDATA_SSL_HANDSHAKE_COMPLETE) { diff --git a/exporting/exporting_engine.h b/exporting/exporting_engine.h index 2141caa41..5f961c303 100644 --- a/exporting/exporting_engine.h +++ b/exporting/exporting_engine.h @@ -271,7 +271,7 @@ size_t exporting_name_copy(char *dst, const char *src, size_t max_len); int rrdhost_is_exportable(struct instance *instance, RRDHOST *host); int rrdset_is_exportable(struct instance *instance, RRDSET *st); -extern EXPORTING_OPTIONS exporting_parse_data_source(const char *source, EXPORTING_OPTIONS exporting_options); +EXPORTING_OPTIONS exporting_parse_data_source(const char *source, EXPORTING_OPTIONS exporting_options); NETDATA_DOUBLE exporting_calculate_value_from_stored_data( @@ -300,7 +300,7 @@ void create_main_rusage_chart(RRDSET **st_rusage, RRDDIM **rd_user, RRDDIM **rd_ void send_main_rusage(RRDSET *st_rusage, RRDDIM *rd_user, RRDDIM *rd_system); void send_internal_metrics(struct instance *instance); -extern void clean_instance(struct instance *ptr); +void clean_instance(struct instance *ptr); void simple_connector_cleanup(struct instance *instance); static inline void disable_instance(struct instance *instance) diff --git a/exporting/graphite/graphite.c b/exporting/graphite/graphite.c index 8ca094b3b..0b33f6428 100644 --- a/exporting/graphite/graphite.c +++ b/exporting/graphite/graphite.c @@ -101,7 +101,7 @@ int format_host_labels_graphite_plaintext(struct instance *instance, RRDHOST *ho if (unlikely(!sending_labels_configured(instance))) return 0; - rrdlabels_to_buffer(host->host_labels, instance->labels_buffer, ";", "=", "", "", + rrdlabels_to_buffer(host->rrdlabels, instance->labels_buffer, ";", "=", "", "", exporting_labels_filter_callback, instance, NULL, sanitize_graphite_label_value); @@ -123,24 +123,24 @@ int format_dimension_collected_graphite_plaintext(struct instance *instance, RRD char chart_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( chart_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? st->name : st->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), RRD_ID_LENGTH_MAX); char dimension_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( dimension_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rd->name : rd->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), RRD_ID_LENGTH_MAX); buffer_sprintf( instance->buffer, "%s.%s.%s.%s%s%s%s " COLLECTED_NUMBER_FORMAT " %llu\n", instance->config.prefix, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), chart_name, dimension_name, (host->tags) ? ";" : "", - (host->tags) ? host->tags : "", + (host->tags) ? rrdhost_tags(host) : "", (instance->labels_buffer) ? buffer_tostring(instance->labels_buffer) : "", rd->last_collected_value, (unsigned long long)rd->last_collected_time.tv_sec); @@ -163,13 +163,13 @@ int format_dimension_stored_graphite_plaintext(struct instance *instance, RRDDIM char chart_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( chart_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? st->name : st->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), RRD_ID_LENGTH_MAX); char dimension_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( dimension_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rd->name : rd->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), RRD_ID_LENGTH_MAX); time_t last_t; @@ -182,11 +182,11 @@ int format_dimension_stored_graphite_plaintext(struct instance *instance, RRDDIM instance->buffer, "%s.%s.%s.%s%s%s%s " NETDATA_DOUBLE_FORMAT " %llu\n", instance->config.prefix, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), chart_name, dimension_name, (host->tags) ? ";" : "", - (host->tags) ? host->tags : "", + (host->tags) ? rrdhost_tags(host) : "", (instance->labels_buffer) ? buffer_tostring(instance->labels_buffer) : "", value, (unsigned long long)last_t); @@ -214,7 +214,7 @@ void graphite_http_prepare_header(struct instance *instance) "\r\n", instance->config.destination, simple_connector_data->auth_string ? simple_connector_data->auth_string : "", - buffer_strlen(simple_connector_data->last_buffer->buffer)); + (unsigned long int) buffer_strlen(simple_connector_data->last_buffer->buffer)); return; } diff --git a/exporting/json/json.c b/exporting/json/json.c index 45a8c9d9f..dd53f6f0a 100644 --- a/exporting/json/json.c +++ b/exporting/json/json.c @@ -125,7 +125,7 @@ int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host) return 0; buffer_strcat(instance->labels_buffer, "\"labels\":{"); - rrdlabels_to_buffer(host->host_labels, instance->labels_buffer, "", ":", "\"", ",", + rrdlabels_to_buffer(host->rrdlabels, instance->labels_buffer, "", ":", "\"", ",", exporting_labels_filter_callback, instance, NULL, sanitize_json_string); buffer_strcat(instance->labels_buffer, "},"); @@ -145,7 +145,7 @@ int format_dimension_collected_json_plaintext(struct instance *instance, RRDDIM RRDSET *st = rd->rrdset; RRDHOST *host = st->rrdhost; - const char *tags_pre = "", *tags_post = "", *tags = host->tags; + const char *tags_pre = "", *tags_post = "", *tags = rrdhost_tags(host); if (!tags) tags = ""; @@ -187,21 +187,20 @@ int format_dimension_collected_json_plaintext(struct instance *instance, RRDDIM "\"timestamp\":%llu}", instance->config.prefix, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), tags_pre, tags, tags_post, instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "", - st->id, - st->name, - st->family, - st->context, - st->type, - st->units, - - rd->id, - rd->name, + rrdset_id(st), + rrdset_name(st), + rrdset_family(st), + rrdset_context(st), + rrdset_parts_type(st), + rrdset_units(st), + rrddim_id(rd), + rrddim_name(rd), rd->last_collected_value, (unsigned long long)rd->last_collected_time.tv_sec); @@ -231,7 +230,7 @@ int format_dimension_stored_json_plaintext(struct instance *instance, RRDDIM *rd if(isnan(value)) return 0; - const char *tags_pre = "", *tags_post = "", *tags = host->tags; + const char *tags_pre = "", *tags_post = "", *tags = rrdhost_tags(host); if (!tags) tags = ""; @@ -272,21 +271,20 @@ int format_dimension_stored_json_plaintext(struct instance *instance, RRDDIM *rd "\"timestamp\": %llu}", instance->config.prefix, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), tags_pre, tags, tags_post, instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "", - st->id, - st->name, - st->family, - st->context, - st->type, - st->units, - - rd->id, - rd->name, + rrdset_id(st), + rrdset_name(st), + rrdset_family(st), + rrdset_context(st), + rrdset_parts_type(st), + rrdset_units(st), + rrddim_id(rd), + rrddim_name(rd), value, (unsigned long long)last_t); @@ -346,7 +344,7 @@ void json_http_prepare_header(struct instance *instance) "\r\n", instance->config.destination, simple_connector_data->auth_string ? simple_connector_data->auth_string : "", - buffer_strlen(simple_connector_data->last_buffer->buffer)); + (unsigned long int) buffer_strlen(simple_connector_data->last_buffer->buffer)); return; } diff --git a/exporting/opentsdb/opentsdb.c b/exporting/opentsdb/opentsdb.c index 282de2e6b..a974c1264 100644 --- a/exporting/opentsdb/opentsdb.c +++ b/exporting/opentsdb/opentsdb.c @@ -156,7 +156,7 @@ int format_host_labels_opentsdb_telnet(struct instance *instance, RRDHOST *host) return 0; buffer_strcat(instance->labels_buffer, " "); - rrdlabels_to_buffer(host->host_labels, instance->labels_buffer, "", "=", "", " ", + rrdlabels_to_buffer(host->rrdlabels, instance->labels_buffer, "", "=", "", " ", exporting_labels_filter_callback, instance, NULL, sanitize_opentsdb_label_value); return 0; @@ -177,13 +177,13 @@ int format_dimension_collected_opentsdb_telnet(struct instance *instance, RRDDIM char chart_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( chart_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? st->name : st->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), RRD_ID_LENGTH_MAX); char dimension_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( dimension_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rd->name : rd->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), RRD_ID_LENGTH_MAX); buffer_sprintf( @@ -194,9 +194,9 @@ int format_dimension_collected_opentsdb_telnet(struct instance *instance, RRDDIM dimension_name, (unsigned long long)rd->last_collected_time.tv_sec, rd->last_collected_value, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), (host->tags) ? " " : "", - (host->tags) ? host->tags : "", + (host->tags) ? rrdhost_tags(host) : "", (instance->labels_buffer) ? buffer_tostring(instance->labels_buffer) : ""); return 0; @@ -217,13 +217,13 @@ int format_dimension_stored_opentsdb_telnet(struct instance *instance, RRDDIM *r char chart_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( chart_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? st->name : st->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), RRD_ID_LENGTH_MAX); char dimension_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( dimension_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rd->name : rd->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), RRD_ID_LENGTH_MAX); time_t last_t; @@ -240,9 +240,9 @@ int format_dimension_stored_opentsdb_telnet(struct instance *instance, RRDDIM *r dimension_name, (unsigned long long)last_t, value, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), (host->tags) ? " " : "", - (host->tags) ? host->tags : "", + (host->tags) ? rrdhost_tags(host) : "", (instance->labels_buffer) ? buffer_tostring(instance->labels_buffer) : ""); return 0; @@ -268,7 +268,7 @@ void opentsdb_http_prepare_header(struct instance *instance) "\r\n", instance->config.destination, simple_connector_data->auth_string ? simple_connector_data->auth_string : "", - buffer_strlen(simple_connector_data->last_buffer->buffer)); + (unsigned long int) buffer_strlen(simple_connector_data->last_buffer->buffer)); return; } @@ -288,7 +288,7 @@ int format_host_labels_opentsdb_http(struct instance *instance, RRDHOST *host) { if (unlikely(!sending_labels_configured(instance))) return 0; - rrdlabels_to_buffer(host->host_labels, instance->labels_buffer, ",", ":", "\"", "", + rrdlabels_to_buffer(host->rrdlabels, instance->labels_buffer, ",", ":", "\"", "", exporting_labels_filter_callback, instance, NULL, sanitize_opentsdb_label_value); return 0; @@ -309,13 +309,13 @@ int format_dimension_collected_opentsdb_http(struct instance *instance, RRDDIM * char chart_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( chart_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? st->name : st->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), RRD_ID_LENGTH_MAX); char dimension_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( dimension_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rd->name : rd->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), RRD_ID_LENGTH_MAX); if (buffer_strlen((BUFFER *)instance->buffer) > 2) @@ -336,9 +336,9 @@ int format_dimension_collected_opentsdb_http(struct instance *instance, RRDDIM * dimension_name, (unsigned long long)rd->last_collected_time.tv_sec, rd->last_collected_value, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), (host->tags) ? " " : "", - (host->tags) ? host->tags : "", + (host->tags) ? rrdhost_tags(host) : "", instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : ""); return 0; @@ -359,13 +359,13 @@ int format_dimension_stored_opentsdb_http(struct instance *instance, RRDDIM *rd) char chart_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( chart_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? st->name : st->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), RRD_ID_LENGTH_MAX); char dimension_name[RRD_ID_LENGTH_MAX + 1]; exporting_name_copy( dimension_name, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rd->name : rd->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), RRD_ID_LENGTH_MAX); time_t last_t; @@ -392,9 +392,9 @@ int format_dimension_stored_opentsdb_http(struct instance *instance, RRDDIM *rd) dimension_name, (unsigned long long)last_t, value, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), (host->tags) ? " " : "", - (host->tags) ? host->tags : "", + (host->tags) ? rrdhost_tags(host) : "", instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : ""); return 0; diff --git a/exporting/process_data.c b/exporting/process_data.c index d5138b787..fbcda0d9b 100644 --- a/exporting/process_data.c +++ b/exporting/process_data.c @@ -77,10 +77,10 @@ NETDATA_DOUBLE exporting_calculate_value_from_stored_data( time_t before = instance->before; // find the edges of the rrd database for this chart - time_t first_t = rd->tiers[0]->query_ops.oldest_time(rd->tiers[0]->db_metric_handle); - time_t last_t = rd->tiers[0]->query_ops.latest_time(rd->tiers[0]->db_metric_handle); + time_t first_t = rd->tiers[0]->query_ops->oldest_time(rd->tiers[0]->db_metric_handle); + time_t last_t = rd->tiers[0]->query_ops->latest_time(rd->tiers[0]->db_metric_handle); time_t update_every = st->update_every; - struct rrddim_query_handle handle; + struct storage_engine_query_handle handle; // step back a little, to make sure we have complete data collection // for all metrics @@ -110,9 +110,9 @@ NETDATA_DOUBLE exporting_calculate_value_from_stored_data( debug( D_EXPORTING, "EXPORTING: %s.%s.%s: aligned timeframe %lu to %lu is outside the chart's database range %lu to %lu", - host->hostname, - st->id, - rd->id, + rrdhost_hostname(host), + rrdset_id(st), + rrddim_id(rd), (unsigned long)after, (unsigned long)before, (unsigned long)first_t, @@ -122,11 +122,13 @@ NETDATA_DOUBLE exporting_calculate_value_from_stored_data( *last_timestamp = before; + size_t points_read = 0; size_t counter = 0; NETDATA_DOUBLE sum = 0; - for (rd->tiers[0]->query_ops.init(rd->tiers[0]->db_metric_handle, &handle, after, before, TIER_QUERY_FETCH_SUM); !rd->tiers[0]->query_ops.is_finished(&handle);) { - STORAGE_POINT sp = rd->tiers[0]->query_ops.next_metric(&handle); + for (rd->tiers[0]->query_ops->init(rd->tiers[0]->db_metric_handle, &handle, after, before); !rd->tiers[0]->query_ops->is_finished(&handle);) { + STORAGE_POINT sp = rd->tiers[0]->query_ops->next_metric(&handle); + points_read++; if (unlikely(storage_point_is_empty(sp))) { // not collected @@ -136,15 +138,16 @@ NETDATA_DOUBLE exporting_calculate_value_from_stored_data( sum += sp.sum; counter += sp.count; } - rd->tiers[0]->query_ops.finalize(&handle); + rd->tiers[0]->query_ops->finalize(&handle); + global_statistics_exporters_query_completed(points_read); if (unlikely(!counter)) { debug( D_EXPORTING, "EXPORTING: %s.%s.%s: no values stored in database for range %lu to %lu", - host->hostname, - st->id, - rd->id, + rrdhost_hostname(host), + rrdset_id(st), + rrddim_id(rd), (unsigned long)after, (unsigned long)before); return NAN; @@ -338,26 +341,22 @@ void prepare_buffers(struct engine *engine) rrd_rdlock(); RRDHOST *host; - rrdhost_foreach_read(host) - { - rrdhost_rdlock(host); + rrdhost_foreach_read(host) { start_host_formatting(engine, host); RRDSET *st; - rrdset_foreach_read(st, host) - { - rrdset_rdlock(st); + rrdset_foreach_read(st, host) { start_chart_formatting(engine, st); RRDDIM *rd; rrddim_foreach_read(rd, st) metric_formatting(engine, rd); + rrddim_foreach_done(rd); end_chart_formatting(engine, st); - rrdset_unlock(st); } + rrdset_foreach_done(st); variables_formatting(engine, host); end_host_formatting(engine, host); - rrdhost_unlock(host); } rrd_unlock(); netdata_thread_enable_cancelability(); diff --git a/exporting/prometheus/README.md b/exporting/prometheus/README.md index 5c15ca580..ae94867fa 100644 --- a/exporting/prometheus/README.md +++ b/exporting/prometheus/README.md @@ -4,7 +4,7 @@ description: "Export Netdata metrics to Prometheus for archiving and further ana custom_edit_url: https://github.com/netdata/netdata/edit/master/exporting/prometheus/README.md sidebar_label: "Using Netdata with Prometheus" --> -import { OneLineInstallWget, OneLineInstallCurl } from '../../../src/components/OneLineInstall/' +import { OneLineInstallWget, OneLineInstallCurl } from '@site/src/components/OneLineInstall/' # Using Netdata with Prometheus diff --git a/exporting/prometheus/prometheus.c b/exporting/prometheus/prometheus.c index 7d632164f..294d8ec2c 100644 --- a/exporting/prometheus/prometheus.c +++ b/exporting/prometheus/prometheus.c @@ -9,9 +9,9 @@ static int is_matches_rrdset(struct instance *instance, RRDSET *st, SIMPLE_PATTERN *filter) { if (instance->config.options & EXPORTING_OPTION_SEND_NAMES) { - return simple_pattern_matches(filter, st->name); + return simple_pattern_matches(filter, rrdset_name(st)); } - return simple_pattern_matches(filter, st->id); + return simple_pattern_matches(filter, rrdset_id(st)); } /** @@ -28,10 +28,6 @@ inline int can_send_rrdset(struct instance *instance, RRDSET *st, SIMPLE_PATTERN RRDHOST *host = st->rrdhost; #endif - // Do not send anomaly rates charts. - if (st->state && st->state->is_ar_chart) - return 0; - if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_EXPORTING_IGNORE))) return 0; @@ -48,8 +44,8 @@ inline int can_send_rrdset(struct instance *instance, RRDSET *st, SIMPLE_PATTERN debug( D_EXPORTING, "EXPORTING: not sending chart '%s' of host '%s', because it is disabled for exporting.", - st->id, - host->hostname); + rrdset_id(st), + rrdhost_hostname(host)); return 0; } } @@ -58,8 +54,8 @@ inline int can_send_rrdset(struct instance *instance, RRDSET *st, SIMPLE_PATTERN debug( D_EXPORTING, "EXPORTING: not sending chart '%s' of host '%s', because it is not available for exporting.", - st->id, - host->hostname); + rrdset_id(st), + rrdhost_hostname(host)); return 0; } @@ -69,8 +65,8 @@ inline int can_send_rrdset(struct instance *instance, RRDSET *st, SIMPLE_PATTERN debug( D_EXPORTING, "EXPORTING: not sending chart '%s' of host '%s' because its memory mode is '%s' and the exporting connector requires database access.", - st->id, - host->hostname, + rrdset_id(st), + rrdhost_hostname(host), rrd_memory_mode_name(host->rrd_memory_mode)); return 0; } @@ -327,7 +323,7 @@ void format_host_labels_prometheus(struct instance *instance, RRDHOST *host) .instance = instance, .count = 0 }; - rrdlabels_walkthrough_read(host->host_labels, format_prometheus_label_callback, &tmp); + rrdlabels_walkthrough_read(host->rrdlabels, format_prometheus_label_callback, &tmp); } struct host_variables_callback_options { @@ -349,11 +345,12 @@ struct host_variables_callback_options { * @param data callback options. * @return Returns 1 if the chart can be sent, 0 otherwise. */ -static int print_host_variables(RRDVAR *rv, void *data) -{ +static int print_host_variables_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rv_ptr __maybe_unused, void *data) { + const RRDVAR_ACQUIRED *rv = (const RRDVAR_ACQUIRED *)item; + struct host_variables_callback_options *opts = data; - if (rv->options & (RRDVAR_OPTION_CUSTOM_HOST_VAR | RRDVAR_OPTION_CUSTOM_CHART_VAR)) { + if (rrdvar_flags(rv) & (RRDVAR_FLAG_CUSTOM_HOST_VAR | RRDVAR_FLAG_CUSTOM_CHART_VAR)) { if (!opts->host_header_printed) { opts->host_header_printed = 1; @@ -366,7 +363,7 @@ static int print_host_variables(RRDVAR *rv, void *data) if (isnan(value) || isinf(value)) { if (opts->output_options & PROMETHEUS_OUTPUT_HELP) buffer_sprintf( - opts->wb, "# COMMENT variable \"%s\" is %s. Skipped.\n", rv->name, (isnan(value)) ? "NAN" : "INF"); + opts->wb, "# COMMENT variable \"%s\" is %s. Skipped.\n", rrdvar_name(rv), (isnan(value)) ? "NAN" : "INF"); return 0; } @@ -378,7 +375,7 @@ static int print_host_variables(RRDVAR *rv, void *data) label_post = "}"; } - prometheus_name_copy(opts->name, rv->name, sizeof(opts->name)); + prometheus_name_copy(opts->name, rrdvar_name(rv), sizeof(opts->name)); if (opts->output_options & PROMETHEUS_OUTPUT_TIMESTAMPS) buffer_sprintf( @@ -445,17 +442,17 @@ static void generate_as_collected_prom_help(BUFFER *wb, struct gen_parameters *p wb, "%s: chart \"%s\", context \"%s\", family \"%s\", dimension \"%s\", value * ", p->suffix, - (p->output_options & PROMETHEUS_OUTPUT_NAMES && p->st->name) ? p->st->name : p->st->id, - p->st->context, - p->st->family, - (p->output_options & PROMETHEUS_OUTPUT_NAMES && p->rd->name) ? p->rd->name : p->rd->id); + (p->output_options & PROMETHEUS_OUTPUT_NAMES && p->st->name) ? rrdset_name(p->st) : rrdset_id(p->st), + rrdset_context(p->st), + rrdset_family(p->st), + (p->output_options & PROMETHEUS_OUTPUT_NAMES && p->rd->name) ? rrddim_name(p->rd) : rrddim_id(p->rd)); if (prometheus_collector) buffer_sprintf(wb, "1 / 1"); else buffer_sprintf(wb, COLLECTED_NUMBER_FORMAT " / " COLLECTED_NUMBER_FORMAT, p->rd->multiplier, p->rd->divisor); - buffer_sprintf(wb, " %s %s (%s)\n", p->relation, p->st->units, p->type); + buffer_sprintf(wb, " %s %s (%s)\n", p->relation, rrdset_units(p->st), p->type); } /** @@ -518,10 +515,9 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( PROMETHEUS_OUTPUT_OPTIONS output_options) { SIMPLE_PATTERN *filter = simple_pattern_create(filter_string, NULL, SIMPLE_PATTERN_EXACT); - rrdhost_rdlock(host); char hostname[PROMETHEUS_ELEMENT_MAX + 1]; - prometheus_label_copy(hostname, host->hostname, PROMETHEUS_ELEMENT_MAX); + prometheus_label_copy(hostname, rrdhost_hostname(host), PROMETHEUS_ELEMENT_MAX); format_host_labels_prometheus(instance, host); @@ -529,8 +525,8 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( wb, "netdata_info{instance=\"%s\",application=\"%s\",version=\"%s\"", hostname, - host->program_name, - host->program_version); + rrdhost_program_name(host), + rrdhost_program_version(host)); if (instance->labels_buffer && *buffer_tostring(instance->labels_buffer)) { buffer_sprintf(wb, ",%s", buffer_tostring(instance->labels_buffer)); @@ -551,34 +547,34 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( // send custom variables set for the host if (output_options & PROMETHEUS_OUTPUT_VARIABLES) { - struct host_variables_callback_options opts = { .host = host, - .wb = wb, - .labels = (labels[0] == ',') ? &labels[1] : labels, - .exporting_options = exporting_options, - .output_options = output_options, - .prefix = prefix, - .now = now_realtime_sec(), - .host_header_printed = 0 }; - foreach_host_variable_callback(host, print_host_variables, &opts); + + struct host_variables_callback_options opts = { + .host = host, + .wb = wb, + .labels = (labels[0] == ',') ? &labels[1] : labels, + .exporting_options = exporting_options, + .output_options = output_options, + .prefix = prefix, + .now = now_realtime_sec(), + .host_header_printed = 0 + }; + + rrdvar_walkthrough_read(host->rrdvars, print_host_variables_callback, &opts); } // for each chart RRDSET *st; - rrdset_foreach_read(st, host) - { + rrdset_foreach_read(st, host) { if (likely(can_send_rrdset(instance, st, filter))) { - rrdset_rdlock(st); - char chart[PROMETHEUS_ELEMENT_MAX + 1]; char context[PROMETHEUS_ELEMENT_MAX + 1]; char family[PROMETHEUS_ELEMENT_MAX + 1]; char units[PROMETHEUS_ELEMENT_MAX + 1] = ""; - prometheus_label_copy( - chart, (output_options & PROMETHEUS_OUTPUT_NAMES && st->name) ? st->name : st->id, PROMETHEUS_ELEMENT_MAX); - prometheus_label_copy(family, st->family, PROMETHEUS_ELEMENT_MAX); - prometheus_name_copy(context, st->context, PROMETHEUS_ELEMENT_MAX); + prometheus_label_copy(chart, (output_options & PROMETHEUS_OUTPUT_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), PROMETHEUS_ELEMENT_MAX); + prometheus_label_copy(family, rrdset_family(st), PROMETHEUS_ELEMENT_MAX); + prometheus_name_copy(context, rrdset_context(st), PROMETHEUS_ELEMENT_MAX); int as_collected = (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_AS_COLLECTED); int homogeneous = 1; @@ -590,13 +586,13 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( if (rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) homogeneous = 0; - if (st->module_name && !strcmp(st->module_name, "prometheus")) + if (!strcmp(rrdset_module_name(st), "prometheus")) prometheus_collector = 1; } else { if (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_AVERAGE && !(output_options & PROMETHEUS_OUTPUT_HIDEUNITS)) prometheus_units_copy( - units, st->units, PROMETHEUS_ELEMENT_MAX, output_options & PROMETHEUS_OUTPUT_OLDUNITS); + units, rrdset_units(st), PROMETHEUS_ELEMENT_MAX, output_options & PROMETHEUS_OUTPUT_OLDUNITS); } if (unlikely(output_options & PROMETHEUS_OUTPUT_HELP)) @@ -604,15 +600,14 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( wb, "\n# COMMENT %s chart \"%s\", context \"%s\", family \"%s\", units \"%s\"\n", (homogeneous) ? "homogeneous" : "heterogeneous", - (output_options & PROMETHEUS_OUTPUT_NAMES && st->name) ? st->name : st->id, - st->context, - st->family, - st->units); + (output_options & PROMETHEUS_OUTPUT_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), + rrdset_context(st), + rrdset_family(st), + rrdset_units(st)); // for each dimension RRDDIM *rd; - rrddim_foreach_read(rd, st) - { + rrddim_foreach_read(rd, st) { if (rd->collections_counter && !rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) { char dimension[PROMETHEUS_ELEMENT_MAX + 1]; char *suffix = ""; @@ -651,7 +646,7 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( prometheus_label_copy( dimension, - (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id, + (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), PROMETHEUS_ELEMENT_MAX); if (unlikely(output_options & PROMETHEUS_OUTPUT_HELP)) @@ -661,13 +656,14 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( buffer_sprintf(wb, "# TYPE %s_%s%s %s\n", prefix, context, suffix, p.type); generate_as_collected_prom_metric(wb, &p, homogeneous, prometheus_collector); - } else { + } + else { // the dimensions of the chart, do not have the same algorithm, multiplier or divisor // we create a metric per dimension prometheus_name_copy( dimension, - (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id, + (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), PROMETHEUS_ELEMENT_MAX); if (unlikely(output_options & PROMETHEUS_OUTPUT_HELP)) @@ -679,7 +675,8 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( generate_as_collected_prom_metric(wb, &p, homogeneous, prometheus_collector); } - } else { + } + else { // we need average or sum of the data time_t first_time = instance->after; @@ -694,7 +691,7 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( prometheus_label_copy( dimension, - (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id, + (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), PROMETHEUS_ELEMENT_MAX); if (unlikely(output_options & PROMETHEUS_OUTPUT_HELP)) @@ -705,8 +702,8 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( context, units, suffix, - (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id, - st->units, + (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), + rrdset_units(st), (unsigned long long)first_time, (unsigned long long)last_time); @@ -746,12 +743,11 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus( } } } - - rrdset_unlock(st); + rrddim_foreach_done(rd); } } + rrdset_foreach_done(st); - rrdhost_unlock(host); simple_pattern_free(filter); } @@ -809,7 +805,7 @@ static inline time_t prometheus_preparation( buffer_sprintf( wb, "# COMMENT netdata \"%s\" to %sprometheus \"%s\", source \"%s\", last seen %lu %s, time range %lu to %lu\n\n", - host->hostname, + rrdhost_hostname(host), (first_seen) ? "FIRST SEEN " : "", server, mode, diff --git a/exporting/prometheus/prometheus.h b/exporting/prometheus/prometheus.h index 4b8860ded..e80b682ae 100644 --- a/exporting/prometheus/prometheus.h +++ b/exporting/prometheus/prometheus.h @@ -22,10 +22,10 @@ typedef enum prometheus_output_flags { PROMETHEUS_OUTPUT_HIDEUNITS = (1 << 6) } PROMETHEUS_OUTPUT_OPTIONS; -extern void rrd_stats_api_v1_charts_allmetrics_prometheus_single_host( +void rrd_stats_api_v1_charts_allmetrics_prometheus_single_host( RRDHOST *host, const char *filter_string, BUFFER *wb, const char *server, const char *prefix, EXPORTING_OPTIONS exporting_options, PROMETHEUS_OUTPUT_OPTIONS output_options); -extern void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts( +void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts( RRDHOST *host, const char *filter_string, BUFFER *wb, const char *server, const char *prefix, EXPORTING_OPTIONS exporting_options, PROMETHEUS_OUTPUT_OPTIONS output_options); @@ -36,6 +36,6 @@ char *prometheus_units_copy(char *d, const char *s, size_t usable, int showoldun void format_host_labels_prometheus(struct instance *instance, RRDHOST *host); -extern void prometheus_clean_server_root(); +void prometheus_clean_server_root(); #endif //NETDATA_EXPORTING_PROMETHEUS_H diff --git a/exporting/prometheus/remote_write/remote_write.c b/exporting/prometheus/remote_write/remote_write.c index 03feb2c08..2e2fa3c12 100644 --- a/exporting/prometheus/remote_write/remote_write.c +++ b/exporting/prometheus/remote_write/remote_write.c @@ -171,19 +171,19 @@ int format_host_prometheus_remote_write(struct instance *instance, RRDHOST *host char hostname[PROMETHEUS_ELEMENT_MAX + 1]; prometheus_label_copy( hostname, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), PROMETHEUS_ELEMENT_MAX); add_host_info( connector_specific_data->write_request, - "netdata_info", hostname, host->program_name, host->program_version, now_realtime_usec() / USEC_PER_MS); + "netdata_info", hostname, rrdhost_program_name(host), rrdhost_program_version(host), now_realtime_usec() / USEC_PER_MS); if (unlikely(sending_labels_configured(instance))) { struct format_remote_write_label_callback tmp = { .write_request = connector_specific_data->write_request, .instance = instance }; - rrdlabels_walkthrough_read(host->host_labels, format_remote_write_label_callback, &tmp); + rrdlabels_walkthrough_read(host->rrdlabels, format_remote_write_label_callback, &tmp); } return 0; @@ -200,10 +200,10 @@ int format_chart_prometheus_remote_write(struct instance *instance, RRDSET *st) { prometheus_label_copy( chart, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? st->name : st->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), PROMETHEUS_ELEMENT_MAX); - prometheus_label_copy(family, st->family, PROMETHEUS_ELEMENT_MAX); - prometheus_name_copy(context, st->context, PROMETHEUS_ELEMENT_MAX); + prometheus_label_copy(family, rrdset_family(st), PROMETHEUS_ELEMENT_MAX); + prometheus_name_copy(context, rrdset_context(st), PROMETHEUS_ELEMENT_MAX); as_collected = (EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED); homogeneous = 1; @@ -215,7 +215,7 @@ int format_chart_prometheus_remote_write(struct instance *instance, RRDSET *st) homogeneous = 0; } else { if (EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AVERAGE) - prometheus_units_copy(units, st->units, PROMETHEUS_ELEMENT_MAX, 0); + prometheus_units_copy(units, rrdset_units(st), PROMETHEUS_ELEMENT_MAX, 0); } return 0; @@ -249,28 +249,33 @@ int format_dimension_prometheus_remote_write(struct instance *instance, RRDDIM * D_EXPORTING, "EXPORTING: not sending dimension '%s' of chart '%s' from host '%s', " "its last data collection (%lu) is not within our timeframe (%lu to %lu)", - rd->id, rd->rrdset->id, - (host == localhost) ? instance->config.hostname : host->hostname, + rrddim_id(rd), rrdset_id(rd->rrdset), + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), (unsigned long)rd->last_collected_time.tv_sec, (unsigned long)instance->after, (unsigned long)instance->before); return 0; } + if (rd->algorithm == RRD_ALGORITHM_INCREMENTAL || rd->algorithm == RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL) { + if (strcmp(rrdset_module_name(rd->rrdset), "prometheus")) + suffix = "_total"; + } + if (homogeneous) { // all the dimensions of the chart, has the same algorithm, multiplier and divisor // we add all dimensions as labels prometheus_label_copy( dimension, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rd->name : rd->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), PROMETHEUS_ELEMENT_MAX); snprintf(name, PROMETHEUS_LABELS_MAX, "%s_%s%s", instance->config.prefix, context, suffix); add_metric( connector_specific_data->write_request, name, chart, family, dimension, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), rd->last_collected_value, timeval_msec(&rd->last_collected_time)); } else { // the dimensions of the chart, do not have the same algorithm, multiplier or divisor @@ -278,7 +283,7 @@ int format_dimension_prometheus_remote_write(struct instance *instance, RRDDIM * prometheus_name_copy( dimension, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rd->name : rd->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), PROMETHEUS_ELEMENT_MAX); snprintf( name, PROMETHEUS_LABELS_MAX, "%s_%s_%s%s", instance->config.prefix, context, dimension, @@ -287,7 +292,7 @@ int format_dimension_prometheus_remote_write(struct instance *instance, RRDDIM * add_metric( connector_specific_data->write_request, name, chart, family, NULL, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), rd->last_collected_value, timeval_msec(&rd->last_collected_time)); } } else { @@ -304,7 +309,7 @@ int format_dimension_prometheus_remote_write(struct instance *instance, RRDDIM * prometheus_label_copy( dimension, - (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rd->name : rd->id, + (instance->config.options & EXPORTING_OPTION_SEND_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd), PROMETHEUS_ELEMENT_MAX); snprintf( name, PROMETHEUS_LABELS_MAX, "%s_%s%s%s", instance->config.prefix, context, units, suffix); @@ -312,7 +317,7 @@ int format_dimension_prometheus_remote_write(struct instance *instance, RRDDIM * add_metric( connector_specific_data->write_request, name, chart, family, dimension, - (host == localhost) ? instance->config.hostname : host->hostname, + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), value, last_t * MSEC_PER_SEC); } } @@ -321,10 +326,12 @@ int format_dimension_prometheus_remote_write(struct instance *instance, RRDDIM * return 0; } -int format_variable_prometheus_remote_write_callback(RRDVAR *rv, void *data) { +static int format_variable_prometheus_remote_write_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rv_ptr __maybe_unused, void *data) { + const RRDVAR_ACQUIRED *rv = (const RRDVAR_ACQUIRED *)item; + struct prometheus_remote_write_variables_callback_options *opts = data; - if (rv->options & (RRDVAR_OPTION_CUSTOM_HOST_VAR | RRDVAR_OPTION_CUSTOM_CHART_VAR)) { + if (rrdvar_flags(rv) & (RRDVAR_FLAG_CUSTOM_HOST_VAR | RRDVAR_FLAG_CUSTOM_CHART_VAR)) { RRDHOST *host = opts->host; struct instance *instance = opts->instance; struct simple_connector_data *simple_connector_data = @@ -335,12 +342,12 @@ int format_variable_prometheus_remote_write_callback(RRDVAR *rv, void *data) { char name[PROMETHEUS_LABELS_MAX + 1]; char *suffix = ""; - prometheus_name_copy(context, rv->name, PROMETHEUS_ELEMENT_MAX); + prometheus_name_copy(context, rrdvar_name(rv), PROMETHEUS_ELEMENT_MAX); snprintf(name, PROMETHEUS_LABELS_MAX, "%s_%s%s", instance->config.prefix, context, suffix); NETDATA_DOUBLE value = rrdvar2number(rv); add_variable(connector_specific_data->write_request, name, - (host == localhost) ? instance->config.hostname : host->hostname, value, opts->now / USEC_PER_MS); + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), value, opts->now / USEC_PER_MS); } return 0; @@ -361,7 +368,7 @@ int format_variables_prometheus_remote_write(struct instance *instance, RRDHOST .now = now_realtime_usec(), }; - return foreach_host_variable_callback(host, format_variable_prometheus_remote_write_callback, &opt); + return rrdvar_walkthrough_read(host->rrdvars, format_variable_prometheus_remote_write_callback, &opt); } /** diff --git a/exporting/prometheus/remote_write/remote_write.h b/exporting/prometheus/remote_write/remote_write.h index 4740772d0..d4e86494b 100644 --- a/exporting/prometheus/remote_write/remote_write.h +++ b/exporting/prometheus/remote_write/remote_write.h @@ -18,7 +18,7 @@ struct prometheus_remote_write_variables_callback_options { }; int init_prometheus_remote_write_instance(struct instance *instance); -extern void clean_prometheus_remote_write(struct instance *instance); +void clean_prometheus_remote_write(struct instance *instance); int format_host_prometheus_remote_write(struct instance *instance, RRDHOST *host); int format_chart_prometheus_remote_write(struct instance *instance, RRDSET *st); diff --git a/exporting/send_data.c b/exporting/send_data.c index ed649b640..1d20f3b74 100644 --- a/exporting/send_data.c +++ b/exporting/send_data.c @@ -313,7 +313,7 @@ void simple_connector_worker(void *instance_p) if (unlikely(sock == -1)) { size_t reconnects = 0; - sock = connect_to_one_of( + sock = connect_to_one_of_urls( instance->config.destination, connector_specific_config->default_port, &timeout, @@ -322,12 +322,12 @@ void simple_connector_worker(void *instance_p) CONNECTED_TO_MAX); #ifdef ENABLE_HTTPS if (exporting_tls_is_enabled(instance->config.type, options) && sock != -1) { - if (netdata_exporting_ctx) { + if (netdata_ssl_exporting_ctx) { if (sock_delnonblock(sock) < 0) error("Exporting cannot remove the non-blocking flag from socket %d", sock); if (connector_specific_data->conn == NULL) { - connector_specific_data->conn = SSL_new(netdata_exporting_ctx); + connector_specific_data->conn = SSL_new(netdata_ssl_exporting_ctx); if (connector_specific_data->conn == NULL) { error("Failed to allocate SSL structure to socket %d.", sock); connector_specific_data->flags = NETDATA_SSL_NO_HANDSHAKE; diff --git a/exporting/send_internal_metrics.c b/exporting/send_internal_metrics.c index defb8d047..515cda3b2 100644 --- a/exporting/send_internal_metrics.c +++ b/exporting/send_internal_metrics.c @@ -11,6 +11,9 @@ */ void create_main_rusage_chart(RRDSET **st_rusage, RRDDIM **rd_user, RRDDIM **rd_system) { + if (!global_statistics_enabled) + return; + if (*st_rusage && *rd_user && *rd_system) return; @@ -31,12 +34,12 @@ void create_main_rusage_chart(RRDSET **st_rusage, RRDDIM **rd_user, RRDDIM **rd_ */ void send_main_rusage(RRDSET *st_rusage, RRDDIM *rd_user, RRDDIM *rd_system) { + if (!global_statistics_enabled) + return; + struct rusage thread; getrusage(RUSAGE_THREAD, &thread); - if (likely(st_rusage->counter_done)) - rrdset_next(st_rusage); - rrddim_set_by_pointer(st_rusage, rd_user, thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec); rrddim_set_by_pointer(st_rusage, rd_system, thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec); @@ -52,6 +55,9 @@ void send_main_rusage(RRDSET *st_rusage, RRDDIM *rd_user, RRDDIM *rd_system) */ void send_internal_metrics(struct instance *instance) { + if (!global_statistics_enabled) + return; + struct stats *stats = &instance->stats; // ------------------------------------------------------------------------ @@ -123,50 +129,28 @@ void send_internal_metrics(struct instance *instance) // ------------------------------------------------------------------------ // update the monitoring charts - if (likely(stats->st_metrics->counter_done)) - rrdset_next(stats->st_metrics); - rrddim_set_by_pointer(stats->st_metrics, stats->rd_buffered_metrics, stats->buffered_metrics); rrddim_set_by_pointer(stats->st_metrics, stats->rd_lost_metrics, stats->lost_metrics); rrddim_set_by_pointer(stats->st_metrics, stats->rd_sent_metrics, stats->sent_metrics); - rrdset_done(stats->st_metrics); - // ------------------------------------------------------------------------ - - if (likely(stats->st_bytes->counter_done)) - rrdset_next(stats->st_bytes); - rrddim_set_by_pointer(stats->st_bytes, stats->rd_buffered_bytes, stats->buffered_bytes); rrddim_set_by_pointer(stats->st_bytes, stats->rd_lost_bytes, stats->lost_bytes); rrddim_set_by_pointer(stats->st_bytes, stats->rd_sent_bytes, stats->sent_bytes); rrddim_set_by_pointer(stats->st_bytes, stats->rd_received_bytes, stats->received_bytes); - rrdset_done(stats->st_bytes); - // ------------------------------------------------------------------------ - - if (likely(stats->st_ops->counter_done)) - rrdset_next(stats->st_ops); - rrddim_set_by_pointer(stats->st_ops, stats->rd_transmission_successes, stats->transmission_successes); rrddim_set_by_pointer(stats->st_ops, stats->rd_data_lost_events, stats->data_lost_events); rrddim_set_by_pointer(stats->st_ops, stats->rd_reconnects, stats->reconnects); rrddim_set_by_pointer(stats->st_ops, stats->rd_transmission_failures, stats->transmission_failures); rrddim_set_by_pointer(stats->st_ops, stats->rd_receptions, stats->receptions); - rrdset_done(stats->st_ops); - // ------------------------------------------------------------------------ - struct rusage thread; getrusage(RUSAGE_THREAD, &thread); - if (likely(stats->st_rusage->counter_done)) - rrdset_next(stats->st_rusage); - rrddim_set_by_pointer(stats->st_rusage, stats->rd_user, thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec); rrddim_set_by_pointer(stats->st_rusage, stats->rd_system, thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec); - rrdset_done(stats->st_rusage); } diff --git a/exporting/tests/exporting_fixtures.c b/exporting/tests/exporting_fixtures.c index aae1c53fb..c9fc9458c 100644 --- a/exporting/tests/exporting_fixtures.c +++ b/exporting/tests/exporting_fixtures.c @@ -33,31 +33,13 @@ int teardown_configured_engine(void **state) return 0; } -int setup_rrdhost() -{ - localhost = calloc(1, sizeof(RRDHOST)); - - localhost->rrd_update_every = 1; - - localhost->tags = strdupz("TAG1=VALUE1 TAG2=VALUE2"); - - localhost->host_labels = rrdlabels_create(); - rrdlabels_add(localhost->host_labels, "key1", "value1", RRDLABEL_SRC_CONFIG); - rrdlabels_add(localhost->host_labels, "key2", "value2", RRDLABEL_SRC_CONFIG); +static void rrddim_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddim, void *st) { + RRDDIM *rd = rrddim; - localhost->rrdset_root = calloc(1, sizeof(RRDSET)); - RRDSET *st = localhost->rrdset_root; - st->rrdhost = localhost; - strcpy(st->id, "chart_id"); - st->name = strdupz("chart_name"); - st->rrd_memory_mode |= RRD_MEMORY_MODE_SAVE; - st->update_every = 1; + rd->id = string_strdupz("dimension_id"); + rd->name = string_strdupz("dimension_name"); - localhost->rrdset_root->dimensions = calloc(1, sizeof(RRDDIM)); - RRDDIM *rd = localhost->rrdset_root->dimensions; - rd->rrdset = st; - rd->id = strdupz("dimension_id"); - rd->name = strdupz("dimension_name"); + rd->rrdset = (RRDSET *)st; rd->last_collected_value = 123000321; rd->last_collected_time.tv_sec = 15051; rd->collections_counter++; @@ -70,25 +52,74 @@ int setup_rrdhost() rd->tiers[0]->query_ops.is_finished = __mock_rrddim_query_is_finished; rd->tiers[0]->query_ops.next_metric = __mock_rrddim_query_next_metric; rd->tiers[0]->query_ops.finalize = __mock_rrddim_query_finalize; +} + +static void rrdset_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdset, void *constructor_data __maybe_unused) { + RRDHOST *host = localhost; + RRDSET *st = rrdset; + + // const char *chart_full_id = dictionary_acquired_item_name(item); + + st->id = string_strdupz("chart_id"); + st->name = string_strdupz("chart_name"); + + st->update_every = 1; + st->rrd_memory_mode = RRD_MEMORY_MODE_SAVE; + + st->rrdhost = host; + + st->rrddim_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + + dictionary_register_insert_callback(st->rrddim_root_index, rrddim_insert_callback, NULL); +} + +int setup_rrdhost() +{ + localhost = calloc(1, sizeof(RRDHOST)); + + localhost->rrd_update_every = 1; + + localhost->tags = string_strdupz("TAG1=VALUE1 TAG2=VALUE2"); + + localhost->rrdlabels = rrdlabels_create(); + rrdlabels_add(localhost->rrdlabels, "key1", "value1", RRDLABEL_SRC_CONFIG); + rrdlabels_add(localhost->rrdlabels, "key2", "value2", RRDLABEL_SRC_CONFIG); + + localhost->rrdset_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + dictionary_register_insert_callback(localhost->rrdset_root_index, rrdset_insert_callback, NULL); + RRDSET *st = dictionary_set_advanced(localhost->rrdset_root_index, "chart_id", -1, NULL, sizeof(RRDSET), NULL); + + st->rrddim_root_index = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + dictionary_register_insert_callback(st->rrddim_root_index, rrddim_insert_callback, NULL); + st->dimensions = dictionary_set_advanced(st->rrddim_root_index, "dimension_id", -1, NULL, sizeof(RRDDIM), st); return 0; } int teardown_rrdhost() { - RRDDIM *rd = localhost->rrdset_root->dimensions; - free((void *)rd->name); - free((void *)rd->id); + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); + + string_freez(rd->id); + string_freez(rd->name); free(rd->tiers[0]); - free(rd); - RRDSET *st = localhost->rrdset_root; - free((void *)st->name); - free(st); + string_freez(st->id); + string_freez(st->name); + dictionary_destroy(st->rrddim_root_index); - rrdlabels_destroy(localhost->host_labels); + rrdlabels_destroy(localhost->rrdlabels); - free((void *)localhost->tags); + string_freez(localhost->tags); + dictionary_destroy(localhost->rrdset_root_index); free(localhost); return 0; diff --git a/exporting/tests/netdata_doubles.c b/exporting/tests/netdata_doubles.c index ee36e887a..7e5017a5f 100644 --- a/exporting/tests/netdata_doubles.c +++ b/exporting/tests/netdata_doubles.c @@ -177,20 +177,6 @@ const char *rrd_memory_mode_name(RRD_MEMORY_MODE id) return RRD_MEMORY_MODE_NONE_NAME; } -NETDATA_DOUBLE rrdvar2number(RRDVAR *rv) -{ - (void)rv; - return 0; -} - -int foreach_host_variable_callback(RRDHOST *host, int (*callback)(RRDVAR *rv, void *data), void *data) -{ - (void)host; - (void)callback; - (void)data; - return 0; -} - void rrdset_update_heterogeneous_flag(RRDSET *st) { (void)st; @@ -212,11 +198,10 @@ time_t __mock_rrddim_query_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle) return mock_type(time_t); } -void __mock_rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_query_handle *handle, time_t start_time, time_t end_time, TIER_QUERY_FETCH tier_query_fetch_type) +void __mock_rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_query_handle *handle, time_t start_time, time_t end_time) { (void)db_metric_handle; (void)handle; - (void)tier_query_fetch_type; function_called(); check_expected(start_time); @@ -248,10 +233,23 @@ void __mock_rrddim_query_finalize(struct rrddim_query_handle *handle) function_called(); } -void sql_store_chart_label(uuid_t *chart_uuid, int source_type, char *label, char *value) +void rrdcalc_update_rrdlabels(RRDSET *st) +{ + (void)st; +} + +void rrdpush_sender_send_this_host_variable_now(RRDHOST *host, const RRDVAR_ACQUIRED *rva) +{ + (void)host; + (void)rva; +} + +void db_execute(const char *cmd) { - (void)chart_uuid; - (void)source_type; - (void)label; - (void)value; + (void)cmd; +} + +DICTIONARY *rrdfamily_rrdvars_dict(const RRDFAMILY_ACQUIRED *rfa) { + (void)rfa; + return NULL; } diff --git a/exporting/tests/test_exporting_engine.c b/exporting/tests/test_exporting_engine.c index 56a28059f..6ea6b1e5c 100644 --- a/exporting/tests/test_exporting_engine.c +++ b/exporting/tests/test_exporting_engine.c @@ -11,6 +11,7 @@ struct config netdata_config; char *netdata_configured_user_config_dir = "."; char *netdata_configured_stock_config_dir = "."; char *netdata_configured_hostname = "test_global_host"; +bool global_statistics_enabled = true; char log_line[MAX_LOG_LINE + 1]; @@ -257,7 +258,10 @@ static void test_rrdset_is_exportable(void **state) { struct engine *engine = *state; struct instance *instance = engine->instance_root; - RRDSET *st = localhost->rrdset_root; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); assert_ptr_equal(st->exporting_flags, NULL); @@ -271,7 +275,10 @@ static void test_false_rrdset_is_exportable(void **state) { struct engine *engine = *state; struct instance *instance = engine->instance_root; - RRDSET *st = localhost->rrdset_root; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); simple_pattern_free(instance->config.charts_pattern); instance->config.charts_pattern = simple_pattern_create("!*", NULL, SIMPLE_PATTERN_EXACT); @@ -288,7 +295,17 @@ static void test_exporting_calculate_value_from_stored_data(void **state) { struct engine *engine = *state; struct instance *instance = engine->instance_root; - RRDDIM *rd = localhost->rrdset_root->dimensions; + + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); + time_t timestamp; instance->after = 3; @@ -348,7 +365,11 @@ static void test_prepare_buffers(void **state) expect_value(__mock_start_host_formatting, host, localhost); will_return(__mock_start_host_formatting, 0); - RRDSET *st = localhost->rrdset_root; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + expect_function_call(__wrap_rrdset_is_exportable); expect_value(__wrap_rrdset_is_exportable, instance, instance); expect_value(__wrap_rrdset_is_exportable, st, st); @@ -359,7 +380,10 @@ static void test_prepare_buffers(void **state) expect_value(__mock_start_chart_formatting, st, st); will_return(__mock_start_chart_formatting, 0); - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); expect_function_call(__mock_metric_formatting); expect_value(__mock_metric_formatting, instance, instance); expect_value(__mock_metric_formatting, rd, rd); @@ -412,7 +436,15 @@ static void test_format_dimension_collected_graphite_plaintext(void **state) { struct engine *engine = *state; - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); assert_int_equal(format_dimension_collected_graphite_plaintext(engine->instance_root, rd), 0); assert_string_equal( buffer_tostring(engine->instance_root->buffer), @@ -426,7 +458,15 @@ static void test_format_dimension_stored_graphite_plaintext(void **state) expect_function_call(__wrap_exporting_calculate_value_from_stored_data); will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_DEFAULT_FLAGS)); - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); assert_int_equal(format_dimension_stored_graphite_plaintext(engine->instance_root, rd), 0); assert_string_equal( buffer_tostring(engine->instance_root->buffer), @@ -437,13 +477,21 @@ static void test_format_dimension_collected_json_plaintext(void **state) { struct engine *engine = *state; - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); assert_int_equal(format_dimension_collected_json_plaintext(engine->instance_root, rd), 0); assert_string_equal( buffer_tostring(engine->instance_root->buffer), "{\"prefix\":\"netdata\",\"hostname\":\"test-host\",\"host_tags\":\"TAG1=VALUE1 TAG2=VALUE2\"," - "\"chart_id\":\"chart_id\",\"chart_name\":\"chart_name\",\"chart_family\":\"(null)\"," - "\"chart_context\":\"(null)\",\"chart_type\":\"(null)\",\"units\":\"(null)\",\"id\":\"dimension_id\"," + "\"chart_id\":\"chart_id\",\"chart_name\":\"chart_name\",\"chart_family\":\"\"," + "\"chart_context\":\"\",\"chart_type\":\"\",\"units\":\"\",\"id\":\"dimension_id\"," "\"name\":\"dimension_name\",\"value\":123000321,\"timestamp\":15051}\n"); } @@ -454,13 +502,21 @@ static void test_format_dimension_stored_json_plaintext(void **state) expect_function_call(__wrap_exporting_calculate_value_from_stored_data); will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_DEFAULT_FLAGS)); - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); assert_int_equal(format_dimension_stored_json_plaintext(engine->instance_root, rd), 0); assert_string_equal( buffer_tostring(engine->instance_root->buffer), "{\"prefix\":\"netdata\",\"hostname\":\"test-host\",\"host_tags\":\"TAG1=VALUE1 TAG2=VALUE2\"," - "\"chart_id\":\"chart_id\",\"chart_name\":\"chart_name\",\"chart_family\":\"(null)\"," \ - "\"chart_context\": \"(null)\",\"chart_type\":\"(null)\",\"units\": \"(null)\",\"id\":\"dimension_id\"," + "\"chart_id\":\"chart_id\",\"chart_name\":\"chart_name\",\"chart_family\":\"\"," \ + "\"chart_context\": \"\",\"chart_type\":\"\",\"units\": \"\",\"id\":\"dimension_id\"," "\"name\":\"dimension_name\",\"value\":690565856.0000000,\"timestamp\": 15052}\n"); } @@ -468,7 +524,15 @@ static void test_format_dimension_collected_opentsdb_telnet(void **state) { struct engine *engine = *state; - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); assert_int_equal(format_dimension_collected_opentsdb_telnet(engine->instance_root, rd), 0); assert_string_equal( buffer_tostring(engine->instance_root->buffer), @@ -482,7 +546,15 @@ static void test_format_dimension_stored_opentsdb_telnet(void **state) expect_function_call(__wrap_exporting_calculate_value_from_stored_data); will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_DEFAULT_FLAGS)); - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); assert_int_equal(format_dimension_stored_opentsdb_telnet(engine->instance_root, rd), 0); assert_string_equal( buffer_tostring(engine->instance_root->buffer), @@ -493,7 +565,15 @@ static void test_format_dimension_collected_opentsdb_http(void **state) { struct engine *engine = *state; - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); assert_int_equal(format_dimension_collected_opentsdb_http(engine->instance_root, rd), 0); assert_string_equal( buffer_tostring(engine->instance_root->buffer), @@ -510,7 +590,15 @@ static void test_format_dimension_stored_opentsdb_http(void **state) expect_function_call(__wrap_exporting_calculate_value_from_stored_data); will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_DEFAULT_FLAGS)); - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); assert_int_equal(format_dimension_stored_opentsdb_http(engine->instance_root, rd), 0); assert_string_equal( buffer_tostring(engine->instance_root->buffer), @@ -616,27 +704,14 @@ static void test_simple_connector_worker(void **state) buffer_sprintf(simple_connector_data->last_buffer->header, "test header"); buffer_sprintf(simple_connector_data->last_buffer->buffer, "test buffer"); - expect_function_call(__wrap_connect_to_one_of); - expect_string(__wrap_connect_to_one_of, destination, "localhost"); - expect_value(__wrap_connect_to_one_of, default_port, 2003); - expect_not_value(__wrap_connect_to_one_of, reconnects_counter, 0); - expect_string(__wrap_connect_to_one_of, connected_to, "localhost"); - expect_value(__wrap_connect_to_one_of, connected_to_size, CONNECTED_TO_MAX); - will_return(__wrap_connect_to_one_of, 2); + expect_function_call(__wrap_now_realtime_sec); + will_return(__wrap_now_realtime_sec, 2); - expect_function_call(__wrap_send); - expect_value(__wrap_send, sockfd, 2); - expect_not_value(__wrap_send, buf, buffer_tostring(simple_connector_data->last_buffer->buffer)); - expect_string(__wrap_send, buf, "test header"); - expect_value(__wrap_send, len, 11); - expect_value(__wrap_send, flags, MSG_NOSIGNAL); + expect_function_call(__wrap_now_realtime_sec); + will_return(__wrap_now_realtime_sec, 2); - expect_function_call(__wrap_send); - expect_value(__wrap_send, sockfd, 2); - expect_value(__wrap_send, buf, buffer_tostring(simple_connector_data->last_buffer->buffer)); - expect_string(__wrap_send, buf, "test buffer"); - expect_value(__wrap_send, len, 11); - expect_value(__wrap_send, flags, MSG_NOSIGNAL); + expect_function_call(__wrap_now_realtime_sec); + will_return(__wrap_now_realtime_sec, 2); expect_function_call(__wrap_send_internal_metrics); expect_value(__wrap_send_internal_metrics, instance, instance); @@ -986,21 +1061,26 @@ static void test_can_send_rrdset(void **state) { (void)*state; - assert_int_equal(can_send_rrdset(prometheus_exporter_instance, localhost->rrdset_root, NULL), 1); + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + assert_int_equal(can_send_rrdset(prometheus_exporter_instance, st, NULL), 1); - rrdset_flag_set(localhost->rrdset_root, RRDSET_FLAG_EXPORTING_IGNORE); - assert_int_equal(can_send_rrdset(prometheus_exporter_instance, localhost->rrdset_root, NULL), 0); - rrdset_flag_clear(localhost->rrdset_root, RRDSET_FLAG_EXPORTING_IGNORE); + rrdset_flag_set(st, RRDSET_FLAG_EXPORTING_IGNORE); + assert_int_equal(can_send_rrdset(prometheus_exporter_instance, st, NULL), 0); + rrdset_flag_clear(st, RRDSET_FLAG_EXPORTING_IGNORE); // TODO: test with a denying simple pattern - rrdset_flag_set(localhost->rrdset_root, RRDSET_FLAG_OBSOLETE); - assert_int_equal(can_send_rrdset(prometheus_exporter_instance, localhost->rrdset_root, NULL), 0); - rrdset_flag_clear(localhost->rrdset_root, RRDSET_FLAG_OBSOLETE); + rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE); + assert_int_equal(can_send_rrdset(prometheus_exporter_instance, st, NULL), 0); + rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); - localhost->rrdset_root->rrd_memory_mode = RRD_MEMORY_MODE_NONE; + st->rrd_memory_mode = RRD_MEMORY_MODE_NONE; prometheus_exporter_instance->config.options |= EXPORTING_SOURCE_DATA_AVERAGE; - assert_int_equal(can_send_rrdset(prometheus_exporter_instance, localhost->rrdset_root, NULL), 0); + assert_int_equal(can_send_rrdset(prometheus_exporter_instance, st, NULL), 0); } static void test_prometheus_name_copy(void **state) @@ -1055,9 +1135,14 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus(void **state) BUFFER *buffer = buffer_create(0); - localhost->hostname = strdupz("test_hostname"); - localhost->rrdset_root->family = strdupz("test_family"); - localhost->rrdset_root->context = strdupz("test_context"); + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + localhost->hostname = string_strdupz("test_hostname"); + st->family = string_strdupz("test_family"); + st->context = string_strdupz("test_context"); expect_function_call(__wrap_now_realtime_sec); will_return(__wrap_now_realtime_sec, 2); @@ -1069,7 +1154,7 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus(void **state) assert_string_equal( buffer_tostring(buffer), - "netdata_info{instance=\"test_hostname\",application=\"(null)\",version=\"(null)\",key1=\"value1\",key2=\"value2\"} 1\n" + "netdata_info{instance=\"test_hostname\",application=\"\",version=\"\",key1=\"value1\",key2=\"value2\"} 1\n" "test_prefix_test_context{chart=\"chart_id\",family=\"test_family\",dimension=\"dimension_id\"} 690565856.0000000\n"); buffer_flush(buffer); @@ -1085,7 +1170,7 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus(void **state) assert_string_equal( buffer_tostring(buffer), - "netdata_info{instance=\"test_hostname\",application=\"(null)\",version=\"(null)\",key1=\"value1\",key2=\"value2\"} 1\n" + "netdata_info{instance=\"test_hostname\",application=\"\",version=\"\",key1=\"value1\",key2=\"value2\"} 1\n" "# TYPE test_prefix_test_context gauge\n" "test_prefix_test_context{chart=\"chart_name\",family=\"test_family\",dimension=\"dimension_name\"} 690565856.0000000\n"); @@ -1101,11 +1186,11 @@ static void rrd_stats_api_v1_charts_allmetrics_prometheus(void **state) assert_string_equal( buffer_tostring(buffer), - "netdata_info{instance=\"test_hostname\",application=\"(null)\",version=\"(null)\",key1=\"value1\",key2=\"value2\"} 1\n" + "netdata_info{instance=\"test_hostname\",application=\"\",version=\"\",key1=\"value1\",key2=\"value2\"} 1\n" "test_prefix_test_context{chart=\"chart_id\",family=\"test_family\",dimension=\"dimension_id\",instance=\"test_hostname\"} 690565856.0000000\n"); - free(localhost->rrdset_root->context); - free(localhost->rrdset_root->family); + free(st->context); + free(st->family); free(localhost->hostname); buffer_free(buffer); } @@ -1207,8 +1292,8 @@ static void test_format_host_prometheus_remote_write(void **state) simple_connector_data->connector_specific_data = (void *)connector_specific_data; connector_specific_data->write_request = (void *)0xff; - localhost->program_name = strdupz("test_program"); - localhost->program_version = strdupz("test_version"); + localhost->program_name = string_strdupz("test_program"); + localhost->program_version = string_strdupz("test_version"); expect_function_call(__wrap_add_host_info); expect_value(__wrap_add_host_info, write_request_p, 0xff); @@ -1249,7 +1334,15 @@ static void test_format_dimension_prometheus_remote_write(void **state) simple_connector_data->connector_specific_data = (void *)connector_specific_data; connector_specific_data->write_request = (void *)0xff; - RRDDIM *rd = localhost->rrdset_root->dimensions; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st); + break; + rrddim_foreach_done(rd); expect_function_call(__wrap_exporting_calculate_value_from_stored_data); will_return(__wrap_exporting_calculate_value_from_stored_data, pack_storage_number(27, SN_DEFAULT_FLAGS)); @@ -1428,7 +1521,11 @@ static void test_aws_kinesis_connector_worker(void **state) expect_value(__wrap_rrdhost_is_exportable, host, localhost); will_return(__wrap_rrdhost_is_exportable, 1); - RRDSET *st = localhost->rrdset_root; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + expect_function_call(__wrap_rrdset_is_exportable); expect_value(__wrap_rrdset_is_exportable, instance, instance); expect_value(__wrap_rrdset_is_exportable, st, st); @@ -1563,7 +1660,11 @@ static void test_pubsub_connector_worker(void **state) expect_value(__wrap_rrdhost_is_exportable, host, localhost); will_return(__wrap_rrdhost_is_exportable, 1); - RRDSET *st = localhost->rrdset_root; + RRDSET *st; + rrdset_foreach_read(st, localhost); + break; + rrdset_foreach_done(st); + expect_function_call(__wrap_rrdset_is_exportable); expect_value(__wrap_rrdset_is_exportable, instance, instance); expect_value(__wrap_rrdset_is_exportable, st, st); diff --git a/exporting/tests/test_exporting_engine.h b/exporting/tests/test_exporting_engine.h index ae0b7df9a..a9180a518 100644 --- a/exporting/tests/test_exporting_engine.h +++ b/exporting/tests/test_exporting_engine.h @@ -4,6 +4,7 @@ #define TEST_EXPORTING_ENGINE_H 1 #include "libnetdata/libnetdata.h" +#include "database/rrdvar.h" #include "exporting/exporting_engine.h" #include "exporting/graphite/graphite.h" @@ -59,7 +60,7 @@ void __rrdset_check_rdlock(RRDSET *st, const char *file, const char *function, c void __rrd_check_rdlock(const char *file, const char *function, const unsigned long line); time_t __mock_rrddim_query_oldest_time(STORAGE_METRIC_HANDLE *db_metric_handle); time_t __mock_rrddim_query_latest_time(STORAGE_METRIC_HANDLE *db_metric_handle); -void __mock_rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_query_handle *handle, time_t start_time, time_t end_time, TIER_QUERY_FETCH tier_query_fetch_type); +void __mock_rrddim_query_init(STORAGE_METRIC_HANDLE *db_metric_handle, struct rrddim_query_handle *handle, time_t start_time, time_t end_time); int __mock_rrddim_query_is_finished(struct rrddim_query_handle *handle); STORAGE_POINT __mock_rrddim_query_next_metric(struct rrddim_query_handle *handle); void __mock_rrddim_query_finalize(struct rrddim_query_handle *handle); diff --git a/health/Makefile.am b/health/Makefile.am index 777b35858..7c8d7f9d2 100644 --- a/health/Makefile.am +++ b/health/Makefile.am @@ -65,8 +65,11 @@ dist_healthconfig_DATA = \ health.d/mysql.conf \ health.d/net.conf \ health.d/netfilter.conf \ + health.d/nvme.conf \ health.d/nut.conf \ health.d/pihole.conf \ + health.d/ping.conf \ + health.d/postgres.conf \ health.d/portcheck.conf \ health.d/processes.conf \ health.d/python.d.plugin.conf \ diff --git a/health/REFERENCE.md b/health/REFERENCE.md index d1af74767..90da4102a 100644 --- a/health/REFERENCE.md +++ b/health/REFERENCE.md @@ -536,12 +536,48 @@ See our [simple patterns docs](/libnetdata/simple_pattern/README.md) for more ex #### Alarm line `info` -The info field can contain a small piece of text describing the alarm or template. This will be rendered in notifications and UI elements whenever the specific alarm is in focus. An example for the `ram_available` alarm is: +The info field can contain a small piece of text describing the alarm or template. This will be rendered in +notifications and UI elements whenever the specific alarm is in focus. An example for the `ram_available` alarm is: ```yaml info: percentage of estimated amount of RAM available for userspace processes, without causing swapping ``` +info fields can contain special variables in their text that will be replaced during run-time to provide more specific +alert information. Current variables supported are: + +| variable | description | +| ---------| ----------- | +| $family | Will be replaced by the family instance for the alert (e.g. eth0) | +| $label: | Followed by a chart label name, this will replace the variable with the chart label's value | + +For example, an info field like the following: + +```yaml +info: average inbound utilization for the network interface $family over the last minute +``` + +Will be rendered on the alert acting on interface `eth0` as: + +```yaml +info: average inbound utilization for the network interface eth0 over the last minute +``` + +An alert acting on a chart that has a chart label named e.g. `target`, with a value of `https://netdata.cloud/`, +can be enriched as follows: + +```yaml +info: average ratio of HTTP responses with unexpected status over the last 5 minutes for the site $label:target +``` + +Will become: + +```yaml +info: average ratio of HTTP responses with unexpected status over the last 5 minutes for the site https://netdata.cloud/ +``` + +> Please note that variable names are case sensitive. + ## Expressions Netdata has an internal [infix expression parser](/libnetdata/eval). This parses expressions and creates an internal diff --git a/health/health.c b/health/health.c index 9eb36a9c6..3784e0f31 100644 --- a/health/health.c +++ b/health/health.c @@ -2,11 +2,166 @@ #include "health.h" +#define WORKER_HEALTH_JOB_RRD_LOCK 0 +#define WORKER_HEALTH_JOB_HOST_LOCK 1 +#define WORKER_HEALTH_JOB_DB_QUERY 2 +#define WORKER_HEALTH_JOB_CALC_EVAL 3 +#define WORKER_HEALTH_JOB_WARNING_EVAL 4 +#define WORKER_HEALTH_JOB_CRITICAL_EVAL 5 +#define WORKER_HEALTH_JOB_ALARM_LOG_ENTRY 6 +#define WORKER_HEALTH_JOB_ALARM_LOG_PROCESS 7 +#define WORKER_HEALTH_JOB_DELAYED_INIT_RRDSET 8 +#define WORKER_HEALTH_JOB_DELAYED_INIT_RRDDIM 9 + +#if WORKER_UTILIZATION_MAX_JOB_TYPES < 10 +#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 10 +#endif + +static bool prepare_command(BUFFER *wb, + const char *exec, + const char *recipient, + const char *registry_hostname, + uint32_t unique_id, + uint32_t alarm_id, + uint32_t alarm_event_id, + uint32_t when, + const char *alert_name, + const char *alert_chart_name, + const char *alert_family, + const char *new_status, + const char *old_status, + NETDATA_DOUBLE new_value, + NETDATA_DOUBLE old_value, + const char *alert_source, + uint32_t duration, + uint32_t non_clear_duration, + const char *alert_units, + const char *alert_info, + const char *new_value_string, + const char *old_value_string, + const char *source, + const char *error_msg, + int n_warn, + int n_crit, + const char *warn_alarms, + const char *crit_alarms, + const char *classification, + const char *edit_command, + const char *machine_guid) +{ + char buf[8192]; + size_t n = 8192 - 1; + + buffer_strcat(wb, "exec"); + + if (!sanitize_command_argument_string(buf, exec, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, recipient, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, registry_hostname, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + buffer_sprintf(wb, " '%u'", unique_id); + + buffer_sprintf(wb, " '%u'", alarm_id); + + buffer_sprintf(wb, " '%u'", alarm_event_id); + + buffer_sprintf(wb, " '%u'", when); + + if (!sanitize_command_argument_string(buf, alert_name, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, alert_chart_name, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, alert_family, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, new_status, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, old_status, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + buffer_sprintf(wb, " '" NETDATA_DOUBLE_FORMAT_ZERO "'", new_value); + + buffer_sprintf(wb, " '" NETDATA_DOUBLE_FORMAT_ZERO "'", old_value); + + if (!sanitize_command_argument_string(buf, alert_source, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + buffer_sprintf(wb, " '%u'", duration); + + buffer_sprintf(wb, " '%u'", non_clear_duration); + + if (!sanitize_command_argument_string(buf, alert_units, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, alert_info, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, new_value_string, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, old_value_string, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, source, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, error_msg, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + buffer_sprintf(wb, " '%d'", n_warn); + + buffer_sprintf(wb, " '%d'", n_crit); + + if (!sanitize_command_argument_string(buf, warn_alarms, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, crit_alarms, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, classification, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, edit_command, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + if (!sanitize_command_argument_string(buf, machine_guid, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + + return true; +} + unsigned int default_health_enabled = 1; char *silencers_filename; // the queue of executed alarm notifications that haven't been waited for yet -static struct { +static __thread struct { ALARM_ENTRY *head; // oldest ALARM_ENTRY *tail; // latest } alarm_notifications_in_progress = {NULL, NULL}; @@ -146,77 +301,51 @@ void health_init(void) { * @param host the structure of the host that the function will reload the configuration. */ static void health_reload_host(RRDHOST *host) { - if(unlikely(!host->health_enabled)) + if(unlikely(!host->health_enabled) && !rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)) return; + log_health("[%s]: Reloading health.", rrdhost_hostname(host)); + char *user_path = health_user_config_dir(); char *stock_path = health_stock_config_dir(); // free all running alarms - rrdhost_wrlock(host); - - while(host->templates) - rrdcalctemplate_unlink_and_free(host, host->templates); - - RRDCALCTEMPLATE *rt,*next; - for(rt = host->alarms_template_with_foreach; rt ; rt = next) { - next = rt->next; - rrdcalctemplate_free(rt); - } - host->alarms_template_with_foreach = NULL; - - while(host->alarms) - rrdcalc_unlink_and_free(host, host->alarms); - - RRDCALC *rc,*nc; - for(rc = host->alarms_with_foreach; rc ; rc = nc) { - nc = rc->next; - rrdcalc_free(rc); - } - host->alarms_with_foreach = NULL; - - rrdhost_unlock(host); + rrdcalc_delete_all(host); + rrdcalctemplate_delete_all(host); // invalidate all previous entries in the alarm log + netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock); ALARM_ENTRY *t; for(t = host->health_log.alarms ; t ; t = t->next) { if(t->new_status != RRDCALC_STATUS_REMOVED) t->flags |= HEALTH_ENTRY_FLAG_UPDATED; } + netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); - rrdhost_rdlock(host); // reset all thresholds to all charts RRDSET *st; rrdset_foreach_read(st, host) { st->green = NAN; st->red = NAN; } - rrdhost_unlock(host); + rrdset_foreach_done(st); // load the new alarms - rrdhost_wrlock(host); health_readdir(host, user_path, stock_path, NULL); //Discard alarms with labels that do not apply to host - rrdcalc_labels_unlink_alarm_from_host(host); + rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(host); // link the loaded alarms to their charts - RRDDIM *rd; rrdset_foreach_write(st, host) { if (rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED)) continue; - rrdsetcalc_link_matching(st); - rrdcalctemplate_link_matching(st); - //This loop must be the last, because ` rrdcalctemplate_link_matching` will create alarms related to it. - rrdset_rdlock(st); - rrddim_foreach_read(rd, st) { - rrdcalc_link_to_rrddim(rd, st, host); - } - rrdset_unlock(st); + rrdcalc_link_matching_alerts_to_rrdset(st); + rrdcalctemplate_link_matching_templates_to_rrdset(st); } - - rrdhost_unlock(host); + rrdset_foreach_done(st); + host->aclk_alert_reloaded = 1; } /** @@ -234,11 +363,6 @@ void health_reload(void) { health_reload_host(host); rrd_unlock(); -#ifdef ENABLE_ACLK - if (netdata_cloud_setting) { - aclk_alert_reloaded = 1; - } -#endif } // ---------------------------------------------------------------------------- @@ -250,7 +374,6 @@ static inline RRDCALC_STATUS rrdcalc_value2status(NETDATA_DOUBLE n) { return RRDCALC_STATUS_CLEAR; } -#define ALARM_EXEC_COMMAND_LENGTH 8192 #define ACTIVE_ALARMS_LIST_EXAMINE 500 #define ACTIVE_ALARMS_LIST 15 @@ -266,13 +389,14 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { if(unlikely(ae->new_status < RRDCALC_STATUS_CLEAR)) { // do not send notifications for internal statuses - debug(D_HEALTH, "Health not sending notification for alarm '%s.%s' status %s (internal statuses)", ae->chart, ae->name, rrdcalc_status2string(ae->new_status)); + debug(D_HEALTH, "Health not sending notification for alarm '%s.%s' status %s (internal statuses)", ae_chart_name(ae), ae_name(ae), rrdcalc_status2string(ae->new_status)); goto done; } if(unlikely(ae->new_status <= RRDCALC_STATUS_CLEAR && (ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION))) { // do not send notifications for disabled statuses - debug(D_HEALTH, "Health not sending notification for alarm '%s.%s' status %s (it has no-clear-notification enabled)", ae->chart, ae->name, rrdcalc_status2string(ae->new_status)); + debug(D_HEALTH, "Health not sending notification for alarm '%s.%s' status %s (it has no-clear-notification enabled)", ae_chart_name(ae), ae_name(ae), rrdcalc_status2string(ae->new_status)); + log_health("[%s]: Health not sending notification for alarm '%s.%s' status %s (it has no-clear-notification enabled)", rrdhost_hostname(host), ae_chart_name(ae), ae_name(ae), rrdcalc_status2string(ae->new_status)); // mark it as run, so that we will send the same alarm if it happens again goto done; } @@ -292,7 +416,9 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { // we have executed this alarm notification in the past if(t && t->new_status == ae->new_status) { // don't send the notification for the same status again - debug(D_HEALTH, "Health not sending again notification for alarm '%s.%s' status %s", ae->chart, ae->name + debug(D_HEALTH, "Health not sending again notification for alarm '%s.%s' status %s", ae_chart_name(ae), ae_name(ae) + , rrdcalc_status2string(ae->new_status)); + log_health("[%s]: Health not sending again notification for alarm '%s.%s' status %s", rrdhost_hostname(host), ae_chart_name(ae), ae_name(ae) , rrdcalc_status2string(ae->new_status)); goto done; } @@ -303,7 +429,7 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { if(unlikely(ae->new_status == RRDCALC_STATUS_CLEAR)) { if((!(ae->flags & HEALTH_ENTRY_RUN_ONCE)) || (ae->flags & HEALTH_ENTRY_RUN_ONCE && ae->old_status < RRDCALC_STATUS_RAISED) ) { debug(D_HEALTH, "Health not sending notification for first initialization of alarm '%s.%s' status %s" - , ae->chart, ae->name, rrdcalc_status2string(ae->new_status)); + , ae_chart_name(ae), ae_name(ae), rrdcalc_status2string(ae->new_status)); goto done; } } @@ -312,14 +438,14 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { // Check if alarm notifications are silenced if (ae->flags & HEALTH_ENTRY_FLAG_SILENCED) { - info("Health not sending notification for alarm '%s.%s' status %s (command API has disabled notifications)", ae->chart, ae->name, rrdcalc_status2string(ae->new_status)); + log_health("[%s]: Health not sending notification for alarm '%s.%s' status %s (command API has disabled notifications)", rrdhost_hostname(host), ae_chart_name(ae), ae_name(ae), rrdcalc_status2string(ae->new_status)); goto done; } - static char command_to_run[ALARM_EXEC_COMMAND_LENGTH + 1]; + log_health("[%s]: Sending notification for alarm '%s.%s' status %s.", rrdhost_hostname(host), ae_chart_name(ae), ae_name(ae), rrdcalc_status2string(ae->new_status)); - const char *exec = (ae->exec) ? ae->exec : host->health_default_exec; - const char *recipient = (ae->recipient) ? ae->recipient : host->health_default_recipient; + const char *exec = (ae->exec) ? ae_exec(ae) : string2str(host->health_default_exec); + const char *recipient = (ae->recipient) ? ae_recipient(ae) : string2str(host->health_default_recipient); int n_warn=0, n_crit=0; RRDCALC *rc; @@ -330,13 +456,16 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { warn_alarms = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE); crit_alarms = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE); - for(rc = host->alarms; rc && (n_warn + n_crit) < ACTIVE_ALARMS_LIST_EXAMINE ; rc = rc->next) { + foreach_rrdcalc_in_rrdhost_read(host, rc) { if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) continue; + if(unlikely((n_warn + n_crit) >= ACTIVE_ALARMS_LIST_EXAMINE)) + break; + if (unlikely(rc->status == RRDCALC_STATUS_WARNING)) { if (likely(ae->alarm_id != rc->id) || likely(ae->alarm_event_id != rc->next_event_id - 1)) { - active_alerts[n_warn+n_crit].name = rc->name; + active_alerts[n_warn+n_crit].name = (char *)rrdcalc_name(rc); active_alerts[n_warn+n_crit].last_status_change = rc->last_status_change; active_alerts[n_warn+n_crit].status = rc->status; n_warn++; @@ -344,7 +473,7 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { expr = rc->warning; } else if (unlikely(rc->status == RRDCALC_STATUS_CRITICAL)) { if (likely(ae->alarm_id != rc->id) || likely(ae->alarm_event_id != rc->next_event_id - 1)) { - active_alerts[n_warn+n_crit].name = rc->name; + active_alerts[n_warn+n_crit].name = (char *)rrdcalc_name(rc); active_alerts[n_warn+n_crit].last_status_change = rc->last_status_change; active_alerts[n_warn+n_crit].status = rc->status; n_crit++; @@ -355,6 +484,7 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { expr = rc->warning; } } + foreach_rrdcalc_in_rrdhost_done(rc); if (n_warn+n_crit>1) qsort (active_alerts, n_warn+n_crit, sizeof(active_alerts_t), compare_active_alerts); @@ -379,51 +509,55 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { } } - char *edit_command = ae->source ? health_edit_command_from_source(ae->source) : strdupz("UNKNOWN=0=UNKNOWN"); - - snprintfz(command_to_run, ALARM_EXEC_COMMAND_LENGTH, "exec %s '%s' '%s' '%u' '%u' '%u' '%lu' '%s' '%s' '%s' '%s' '%s' '" NETDATA_DOUBLE_FORMAT_ZERO - "' '" NETDATA_DOUBLE_FORMAT_ZERO - "' '%s' '%u' '%u' '%s' '%s' '%s' '%s' '%s' '%s' '%d' '%d' '%s' '%s' '%s' '%s' '%s'", - exec, - recipient, - host->registry_hostname, - ae->unique_id, - ae->alarm_id, - ae->alarm_event_id, - (unsigned long)ae->when, - ae->name, - ae->chart?ae->chart:"NOCHART", - ae->family?ae->family:"NOFAMILY", - rrdcalc_status2string(ae->new_status), - rrdcalc_status2string(ae->old_status), - ae->new_value, - ae->old_value, - ae->source?ae->source:"UNKNOWN", - (uint32_t)ae->duration, - (uint32_t)ae->non_clear_duration, - ae->units?ae->units:"", - ae->info?ae->info:"", - ae->new_value_string, - ae->old_value_string, - (expr && expr->source)?expr->source:"NOSOURCE", - (expr && expr->error_msg)?buffer_tostring(expr->error_msg):"NOERRMSG", - n_warn, - n_crit, - buffer_tostring(warn_alarms), - buffer_tostring(crit_alarms), - ae->classification?ae->classification:"Unknown", - edit_command, - host != localhost ? host->machine_guid:"" - ); - - ae->flags |= HEALTH_ENTRY_FLAG_EXEC_RUN; - ae->exec_run_timestamp = now_realtime_sec(); /* will be updated by real time after spawning */ - - debug(D_HEALTH, "executing command '%s'", command_to_run); - ae->flags |= HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS; - ae->exec_spawn_serial = spawn_enq_cmd(command_to_run); - enqueue_alarm_notify_in_progress(ae); + char *edit_command = ae->source ? health_edit_command_from_source(ae_source(ae)) : strdupz("UNKNOWN=0=UNKNOWN"); + + BUFFER *wb = buffer_create(8192); + bool ok = prepare_command(wb, + exec, + recipient, + rrdhost_registry_hostname(host), + ae->unique_id, + ae->alarm_id, + ae->alarm_event_id, + (unsigned long)ae->when, + ae_name(ae), + ae->chart?ae_chart_name(ae):"NOCHART", + ae->family?ae_family(ae):"NOFAMILY", + rrdcalc_status2string(ae->new_status), + rrdcalc_status2string(ae->old_status), + ae->new_value, + ae->old_value, + ae->source?ae_source(ae):"UNKNOWN", + (uint32_t)ae->duration, + (uint32_t)ae->non_clear_duration, + ae_units(ae), + ae_info(ae), + ae_new_value_string(ae), + ae_old_value_string(ae), + (expr && expr->source)?expr->source:"NOSOURCE", + (expr && expr->error_msg)?buffer_tostring(expr->error_msg):"NOERRMSG", + n_warn, + n_crit, + buffer_tostring(warn_alarms), + buffer_tostring(crit_alarms), + ae->classification?ae_classification(ae):"Unknown", + edit_command, + host != localhost ? host->machine_guid:""); + + const char *command_to_run = buffer_tostring(wb); + if (ok) { + ae->flags |= HEALTH_ENTRY_FLAG_EXEC_RUN; + ae->exec_run_timestamp = now_realtime_sec(); /* will be updated by real time after spawning */ + + debug(D_HEALTH, "executing command '%s'", command_to_run); + ae->flags |= HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS; + ae->exec_spawn_serial = spawn_enq_cmd(command_to_run); + enqueue_alarm_notify_in_progress(ae); + } else { + error("Failed to format command arguments"); + } + buffer_free(wb); freez(edit_command); buffer_free(warn_alarms); buffer_free(crit_alarms); @@ -450,7 +584,7 @@ static inline void health_alarm_wait_for_execution(ALARM_ENTRY *ae) { static inline void health_process_notifications(RRDHOST *host, ALARM_ENTRY *ae) { debug(D_HEALTH, "Health alarm '%s.%s' = " NETDATA_DOUBLE_FORMAT_AUTO " - changed status from %s to %s", - ae->chart?ae->chart:"NOCHART", ae->name, + ae->chart?ae_chart_name(ae):"NOCHART", ae_name(ae), ae->new_value, rrdcalc_status2string(ae->old_status), rrdcalc_status2string(ae->new_status) @@ -467,7 +601,7 @@ static inline void health_alarm_log_process(RRDHOST *host) { ALARM_ENTRY *ae; for(ae = host->health_log.alarms; ae && ae->unique_id >= host->health_last_processed_id; ae = ae->next) { - if(likely(!alarm_entry_isrepeating(host, ae))) { + if(likely(!(ae->flags & HEALTH_ENTRY_FLAG_IS_REPEATING))) { if(unlikely( !(ae->flags & HEALTH_ENTRY_FLAG_PROCESSED) && !(ae->flags & HEALTH_ENTRY_FLAG_UPDATED) @@ -481,13 +615,13 @@ static inline void health_alarm_log_process(RRDHOST *host) { } } + netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); + // remember this for the next iteration host->health_last_processed_id = first_waiting; bool cleanup_excess_log_entries = host->health_log.count > host->health_log.max; - netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); - if (!cleanup_excess_log_entries) return; @@ -508,7 +642,7 @@ static inline void health_alarm_log_process(RRDHOST *host) { ALARM_ENTRY *t = ae->next; - if(likely(!alarm_entry_isrepeating(host, ae))) { + if(likely(!(ae->flags & HEALTH_ENTRY_FLAG_IS_REPEATING))) { health_alarm_wait_for_execution(ae); health_alarm_log_free_one_nochecks_nounlink(ae); host->health_log.count--; @@ -522,7 +656,7 @@ static inline void health_alarm_log_process(RRDHOST *host) { static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) { if(unlikely(!rc->rrdset)) { - debug(D_HEALTH, "Health not running alarm '%s.%s'. It is not linked to a chart.", rc->chart?rc->chart:"NOCHART", rc->name); + debug(D_HEALTH, "Health not running alarm '%s.%s'. It is not linked to a chart.", rrdcalc_chart_name(rc), rrdcalc_name(rc)); return 0; } @@ -533,40 +667,38 @@ static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) *next_run = rc->next_update; } - debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rc->chart?rc->chart:"NOCHART", rc->name, (int) (rc->next_update - now)); + debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rrdcalc_chart_name(rc), rrdcalc_name(rc), (int) (rc->next_update - now)); return 0; } if(unlikely(!rc->update_every)) { - debug(D_HEALTH, "Health not running alarm '%s.%s'. It does not have an update frequency", rc->chart?rc->chart:"NOCHART", rc->name); + debug(D_HEALTH, "Health not running alarm '%s.%s'. It does not have an update frequency", rrdcalc_chart_name(rc), rrdcalc_name(rc)); return 0; } if(unlikely(rrdset_flag_check(rc->rrdset, RRDSET_FLAG_OBSOLETE))) { - debug(D_HEALTH, "Health not running alarm '%s.%s'. The chart has been marked as obsolete", rc->chart?rc->chart:"NOCHART", rc->name); + debug(D_HEALTH, "Health not running alarm '%s.%s'. The chart has been marked as obsolete", rrdcalc_chart_name(rc), rrdcalc_name(rc)); return 0; } if(unlikely(rrdset_flag_check(rc->rrdset, RRDSET_FLAG_ARCHIVED))) { - debug(D_HEALTH, "Health not running alarm '%s.%s'. The chart has been marked as archived", rc->chart?rc->chart:"NOCHART", rc->name); + debug(D_HEALTH, "Health not running alarm '%s.%s'. The chart has been marked as archived", rrdcalc_chart_name(rc), rrdcalc_name(rc)); return 0; } if(unlikely(!rc->rrdset->last_collected_time.tv_sec || rc->rrdset->counter_done < 2)) { - debug(D_HEALTH, "Health not running alarm '%s.%s'. Chart is not fully collected yet.", rc->chart?rc->chart:"NOCHART", rc->name); + debug(D_HEALTH, "Health not running alarm '%s.%s'. Chart is not fully collected yet.", rrdcalc_chart_name(rc), rrdcalc_name(rc)); return 0; } int update_every = rc->rrdset->update_every; - rrdset_rdlock(rc->rrdset); - time_t first = rrdset_first_entry_t_nolock(rc->rrdset); - time_t last = rrdset_last_entry_t_nolock(rc->rrdset); - rrdset_unlock(rc->rrdset); + time_t first = rrdset_first_entry_t(rc->rrdset); + time_t last = rrdset_last_entry_t(rc->rrdset); if(unlikely(now + update_every < first /* || now - update_every > last */)) { debug(D_HEALTH , "Health not examining alarm '%s.%s' yet (wanted time is out of bounds - we need %lu but got %lu - %lu)." - , rc->chart ? rc->chart : "NOCHART", rc->name, (unsigned long) now, (unsigned long) first + , rrdcalc_chart_name(rc), rrdcalc_name(rc), (unsigned long) now, (unsigned long) first , (unsigned long) last); return 0; } @@ -577,7 +709,7 @@ static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) if(needed + update_every < first || needed - update_every > last) { debug(D_HEALTH , "Health not examining alarm '%s.%s' yet (not enough data yet - we need %lu but got %lu - %lu)." - , rc->chart ? rc->chart : "NOCHART", rc->name, (unsigned long) needed, (unsigned long) first + , rrdcalc_chart_name(rc), rrdcalc_name(rc), (unsigned long) needed, (unsigned long) first , (unsigned long) last); return 0; } @@ -587,7 +719,7 @@ static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) } static inline int check_if_resumed_from_suspension(void) { - static usec_t last_realtime = 0, last_monotonic = 0; + static __thread usec_t last_realtime = 0, last_monotonic = 0; usec_t realtime = now_realtime_usec(), monotonic = now_monotonic_usec(); int ret = 0; @@ -603,41 +735,142 @@ static inline int check_if_resumed_from_suspension(void) { return ret; } -static void health_main_cleanup(void *ptr) { +static void health_thread_cleanup(void *ptr) { worker_unregister(); - struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; - static_thread->enabled = NETDATA_MAIN_THREAD_EXITING; + struct health_state *h = ptr; + h->host->health_spawn = 0; + + netdata_thread_cancel(netdata_thread_self()); + log_health("[%s]: Health thread ended.", rrdhost_hostname(h->host)); + debug(D_HEALTH, "HEALTH %s: Health thread ended.", rrdhost_hostname(h->host)); +} + +static void initialize_health(RRDHOST *host, int is_localhost) { + if(!host->health_enabled || rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)) return; + rrdhost_flag_set(host, RRDHOST_FLAG_INITIALIZED_HEALTH); + + log_health("[%s]: Initializing health.", rrdhost_hostname(host)); + + host->health_default_warn_repeat_every = config_get_duration(CONFIG_SECTION_HEALTH, "default repeat warning", "never"); + host->health_default_crit_repeat_every = config_get_duration(CONFIG_SECTION_HEALTH, "default repeat critical", "never"); + + host->health_log.next_log_id = 1; + host->health_log.next_alarm_id = 1; + host->health_log.max = 1000; + host->health_log.next_log_id = (uint32_t)now_realtime_sec(); + host->health_log.next_alarm_id = 0; + + long n = config_get_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", host->health_log.max); + if(n < 10) { + error("Host '%s': health configuration has invalid max log entries %ld. Using default %u", rrdhost_hostname(host), n, host->health_log.max); + config_set_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", (long)host->health_log.max); + } + else + host->health_log.max = (unsigned int)n; + + netdata_rwlock_init(&host->health_log.alarm_log_rwlock); + + char filename[FILENAME_MAX + 1]; + + if(!is_localhost) { + int r = mkdir(host->varlib_dir, 0775); + if (r != 0 && errno != EEXIST) + error("Host '%s': cannot create directory '%s'", rrdhost_hostname(host), host->varlib_dir); + } + + { + snprintfz(filename, FILENAME_MAX, "%s/health", host->varlib_dir); + int r = mkdir(filename, 0775); + if(r != 0 && errno != EEXIST) + error("Host '%s': cannot create directory '%s'", rrdhost_hostname(host), filename); + } + snprintfz(filename, FILENAME_MAX, "%s/health/health-log.db", host->varlib_dir); + host->health_log_filename = strdupz(filename); + + snprintfz(filename, FILENAME_MAX, "%s/alarm-notify.sh", netdata_configured_primary_plugins_dir); + host->health_default_exec = string_strdupz(config_get(CONFIG_SECTION_HEALTH, "script to execute on alarm", filename)); + host->health_default_recipient = string_strdupz("root"); + + if (!file_is_migrated(host->health_log_filename)) { + int rc = sql_create_health_log_table(host); + if (unlikely(rc)) { + log_health("[%s]: Failed to create health log table in the database", rrdhost_hostname(host)); + health_alarm_log_load(host); + health_alarm_log_open(host); + } + else { + health_alarm_log_load(host); + add_migrated_file(host->health_log_filename, 0); + } + } else { + // TODO: This needs to go to the metadata thread + // Health should wait before accessing the table (needs to be created by the metadata thread) + sql_create_health_log_table(host); + sql_health_alarm_log_load(host); + } + + // ------------------------------------------------------------------------ + // load health configuration + + health_readdir(host, health_user_config_dir(), health_stock_config_dir(), NULL); - info("cleaning up..."); + // link the loaded alarms to their charts + RRDSET *st; + rrdset_foreach_write(st, host) { + if (rrdset_flag_check(st, RRDSET_FLAG_ARCHIVED)) + continue; + + rrdcalc_link_matching_alerts_to_rrdset(st); + rrdcalctemplate_link_matching_templates_to_rrdset(st); + } + rrdset_foreach_done(st); - static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; + //Discard alarms with labels that do not apply to host + rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(host); + + health_silencers_init(); +} + +static void health_sleep(time_t next_run, unsigned int loop __maybe_unused, RRDHOST *host) { + time_t now = now_realtime_sec(); + if(now < next_run) { + worker_is_idle(); + debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs", loop, (int) (next_run - now)); + while (now < next_run && host->health_enabled && !netdata_exit) { + sleep_usec(USEC_PER_SEC); + now = now_realtime_sec(); + } + } + else { + debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration now", loop); + } } -static SILENCE_TYPE check_silenced(RRDCALC *rc, char* host, SILENCERS *silencers) { +static SILENCE_TYPE check_silenced(RRDCALC *rc, const char *host, SILENCERS *silencers) { SILENCER *s; debug(D_HEALTH, "Checking if alarm was silenced via the command API. Alarm info name:%s context:%s chart:%s host:%s family:%s", - rc->name, (rc->rrdset)?rc->rrdset->context:"", rc->chart, host, (rc->rrdset)?rc->rrdset->family:""); + rrdcalc_name(rc), (rc->rrdset)?rrdset_context(rc->rrdset):"", rrdcalc_chart_name(rc), host, (rc->rrdset)?rrdset_family(rc->rrdset):""); for (s = silencers->silencers; s!=NULL; s=s->next){ if ( - (!s->alarms_pattern || (rc->name && s->alarms_pattern && simple_pattern_matches(s->alarms_pattern,rc->name))) && - (!s->contexts_pattern || (rc->rrdset && rc->rrdset->context && s->contexts_pattern && simple_pattern_matches(s->contexts_pattern,rc->rrdset->context))) && + (!s->alarms_pattern || (rc->name && s->alarms_pattern && simple_pattern_matches(s->alarms_pattern, rrdcalc_name(rc)))) && + (!s->contexts_pattern || (rc->rrdset && rc->rrdset->context && s->contexts_pattern && simple_pattern_matches(s->contexts_pattern, rrdset_context(rc->rrdset)))) && (!s->hosts_pattern || (host && s->hosts_pattern && simple_pattern_matches(s->hosts_pattern,host))) && - (!s->charts_pattern || (rc->chart && s->charts_pattern && simple_pattern_matches(s->charts_pattern,rc->chart))) && - (!s->families_pattern || (rc->rrdset && rc->rrdset->family && s->families_pattern && simple_pattern_matches(s->families_pattern,rc->rrdset->family))) + (!s->charts_pattern || (rc->chart && s->charts_pattern && simple_pattern_matches(s->charts_pattern, rrdcalc_chart_name(rc)))) && + (!s->families_pattern || (rc->rrdset && rc->rrdset->family && s->families_pattern && simple_pattern_matches(s->families_pattern, rrdset_family(rc->rrdset)))) ) { debug(D_HEALTH, "Alarm matches command API silence entry %s:%s:%s:%s:%s", s->alarms,s->charts, s->contexts, s->hosts, s->families); if (unlikely(silencers->stype == STYPE_NONE)) { - debug(D_HEALTH, "Alarm %s matched a silence entry, but no SILENCE or DISABLE command was issued via the command API. The match has no effect.", rc->name); + debug(D_HEALTH, "Alarm %s matched a silence entry, but no SILENCE or DISABLE command was issued via the command API. The match has no effect.", rrdcalc_name(rc)); } else { debug(D_HEALTH, "Alarm %s via the command API - name:%s context:%s chart:%s host:%s family:%s" , (silencers->stype == STYPE_DISABLE_ALARMS)?"Disabled":"Silenced" - , rc->name - , (rc->rrdset)?rc->rrdset->context:"" - , rc->chart + , rrdcalc_name(rc) + , (rc->rrdset)?rrdset_context(rc->rrdset):"" + , rrdcalc_chart_name(rc) , host - , (rc->rrdset)?rc->rrdset->family:"" + , (rc->rrdset)?rrdset_family(rc->rrdset):"" ); } return silencers->stype; @@ -657,66 +890,86 @@ static SILENCE_TYPE check_silenced(RRDCALC *rc, char* host, SILENCERS *silencers * @return It returns 1 case rrdcalc_flags is DISABLED or 0 otherwise */ static int update_disabled_silenced(RRDHOST *host, RRDCALC *rc) { - uint32_t rrdcalc_flags_old = rc->rrdcalc_flags; + uint32_t rrdcalc_flags_old = rc->run_flags; // Clear the flags - rc->rrdcalc_flags &= ~(RRDCALC_FLAG_DISABLED | RRDCALC_FLAG_SILENCED); + rc->run_flags &= ~(RRDCALC_FLAG_DISABLED | RRDCALC_FLAG_SILENCED); if (unlikely(silencers->all_alarms)) { - if (silencers->stype == STYPE_DISABLE_ALARMS) rc->rrdcalc_flags |= RRDCALC_FLAG_DISABLED; - else if (silencers->stype == STYPE_SILENCE_NOTIFICATIONS) rc->rrdcalc_flags |= RRDCALC_FLAG_SILENCED; + if (silencers->stype == STYPE_DISABLE_ALARMS) rc->run_flags |= RRDCALC_FLAG_DISABLED; + else if (silencers->stype == STYPE_SILENCE_NOTIFICATIONS) rc->run_flags |= RRDCALC_FLAG_SILENCED; } else { - SILENCE_TYPE st = check_silenced(rc, host->hostname, silencers); - if (st == STYPE_DISABLE_ALARMS) rc->rrdcalc_flags |= RRDCALC_FLAG_DISABLED; - else if (st == STYPE_SILENCE_NOTIFICATIONS) rc->rrdcalc_flags |= RRDCALC_FLAG_SILENCED; + SILENCE_TYPE st = check_silenced(rc, rrdhost_hostname(host), silencers); + if (st == STYPE_DISABLE_ALARMS) rc->run_flags |= RRDCALC_FLAG_DISABLED; + else if (st == STYPE_SILENCE_NOTIFICATIONS) rc->run_flags |= RRDCALC_FLAG_SILENCED; } - if (rrdcalc_flags_old != rc->rrdcalc_flags) { + if (rrdcalc_flags_old != rc->run_flags) { info("Alarm silencing changed for host '%s' alarm '%s': Disabled %s->%s Silenced %s->%s", - host->hostname, - rc->name, + rrdhost_hostname(host), + rrdcalc_name(rc), (rrdcalc_flags_old & RRDCALC_FLAG_DISABLED)?"true":"false", - (rc->rrdcalc_flags & RRDCALC_FLAG_DISABLED)?"true":"false", + (rc->run_flags & RRDCALC_FLAG_DISABLED)?"true":"false", (rrdcalc_flags_old & RRDCALC_FLAG_SILENCED)?"true":"false", - (rc->rrdcalc_flags & RRDCALC_FLAG_SILENCED)?"true":"false" + (rc->run_flags & RRDCALC_FLAG_SILENCED)?"true":"false" ); } - if (rc->rrdcalc_flags & RRDCALC_FLAG_DISABLED) + if (rc->run_flags & RRDCALC_FLAG_DISABLED) return 1; else return 0; } -// Create alarms for dimensions that have been added to charts -// since the previous iteration. -static void init_pending_foreach_alarms(RRDHOST *host) { +static void health_execute_delayed_initializations(RRDHOST *host) { RRDSET *st; - RRDDIM *rd; - if (!rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_FOREACH_ALARMS)) - return; + if (!rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION)) return; + rrdhost_flag_clear(host, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION); - rrdhost_wrlock(host); + rrdset_foreach_reentrant(st, host) { + if(!rrdset_flag_check(st, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION)) continue; + rrdset_flag_clear(st, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION); - rrdset_foreach_write(st, host) { - if (!rrdset_flag_check(st, RRDSET_FLAG_PENDING_FOREACH_ALARMS)) - continue; + worker_is_busy(WORKER_HEALTH_JOB_DELAYED_INIT_RRDSET); + + if(!st->rrdfamily) + st->rrdfamily = rrdfamily_add_and_acquire(host, rrdset_family(st)); + + if(!st->rrdvars) + st->rrdvars = rrdvariables_create(); + + rrddimvar_index_init(st); - rrdset_rdlock(st); + rrdsetvar_add_and_leave_released(st, "last_collected_t", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, RRDVAR_FLAG_NONE); + rrdsetvar_add_and_leave_released(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, RRDVAR_FLAG_NONE); + rrdsetvar_add_and_leave_released(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, RRDVAR_FLAG_NONE); + rrdsetvar_add_and_leave_released(st, "update_every", RRDVAR_TYPE_INT, &st->update_every, RRDVAR_FLAG_NONE); + rrdcalc_link_matching_alerts_to_rrdset(st); + rrdcalctemplate_link_matching_templates_to_rrdset(st); + + RRDDIM *rd; rrddim_foreach_read(rd, st) { - if (!rrddim_flag_check(rd, RRDDIM_FLAG_PENDING_FOREACH_ALARM)) - continue; + if(!rrddim_flag_check(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION)) continue; + rrddim_flag_clear(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION); - rrdcalc_link_to_rrddim(rd, st, host); + worker_is_busy(WORKER_HEALTH_JOB_DELAYED_INIT_RRDDIM); - rrddim_flag_clear(rd, RRDDIM_FLAG_PENDING_FOREACH_ALARM); - } + rrddimvar_add_and_leave_released(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->last_stored_value, RRDVAR_FLAG_NONE); + rrddimvar_add_and_leave_released(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->last_collected_value, RRDVAR_FLAG_NONE); + rrddimvar_add_and_leave_released(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->last_collected_time.tv_sec, RRDVAR_FLAG_NONE); - rrdset_flag_clear(st, RRDSET_FLAG_PENDING_FOREACH_ALARMS); - rrdset_unlock(st); - } + RRDCALCTEMPLATE *rt; + foreach_rrdcalctemplate_read(host, rt) { + if(!rt->foreach_dimension_pattern) + continue; - rrdhost_flag_clear(host, RRDHOST_FLAG_PENDING_FOREACH_ALARMS); - rrdhost_unlock(host); + if(rrdcalctemplate_check_rrdset_conditions(rt, st, host)) + rrdcalctemplate_check_rrddim_conditions_and_link(rt, st, rd, host); + } + foreach_rrdcalctemplate_done(rt); + } + rrddim_foreach_done(rd); + } + rrdset_foreach_done(st); } /** @@ -729,19 +982,6 @@ static void init_pending_foreach_alarms(RRDHOST *host) { * @return It always returns NULL */ -#define WORKER_HEALTH_JOB_RRD_LOCK 0 -#define WORKER_HEALTH_JOB_HOST_LOCK 1 -#define WORKER_HEALTH_JOB_DB_QUERY 2 -#define WORKER_HEALTH_JOB_CALC_EVAL 3 -#define WORKER_HEALTH_JOB_WARNING_EVAL 4 -#define WORKER_HEALTH_JOB_CRITICAL_EVAL 5 -#define WORKER_HEALTH_JOB_ALARM_LOG_ENTRY 6 -#define WORKER_HEALTH_JOB_ALARM_LOG_PROCESS 7 - -#if WORKER_UTILIZATION_MAX_JOB_TYPES < 8 -#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 8 -#endif - void *health_main(void *ptr) { worker_register("HEALTH"); worker_register_job_name(WORKER_HEALTH_JOB_RRD_LOCK, "rrd lock"); @@ -752,8 +992,14 @@ void *health_main(void *ptr) { worker_register_job_name(WORKER_HEALTH_JOB_CRITICAL_EVAL, "critical eval"); worker_register_job_name(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY, "alarm log entry"); worker_register_job_name(WORKER_HEALTH_JOB_ALARM_LOG_PROCESS, "alarm log process"); + worker_register_job_name(WORKER_HEALTH_JOB_DELAYED_INIT_RRDSET, "rrdset init"); + worker_register_job_name(WORKER_HEALTH_JOB_DELAYED_INIT_RRDDIM, "rrddim init"); - netdata_thread_cleanup_push(health_main_cleanup, ptr); + struct health_state *h = ptr; + netdata_thread_cleanup_push(health_thread_cleanup, ptr); + + RRDHOST *host = h->host; + initialize_health(host, host == localhost); int min_run_every = (int)config_get_number(CONFIG_SECTION_HEALTH, "run at least every seconds", 10); if(min_run_every < 1) min_run_every = 1; @@ -763,16 +1009,21 @@ void *health_main(void *ptr) { time_t now = now_realtime_sec(); time_t hibernation_delay = config_get_number(CONFIG_SECTION_HEALTH, "postpone alarms during hibernation for seconds", 60); - rrdcalc_labels_unlink(); + bool health_running_logged = false; + + rrdhost_rdlock(host); //CHECK + rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(host); + rrdhost_unlock(host); unsigned int loop = 0; #ifdef ENABLE_ACLK unsigned int marked_aclk_reload_loop = 0; #endif - while(!netdata_exit) { + while(!netdata_exit && host->health_enabled) { loop++; debug(D_HEALTH, "Health monitoring iteration no %u started", loop); + now = now_realtime_sec(); int runnable = 0, apply_hibernation_delay = 0; time_t next_run = now + min_run_every; RRDCALC *rc; @@ -780,433 +1031,500 @@ void *health_main(void *ptr) { if (unlikely(check_if_resumed_from_suspension())) { apply_hibernation_delay = 1; - info( - "Postponing alarm checks for %"PRId64" seconds, " - "because it seems that the system was just resumed from suspension.", - (int64_t)hibernation_delay); + log_health( + "[%s]: Postponing alarm checks for %"PRId64" seconds, " + "because it seems that the system was just resumed from suspension.", + rrdhost_hostname(host), + (int64_t)hibernation_delay); } if (unlikely(silencers->all_alarms && silencers->stype == STYPE_DISABLE_ALARMS)) { - static int logged=0; + static __thread int logged=0; if (!logged) { - info("Skipping health checks, because all alarms are disabled via a %s command.", - HEALTH_CMDAPI_CMD_DISABLEALL); + log_health("[%s]: Skipping health checks, because all alarms are disabled via a %s command.", + rrdhost_hostname(host), + HEALTH_CMDAPI_CMD_DISABLEALL); logged = 1; } } #ifdef ENABLE_ACLK - if (aclk_alert_reloaded && !marked_aclk_reload_loop) + if (host->aclk_alert_reloaded && !marked_aclk_reload_loop) marked_aclk_reload_loop = loop; #endif - worker_is_busy(WORKER_HEALTH_JOB_RRD_LOCK); - rrd_rdlock(); + if (unlikely(apply_hibernation_delay)) { + log_health( + "[%s]: Postponing health checks for %"PRId64" seconds.", + rrdhost_hostname(host), + (int64_t)hibernation_delay); - RRDHOST *host; - rrdhost_foreach_read(host) { - if (unlikely(!host->health_enabled)) + host->health_delay_up_to = now + hibernation_delay; + next_run = now + hibernation_delay; + health_sleep(next_run, loop, host); + } + + if (unlikely(host->health_delay_up_to)) { + if (unlikely(now < host->health_delay_up_to)) { + next_run = host->health_delay_up_to; + health_sleep(next_run, loop, host); continue; + } - if (unlikely(apply_hibernation_delay)) { - info( - "Postponing health checks for %"PRId64" seconds, on host '%s'.", - (int64_t)hibernation_delay, - host->hostname); + log_health("[%s]: Resuming health checks after delay.", rrdhost_hostname(host)); + host->health_delay_up_to = 0; + } - host->health_delay_up_to = now + hibernation_delay; + // wait until cleanup of obsolete charts on children is complete + if (host != localhost) { + if (unlikely(host->trigger_chart_obsoletion_check == 1)) { + log_health("[%s]: Waiting for chart obsoletion check.", rrdhost_hostname(host)); + health_sleep(next_run, loop, host); + continue; } + } - if (unlikely(host->health_delay_up_to)) { - if (unlikely(now < host->health_delay_up_to)) - continue; + if (!health_running_logged) { + log_health("[%s]: Health is running.", rrdhost_hostname(host)); + health_running_logged = true; + } - info("Resuming health checks on host '%s'.", host->hostname); - host->health_delay_up_to = 0; - } + if(likely(!host->health_log_fp) && (loop == 1 || loop % cleanup_sql_every_loop == 0)) + sql_health_alarm_log_cleanup(host); - // wait until cleanup of obsolete charts on children is complete - if (host != localhost) - if (unlikely(host->trigger_chart_obsoletion_check == 1)) - continue; + health_execute_delayed_initializations(host); - if(likely(!host->health_log_fp) && (loop == 1 || loop % cleanup_sql_every_loop == 0)) - sql_health_alarm_log_cleanup(host); + worker_is_busy(WORKER_HEALTH_JOB_HOST_LOCK); - init_pending_foreach_alarms(host); + // the first loop is to lookup values from the db + foreach_rrdcalc_in_rrdhost_read(host, rc) { - worker_is_busy(WORKER_HEALTH_JOB_HOST_LOCK); - rrdhost_rdlock(host); + rrdcalc_update_info_using_rrdset_labels(rc); - // the first loop is to lookup values from the db - for (rc = host->alarms; rc; rc = rc->next) { + if (update_disabled_silenced(host, rc)) + continue; - if (update_disabled_silenced(host, rc)) - continue; + // create an alert removed event if the chart is obsolete and + // has stopped being collected for 60 seconds + if (unlikely(rc->rrdset && rc->status != RRDCALC_STATUS_REMOVED && + rrdset_flag_check(rc->rrdset, RRDSET_FLAG_OBSOLETE) && + now > (rc->rrdset->last_collected_time.tv_sec + 60))) { + if (!rrdcalc_isrepeating(rc)) { + worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY); + time_t now = now_realtime_sec(); + + ALARM_ENTRY *ae = health_create_alarm_entry( + host, + rc->id, + rc->next_event_id++, + rc->config_hash_id, + now, + rc->name, + rc->rrdset->id, + rc->rrdset->context, + rc->rrdset->family, + rc->classification, + rc->component, + rc->type, + rc->exec, + rc->recipient, + now - rc->last_status_change, + rc->value, + NAN, + rc->status, + RRDCALC_STATUS_REMOVED, + rc->source, + rc->units, + rc->info, + 0, + rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0); + + if (ae) { + health_alarm_log_add_entry(host, ae); + rc->old_status = rc->status; + rc->status = RRDCALC_STATUS_REMOVED; + rc->last_status_change = now; + rc->last_updated = now; + rc->value = NAN; - // create an alert removed event if the chart is obsolete and - // has stopped being collected for 60 seconds - if (unlikely(rc->rrdset && rc->status != RRDCALC_STATUS_REMOVED && - rrdset_flag_check(rc->rrdset, RRDSET_FLAG_OBSOLETE) && - now > (rc->rrdset->last_collected_time.tv_sec + 60))) { - if (!rrdcalc_isrepeating(rc)) { - worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY); - time_t now = now_realtime_sec(); - ALARM_ENTRY *ae = health_create_alarm_entry( - host, rc->id, rc->next_event_id++, rc->config_hash_id, now, rc->name, rc->rrdset->id, rc->rrdset->context, - rc->rrdset->family, rc->classification, rc->component, rc->type, rc->exec, rc->recipient, now - rc->last_status_change, - rc->value, NAN, rc->status, RRDCALC_STATUS_REMOVED, rc->source, rc->units, rc->info, 0, 0); - if (ae) { - health_alarm_log(host, ae); - rc->old_status = rc->status; - rc->status = RRDCALC_STATUS_REMOVED; - rc->last_status_change = now; - rc->last_updated = now; - rc->value = NAN; #ifdef ENABLE_ACLK - if (netdata_cloud_setting && likely(!aclk_alert_reloaded)) - sql_queue_alarm_to_aclk(host, ae, 1); + if (netdata_cloud_setting && likely(!host->aclk_alert_reloaded)) + sql_queue_alarm_to_aclk(host, ae, 1); #endif - } } } + } - if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) { - if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE)) - rc->rrdcalc_flags &= ~RRDCALC_FLAG_RUNNABLE; - continue; - } - - runnable++; - rc->old_value = rc->value; - rc->rrdcalc_flags |= RRDCALC_FLAG_RUNNABLE; - - // ------------------------------------------------------------ - // if there is database lookup, do it - - if (unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) { - worker_is_busy(WORKER_HEALTH_JOB_DB_QUERY); - - /* time_t old_db_timestamp = rc->db_before; */ - int value_is_null = 0; + if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) { + if (unlikely(rc->run_flags & RRDCALC_FLAG_RUNNABLE)) + rc->run_flags &= ~RRDCALC_FLAG_RUNNABLE; + continue; + } - int ret = rrdset2value_api_v1(rc->rrdset, NULL, &rc->value, rc->dimensions, 1, - rc->after, rc->before, rc->group, NULL, - 0, rc->options, - &rc->db_after,&rc->db_before, - NULL, NULL, NULL, - &value_is_null, NULL, 0, 0); + runnable++; + rc->old_value = rc->value; + rc->run_flags |= RRDCALC_FLAG_RUNNABLE; + + // ------------------------------------------------------------ + // if there is database lookup, do it + + if (unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) { + worker_is_busy(WORKER_HEALTH_JOB_DB_QUERY); + + /* time_t old_db_timestamp = rc->db_before; */ + int value_is_null = 0; + + int ret = rrdset2value_api_v1(rc->rrdset, NULL, &rc->value, rrdcalc_dimensions(rc), 1, + rc->after, rc->before, rc->group, NULL, + 0, rc->options, + &rc->db_after,&rc->db_before, + NULL, NULL, NULL, + &value_is_null, NULL, 0, 0, + QUERY_SOURCE_HEALTH); + + if (unlikely(ret != 200)) { + // database lookup failed + rc->value = NAN; + rc->run_flags |= RRDCALC_FLAG_DB_ERROR; + + debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database lookup returned error %d", + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), ret + ); + } else + rc->run_flags &= ~RRDCALC_FLAG_DB_ERROR; + + /* - RRDCALC_FLAG_DB_STALE not currently used + if (unlikely(old_db_timestamp == rc->db_before)) { + // database is stale + + debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database is stale", host->hostname, rc->chart?rc->chart:"NOCHART", rc->name); + + if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE))) { + rc->rrdcalc_flags |= RRDCALC_FLAG_DB_STALE; + error("Health on host '%s', alarm '%s.%s': database is stale", host->hostname, rc->chart?rc->chart:"NOCHART", rc->name); + } + } + else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE)) + rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_STALE; + */ + + if (unlikely(value_is_null)) { + // collected value is null + rc->value = NAN; + rc->run_flags |= RRDCALC_FLAG_DB_NAN; + + debug(D_HEALTH, + "Health on host '%s', alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)", + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc) + ); + } else + rc->run_flags &= ~RRDCALC_FLAG_DB_NAN; + + debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database lookup gave value " NETDATA_DOUBLE_FORMAT, + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), rc->value + ); + } - if (unlikely(ret != 200)) { - // database lookup failed - rc->value = NAN; - rc->rrdcalc_flags |= RRDCALC_FLAG_DB_ERROR; + // ------------------------------------------------------------ + // if there is calculation expression, run it - debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database lookup returned error %d", - host->hostname, rc->chart ? rc->chart : "NOCHART", rc->name, ret - ); - } else - rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_ERROR; + if (unlikely(rc->calculation)) { + worker_is_busy(WORKER_HEALTH_JOB_CALC_EVAL); - /* - RRDCALC_FLAG_DB_STALE not currently used - if (unlikely(old_db_timestamp == rc->db_before)) { - // database is stale + if (unlikely(!expression_evaluate(rc->calculation))) { + // calculation failed + rc->value = NAN; + rc->run_flags |= RRDCALC_FLAG_CALC_ERROR; - debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database is stale", host->hostname, rc->chart?rc->chart:"NOCHART", rc->name); + debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' failed: %s", + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), + rc->calculation->parsed_as, buffer_tostring(rc->calculation->error_msg) + ); + } else { + rc->run_flags &= ~RRDCALC_FLAG_CALC_ERROR; - if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE))) { - rc->rrdcalc_flags |= RRDCALC_FLAG_DB_STALE; - error("Health on host '%s', alarm '%s.%s': database is stale", host->hostname, rc->chart?rc->chart:"NOCHART", rc->name); - } - } - else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE)) - rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_STALE; - */ + debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' gave value " + NETDATA_DOUBLE_FORMAT + ": %s (source: %s)", rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), + rc->calculation->parsed_as, rc->calculation->result, + buffer_tostring(rc->calculation->error_msg), rrdcalc_source(rc) + ); - if (unlikely(value_is_null)) { - // collected value is null - rc->value = NAN; - rc->rrdcalc_flags |= RRDCALC_FLAG_DB_NAN; + rc->value = rc->calculation->result; + } + } + } + foreach_rrdcalc_in_rrdhost_done(rc); - debug(D_HEALTH, - "Health on host '%s', alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)", - host->hostname, rc->chart ? rc->chart : "NOCHART", rc->name - ); - } else - rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_NAN; + if (unlikely(runnable && !netdata_exit)) { + foreach_rrdcalc_in_rrdhost_read(host, rc) { + if (unlikely(!(rc->run_flags & RRDCALC_FLAG_RUNNABLE))) + continue; - debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database lookup gave value " NETDATA_DOUBLE_FORMAT, host->hostname, rc->chart ? rc->chart : "NOCHART", rc->name, - rc->value - ); + if (rc->run_flags & RRDCALC_FLAG_DISABLED) { + continue; } + RRDCALC_STATUS warning_status = RRDCALC_STATUS_UNDEFINED; + RRDCALC_STATUS critical_status = RRDCALC_STATUS_UNDEFINED; - // ------------------------------------------------------------ - // if there is calculation expression, run it + // -------------------------------------------------------- + // check the warning expression - if (unlikely(rc->calculation)) { - worker_is_busy(WORKER_HEALTH_JOB_CALC_EVAL); + if (likely(rc->warning)) { + worker_is_busy(WORKER_HEALTH_JOB_WARNING_EVAL); - if (unlikely(!expression_evaluate(rc->calculation))) { + if (unlikely(!expression_evaluate(rc->warning))) { // calculation failed - rc->value = NAN; - rc->rrdcalc_flags |= RRDCALC_FLAG_CALC_ERROR; + rc->run_flags |= RRDCALC_FLAG_WARN_ERROR; - debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' failed: %s", - host->hostname, rc->chart ? rc->chart : "NOCHART", rc->name, - rc->calculation->parsed_as, buffer_tostring(rc->calculation->error_msg) - ); + debug(D_HEALTH, + "Health on host '%s', alarm '%s.%s': warning expression failed with error: %s", + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), + buffer_tostring(rc->warning->error_msg) + ); } else { - rc->rrdcalc_flags &= ~RRDCALC_FLAG_CALC_ERROR; - - debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' gave value " + rc->run_flags &= ~RRDCALC_FLAG_WARN_ERROR; + debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': warning expression gave value " NETDATA_DOUBLE_FORMAT - ": %s (source: %s)", host->hostname, rc->chart ? rc->chart : "NOCHART", rc->name, - rc->calculation->parsed_as, rc->calculation->result, - buffer_tostring(rc->calculation->error_msg), rc->source - ); - - rc->value = rc->calculation->result; - - if (rc->local) rc->local->last_updated = now; - if (rc->family) rc->family->last_updated = now; - if (rc->hostid) rc->hostid->last_updated = now; - if (rc->hostname) rc->hostname->last_updated = now; + ": %s (source: %s)", rrdhost_hostname(host), rrdcalc_chart_name(rc), + rrdcalc_name(rc), rc->warning->result, buffer_tostring(rc->warning->error_msg), rrdcalc_source(rc) + ); + warning_status = rrdcalc_value2status(rc->warning->result); } } - } - - rrdhost_unlock(host); - - if (unlikely(runnable && !netdata_exit)) { - rrdhost_rdlock(host); - for (rc = host->alarms; rc; rc = rc->next) { - if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE))) - continue; + // -------------------------------------------------------- + // check the critical expression - if (rc->rrdcalc_flags & RRDCALC_FLAG_DISABLED) { - continue; - } - RRDCALC_STATUS warning_status = RRDCALC_STATUS_UNDEFINED; - RRDCALC_STATUS critical_status = RRDCALC_STATUS_UNDEFINED; - - // -------------------------------------------------------- - // check the warning expression - - if (likely(rc->warning)) { - worker_is_busy(WORKER_HEALTH_JOB_WARNING_EVAL); - - if (unlikely(!expression_evaluate(rc->warning))) { - // calculation failed - rc->rrdcalc_flags |= RRDCALC_FLAG_WARN_ERROR; - - debug(D_HEALTH, - "Health on host '%s', alarm '%s.%s': warning expression failed with error: %s", - host->hostname, rc->chart ? rc->chart : "NOCHART", rc->name, - buffer_tostring(rc->warning->error_msg) - ); - } else { - rc->rrdcalc_flags &= ~RRDCALC_FLAG_WARN_ERROR; - debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': warning expression gave value " - NETDATA_DOUBLE_FORMAT - ": %s (source: %s)", host->hostname, rc->chart ? rc->chart : "NOCHART", - rc->name, rc->warning->result, buffer_tostring(rc->warning->error_msg), rc->source - ); - warning_status = rrdcalc_value2status(rc->warning->result); - } - } + if (likely(rc->critical)) { + worker_is_busy(WORKER_HEALTH_JOB_CRITICAL_EVAL); - // -------------------------------------------------------- - // check the critical expression - - if (likely(rc->critical)) { - worker_is_busy(WORKER_HEALTH_JOB_CRITICAL_EVAL); - - if (unlikely(!expression_evaluate(rc->critical))) { - // calculation failed - rc->rrdcalc_flags |= RRDCALC_FLAG_CRIT_ERROR; - - debug(D_HEALTH, - "Health on host '%s', alarm '%s.%s': critical expression failed with error: %s", - host->hostname, rc->chart ? rc->chart : "NOCHART", rc->name, - buffer_tostring(rc->critical->error_msg) - ); - } else { - rc->rrdcalc_flags &= ~RRDCALC_FLAG_CRIT_ERROR; - debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': critical expression gave value " - NETDATA_DOUBLE_FORMAT - ": %s (source: %s)", host->hostname, rc->chart ? rc->chart : "NOCHART", - rc->name, rc->critical->result, buffer_tostring(rc->critical->error_msg), - rc->source - ); - critical_status = rrdcalc_value2status(rc->critical->result); - } - } - - // -------------------------------------------------------- - // decide the final alarm status - - RRDCALC_STATUS status = RRDCALC_STATUS_UNDEFINED; - - switch (warning_status) { - case RRDCALC_STATUS_CLEAR: - status = RRDCALC_STATUS_CLEAR; - break; - - case RRDCALC_STATUS_RAISED: - status = RRDCALC_STATUS_WARNING; - break; + if (unlikely(!expression_evaluate(rc->critical))) { + // calculation failed + rc->run_flags |= RRDCALC_FLAG_CRIT_ERROR; - default: - break; + debug(D_HEALTH, + "Health on host '%s', alarm '%s.%s': critical expression failed with error: %s", + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), + buffer_tostring(rc->critical->error_msg) + ); + } else { + rc->run_flags &= ~RRDCALC_FLAG_CRIT_ERROR; + debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': critical expression gave value " + NETDATA_DOUBLE_FORMAT + ": %s (source: %s)", rrdhost_hostname(host), rrdcalc_chart_name(rc), + rrdcalc_name(rc), rc->critical->result, buffer_tostring(rc->critical->error_msg), + rrdcalc_source(rc) + ); + critical_status = rrdcalc_value2status(rc->critical->result); } + } - switch (critical_status) { - case RRDCALC_STATUS_CLEAR: - if (status == RRDCALC_STATUS_UNDEFINED) - status = RRDCALC_STATUS_CLEAR; - break; + // -------------------------------------------------------- + // decide the final alarm status - case RRDCALC_STATUS_RAISED: - status = RRDCALC_STATUS_CRITICAL; - break; + RRDCALC_STATUS status = RRDCALC_STATUS_UNDEFINED; - default: - break; - } + switch (warning_status) { + case RRDCALC_STATUS_CLEAR: + status = RRDCALC_STATUS_CLEAR; + break; - // -------------------------------------------------------- - // check if the new status and the old differ + case RRDCALC_STATUS_RAISED: + status = RRDCALC_STATUS_WARNING; + break; - if (status != rc->status) { - worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY); - int delay = 0; - - // apply trigger hysteresis + default: + break; + } - if (now > rc->delay_up_to_timestamp) { - rc->delay_up_current = rc->delay_up_duration; - rc->delay_down_current = rc->delay_down_duration; - rc->delay_last = 0; - rc->delay_up_to_timestamp = 0; - } else { - rc->delay_up_current = (int) (rc->delay_up_current * rc->delay_multiplier); - if (rc->delay_up_current > rc->delay_max_duration) - rc->delay_up_current = rc->delay_max_duration; + switch (critical_status) { + case RRDCALC_STATUS_CLEAR: + if (status == RRDCALC_STATUS_UNDEFINED) + status = RRDCALC_STATUS_CLEAR; + break; - rc->delay_down_current = (int) (rc->delay_down_current * rc->delay_multiplier); - if (rc->delay_down_current > rc->delay_max_duration) - rc->delay_down_current = rc->delay_max_duration; - } + case RRDCALC_STATUS_RAISED: + status = RRDCALC_STATUS_CRITICAL; + break; - if (status > rc->status) - delay = rc->delay_up_current; - else - delay = rc->delay_down_current; + default: + break; + } - // COMMENTED: because we do need to send raising alarms - // if(now + delay < rc->delay_up_to_timestamp) - // delay = (int)(rc->delay_up_to_timestamp - now); + // -------------------------------------------------------- + // check if the new status and the old differ - rc->delay_last = delay; - rc->delay_up_to_timestamp = now + delay; + if (status != rc->status) { + worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY); + int delay = 0; + // apply trigger hysteresis - ALARM_ENTRY *ae = health_create_alarm_entry( - host, rc->id, rc->next_event_id++, rc->config_hash_id, now, rc->name, rc->rrdset->id, rc->rrdset->context, - rc->rrdset->family, rc->classification, rc->component, rc->type, rc->exec, rc->recipient, now - rc->last_status_change, - rc->old_value, rc->value, rc->status, status, rc->source, rc->units, rc->info, - rc->delay_last, - ( - ((rc->options & RRDCALC_FLAG_NO_CLEAR_NOTIFICATION)? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0) | - ((rc->rrdcalc_flags & RRDCALC_FLAG_SILENCED)? HEALTH_ENTRY_FLAG_SILENCED : 0) - ) - ); - health_alarm_log(host, ae); + if (now > rc->delay_up_to_timestamp) { + rc->delay_up_current = rc->delay_up_duration; + rc->delay_down_current = rc->delay_down_duration; + rc->delay_last = 0; + rc->delay_up_to_timestamp = 0; + } else { + rc->delay_up_current = (int) (rc->delay_up_current * rc->delay_multiplier); + if (rc->delay_up_current > rc->delay_max_duration) + rc->delay_up_current = rc->delay_max_duration; - rc->last_status_change = now; - rc->old_status = rc->status; - rc->status = status; + rc->delay_down_current = (int) (rc->delay_down_current * rc->delay_multiplier); + if (rc->delay_down_current > rc->delay_max_duration) + rc->delay_down_current = rc->delay_max_duration; } - rc->last_updated = now; - rc->next_update = now + rc->update_every; - - if (next_run > rc->next_update) - next_run = rc->next_update; + if (status > rc->status) + delay = rc->delay_up_current; + else + delay = rc->delay_down_current; + + // COMMENTED: because we do need to send raising alarms + // if(now + delay < rc->delay_up_to_timestamp) + // delay = (int)(rc->delay_up_to_timestamp - now); + + rc->delay_last = delay; + rc->delay_up_to_timestamp = now + delay; + + ALARM_ENTRY *ae = health_create_alarm_entry( + host, + rc->id, + rc->next_event_id++, + rc->config_hash_id, + now, + rc->name, + rc->rrdset->id, + rc->rrdset->context, + rc->rrdset->family, + rc->classification, + rc->component, + rc->type, + rc->exec, + rc->recipient, + now - rc->last_status_change, + rc->old_value, + rc->value, + rc->status, + status, + rc->source, + rc->units, + rc->info, + rc->delay_last, + ( + ((rc->options & RRDCALC_OPTION_NO_CLEAR_NOTIFICATION)? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0) | + ((rc->run_flags & RRDCALC_FLAG_SILENCED)? HEALTH_ENTRY_FLAG_SILENCED : 0) | + (rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0) + ) + ); + + health_alarm_log_add_entry(host, ae); + + log_health("[%s]: Alert event for [%s.%s], value [%s], status [%s].", rrdhost_hostname(host), ae_chart_name(ae), ae_name(ae), ae_new_value_string(ae), rrdcalc_status2string(ae->new_status)); + + rc->last_status_change = now; + rc->old_status = rc->status; + rc->status = status; } - // process repeating alarms - RRDCALC *rc; - for(rc = host->alarms; rc ; rc = rc->next) { - int repeat_every = 0; - if(unlikely(rrdcalc_isrepeating(rc) && rc->delay_up_to_timestamp <= now)) { - if(unlikely(rc->status == RRDCALC_STATUS_WARNING)) { - rc->rrdcalc_flags &= ~RRDCALC_FLAG_RUN_ONCE; - repeat_every = rc->warn_repeat_every; - } else if(unlikely(rc->status == RRDCALC_STATUS_CRITICAL)) { - rc->rrdcalc_flags &= ~RRDCALC_FLAG_RUN_ONCE; - repeat_every = rc->crit_repeat_every; - } else if(unlikely(rc->status == RRDCALC_STATUS_CLEAR)) { - if(!(rc->rrdcalc_flags & RRDCALC_FLAG_RUN_ONCE)) { - if(rc->old_status == RRDCALC_STATUS_CRITICAL) { - repeat_every = 1; - } else if (rc->old_status == RRDCALC_STATUS_WARNING) { - repeat_every = 1; - } + rc->last_updated = now; + rc->next_update = now + rc->update_every; + + if (next_run > rc->next_update) + next_run = rc->next_update; + } + foreach_rrdcalc_in_rrdhost_done(rc); + + // process repeating alarms + foreach_rrdcalc_in_rrdhost_read(host, rc) { + int repeat_every = 0; + if(unlikely(rrdcalc_isrepeating(rc) && rc->delay_up_to_timestamp <= now)) { + if(unlikely(rc->status == RRDCALC_STATUS_WARNING)) { + rc->run_flags &= ~RRDCALC_FLAG_RUN_ONCE; + repeat_every = rc->warn_repeat_every; + } else if(unlikely(rc->status == RRDCALC_STATUS_CRITICAL)) { + rc->run_flags &= ~RRDCALC_FLAG_RUN_ONCE; + repeat_every = rc->crit_repeat_every; + } else if(unlikely(rc->status == RRDCALC_STATUS_CLEAR)) { + if(!(rc->run_flags & RRDCALC_FLAG_RUN_ONCE)) { + if(rc->old_status == RRDCALC_STATUS_CRITICAL) { + repeat_every = 1; + } else if (rc->old_status == RRDCALC_STATUS_WARNING) { + repeat_every = 1; } } - } else { - continue; } + } else { + continue; + } - if(unlikely(repeat_every > 0 && (rc->last_repeat + repeat_every) <= now)) { - worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY); - rc->last_repeat = now; - if (likely(rc->times_repeat < UINT32_MAX)) rc->times_repeat++; - ALARM_ENTRY *ae = health_create_alarm_entry( - host, rc->id, rc->next_event_id++, rc->config_hash_id, now, rc->name, rc->rrdset->id, rc->rrdset->context, - rc->rrdset->family, rc->classification, rc->component, rc->type, rc->exec, rc->recipient, now - rc->last_status_change, - rc->old_value, rc->value, rc->old_status, rc->status, rc->source, rc->units, rc->info, - rc->delay_last, - ( - ((rc->options & RRDCALC_FLAG_NO_CLEAR_NOTIFICATION)? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0) | - ((rc->rrdcalc_flags & RRDCALC_FLAG_SILENCED)? HEALTH_ENTRY_FLAG_SILENCED : 0) - ) - ); - ae->last_repeat = rc->last_repeat; - if (!(rc->rrdcalc_flags & RRDCALC_FLAG_RUN_ONCE) && rc->status == RRDCALC_STATUS_CLEAR) { - ae->flags |= HEALTH_ENTRY_RUN_ONCE; - } - rc->rrdcalc_flags |= RRDCALC_FLAG_RUN_ONCE; - health_process_notifications(host, ae); - debug(D_HEALTH, "Notification sent for the repeating alarm %u.", ae->alarm_id); - health_alarm_wait_for_execution(ae); - health_alarm_log_free_one_nochecks_nounlink(ae); + if(unlikely(repeat_every > 0 && (rc->last_repeat + repeat_every) <= now)) { + worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY); + rc->last_repeat = now; + if (likely(rc->times_repeat < UINT32_MAX)) rc->times_repeat++; + + ALARM_ENTRY *ae = health_create_alarm_entry( + host, + rc->id, + rc->next_event_id++, + rc->config_hash_id, + now, + rc->name, + rc->rrdset->id, + rc->rrdset->context, + rc->rrdset->family, + rc->classification, + rc->component, + rc->type, + rc->exec, + rc->recipient, + now - rc->last_status_change, + rc->old_value, + rc->value, + rc->old_status, + rc->status, + rc->source, + rc->units, + rc->info, + rc->delay_last, + ( + ((rc->options & RRDCALC_OPTION_NO_CLEAR_NOTIFICATION)? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0) | + ((rc->run_flags & RRDCALC_FLAG_SILENCED)? HEALTH_ENTRY_FLAG_SILENCED : 0) | + (rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0) + ) + ); + + ae->last_repeat = rc->last_repeat; + if (!(rc->run_flags & RRDCALC_FLAG_RUN_ONCE) && rc->status == RRDCALC_STATUS_CLEAR) { + ae->flags |= HEALTH_ENTRY_RUN_ONCE; } + rc->run_flags |= RRDCALC_FLAG_RUN_ONCE; + health_process_notifications(host, ae); + debug(D_HEALTH, "Notification sent for the repeating alarm %u.", ae->alarm_id); + health_alarm_wait_for_execution(ae); + health_alarm_log_free_one_nochecks_nounlink(ae); } - - rrdhost_unlock(host); } + foreach_rrdcalc_in_rrdhost_done(rc); + } - if (unlikely(netdata_exit)) - break; + if (unlikely(netdata_exit)) + break; - // execute notifications - // and cleanup - worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_PROCESS); - health_alarm_log_process(host); + // execute notifications + // and cleanup + worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_PROCESS); + health_alarm_log_process(host); - if (unlikely(netdata_exit)) { - // wait for all notifications to finish before allowing health to be cleaned up - ALARM_ENTRY *ae; - while (NULL != (ae = alarm_notifications_in_progress.head)) { - health_alarm_wait_for_execution(ae); - } - break; + if (unlikely(netdata_exit)) { + // wait for all notifications to finish before allowing health to be cleaned up + ALARM_ENTRY *ae; + while (NULL != (ae = alarm_notifications_in_progress.head)) { + health_alarm_wait_for_execution(ae); } - - } /* rrdhost_foreach */ + break; + } // wait for all notifications to finish before allowing health to be cleaned up ALARM_ENTRY *ae; @@ -1215,34 +1533,49 @@ void *health_main(void *ptr) { } #ifdef ENABLE_ACLK - if (netdata_cloud_setting && unlikely(aclk_alert_reloaded) && loop > (marked_aclk_reload_loop + 2)) { - rrdhost_foreach_read(host) { - if (unlikely(!host->health_enabled)) - continue; - sql_queue_removed_alerts_to_aclk(host); - } - aclk_alert_reloaded = 0; - marked_aclk_reload_loop = 0; - } + if (netdata_cloud_setting && unlikely(host->aclk_alert_reloaded) && loop > (marked_aclk_reload_loop + 2)) { + sql_queue_removed_alerts_to_aclk(host); + host->aclk_alert_reloaded = 0; + marked_aclk_reload_loop = 0; + } #endif - rrd_unlock(); - if(unlikely(netdata_exit)) break; - now = now_realtime_sec(); - if(now < next_run) { - worker_is_idle(); - debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs", loop, (int) (next_run - now)); - sleep_usec(USEC_PER_SEC * (usec_t) (next_run - now)); - now = now_realtime_sec(); - } - else - debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration now", loop); + health_sleep(next_run, loop, host); } // forever netdata_thread_cleanup_pop(1); return NULL; } + +void health_add_host_labels(void) { + DICTIONARY *labels = localhost->rrdlabels; + + int is_ephemeral = appconfig_get_boolean(&netdata_config, CONFIG_SECTION_HEALTH, "is ephemeral", CONFIG_BOOLEAN_NO); + rrdlabels_add(labels, "_is_ephemeral", is_ephemeral ? "true" : "false", RRDLABEL_SRC_CONFIG); + + int has_unstable_connection = appconfig_get_boolean(&netdata_config, CONFIG_SECTION_HEALTH, "has unstable connection", CONFIG_BOOLEAN_NO); + rrdlabels_add(labels, "_has_unstable_connection", has_unstable_connection ? "true" : "false", RRDLABEL_SRC_CONFIG); +} + +void health_thread_spawn(RRDHOST * host) { + if(!host->health_spawn) { + char tag[NETDATA_THREAD_TAG_MAX + 1]; + snprintfz(tag, NETDATA_THREAD_TAG_MAX, "HEALTH[%s]", rrdhost_hostname(host)); + struct health_state *health = callocz(1, sizeof(*health)); + health->host = host; + + if(netdata_thread_create(&host->health_thread, tag, NETDATA_THREAD_OPTION_JOINABLE, health_main, (void *) health)) { + log_health("[%s]: Failed to create new thread for client.", rrdhost_hostname(host)); + error("HEALTH [%s]: Failed to create new thread for client.", rrdhost_hostname(host)); + } + else { + log_health("[%s]: Created new thread for client.", rrdhost_hostname(host)); + host->health_spawn = 1; + host->aclk_alert_reloaded = 1; + } + } +} diff --git a/health/health.d/dns_query.conf b/health/health.d/dns_query.conf index ec4937c0a..b9d6c2374 100644 --- a/health/health.d/dns_query.conf +++ b/health/health.d/dns_query.conf @@ -1,15 +1,14 @@ - # detect dns query failure - template: dns_query_time_query_time - on: dns_query_time.query_time - class: Latency + template: dns_query_query_status + on: dns_query.query_status + class: Errors type: DNS component: DNS - lookup: average -10s unaligned foreach * - units: ms + calc: $success + units: status every: 10s - warn: $this == nan - delay: up 20s down 5m multiplier 1.5 max 1h - info: average DNS query round trip time over the last 10 seconds + warn: $this != nan && $this != 1 + delay: up 30s down 5m multiplier 1.5 max 1h + info: DNS request type $label:record_type to server $label:server is unsuccessful to: sysadmin diff --git a/health/health.d/go.d.plugin.conf b/health/health.d/go.d.plugin.conf index a84ab342f..cd87fe0e7 100644 --- a/health/health.d/go.d.plugin.conf +++ b/health/health.d/go.d.plugin.conf @@ -3,7 +3,7 @@ template: go.d_job_last_collected_secs on: netdata.go_plugin_execution_time - class: Error + class: Errors type: Netdata component: go.d.plugin module: !* * diff --git a/health/health.d/ml.conf b/health/health.d/ml.conf index 9bcc81e76..6836ce7b1 100644 --- a/health/health.d/ml.conf +++ b/health/health.d/ml.conf @@ -1,10 +1,26 @@ # below are some examples of using the `anomaly-bit` option to define alerts based on anomaly # rates as opposed to raw metric values. You can read more about the anomaly-bit and Netdata's # native anomaly detection here: -# https://learn.netdata.cloud/docs/configure/machine-learning#anomaly-bit---100--anomalous-0--normal +# https://learn.netdata.cloud/docs/agent/ml#anomaly-bit---100--anomalous-0--normal # examples below are commented, you would need to uncomment and adjust as desired to enable them. +# node level anomaly rate example +# https://learn.netdata.cloud/docs/agent/ml#node-anomaly-rate +# if node level anomaly rate is between 1-5% then warning (pick your own threshold that works best via tial and error). +# if node level anomaly rate is above 5% then critical (pick your own threshold that works best via tial and error). +# template: ml_1min_node_ar +# on: anomaly_detection.anomaly_rate +# os: linux +# hosts: * +# lookup: average -1m foreach anomaly_rate +# calc: $this +# units: % +# every: 30s +# warn: $this > (($status >= $WARNING) ? (1) : (5)) +# crit: $this > (($status == $CRITICAL) ? (5) : (100)) +# info: rolling 1min node level anomaly rate + # alert per dimension example # if anomaly rate is between 5-20% then warning (pick your own threshold that works best via tial and error). # if anomaly rate is above 20% then critical (pick your own threshold that works best via tial and error). @@ -33,4 +49,5 @@ # every: 30s # warn: $this > (($status >= $WARNING) ? (5) : (20)) # crit: $this > (($status == $CRITICAL) ? (20) : (100)) -# info: rolling 5min anomaly rate for system.cpu chart \ No newline at end of file +# info: rolling 5min anomaly rate for system.cpu chart + diff --git a/health/health.d/mysql.conf b/health/health.d/mysql.conf index 34452d983..3941c71cc 100644 --- a/health/health.d/mysql.conf +++ b/health/health.d/mysql.conf @@ -114,10 +114,10 @@ component: MySQL class: Utilization type: Database component: MySQL - lookup: max -2m absolute + lookup: max -2m at -1m unaligned units: nodes every: 10s - info: maximum galera cluster size in the last 2 minutes + info: maximum galera cluster size in the last 2 minutes starting one minute ago to: dba template: mysql_galera_cluster_size @@ -136,20 +136,29 @@ component: MySQL # galera node state - template: mysql_galera_cluster_state + template: mysql_galera_cluster_state_warn on: mysql.galera_cluster_state class: Errors type: Database component: MySQL - calc: $state + calc: $donor + $joined every: 10s - warn: $this == 2 OR $this == 3 - crit: $this == 0 OR $this == 1 OR $this >= 5 + warn: $this != nan AND $this != 0 delay: up 30s down 5m multiplier 1.5 max 1h - info: galera node state \ - (0: Undefined, 1: Joining, 2: Donor/Desynced, 3: Joined, 4: Synced, 5: Inconsistent) + info: galera node state is either Donor/Desynced or Joined. to: dba + template: mysql_galera_cluster_state_crit + on: mysql.galera_cluster_state + class: Errors + type: Database +component: MySQL + calc: $undefined + $joining + $error + every: 10s + crit: $this != nan AND $this != 0 + delay: up 30s down 5m multiplier 1.5 max 1h + info: galera node state is either Undefined or Joining or Error. + to: dba # galera node status @@ -158,11 +167,10 @@ component: MySQL class: Errors type: Database component: MySQL - calc: $wsrep_cluster_status + calc: $primary every: 10s - crit: $mysql_galera_cluster_state != nan AND $this != 0 + crit: $this != nan AND $this != 1 delay: up 30s down 5m multiplier 1.5 max 1h - info: galera node cluster component status \ - (-1: unknown, 0: primary/quorum present, 1: non-primary/quorum lost, 2: disconnected). \ - Any other value than primary indicates that the node is part of a nonoperational component. + info: galera node is part of a nonoperational component. \ + This occurs in cases of multiple membership changes that result in a loss of Quorum or in cases of split-brain situations. to: dba diff --git a/health/health.d/nvme.conf b/health/health.d/nvme.conf new file mode 100644 index 000000000..5f729d52b --- /dev/null +++ b/health/health.d/nvme.conf @@ -0,0 +1,15 @@ +# you can disable an alarm notification by setting the 'to' line to: silent + + template: nvme_device_critical_warnings_state + families: * + on: nvme.device_critical_warnings_state + class: Errors + type: System +component: Disk + lookup: max -30s unaligned + units: state + every: 10s + crit: $this != nan AND $this != 0 + delay: down 5m multiplier 1.5 max 2h + info: NVMe device $label:device has critical warnings + to: sysadmin diff --git a/health/health.d/pihole.conf b/health/health.d/pihole.conf index 2e5c1cbfd..ee6c57cc5 100644 --- a/health/health.d/pihole.conf +++ b/health/health.d/pihole.conf @@ -15,21 +15,6 @@ component: Pi-hole info: gravity.list (blocklist) file last update time to: sysadmin -# Gravity file check (gravity.list). - - template: pihole_blocklist_gravity_file - on: pihole.blocklist_last_update - class: Errors - type: Ad Filtering -component: Pi-hole - every: 10s - units: boolean - calc: $file_exists - crit: $this != 1 - delay: up 2m down 5m - info: gravity.list (blocklist) file existence state (0: not-exists, 1: exists) - to: sysadmin - # Pi-hole's ability to block unwanted domains. # Should be enabled. The whole point of Pi-hole! @@ -39,9 +24,9 @@ component: Pi-hole type: Ad Filtering component: Pi-hole every: 10s - units: boolean - calc: $enabled - warn: $this != 1 + units: status + calc: $disabled + warn: $this != nan AND $this == 1 delay: up 2m down 5m - info: unwanted domains blocking status (0: disabled, 1: enabled) + info: unwanted domains blocking is disabled to: sysadmin diff --git a/health/health.d/ping.conf b/health/health.d/ping.conf new file mode 100644 index 000000000..cbe7c30c9 --- /dev/null +++ b/health/health.d/ping.conf @@ -0,0 +1,50 @@ +# you can disable an alarm notification by setting the 'to' line to: silent + + template: ping_host_reachable + families: * + on: ping.host_packet_loss + class: Errors + type: Other +component: Network + lookup: average -30s unaligned of loss + calc: $this != nan AND $this < 100 + units: up/down + every: 10s + crit: $this == 0 + delay: down 30m multiplier 1.5 max 2h + info: network host $label:host reachability status + to: sysadmin + + template: ping_packet_loss + families: * + on: ping.host_packet_loss + class: Errors + type: Other +component: Network + lookup: average -10m unaligned of loss + green: 5 + red: 10 + units: % + every: 10s + warn: $this > $green + crit: $this > $red + delay: down 30m multiplier 1.5 max 2h + info: packet loss percentage to the network host $label:host over the last 10 minutes + to: sysadmin + + template: ping_host_latency + families: * + on: ping.host_rtt + class: Latency + type: Other +component: Network + lookup: average -10s unaligned of avg + units: ms + every: 10s + green: 500 + red: 1000 + warn: $this > $green OR $max > $red + crit: $this > $red + delay: down 30m multiplier 1.5 max 2h + info: average latency to the network host $label:host over the last 10 seconds + to: sysadmin diff --git a/health/health.d/postgres.conf b/health/health.d/postgres.conf new file mode 100644 index 000000000..66d034cfe --- /dev/null +++ b/health/health.d/postgres.conf @@ -0,0 +1,214 @@ +# you can disable an alarm notification by setting the 'to' line to: silent + + template: postgres_total_connection_utilization + on: postgres.connections_utilization + class: Utilization + type: Database +component: PostgreSQL + hosts: * + lookup: average -1m unaligned of used + units: % + every: 1m + warn: $this > (($status >= $WARNING) ? (70) : (80)) + crit: $this > (($status == $CRITICAL) ? (80) : (90)) + delay: down 15m multiplier 1.5 max 1h + info: average total connection utilization over the last minute + to: dba + + template: postgres_acquired_locks_utilization + on: postgres.locks_utilization + class: Utilization + type: Database +component: PostgreSQL + hosts: * + lookup: average -1m unaligned of used + units: % + every: 1m + warn: $this > (($status >= $WARNING) ? (15) : (20)) + delay: down 15m multiplier 1.5 max 1h + info: average acquired locks utilization over the last minute + to: dba + + template: postgres_txid_exhaustion_perc + on: postgres.txid_exhaustion_perc + class: Utilization + type: Database +component: PostgreSQL + hosts: * + calc: $txid_exhaustion + units: % + every: 1m + warn: $this > 90 + delay: down 15m multiplier 1.5 max 1h + info: percent towards TXID wraparound + to: dba + +# Database alarms + + template: postgres_db_cache_io_ratio + on: postgres.db_cache_io_ratio + class: Workload + type: Database +component: PostgreSQL + hosts: * + lookup: average -1m unaligned of miss + calc: 100 - $this + units: % + every: 1m + warn: $this < (($status >= $WARNING) ? (70) : (60)) + crit: $this < (($status == $CRITICAL) ? (60) : (50)) + delay: down 15m multiplier 1.5 max 1h + info: average cache hit ratio in db $label:database over the last minute + to: dba + + template: postgres_db_transactions_rollback_ratio + on: postgres.db_transactions_ratio + class: Workload + type: Database +component: PostgreSQL + hosts: * + lookup: average -5m unaligned of rollback + units: % + every: 1m + warn: $this > (($status >= $WARNING) ? (0) : (2)) + delay: down 15m multiplier 1.5 max 1h + info: average aborted transactions percentage in db $label:database over the last five minutes + to: dba + + template: postgres_db_deadlocks_rate + on: postgres.db_deadlocks_rate + class: Errors + type: Database +component: PostgreSQL + hosts: * + lookup: sum -1m unaligned of deadlocks + units: deadlocks + every: 1m + warn: $this > (($status >= $WARNING) ? (0) : (10)) + delay: down 15m multiplier 1.5 max 1h + info: number of deadlocks detected in db $label:database in the last minute + to: dba + +# Table alarms + + template: postgres_table_cache_io_ratio + on: postgres.table_cache_io_ratio + class: Workload + type: Database +component: PostgreSQL + hosts: * + lookup: average -1m unaligned of miss + calc: 100 - $this + units: % + every: 1m + warn: $this < (($status >= $WARNING) ? (70) : (60)) + crit: $this < (($status == $CRITICAL) ? (60) : (50)) + delay: down 15m multiplier 1.5 max 1h + info: average cache hit ratio in db $label:database table $label:table over the last minute + to: dba + + template: postgres_table_index_cache_io_ratio + on: postgres.table_index_cache_io_ratio + class: Workload + type: Database +component: PostgreSQL + hosts: * + lookup: average -1m unaligned of miss + calc: 100 - $this + units: % + every: 1m + warn: $this < (($status >= $WARNING) ? (70) : (60)) + crit: $this < (($status == $CRITICAL) ? (60) : (50)) + delay: down 15m multiplier 1.5 max 1h + info: average index cache hit ratio in db $label:database table $label:table over the last minute + to: dba + + template: postgres_table_toast_cache_io_ratio + on: postgres.table_toast_cache_io_ratio + class: Workload + type: Database +component: PostgreSQL + hosts: * + lookup: average -1m unaligned of miss + calc: 100 - $this + units: % + every: 1m + warn: $this < (($status >= $WARNING) ? (70) : (60)) + crit: $this < (($status == $CRITICAL) ? (60) : (50)) + delay: down 15m multiplier 1.5 max 1h + info: average TOAST hit ratio in db $label:database table $label:table over the last minute + to: dba + + template: postgres_table_toast_index_cache_io_ratio + on: postgres.table_toast_index_cache_io_ratio + class: Workload + type: Database +component: PostgreSQL + hosts: * + lookup: average -1m unaligned of miss + calc: 100 - $this + units: % + every: 1m + warn: $this < (($status >= $WARNING) ? (70) : (60)) + crit: $this < (($status == $CRITICAL) ? (60) : (50)) + delay: down 15m multiplier 1.5 max 1h + info: average index TOAST hit ratio in db $label:database table $label:table over the last minute + to: dba + + template: postgres_table_bloat_size_perc + on: postgres.table_bloat_size_perc + class: Errors + type: Database +component: PostgreSQL + hosts: * + calc: $bloat + units: % + every: 1m + warn: $this > (($status >= $WARNING) ? (60) : (70)) + crit: $this > (($status == $CRITICAL) ? (70) : (80)) + delay: down 15m multiplier 1.5 max 1h + info: bloat size percentage in db $label:database table $label:table + to: dba + + template: postgres_table_last_autovacuum_time + on: postgres.table_autovacuum_since_time + class: Errors + type: Database +component: PostgreSQL + hosts: !* + calc: $time + units: seconds + every: 1m + warn: $this != nan AND $this > (60 * 60 * 24 * 7) + info: time elapsed since db $label:database table $label:table was vacuumed by the autovacuum daemon + to: dba + + template: postgres_table_last_autoanalyze_time + on: postgres.table_autoanalyze_since_time + class: Errors + type: Database +component: PostgreSQL + hosts: !* + calc: $time + units: seconds + every: 1m + warn: $this != nan AND $this > (60 * 60 * 24 * 7) + info: time elapsed since db $label:database table $label:table was analyzed by the autovacuum daemon + to: dba + +# Index alarms + + template: postgres_index_bloat_size_perc + on: postgres.index_bloat_size_perc + class: Errors + type: Database +component: PostgreSQL + hosts: * + calc: $bloat + units: % + every: 1m + warn: $this > (($status >= $WARNING) ? (60) : (70)) + crit: $this > (($status == $CRITICAL) ? (70) : (80)) + delay: down 15m multiplier 1.5 max 1h + info: bloat size percentage in db $label:database table $label:table index $label:index + to: dba diff --git a/health/health.d/python.d.plugin.conf b/health/health.d/python.d.plugin.conf index e3b3d11cf..0e81a482f 100644 --- a/health/health.d/python.d.plugin.conf +++ b/health/health.d/python.d.plugin.conf @@ -3,7 +3,7 @@ template: python.d_job_last_collected_secs on: netdata.pythond_runtime - class: Error + class: Errors type: Netdata component: python.d.plugin module: !* * diff --git a/health/health.d/redis.conf b/health/health.d/redis.conf index cad5230c5..34d00b5df 100644 --- a/health/health.d/redis.conf +++ b/health/health.d/redis.conf @@ -1,3 +1,18 @@ +# you can disable an alarm notification by setting the 'to' line to: silent + + template: redis_connections_rejected + families: * + on: redis.connections + class: Errors + type: KV Storage +component: Redis + lookup: sum -1m unaligned of rejected + every: 10s + units: connections + warn: $this > 0 + info: connections rejected because of maxclients limit in the last minute + delay: down 5m multiplier 1.5 max 1h + to: dba template: redis_bgsave_broken families: * @@ -26,3 +41,17 @@ component: Redis info: duration of the on-going RDB save operation delay: down 5m multiplier 1.5 max 1h to: dba + + template: redis_master_link_down + families: * + on: redis.master_link_down_since_time + class: Errors + type: KV Storage +component: Redis + every: 10s + calc: $time + units: seconds + crit: $this != nan AND $this > 0 + info: time elapsed since the link between master and slave is down + delay: down 5m multiplier 1.5 max 1h + to: dba diff --git a/health/health.d/systemdunits.conf b/health/health.d/systemdunits.conf index 38213a8db..531d62fac 100644 --- a/health/health.d/systemdunits.conf +++ b/health/health.d/systemdunits.conf @@ -1,142 +1,141 @@ -## Check if the are any systemd units in the failed state (crashed). -## States: 1 - active, 2 - inactive, 3 - activating, 4 - deactivating, 5 - failed. +# you can disable an alarm notification by setting the 'to' line to: silent ## Service units - template: systemd_service_units_state - on: systemd.service_units_state + template: systemd_service_unit_failed_state + on: systemd.service_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more systemd service units are in the failed state + info: systemd service unit in the failed state to: sysadmin ## Socket units - template: systemd_socket_units_state + template: systemd_socket_unit_failed_state on: systemd.socket_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more systemd socket units are in the failed state + info: systemd socket unit in the failed state to: sysadmin ## Target units - template: systemd_target_units_state + template: systemd_target_unit_failed_state on: systemd.target_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more systemd target units are in the failed state + info: systemd target unit in the failed state to: sysadmin ## Path units - template: systemd_path_units_state + template: systemd_path_unit_failed_state on: systemd.path_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more systemd path units are in the failed state + info: systemd path unit in the failed state to: sysadmin ## Device units - template: systemd_device_units_state + template: systemd_device_unit_failed_state on: systemd.device_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more the systemd device units are in the failed state + info: systemd device unit in the failed state to: sysadmin ## Mount units - template: systemd_mount_units_state + template: systemd_mount_unit_failed_state on: systemd.mount_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more the systemd mount units are in the failed state + info: systemd mount units in the failed state to: sysadmin ## Automount units - template: systemd_automount_units_state + template: systemd_automount_unit_failed_state on: systemd.automount_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more systemd automount units are in the failed state + info: systemd automount unit in the failed state to: sysadmin ## Swap units - template: systemd_swap_units_state + template: systemd_swap_unit_failed_state on: systemd.swap_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more systemd swap units are in the failed state + info: systemd swap units in the failed state to: sysadmin ## Scope units - template: systemd_scope_units_state + template: systemd_scope_unit_failed_state on: systemd.scope_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more systemd scope units are in the failed state + info: systemd scope units in the failed state to: sysadmin ## Slice units - template: systemd_slice_units_state + template: systemd_slice_unit_failed_state on: systemd.slice_unit_state class: Errors type: Linux component: Systemd units - lookup: max -1s min2max - units: ok/failed + calc: $failed + units: state every: 10s - warn: $this != nan AND $this == 5 + warn: $this != nan AND $this == 1 delay: down 5m multiplier 1.5 max 1h - info: one or more systemd slice units are in the failed state + info: systemd slice units in the failed state to: sysadmin diff --git a/health/health.d/tcp_resets.conf b/health/health.d/tcp_resets.conf index 35cb6366c..ff116db64 100644 --- a/health/health.d/tcp_resets.conf +++ b/health/health.d/tcp_resets.conf @@ -26,7 +26,7 @@ component: Network lookup: average -10s unaligned absolute of OutRsts units: tcp resets/s every: 10s - warn: $this > ((($1m_ipv4_tcp_resets_sent < 5)?(5):($1m_ipv4_tcp_resets_sent)) * (($status >= $WARNING) ? (1) : (10))) + warn: $netdata.uptime.uptime > (1 * 60) AND $this > ((($1m_ipv4_tcp_resets_sent < 5)?(5):($1m_ipv4_tcp_resets_sent)) * (($status >= $WARNING) ? (1) : (10))) delay: up 20s down 60m multiplier 1.2 max 2h options: no-clear-notification info: average number of sent TCP RESETS over the last 10 seconds. \ @@ -60,7 +60,7 @@ component: Network lookup: average -10s unaligned absolute of AttemptFails units: tcp resets/s every: 10s - warn: $this > ((($1m_ipv4_tcp_resets_received < 5)?(5):($1m_ipv4_tcp_resets_received)) * (($status >= $WARNING) ? (1) : (10))) + warn: $netdata.uptime.uptime > (1 * 60) AND $this > ((($1m_ipv4_tcp_resets_received < 5)?(5):($1m_ipv4_tcp_resets_received)) * (($status >= $WARNING) ? (1) : (10))) delay: up 20s down 60m multiplier 1.2 max 2h options: no-clear-notification info: average number of received TCP RESETS over the last 10 seconds. \ diff --git a/health/health.d/timex.conf b/health/health.d/timex.conf index 23c18ba10..2e9b1a3cf 100644 --- a/health/health.d/timex.conf +++ b/health/health.d/timex.conf @@ -5,7 +5,7 @@ alarm: system_clock_sync_state on: system.clock_sync_state os: linux - class: Error + class: Errors type: System component: Clock calc: $state diff --git a/health/health.h b/health/health.h index 3e77c12a7..15d8326ee 100644 --- a/health/health.h +++ b/health/health.h @@ -14,6 +14,7 @@ extern unsigned int default_health_enabled; #define HEALTH_ENTRY_FLAG_SILENCED 0x00000010 #define HEALTH_ENTRY_RUN_ONCE 0x00000020 #define HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS 0x00000040 +#define HEALTH_ENTRY_FLAG_IS_REPEATING 0x00000080 #define HEALTH_ENTRY_FLAG_SAVED 0x10000000 #define HEALTH_ENTRY_FLAG_ACLK_QUEUED 0x20000000 @@ -31,65 +32,72 @@ extern unsigned int default_health_enabled; extern char *silencers_filename; -extern void health_init(void); +void health_init(void); -extern void health_reload(void); +void health_reload(void); -extern int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, NETDATA_DOUBLE *result); -extern void health_aggregate_alarms(RRDHOST *host, BUFFER *wb, BUFFER* context, RRDCALC_STATUS status); -extern void health_alarms2json(RRDHOST *host, BUFFER *wb, int all); -extern void health_alarms_values2json(RRDHOST *host, BUFFER *wb, int all); -extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after, char *chart); +void health_aggregate_alarms(RRDHOST *host, BUFFER *wb, BUFFER* context, RRDCALC_STATUS status); +void health_alarms2json(RRDHOST *host, BUFFER *wb, int all); +void health_alarms_values2json(RRDHOST *host, BUFFER *wb, int all); +void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after, char *chart); void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf); void health_api_v1_chart_custom_variables2json(RRDSET *st, BUFFER *buf); -extern int health_alarm_log_open(RRDHOST *host); -extern void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae); -extern void health_alarm_log_load(RRDHOST *host); - -extern ALARM_ENTRY* health_create_alarm_entry( - RRDHOST *host, - uint32_t alarm_id, - uint32_t alarm_event_id, - uuid_t config_hash_id, - time_t when, - const char *name, - const char *chart, - const char *chart_context, - const char *family, - const char *classification, - const char *component, - const char *type, - const char *exec, - const char *recipient, - time_t duration, - NETDATA_DOUBLE old_value, - NETDATA_DOUBLE new_value, - RRDCALC_STATUS old_status, - RRDCALC_STATUS new_status, - const char *source, - const char *units, - const char *info, - int delay, - uint32_t flags); - -extern void health_alarm_log(RRDHOST *host, ALARM_ENTRY *ae); - -extern void health_readdir(RRDHOST *host, const char *user_path, const char *stock_path, const char *subpath); -extern char *health_user_config_dir(void); -extern char *health_stock_config_dir(void); -extern void health_alarm_log_free(RRDHOST *host); - -extern void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae); - -extern void *health_cmdapi_thread(void *ptr); - -extern void health_label_log_save(RRDHOST *host); - -extern char *health_edit_command_from_source(const char *source); -extern void sql_refresh_hashes(void); - -extern SIMPLE_PATTERN *health_pattern_from_foreach(char *s); +int health_alarm_log_open(RRDHOST *host); +void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae); +void health_alarm_log_load(RRDHOST *host); + +void health_thread_spawn(RRDHOST *host); +void health_thread_stop(RRDHOST *host); + +ALARM_ENTRY* health_create_alarm_entry( + RRDHOST *host, + uint32_t alarm_id, + uint32_t alarm_event_id, + const uuid_t config_hash_id, + time_t when, + STRING *name, + STRING *chart, + STRING *chart_context, + STRING *family, + STRING *classification, + STRING *component, + STRING *type, + STRING *exec, + STRING *recipient, + time_t duration, + NETDATA_DOUBLE old_value, + NETDATA_DOUBLE new_value, + RRDCALC_STATUS old_status, + RRDCALC_STATUS new_status, + STRING *source, + STRING *units, + STRING *info, + int delay, + uint32_t flags); + +void health_alarm_log_add_entry(RRDHOST *host, ALARM_ENTRY *ae); + +struct health_state { + RRDHOST *host; + netdata_thread_t thread; +}; + +void health_readdir(RRDHOST *host, const char *user_path, const char *stock_path, const char *subpath); +char *health_user_config_dir(void); +char *health_stock_config_dir(void); +void health_alarm_log_free(RRDHOST *host); + +void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae); + +void *health_cmdapi_thread(void *ptr); + +void health_label_log_save(RRDHOST *host); + +char *health_edit_command_from_source(const char *source); +void sql_refresh_hashes(void); + +void health_add_host_labels(void); #endif //NETDATA_HEALTH_H diff --git a/health/health_config.c b/health/health_config.c index e1dd32ab1..f9decfad5 100644 --- a/health/health_config.c +++ b/health/health_config.c @@ -33,148 +33,6 @@ #define HEALTH_HOST_LABEL_KEY "host labels" #define HEALTH_FOREACH_KEY "foreach" -static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) { - if(!rc->chart) { - error("Health configuration for alarm '%s' does not have a chart", rc->name); - return 0; - } - - if(!rc->update_every) { - error("Health configuration for alarm '%s.%s' has no frequency (parameter 'every'). Ignoring it.", rc->chart?rc->chart:"NOCHART", rc->name); - return 0; - } - - if(!RRDCALC_HAS_DB_LOOKUP(rc) && !rc->calculation && !rc->warning && !rc->critical) { - error("Health configuration for alarm '%s.%s' is useless (no db lookup, no calculation, no warning and no critical expressions)", rc->chart?rc->chart:"NOCHART", rc->name); - return 0; - } - - if (rrdcalc_exists(host, rc->chart, rc->name, rc->hash_chart, rc->hash)) - return 0; - - rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->name, &rc->next_event_id); - - debug(D_HEALTH, "Health configuration adding alarm '%s.%s' (%u): exec '%s', recipient '%s', green " NETDATA_DOUBLE_FORMAT_AUTO - ", red " NETDATA_DOUBLE_FORMAT_AUTO - ", lookup: group %d, after %d, before %d, options %u, dimensions '%s', for each dimension '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f, warn_repeat_every %u, crit_repeat_every %u", - rc->chart?rc->chart:"NOCHART", - rc->name, - rc->id, - (rc->exec)?rc->exec:"DEFAULT", - (rc->recipient)?rc->recipient:"DEFAULT", - rc->green, - rc->red, - (int)rc->group, - rc->after, - rc->before, - rc->options, - (rc->dimensions)?rc->dimensions:"NONE", - (rc->foreachdim)?rc->foreachdim:"NONE", - rc->update_every, - (rc->calculation)?rc->calculation->parsed_as:"NONE", - (rc->warning)?rc->warning->parsed_as:"NONE", - (rc->critical)?rc->critical->parsed_as:"NONE", - rc->source, - rc->delay_up_duration, - rc->delay_down_duration, - rc->delay_max_duration, - rc->delay_multiplier, - rc->warn_repeat_every, - rc->crit_repeat_every - ); - - rrdcalc_add_to_host(host, rc); - - return 1; -} - -static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) { - if(unlikely(!rt->context)) { - error("Health configuration for template '%s' does not have a context", rt->name); - return 0; - } - - if(unlikely(!rt->update_every)) { - error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rt->name); - return 0; - } - - if(unlikely(!RRDCALCTEMPLATE_HAS_DB_LOOKUP(rt) && !rt->calculation && !rt->warning && !rt->critical)) { - error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rt->name); - return 0; - } - - RRDCALCTEMPLATE *t, *last = NULL; - if(!rt->foreachdim) { - for (t = host->templates; t ; last = t, t = t->next) { - if(unlikely(t->hash_name == rt->hash_name - && !strcmp(t->name, rt->name) - && !strcmp(t->family_match?t->family_match:"*", rt->family_match?rt->family_match:"*") - )) { - info("Health configuration template '%s' already exists for host '%s'.", rt->name, host->hostname); - return 0; - } - } - - if(likely(last)) { - last->next = rt; - } - else { - rt->next = host->templates; - host->templates = rt; - } - } else { - for (t = host->alarms_template_with_foreach; t ; last = t, t = t->next) { - if(unlikely(t->hash_name == rt->hash_name - && !strcmp(t->name, rt->name) - && !strcmp(t->family_match?t->family_match:"*", rt->family_match?rt->family_match:"*") - )) { - info("Health configuration template '%s' already exists for host '%s'.", rt->name, host->hostname); - return 0; - } - } - - if(likely(last)) { - last->next = rt; - } - else { - rt->next = host->alarms_template_with_foreach; - host->alarms_template_with_foreach = rt; - } - } - - debug(D_HEALTH, "Health configuration adding template '%s': context '%s', exec '%s', recipient '%s', green " NETDATA_DOUBLE_FORMAT_AUTO - ", red " NETDATA_DOUBLE_FORMAT_AUTO - ", lookup: group %d, after %d, before %d, options %u, dimensions '%s', for each dimension '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f, warn_repeat_every %u, crit_repeat_every %u", - rt->name, - (rt->context)?rt->context:"NONE", - (rt->exec)?rt->exec:"DEFAULT", - (rt->recipient)?rt->recipient:"DEFAULT", - rt->green, - rt->red, - (int)rt->group, - rt->after, - rt->before, - rt->options, - (rt->dimensions)?rt->dimensions:"NONE", - (rt->foreachdim)?rt->foreachdim:"NONE", - rt->update_every, - (rt->calculation)?rt->calculation->parsed_as:"NONE", - (rt->warning)?rt->warning->parsed_as:"NONE", - (rt->critical)?rt->critical->parsed_as:"NONE", - rt->source, - rt->delay_up_duration, - rt->delay_down_duration, - rt->delay_max_duration, - rt->delay_multiplier, - rt->warn_repeat_every, - rt->crit_repeat_every - ); - - - return 1; -} - static inline int health_parse_delay( size_t line, const char *filename, char *string, int *delay_up_duration, @@ -275,7 +133,7 @@ static inline uint32_t health_parse_options(const char *s) { buf[count] = '\0'; if(!strcasecmp(buf, "no-clear-notification") || !strcasecmp(buf, "no-clear")) - options |= RRDCALC_FLAG_NO_CLEAR_NOTIFICATION; + options |= RRDCALC_OPTION_NO_CLEAR_NOTIFICATION; else error("Ignoring unknown alarm option '%s'", buf); } @@ -334,13 +192,21 @@ static inline int health_parse_repeat( * * @param s the string that will be used to create the simple pattern. */ -SIMPLE_PATTERN *health_pattern_from_foreach(char *s) { + +static void dimension_remove_pipe_comma(char *str) { + while(*str) { + if(*str == '|' || *str == ',') *str = ' '; + str++; + } +} + +static SIMPLE_PATTERN *health_pattern_from_foreach(const char *s) { char *convert= strdupz(s); SIMPLE_PATTERN *val = NULL; + if(convert) { dimension_remove_pipe_comma(convert); val = simple_pattern_create(convert, NULL, SIMPLE_PATTERN_EXACT); - freez(convert); } @@ -350,18 +216,18 @@ SIMPLE_PATTERN *health_pattern_from_foreach(char *s) { static inline int health_parse_db_lookup( size_t line, const char *filename, char *string, RRDR_GROUPING *group_method, int *after, int *before, int *every, - uint32_t *options, char **dimensions, char **foreachdim + RRDCALC_OPTIONS *options, STRING **dimensions, STRING **foreachdim ) { debug(D_HEALTH, "Health configuration parsing database lookup %zu@%s: %s", line, filename, string); - if(*dimensions) freez(*dimensions); - if(*foreachdim) freez(*foreachdim); + if(*dimensions) string_freez(*dimensions); + if(*foreachdim) string_freez(*foreachdim); *dimensions = NULL; *foreachdim = NULL; *after = 0; *before = 0; *every = 0; - *options = 0; + *options = (*options) & RRDCALC_ALL_OPTIONS_EXCLUDING_THE_RRDR_ONES; // preserve rrdcalc options char *s = string, *key; @@ -453,7 +319,7 @@ static inline int health_parse_db_lookup( if(find) { *find = '\0'; } - *dimensions = strdupz(s); + *dimensions = string_strdupz(s); } if(!find) { @@ -462,7 +328,7 @@ static inline int health_parse_db_lookup( s = ++find; } else if(!strcasecmp(key, HEALTH_FOREACH_KEY )) { - *foreachdim = strdupz(s); + *foreachdim = string_strdupz(s); break; } else { @@ -474,10 +340,10 @@ static inline int health_parse_db_lookup( return 1; } -static inline char *health_source_file(size_t line, const char *file) { +static inline STRING *health_source_file(size_t line, const char *file) { char buffer[FILENAME_MAX + 1]; snprintfz(buffer, FILENAME_MAX, "%zu@%s", line, file); - return strdupz(buffer); + return string_strdupz(buffer); } char *health_edit_command_from_source(const char *source) @@ -496,7 +362,7 @@ char *health_edit_command_from_source(const char *source) netdata_configured_user_config_dir, file_no_path + 1, temp, - localhost->registry_hostname); + rrdhost_registry_hostname(localhost)); } else buffer[0] = '\0'; @@ -513,35 +379,35 @@ static inline void strip_quotes(char *s) { static inline void alert_config_free(struct alert_config *cfg) { - freez(cfg->alarm); - freez(cfg->template_key); - freez(cfg->os); - freez(cfg->host); - freez(cfg->on); - freez(cfg->families); - freez(cfg->plugin); - freez(cfg->module); - freez(cfg->charts); - freez(cfg->lookup); - freez(cfg->calc); - freez(cfg->warn); - freez(cfg->crit); - freez(cfg->every); - freez(cfg->green); - freez(cfg->red); - freez(cfg->exec); - freez(cfg->to); - freez(cfg->units); - freez(cfg->info); - freez(cfg->classification); - freez(cfg->component); - freez(cfg->type); - freez(cfg->delay); - freez(cfg->options); - freez(cfg->repeat); - freez(cfg->host_labels); - freez(cfg->p_db_lookup_dimensions); - freez(cfg->p_db_lookup_method); + string_freez(cfg->alarm); + string_freez(cfg->template_key); + string_freez(cfg->os); + string_freez(cfg->host); + string_freez(cfg->on); + string_freez(cfg->families); + string_freez(cfg->plugin); + string_freez(cfg->module); + string_freez(cfg->charts); + string_freez(cfg->lookup); + string_freez(cfg->calc); + string_freez(cfg->warn); + string_freez(cfg->crit); + string_freez(cfg->every); + string_freez(cfg->green); + string_freez(cfg->red); + string_freez(cfg->exec); + string_freez(cfg->to); + string_freez(cfg->units); + string_freez(cfg->info); + string_freez(cfg->classification); + string_freez(cfg->component); + string_freez(cfg->type); + string_freez(cfg->delay); + string_freez(cfg->options); + string_freez(cfg->repeat); + string_freez(cfg->host_labels); + string_freez(cfg->p_db_lookup_dimensions); + string_freez(cfg->p_db_lookup_method); freez(cfg); } @@ -670,23 +536,35 @@ static int health_readfile(const char *filename, void *data) { if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) { if(rc) { - if(!alert_hash_and_store_config(rc->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this || !rrdcalc_add_alarm_from_config(host, rc)) { - rrdcalc_free(rc); - } + if(!alert_hash_and_store_config(rc->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this) + rrdcalc_free_unused_rrdcalc_loaded_from_config(rc); + else + rrdcalc_add_from_config(host, rc); + // health_add_alarms_loop(host, rc, ignore_this) ; } if(rt) { - if (!alert_hash_and_store_config(rt->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this || !rrdcalctemplate_add_template_from_config(host, rt)) { - rrdcalctemplate_free(rt); - } + if(!alert_hash_and_store_config(rt->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this) + rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(rt); + else + rrdcalctemplate_add_from_config(host, rt); + rt = NULL; } rc = callocz(1, sizeof(RRDCALC)); rc->next_event_id = 1; - rc->name = strdupz(value); - rc->hash = simple_hash(rc->name); + + { + char *tmp = strdupz(value); + if(rrdvar_fix_name(tmp)) + error("Health configuration renamed alarm '%s' to '%s'", value, tmp); + + rc->name = string_strdupz(tmp); + freez(tmp); + } + rc->source = health_source_file(line, filename); rc->green = NAN; rc->red = NAN; @@ -700,58 +578,62 @@ static int health_readfile(const char *filename, void *data) { alert_config_free(alert_cfg); alert_cfg = callocz(1, sizeof(struct alert_config)); - if(rrdvar_fix_name(rc->name)) - error("Health configuration renamed alarm '%s' to '%s'", value, rc->name); - - alert_cfg->alarm = strdupz(rc->name); + alert_cfg->alarm = string_dup(rc->name); ignore_this = 0; } else if(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) { if(rc) { // health_add_alarms_loop(host, rc, ignore_this) ; - if(!alert_hash_and_store_config(rc->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this || !rrdcalc_add_alarm_from_config(host, rc)) { - rrdcalc_free(rc); - } + if(!alert_hash_and_store_config(rc->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this) + rrdcalc_free_unused_rrdcalc_loaded_from_config(rc); + else + rrdcalc_add_from_config(host, rc); rc = NULL; } if(rt) { - if(!alert_hash_and_store_config(rt->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this || !rrdcalctemplate_add_template_from_config(host, rt)) { - rrdcalctemplate_free(rt); - } + if(!alert_hash_and_store_config(rt->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this) + rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(rt); + else + rrdcalctemplate_add_from_config(host, rt); } rt = callocz(1, sizeof(RRDCALCTEMPLATE)); - rt->name = strdupz(value); - rt->hash_name = simple_hash(rt->name); + + { + char *tmp = strdupz(value); + if(rrdvar_fix_name(tmp)) + error("Health configuration renamed template '%s' to '%s'", value, tmp); + + rt->name = string_strdupz(tmp); + freez(tmp); + } + rt->source = health_source_file(line, filename); rt->green = NAN; rt->red = NAN; - rt->delay_multiplier = 1.0; + rt->delay_multiplier = (float)1.0; rt->warn_repeat_every = host->health_default_warn_repeat_every; rt->crit_repeat_every = host->health_default_crit_repeat_every; if (alert_cfg) alert_config_free(alert_cfg); alert_cfg = callocz(1, sizeof(struct alert_config)); - if(rrdvar_fix_name(rt->name)) - error("Health configuration renamed template '%s' to '%s'", value, rt->name); - - alert_cfg->template_key = strdupz(rt->name); + alert_cfg->template_key = string_dup(rt->name); ignore_this = 0; } else if(hash == hash_os && !strcasecmp(key, HEALTH_OS_KEY)) { char *os_match = value; - if (alert_cfg) alert_cfg->os = strdupz(value); + if (alert_cfg) alert_cfg->os = string_strdupz(value); SIMPLE_PATTERN *os_pattern = simple_pattern_create(os_match, NULL, SIMPLE_PATTERN_EXACT); - if(!simple_pattern_matches(os_pattern, host->os)) { + if(!simple_pattern_matches(os_pattern, rrdhost_os(host))) { if(rc) - debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s: host O/S does not match '%s'", host->hostname, rc->name, line, filename, os_match); + debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s: host O/S does not match '%s'", rrdhost_hostname(host), rrdcalc_name(rc), line, filename, os_match); if(rt) - debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s: host O/S does not match '%s'", host->hostname, rt->name, line, filename, os_match); + debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s: host O/S does not match '%s'", rrdhost_hostname(host), rrdcalctemplate_name(rt), line, filename, os_match); ignore_this = 1; } @@ -760,15 +642,15 @@ static int health_readfile(const char *filename, void *data) { } else if(hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) { char *host_match = value; - if (alert_cfg) alert_cfg->host = strdupz(value); + if (alert_cfg) alert_cfg->host = string_strdupz(value); SIMPLE_PATTERN *host_pattern = simple_pattern_create(host_match, NULL, SIMPLE_PATTERN_EXACT); - if(!simple_pattern_matches(host_pattern, host->hostname)) { + if(!simple_pattern_matches(host_pattern, rrdhost_hostname(host))) { if(rc) - debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s: hostname does not match '%s'", host->hostname, rc->name, line, filename, host_match); + debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s: hostname does not match '%s'", rrdhost_hostname(host), rrdcalc_name(rc), line, filename, host_match); if(rt) - debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s: hostname does not match '%s'", host->hostname, rt->name, line, filename, host_match); + debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s: hostname does not match '%s'", rrdhost_hostname(host), rrdcalctemplate_name(rt), line, filename, host_match); ignore_this = 1; } @@ -777,65 +659,68 @@ static int health_readfile(const char *filename, void *data) { } else if(rc) { if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) { - alert_cfg->on = strdupz(value); + alert_cfg->on = string_strdupz(value); if(rc->chart) { - if(strcmp(rc->chart, value) != 0) + if(strcmp(rrdcalc_chart_name(rc), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rc->name, key, rc->chart, value, value); + line, filename, rrdcalc_name(rc), key, rrdcalc_chart_name(rc), value, value); - freez(rc->chart); + string_freez(rc->chart); } - rc->chart = strdupz(value); - rc->hash_chart = simple_hash(rc->chart); + rc->chart = string_strdupz(value); } else if(hash == hash_class && !strcasecmp(key, HEALTH_CLASS_KEY)) { - alert_cfg->classification = strdupz(value); + strip_quotes(value); + + alert_cfg->classification = string_strdupz(value); if(rc->classification) { - if(strcmp(rc->classification, value) != 0) + if(strcmp(rrdcalc_classification(rc), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rc->name, key, rc->classification, value, value); + line, filename, rrdcalc_name(rc), key, rrdcalc_classification(rc), value, value); - freez(rc->classification); + string_freez(rc->classification); } - rc->classification = strdupz(value); - strip_quotes(rc->classification); + rc->classification = string_strdupz(value); } else if(hash == hash_component && !strcasecmp(key, HEALTH_COMPONENT_KEY)) { - alert_cfg->component = strdupz(value); + strip_quotes(value); + + alert_cfg->component = string_strdupz(value); if(rc->component) { - if(strcmp(rc->component, value) != 0) + if(strcmp(rrdcalc_component(rc), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rc->name, key, rc->component, value, value); + line, filename, rrdcalc_name(rc), key, rrdcalc_component(rc), value, value); - freez(rc->component); + string_freez(rc->component); } - rc->component = strdupz(value); - strip_quotes(rc->component); + rc->component = string_strdupz(value); } else if(hash == hash_type && !strcasecmp(key, HEALTH_TYPE_KEY)) { - alert_cfg->type = strdupz(value); + strip_quotes(value); + + alert_cfg->type = string_strdupz(value); if(rc->type) { - if(strcmp(rc->type, value) != 0) + if(strcmp(rrdcalc_type(rc), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rc->name, key, rc->type, value, value); + line, filename, rrdcalc_name(rc), key, rrdcalc_type(rc), value, value); - freez(rc->type); + string_freez(rc->type); } - rc->type = strdupz(value); - strip_quotes(rc->type); + rc->type = string_strdupz(value); } else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) { - alert_cfg->lookup = strdupz(value); + alert_cfg->lookup = string_strdupz(value); health_parse_db_lookup(line, filename, value, &rc->group, &rc->after, &rc->before, - &rc->update_every, &rc->options, &rc->dimensions, &rc->foreachdim); - if(rc->foreachdim) { - rc->spdim = health_pattern_from_foreach(rc->foreachdim); - } + &rc->update_every, &rc->options, &rc->dimensions, &rc->foreach_dimension); + + if(rc->foreach_dimension) + rc->foreach_dimension_pattern = health_pattern_from_foreach(rrdcalc_foreachdim(rc)); + if (rc->after) { if (rc->dimensions) - alert_cfg->p_db_lookup_dimensions = strdupz(rc->dimensions); + alert_cfg->p_db_lookup_dimensions = string_dup(rc->dimensions); if (rc->group) - alert_cfg->p_db_lookup_method = strdupz(group_method2string(rc->group)); + alert_cfg->p_db_lookup_method = string_strdupz(group_method2string(rc->group)); alert_cfg->p_db_lookup_options = rc->options; alert_cfg->p_db_lookup_after = rc->after; alert_cfg->p_db_lookup_before = rc->before; @@ -843,248 +728,261 @@ static int health_readfile(const char *filename, void *data) { } } else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) { - alert_cfg->every = strdupz(value); + alert_cfg->every = string_strdupz(value); if(!config_parse_duration(value, &rc->update_every)) error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.", - line, filename, rc->name, key, value); + line, filename, rrdcalc_name(rc), key, value); alert_cfg->p_update_every = rc->update_every; } else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) { - alert_cfg->green = strdupz(value); + alert_cfg->green = string_strdupz(value); char *e; rc->green = str2ndd(value, &e); if(e && *e) { error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.", - line, filename, rc->name, key, e); + line, filename, rrdcalc_name(rc), key, e); } } else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) { - alert_cfg->red = strdupz(value); + alert_cfg->red = string_strdupz(value); char *e; rc->red = str2ndd(value, &e); if(e && *e) { error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.", - line, filename, rc->name, key, e); + line, filename, rrdcalc_name(rc), key, e); } } else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) { - alert_cfg->calc = strdupz(value); + alert_cfg->calc = string_strdupz(value); const char *failed_at = NULL; int error = 0; rc->calculation = expression_parse(value, &failed_at, &error); if(!rc->calculation) { error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'", - line, filename, rc->name, key, value, expression_strerror(error), failed_at); + line, filename, rrdcalc_name(rc), key, value, expression_strerror(error), failed_at); } } else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) { - alert_cfg->warn = strdupz(value); + alert_cfg->warn = string_strdupz(value); const char *failed_at = NULL; int error = 0; rc->warning = expression_parse(value, &failed_at, &error); if(!rc->warning) { error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'", - line, filename, rc->name, key, value, expression_strerror(error), failed_at); + line, filename, rrdcalc_name(rc), key, value, expression_strerror(error), failed_at); } } else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) { - alert_cfg->crit = strdupz(value); + alert_cfg->crit = string_strdupz(value); const char *failed_at = NULL; int error = 0; rc->critical = expression_parse(value, &failed_at, &error); if(!rc->critical) { error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'", - line, filename, rc->name, key, value, expression_strerror(error), failed_at); + line, filename, rrdcalc_name(rc), key, value, expression_strerror(error), failed_at); } } else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) { - alert_cfg->exec = strdupz(value); + alert_cfg->exec = string_strdupz(value); if(rc->exec) { - if(strcmp(rc->exec, value) != 0) + if(strcmp(rrdcalc_exec(rc), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rc->name, key, rc->exec, value, value); + line, filename, rrdcalc_name(rc), key, rrdcalc_exec(rc), value, value); - freez(rc->exec); + string_freez(rc->exec); } - rc->exec = strdupz(value); + rc->exec = string_strdupz(value); } else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) { - alert_cfg->to = strdupz(value); + alert_cfg->to = string_strdupz(value); if(rc->recipient) { - if(strcmp(rc->recipient, value) != 0) + if(strcmp(rrdcalc_recipient(rc), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rc->name, key, rc->recipient, value, value); + line, filename, rrdcalc_name(rc), key, rrdcalc_recipient(rc), value, value); - freez(rc->recipient); + string_freez(rc->recipient); } - rc->recipient = strdupz(value); + rc->recipient = string_strdupz(value); } else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) { - alert_cfg->units = strdupz(value); + strip_quotes(value); + + alert_cfg->units = string_strdupz(value); if(rc->units) { - if(strcmp(rc->units, value) != 0) + if(strcmp(rrdcalc_units(rc), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rc->name, key, rc->units, value, value); + line, filename, rrdcalc_name(rc), key, rrdcalc_units(rc), value, value); - freez(rc->units); + string_freez(rc->units); } - rc->units = strdupz(value); - strip_quotes(rc->units); + rc->units = string_strdupz(value); } else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) { - alert_cfg->info = strdupz(value); + strip_quotes(value); + + alert_cfg->info = string_strdupz(value); if(rc->info) { - if(strcmp(rc->info, value) != 0) + if(strcmp(rrdcalc_info(rc), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rc->name, key, rc->info, value, value); + line, filename, rrdcalc_name(rc), key, rrdcalc_info(rc), value, value); - freez(rc->info); + string_freez(rc->info); + string_freez(rc->original_info); } - rc->info = strdupz(value); - strip_quotes(rc->info); + rc->info = string_strdupz(value); + rc->original_info = string_dup(rc->info); } else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) { - alert_cfg->delay = strdupz(value); + alert_cfg->delay = string_strdupz(value); health_parse_delay(line, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier); } else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) { - alert_cfg->options = strdupz(value); + alert_cfg->options = string_strdupz(value); rc->options |= health_parse_options(value); } else if(hash == hash_repeat && !strcasecmp(key, HEALTH_REPEAT_KEY)){ - alert_cfg->repeat = strdupz(value); + alert_cfg->repeat = string_strdupz(value); health_parse_repeat(line, filename, value, &rc->warn_repeat_every, &rc->crit_repeat_every); } else if(hash == hash_host_label && !strcasecmp(key, HEALTH_HOST_LABEL_KEY)) { - alert_cfg->host_labels = strdupz(value); + alert_cfg->host_labels = string_strdupz(value); if(rc->host_labels) { - if(strcmp(rc->host_labels, value) != 0) + if(strcmp(rrdcalc_host_labels(rc), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'.", - line, filename, rc->name, key, value, value); + line, filename, rrdcalc_name(rc), key, value, value); - freez(rc->host_labels); + string_freez(rc->host_labels); simple_pattern_free(rc->host_labels_pattern); } - rc->host_labels = simple_pattern_trim_around_equal(value); - rc->host_labels_pattern = simple_pattern_create(rc->host_labels, NULL, SIMPLE_PATTERN_EXACT); + { + char *tmp = simple_pattern_trim_around_equal(value); + rc->host_labels = string_strdupz(tmp); + freez(tmp); + } + rc->host_labels_pattern = simple_pattern_create(rrdcalc_host_labels(rc), NULL, SIMPLE_PATTERN_EXACT); } else if(hash == hash_plugin && !strcasecmp(key, HEALTH_PLUGIN_KEY)) { - alert_cfg->plugin = strdupz(value); - freez(rc->plugin_match); + alert_cfg->plugin = string_strdupz(value); + string_freez(rc->plugin_match); simple_pattern_free(rc->plugin_pattern); - rc->plugin_match = strdupz(value); - rc->plugin_pattern = simple_pattern_create(rc->plugin_match, NULL, SIMPLE_PATTERN_EXACT); + rc->plugin_match = string_strdupz(value); + rc->plugin_pattern = simple_pattern_create(rrdcalc_plugin_match(rc), NULL, SIMPLE_PATTERN_EXACT); } else if(hash == hash_module && !strcasecmp(key, HEALTH_MODULE_KEY)) { - alert_cfg->module = strdupz(value); - freez(rc->module_match); + alert_cfg->module = string_strdupz(value); + string_freez(rc->module_match); simple_pattern_free(rc->module_pattern); - rc->module_match = strdupz(value); - rc->module_pattern = simple_pattern_create(rc->module_match, NULL, SIMPLE_PATTERN_EXACT); + rc->module_match = string_strdupz(value); + rc->module_pattern = simple_pattern_create(rrdcalc_module_match(rc), NULL, SIMPLE_PATTERN_EXACT); } else { error("Health configuration at line %zu of file '%s' for alarm '%s' has unknown key '%s'.", - line, filename, rc->name, key); + line, filename, rrdcalc_name(rc), key); } } else if(rt) { if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) { - alert_cfg->on = strdupz(value); + alert_cfg->on = string_strdupz(value); if(rt->context) { - if(strcmp(rt->context, value) != 0) + if(strcmp(string2str(rt->context), value) != 0) error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rt->name, key, rt->context, value, value); + line, filename, rrdcalctemplate_name(rt), key, string2str(rt->context), value, value); - freez(rt->context); + string_freez(rt->context); } - rt->context = strdupz(value); - rt->hash_context = simple_hash(rt->context); + rt->context = string_strdupz(value); } else if(hash == hash_class && !strcasecmp(key, HEALTH_CLASS_KEY)) { - alert_cfg->classification = strdupz(value); + strip_quotes(value); + + alert_cfg->classification = string_strdupz(value); if(rt->classification) { - if(strcmp(rt->classification, value) != 0) + if(strcmp(rrdcalctemplate_classification(rt), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rt->name, key, rt->classification, value, value); + line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_classification(rt), value, value); - freez(rt->classification); + string_freez(rt->classification); } - rt->classification = strdupz(value); - strip_quotes(rt->classification); + rt->classification = string_strdupz(value); } else if(hash == hash_component && !strcasecmp(key, HEALTH_COMPONENT_KEY)) { - alert_cfg->component = strdupz(value); + strip_quotes(value); + + alert_cfg->component = string_strdupz(value); if(rt->component) { - if(strcmp(rt->component, value) != 0) + if(strcmp(rrdcalctemplate_component(rt), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rt->name, key, rt->component, value, value); + line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_component(rt), value, value); - freez(rt->component); + string_freez(rt->component); } - rt->component = strdupz(value); - strip_quotes(rt->component); + rt->component = string_strdupz(value); } else if(hash == hash_type && !strcasecmp(key, HEALTH_TYPE_KEY)) { - alert_cfg->type = strdupz(value); + strip_quotes(value); + + alert_cfg->type = string_strdupz(value); if(rt->type) { - if(strcmp(rt->type, value) != 0) + if(strcmp(rrdcalctemplate_type(rt), value) != 0) error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rt->name, key, rt->type, value, value); + line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_type(rt), value, value); - freez(rt->type); + string_freez(rt->type); } - rt->type = strdupz(value); - strip_quotes(rt->type); + rt->type = string_strdupz(value); } else if(hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) { - alert_cfg->families = strdupz(value); - freez(rt->family_match); + alert_cfg->families = string_strdupz(value); + string_freez(rt->family_match); simple_pattern_free(rt->family_pattern); - rt->family_match = strdupz(value); - rt->family_pattern = simple_pattern_create(rt->family_match, NULL, SIMPLE_PATTERN_EXACT); + rt->family_match = string_strdupz(value); + rt->family_pattern = simple_pattern_create(rrdcalctemplate_family_match(rt), NULL, SIMPLE_PATTERN_EXACT); } else if(hash == hash_plugin && !strcasecmp(key, HEALTH_PLUGIN_KEY)) { - alert_cfg->plugin = strdupz(value); - freez(rt->plugin_match); + alert_cfg->plugin = string_strdupz(value); + string_freez(rt->plugin_match); simple_pattern_free(rt->plugin_pattern); - rt->plugin_match = strdupz(value); - rt->plugin_pattern = simple_pattern_create(rt->plugin_match, NULL, SIMPLE_PATTERN_EXACT); + rt->plugin_match = string_strdupz(value); + rt->plugin_pattern = simple_pattern_create(rrdcalctemplate_plugin_match(rt), NULL, SIMPLE_PATTERN_EXACT); } else if(hash == hash_module && !strcasecmp(key, HEALTH_MODULE_KEY)) { - alert_cfg->module = strdupz(value); - freez(rt->module_match); + alert_cfg->module = string_strdupz(value); + string_freez(rt->module_match); simple_pattern_free(rt->module_pattern); - rt->module_match = strdupz(value); - rt->module_pattern = simple_pattern_create(rt->module_match, NULL, SIMPLE_PATTERN_EXACT); + rt->module_match = string_strdupz(value); + rt->module_pattern = simple_pattern_create(rrdcalctemplate_module_match(rt), NULL, SIMPLE_PATTERN_EXACT); } else if(hash == hash_charts && !strcasecmp(key, HEALTH_CHARTS_KEY)) { - alert_cfg->charts = strdupz(value); - freez(rt->charts_match); + alert_cfg->charts = string_strdupz(value); + string_freez(rt->charts_match); simple_pattern_free(rt->charts_pattern); - rt->charts_match = strdupz(value); - rt->charts_pattern = simple_pattern_create(rt->charts_match, NULL, SIMPLE_PATTERN_EXACT); + rt->charts_match = string_strdupz(value); + rt->charts_pattern = simple_pattern_create(rrdcalctemplate_charts_match(rt), NULL, SIMPLE_PATTERN_EXACT); } else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) { - alert_cfg->lookup = strdupz(value); + alert_cfg->lookup = string_strdupz(value); health_parse_db_lookup(line, filename, value, &rt->group, &rt->after, &rt->before, - &rt->update_every, &rt->options, &rt->dimensions, &rt->foreachdim); - if(rt->foreachdim) { - rt->spdim = health_pattern_from_foreach(rt->foreachdim); - } + &rt->update_every, &rt->options, &rt->dimensions, &rt->foreach_dimension); + + if(rt->foreach_dimension) + rt->foreach_dimension_pattern = health_pattern_from_foreach(rrdcalctemplate_foreachdim(rt)); + if (rt->after) { if (rt->dimensions) - alert_cfg->p_db_lookup_dimensions = strdupz(rt->dimensions); + alert_cfg->p_db_lookup_dimensions = string_dup(rt->dimensions); + if (rt->group) - alert_cfg->p_db_lookup_method = strdupz(group_method2string(rt->group)); + alert_cfg->p_db_lookup_method = string_strdupz(group_method2string(rt->group)); + alert_cfg->p_db_lookup_options = rt->options; alert_cfg->p_db_lookup_after = rt->after; alert_cfg->p_db_lookup_before = rt->before; @@ -1092,137 +990,143 @@ static int health_readfile(const char *filename, void *data) { } } else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) { - alert_cfg->every = strdupz(value); + alert_cfg->every = string_strdupz(value); if(!config_parse_duration(value, &rt->update_every)) error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' cannot parse duration: '%s'.", - line, filename, rt->name, key, value); + line, filename, rrdcalctemplate_name(rt), key, value); alert_cfg->p_update_every = rt->update_every; } else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) { - alert_cfg->green = strdupz(value); + alert_cfg->green = string_strdupz(value); char *e; rt->green = str2ndd(value, &e); if(e && *e) { error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.", - line, filename, rt->name, key, e); + line, filename, rrdcalctemplate_name(rt), key, e); } } else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) { - alert_cfg->red = strdupz(value); + alert_cfg->red = string_strdupz(value); char *e; rt->red = str2ndd(value, &e); if(e && *e) { error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.", - line, filename, rt->name, key, e); + line, filename, rrdcalctemplate_name(rt), key, e); } } else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) { - alert_cfg->calc = strdupz(value); + alert_cfg->calc = string_strdupz(value); const char *failed_at = NULL; int error = 0; rt->calculation = expression_parse(value, &failed_at, &error); if(!rt->calculation) { error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'", - line, filename, rt->name, key, value, expression_strerror(error), failed_at); + line, filename, rrdcalctemplate_name(rt), key, value, expression_strerror(error), failed_at); } } else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) { - alert_cfg->warn = strdupz(value); + alert_cfg->warn = string_strdupz(value); const char *failed_at = NULL; int error = 0; rt->warning = expression_parse(value, &failed_at, &error); if(!rt->warning) { error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'", - line, filename, rt->name, key, value, expression_strerror(error), failed_at); + line, filename, rrdcalctemplate_name(rt), key, value, expression_strerror(error), failed_at); } } else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) { - alert_cfg->crit = strdupz(value); + alert_cfg->crit = string_strdupz(value); const char *failed_at = NULL; int error = 0; rt->critical = expression_parse(value, &failed_at, &error); if(!rt->critical) { error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'", - line, filename, rt->name, key, value, expression_strerror(error), failed_at); + line, filename, rrdcalctemplate_name(rt), key, value, expression_strerror(error), failed_at); } } else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) { - alert_cfg->exec = strdupz(value); + alert_cfg->exec = string_strdupz(value); if(rt->exec) { - if(strcmp(rt->exec, value) != 0) + if(strcmp(rrdcalctemplate_exec(rt), value) != 0) error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rt->name, key, rt->exec, value, value); + line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_exec(rt), value, value); - freez(rt->exec); + string_freez(rt->exec); } - rt->exec = strdupz(value); + rt->exec = string_strdupz(value); } else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) { - alert_cfg->to = strdupz(value); + alert_cfg->to = string_strdupz(value); if(rt->recipient) { - if(strcmp(rt->recipient, value) != 0) + if(strcmp(rrdcalctemplate_recipient(rt), value) != 0) error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rt->name, key, rt->recipient, value, value); + line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_recipient(rt), value, value); - freez(rt->recipient); + string_freez(rt->recipient); } - rt->recipient = strdupz(value); + rt->recipient = string_strdupz(value); } else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) { - alert_cfg->units = strdupz(value); + strip_quotes(value); + + alert_cfg->units = string_strdupz(value); if(rt->units) { - if(strcmp(rt->units, value) != 0) + if(strcmp(rrdcalctemplate_units(rt), value) != 0) error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rt->name, key, rt->units, value, value); + line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_units(rt), value, value); - freez(rt->units); + string_freez(rt->units); } - rt->units = strdupz(value); - strip_quotes(rt->units); + rt->units = string_strdupz(value); } else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) { - alert_cfg->info = strdupz(value); + strip_quotes(value); + + alert_cfg->info = string_strdupz(value); if(rt->info) { - if(strcmp(rt->info, value) != 0) + if(strcmp(rrdcalctemplate_info(rt), value) != 0) error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rt->name, key, rt->info, value, value); + line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_info(rt), value, value); - freez(rt->info); + string_freez(rt->info); } - rt->info = strdupz(value); - strip_quotes(rt->info); + rt->info = string_strdupz(value); } else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) { - alert_cfg->delay = strdupz(value); + alert_cfg->delay = string_strdupz(value); health_parse_delay(line, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier); } else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) { - alert_cfg->options = strdupz(value); + alert_cfg->options = string_strdupz(value); rt->options |= health_parse_options(value); } else if(hash == hash_repeat && !strcasecmp(key, HEALTH_REPEAT_KEY)){ - alert_cfg->repeat = strdupz(value); + alert_cfg->repeat = string_strdupz(value); health_parse_repeat(line, filename, value, &rt->warn_repeat_every, &rt->crit_repeat_every); } else if(hash == hash_host_label && !strcasecmp(key, HEALTH_HOST_LABEL_KEY)) { - alert_cfg->host_labels = strdupz(value); + alert_cfg->host_labels = string_strdupz(value); if(rt->host_labels) { - if(strcmp(rt->host_labels, value) != 0) + if(strcmp(rrdcalctemplate_host_labels(rt), value) != 0) error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').", - line, filename, rt->name, key, rt->host_labels, value, value); + line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_host_labels(rt), value, value); - freez(rt->host_labels); + string_freez(rt->host_labels); simple_pattern_free(rt->host_labels_pattern); } - rt->host_labels = simple_pattern_trim_around_equal(value); - rt->host_labels_pattern = simple_pattern_create(rt->host_labels, NULL, SIMPLE_PATTERN_EXACT); + { + char *tmp = simple_pattern_trim_around_equal(value); + rt->host_labels = string_strdupz(tmp); + freez(tmp); + } + rt->host_labels_pattern = simple_pattern_create(rrdcalctemplate_host_labels(rt), NULL, SIMPLE_PATTERN_EXACT); } else { error("Health configuration at line %zu of file '%s' for template '%s' has unknown key '%s'.", - line, filename, rt->name, key); + line, filename, rrdcalctemplate_name(rt), key); } } else { @@ -1233,15 +1137,17 @@ static int health_readfile(const char *filename, void *data) { if(rc) { //health_add_alarms_loop(host, rc, ignore_this) ; - if(!alert_hash_and_store_config(rc->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this || !rrdcalc_add_alarm_from_config(host, rc)) { - rrdcalc_free(rc); - } + if(!alert_hash_and_store_config(rc->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this) + rrdcalc_free_unused_rrdcalc_loaded_from_config(rc); + else + rrdcalc_add_from_config(host, rc); } if(rt) { - if(!alert_hash_and_store_config(rt->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this || !rrdcalctemplate_add_template_from_config(host, rt)) { - rrdcalctemplate_free(rt); - } + if(!alert_hash_and_store_config(rt->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this) + rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(rt); + else + rrdcalctemplate_add_from_config(host, rt); } if (alert_cfg) @@ -1257,8 +1163,8 @@ void sql_refresh_hashes(void) } void health_readdir(RRDHOST *host, const char *user_path, const char *stock_path, const char *subpath) { - if(unlikely(!host->health_enabled)) { - debug(D_HEALTH, "CONFIG health is not enabled for host '%s'", host->hostname); + if(unlikely(!host->health_enabled) && !rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)) { + debug(D_HEALTH, "CONFIG health is not enabled for host '%s'", rrdhost_hostname(host)); return; } @@ -1266,10 +1172,11 @@ void health_readdir(RRDHOST *host, const char *user_path, const char *stock_path CONFIG_BOOLEAN_YES); if (!stock_enabled) { - info("Netdata will not load stock alarms."); + log_health("[%s]: Netdata will not load stock alarms.", rrdhost_hostname(host)); stock_path = user_path; } recursive_config_double_dir_load(user_path, stock_path, subpath, health_readfile, (void *) host, 0); + log_health("[%s]: Read health configuration.", rrdhost_hostname(host)); sql_store_hashes = 0; } diff --git a/health/health_json.c b/health/health_json.c index 4e8f43761..2dd59fd46 100644 --- a/health/health_json.c +++ b/health/health_json.c @@ -14,7 +14,7 @@ void health_string2json(BUFFER *wb, const char *prefix, const char *label, const } void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host) { - char *edit_command = ae->source ? health_edit_command_from_source(ae->source) : strdupz("UNKNOWN=0=UNKNOWN"); + char *edit_command = ae->source ? health_edit_command_from_source(ae_source(ae)) : strdupz("UNKNOWN=0=UNKNOWN"); char config_hash_id[GUID_LEN + 1]; uuid_unparse_lower(ae->config_hash_id, config_hash_id); @@ -57,30 +57,30 @@ void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host) "\t\t\"old_value_string\": \"%s\",\n" "\t\t\"last_repeat\": \"%lu\",\n" "\t\t\"silenced\": \"%s\",\n" - , host->hostname + , rrdhost_hostname(host) , host->utc_offset - , host->abbrev_timezone + , rrdhost_abbrev_timezone(host) , ae->unique_id , ae->alarm_id , ae->alarm_event_id , config_hash_id - , ae->name - , ae->chart - , ae->chart_context - , ae->family - , ae->classification?ae->classification:"Unknown" - , ae->component?ae->component:"Unknown" - , ae->type?ae->type:"Unknown" + , ae_name(ae) + , ae_chart_name(ae) + , ae_chart_context(ae) + , ae_family(ae) + , ae->classification?ae_classification(ae):"Unknown" + , ae->component?ae_component(ae):"Unknown" + , ae->type?ae_type(ae):"Unknown" , (ae->flags & HEALTH_ENTRY_FLAG_PROCESSED)?"true":"false" , (ae->flags & HEALTH_ENTRY_FLAG_UPDATED)?"true":"false" , (unsigned long)ae->exec_run_timestamp , (ae->flags & HEALTH_ENTRY_FLAG_EXEC_FAILED)?"true":"false" - , ae->exec?ae->exec:host->health_default_exec - , ae->recipient?ae->recipient:host->health_default_recipient + , ae->exec?ae_exec(ae):string2str(host->health_default_exec) + , ae->recipient?ae_recipient(ae):string2str(host->health_default_recipient) , ae->exec_code - , ae->source + , ae_source(ae) , edit_command - , ae->units?ae->units:"" + , ae_units(ae) , (unsigned long)ae->when , (unsigned long)ae->duration , (unsigned long)ae->non_clear_duration @@ -90,28 +90,13 @@ void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host) , (unsigned long)ae->delay_up_to_timestamp , ae->updated_by_id , ae->updates_id - , ae->new_value_string - , ae->old_value_string + , ae_new_value_string(ae) + , ae_old_value_string(ae) , (unsigned long)ae->last_repeat , (ae->flags & HEALTH_ENTRY_FLAG_SILENCED)?"true":"false" ); - char *replaced_info = NULL; - if (likely(ae->info)) { - char *m = NULL; - replaced_info = strdupz(ae->info); - size_t pos = 0; - while ((m = strstr(replaced_info + pos, "$family"))) { - char *buf = NULL; - pos = m - replaced_info; - buf = find_and_replace(replaced_info, "$family", ae->family ? ae->family : "", m); - freez(replaced_info); - replaced_info = strdupz(buf); - freez(buf); - } - } - - health_string2json(wb, "\t\t", "info", replaced_info?replaced_info:"", ",\n"); + health_string2json(wb, "\t\t", "info", ae->info ? ae_info(ae) : "", ",\n"); if(unlikely(ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION)) { buffer_strcat(wb, "\t\t\"no_clear_notification\": true,\n"); @@ -127,22 +112,23 @@ void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host) buffer_strcat(wb, "\t}"); - freez(replaced_info); freez(edit_command); } void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after, char *chart) { - netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock); buffer_strcat(wb, "["); unsigned int max = host->health_log.max; unsigned int count = 0; - uint32_t hash_chart = 0; - if (chart) hash_chart = simple_hash(chart); + + STRING *chart_string = string_strdupz(chart); + + netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock); + ALARM_ENTRY *ae; for (ae = host->health_log.alarms; ae && count < max; ae = ae->next) { - if ((ae->unique_id > after) && (!chart || (ae->hash_chart == hash_chart && !strcmp(ae->chart, chart)))) { + if ((ae->unique_id > after) && (!chart || chart_string == ae->chart)) { if (likely(count)) buffer_strcat(wb, ","); health_alarm_entry2json_nolock(wb, ae, host); @@ -150,9 +136,11 @@ void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after, char *char } } - buffer_strcat(wb, "\n]\n"); - netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); + + string_freez(chart_string); + + buffer_strcat(wb, "\n]\n"); } static inline void health_rrdcalc_values2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC *rc) { @@ -160,7 +148,7 @@ static inline void health_rrdcalc_values2json_nolock(RRDHOST *host, BUFFER *wb, buffer_sprintf(wb, "\t\t\"%s.%s\": {\n" "\t\t\t\"id\": %lu,\n" - , rc->chart, rc->name + , rrdcalc_chart_name(rc), rrdcalc_name(rc) , (unsigned long)rc->id); buffer_strcat(wb, "\t\t\t\"value\":"); @@ -180,22 +168,7 @@ static inline void health_rrdcalc_values2json_nolock(RRDHOST *host, BUFFER *wb, static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC *rc) { char value_string[100 + 1]; - format_value_and_unit(value_string, 100, rc->value, rc->units, -1); - - char *replaced_info = NULL; - if (likely(rc->info)) { - char *m; - replaced_info = strdupz(rc->info); - size_t pos = 0; - while ((m = strstr(replaced_info + pos, "$family"))) { - char *buf = NULL; - pos = m - replaced_info; - buf = find_and_replace(replaced_info, "$family", (rc->rrdset && rc->rrdset->family) ? rc->rrdset->family : "", m); - freez(replaced_info); - replaced_info = strdupz(buf); - freez(buf); - } - } + format_value_and_unit(value_string, 100, rc->value, rrdcalc_units(rc), -1); char hash_id[GUID_LEN + 1]; uuid_unparse_lower(rc->config_hash_id, hash_id); @@ -234,23 +207,23 @@ static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC "\t\t\t\"value_string\": \"%s\",\n" "\t\t\t\"last_repeat\": \"%lu\",\n" "\t\t\t\"times_repeat\": %lu,\n" - , rc->chart, rc->name + , rrdcalc_chart_name(rc), rrdcalc_name(rc) , (unsigned long)rc->id , hash_id - , rc->name - , rc->chart - , (rc->rrdset && rc->rrdset->family)?rc->rrdset->family:"" - , rc->classification?rc->classification:"Unknown" - , rc->component?rc->component:"Unknown" - , rc->type?rc->type:"Unknown" + , rrdcalc_name(rc) + , rrdcalc_chart_name(rc) + , (rc->rrdset)?rrdset_family(rc->rrdset):"" + , rc->classification?rrdcalc_classification(rc):"Unknown" + , rc->component?rrdcalc_component(rc):"Unknown" + , rc->type?rrdcalc_type(rc):"Unknown" , (rc->rrdset)?"true":"false" - , (rc->rrdcalc_flags & RRDCALC_FLAG_DISABLED)?"true":"false" - , (rc->rrdcalc_flags & RRDCALC_FLAG_SILENCED)?"true":"false" - , rc->exec?rc->exec:host->health_default_exec - , rc->recipient?rc->recipient:host->health_default_recipient - , rc->source - , rc->units?rc->units:"" - , replaced_info?replaced_info:"" + , (rc->run_flags & RRDCALC_FLAG_DISABLED)?"true":"false" + , (rc->run_flags & RRDCALC_FLAG_SILENCED)?"true":"false" + , rc->exec?rrdcalc_exec(rc):string2str(host->health_default_exec) + , rc->recipient?rrdcalc_recipient(rc):string2str(host->health_default_recipient) + , rrdcalc_source(rc) + , rrdcalc_units(rc) + , rrdcalc_info(rc) , rrdcalc_status2string(rc->status) , (unsigned long)rc->last_status_change , (unsigned long)rc->last_updated @@ -269,13 +242,13 @@ static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC , (unsigned long)rc->times_repeat ); - if(unlikely(rc->options & RRDCALC_FLAG_NO_CLEAR_NOTIFICATION)) { + if(unlikely(rc->options & RRDCALC_OPTION_NO_CLEAR_NOTIFICATION)) { buffer_strcat(wb, "\t\t\t\"no_clear_notification\": true,\n"); } if(RRDCALC_HAS_DB_LOOKUP(rc)) { - if(rc->dimensions && *rc->dimensions) - health_string2json(wb, "\t\t\t", "lookup_dimensions", rc->dimensions, ",\n"); + if(rc->dimensions) + health_string2json(wb, "\t\t\t", "lookup_dimensions", rrdcalc_dimensions(rc), ",\n"); buffer_sprintf(wb, "\t\t\t\"db_after\": %lu,\n" @@ -322,8 +295,6 @@ static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC buffer_strcat(wb, "\n"); buffer_strcat(wb, "\t\t}"); - - freez(replaced_info); } //void health_rrdcalctemplate2json_nolock(BUFFER *wb, RRDCALCTEMPLATE *rt) { @@ -336,27 +307,30 @@ void health_aggregate_alarms(RRDHOST *host, BUFFER *wb, BUFFER* contexts, RRDCAL char *tok = NULL; char *p = NULL; - rrdhost_rdlock(host); - if (contexts) { p = (char*)buffer_tostring(contexts); while(p && *p && (tok = mystrsep(&p, ", |"))) { if(!*tok) continue; - for(rc = host->alarms; rc ; rc = rc->next) { + STRING *tok_string = string_strdupz(tok); + + foreach_rrdcalc_in_rrdhost_read(host, rc) { if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) continue; if (unlikely(!rrdset_is_available_for_exporting_and_alarms(rc->rrdset))) continue; - if(unlikely(rc->rrdset && rc->rrdset->hash_context == simple_hash(tok) - && !strcmp(rc->rrdset->context, tok) - && ((status==RRDCALC_STATUS_RAISED)?(rc->status >= RRDCALC_STATUS_WARNING):rc->status == status))) + if(unlikely(rc->rrdset + && rc->rrdset->context == tok_string + && ((status==RRDCALC_STATUS_RAISED)?(rc->status >= RRDCALC_STATUS_WARNING):rc->status == status))) numberOfAlarms++; } + foreach_rrdcalc_in_rrdhost_done(rc); + + string_freez(tok_string); } } else { - for(rc = host->alarms; rc ; rc = rc->next) { + foreach_rrdcalc_in_rrdhost_read(host, rc) { if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) continue; if (unlikely(!rrdset_is_available_for_exporting_and_alarms(rc->rrdset))) @@ -364,16 +338,16 @@ void health_aggregate_alarms(RRDHOST *host, BUFFER *wb, BUFFER* contexts, RRDCAL if(unlikely((status==RRDCALC_STATUS_RAISED)?(rc->status >= RRDCALC_STATUS_WARNING):rc->status == status)) numberOfAlarms++; } + foreach_rrdcalc_in_rrdhost_done(rc); } buffer_sprintf(wb, "%d", numberOfAlarms); - rrdhost_unlock(host); } static void health_alarms2json_fill_alarms(RRDHOST *host, BUFFER *wb, int all, void (*fp)(RRDHOST *, BUFFER *, RRDCALC *)) { RRDCALC *rc; - int i; - for(i = 0, rc = host->alarms; rc ; rc = rc->next) { + int i = 0; + foreach_rrdcalc_in_rrdhost_read(host, rc) { if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) continue; @@ -387,44 +361,43 @@ static void health_alarms2json_fill_alarms(RRDHOST *host, BUFFER *wb, int all, v fp(host, wb, rc); i++; } + foreach_rrdcalc_in_rrdhost_done(rc); } void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) { - rrdhost_rdlock(host); buffer_sprintf(wb, "{\n\t\"hostname\": \"%s\"," "\n\t\"latest_alarm_log_unique_id\": %u," "\n\t\"status\": %s," "\n\t\"now\": %lu," "\n\t\"alarms\": {\n", - host->hostname, + rrdhost_hostname(host), (host->health_log.next_log_id > 0)?(host->health_log.next_log_id - 1):0, host->health_enabled?"true":"false", (unsigned long)now_realtime_sec()); health_alarms2json_fill_alarms(host, wb, all, health_rrdcalc2json_nolock); +// rrdhost_rdlock(host); // buffer_strcat(wb, "\n\t},\n\t\"templates\": {"); // RRDCALCTEMPLATE *rt; // for(rt = host->templates; rt ; rt = rt->next) // health_rrdcalctemplate2json_nolock(wb, rt); +// rrdhost_unlock(host); buffer_strcat(wb, "\n\t}\n}\n"); - rrdhost_unlock(host); } void health_alarms_values2json(RRDHOST *host, BUFFER *wb, int all) { - rrdhost_rdlock(host); buffer_sprintf(wb, "{\n\t\"hostname\": \"%s\"," "\n\t\"alarms\": {\n", - host->hostname); + rrdhost_hostname(host)); health_alarms2json_fill_alarms(host, wb, all, health_rrdcalc_values2json_nolock); buffer_strcat(wb, "\n\t}\n}\n"); - rrdhost_unlock(host); } -static int have_recent_alarm(RRDHOST *host, uint32_t alarm_id, time_t mark) +static int have_recent_alarm(RRDHOST *host, uint32_t alarm_id, uint32_t mark) { ALARM_ENTRY *ae = host->health_log.alarms; diff --git a/health/health_log.c b/health/health_log.c index f0a05531d..8105e01ae 100644 --- a/health/health_log.c +++ b/health/health_log.c @@ -14,11 +14,11 @@ inline int health_alarm_log_open(RRDHOST *host) { if(host->health_log_fp) { if (setvbuf(host->health_log_fp, NULL, _IOLBF, 0) != 0) - error("HEALTH [%s]: cannot set line buffering on health log file '%s'.", host->hostname, host->health_log_filename); + error("HEALTH [%s]: cannot set line buffering on health log file '%s'.", rrdhost_hostname(host), host->health_log_filename); return 0; } - error("HEALTH [%s]: cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", host->hostname, host->health_log_filename); + error("HEALTH [%s]: cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", rrdhost_hostname(host), host->health_log_filename); return -1; } @@ -45,13 +45,13 @@ static inline void health_log_rotate(RRDHOST *host) { snprintfz(old_filename, FILENAME_MAX, "%s.old", host->health_log_filename); if(unlink(old_filename) == -1 && errno != ENOENT) - error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, old_filename); + error("HEALTH [%s]: cannot remove old alarms log file '%s'", rrdhost_hostname(host), old_filename); if(link(host->health_log_filename, old_filename) == -1 && errno != ENOENT) - error("HEALTH [%s]: cannot move file '%s' to '%s'.", host->hostname, host->health_log_filename, old_filename); + error("HEALTH [%s]: cannot move file '%s' to '%s'.", rrdhost_hostname(host), host->health_log_filename, old_filename); if(unlink(host->health_log_filename) == -1 && errno != ENOENT) - error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, host->health_log_filename); + error("HEALTH [%s]: cannot remove old alarms log file '%s'", rrdhost_hostname(host), host->health_log_filename); // open it with truncate host->health_log_fp = fopen(host->health_log_filename, "w"); @@ -59,7 +59,7 @@ static inline void health_log_rotate(RRDHOST *host) { if(host->health_log_fp) fclose(host->health_log_fp); else - error("HEALTH [%s]: cannot truncate health log '%s'", host->hostname, host->health_log_filename); + error("HEALTH [%s]: cannot truncate health log '%s'", rrdhost_hostname(host), host->health_log_filename); host->health_log_fp = NULL; @@ -75,12 +75,12 @@ inline void health_label_log_save(RRDHOST *host) { if(unlikely(host->health_log_fp)) { BUFFER *wb = buffer_create(1024); - rrdlabels_to_buffer(localhost->host_labels, wb, "", "=", "", "\t ", NULL, NULL, NULL, NULL); + rrdlabels_to_buffer(localhost->rrdlabels, wb, "", "=", "", "\t ", NULL, NULL, NULL, NULL); char *write = (char *) buffer_tostring(wb); if (unlikely(fprintf(host->health_log_fp, "L\t%s", write) < 0)) error("HEALTH [%s]: failed to save alarm log entry to '%s'. Health data may be lost in case of abnormal restart.", - host->hostname, host->health_log_filename); + rrdhost_hostname(host), host->health_log_filename); else host->health_log_entries_written++; @@ -103,7 +103,7 @@ inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) { "\t%s\t%s\t%s" "\n" , (ae->flags & HEALTH_ENTRY_FLAG_SAVED)?'U':'A' - , host->hostname + , rrdhost_hostname(host) , ae->unique_id , ae->alarm_id @@ -118,14 +118,14 @@ inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) { , (uint32_t)ae->exec_run_timestamp , (uint32_t)ae->delay_up_to_timestamp - , (ae->name)?ae->name:"" - , (ae->chart)?ae->chart:"" - , (ae->family)?ae->family:"" - , (ae->exec)?ae->exec:"" - , (ae->recipient)?ae->recipient:"" - , (ae->source)?ae->source:"" - , (ae->units)?ae->units:"" - , (ae->info)?ae->info:"" + , ae_name(ae) + , ae_chart_name(ae) + , ae_family(ae) + , ae_exec(ae) + , ae_recipient(ae) + , ae_source(ae) + , ae_units(ae) + , ae_info(ae) , ae->exec_code , ae->new_status @@ -135,11 +135,11 @@ inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) { , ae->new_value , ae->old_value , (uint64_t)ae->last_repeat - , (ae->classification)?ae->classification:"Unknown" - , (ae->component)?ae->component:"Unknown" - , (ae->type)?ae->type:"Unknown" + , (ae->classification)?ae_classification(ae):"Unknown" + , (ae->component)?ae_component(ae):"Unknown" + , (ae->type)?ae_type(ae):"Unknown" ) < 0)) - error("HEALTH [%s]: failed to save alarm log entry to '%s'. Health data may be lost in case of abnormal restart.", host->hostname, host->health_log_filename); + error("HEALTH [%s]: failed to save alarm log entry to '%s'. Health data may be lost in case of abnormal restart.", rrdhost_hostname(host), host->health_log_filename); else { ae->flags |= HEALTH_ENTRY_FLAG_SAVED; host->health_log_entries_written++; @@ -156,18 +156,23 @@ inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) { static uint32_t is_valid_alarm_id(RRDHOST *host, const char *chart, const char *name, uint32_t alarm_id) { - uint32_t hash_chart = simple_hash(chart); - uint32_t hash_name = simple_hash(name); + STRING *chart_string = string_strdupz(chart); + STRING *name_string = string_strdupz(name); + + uint32_t ret = 1; ALARM_ENTRY *ae; for(ae = host->health_log.alarms; ae ;ae = ae->next) { - if (unlikely( - ae->alarm_id == alarm_id && (!(ae->hash_name == hash_name && ae->hash_chart == hash_chart && - !strcmp(name, ae->name) && !strcmp(chart, ae->chart))))) { - return 0; + if (unlikely(ae->alarm_id == alarm_id && (!(chart_string == ae->chart && name_string == ae->name)))) { + ret = 0; + break; } } - return 1; + + string_freez(chart_string); + string_freez(name_string); + + return ret; } static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *filename) { @@ -177,6 +182,14 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char size_t line = 0, len = 0; ssize_t loaded = 0, updated = 0, errored = 0, duplicate = 0; + DICTIONARY *all_rrdcalcs = dictionary_create( + DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE); + RRDCALC *rc; + foreach_rrdcalc_in_rrdhost_read(host, rc) { + dictionary_set(all_rrdcalcs, rrdcalc_name(rc), rc, sizeof(*rc)); + } + foreach_rrdcalc_in_rrdhost_done(rc); + netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock); while((s = fgets_trim_len(buf, 65536, fp, &len))) { @@ -192,7 +205,7 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *s = '\0'; pointers[entries++] = ++s; if(entries >= max_entries) { - error("HEALTH [%s]: line %zu of file '%s' has more than %d entries. Ignoring excessive entries.", host->hostname, line, filename, max_entries); + error("HEALTH [%s]: line %zu of file '%s' has more than %d entries. Ignoring excessive entries.", rrdhost_hostname(host), line, filename, max_entries); break; } } @@ -206,7 +219,7 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char ALARM_ENTRY *ae = NULL; if(entries < 27) { - error("HEALTH [%s]: line %zu of file '%s' should have at least 27 entries, but it has %d. Ignoring it.", host->hostname, line, filename, entries); + error("HEALTH [%s]: line %zu of file '%s' should have at least 27 entries, but it has %d. Ignoring it.", rrdhost_hostname(host), line, filename, entries); errored++; continue; } @@ -214,14 +227,14 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char // check that we have valid ids uint32_t unique_id = (uint32_t)strtoul(pointers[2], NULL, 16); if(!unique_id) { - error("HEALTH [%s]: line %zu of file '%s' states alarm entry with invalid unique id %u (%s). Ignoring it.", host->hostname, line, filename, unique_id, pointers[2]); + error("HEALTH [%s]: line %zu of file '%s' states alarm entry with invalid unique id %u (%s). Ignoring it.", rrdhost_hostname(host), line, filename, unique_id, pointers[2]); errored++; continue; } uint32_t alarm_id = (uint32_t)strtoul(pointers[3], NULL, 16); if(!alarm_id) { - error("HEALTH [%s]: line %zu of file '%s' states alarm entry for invalid alarm id %u (%s). Ignoring it.", host->hostname, line, filename, alarm_id, pointers[3]); + error("HEALTH [%s]: line %zu of file '%s' states alarm entry for invalid alarm id %u (%s). Ignoring it.", rrdhost_hostname(host), line, filename, alarm_id, pointers[3]); errored++; continue; } @@ -232,18 +245,7 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char char* alarm_name = pointers[13]; last_repeat = (time_t)strtoul(pointers[27], NULL, 16); - RRDCALC *rc = alarm_max_last_repeat(host, alarm_name,simple_hash(alarm_name)); - if (!rc) { - for(rc = host->alarms; rc ; rc = rc->next) { - RRDCALC *rdcmp = (RRDCALC *) avl_insert_lock(&(host)->alarms_idx_name, (avl_t *)rc); - if(rdcmp != rc) { - error("Cannot insert the alarm index ID using log %s", rc->name); - } - } - - rc = alarm_max_last_repeat(host, alarm_name,simple_hash(alarm_name)); - } - + rc = dictionary_get(all_rrdcalcs, alarm_name); if(unlikely(rc)) { if (rrdcalc_isrepeating(rc)) { rc->last_repeat = last_repeat; @@ -259,7 +261,7 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char // make sure it is properly numbered if(unlikely(host->health_log.alarms && unique_id < host->health_log.alarms->unique_id)) { error( "HEALTH [%s]: line %zu of file '%s' has alarm log entry %u in wrong order. Ignoring it." - , host->hostname, line, filename, unique_id); + , rrdhost_hostname(host), line, filename, unique_id); errored++; continue; } @@ -272,7 +274,7 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char if(unlikely(unique_id == ae->unique_id)) { if(unlikely(*pointers[0] == 'A')) { error("HEALTH [%s]: line %zu of file '%s' adds duplicate alarm log entry %u. Using the later." - , host->hostname, line, filename, unique_id); + , rrdhost_hostname(host), line, filename, unique_id); *pointers[0] = 'U'; duplicate++; } @@ -298,8 +300,13 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char // error("HEALTH [%s]: line %zu of file '%s' provides an alarm for host '%s' but this is named '%s'.", host->hostname, line, filename, pointers[1], host->hostname); ae->unique_id = unique_id; - if (!is_valid_alarm_id(host, pointers[14], pointers[13], alarm_id)) - alarm_id = rrdcalc_get_unique_id(host, pointers[14], pointers[13], NULL); + if (!is_valid_alarm_id(host, pointers[14], pointers[13], alarm_id)) { + STRING *chart = string_strdupz(pointers[14]); + STRING *name = string_strdupz(pointers[13]); + alarm_id = rrdcalc_get_unique_id(host, chart, name, NULL); + string_freez(chart); + string_freez(name); + } ae->alarm_id = alarm_id; ae->alarm_event_id = (uint32_t)strtoul(pointers[4], NULL, 16); ae->updated_by_id = (uint32_t)strtoul(pointers[5], NULL, 16); @@ -315,36 +322,29 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char ae->exec_run_timestamp = (uint32_t)strtoul(pointers[11], NULL, 16); ae->delay_up_to_timestamp = (uint32_t)strtoul(pointers[12], NULL, 16); - freez(ae->name); - ae->name = strdupz(pointers[13]); - ae->hash_name = simple_hash(ae->name); + string_freez(ae->name); + ae->name = string_strdupz(pointers[13]); - freez(ae->chart); - ae->chart = strdupz(pointers[14]); - ae->hash_chart = simple_hash(ae->chart); + string_freez(ae->chart); + ae->chart = string_strdupz(pointers[14]); - freez(ae->family); - ae->family = strdupz(pointers[15]); + string_freez(ae->family); + ae->family = string_strdupz(pointers[15]); - freez(ae->exec); - ae->exec = strdupz(pointers[16]); - if(!*ae->exec) { freez(ae->exec); ae->exec = NULL; } + string_freez(ae->exec); + ae->exec = string_strdupz(pointers[16]); - freez(ae->recipient); - ae->recipient = strdupz(pointers[17]); - if(!*ae->recipient) { freez(ae->recipient); ae->recipient = NULL; } + string_freez(ae->recipient); + ae->recipient = string_strdupz(pointers[17]); - freez(ae->source); - ae->source = strdupz(pointers[18]); - if(!*ae->source) { freez(ae->source); ae->source = NULL; } + string_freez(ae->source); + ae->source = string_strdupz(pointers[18]); - freez(ae->units); - ae->units = strdupz(pointers[19]); - if(!*ae->units) { freez(ae->units); ae->units = NULL; } + string_freez(ae->units); + ae->units = string_strdupz(pointers[19]); - freez(ae->info); - ae->info = strdupz(pointers[20]); - if(!*ae->info) { freez(ae->info); ae->info = NULL; } + string_freez(ae->info); + ae->info = string_strdupz(pointers[20]); ae->exec_code = str2i(pointers[21]); ae->new_status = str2i(pointers[22]); @@ -357,24 +357,21 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char ae->last_repeat = last_repeat; if (likely(entries > 30)) { - freez(ae->classification); - ae->classification = strdupz(pointers[28]); - if(!*ae->classification) { freez(ae->classification); ae->classification = NULL; } + string_freez(ae->classification); + ae->classification = string_strdupz(pointers[28]); - freez(ae->component); - ae->component = strdupz(pointers[29]); - if(!*ae->component) { freez(ae->component); ae->component = NULL; } + string_freez(ae->component); + ae->component = string_strdupz(pointers[29]); - freez(ae->type); - ae->type = strdupz(pointers[30]); - if(!*ae->type) { freez(ae->type); ae->type = NULL; } + string_freez(ae->type); + ae->type = string_strdupz(pointers[30]); } char value_string[100 + 1]; - freez(ae->old_value_string); - freez(ae->new_value_string); - ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1)); - ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1)); + string_freez(ae->old_value_string); + string_freez(ae->new_value_string); + ae->old_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae_units(ae), -1)); + ae->new_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae_units(ae), -1)); // add it to host if not already there if(unlikely(*pointers[0] == 'A')) { @@ -395,13 +392,16 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char host->health_max_alarm_id = ae->alarm_id; } else { - error("HEALTH [%s]: line %zu of file '%s' is invalid (unrecognized entry type '%s').", host->hostname, line, filename, pointers[0]); + error("HEALTH [%s]: line %zu of file '%s' is invalid (unrecognized entry type '%s').", rrdhost_hostname(host), line, filename, pointers[0]); errored++; } } netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); + dictionary_destroy(all_rrdcalcs); + all_rrdcalcs = NULL; + freez(buf); if(!host->health_max_unique_id) host->health_max_unique_id = (uint32_t)now_realtime_sec(); @@ -411,7 +411,7 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char if (unlikely(!host->health_log.next_alarm_id || host->health_log.next_alarm_id <= host->health_max_alarm_id)) host->health_log.next_alarm_id = host->health_max_alarm_id + 1; - debug(D_HEALTH, "HEALTH [%s]: loaded file '%s' with %zd new alarm entries, updated %zd alarms, errors %zd entries, duplicate %zd", host->hostname, filename, loaded, updated, errored, duplicate); + debug(D_HEALTH, "HEALTH [%s]: loaded file '%s' with %zd new alarm entries, updated %zd alarms, errors %zd entries, duplicate %zd", rrdhost_hostname(host), filename, loaded, updated, errored, duplicate); return loaded; } @@ -422,7 +422,7 @@ inline void health_alarm_log_load(RRDHOST *host) { snprintfz(filename, FILENAME_MAX, "%s.old", host->health_log_filename); FILE *fp = fopen(filename, "r"); if(!fp) - error("HEALTH [%s]: cannot open health file: %s", host->hostname, filename); + error("HEALTH [%s]: cannot open health file: %s", rrdhost_hostname(host), filename); else { health_alarm_log_read(host, fp, filename); fclose(fp); @@ -431,7 +431,7 @@ inline void health_alarm_log_load(RRDHOST *host) { host->health_log_entries_written = 0; fp = fopen(host->health_log_filename, "r"); if(!fp) - error("HEALTH [%s]: cannot open health file: %s", host->hostname, host->health_log_filename); + error("HEALTH [%s]: cannot open health file: %s", rrdhost_hostname(host), host->health_log_filename); else { health_alarm_log_read(host, fp, host->health_log_filename); fclose(fp); @@ -443,63 +443,48 @@ inline void health_alarm_log_load(RRDHOST *host) { // health alarm log management inline ALARM_ENTRY* health_create_alarm_entry( - RRDHOST *host, - uint32_t alarm_id, - uint32_t alarm_event_id, - uuid_t config_hash_id, - time_t when, - const char *name, - const char *chart, - const char *chart_context, - const char *family, - const char *class, - const char *component, - const char *type, - const char *exec, - const char *recipient, - time_t duration, - NETDATA_DOUBLE old_value, - NETDATA_DOUBLE new_value, - RRDCALC_STATUS old_status, - RRDCALC_STATUS new_status, - const char *source, - const char *units, - const char *info, - int delay, - uint32_t flags + RRDHOST *host, + uint32_t alarm_id, + uint32_t alarm_event_id, + const uuid_t config_hash_id, + time_t when, + STRING *name, + STRING *chart, + STRING *chart_context, + STRING *family, + STRING *class, + STRING *component, + STRING *type, + STRING *exec, + STRING *recipient, + time_t duration, + NETDATA_DOUBLE old_value, + NETDATA_DOUBLE new_value, + RRDCALC_STATUS old_status, + RRDCALC_STATUS new_status, + STRING *source, + STRING *units, + STRING *info, + int delay, + uint32_t flags ) { debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id); ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY)); - ae->name = strdupz(name); - ae->hash_name = simple_hash(ae->name); - - if(chart) { - ae->chart = strdupz(chart); - ae->hash_chart = simple_hash(ae->chart); - } - - if(chart_context) - ae->chart_context = strdupz(chart_context); + ae->name = string_dup(name); + ae->chart = string_dup(chart); + ae->chart_context = string_dup(chart_context); uuid_copy(ae->config_hash_id, *((uuid_t *) config_hash_id)); - if(family) - ae->family = strdupz(family); - - if (class) - ae->classification = strdupz(class); - - if (component) - ae->component = strdupz(component); - - if (type) - ae->type = strdupz(type); - - if(exec) ae->exec = strdupz(exec); - if(recipient) ae->recipient = strdupz(recipient); - if(source) ae->source = strdupz(source); - if(units) ae->units = strdupz(units); + ae->family = string_dup(family); + ae->classification = string_dup(class); + ae->component = string_dup(component); + ae->type = string_dup(type); + ae->exec = string_dup(exec); + ae->recipient = string_dup(recipient); + ae->source = string_dup(source); + ae->units = string_dup(units); ae->unique_id = host->health_log.next_log_id++; ae->alarm_id = alarm_id; @@ -509,27 +494,10 @@ inline ALARM_ENTRY* health_create_alarm_entry( ae->new_value = new_value; char value_string[100 + 1]; - ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1)); - ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1)); - - char *replaced_info = NULL; - if (likely(info)) { - char *m; - replaced_info = strdupz(info); - size_t pos = 0; - while ((m = strstr(replaced_info + pos, "$family"))) { - char *buf = NULL; - pos = m - replaced_info; - buf = find_and_replace(replaced_info, "$family", (ae->family) ? ae->family : "", m); - freez(replaced_info); - replaced_info = strdupz(buf); - freez(buf); - } - } - - if(replaced_info) ae->info = strdupz(replaced_info); - freez(replaced_info); + ae->old_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae_units(ae), -1)); + ae->new_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae_units(ae), -1)); + ae->info = string_dup(info); ae->old_status = old_status; ae->new_status = new_status; ae->duration = duration; @@ -545,7 +513,7 @@ inline ALARM_ENTRY* health_create_alarm_entry( return ae; } -inline void health_alarm_log( +inline void health_alarm_log_add_entry( RRDHOST *host, ALARM_ENTRY *ae ) { @@ -585,26 +553,24 @@ inline void health_alarm_log( } inline void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae) { - freez(ae->name); - freez(ae->chart); - freez(ae->chart_context); - freez(ae->family); - freez(ae->classification); - freez(ae->component); - freez(ae->type); - freez(ae->exec); - freez(ae->recipient); - freez(ae->source); - freez(ae->units); - freez(ae->info); - freez(ae->old_value_string); - freez(ae->new_value_string); + string_freez(ae->name); + string_freez(ae->chart); + string_freez(ae->chart_context); + string_freez(ae->family); + string_freez(ae->classification); + string_freez(ae->component); + string_freez(ae->type); + string_freez(ae->exec); + string_freez(ae->recipient); + string_freez(ae->source); + string_freez(ae->units); + string_freez(ae->info); + string_freez(ae->old_value_string); + string_freez(ae->new_value_string); freez(ae); } inline void health_alarm_log_free(RRDHOST *host) { - rrdhost_check_wrlock(host); - netdata_rwlock_wrlock(&host->health_log.alarm_log_rwlock); ALARM_ENTRY *ae; diff --git a/health/notifications/alarm-notify.sh.in b/health/notifications/alarm-notify.sh.in index 0dfecade5..3edf3d083 100755 --- a/health/notifications/alarm-notify.sh.in +++ b/health/notifications/alarm-notify.sh.in @@ -199,7 +199,7 @@ fi [ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@" [ -z "${NETDATA_CACHE_DIR}" ] && NETDATA_CACHE_DIR="@cachedir_POST@" [ -z "${NETDATA_REGISTRY_URL}" ] && NETDATA_REGISTRY_URL="https://registry.my-netdata.io" -[ -z "${NETDATA_REGISTRY_CLOUD_BASE_URL}" ] && NETDATA_REGISTRY_CLOUD_BASE_URL="https://app.netdata.cloud" +[ -z "${NETDATA_REGISTRY_CLOUD_BASE_URL}" ] && NETDATA_REGISTRY_CLOUD_BASE_URL="https://api.netdata.cloud" # ----------------------------------------------------------------------------- # parse command line parameters @@ -250,7 +250,7 @@ fi # ----------------------------------------------------------------------------- # find a suitable hostname to use, if netdata did not supply a hostname -if [ -z ${args_host} ]; then +if [ -z "${args_host}" ]; then this_host=$(hostname -s 2>/dev/null) host="${this_host}" args_host="${this_host}" @@ -428,6 +428,10 @@ else done fi +if [[ ! $curl_options =~ .*\--connect-timeout ]]; then + curl_options+=" --connect-timeout 5" +fi + OPSGENIE_API_URL=${OPSGENIE_API_URL:-"https://api.opsgenie.com"} # If we didn't autodetect the character set for e-mail and it wasn't @@ -1335,21 +1339,37 @@ send_telegram() { if [ "${SEND_TELEGRAM}" = "YES" ] && [ -n "${bottoken}" ] && [ -n "${chatids}" ] && [ -n "${message}" ]; then for chatid in ${chatids}; do - # https://core.telegram.org/bots/api#sendmessage - httpcode=$(docurl ${disableNotification} \ - --data-urlencode "parse_mode=HTML" \ - --data-urlencode "disable_web_page_preview=true" \ - --data-urlencode "text=${emoji} ${message}" \ - "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=${chatid}") - - if [ "${httpcode}" = "200" ]; then - info "sent telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}'" - sent=$((sent + 1)) - elif [ "${httpcode}" = "401" ]; then - error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': Wrong bot token." - else - error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}' with HTTP response status code ${httpcode}." - fi + notify_telegram=1 + notify_retries=${TELEGRAM_RETRIES_ON_LIMIT:-0} + + while [ ${notify_telegram} -eq 1 ]; do + # https://core.telegram.org/bots/api#sendmessage + httpcode=$(docurl ${disableNotification} \ + --data-urlencode "parse_mode=HTML" \ + --data-urlencode "disable_web_page_preview=true" \ + --data-urlencode "text=${emoji} ${message}" \ + "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=${chatid}") + + notify_telegram=0 + + if [ "${httpcode}" = "200" ]; then + info "sent telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}'" + sent=$((sent + 1)) + elif [ "${httpcode}" = "401" ]; then + error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': Wrong bot token." + elif [ "${httpcode}" = "429" ]; then + if [ "$notify_retries" -gt 0 ]; then + error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': rate limit exceeded, retrying after 1s." + notify_retries=$((notify_retries - 1)) + notify_telegram=1 + sleep 1 + else + error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': rate limit exceeded." + fi + else + error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}' with HTTP response status code ${httpcode}." + fi + done done [ ${sent} -gt 0 ] && return 0 @@ -2398,7 +2418,7 @@ status_email_subject="${status}" case "${status}" in CRITICAL) image="${images_base_url}/images/alert-128-red.png" - alarm_badge="${NETDATA_REGISTRY_CLOUD_BASE_URL}/static/email/img/label_critical.png" + alarm_badge="https://app.netdata.cloud/static/email/img/label_critical.png" status_message="is critical" status_email_subject="Critical" color="#ca414b" @@ -2411,7 +2431,7 @@ CRITICAL) WARNING) image="${images_base_url}/images/alert-128-orange.png" - alarm_badge="${NETDATA_REGISTRY_CLOUD_BASE_URL}/static/email/img/label_warning.png" + alarm_badge="https://app.netdata.cloud/static/email/img/label_warning.png" status_message="needs attention" status_email_subject="Warning" color="#ffc107" @@ -2424,7 +2444,7 @@ WARNING) CLEAR) image="${images_base_url}/images/check-mark-2-128-green.png" - alarm_badge="${NETDATA_REGISTRY_CLOUD_BASE_URL}/static/email/img/label_recovered.png" + alarm_badge="https://app.netdata.cloud/static/email/img/label_recovered.png" status_message="recovered" status_email_subject="Clear" color="#77ca6d" diff --git a/health/notifications/health_alarm_notify.conf b/health/notifications/health_alarm_notify.conf index b69c6d538..52de86645 100755 --- a/health/notifications/health_alarm_notify.conf +++ b/health/notifications/health_alarm_notify.conf @@ -443,6 +443,11 @@ SEND_TELEGRAM="YES" # Without it, netdata cannot send telegram messages. TELEGRAM_BOT_TOKEN="" +# If an API limit error is returned on sending a message, Netdata will retry this number of times before giving up. +# Setting the number to 0 makes Netdata do no retries (which is the default). +# See https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this +TELEGRAM_RETRIES_ON_LIMIT="0" + # To get your chat ID send the command /getid to telegram bot @myidbot # (https://t.me/myidbot). Each user also needs to open a conversation with the # bot that will be sending notifications. diff --git a/libnetdata/Makefile.am b/libnetdata/Makefile.am index 5962323e8..1208d16c2 100644 --- a/libnetdata/Makefile.am +++ b/libnetdata/Makefile.am @@ -25,6 +25,7 @@ SUBDIRS = \ socket \ statistical \ storage_number \ + string \ threads \ url \ worker_utilization \ diff --git a/libnetdata/adaptive_resortable_list/adaptive_resortable_list.h b/libnetdata/adaptive_resortable_list/adaptive_resortable_list.h index 011ee73d9..294c52e81 100644 --- a/libnetdata/adaptive_resortable_list/adaptive_resortable_list.h +++ b/libnetdata/adaptive_resortable_list/adaptive_resortable_list.h @@ -64,25 +64,25 @@ typedef struct arl_base { } ARL_BASE; // create a new ARL -extern ARL_BASE *arl_create(const char *name, void (*processor)(const char *, uint32_t, const char *, void *), size_t rechecks); +ARL_BASE *arl_create(const char *name, void (*processor)(const char *, uint32_t, const char *, void *), size_t rechecks); // free an ARL -extern void arl_free(ARL_BASE *arl_base); +void arl_free(ARL_BASE *arl_base); // register an expected keyword to the ARL // together with its destination ( i.e. the output of the processor() ) -extern ARL_ENTRY *arl_expect_custom(ARL_BASE *base, const char *keyword, void (*processor)(const char *name, uint32_t hash, const char *value, void *dst), void *dst); +ARL_ENTRY *arl_expect_custom(ARL_BASE *base, const char *keyword, void (*processor)(const char *name, uint32_t hash, const char *value, void *dst), void *dst); #define arl_expect(base, keyword, dst) arl_expect_custom(base, keyword, NULL, dst) // an internal call to complete the check() call -extern int arl_find_or_create_and_relink(ARL_BASE *base, const char *s, const char *value); +int arl_find_or_create_and_relink(ARL_BASE *base, const char *s, const char *value); // begin an ARL iteration -extern void arl_begin(ARL_BASE *base); +void arl_begin(ARL_BASE *base); -extern void arl_callback_str2ull(const char *name, uint32_t hash, const char *value, void *dst); -extern void arl_callback_str2kernel_uint_t(const char *name, uint32_t hash, const char *value, void *dst); -extern void arl_callback_ssize_t(const char *name, uint32_t hash, const char *value, void *dst); +void arl_callback_str2ull(const char *name, uint32_t hash, const char *value, void *dst); +void arl_callback_str2kernel_uint_t(const char *name, uint32_t hash, const char *value, void *dst); +void arl_callback_ssize_t(const char *name, uint32_t hash, const char *value, void *dst); // check a keyword against the ARL // this is to be called for each keyword read from source data diff --git a/libnetdata/arrayalloc/arrayalloc.c b/libnetdata/arrayalloc/arrayalloc.c index bdf1384d4..f337279ae 100644 --- a/libnetdata/arrayalloc/arrayalloc.c +++ b/libnetdata/arrayalloc/arrayalloc.c @@ -6,7 +6,9 @@ #define ARAL_MAX_PAGE_SIZE_MMAP (1*1024*1024*1024) // max malloc size -#define ARAL_MAX_PAGE_SIZE_MALLOC (10*1024*1024) +// optimal at current versions of libc is up to 256k +// ideal to have the same overhead as libc is 4k +#define ARAL_MAX_PAGE_SIZE_MALLOC (64*1024) typedef struct arrayalloc_free { size_t size; @@ -32,6 +34,33 @@ static inline size_t natural_alignment(size_t size, size_t alignment) { return size; } +static void arrayalloc_delete_leftover_files(const char *path, const char *required_prefix) { + DIR *dir = opendir(path); + if(!dir) return; + + char fullpath[FILENAME_MAX + 1]; + size_t len = strlen(required_prefix); + + struct dirent *de = NULL; + while((de = readdir(dir))) { + if(de->d_type == DT_DIR) + continue; + + if(strncmp(de->d_name, required_prefix, len) != 0) + continue; + + snprintfz(fullpath, FILENAME_MAX, "%s/%s", path, de->d_name); + info("ARRAYALLOC: removing left-over file '%s'", fullpath); + if(unlikely(unlink(fullpath) == -1)) + error("Cannot delete file '%s'", fullpath); + } + + closedir(dir); +} + +// ---------------------------------------------------------------------------- +// arrayalloc_init() + static void arrayalloc_init(ARAL *ar) { static netdata_mutex_t mutex = NETDATA_MUTEX_INITIALIZER; netdata_mutex_lock(&mutex); @@ -47,7 +76,7 @@ static void arrayalloc_init(ARAL *ar) { // we need to add a page pointer after the element // so, first align the element size to the pointer size - ar->internal.element_size = natural_alignment(ar->element_size, sizeof(uintptr_t)); + ar->internal.element_size = natural_alignment(ar->requested_element_size, sizeof(uintptr_t)); // then add the size of a pointer to it ar->internal.element_size += sizeof(uintptr_t); @@ -59,18 +88,18 @@ static void arrayalloc_init(ARAL *ar) { // and finally align it to the natural alignment ar->internal.element_size = natural_alignment(ar->internal.element_size, ARAL_NATURAL_ALIGNMENT); - // this is where we should write the pointer + // we write the page pointer just after each element ar->internal.page_ptr_offset = ar->internal.element_size - sizeof(uintptr_t); - if(ar->element_size + sizeof(uintptr_t) > ar->internal.element_size) + if(ar->requested_element_size + sizeof(uintptr_t) > ar->internal.element_size) fatal("ARRAYALLOC: failed to calculate properly page_ptr_offset: element size %zu, sizeof(uintptr_t) %zu, natural alignment %zu, final element size %zu, page_ptr_offset %zu", - ar->element_size, sizeof(uintptr_t), ARAL_NATURAL_ALIGNMENT, ar->internal.element_size, ar->internal.page_ptr_offset); + ar->requested_element_size, sizeof(uintptr_t), ARAL_NATURAL_ALIGNMENT, ar->internal.element_size, ar->internal.page_ptr_offset); //info("ARRAYALLOC: element size %zu, sizeof(uintptr_t) %zu, natural alignment %zu, final element size %zu, page_ptr_offset %zu", // ar->element_size, sizeof(uintptr_t), ARAL_NATURAL_ALIGNMENT, ar->internal.element_size, ar->internal.page_ptr_offset); - if (ar->elements < 10) - ar->elements = 10; + if (ar->initial_elements < 10) + ar->initial_elements = 10; ar->internal.mmap = (ar->use_mmap && ar->cache_dir && *ar->cache_dir) ? true : false; ar->internal.max_alloc_size = ar->internal.mmap ? ARAL_MAX_PAGE_SIZE_MMAP : ARAL_MAX_PAGE_SIZE_MALLOC; @@ -81,17 +110,20 @@ static void arrayalloc_init(ARAL *ar) { if(ar->internal.max_alloc_size % ar->internal.element_size) ar->internal.max_alloc_size -= ar->internal.max_alloc_size % ar->internal.element_size; - ar->internal.first_page = NULL; - ar->internal.last_page = NULL; + ar->internal.pages = NULL; ar->internal.allocation_multiplier = 1; ar->internal.file_number = 0; if(ar->internal.mmap) { - char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/array_alloc.mmap", *ar->cache_dir); - int r = mkdir(filename, 0775); + char directory_name[FILENAME_MAX + 1]; + snprintfz(directory_name, FILENAME_MAX, "%s/array_alloc.mmap", *ar->cache_dir); + int r = mkdir(directory_name, 0775); if (r != 0 && errno != EEXIST) - fatal("Cannot create directory '%s'", filename); + fatal("Cannot create directory '%s'", directory_name); + + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s.", ar->filename); + arrayalloc_delete_leftover_files(directory_name, filename); } ar->internal.initialized = true; @@ -100,8 +132,11 @@ static void arrayalloc_init(ARAL *ar) { netdata_mutex_unlock(&mutex); } +// ---------------------------------------------------------------------------- +// check a free slot + #ifdef NETDATA_INTERNAL_CHECKS -static inline void arrayalloc_free_checks(ARAL *ar, ARAL_FREE *fr) { +static inline void arrayalloc_free_validate_internal_check(ARAL *ar, ARAL_FREE *fr) { if(fr->size < ar->internal.element_size) fatal("ARRAYALLOC: free item of size %zu, less than the expected element size %zu", fr->size, ar->internal.element_size); @@ -109,65 +144,58 @@ static inline void arrayalloc_free_checks(ARAL *ar, ARAL_FREE *fr) { fatal("ARRAYALLOC: free item of size %zu is not multiple to element size %zu", fr->size, ar->internal.element_size); } #else -#define arrayalloc_free_checks(ar, fr) debug_dummy() +#define arrayalloc_free_validate_internal_check(ar, fr) debug_dummy() #endif -static inline void unlink_page(ARAL *ar, ARAL_PAGE *page) { - if(unlikely(!page)) return; - - if(page->next) - page->next->prev = page->prev; - - if(page->prev) - page->prev->next = page->next; - - if(page == ar->internal.first_page) - ar->internal.first_page = page->next; - - if(page == ar->internal.last_page) - ar->internal.last_page = page->prev; -} +// ---------------------------------------------------------------------------- +// find the page a pointer belongs to -static inline void link_page_first(ARAL *ar, ARAL_PAGE *page) { - page->prev = NULL; - page->next = ar->internal.first_page; - if(page->next) page->next->prev = page; +#ifdef NETDATA_INTERNAL_CHECKS +static inline ARAL_PAGE *find_page_with_allocation_internal_check(ARAL *ar, void *ptr) { + uintptr_t seeking = (uintptr_t)ptr; + ARAL_PAGE *page; - ar->internal.first_page = page; + for(page = ar->internal.pages; page ; page = page->next) { + if(unlikely(seeking >= (uintptr_t)page->data && seeking < (uintptr_t)page->data + page->size)) + break; + } - if(!ar->internal.last_page) - ar->internal.last_page = page; + return page; } +#endif -static inline void link_page_last(ARAL *ar, ARAL_PAGE *page) { - page->next = NULL; - page->prev = ar->internal.last_page; - if(page->prev) page->prev->next = page; - - ar->internal.last_page = page; - - if(!ar->internal.first_page) - ar->internal.first_page = page; -} +// ---------------------------------------------------------------------------- +// find a page with a free slot (there shouldn't be any) -static inline ARAL_PAGE *find_page_with_allocation(ARAL *ar, void *ptr) { - uintptr_t seeking = (uintptr_t)ptr; +#ifdef NETDATA_INTERNAL_CHECKS +static inline ARAL_PAGE *find_page_with_free_slots_internal_check(ARAL *ar) { ARAL_PAGE *page; - for(page = ar->internal.first_page; page ; page = page->next) { - if(unlikely(seeking >= (uintptr_t)page->data && seeking < (uintptr_t)page->data + page->size)) + for(page = ar->internal.pages; page ; page = page->next) { + if(page->free_list) break; + + internal_fatal(page->size - page->used_elements * ar->internal.element_size >= ar->internal.element_size, + "ARRAYALLOC: a page is marked full, but it is not!"); + + internal_fatal(page->size < page->used_elements * ar->internal.element_size, + "ARRAYALLOC: a page has been overflown!"); } return page; } +#endif -static void arrayalloc_increase(ARAL *ar) { +#ifdef NETDATA_TRACE_ALLOCATIONS +static void arrayalloc_add_page(ARAL *ar, const char *file, const char *function, size_t line) { +#else +static void arrayalloc_add_page(ARAL *ar) { +#endif if(unlikely(!ar->internal.initialized)) arrayalloc_init(ar); ARAL_PAGE *page = callocz(1, sizeof(ARAL_PAGE)); - page->size = ar->elements * ar->internal.element_size * ar->internal.allocation_multiplier; + page->size = ar->initial_elements * ar->internal.element_size * ar->internal.allocation_multiplier; if(page->size > ar->internal.max_alloc_size) page->size = ar->internal.max_alloc_size; else @@ -182,8 +210,13 @@ static void arrayalloc_increase(ARAL *ar) { if (unlikely(!page->data)) fatal("Cannot allocate arrayalloc buffer of size %zu on filename '%s'", page->size, page->filename); } - else + else { +#ifdef NETDATA_TRACE_ALLOCATIONS + page->data = mallocz_int(page->size, file, function, line); +#else page->data = mallocz(page->size); +#endif + } // link the free space to its page ARAL_FREE *fr = (ARAL_FREE *)page->data; @@ -193,9 +226,9 @@ static void arrayalloc_increase(ARAL *ar) { page->free_list = fr; // link the new page at the front of the list of pages - link_page_first(ar, page); + DOUBLE_LINKED_LIST_PREPEND_UNSAFE(ar->internal.pages, page, prev, next); - arrayalloc_free_checks(ar, fr); + arrayalloc_free_validate_internal_check(ar, fr); } static void arrayalloc_lock(ARAL *ar) { @@ -208,63 +241,92 @@ static void arrayalloc_unlock(ARAL *ar) { netdata_mutex_unlock(&ar->internal.mutex); } -ARAL *arrayalloc_create(size_t element_size, size_t elements, const char *filename, char **cache_dir) { +ARAL *arrayalloc_create(size_t element_size, size_t elements, const char *filename, char **cache_dir, bool mmap) { ARAL *ar = callocz(1, sizeof(ARAL)); - ar->element_size = element_size; - ar->elements = elements; + ar->requested_element_size = element_size; + ar->initial_elements = elements; ar->filename = filename; ar->cache_dir = cache_dir; + ar->use_mmap = mmap; return ar; } +#ifdef NETDATA_TRACE_ALLOCATIONS +void *arrayalloc_mallocz_int(ARAL *ar, const char *file, const char *function, size_t line) { +#else void *arrayalloc_mallocz(ARAL *ar) { +#endif + if(unlikely(!ar->internal.initialized)) + arrayalloc_init(ar); + arrayalloc_lock(ar); - if(unlikely(!ar->internal.first_page || !ar->internal.first_page->free_list)) - arrayalloc_increase(ar); + if(unlikely(!ar->internal.pages || !ar->internal.pages->free_list)) { + internal_fatal(find_page_with_free_slots_internal_check(ar) != NULL, + "ARRAYALLOC: first page does not have any free slots, but there is another that has!"); - ARAL_PAGE *page = ar->internal.first_page; - ARAL_FREE *fr = page->free_list; +#ifdef NETDATA_TRACE_ALLOCATIONS + arrayalloc_add_page(ar, file, function, line); +#else + arrayalloc_add_page(ar); +#endif + } + + ARAL_PAGE *page = ar->internal.pages; + ARAL_FREE *found_fr = page->free_list; - if(unlikely(!fr)) - fatal("ARRAYALLOC: free item cannot be NULL."); + internal_fatal(!found_fr, + "ARRAYALLOC: free item to use, cannot be NULL."); - if(unlikely(fr->size < ar->internal.element_size)) - fatal("ARRAYALLOC: free item size %zu is smaller than %zu", fr->size, ar->internal.element_size); + internal_fatal(found_fr->size < ar->internal.element_size, + "ARRAYALLOC: free item size %zu, cannot be smaller than %zu", + found_fr->size, ar->internal.element_size); - if(fr->size - ar->internal.element_size <= ar->internal.element_size) { - // we are done with this page - page->free_list = NULL; + if(unlikely(found_fr->size - ar->internal.element_size < ar->internal.element_size)) { + // we can use the entire free space entry - if(page != ar->internal.last_page) { - unlink_page(ar, page); - link_page_last(ar, page); + page->free_list = found_fr->next; + + if(unlikely(!page->free_list)) { + // we are done with this page + // move the full page last + // so that pages with free items remain first in the list + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(ar->internal.pages, page, prev, next); + DOUBLE_LINKED_LIST_APPEND_UNSAFE(ar->internal.pages, page, prev, next); } } else { - uint8_t *data = (uint8_t *)fr; - ARAL_FREE *fr2 = (ARAL_FREE *)&data[ar->internal.element_size]; - fr2->page = fr->page; - fr2->size = fr->size - ar->internal.element_size; - fr2->next = fr->next; - page->free_list = fr2; - - arrayalloc_free_checks(ar, fr2); + // we can split the free space entry + + uint8_t *data = (uint8_t *)found_fr; + ARAL_FREE *fr = (ARAL_FREE *)&data[ar->internal.element_size]; + fr->page = page; + fr->size = found_fr->size - ar->internal.element_size; + + // link the free slot first in the page + fr->next = found_fr->next; + page->free_list = fr; + + arrayalloc_free_validate_internal_check(ar, fr); } - fr->page->used_elements++; + page->used_elements++; // put the page pointer after the element - uint8_t *data = (uint8_t *)fr; + uint8_t *data = (uint8_t *)found_fr; ARAL_PAGE **page_ptr = (ARAL_PAGE **)&data[ar->internal.page_ptr_offset]; *page_ptr = page; arrayalloc_unlock(ar); - return (void *)fr; + return (void *)found_fr; } +#ifdef NETDATA_TRACE_ALLOCATIONS +void arrayalloc_freez_int(ARAL *ar, void *ptr, const char *file, const char *function, size_t line) { +#else void arrayalloc_freez(ARAL *ar, void *ptr) { - if(!ptr) return; +#endif + if(unlikely(!ptr)) return; arrayalloc_lock(ar); // get the page pointer @@ -282,10 +344,10 @@ void arrayalloc_freez(ARAL *ar, void *ptr) { #endif } -#ifdef NETDATA_INTERNAL_CHECKS +#ifdef NETDATA_ARRAYALLOC_INTERNAL_CHECKS { // find the page ptr belongs - ARAL_PAGE *page2 = find_page_with_allocation(ar, ptr); + ARAL_PAGE *page2 = find_page_with_allocation_internal_check(ar, ptr); if(unlikely(page != page2)) fatal("ARRAYALLOC: page pointers do not match!"); @@ -312,24 +374,116 @@ void arrayalloc_freez(ARAL *ar, void *ptr) { // if the page is empty, release it if(!page->used_elements) { - unlink_page(ar, page); + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(ar->internal.pages, page, prev, next); // free it if(ar->internal.mmap) { - munmap(page->data, page->size); + netdata_munmap(page->data, page->size); if (unlikely(unlink(page->filename) == 1)) error("Cannot delete file '%s'", page->filename); freez((void *)page->filename); } - else + else { +#ifdef NETDATA_TRACE_ALLOCATIONS + freez_int(page->data, file, function, line); +#else freez(page->data); +#endif + } freez(page); } - else if(page != ar->internal.first_page) { - unlink_page(ar, page); - link_page_first(ar, page); + else if(page != ar->internal.pages) { + // move the page with free item first + // so that the next allocation will use this page + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(ar->internal.pages, page, prev, next); + DOUBLE_LINKED_LIST_PREPEND_UNSAFE(ar->internal.pages, page, prev, next); } arrayalloc_unlock(ar); } + +int aral_unittest(size_t elements) { + char *cache_dir = "/tmp/"; + ARAL *ar = arrayalloc_create(20, 10, "test-aral", &cache_dir, false); + + void *pointers[elements]; + + for(size_t i = 0; i < elements ;i++) { + pointers[i] = arrayalloc_mallocz(ar); + } + + for(size_t div = 5; div >= 2 ;div--) { + for (size_t i = 0; i < elements / div; i++) { + arrayalloc_freez(ar, pointers[i]); + } + + for (size_t i = 0; i < elements / div; i++) { + pointers[i] = arrayalloc_mallocz(ar); + } + } + + for(size_t step = 50; step >= 10 ;step -= 10) { + for (size_t i = 0; i < elements; i += step) { + arrayalloc_freez(ar, pointers[i]); + } + + for (size_t i = 0; i < elements; i += step) { + pointers[i] = arrayalloc_mallocz(ar); + } + } + + for(size_t i = 0; i < elements ;i++) { + arrayalloc_freez(ar, pointers[i]); + } + + if(ar->internal.pages) { + fprintf(stderr, "ARAL leftovers detected (1)"); + return 1; + } + + size_t ops = 0; + size_t increment = elements / 10; + size_t allocated = 0; + for(size_t all = increment; all <= elements ; all += increment) { + + for(; allocated < all ; allocated++) { + pointers[allocated] = arrayalloc_mallocz(ar); + ops++; + } + + size_t to_free = now_realtime_usec() % all; + size_t free_list[to_free]; + for(size_t i = 0; i < to_free ;i++) { + size_t pos; + do { + pos = now_realtime_usec() % all; + } while(!pointers[pos]); + + arrayalloc_freez(ar, pointers[pos]); + pointers[pos] = NULL; + free_list[i] = pos; + ops++; + } + + for(size_t i = 0; i < to_free ;i++) { + size_t pos = free_list[i]; + pointers[pos] = arrayalloc_mallocz(ar); + ops++; + } + } + + for(size_t i = 0; i < allocated - 1 ;i++) { + arrayalloc_freez(ar, pointers[i]); + ops++; + } + + arrayalloc_freez(ar, pointers[allocated - 1]); + + if(ar->internal.pages) { + fprintf(stderr, "ARAL leftovers detected (2)"); + return 1; + } + + return 0; +} diff --git a/libnetdata/arrayalloc/arrayalloc.h b/libnetdata/arrayalloc/arrayalloc.h index e0e9e7f9f..cf80b73fd 100644 --- a/libnetdata/arrayalloc/arrayalloc.h +++ b/libnetdata/arrayalloc/arrayalloc.h @@ -5,8 +5,8 @@ #include "../libnetdata.h" typedef struct arrayalloc { - size_t element_size; - size_t elements; + size_t requested_element_size; + size_t initial_elements; const char *filename; char **cache_dir; bool use_mmap; @@ -23,13 +23,26 @@ typedef struct arrayalloc { size_t allocation_multiplier; size_t max_alloc_size; netdata_mutex_t mutex; - struct arrayalloc_page *first_page; - struct arrayalloc_page *last_page; + struct arrayalloc_page *pages; } internal; } ARAL; -extern ARAL *arrayalloc_create(size_t element_size, size_t elements, const char *filename, char **cache_dir); -extern void *arrayalloc_mallocz(ARAL *ar); -extern void arrayalloc_freez(ARAL *ar, void *ptr); +ARAL *arrayalloc_create(size_t element_size, size_t elements, const char *filename, char **cache_dir, bool mmap); +int aral_unittest(size_t elements); + +#ifdef NETDATA_TRACE_ALLOCATIONS + +#define arrayalloc_mallocz(ar) arrayalloc_mallocz_int(ar, __FILE__, __FUNCTION__, __LINE__) +#define arrayalloc_freez(ar, ptr) arrayalloc_freez_int(ar, ptr, __FILE__, __FUNCTION__, __LINE__) + +void *arrayalloc_mallocz_int(ARAL *ar, const char *file, const char *function, size_t line); +void arrayalloc_freez_int(ARAL *ar, void *ptr, const char *file, const char *function, size_t line); + +#else // NETDATA_TRACE_ALLOCATIONS + +void *arrayalloc_mallocz(ARAL *ar); +void arrayalloc_freez(ARAL *ar, void *ptr); + +#endif // NETDATA_TRACE_ALLOCATIONS #endif // ARRAYALLOC_H diff --git a/libnetdata/buffer/buffer.c b/libnetdata/buffer/buffer.c index 8a32184f6..d0940588f 100644 --- a/libnetdata/buffer/buffer.c +++ b/libnetdata/buffer/buffer.c @@ -136,6 +136,66 @@ void buffer_print_llu(BUFFER *wb, unsigned long long uvalue) wb->len += wstr - str; } +void buffer_print_ll(BUFFER *wb, long long value) +{ + buffer_need_bytes(wb, 50); + + if(value < 0) { + buffer_fast_strcat(wb, "-", 1); + value = -value; + } + + buffer_print_llu(wb, value); +} + +static unsigned char bits03_to_hex[16] = { + [0] = '0', + [1] = '1', + [2] = '2', + [3] = '3', + [4] = '4', + [5] = '5', + [6] = '6', + [7] = '7', + [8] = '8', + [9] = '9', + [10] = 'A', + [11] = 'B', + [12] = 'C', + [13] = 'D', + [14] = 'E', + [15] = 'F' +}; + +void buffer_print_llu_hex(BUFFER *wb, unsigned long long value) +{ + unsigned char buffer[sizeof(unsigned long long) * 2 + 2 + 1]; // 8 bytes * 2 + '0x' + '\0' + unsigned char *e = &buffer[sizeof(unsigned long long) * 2 + 2]; + unsigned char *p = e; + + *p-- = '\0'; + *p-- = bits03_to_hex[value & 0xF]; + value >>= 4; + if(value) { + *p-- = bits03_to_hex[value & 0xF]; + value >>= 4; + + while(value) { + *p-- = bits03_to_hex[value & 0xF]; + value >>= 4; + + if(value) { + *p-- = bits03_to_hex[value & 0xF]; + value >>= 4; + } + } + } + *p-- = 'x'; + *p = '0'; + + buffer_fast_strcat(wb, (char *)p, e - p); +} + void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) { if(unlikely(!txt || !*txt)) return; diff --git a/libnetdata/buffer/buffer.h b/libnetdata/buffer/buffer.h index 42425b4cb..ce6f52899 100644 --- a/libnetdata/buffer/buffer.h +++ b/libnetdata/buffer/buffer.h @@ -49,35 +49,37 @@ typedef struct web_buffer { #define buffer_no_cacheable(wb) do { (wb)->options |= WB_CONTENT_NO_CACHEABLE; if((wb)->options & WB_CONTENT_CACHEABLE) (wb)->options &= ~WB_CONTENT_CACHEABLE; (wb)->expires = 0; } while(0) #define buffer_strlen(wb) ((wb)->len) -extern const char *buffer_tostring(BUFFER *wb); +const char *buffer_tostring(BUFFER *wb); #define buffer_flush(wb) wb->buffer[(wb)->len = 0] = '\0' -extern void buffer_reset(BUFFER *wb); +void buffer_reset(BUFFER *wb); -extern void buffer_strcat(BUFFER *wb, const char *txt); -extern void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len); -extern void buffer_rrd_value(BUFFER *wb, NETDATA_DOUBLE value); +void buffer_strcat(BUFFER *wb, const char *txt); +void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len); +void buffer_rrd_value(BUFFER *wb, NETDATA_DOUBLE value); -extern void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds); -extern void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds); +void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds); +void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds); -extern BUFFER *buffer_create(size_t size); -extern void buffer_free(BUFFER *b); -extern void buffer_increase(BUFFER *b, size_t free_size_required); +BUFFER *buffer_create(size_t size); +void buffer_free(BUFFER *b); +void buffer_increase(BUFFER *b, size_t free_size_required); -extern void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) PRINTFLIKE(3, 4); -extern void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args); -extern void buffer_sprintf(BUFFER *wb, const char *fmt, ...) PRINTFLIKE(2,3); -extern void buffer_strcat_jsonescape(BUFFER *wb, const char *txt); -extern void buffer_strcat_htmlescape(BUFFER *wb, const char *txt); +void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) PRINTFLIKE(3, 4); +void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args); +void buffer_sprintf(BUFFER *wb, const char *fmt, ...) PRINTFLIKE(2,3); +void buffer_strcat_jsonescape(BUFFER *wb, const char *txt); +void buffer_strcat_htmlescape(BUFFER *wb, const char *txt); -extern void buffer_char_replace(BUFFER *wb, char from, char to); +void buffer_char_replace(BUFFER *wb, char from, char to); -extern char *print_number_lu_r(char *str, unsigned long uvalue); -extern char *print_number_llu_r(char *str, unsigned long long uvalue); -extern char *print_number_llu_r_smart(char *str, unsigned long long uvalue); +char *print_number_lu_r(char *str, unsigned long uvalue); +char *print_number_llu_r(char *str, unsigned long long uvalue); +char *print_number_llu_r_smart(char *str, unsigned long long uvalue); -extern void buffer_print_llu(BUFFER *wb, unsigned long long uvalue); +void buffer_print_llu(BUFFER *wb, unsigned long long uvalue); +void buffer_print_ll(BUFFER *wb, long long value); +void buffer_print_llu_hex(BUFFER *wb, unsigned long long value); static inline void buffer_need_bytes(BUFFER *buffer, size_t needed_free_size) { if(unlikely(buffer->size - buffer->len < needed_free_size)) diff --git a/libnetdata/circular_buffer/circular_buffer.c b/libnetdata/circular_buffer/circular_buffer.c index 998008db2..c791b420b 100644 --- a/libnetdata/circular_buffer/circular_buffer.c +++ b/libnetdata/circular_buffer/circular_buffer.c @@ -46,6 +46,11 @@ static int cbuffer_realloc_unsafe(struct circular_buffer *buf) { return 0; } +size_t cbuffer_available_size_unsafe(struct circular_buffer *buf) { + size_t len = (buf->write >= buf->read) ? (buf->write - buf->read) : (buf->size - buf->read + buf->write); + return buf->max_size - len; +} + int cbuffer_add_unsafe(struct circular_buffer *buf, const char *d, size_t d_len) { size_t len = (buf->write >= buf->read) ? (buf->write - buf->read) : (buf->size - buf->read + buf->write); while (d_len + len >= buf->size) { @@ -78,8 +83,14 @@ void cbuffer_remove_unsafe(struct circular_buffer *buf, size_t num) { size_t cbuffer_next_unsafe(struct circular_buffer *buf, char **start) { if (start != NULL) *start = buf->data + buf->read; + if (buf->read <= buf->write) { return buf->write - buf->read; // Includes empty case } return buf->size - buf->read; } + +void cbuffer_flush(struct circular_buffer*buf) { + buf->write = 0; + buf->read = 0; +} \ No newline at end of file diff --git a/libnetdata/circular_buffer/circular_buffer.h b/libnetdata/circular_buffer/circular_buffer.h index ba37e0ebf..8c42aa807 100644 --- a/libnetdata/circular_buffer/circular_buffer.h +++ b/libnetdata/circular_buffer/circular_buffer.h @@ -8,9 +8,12 @@ struct circular_buffer { char *data; }; -extern struct circular_buffer *cbuffer_new(size_t initial, size_t max); -extern void cbuffer_free(struct circular_buffer *buf); -extern int cbuffer_add_unsafe(struct circular_buffer *buf, const char *d, size_t d_len); -extern void cbuffer_remove_unsafe(struct circular_buffer *buf, size_t num); -extern size_t cbuffer_next_unsafe(struct circular_buffer *buf, char **start); +struct circular_buffer *cbuffer_new(size_t initial, size_t max); +void cbuffer_free(struct circular_buffer *buf); +int cbuffer_add_unsafe(struct circular_buffer *buf, const char *d, size_t d_len); +void cbuffer_remove_unsafe(struct circular_buffer *buf, size_t num); +size_t cbuffer_next_unsafe(struct circular_buffer *buf, char **start); +size_t cbuffer_available_size_unsafe(struct circular_buffer *buf); +void cbuffer_flush(struct circular_buffer*buf); + #endif diff --git a/libnetdata/clocks/clocks.c b/libnetdata/clocks/clocks.c index e4dca6c8b..cabc0000e 100644 --- a/libnetdata/clocks/clocks.c +++ b/libnetdata/clocks/clocks.c @@ -11,14 +11,14 @@ usec_t clock_monotonic_resolution = 1000; usec_t clock_realtime_resolution = 1000; #ifndef HAVE_CLOCK_GETTIME -inline int clock_gettime(clockid_t clk_id, struct timespec *ts) { +inline int clock_gettime(clockid_t clk_id __maybe_unused, struct timespec *ts) { struct timeval tv; if(unlikely(gettimeofday(&tv, NULL) == -1)) { error("gettimeofday() failed."); return -1; } ts->tv_sec = tv.tv_sec; - ts->tv_nsec = (tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC; + ts->tv_nsec = (long)((tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC); return 0; } #endif @@ -197,28 +197,28 @@ void sleep_to_absolute_time(usec_t usec) { if (!einval_printed) { einval_printed++; error( - "Invalid time given to clock_nanosleep(): clockid = %d, tv_sec = %ld, tv_nsec = %ld", + "Invalid time given to clock_nanosleep(): clockid = %d, tv_sec = %lld, tv_nsec = %ld", clock, - req.tv_sec, + (long long)req.tv_sec, req.tv_nsec); } } else if (ret == ENOTSUP) { if (!enotsup_printed) { enotsup_printed++; error( - "Invalid clock id given to clock_nanosleep(): clockid = %d, tv_sec = %ld, tv_nsec = %ld", + "Invalid clock id given to clock_nanosleep(): clockid = %d, tv_sec = %lld, tv_nsec = %ld", clock, - req.tv_sec, + (long long)req.tv_sec, req.tv_nsec); } } else { if (!eunknown_printed) { eunknown_printed++; error( - "Unknown return value %d from clock_nanosleep(): clockid = %d, tv_sec = %ld, tv_nsec = %ld", + "Unknown return value %d from clock_nanosleep(): clockid = %d, tv_sec = %lld, tv_nsec = %ld", ret, clock, - req.tv_sec, + (long long)req.tv_sec, req.tv_nsec); } } @@ -341,20 +341,21 @@ usec_t heartbeat_next(heartbeat_t *hb, usec_t tick) { void sleep_usec(usec_t usec) { // we expect microseconds (1.000.000 per second) // but timespec is nanoseconds (1.000.000.000 per second) - struct timespec rem, req = { + struct timespec rem = { 0, 0 }, req = { .tv_sec = (time_t) (usec / USEC_PER_SEC), .tv_nsec = (suseconds_t) ((usec % USEC_PER_SEC) * NSEC_PER_USEC) }; #ifdef __linux__ - while ((errno = clock_nanosleep(CLOCK_REALTIME, 0, &req, &rem)) != 0) { + while (clock_nanosleep(CLOCK_REALTIME, 0, &req, &rem) != 0) { #else - while ((errno = nanosleep(&req, &rem)) != 0) { + while (nanosleep(&req, &rem) != 0) { #endif - if (likely(errno == EINTR)) { - req.tv_sec = rem.tv_sec; - req.tv_nsec = rem.tv_nsec; - } else { + if (likely(errno == EINTR && (rem.tv_sec || rem.tv_nsec))) { + req = rem; + rem = (struct timespec){ 0, 0 }; + } + else { #ifdef __linux__ error("Cannot clock_nanosleep(CLOCK_REALTIME) for %llu microseconds.", usec); #else diff --git a/libnetdata/clocks/clocks.h b/libnetdata/clocks/clocks.h index 53c036ece..7738a2c8e 100644 --- a/libnetdata/clocks/clocks.h +++ b/libnetdata/clocks/clocks.h @@ -82,7 +82,7 @@ typedef struct heartbeat { * make systems without clock_gettime() support sensitive * to time jumps or hibernation/suspend side effects. */ -extern int clock_gettime(clockid_t clk_id, struct timespec *ts); +int clock_gettime(clockid_t clk_id, struct timespec *ts); #endif /* @@ -111,50 +111,50 @@ extern int clock_gettime(clockid_t clk_id, struct timespec *ts); * All now_*_usec() functions return the time in microseconds from the appropriate clock, or 0 on error. * */ -extern int now_realtime_timeval(struct timeval *tv); -extern time_t now_realtime_sec(void); -extern usec_t now_realtime_usec(void); +int now_realtime_timeval(struct timeval *tv); +time_t now_realtime_sec(void); +usec_t now_realtime_usec(void); -extern int now_monotonic_timeval(struct timeval *tv); -extern time_t now_monotonic_sec(void); -extern usec_t now_monotonic_usec(void); -extern int now_monotonic_high_precision_timeval(struct timeval *tv); -extern time_t now_monotonic_high_precision_sec(void); -extern usec_t now_monotonic_high_precision_usec(void); +int now_monotonic_timeval(struct timeval *tv); +time_t now_monotonic_sec(void); +usec_t now_monotonic_usec(void); +int now_monotonic_high_precision_timeval(struct timeval *tv); +time_t now_monotonic_high_precision_sec(void); +usec_t now_monotonic_high_precision_usec(void); -extern int now_boottime_timeval(struct timeval *tv); -extern time_t now_boottime_sec(void); -extern usec_t now_boottime_usec(void); +int now_boottime_timeval(struct timeval *tv); +time_t now_boottime_sec(void); +usec_t now_boottime_usec(void); -extern usec_t timeval_usec(struct timeval *tv); -extern msec_t timeval_msec(struct timeval *tv); +usec_t timeval_usec(struct timeval *tv); +msec_t timeval_msec(struct timeval *tv); -extern usec_t dt_usec(struct timeval *now, struct timeval *old); -extern susec_t dt_usec_signed(struct timeval *now, struct timeval *old); +usec_t dt_usec(struct timeval *now, struct timeval *old); +susec_t dt_usec_signed(struct timeval *now, struct timeval *old); -extern void heartbeat_init(heartbeat_t *hb); +void heartbeat_init(heartbeat_t *hb); /* Sleeps until next multiple of tick using monotonic clock. * Returns elapsed time in microseconds since previous heartbeat */ -extern usec_t heartbeat_next(heartbeat_t *hb, usec_t tick); +usec_t heartbeat_next(heartbeat_t *hb, usec_t tick); -extern void heartbeat_statistics(usec_t *min_ptr, usec_t *max_ptr, usec_t *average_ptr, size_t *count_ptr); +void heartbeat_statistics(usec_t *min_ptr, usec_t *max_ptr, usec_t *average_ptr, size_t *count_ptr); -extern void sleep_usec(usec_t usec); +void sleep_usec(usec_t usec); -extern void clocks_init(void); +void clocks_init(void); // lower level functions - avoid using directly -extern time_t now_sec(clockid_t clk_id); -extern usec_t now_usec(clockid_t clk_id); -extern int now_timeval(clockid_t clk_id, struct timeval *tv); +time_t now_sec(clockid_t clk_id); +usec_t now_usec(clockid_t clk_id); +int now_timeval(clockid_t clk_id, struct timeval *tv); -extern collected_number uptime_msec(char *filename); +collected_number uptime_msec(char *filename); extern usec_t clock_monotonic_resolution; extern usec_t clock_realtime_resolution; -extern void sleep_to_absolute_time(usec_t usec); +void sleep_to_absolute_time(usec_t usec); #endif /* NETDATA_CLOCKS_H */ diff --git a/libnetdata/config/appconfig.c b/libnetdata/config/appconfig.c index 1288366da..938c7dde2 100644 --- a/libnetdata/config/appconfig.c +++ b/libnetdata/config/appconfig.c @@ -32,8 +32,8 @@ _CONNECTOR_INSTANCE *add_connector_instance(struct section *connector, struct se local_ci = callocz(1, sizeof(struct _connector_instance)); local_ci->instance = instance; local_ci->connector = connector; - strncpy(local_ci->instance_name, instance->name, CONFIG_MAX_NAME); - strncpy(local_ci->connector_name, connector->name, CONFIG_MAX_NAME); + strncpyz(local_ci->instance_name, instance->name, CONFIG_MAX_NAME); + strncpyz(local_ci->connector_name, connector->name, CONFIG_MAX_NAME); local_ci->next = global_connector_instance; global_connector_instance = local_ci; @@ -473,7 +473,7 @@ NETDATA_DOUBLE appconfig_get_float(struct config *root, const char *section, con return str2ndd(s, NULL); } -static inline int appconfig_test_boolean_value(char *s) { +inline int appconfig_test_boolean_value(char *s) { if(!strcasecmp(s, "yes") || !strcasecmp(s, "true") || !strcasecmp(s, "on") || !strcasecmp(s, "auto") || !strcasecmp(s, "on demand")) return 1; @@ -686,14 +686,14 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used, cons int rc; rc = is_valid_connector(s, 0); if (likely(rc)) { - strncpy(working_connector, s, CONFIG_MAX_NAME); + strncpyz(working_connector, s, CONFIG_MAX_NAME); s = s + rc + 1; if (unlikely(!(*s))) { _connectors++; sprintf(buffer, "instance_%d", _connectors); s = buffer; } - strncpy(working_instance, s, CONFIG_MAX_NAME); + strncpyz(working_instance, s, CONFIG_MAX_NAME); working_connector_section = NULL; if (unlikely(appconfig_section_find(root, working_instance))) { error("Instance (%s) already exists", working_instance); diff --git a/libnetdata/config/appconfig.h b/libnetdata/config/appconfig.h index d72a3140e..2828e107a 100644 --- a/libnetdata/config/appconfig.h +++ b/libnetdata/config/appconfig.h @@ -163,41 +163,43 @@ struct config { #define CONFIG_BOOLEAN_AUTO 2 // enabled if it has useful info when enabled #endif -extern int appconfig_load(struct config *root, char *filename, int overwrite_used, const char *section_name); -extern void config_section_wrlock(struct section *co); -extern void config_section_unlock(struct section *co); +int appconfig_load(struct config *root, char *filename, int overwrite_used, const char *section_name); +void config_section_wrlock(struct section *co); +void config_section_unlock(struct section *co); -extern char *appconfig_get_by_section(struct section *co, const char *name, const char *default_value); -extern char *appconfig_get(struct config *root, const char *section, const char *name, const char *default_value); -extern long long appconfig_get_number(struct config *root, const char *section, const char *name, long long value); -extern NETDATA_DOUBLE appconfig_get_float(struct config *root, const char *section, const char *name, NETDATA_DOUBLE value); -extern int appconfig_get_boolean_by_section(struct section *co, const char *name, int value); -extern int appconfig_get_boolean(struct config *root, const char *section, const char *name, int value); -extern int appconfig_get_boolean_ondemand(struct config *root, const char *section, const char *name, int value); -extern int appconfig_get_duration(struct config *root, const char *section, const char *name, const char *value); +char *appconfig_get_by_section(struct section *co, const char *name, const char *default_value); +char *appconfig_get(struct config *root, const char *section, const char *name, const char *default_value); +long long appconfig_get_number(struct config *root, const char *section, const char *name, long long value); +NETDATA_DOUBLE appconfig_get_float(struct config *root, const char *section, const char *name, NETDATA_DOUBLE value); +int appconfig_get_boolean_by_section(struct section *co, const char *name, int value); +int appconfig_get_boolean(struct config *root, const char *section, const char *name, int value); +int appconfig_get_boolean_ondemand(struct config *root, const char *section, const char *name, int value); +int appconfig_get_duration(struct config *root, const char *section, const char *name, const char *value); -extern const char *appconfig_set(struct config *root, const char *section, const char *name, const char *value); -extern const char *appconfig_set_default(struct config *root, const char *section, const char *name, const char *value); -extern long long appconfig_set_number(struct config *root, const char *section, const char *name, long long value); -extern NETDATA_DOUBLE appconfig_set_float(struct config *root, const char *section, const char *name, NETDATA_DOUBLE value); -extern int appconfig_set_boolean(struct config *root, const char *section, const char *name, int value); +const char *appconfig_set(struct config *root, const char *section, const char *name, const char *value); +const char *appconfig_set_default(struct config *root, const char *section, const char *name, const char *value); +long long appconfig_set_number(struct config *root, const char *section, const char *name, long long value); +NETDATA_DOUBLE appconfig_set_float(struct config *root, const char *section, const char *name, NETDATA_DOUBLE value); +int appconfig_set_boolean(struct config *root, const char *section, const char *name, int value); -extern int appconfig_exists(struct config *root, const char *section, const char *name); -extern int appconfig_move(struct config *root, const char *section_old, const char *name_old, const char *section_new, const char *name_new); +int appconfig_exists(struct config *root, const char *section, const char *name); +int appconfig_move(struct config *root, const char *section_old, const char *name_old, const char *section_new, const char *name_new); -extern void appconfig_generate(struct config *root, BUFFER *wb, int only_changed); +void appconfig_generate(struct config *root, BUFFER *wb, int only_changed); -extern int appconfig_section_compare(void *a, void *b); +int appconfig_section_compare(void *a, void *b); -extern void appconfig_section_destroy_non_loaded(struct config *root, const char *section); -extern void appconfig_section_option_destroy_non_loaded(struct config *root, const char *section, const char *name); +void appconfig_section_destroy_non_loaded(struct config *root, const char *section); +void appconfig_section_option_destroy_non_loaded(struct config *root, const char *section, const char *name); -extern int config_parse_duration(const char* string, int* result); +int config_parse_duration(const char* string, int* result); -extern struct section *appconfig_get_section(struct config *root, const char *name); +struct section *appconfig_get_section(struct config *root, const char *name); -extern void appconfig_wrlock(struct config *root); -extern void appconfig_unlock(struct config *root); +void appconfig_wrlock(struct config *root); +void appconfig_unlock(struct config *root); + +int appconfig_test_boolean_value(char *s); struct connector_instance { char instance_name[CONFIG_MAX_NAME + 1]; @@ -212,6 +214,6 @@ typedef struct _connector_instance { struct _connector_instance *next; // Next instance } _CONNECTOR_INSTANCE; -extern _CONNECTOR_INSTANCE *add_connector_instance(struct section *connector, struct section *instance); +_CONNECTOR_INSTANCE *add_connector_instance(struct section *connector, struct section *instance); #endif /* NETDATA_CONFIG_H */ diff --git a/libnetdata/dictionary/README.md b/libnetdata/dictionary/README.md index 879c1bcc1..6d7e55392 100644 --- a/libnetdata/dictionary/README.md +++ b/libnetdata/dictionary/README.md @@ -18,52 +18,61 @@ Dictionaries provide an interface to: - **Delete** an item from the dictionary (provided its `name`) - **Traverse** the list of items in the dictionary -Dictionaries are **ordered**, meaning that the order they have been added is preserved while traversing them. The caller may reverse this order by passing the flag `DICTIONARY_FLAG_ADD_IN_FRONT` when creating the dictionary. +Dictionaries are **ordered**, meaning that the order they have been added, is preserved while traversing them. The caller may reverse this order by passing the flag `DICT_OPTION_ADD_IN_FRONT` when creating the dictionary. -Dictionaries guarantee **uniqueness** of all items added to them, meaning that only one item with a given name can exist in the dictionary at any given time. +Dictionaries guarantee **uniqueness** of all items added to them, meaning that only one item with a given `name` can exist in the dictionary at any given time. -Dictionaries are extremely fast in all operations. They are indexing the keys with `JudyHS` (or `AVL` when `libJudy` is not available) and they utilize a double-linked-list for the traversal operations. Deletion is the most expensive operation, usually somewhat slower than insertion. +Dictionaries are extremely fast in all operations. They are indexing the keys with `JudyHS` and they utilize a double-linked-list for the traversal operations. Deletion is the most expensive operation, usually somewhat slower than insertion. ## Memory management Dictionaries come with 2 memory management options: -- **Clone** (copy) the name and/or the value to memory allocated by the dictionary. -- **Link** the name and/or the value, without allocating any memory about them. +- **Clone** (copy) the `name` and/or the `value` to memory allocated by the dictionary. +- **Link** the `name` and/or the `value`, without allocating any memory about them. -In **clone** mode, the dictionary guarantees that all operations on the dictionary items will automatically take care of the memory used by the name and/or the value. In case the value is an object that needs to have user allocated memory, the following callback functions can be registered: +In **clone** mode, the dictionary guarantees that all operations on the dictionary items, will automatically take care of the memory used by the `name` and/or the `value`. In case the `value` is an object that needs to have user allocated memory, the following callback functions can be registered: - 1.`dictionary_register_insert_callback()` that will be called just after the insertion of an item to the dictionary, or after the replacement of the value of a dictionary item (but while the dictionary is write-locked - if locking is enabled). - 2. `dictionary_register_delete_callback()` that will be called just prior to the deletion of an item from the dictionary, or prior to the replacement of the value of a dictionary item (but while the dictionary is write-locked - if locking is enabled). - 3. `dictionary_register_conflict_callback()` that will be called when `DICTIONARY_FLAG_DONT_OVERWRITE_VALUE` is set and another value is attempted to be inserted for the same key. +1. `dictionary_register_insert_callback()` that can be called just after the insertion of an item to the dictionary, or after the replacement of the value of a dictionary item. +2. `dictionary_register_delete_callback()` that will be called just prior to the deletion of an item from the dictionary, or prior to the replacement of the value of a dictionary item. +3. `dictionary_register_conflict_callback()` that will be called when `DICT_OPTION_DONT_OVERWRITE_VALUE` is set, and another `value` is attempted to be inserted for the same key. +4. `dictionary_register_react_callback()` that will be called after the the `insert` and the `conflict` callbacks. The `conflict` callback is called while the dictionary hash table is available for other threads. -In **link** mode, the name and/or the value are just linked to the dictionary item, and it is the user's responsibility to free the memory they use after an item is deleted from the dictionary or when the dictionary is destroyed. +In **link** mode, the `name` and/or the `value` are just linked to the dictionary item, and it is the user's responsibility to free the memory they use after an item is deleted from the dictionary or when the dictionary is destroyed. By default, **clone** mode is used for both the name and the value. -To use **link** mode for names, add `DICTIONARY_FLAG_NAME_LINK_DONT_CLONE` to the flags when creating the dictionary. +To use **link** mode for names, add `DICT_OPTION_NAME_LINK_DONT_CLONE` to the flags when creating the dictionary. -To use **link** mode for values, add `DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE` to the flags when creating the dictionary. +To use **link** mode for values, add `DICT_OPTION_VALUE_LINK_DONT_CLONE` to the flags when creating the dictionary. ## Locks The dictionary allows both **single-threaded** operation (no locks - faster) and **multi-threaded** operation utilizing a read-write lock. -The default is **multi-threaded**. To enable **single-threaded** add `DICTIONARY_FLAG_SINGLE_THREADED` to the flags when creating the dictionary. +The default is **multi-threaded**. To enable **single-threaded** add `DICT_OPTION_SINGLE_THREADED` to the flags when creating the dictionary. + +When in **multi-threaded** mode, the dictionaries have 2 independent R/W locks. One for the linked list and one for the hash table (index). An insertion and a deletion will acquire both independently (one after another) for as long as they are needed, but a traversal may hold the the linked list for longer durations. The hash table (index) lock may be acquired while the linked list is acquired, but not the other way around (and the way the code is structured, it is not technically possible to hold and index lock and then lock the linked list one). + +These locks are R/W locks. They allow multiple readers, but only one writer. + +Unlike POSIX standards, the linked-list lock, allows one writer to lock it multiple times. This has been implemented in such a way, so that a traversal to the items of the dictionary in write-lock mode, allows the writing thread to call `dictionary_set()` or `dictionary_del()`, which alter the dictionary index and the linked list. Especially for the deletion of the currently working item, the dictionary support delayed removal, so it will remove it from the index immediately and mark it as deleted, so that it can be added to the dictionary again with a different value and the traversal will still proceed from the point it was. ## Hash table operations The dictionary supports the following operations supported by the hash table: - `dictionary_set()` to add an item to the dictionary, or change its value. -- `dictionary_get()` to get an item from the dictionary. +- `dictionary_get()` and `dictionary_get_and_acquire_item()` to get an item from the dictionary. - `dictionary_del()` to delete an item from the dictionary. +For all the calls, there are also `*_advanced()` versions of them, that support more parameters. Check the header file for more information about them. + ## Creation and destruction Use `dictionary_create()` to create a dictionary. -Use `dictionary_destroy()` to destroy a dictionary. When destroyed, a dictionary frees all the memory it has allocated on its own. This can be complemented by the registration of a deletion callback function that can be called upon deletion of each item in the dictionary, which may free additional resources. +Use `dictionary_destroy()` to destroy a dictionary. When destroyed, a dictionary frees all the memory it has allocated on its own. This can be complemented by the registration of a deletion callback function that can be called upon deletion of each item in the dictionary, which may free additional resources linked to it. ### dictionary_set() @@ -72,9 +81,7 @@ This call is used to: - **add** an item to the dictionary. - **reset** the value of an existing item in the dictionary. -If **resetting** is not desired, add `DICTIONARY_FLAG_DONT_OVERWRITE_VALUE` to the flags when creating the dictionary. In this case, `dictionary_set()` will return the value of the original item found in the dictionary instead of resetting it and the value passed to the call will be ignored. Optionally a conflict callback function can be registered, to manipulate (probably merge or extend) the original value, based on the new value attempted to be added to the dictionary. - -For **multi-threaded** operation, the `dictionary_set()` calls get an exclusive write lock on the dictionary. +If **resetting** is not desired, add `DICT_OPTION_DONT_OVERWRITE_VALUE` to the flags when creating the dictionary. In this case, `dictionary_set()` will return the value of the original item found in the dictionary instead of resetting it and the value passed to the call will be ignored. Optionally a conflict callback function can be registered, to manipulate (probably merge or extend) the original value, based on the new value attempted to be added to the dictionary. The format is: @@ -87,17 +94,15 @@ Where: * `dict` is a pointer to the dictionary previously created. * `name` is a pointer to a string to be used as the key of this item. The name must not be `NULL` and must not be an empty string `""`. * `value` is a pointer to the value associated with this item. In **clone** mode, if `value` is `NULL`, a new memory allocation will be made of `value_len` size and will be initialized to zero. -* `value_len` is the size of the `value` data. If `value_len` is zero, no allocation will be done and the dictionary item will permanently have the `NULL` value. - -> **IMPORTANT**
There is also an **unsafe** version (without locks) of this call. This is to be used when traversing the dictionary in write mode. It should never be called without an active lock on the dictionary, which can only be acquired while traversing. +* `value_len` is the size of the `value` data in bytes. If `value_len` is zero, no allocation will be done and the dictionary item will permanently have the `NULL` value. ### dictionary_get() -This call is used to get the value of an item, given its name. It utilizes the `JudyHS` hash table for making the lookup. +This call is used to get the `value` of an item, given its `name`. It utilizes the hash table (index) for making the lookup. -For **multi-threaded** operation, the `dictionary_get()` call gets a shared read lock on the dictionary. +For **multi-threaded** operation, the `dictionary_get()` call gets a shared read lock on the index lock (multiple readers are allowed). The linked-list lock is not used. -In clone mode, the value returned is not guaranteed to be valid, as any other thread may delete the item from the dictionary at any time. To ensure the value will be available, use `dictionary_get_and_acquire_item()`, which uses a reference counter to defer deletes until the item is released. +In clone mode, the value returned is not guaranteed to be valid, as any other thread may delete the item from the dictionary at any time. To ensure the value will be available, use `dictionary_get_and_acquire_item()`, which uses a reference counter to defer deletes until the item is released with `dictionary_acquired_item_release()`. The format is: @@ -110,16 +115,12 @@ Where: * `dict` is a pointer to the dictionary previously created. * `name` is a pointer to a string to be used as the key of this item. The name must not be `NULL` and must not be an empty string `""`. -> **IMPORTANT**
There is also an **unsafe** version (without locks) of this call. This is to be used when traversing the dictionary. It should never be called without an active lock on the dictionary, which can only be acquired while traversing. - ### dictionary_del() This call is used to delete an item from the dictionary, given its name. If there is a deletion callback registered to the dictionary (`dictionary_register_delete_callback()`), it is called prior to the actual deletion of the item. -For **multi-threaded** operation, the `dictionary_del()` calls get an exclusive write lock on the dictionary. - The format is: ```c @@ -131,11 +132,9 @@ Where: * `dict` is a pointer to the dictionary previously created. * `name` is a pointer to a string to be used as the key of this item. The name must not be `NULL` and must not be an empty string `""`. -> **IMPORTANT**
There is also an **unsafe** version (without locks) of this call. This is to be used when traversing the dictionary, to delete the current item. It should never be called without an active lock on the dictionary, which can only be acquired while traversing. - ### dictionary_get_and_acquire_item() -This call can be used the search and get a dictionary item, while ensuring that it will be available for use, until `dictionary_acquired_item_release()` is called. +This call can be used to search and acquire a dictionary item, while ensuring that it will be available for use, until `dictionary_acquired_item_release()` is called. This call **does not return the value** of the dictionary item. It returns an internal pointer to a structure that maintains the reference counter used to protect the actual value. To get the value of the item (the same value as returned by `dictionary_get()`), the function `dictionary_acquired_item_value()` has to be called. @@ -143,13 +142,13 @@ Example: ```c // create the dictionary -DICTIONARY *dict = dictionary_create(DICTIONARY_FLAGS_NONE); +DICTIONARY *dict = dictionary_create(DICT_OPTION_NONE); // add an item to it dictionary_set(dict, "name", "value", 6); // find the item we added and acquire it -void *item = dictionary_get_and_acquire_item(dict, "name"); +const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dict, "name"); // extract its value char *value = (char *)dictionary_acquired_item_value(dict, item); @@ -164,7 +163,7 @@ dictionary_acquired_item_release(dict, item); dictionary_destroy(dict); ``` -When items are acquired, a reference counter is maintained to keep track of how many users exist for it. If an item with a non-zero number of users is deleted, it is removed from the index, it can be added again to the index (without conflict), and although it exists in the linked-list, it is not offered during traversal. Garbage collection to actually delete the item happens every time a write-locked dictionary is unlocked (just before the unlock) and items are deleted only if no users are using them. +When items are acquired, a reference counter is maintained to keep track of how many users exist for it. If an item with a non-zero number of users is deleted, it is removed from the index, it can be added again to the index (without conflict), and although it exists in the linked-list, it is not offered during traversal. Garbage collection to actually delete the item happens every time another item is added or removed from the linked-list and items are deleted only if no users are using them. If any item is still acquired when the dictionary is destroyed, the destruction of the dictionary is also deferred until all the acquired items are released. When the dictionary is destroyed like that, all operations on the dictionary fail (traversals do not traverse, insertions do not insert, deletions do not delete, searches do not find any items, etc). Once the last item in the dictionary is released, the dictionary is automatically destroyed too. @@ -176,20 +175,16 @@ Dictionaries offer 3 ways to traverse the entire dictionary: - **sorted walkthrough**, which first sorts the dictionary and then call a callback function for every item. - **foreach**, a way to traverse the dictionary with a for-next loop. -All these methods are available in **read** or **write** mode. In **read** mode only lookups are allowed to the dictionary. In **write** lookups but also insertions and deletions are allowed. - -While traversing the dictionary with any of these methods, all calls to the dictionary have to use the `_unsafe` versions of the function calls, otherwise deadlocks may arise. - -> **IMPORTANT**
The dictionary itself does not check to ensure that a user is actually using the right lock mode (read or write) while traversing the dictionary for each of the unsafe calls. +All these methods are available in **read**, **write**, or **reentrant** mode. In **read** mode only lookups are allowed to the dictionary. In **write** lookups but also insertions and deletions are allowed, and in **reentrant** mode the dictionary is unlocked outside dictionary code. ### walkthrough (callback) There are 4 calls: -- `dictionary_walkthrough_read()` and `dictionary_sorted_walkthrough_read()` that acquire a shared read lock, and they call a callback function for every item of the dictionary. The callback function may use the unsafe versions of the `dictionary_get()` calls to lookup other items in the dictionary, but it should not attempt to add or remove items to/from the dictionary. -- `dictionary_walkthrough_write()` and `dictionary_sorted_walkthrough_write()` that acquire an exclusive write lock, and they call a callback function for every item of the dictionary. This is to be used when items need to be added to or removed from the dictionary. The `write` versions can be used to delete any or all the items from the dictionary, including the currently working one. For the `sorted` version, all items in the dictionary maintain a reference counter, so all deletions are deferred until the sorted walkthrough finishes.** +- `dictionary_walkthrough_read()` and `dictionary_sorted_walkthrough_read()` acquire a shared read lock on the linked-list, and they call a callback function for every item of the dictionary. +- `dictionary_walkthrough_write()` and `dictionary_sorted_walkthrough_write()` acquire a write lock on the linked-list, and they call a callback function for every item of the dictionary. This is to be used when items need to be added to or removed from the dictionary. The `write` versions can be used to delete any or all the items from the dictionary, including the currently working one. For the `sorted` version, all items in the dictionary maintain a reference counter, so all deletions are deferred until the sorted walkthrough finishes. -The non sorted versions traverse the items in the same order they have been added to the dictionary (or the reverse order if the flag `DICTIONARY_FLAG_ADD_IN_FRONT` is set during dictionary creation). The sorted versions sort alphabetically the items based on their name, and then they traverse them in the sorted order. +The non sorted versions traverse the items in the same order they have been added to the dictionary (or the reverse order if the flag `DICT_OPTION_ADD_IN_FRONT` is set during dictionary creation). The sorted versions sort alphabetically the items based on their name, and then they traverse them in the sorted order. The callback function returns an `int`. If this value is negative, traversal of the dictionary is stopped immediately and the negative value is returned to the caller. If the returned value of all callback calls is zero or positive, the walkthrough functions return the sum of the return values of all callbacks. So, if you are just interested to know how many items fall into some condition, write a callback function that returns 1 when the item satisfies that condition and 0 when it does not and the walkthrough function will return how many tested positive. @@ -198,48 +193,39 @@ The callback function returns an `int`. If this value is negative, traversal of The following is a snippet of such a loop: ```c -MY_ITEM *item; -dfe_start_read(dict, item) { - printf("hey, I got an item named '%s' with value ptr %08X", item_name, item); +MY_STRUCTURE *x; +dfe_start_read(dict, x) { + printf("hey, I got an item named '%s' with value ptr %08X", x_dfe.name, x); } -dfe_done(item); +dfe_done(x); ``` -The `item` parameter gives the name of the pointer to be used while iterating the items. Any name is accepted. +The `x` parameter gives the name of the pointer to be used while iterating the items. Any name is accepted. `x` points to the `value` of the item in the dictionary. -The `item_name` is a variable that is automatically created, by concatenating whatever is given as `item` and `_name`. So, if you call `dfe_start_read(dict, myvar)`, the name will be `myvar_name`. +The `x_dfe.name` is a variable that is automatically created, by concatenating whatever is given as `x` and `_dfe`. It is an object and it has a few members, including `x_dfe.counter` that counts the iterations made so far, `x_dfe.item` that provides the acquired item from the dictionary and which can be used to pass it over for further processing, etc. Check the header file for more info. So, if you call `dfe_start_read(dict, myvar)`, the name will be `myvar_dfe`. Both `dfe_start_read(dict, item)` and `dfe_done(item)` are together inside a `do { ... } while(0)` loop, so that the following will work: ```c MY_ITEM *item; -if(x = 1) +if(a = 1) // do { - dfe_start_read(dict, item) - printf("hey, I got an item named '%s' with value ptr %08X", item_name, item); - dfe_done(item); + dfe_start_read(dict, x) + printf("hey, I got an item named '%s' with value ptr %08X", x_dfe.name, x); + dfe_done(x); // } while(0); else something else; ``` -In the above, the `if(x == 1)` condition will work as expected. It will do the foreach loop when x is 1, otherwise it will run `something else`. +In the above, the `if(a == 1)` condition will work as expected. It will do the foreach loop when a is 1, otherwise it will run `something else`. There are 2 versions of `dfe_start`: -- `dfe_start_read()` that acquires a shared read lock to the dictionary. -- `dfe_start_write()` that acquires an exclusive write lock to the dictionary. +- `dfe_start_read()` that acquires a shared read linked-list lock to the dictionary. +- `dfe_start_write()` that acquires an exclusive write linked-list lock to the dictionary. -While in the loop, depending on the read or write versions of `dfe_start`, the caller may lookup or manipulate the dictionary using the unsafe functions. The rules are the same with the unsorted walkthrough callback functions. +While in the loop, depending on the read or write versions of `dfe_start`, the caller may lookup or manipulate the dictionary. The rules are the same with the unsorted walkthrough callback functions. PS: DFE is Dictionary For Each. - -## special multi-threaded lockless case - -Since the dictionary uses a hash table and a double linked list, if the contract between 2 threads is for one to use the hash table functions only (`set`, `get` - but no `del`) and the other to use the traversal ones only, the dictionary allows concurrent use without locks. - -This is currently used in statsd: - -- the data collection thread uses only `get` and `set`. It never uses `del`. New items are added at the front of the linked list (`DICTIONARY_FLAG_ADD_IN_FRONT`). -- the flushing thread is only traversing the dictionary up to the point it last traversed it (it uses a flag for that to know where it stopped last time). It never uses `get`, `set` or `del`. diff --git a/libnetdata/dictionary/dictionary.c b/libnetdata/dictionary/dictionary.c index c1325ecb5..f03da25ab 100644 --- a/libnetdata/dictionary/dictionary.c +++ b/libnetdata/dictionary/dictionary.c @@ -1,64 +1,137 @@ // SPDX-License-Identifier: GPL-3.0-or-later -// NOT TO BE USED BY USERS -#define DICTIONARY_FLAG_EXCLUSIVE_ACCESS (1 << 29) // there is only one thread accessing the dictionary -#define DICTIONARY_FLAG_DESTROYED (1 << 30) // this dictionary has been destroyed -#define DICTIONARY_FLAG_DEFER_ALL_DELETIONS (1 << 31) // defer all deletions of items in the dictionary - -// our reserved flags that cannot be set by users -#define DICTIONARY_FLAGS_RESERVED (DICTIONARY_FLAG_EXCLUSIVE_ACCESS|DICTIONARY_FLAG_DESTROYED|DICTIONARY_FLAG_DEFER_ALL_DELETIONS) - -typedef struct dictionary DICTIONARY; #define DICTIONARY_INTERNALS #include "../libnetdata.h" +#include -#ifndef ENABLE_DBENGINE -#define DICTIONARY_WITH_AVL -#warning Compiling DICTIONARY with an AVL index -#else -#define DICTIONARY_WITH_JUDYHS -#endif +// runtime flags of the dictionary - must be checked with atomics +typedef enum { + DICT_FLAG_NONE = 0, + DICT_FLAG_DESTROYED = (1 << 0), // this dictionary has been destroyed +} DICT_FLAGS; -#ifdef DICTIONARY_WITH_JUDYHS -#include -#endif +#define dict_flag_check(dict, flag) (__atomic_load_n(&((dict)->flags), __ATOMIC_SEQ_CST) & (flag)) +#define dict_flag_set(dict, flag) __atomic_or_fetch(&((dict)->flags), flag, __ATOMIC_SEQ_CST) +#define dict_flag_clear(dict, flag) __atomic_and_fetch(&((dict)->flags), ~(flag), __ATOMIC_SEQ_CST) + +// flags macros +#define is_dictionary_destroyed(dict) dict_flag_check(dict, DICT_FLAG_DESTROYED) + +// configuration options macros +#define is_dictionary_single_threaded(dict) ((dict)->options & DICT_OPTION_SINGLE_THREADED) +#define is_view_dictionary(dict) ((dict)->master) +#define is_master_dictionary(dict) (!is_view_dictionary(dict)) + +typedef enum item_options { + ITEM_OPTION_NONE = 0, + ITEM_OPTION_ALLOCATED_NAME = (1 << 0), // the name pointer is a STRING + + // IMPORTANT: This is 1-bit - to add more change ITEM_OPTIONS_BITS +} ITEM_OPTIONS; + +typedef enum item_flags { + ITEM_FLAG_NONE = 0, + ITEM_FLAG_DELETED = (1 << 0), // this item is marked deleted, so it is not available for traversal (deleted from the index too) + ITEM_FLAG_BEING_CREATED = (1 << 1), // this item is currently being created - this flag is removed when construction finishes + + // IMPORTANT: This is 8-bit +} ITEM_FLAGS; + +#define item_flag_check(item, flag) (__atomic_load_n(&((item)->flags), __ATOMIC_SEQ_CST) & (flag)) +#define item_flag_set(item, flag) __atomic_or_fetch(&((item)->flags), flag, __ATOMIC_SEQ_CST) +#define item_flag_clear(item, flag) __atomic_and_fetch(&((item)->flags), ~(flag), __ATOMIC_SEQ_CST) + +#define item_shared_flag_check(item, flag) (__atomic_load_n(&((item)->shared->flags), __ATOMIC_SEQ_CST) & (flag)) +#define item_shared_flag_set(item, flag) __atomic_or_fetch(&((item)->shared->flags), flag, __ATOMIC_SEQ_CST) +#define item_shared_flag_clear(item, flag) __atomic_and_fetch(&((item)->shared->flags), ~(flag), __ATOMIC_SEQ_CST) + +#define REFCOUNT_DELETING (-100) -typedef enum name_value_flags { - NAME_VALUE_FLAG_NONE = 0, - NAME_VALUE_FLAG_NAME_IS_ALLOCATED = (1 << 0), // the name pointer is a STRING - NAME_VALUE_FLAG_DELETED = (1 << 1), // this item is deleted, so it is not available for traversal - NAME_VALUE_FLAG_NEW_OR_UPDATED = (1 << 2), // this item is new or just updated (used by the react callback) +#define ITEM_FLAGS_TYPE uint8_t +#define KEY_LEN_TYPE uint32_t +#define VALUE_LEN_TYPE uint32_t + +#define ITEM_OPTIONS_BITS 1 +#define KEY_LEN_BITS ((sizeof(KEY_LEN_TYPE) * 8) - (sizeof(ITEM_FLAGS_TYPE) * 8) - ITEM_OPTIONS_BITS) +#define KEY_LEN_MAX ((1 << KEY_LEN_BITS) - 1) + +#define VALUE_LEN_BITS ((sizeof(VALUE_LEN_TYPE) * 8) - (sizeof(ITEM_FLAGS_TYPE) * 8)) +#define VALUE_LEN_MAX ((1 << VALUE_LEN_BITS) - 1) - // IMPORTANT: IF YOU ADD ANOTHER FLAG, YOU NEED TO ALLOCATE ANOTHER BIT TO FLAGS IN NAME_VALUE !!! -} NAME_VALUE_FLAGS; /* * Every item in the dictionary has the following structure. */ -typedef struct name_value { -#ifdef DICTIONARY_WITH_AVL - avl_t avl_node; -#endif +typedef int32_t REFCOUNT; + +typedef struct dictionary_item_shared { + void *value; // the value of the dictionary item + // the order of the following items is important! + // The total of their storage should be 64-bits + + REFCOUNT links; // how many links this item has + VALUE_LEN_TYPE value_len:VALUE_LEN_BITS; // the size of the value + ITEM_FLAGS_TYPE flags; // shared flags +} DICTIONARY_ITEM_SHARED; + +struct dictionary_item { #ifdef NETDATA_INTERNAL_CHECKS DICTIONARY *dict; + pid_t creator_pid; + pid_t deleter_pid; + pid_t ll_adder_pid; + pid_t ll_remover_pid; #endif - struct name_value *next; // a double linked list to allow fast insertions and deletions - struct name_value *prev; + DICTIONARY_ITEM_SHARED *shared; - uint32_t refcount; // the reference counter - uint32_t value_len:29; // the size of the value (assumed binary) - uint8_t flags:3; // the flags for this item + struct dictionary_item *next; // a double linked list to allow fast insertions and deletions + struct dictionary_item *prev; - void *value; // the value of the dictionary item union { - STRING *string_name; // the name of the dictionary item - char *caller_name; // the user supplied string pointer + STRING *string_name; // the name of the dictionary item + char *caller_name; // the user supplied string pointer +// void *key_ptr; // binary key pointer }; -} NAME_VALUE; + + // the order of the following items is important! + // The total of their storage should be 64-bits + + REFCOUNT refcount; // the private reference counter + + KEY_LEN_TYPE key_len:KEY_LEN_BITS; // the size of key indexed (for strings, including the null terminator) + // this is (2^23 - 1) = 8.388.607 bytes max key length. + + ITEM_OPTIONS options:ITEM_OPTIONS_BITS; // permanent configuration options + // (no atomic operations on this - they never change) + + ITEM_FLAGS_TYPE flags; // runtime changing flags for this item (atomic operations on this) + // cannot be a bit field because of atomics. +}; + +struct dictionary_hooks { + REFCOUNT links; + usec_t last_master_deletion_us; + + void (*ins_callback)(const DICTIONARY_ITEM *item, void *value, void *data); + void *ins_callback_data; + + bool (*conflict_callback)(const DICTIONARY_ITEM *item, void *old_value, void *new_value, void *data); + void *conflict_callback_data; + + void (*react_callback)(const DICTIONARY_ITEM *item, void *value, void *data); + void *react_callback_data; + + void (*del_callback)(const DICTIONARY_ITEM *item, void *value, void *data); + void *del_callback_data; +}; + +struct dictionary_stats dictionary_stats_category_other = { + .name = "other", +}; struct dictionary { #ifdef NETDATA_INTERNAL_CHECKS @@ -67,331 +140,685 @@ struct dictionary { size_t creation_line; #endif - DICTIONARY_FLAGS flags; // the flags of the dictionary + usec_t last_gc_run_us; + DICT_OPTIONS options; // the configuration flags of the dictionary (they never change - no atomics) + DICT_FLAGS flags; // run time flags for the dictionary (they change all the time - atomics needed) + + struct { // support for multiple indexing engines + Pvoid_t JudyHSArray; // the hash table + netdata_rwlock_t rwlock; // protect the index + } index; + + struct { + DICTIONARY_ITEM *list; // the double linked list of all items in the dictionary + netdata_rwlock_t rwlock; // protect the linked-list + pid_t writer_pid; // the gettid() of the writer + size_t writer_depth; // nesting of write locks + } items; + + struct dictionary_hooks *hooks; // pointer to external function callbacks to be called at certain points + struct dictionary_stats *stats; // statistics data, when DICT_OPTION_STATS is set + + DICTIONARY *master; // the master dictionary + DICTIONARY *next; // linked list for delayed destruction (garbage collection of whole dictionaries) + + size_t version; // the current version of the dictionary + // it is incremented when: + // - item added + // - item removed + // - item value reset + // - conflict callback returns true + // - function dictionary_version_increment() is called + + long int entries; // how many items are currently in the index (the linked list may have more) + long int referenced_items; // how many items of the dictionary are currently being used by 3rd parties + long int pending_deletion_items; // how many items of the dictionary have been deleted, but have not been removed yet + +#ifdef NETDATA_INTERNAL_CHECKS + netdata_mutex_t global_pointer_registry_mutex; + Pvoid_t global_pointer_registry; +#endif +}; + +// forward definitions of functions used in reverse order in the code +static void garbage_collect_pending_deletes(DICTIONARY *dict); +static inline void item_linked_list_remove(DICTIONARY *dict, DICTIONARY_ITEM *item); +static size_t dict_item_free_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item); +static inline const char *item_get_name(const DICTIONARY_ITEM *item); +static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, size_t name_len, void *item); +static void item_release(DICTIONARY *dict, DICTIONARY_ITEM *item); +static bool dict_item_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item); + +#define RC_ITEM_OK ( 0) +#define RC_ITEM_MARKED_FOR_DELETION (-1) // the item is marked for deletion +#define RC_ITEM_IS_CURRENTLY_BEING_DELETED (-2) // the item is currently being deleted +#define RC_ITEM_IS_CURRENTLY_BEING_CREATED (-3) // the item is currently being deleted +#define RC_ITEM_IS_REFERENCED (-4) // the item is currently referenced +#define item_check_and_acquire(dict, item) (item_check_and_acquire_advanced(dict, item, false) == RC_ITEM_OK) +static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *item, bool having_index_lock); +#define item_is_not_referenced_and_can_be_removed(dict, item) (item_is_not_referenced_and_can_be_removed_advanced(dict, item) == RC_ITEM_OK) +static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY *dict, DICTIONARY_ITEM *item); + +// ---------------------------------------------------------------------------- +// validate each pointer is indexed once - internal checks only - NAME_VALUE *first_item; // the double linked list base pointers - NAME_VALUE *last_item; +static inline void pointer_index_init(DICTIONARY *dict __maybe_unused) { +#ifdef NETDATA_INTERNAL_CHECKS + netdata_mutex_init(&dict->global_pointer_registry_mutex); +#else + ; +#endif +} -#ifdef DICTIONARY_WITH_AVL - avl_tree_type values_index; - NAME_VALUE *hash_base; - void *(*get_thread_static_name_value)(const char *name); +static inline void pointer_destroy_index(DICTIONARY *dict __maybe_unused) { +#ifdef NETDATA_INTERNAL_CHECKS + netdata_mutex_lock(&dict->global_pointer_registry_mutex); + JudyHSFreeArray(&dict->global_pointer_registry, PJE0); + netdata_mutex_unlock(&dict->global_pointer_registry_mutex); +#else + ; +#endif +} +static inline void pointer_add(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) { +#ifdef NETDATA_INTERNAL_CHECKS + netdata_mutex_lock(&dict->global_pointer_registry_mutex); + Pvoid_t *PValue = JudyHSIns(&dict->global_pointer_registry, &item, sizeof(void *), PJE0); + if(*PValue != NULL) + fatal("pointer already exists in registry"); + *PValue = item; + netdata_mutex_unlock(&dict->global_pointer_registry_mutex); +#else + ; #endif +} -#ifdef DICTIONARY_WITH_JUDYHS - Pvoid_t JudyHSArray; // the hash table +static inline void pointer_check(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) { +#ifdef NETDATA_INTERNAL_CHECKS + netdata_mutex_lock(&dict->global_pointer_registry_mutex); + Pvoid_t *PValue = JudyHSGet(dict->global_pointer_registry, &item, sizeof(void *)); + if(PValue == NULL) + fatal("pointer is not found in registry"); + netdata_mutex_unlock(&dict->global_pointer_registry_mutex); +#else + ; #endif +} - netdata_rwlock_t rwlock; // the r/w lock when DICTIONARY_FLAG_SINGLE_THREADED is not set +static inline void pointer_del(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) { +#ifdef NETDATA_INTERNAL_CHECKS + netdata_mutex_lock(&dict->global_pointer_registry_mutex); + int ret = JudyHSDel(&dict->global_pointer_registry, &item, sizeof(void *), PJE0); + if(!ret) + fatal("pointer to be deleted does not exist in registry"); + netdata_mutex_unlock(&dict->global_pointer_registry_mutex); +#else + ; +#endif +} - void (*ins_callback)(const char *name, void *value, void *data); - void *ins_callback_data; +// ---------------------------------------------------------------------------- +// memory statistics - void (*react_callback)(const char *name, void *value, void *data); - void *react_callback_data; +static inline void DICTIONARY_STATS_PLUS_MEMORY(DICTIONARY *dict, size_t key_size, size_t item_size, size_t value_size) { + if(key_size) + __atomic_fetch_add(&dict->stats->memory.indexed, (long)key_size, __ATOMIC_RELAXED); - void (*del_callback)(const char *name, void *value, void *data); - void *del_callback_data; + if(item_size) + __atomic_fetch_add(&dict->stats->memory.dict, (long)item_size, __ATOMIC_RELAXED); - void (*conflict_callback)(const char *name, void *old_value, void *new_value, void *data); - void *conflict_callback_data; + if(value_size) + __atomic_fetch_add(&dict->stats->memory.values, (long)value_size, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_MINUS_MEMORY(DICTIONARY *dict, size_t key_size, size_t item_size, size_t value_size) { + if(key_size) + __atomic_fetch_sub(&dict->stats->memory.indexed, (long)key_size, __ATOMIC_RELAXED); - size_t version; // the current version of the dictionary - size_t inserts; // how many index insertions have been performed - size_t deletes; // how many index deletions have been performed - size_t searches; // how many index searches have been performed - size_t resets; // how many times items have reset their values - size_t walkthroughs; // how many walkthroughs have been done - long int memory; // how much memory the dictionary has currently allocated - long int entries; // how many items are currently in the index (the linked list may have more) - long int referenced_items; // how many items of the dictionary are currently being used by 3rd parties - long int pending_deletion_items; // how many items of the dictionary have been deleted, but have not been removed yet - int readers; // how many readers are currently using the dictionary - int writers; // how many writers are currently using the dictionary - - size_t scratchpad_size; // the size of the scratchpad in bytes - uint8_t scratchpad[]; // variable size scratchpad requested by the caller -}; + if(item_size) + __atomic_fetch_sub(&dict->stats->memory.dict, (long)item_size, __ATOMIC_RELAXED); -static inline void linkedlist_namevalue_unlink_unsafe(DICTIONARY *dict, NAME_VALUE *nv); -static size_t namevalue_destroy_unsafe(DICTIONARY *dict, NAME_VALUE *nv); -static inline const char *namevalue_get_name(NAME_VALUE *nv); + if(value_size) + __atomic_fetch_sub(&dict->stats->memory.values, (long)value_size, __ATOMIC_RELAXED); +} // ---------------------------------------------------------------------------- // callbacks registration -void dictionary_register_insert_callback(DICTIONARY *dict, void (*ins_callback)(const char *name, void *value, void *data), void *data) { - dict->ins_callback = ins_callback; - dict->ins_callback_data = data; -} +static inline void dictionary_hooks_allocate(DICTIONARY *dict) { + if(dict->hooks) return; -void dictionary_register_delete_callback(DICTIONARY *dict, void (*del_callback)(const char *name, void *value, void *data), void *data) { - dict->del_callback = del_callback; - dict->del_callback_data = data; -} + dict->hooks = callocz(1, sizeof(struct dictionary_hooks)); + dict->hooks->links = 1; -void dictionary_register_conflict_callback(DICTIONARY *dict, void (*conflict_callback)(const char *name, void *old_value, void *new_value, void *data), void *data) { - dict->conflict_callback = conflict_callback; - dict->conflict_callback_data = data; + DICTIONARY_STATS_PLUS_MEMORY(dict, 0, sizeof(struct dictionary_hooks), 0); } -void dictionary_register_react_callback(DICTIONARY *dict, void (*react_callback)(const char *name, void *value, void *data), void *data) { - dict->react_callback = react_callback; - dict->react_callback_data = data; +static inline size_t dictionary_hooks_free(DICTIONARY *dict) { + if(!dict->hooks) return 0; + + REFCOUNT links = __atomic_sub_fetch(&dict->hooks->links, 1, __ATOMIC_SEQ_CST); + if(links == 0) { + freez(dict->hooks); + dict->hooks = NULL; + + DICTIONARY_STATS_MINUS_MEMORY(dict, 0, sizeof(struct dictionary_hooks), 0); + return sizeof(struct dictionary_hooks); + } + + return 0; } -// ---------------------------------------------------------------------------- -// dictionary statistics maintenance +void dictionary_register_insert_callback(DICTIONARY *dict, void (*ins_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data) { + if(unlikely(is_view_dictionary(dict))) + fatal("DICTIONARY: called %s() on a view.", __FUNCTION__ ); -long int dictionary_stats_allocated_memory(DICTIONARY *dict) { - return dict->memory; + dictionary_hooks_allocate(dict); + dict->hooks->ins_callback = ins_callback; + dict->hooks->ins_callback_data = data; } -long int dictionary_stats_entries(DICTIONARY *dict) { - return dict->entries; + +void dictionary_register_conflict_callback(DICTIONARY *dict, bool (*conflict_callback)(const DICTIONARY_ITEM *item, void *old_value, void *new_value, void *data), void *data) { + if(unlikely(is_view_dictionary(dict))) + fatal("DICTIONARY: called %s() on a view.", __FUNCTION__ ); + + internal_error(!(dict->options & DICT_OPTION_DONT_OVERWRITE_VALUE), "DICTIONARY: registering conflict callback without DICT_OPTION_DONT_OVERWRITE_VALUE"); + dict->options |= DICT_OPTION_DONT_OVERWRITE_VALUE; + + dictionary_hooks_allocate(dict); + dict->hooks->conflict_callback = conflict_callback; + dict->hooks->conflict_callback_data = data; } -size_t dictionary_stats_version(DICTIONARY *dict) { - return dict->version; + +void dictionary_register_react_callback(DICTIONARY *dict, void (*react_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data) { + if(unlikely(is_view_dictionary(dict))) + fatal("DICTIONARY: called %s() on a view.", __FUNCTION__ ); + + dictionary_hooks_allocate(dict); + dict->hooks->react_callback = react_callback; + dict->hooks->react_callback_data = data; } -size_t dictionary_stats_searches(DICTIONARY *dict) { - return dict->searches; + +void dictionary_register_delete_callback(DICTIONARY *dict, void (*del_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data) { + if(unlikely(is_view_dictionary(dict))) + fatal("DICTIONARY: called %s() on a view.", __FUNCTION__ ); + + dictionary_hooks_allocate(dict); + dict->hooks->del_callback = del_callback; + dict->hooks->del_callback_data = data; } -size_t dictionary_stats_inserts(DICTIONARY *dict) { - return dict->inserts; + +// ---------------------------------------------------------------------------- +// dictionary statistics API + +size_t dictionary_version(DICTIONARY *dict) { + if(unlikely(!dict)) return 0; + + // this is required for views to return the right number + garbage_collect_pending_deletes(dict); + + return __atomic_load_n(&dict->version, __ATOMIC_SEQ_CST); } -size_t dictionary_stats_deletes(DICTIONARY *dict) { - return dict->deletes; +size_t dictionary_entries(DICTIONARY *dict) { + if(unlikely(!dict)) return 0; + + // this is required for views to return the right number + garbage_collect_pending_deletes(dict); + + long int entries = __atomic_load_n(&dict->entries, __ATOMIC_SEQ_CST); + if(entries < 0) + fatal("DICTIONARY: entries is negative: %ld", entries); + + return entries; } -size_t dictionary_stats_resets(DICTIONARY *dict) { - return dict->resets; +size_t dictionary_referenced_items(DICTIONARY *dict) { + if(unlikely(!dict)) return 0; + + long int referenced_items = __atomic_load_n(&dict->referenced_items, __ATOMIC_SEQ_CST); + if(referenced_items < 0) + fatal("DICTIONARY: referenced items is negative: %ld", referenced_items); + + return referenced_items; } -size_t dictionary_stats_walkthroughs(DICTIONARY *dict) { - return dict->walkthroughs; + +long int dictionary_stats_for_registry(DICTIONARY *dict) { + if(unlikely(!dict)) return 0; + return (dict->stats->memory.indexed + dict->stats->memory.dict); } -size_t dictionary_stats_referenced_items(DICTIONARY *dict) { - return __atomic_load_n(&dict->referenced_items, __ATOMIC_SEQ_CST); +void dictionary_version_increment(DICTIONARY *dict) { + __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST); } +// ---------------------------------------------------------------------------- +// internal statistics API + static inline void DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) { - if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) { - dict->searches++; - } - else { - __atomic_fetch_add(&dict->searches, 1, __ATOMIC_RELAXED); - } + __atomic_fetch_add(&dict->stats->ops.searches, 1, __ATOMIC_RELAXED); } -static inline void DICTIONARY_STATS_ENTRIES_PLUS1(DICTIONARY *dict, size_t size) { - if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) { +static inline void DICTIONARY_ENTRIES_PLUS1(DICTIONARY *dict) { + // statistics + __atomic_fetch_add(&dict->stats->items.entries, 1, __ATOMIC_RELAXED); + __atomic_fetch_add(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED); + __atomic_fetch_add(&dict->stats->ops.inserts, 1, __ATOMIC_RELAXED); + + if(unlikely(is_dictionary_single_threaded(dict))) { dict->version++; - dict->inserts++; dict->entries++; - dict->memory += (long)size; + dict->referenced_items++; + } else { __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST); - __atomic_fetch_add(&dict->inserts, 1, __ATOMIC_RELAXED); - __atomic_fetch_add(&dict->entries, 1, __ATOMIC_RELAXED); - __atomic_fetch_add(&dict->memory, (long)size, __ATOMIC_RELAXED); + __atomic_fetch_add(&dict->entries, 1, __ATOMIC_SEQ_CST); + __atomic_fetch_add(&dict->referenced_items, 1, __ATOMIC_SEQ_CST); } } -static inline void DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict) { - if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) { +static inline void DICTIONARY_ENTRIES_MINUS1(DICTIONARY *dict) { + // statistics + __atomic_fetch_add(&dict->stats->ops.deletes, 1, __ATOMIC_RELAXED); + __atomic_fetch_sub(&dict->stats->items.entries, 1, __ATOMIC_RELAXED); + + size_t entries; (void)entries; + if(unlikely(is_dictionary_single_threaded(dict))) { dict->version++; - dict->deletes++; - dict->entries--; + entries = dict->entries++; } else { __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST); - __atomic_fetch_add(&dict->deletes, 1, __ATOMIC_RELAXED); - __atomic_fetch_sub(&dict->entries, 1, __ATOMIC_RELAXED); - } -} -static inline void DICTIONARY_STATS_ENTRIES_MINUS_MEMORY(DICTIONARY *dict, size_t size) { - if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) { - dict->memory -= (long)size; - } - else { - __atomic_fetch_sub(&dict->memory, (long)size, __ATOMIC_RELAXED); + entries = __atomic_fetch_sub(&dict->entries, 1, __ATOMIC_SEQ_CST); } + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(entries == 0)) + fatal("DICT: negative number of entries in dictionary created from %s() (%zu@%s)", + dict->creation_function, + dict->creation_line, + dict->creation_file); +#endif } -static inline void DICTIONARY_STATS_VALUE_RESETS_PLUS1(DICTIONARY *dict, size_t oldsize, size_t newsize) { - if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) { +static inline void DICTIONARY_VALUE_RESETS_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->ops.resets, 1, __ATOMIC_RELAXED); + + if(unlikely(is_dictionary_single_threaded(dict))) dict->version++; - dict->resets++; - dict->memory += (long)newsize; - dict->memory -= (long)oldsize; - } - else { + else __atomic_fetch_add(&dict->version, 1, __ATOMIC_SEQ_CST); - __atomic_fetch_add(&dict->resets, 1, __ATOMIC_RELAXED); - __atomic_fetch_add(&dict->memory, (long)newsize, __ATOMIC_RELAXED); - __atomic_fetch_sub(&dict->memory, (long)oldsize, __ATOMIC_RELAXED); - } } - +static inline void DICTIONARY_STATS_TRAVERSALS_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->ops.traversals, 1, __ATOMIC_RELAXED); +} static inline void DICTIONARY_STATS_WALKTHROUGHS_PLUS1(DICTIONARY *dict) { - if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) { - dict->walkthroughs++; - } - else { - __atomic_fetch_add(&dict->walkthroughs, 1, __ATOMIC_RELAXED); - } + __atomic_fetch_add(&dict->stats->ops.walkthroughs, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_CHECK_SPINS_PLUS(DICTIONARY *dict, size_t count) { + __atomic_fetch_add(&dict->stats->spin_locks.use_spins, count, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_INSERT_SPINS_PLUS(DICTIONARY *dict, size_t count) { + __atomic_fetch_add(&dict->stats->spin_locks.insert_spins, count, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_DELETE_SPINS_PLUS(DICTIONARY *dict, size_t count) { + __atomic_fetch_add(&dict->stats->spin_locks.delete_spins, count, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_SEARCH_IGNORES_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->spin_locks.search_spins, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->callbacks.inserts, 1, __ATOMIC_RELAXED); } +static inline void DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->callbacks.conflicts, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->callbacks.reacts, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->callbacks.deletes, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_GARBAGE_COLLECTIONS_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->ops.garbage_collections, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_DICT_CREATIONS_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->dictionaries.active, 1, __ATOMIC_RELAXED); + __atomic_fetch_add(&dict->stats->ops.creations, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_DICT_DESTRUCTIONS_PLUS1(DICTIONARY *dict) { + __atomic_fetch_sub(&dict->stats->dictionaries.active, 1, __ATOMIC_RELAXED); + __atomic_fetch_add(&dict->stats->ops.destructions, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_DICT_DESTROY_QUEUED_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->dictionaries.deleted, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_DICT_DESTROY_QUEUED_MINUS1(DICTIONARY *dict) { + __atomic_fetch_sub(&dict->stats->dictionaries.deleted, 1, __ATOMIC_RELAXED); +} +static inline void DICTIONARY_STATS_DICT_FLUSHES_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->ops.flushes, 1, __ATOMIC_RELAXED); +} + +static inline long int DICTIONARY_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED); + + if(unlikely(is_dictionary_single_threaded(dict))) + return ++dict->referenced_items; + else + return __atomic_add_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST); +} + +static inline long int DICTIONARY_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) { + __atomic_fetch_sub(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED); + + long int referenced_items; + if(unlikely(is_dictionary_single_threaded(dict))) + referenced_items = --dict->referenced_items; + else + referenced_items = __atomic_sub_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST); -static inline size_t DICTIONARY_STATS_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) { - return __atomic_add_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST); +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(referenced_items < 0)) + fatal("DICT: negative number of referenced items (%ld) in dictionary created from %s() (%zu@%s)", + referenced_items, + dict->creation_function, + dict->creation_line, + dict->creation_file); +#endif + + return referenced_items; } -static inline size_t DICTIONARY_STATS_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) { - return __atomic_sub_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST); +static inline long int DICTIONARY_PENDING_DELETES_PLUS1(DICTIONARY *dict) { + __atomic_fetch_add(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELAXED); + + if(unlikely(is_dictionary_single_threaded(dict))) + return ++dict->pending_deletion_items; + else + return __atomic_add_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST); } -static inline size_t DICTIONARY_STATS_PENDING_DELETES_PLUS1(DICTIONARY *dict) { - return __atomic_add_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST); +static inline long int DICTIONARY_PENDING_DELETES_MINUS1(DICTIONARY *dict) { + __atomic_fetch_sub(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELAXED); + + if(unlikely(is_dictionary_single_threaded(dict))) + return --dict->pending_deletion_items; + else + return __atomic_sub_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST); } -static inline size_t DICTIONARY_STATS_PENDING_DELETES_MINUS1(DICTIONARY *dict) { - return __atomic_sub_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST); +static inline long int DICTIONARY_PENDING_DELETES_GET(DICTIONARY *dict) { + if(unlikely(is_dictionary_single_threaded(dict))) + return dict->pending_deletion_items; + else + return __atomic_load_n(&dict->pending_deletion_items, __ATOMIC_SEQ_CST); } -static inline size_t DICTIONARY_STATS_PENDING_DELETES_GET(DICTIONARY *dict) { - return __atomic_load_n(&dict->pending_deletion_items, __ATOMIC_SEQ_CST); +static inline REFCOUNT DICTIONARY_ITEM_REFCOUNT_GET(DICTIONARY *dict, DICTIONARY_ITEM *item) { + if(unlikely(dict && is_dictionary_single_threaded(dict))) // this is an exception, dict can be null + return item->refcount; + else + return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_SEQ_CST); } -static inline int DICTIONARY_NAME_VALUE_REFCOUNT_GET(NAME_VALUE *nv) { - return __atomic_load_n(&nv->refcount, __ATOMIC_SEQ_CST); +static inline REFCOUNT DICTIONARY_ITEM_REFCOUNT_GET_SOLE(DICTIONARY_ITEM *item) { + return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_SEQ_CST); } // ---------------------------------------------------------------------------- -// garbage collector -// it is called every time someone gets a write lock to the dictionary +// callbacks execution + +static void dictionary_execute_insert_callback(DICTIONARY *dict, DICTIONARY_ITEM *item, void *constructor_data) { + if(likely(!dict->hooks || !dict->hooks->ins_callback)) + return; -static void garbage_collect_pending_deletes_unsafe(DICTIONARY *dict) { - if(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS)) return; + if(unlikely(is_view_dictionary(dict))) + fatal("DICTIONARY: called %s() on a view.", __FUNCTION__ ); - if(likely(!DICTIONARY_STATS_PENDING_DELETES_GET(dict))) return; + internal_error(false, + "DICTIONARY: Running insert callback on item '%s' of dictionary created from %s() %zu@%s.", + item_get_name(item), + dict->creation_function, + dict->creation_line, + dict->creation_file); - NAME_VALUE *nv = dict->first_item; - while(nv) { - if((nv->flags & NAME_VALUE_FLAG_DELETED) && DICTIONARY_NAME_VALUE_REFCOUNT_GET(nv) == 0) { - NAME_VALUE *nv_next = nv->next; + DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(dict); + dict->hooks->ins_callback(item, item->shared->value, constructor_data?constructor_data:dict->hooks->ins_callback_data); +} - linkedlist_namevalue_unlink_unsafe(dict, nv); - namevalue_destroy_unsafe(dict, nv); +static bool dictionary_execute_conflict_callback(DICTIONARY *dict, DICTIONARY_ITEM *item, void *new_value, void *constructor_data) { + if(likely(!dict->hooks || !dict->hooks->conflict_callback)) + return false; - size_t pending = DICTIONARY_STATS_PENDING_DELETES_MINUS1(dict); - if(!pending) break; + if(unlikely(is_view_dictionary(dict))) + fatal("DICTIONARY: called %s() on a view.", __FUNCTION__ ); - nv = nv_next; - } - else - nv = nv->next; - } + internal_error(false, + "DICTIONARY: Running conflict callback on item '%s' of dictionary created from %s() %zu@%s.", + item_get_name(item), + dict->creation_function, + dict->creation_line, + dict->creation_file); + + DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(dict); + return dict->hooks->conflict_callback( + item, item->shared->value, new_value, + constructor_data ? constructor_data : dict->hooks->conflict_callback_data); } -// ---------------------------------------------------------------------------- -// dictionary locks +static void dictionary_execute_react_callback(DICTIONARY *dict, DICTIONARY_ITEM *item, void *constructor_data) { + if(likely(!dict->hooks || !dict->hooks->react_callback)) + return; + + if(unlikely(is_view_dictionary(dict))) + fatal("DICTIONARY: called %s() on a view.", __FUNCTION__ ); + + internal_error(false, + "DICTIONARY: Running react callback on item '%s' of dictionary created from %s() %zu@%s.", + item_get_name(item), + dict->creation_function, + dict->creation_line, + dict->creation_file); + + DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(dict); + dict->hooks->react_callback(item, item->shared->value, + constructor_data?constructor_data:dict->hooks->react_callback_data); +} -static inline size_t dictionary_lock_init(DICTIONARY *dict) { - if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) { - netdata_rwlock_init(&dict->rwlock); +static void dictionary_execute_delete_callback(DICTIONARY *dict, DICTIONARY_ITEM *item) { + if(likely(!dict->hooks || !dict->hooks->del_callback)) + return; + + // We may execute the delete callback on items deleted from a view, + // because we may have references to it, after the master is gone + // so, the shared structure will remain until the last reference is released. + + internal_error(false, + "DICTIONARY: Running delete callback on item '%s' of dictionary created from %s() %zu@%s.", + item_get_name(item), + dict->creation_function, + dict->creation_line, + dict->creation_file); + + DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(dict); + dict->hooks->del_callback(item, item->shared->value, dict->hooks->del_callback_data); +} - if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) - dict->flags &= ~DICTIONARY_FLAG_EXCLUSIVE_ACCESS; +// ---------------------------------------------------------------------------- +// dictionary locks +static inline size_t dictionary_locks_init(DICTIONARY *dict) { + if(likely(!is_dictionary_single_threaded(dict))) { + netdata_rwlock_init(&dict->index.rwlock); + netdata_rwlock_init(&dict->items.rwlock); return 0; } - - // we are single threaded - dict->flags |= DICTIONARY_FLAG_EXCLUSIVE_ACCESS; return 0; } -static inline size_t dictionary_lock_free(DICTIONARY *dict) { - if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) { - netdata_rwlock_destroy(&dict->rwlock); +static inline size_t dictionary_locks_destroy(DICTIONARY *dict) { + if(likely(!is_dictionary_single_threaded(dict))) { + netdata_rwlock_destroy(&dict->index.rwlock); + netdata_rwlock_destroy(&dict->items.rwlock); return 0; } return 0; } -static void dictionary_lock(DICTIONARY *dict, char rw) { - if(rw == 'u' || rw == 'U') return; +static inline void ll_recursive_lock_set_thread_as_writer(DICTIONARY *dict) { + pid_t expected = 0, desired = gettid(); + if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) + fatal("DICTIONARY: Cannot set thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_SEQ_CST)); +} - if(rw == 'r' || rw == 'R') { - // read lock - __atomic_add_fetch(&dict->readers, 1, __ATOMIC_RELAXED); - } - else { - // write lock - __atomic_add_fetch(&dict->writers, 1, __ATOMIC_RELAXED); - } +static inline void ll_recursive_unlock_unset_thread_writer(DICTIONARY *dict) { + pid_t expected = gettid(), desired = 0; + if(!__atomic_compare_exchange_n(&dict->items.writer_pid, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) + fatal("DICTIONARY: Cannot unset thread %d as exclusive writer, expected %d, desired %d, found %d.", gettid(), expected, desired, __atomic_load_n(&dict->items.writer_pid, __ATOMIC_SEQ_CST)); +} + +static inline bool ll_recursive_lock_is_thread_the_writer(DICTIONARY *dict) { + pid_t tid = gettid(); + return tid > 0 && tid == __atomic_load_n(&dict->items.writer_pid, __ATOMIC_SEQ_CST); +} - if(likely(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED)) +static void ll_recursive_lock(DICTIONARY *dict, char rw) { + if(unlikely(is_dictionary_single_threaded(dict))) return; - if(rw == 'r' || rw == 'R') { - // read lock - netdata_rwlock_rdlock(&dict->rwlock); + if(ll_recursive_lock_is_thread_the_writer(dict)) { + dict->items.writer_depth++; + return; + } - if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) { - internal_error(true, "DICTIONARY: left-over exclusive access to dictionary created by %s (%zu@%s) found", dict->creation_function, dict->creation_line, dict->creation_file); - dict->flags &= ~DICTIONARY_FLAG_EXCLUSIVE_ACCESS; - } + if(rw == DICTIONARY_LOCK_READ || rw == DICTIONARY_LOCK_REENTRANT || rw == 'R') { + // read lock + netdata_rwlock_rdlock(&dict->items.rwlock); } else { // write lock - netdata_rwlock_wrlock(&dict->rwlock); - - dict->flags |= DICTIONARY_FLAG_EXCLUSIVE_ACCESS; + netdata_rwlock_wrlock(&dict->items.rwlock); + ll_recursive_lock_set_thread_as_writer(dict); } } -static void dictionary_unlock(DICTIONARY *dict, char rw) { - if(rw == 'u' || rw == 'U') return; +static void ll_recursive_unlock(DICTIONARY *dict, char rw) { + if(unlikely(is_dictionary_single_threaded(dict))) + return; + + if(ll_recursive_lock_is_thread_the_writer(dict) && dict->items.writer_depth > 0) { + dict->items.writer_depth--; + return; + } - if(rw == 'r' || rw == 'R') { + if(rw == DICTIONARY_LOCK_READ || rw == DICTIONARY_LOCK_REENTRANT || rw == 'R') { // read unlock - __atomic_sub_fetch(&dict->readers, 1, __ATOMIC_RELAXED); + + netdata_rwlock_unlock(&dict->items.rwlock); } else { // write unlock - garbage_collect_pending_deletes_unsafe(dict); - __atomic_sub_fetch(&dict->writers, 1, __ATOMIC_RELAXED); + + ll_recursive_unlock_unset_thread_writer(dict); + + netdata_rwlock_unlock(&dict->items.rwlock); } +} + +void dictionary_write_lock(DICTIONARY *dict) { + ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE); +} +void dictionary_write_unlock(DICTIONARY *dict) { + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE); +} - if(likely(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED)) +static inline void dictionary_index_lock_rdlock(DICTIONARY *dict) { + if(unlikely(is_dictionary_single_threaded(dict))) return; - if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) - dict->flags &= ~DICTIONARY_FLAG_EXCLUSIVE_ACCESS; + netdata_rwlock_rdlock(&dict->index.rwlock); +} + +static inline void dictionary_index_rdlock_unlock(DICTIONARY *dict) { + if(unlikely(is_dictionary_single_threaded(dict))) + return; - netdata_rwlock_unlock(&dict->rwlock); + netdata_rwlock_unlock(&dict->index.rwlock); } -// ---------------------------------------------------------------------------- -// deferred deletions +static inline void dictionary_index_lock_wrlock(DICTIONARY *dict) { + if(unlikely(is_dictionary_single_threaded(dict))) + return; -void dictionary_defer_all_deletions_unsafe(DICTIONARY *dict, char rw) { - if(rw == 'r' || rw == 'R') { - // read locked - no need to defer deletions - ; - } - else { - // write locked - defer deletions - dict->flags |= DICTIONARY_FLAG_DEFER_ALL_DELETIONS; - } + netdata_rwlock_wrlock(&dict->index.rwlock); +} +static inline void dictionary_index_wrlock_unlock(DICTIONARY *dict) { + if(unlikely(is_dictionary_single_threaded(dict))) + return; + + netdata_rwlock_unlock(&dict->index.rwlock); } -void dictionary_restore_all_deletions_unsafe(DICTIONARY *dict, char rw) { - if(rw == 'r' || rw == 'R') { - // read locked - no need to defer deletions - internal_error(dict->flags & DICTIONARY_FLAG_DEFER_ALL_DELETIONS, "DICTIONARY: deletions are deferred on a read lock"); - } - else { - // write locked - defer deletions - if(dict->flags & DICTIONARY_FLAG_DEFER_ALL_DELETIONS) - dict->flags &= ~DICTIONARY_FLAG_DEFER_ALL_DELETIONS; +// ---------------------------------------------------------------------------- +// items garbage collector + +static void garbage_collect_pending_deletes(DICTIONARY *dict) { + usec_t last_master_deletion_us = dict->hooks?__atomic_load_n(&dict->hooks->last_master_deletion_us, __ATOMIC_SEQ_CST):0; + usec_t last_gc_run_us = __atomic_load_n(&dict->last_gc_run_us, __ATOMIC_SEQ_CST); + + bool is_view = is_view_dictionary(dict); + + if(likely(!( + DICTIONARY_PENDING_DELETES_GET(dict) > 0 || + (is_view && last_master_deletion_us > last_gc_run_us) + ))) + return; + + ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE); + + __atomic_store_n(&dict->last_gc_run_us, now_realtime_usec(), __ATOMIC_SEQ_CST); + + if(is_view) + dictionary_index_lock_wrlock(dict); + + DICTIONARY_STATS_GARBAGE_COLLECTIONS_PLUS1(dict); + + size_t deleted = 0, pending = 0, examined = 0; + DICTIONARY_ITEM *item = dict->items.list, *item_next; + while(item) { + examined++; + + // this will cleanup + item_next = item->next; + int rc = item_check_and_acquire_advanced(dict, item, is_view); + + if(rc == RC_ITEM_MARKED_FOR_DELETION) { + // we didn't get a reference + + if(item_is_not_referenced_and_can_be_removed(dict, item)) { + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(dict->items.list, item, prev, next); + dict_item_free_with_hooks(dict, item); + deleted++; + + pending = DICTIONARY_PENDING_DELETES_MINUS1(dict); + if (!pending) + break; + } + } + else if(rc == RC_ITEM_IS_CURRENTLY_BEING_DELETED) + ; // do not touch this item (we didn't get a reference) + + else if(rc == RC_ITEM_OK) + item_release(dict, item); + + item = item_next; } + + if(is_view) + dictionary_index_wrlock_unlock(dict); + + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE); + + (void)deleted; + (void)examined; + + internal_error(false, "DICTIONARY: garbage collected dictionary created by %s (%zu@%s), examined %zu items, deleted %zu items, still pending %zu items", + dict->creation_function, dict->creation_line, dict->creation_file, examined, deleted, pending); + } // ---------------------------------------------------------------------------- @@ -413,144 +840,248 @@ static inline size_t reference_counter_free(DICTIONARY *dict) { return 0; } -static int reference_counter_increase(NAME_VALUE *nv) { - int refcount = __atomic_add_fetch(&nv->refcount, 1, __ATOMIC_SEQ_CST); - if(refcount == 1) - fatal("DICTIONARY: request to dup item '%s' but its reference counter was zero", namevalue_get_name(nv)); - return refcount; -} +static void item_acquire(DICTIONARY *dict, DICTIONARY_ITEM *item) { + REFCOUNT refcount; -static int reference_counter_acquire(DICTIONARY *dict, NAME_VALUE *nv) { - int refcount; - if(likely(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED)) - refcount = ++nv->refcount; - else - refcount = __atomic_add_fetch(&nv->refcount, 1, __ATOMIC_SEQ_CST); + if(unlikely(is_dictionary_single_threaded(dict))) { + refcount = ++item->refcount; + } + else { + // increment the refcount + refcount = __atomic_add_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST); + } + + if(refcount <= 0) { + internal_error( + true, + "DICTIONARY: attempted to acquire item which is deleted (refcount = %d): " + "'%s' on dictionary created by %s() (%zu@%s)", + refcount - 1, + item_get_name(item), + dict->creation_function, + dict->creation_line, + dict->creation_file); + + fatal( + "DICTIONARY: request to acquire item '%s', which is deleted (refcount = %d)!", + item_get_name(item), + refcount - 1); + } if(refcount == 1) { // referenced items counts number of unique items referenced // so, we increase it only when refcount == 1 - DICTIONARY_STATS_REFERENCED_ITEMS_PLUS1(dict); + DICTIONARY_REFERENCED_ITEMS_PLUS1(dict); // if this is a deleted item, but the counter increased to 1 // we need to remove it from the pending items to delete - if (nv->flags & NAME_VALUE_FLAG_DELETED) - DICTIONARY_STATS_PENDING_DELETES_MINUS1(dict); + if(item_flag_check(item, ITEM_FLAG_DELETED)) + DICTIONARY_PENDING_DELETES_MINUS1(dict); } - - return refcount; } -static uint32_t reference_counter_release(DICTIONARY *dict, NAME_VALUE *nv, bool can_get_write_lock) { +static void item_release(DICTIONARY *dict, DICTIONARY_ITEM *item) { // this function may be called without any lock on the dictionary - // or even when someone else has a write lock on the dictionary - // so, we cannot check for EXCLUSIVE ACCESS + // or even when someone else has write lock on the dictionary - uint32_t refcount; - if(likely(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED)) - refcount = nv->refcount--; - else - refcount = __atomic_fetch_sub(&nv->refcount, 1, __ATOMIC_SEQ_CST); + bool is_deleted; + REFCOUNT refcount; - if(refcount == 0) { - internal_error(true, "DICTIONARY: attempted to release item without references: '%s' on dictionary created by %s() (%zu@%s)", namevalue_get_name(nv), dict->creation_function, dict->creation_line, dict->creation_file); - fatal("DICTIONARY: attempted to release item without references: '%s'", namevalue_get_name(nv)); + if(unlikely(is_dictionary_single_threaded(dict))) { + is_deleted = item->flags & ITEM_FLAG_DELETED; + refcount = --item->refcount; } + else { + // get the flags before decrementing any reference counters + // (the other way around may lead to use-after-free) + is_deleted = item_flag_check(item, ITEM_FLAG_DELETED); - if(refcount == 1) { - if((nv->flags & NAME_VALUE_FLAG_DELETED)) - DICTIONARY_STATS_PENDING_DELETES_PLUS1(dict); + // decrement the refcount + refcount = __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST); + } + + if(refcount < 0) { + internal_error( + true, + "DICTIONARY: attempted to release item without references (refcount = %d): " + "'%s' on dictionary created by %s() (%zu@%s)", + refcount + 1, + item_get_name(item), + dict->creation_function, + dict->creation_line, + dict->creation_file); + + fatal( + "DICTIONARY: attempted to release item '%s' without references (refcount = %d)", + item_get_name(item), + refcount + 1); + } + + if(refcount == 0) { + + if(is_deleted) + DICTIONARY_PENDING_DELETES_PLUS1(dict); // referenced items counts number of unique items referenced // so, we decrease it only when refcount == 0 - DICTIONARY_STATS_REFERENCED_ITEMS_MINUS1(dict); + DICTIONARY_REFERENCED_ITEMS_MINUS1(dict); } +} + +static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *item, bool having_index_lock) { + size_t spins = 0; + REFCOUNT refcount, desired; + + int ret = RC_ITEM_OK; + + do { + spins++; - if(can_get_write_lock && DICTIONARY_STATS_PENDING_DELETES_GET(dict)) { - // we can garbage collect now + refcount = DICTIONARY_ITEM_REFCOUNT_GET(dict, item); + + if(refcount < 0) { + // we can't use this item + ret = RC_ITEM_IS_CURRENTLY_BEING_DELETED; + break; + } + + if(item_flag_check(item, ITEM_FLAG_DELETED)) { + // we can't use this item + ret = RC_ITEM_MARKED_FOR_DELETION; + break; + } - dictionary_lock(dict, DICTIONARY_LOCK_WRITE); - garbage_collect_pending_deletes_unsafe(dict); - dictionary_unlock(dict, DICTIONARY_LOCK_WRITE); + desired = refcount + 1; + + } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, + false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)); + + // if ret == ITEM_OK, we acquired the item + + if(ret == RC_ITEM_OK) { + if (is_view_dictionary(dict) && + item_shared_flag_check(item, ITEM_FLAG_DELETED) && + !item_flag_check(item, ITEM_FLAG_DELETED)) { + // but, we can't use this item + + if (having_index_lock) { + // delete it from the hashtable + if(hashtable_delete_unsafe(dict, item_get_name(item), item->key_len, item) == 0) + error("DICTIONARY: INTERNAL ERROR VIEW: tried to delete item with name '%s', name_len %u that is not in the index", item_get_name(item), (KEY_LEN_TYPE)(item->key_len - 1)); + else + pointer_del(dict, item); + + // mark it in our dictionary as deleted too + // this is safe to be done here, because we have got + // a reference counter on item + dict_item_set_deleted(dict, item); + + // decrement the refcount we incremented above + if (__atomic_sub_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST) == 0) { + // this is a deleted item, and we are the last one + DICTIONARY_PENDING_DELETES_PLUS1(dict); + } + + // do not touch the item below this point + } else { + // this is traversal / walkthrough + // decrement the refcount we incremented above + __atomic_sub_fetch(&item->refcount, 1, __ATOMIC_SEQ_CST); + } + + return RC_ITEM_MARKED_FOR_DELETION; + } + + if(desired == 1) + DICTIONARY_REFERENCED_ITEMS_PLUS1(dict); } - return refcount; + + if(unlikely(spins > 1 && dict->stats)) + DICTIONARY_STATS_CHECK_SPINS_PLUS(dict, spins - 1); + + return ret; } -// ---------------------------------------------------------------------------- -// hash table +// if a dictionary item can be deleted, return true, otherwise return false +// we use the private reference counter +static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY *dict, DICTIONARY_ITEM *item) { + // if we can set refcount to REFCOUNT_DELETING, we can delete this item -#ifdef DICTIONARY_WITH_AVL -static inline const char *namevalue_get_name(NAME_VALUE *nv); + size_t spins = 0; + REFCOUNT refcount, desired = REFCOUNT_DELETING; -static int name_value_compare(void* a, void* b) { - return strcmp(namevalue_get_name((NAME_VALUE *)a), namevalue_get_name((NAME_VALUE *)b)); -} + int ret = RC_ITEM_OK; -static void *get_thread_static_name_value(const char *name) { - static __thread NAME_VALUE tmp = { 0 }; - tmp.flags = NAME_VALUE_FLAG_NONE; - tmp.caller_name = (char *)name; - return &tmp; -} + do { + spins++; -static void hashtable_init_unsafe(DICTIONARY *dict) { - avl_init(&dict->values_index, name_value_compare); - dict->get_thread_static_name_value = get_thread_static_name_value; -} + refcount = DICTIONARY_ITEM_REFCOUNT_GET(dict, item); -static size_t hashtable_destroy_unsafe(DICTIONARY *dict) { - (void)dict; - return 0; -} + if(refcount < 0) { + // we can't use this item + ret = RC_ITEM_IS_CURRENTLY_BEING_DELETED; + break; + } -static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, size_t name_len, void *nv) { - (void)name; - (void)name_len; + if(refcount > 0) { + // we can't delete this + ret = RC_ITEM_IS_REFERENCED; + break; + } - if(unlikely(avl_remove(&(dict->values_index), (avl_t *)(nv)) != (avl_t *)nv)) - return 0; + if(item_flag_check(item, ITEM_FLAG_BEING_CREATED)) { + // we can't use this item + ret = RC_ITEM_IS_CURRENTLY_BEING_CREATED; + break; + } + } while(!__atomic_compare_exchange_n(&item->refcount, &refcount, desired, + false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)); - return 1; -} +#ifdef NETDATA_INTERNAL_CHECKS + if(ret == RC_ITEM_OK) + item->deleter_pid = gettid(); +#endif -static inline NAME_VALUE *hashtable_get_unsafe(DICTIONARY *dict, const char *name, size_t name_len) { - (void)name_len; + if(unlikely(spins > 1 && dict->stats)) + DICTIONARY_STATS_DELETE_SPINS_PLUS(dict, spins - 1); - void *tmp = dict->get_thread_static_name_value(name); - return (NAME_VALUE *)avl_search(&(dict->values_index), (avl_t *)tmp); + return ret; } -static inline NAME_VALUE **hashtable_insert_unsafe(DICTIONARY *dict, const char *name, size_t name_len) { - // AVL needs a NAME_VALUE to insert into the dictionary but we don't have it yet. - // So, the only thing we can do, is return an existing one if it is already there. - // Returning NULL will make the caller thing we added it, will allocate one - // and will call hashtable_inserted_name_value_unsafe(), at which we will do - // the actual indexing. +// if a dictionary item can be freed, return true, otherwise return false +// we use the shared reference counter +static inline bool item_shared_release_and_check_if_it_can_be_freed(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item) { + // if we can set refcount to REFCOUNT_DELETING, we can delete this item - dict->hash_base = hashtable_get_unsafe(dict, name, name_len); - return &dict->hash_base; -} + REFCOUNT links = __atomic_sub_fetch(&item->shared->links, 1, __ATOMIC_SEQ_CST); + if(links == 0 && __atomic_compare_exchange_n(&item->shared->links, &links, REFCOUNT_DELETING, + false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { -static inline void hashtable_inserted_name_value_unsafe(DICTIONARY *dict, void *nv) { - // we have our new NAME_VALUE object. - // Let's index it. + // we can delete it + return true; + } - if(unlikely(avl_insert(&((dict)->values_index), (avl_t *)(nv)) != (avl_t *)nv)) - error("dictionary: INTERNAL ERROR: duplicate insertion to dictionary."); + // we can't delete it + return false; } -#endif -#ifdef DICTIONARY_WITH_JUDYHS -static void hashtable_init_unsafe(DICTIONARY *dict) { - dict->JudyHSArray = NULL; + +// ---------------------------------------------------------------------------- +// hash table operations + +static size_t hashtable_init_unsafe(DICTIONARY *dict) { + dict->index.JudyHSArray = NULL; + return 0; } static size_t hashtable_destroy_unsafe(DICTIONARY *dict) { - if(unlikely(!dict->JudyHSArray)) return 0; + if(unlikely(!dict->index.JudyHSArray)) return 0; + + pointer_destroy_index(dict); JError_t J_Error; - Word_t ret = JudyHSFreeArray(&dict->JudyHSArray, &J_Error); + Word_t ret = JudyHSFreeArray(&dict->index.JudyHSArray, &J_Error); if(unlikely(ret == (Word_t) JERR)) { error("DICTIONARY: Cannot destroy JudyHS, JU_ERRNO_* == %u, ID == %d", JU_ERRNO(&J_Error), JU_ERRID(&J_Error)); @@ -558,17 +1089,15 @@ static size_t hashtable_destroy_unsafe(DICTIONARY *dict) { debug(D_DICTIONARY, "Dictionary: hash table freed %lu bytes", ret); - dict->JudyHSArray = NULL; + dict->index.JudyHSArray = NULL; return (size_t)ret; } -static inline NAME_VALUE **hashtable_insert_unsafe(DICTIONARY *dict, const char *name, size_t name_len) { - internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: inserting item from the index without exclusive access to the dictionary created by %s() (%zu@%s)", dict->creation_function, dict->creation_line, dict->creation_file); - +static inline void **hashtable_insert_unsafe(DICTIONARY *dict, const char *name, size_t name_len) { JError_t J_Error; - Pvoid_t *Rc = JudyHSIns(&dict->JudyHSArray, (void *)name, name_len, &J_Error); + Pvoid_t *Rc = JudyHSIns(&dict->index.JudyHSArray, (void *)name, name_len, &J_Error); if (unlikely(Rc == PJERR)) { - fatal("DICTIONARY: Cannot insert entry with name '%s' to JudyHS, JU_ERRNO_* == %u, ID == %d", + error("DICTIONARY: Cannot insert entry with name '%s' to JudyHS, JU_ERRNO_* == %u, ID == %d", name, JU_ERRNO(&J_Error), JU_ERRID(&J_Error)); } @@ -579,17 +1108,15 @@ static inline NAME_VALUE **hashtable_insert_unsafe(DICTIONARY *dict, const char // put anything needed at the value of the index. // The pointer to pointer we return has to be used before // any other operation that may change the index (insert/delete). - return (NAME_VALUE **)Rc; + return Rc; } -static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, size_t name_len, void *nv) { - internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: deleting item from the index without exclusive access to the dictionary created by %s() (%zu@%s)", dict->creation_function, dict->creation_line, dict->creation_file); - - (void)nv; - if(unlikely(!dict->JudyHSArray)) return 0; +static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, size_t name_len, void *item) { + (void)item; + if(unlikely(!dict->index.JudyHSArray)) return 0; JError_t J_Error; - int ret = JudyHSDel(&dict->JudyHSArray, (void *)name, name_len, &J_Error); + int ret = JudyHSDel(&dict->index.JudyHSArray, (void *)name, name_len, &J_Error); if(unlikely(ret == JERR)) { error("DICTIONARY: Cannot delete entry with name '%s' from JudyHS, JU_ERRNO_* == %u, ID == %d", name, JU_ERRNO(&J_Error), JU_ERRID(&J_Error)); @@ -609,16 +1136,17 @@ static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, si } } -static inline NAME_VALUE *hashtable_get_unsafe(DICTIONARY *dict, const char *name, size_t name_len) { - if(unlikely(!dict->JudyHSArray)) return NULL; +static inline DICTIONARY_ITEM *hashtable_get_unsafe(DICTIONARY *dict, const char *name, size_t name_len) { + if(unlikely(!dict->index.JudyHSArray)) return NULL; DICTIONARY_STATS_SEARCHES_PLUS1(dict); Pvoid_t *Rc; - Rc = JudyHSGet(dict->JudyHSArray, (void *)name, name_len); + Rc = JudyHSGet(dict->index.JudyHSArray, (void *)name, name_len); if(likely(Rc)) { // found in the hash table - return (NAME_VALUE *)*Rc; + pointer_check(dict, (DICTIONARY_ITEM *)*Rc); + return (DICTIONARY_ITEM *)*Rc; } else { // not found in the hash table @@ -626,345 +1154,419 @@ static inline NAME_VALUE *hashtable_get_unsafe(DICTIONARY *dict, const char *nam } } -static inline void hashtable_inserted_name_value_unsafe(DICTIONARY *dict, void *nv) { +static inline void hashtable_inserted_item_unsafe(DICTIONARY *dict, void *item) { (void)dict; - (void)nv; + (void)item; + + // this is called just after an item is successfully inserted to the hashtable + // we don't need this for judy, but we may need it if we integrate more hash tables + ; } -#endif // DICTIONARY_WITH_JUDYHS - // ---------------------------------------------------------------------------- // linked list management -static inline void linkedlist_namevalue_link_unsafe(DICTIONARY *dict, NAME_VALUE *nv) { - internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: adding item to the linked-list without exclusive access to the dictionary created by %s() (%zu@%s)", dict->creation_function, dict->creation_line, dict->creation_file); +static inline void item_linked_list_add(DICTIONARY *dict, DICTIONARY_ITEM *item) { + ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE); - if (unlikely(!dict->first_item)) { - // we are the only ones here - nv->next = NULL; - nv->prev = NULL; - dict->first_item = dict->last_item = nv; - return; - } + if(dict->options & DICT_OPTION_ADD_IN_FRONT) + DOUBLE_LINKED_LIST_PREPEND_UNSAFE(dict->items.list, item, prev, next); + else + DOUBLE_LINKED_LIST_APPEND_UNSAFE(dict->items.list, item, prev, next); - if(dict->flags & DICTIONARY_FLAG_ADD_IN_FRONT) { - // add it at the beginning - nv->prev = NULL; - nv->next = dict->first_item; +#ifdef NETDATA_INTERNAL_CHECKS + item->ll_adder_pid = gettid(); +#endif - if (likely(nv->next)) nv->next->prev = nv; - dict->first_item = nv; - } - else { - // add it at the end - nv->next = NULL; - nv->prev = dict->last_item; + // clear the BEING created flag, + // after it has been inserted into the linked list + item_flag_clear(item, ITEM_FLAG_BEING_CREATED); - if (likely(nv->prev)) nv->prev->next = nv; - dict->last_item = nv; - } + garbage_collect_pending_deletes(dict); + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE); } -static inline void linkedlist_namevalue_unlink_unsafe(DICTIONARY *dict, NAME_VALUE *nv) { - internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: removing item from the linked-list without exclusive access to the dictionary created by %s() (%zu@%s)", dict->creation_function, dict->creation_line, dict->creation_file); +static inline void item_linked_list_remove(DICTIONARY *dict, DICTIONARY_ITEM *item) { + ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE); + + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(dict->items.list, item, prev, next); + +#ifdef NETDATA_INTERNAL_CHECKS + item->ll_remover_pid = gettid(); +#endif - if(nv->next) nv->next->prev = nv->prev; - if(nv->prev) nv->prev->next = nv->next; - if(dict->first_item == nv) dict->first_item = nv->next; - if(dict->last_item == nv) dict->last_item = nv->prev; + garbage_collect_pending_deletes(dict); + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE); } // ---------------------------------------------------------------------------- -// NAME_VALUE methods +// ITEM initialization and updates -static inline size_t namevalue_set_name(DICTIONARY *dict, NAME_VALUE *nv, const char *name, size_t name_len) { - if(likely(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) { - nv->caller_name = (char *)name; - return 0; +static inline size_t item_set_name(DICTIONARY *dict, DICTIONARY_ITEM *item, const char *name, size_t name_len) { + if(likely(dict->options & DICT_OPTION_NAME_LINK_DONT_CLONE)) { + item->caller_name = (char *)name; + item->key_len = name_len; + } + else { + item->string_name = string_strdupz(name); + item->key_len = string_strlen(item->string_name) + 1; + item->options |= ITEM_OPTION_ALLOCATED_NAME; } - nv->string_name = string_strdupz(name); - nv->flags |= NAME_VALUE_FLAG_NAME_IS_ALLOCATED; - return name_len; + return item->key_len; } -static inline size_t namevalue_free_name(DICTIONARY *dict, NAME_VALUE *nv) { - if(unlikely(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE))) - string_freez(nv->string_name); +static inline size_t item_free_name(DICTIONARY *dict, DICTIONARY_ITEM *item) { + if(likely(!(dict->options & DICT_OPTION_NAME_LINK_DONT_CLONE))) + string_freez(item->string_name); - return 0; + return item->key_len; +} + +static inline const char *item_get_name(const DICTIONARY_ITEM *item) { + if(item->options & ITEM_OPTION_ALLOCATED_NAME) + return string2str(item->string_name); + else + return item->caller_name; } -static inline const char *namevalue_get_name(NAME_VALUE *nv) { - if(nv->flags & NAME_VALUE_FLAG_NAME_IS_ALLOCATED) - return string2str(nv->string_name); +static inline size_t item_get_name_len(const DICTIONARY_ITEM *item) { + if(item->options & ITEM_OPTION_ALLOCATED_NAME) + return string_strlen(item->string_name); else - return nv->caller_name; + return strlen(item->caller_name); } -static NAME_VALUE *namevalue_create_unsafe(DICTIONARY *dict, const char *name, size_t name_len, void *value, size_t value_len) { - debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name); +static DICTIONARY_ITEM *dict_item_create(DICTIONARY *dict __maybe_unused, size_t *allocated_bytes, DICTIONARY_ITEM *master_item) { + DICTIONARY_ITEM *item; - size_t size = sizeof(NAME_VALUE); - NAME_VALUE *nv = mallocz(size); - size_t allocated = size; + size_t size = sizeof(DICTIONARY_ITEM); + item = callocz(1, size); #ifdef NETDATA_INTERNAL_CHECKS - nv->dict = dict; + item->creator_pid = gettid(); #endif - nv->refcount = 0; - nv->flags = NAME_VALUE_FLAG_NONE; - nv->value_len = value_len; + item->refcount = 1; + item->flags = ITEM_FLAG_BEING_CREATED; - allocated += namevalue_set_name(dict, nv, name, name_len); + *allocated_bytes += size; - if(likely(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) - nv->value = value; + if(master_item) { + item->shared = master_item->shared; + + if(unlikely(__atomic_add_fetch(&item->shared->links, 1, __ATOMIC_SEQ_CST) <= 1)) + fatal("DICTIONARY: attempted to link to a shared item structure that had zero references"); + } else { - if(likely(value_len)) { - if (value) { - // a value has been supplied - // copy it - nv->value = mallocz(value_len); - memcpy(nv->value, value, value_len); - } - else { - // no value has been supplied - // allocate a clear memory block - nv->value = callocz(1, value_len); - } + size = sizeof(DICTIONARY_ITEM_SHARED); + item->shared = callocz(1, size); + item->shared->links = 1; + *allocated_bytes += size; + } + +#ifdef NETDATA_INTERNAL_CHECKS + item->dict = dict; +#endif + return item; +} + +static void *dict_item_value_create(void *value, size_t value_len) { + void *ptr = NULL; + + if(likely(value_len)) { + if (likely(value)) { + // a value has been supplied + // copy it + ptr = mallocz(value_len); + memcpy(ptr, value, value_len); } else { - // the caller wants an item without any value - nv->value = NULL; + // no value has been supplied + // allocate a clear memory block + ptr = callocz(1, value_len); } + } + // else + // the caller wants an item without any value + + return ptr; +} + +static DICTIONARY_ITEM *dict_item_create_with_hooks(DICTIONARY *dict, const char *name, size_t name_len, void *value, size_t value_len, void *constructor_data, DICTIONARY_ITEM *master_item) { +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(name_len > KEY_LEN_MAX)) + fatal("DICTIONARY: tried to index a key of size %zu, but the maximum acceptable is %zu", name_len, (size_t)KEY_LEN_MAX); + + if(unlikely(value_len > VALUE_LEN_MAX)) + fatal("DICTIONARY: tried to add an item of size %zu, but the maximum acceptable is %zu", value_len, (size_t)VALUE_LEN_MAX); +#endif + + size_t item_size = 0, key_size = 0, value_size = 0; + + DICTIONARY_ITEM *item = dict_item_create(dict, &item_size, master_item); + key_size += item_set_name(dict, item, name, name_len); - allocated += value_len; + if(unlikely(is_view_dictionary(dict))) { + // we are on a view dictionary + // do not touch the value + ; + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(!master_item)) + fatal("DICTIONARY: cannot add an item to a view without a master item."); +#endif } + else { + // we are on the master dictionary + + if(unlikely(dict->options & DICT_OPTION_VALUE_LINK_DONT_CLONE)) + item->shared->value = value; + else + item->shared->value = dict_item_value_create(value, value_len); + + item->shared->value_len = value_len; + value_size += value_len; - DICTIONARY_STATS_ENTRIES_PLUS1(dict, allocated); + dictionary_execute_insert_callback(dict, item, constructor_data); + } - if(dict->ins_callback) - dict->ins_callback(namevalue_get_name(nv), nv->value, dict->ins_callback_data); + DICTIONARY_ENTRIES_PLUS1(dict); + DICTIONARY_STATS_PLUS_MEMORY(dict, key_size, item_size, value_size); - return nv; + return item; } -static void namevalue_reset_unsafe(DICTIONARY *dict, NAME_VALUE *nv, void *value, size_t value_len) { - debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", namevalue_get_name(nv)); +static void dict_item_reset_value_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item, void *value, size_t value_len, void *constructor_data) { + if(unlikely(is_view_dictionary(dict))) + fatal("DICTIONARY: %s() should never be called on views.", __FUNCTION__ ); + + debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", item_get_name(item)); + + DICTIONARY_VALUE_RESETS_PLUS1(dict); - DICTIONARY_STATS_VALUE_RESETS_PLUS1(dict, nv->value_len, value_len); + if(item->shared->value_len != value_len) { + DICTIONARY_STATS_PLUS_MEMORY(dict, 0, 0, value_len); + DICTIONARY_STATS_MINUS_MEMORY(dict, 0, 0, item->shared->value_len); + } - if(dict->del_callback) - dict->del_callback(namevalue_get_name(nv), nv->value, dict->del_callback_data); + dictionary_execute_delete_callback(dict, item); - if(likely(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) { - debug(D_DICTIONARY, "Dictionary: linking value to '%s'", namevalue_get_name(nv)); - nv->value = value; - nv->value_len = value_len; + if(likely(dict->options & DICT_OPTION_VALUE_LINK_DONT_CLONE)) { + debug(D_DICTIONARY, "Dictionary: linking value to '%s'", item_get_name(item)); + item->shared->value = value; + item->shared->value_len = value_len; } else { - debug(D_DICTIONARY, "Dictionary: cloning value to '%s'", namevalue_get_name(nv)); + debug(D_DICTIONARY, "Dictionary: cloning value to '%s'", item_get_name(item)); - void *oldvalue = nv->value; - void *newvalue = NULL; + void *old_value = item->shared->value; + void *new_value = NULL; if(value_len) { - newvalue = mallocz(value_len); - if(value) memcpy(newvalue, value, value_len); - else memset(newvalue, 0, value_len); + new_value = mallocz(value_len); + if(value) memcpy(new_value, value, value_len); + else memset(new_value, 0, value_len); } - nv->value = newvalue; - nv->value_len = value_len; + item->shared->value = new_value; + item->shared->value_len = value_len; - debug(D_DICTIONARY, "Dictionary: freeing old value of '%s'", namevalue_get_name(nv)); - freez(oldvalue); + debug(D_DICTIONARY, "Dictionary: freeing old value of '%s'", item_get_name(item)); + freez(old_value); } - if(dict->ins_callback) - dict->ins_callback(namevalue_get_name(nv), nv->value, dict->ins_callback_data); + dictionary_execute_insert_callback(dict, item, constructor_data); } -static size_t namevalue_destroy_unsafe(DICTIONARY *dict, NAME_VALUE *nv) { - debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", namevalue_get_name(nv)); +static size_t dict_item_free_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item) { + debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", item_get_name(item)); - if(dict->del_callback) - dict->del_callback(namevalue_get_name(nv), nv->value, dict->del_callback_data); + if(!item_flag_check(item, ITEM_FLAG_DELETED)) + DICTIONARY_ENTRIES_MINUS1(dict); - size_t freed = 0; + size_t item_size = 0, key_size = 0, value_size = 0; - if(unlikely(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE))) { - debug(D_DICTIONARY, "Dictionary freeing value of '%s'", namevalue_get_name(nv)); - freez(nv->value); - freed += nv->value_len; - } + key_size += item->key_len; + if(unlikely(!(dict->options & DICT_OPTION_NAME_LINK_DONT_CLONE))) + item_free_name(dict, item); + + if(item_shared_release_and_check_if_it_can_be_freed(dict, item)) { + dictionary_execute_delete_callback(dict, item); - if(unlikely(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE))) { - debug(D_DICTIONARY, "Dictionary freeing name '%s'", namevalue_get_name(nv)); - freed += namevalue_free_name(dict, nv); + if(unlikely(!(dict->options & DICT_OPTION_VALUE_LINK_DONT_CLONE))) { + debug(D_DICTIONARY, "Dictionary freeing value of '%s'", item_get_name(item)); + freez(item->shared->value); + item->shared->value = NULL; + } + value_size += item->shared->value_len; + + freez(item->shared); + item->shared = NULL; + item_size += sizeof(DICTIONARY_ITEM_SHARED); } - freez(nv); - freed += sizeof(NAME_VALUE); + freez(item); + item_size += sizeof(DICTIONARY_ITEM); - DICTIONARY_STATS_ENTRIES_MINUS_MEMORY(dict, freed); + DICTIONARY_STATS_MINUS_MEMORY(dict, key_size, item_size, value_size); - return freed; + // we return the memory we actually freed + return item_size + ((dict->options & DICT_OPTION_VALUE_LINK_DONT_CLONE) ? 0 : value_size); } -// if a dictionary item can be deleted, return true, otherwise return false -static bool name_value_can_be_deleted(DICTIONARY *dict, NAME_VALUE *nv) { - if(unlikely(dict->flags & DICTIONARY_FLAG_DEFER_ALL_DELETIONS)) - return false; +// ---------------------------------------------------------------------------- +// item operations - if(unlikely(DICTIONARY_NAME_VALUE_REFCOUNT_GET(nv) > 0)) - return false; +static void dict_item_shared_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) { + if(is_master_dictionary(dict)) { + item_shared_flag_set(item, ITEM_FLAG_DELETED); - return true; + if(dict->hooks) + __atomic_store_n(&dict->hooks->last_master_deletion_us, now_realtime_usec(), __ATOMIC_SEQ_CST); + } } -// ---------------------------------------------------------------------------- -// API - dictionary management -#ifdef NETDATA_INTERNAL_CHECKS -DICTIONARY *dictionary_create_advanced_with_trace(DICTIONARY_FLAGS flags, size_t scratchpad_size, const char *function, size_t line, const char *file) { -#else -DICTIONARY *dictionary_create_advanced(DICTIONARY_FLAGS flags, size_t scratchpad_size) { -#endif - debug(D_DICTIONARY, "Creating dictionary."); +// returns true if we set the deleted flag on this item +static bool dict_item_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) { + ITEM_FLAGS expected, desired; - if(unlikely(flags & DICTIONARY_FLAGS_RESERVED)) - flags &= ~DICTIONARY_FLAGS_RESERVED; + do { + expected = __atomic_load_n(&item->flags, __ATOMIC_SEQ_CST); - DICTIONARY *dict = callocz(1, sizeof(DICTIONARY) + scratchpad_size); - size_t allocated = sizeof(DICTIONARY) + scratchpad_size; + if (expected & ITEM_FLAG_DELETED) + return false; - dict->scratchpad_size = scratchpad_size; - dict->flags = flags; - dict->first_item = dict->last_item = NULL; + desired = expected | ITEM_FLAG_DELETED; - allocated += dictionary_lock_init(dict); - allocated += reference_counter_init(dict); - dict->memory = (long)allocated; + } while(!__atomic_compare_exchange_n(&item->flags, &expected, desired, + false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)); - hashtable_init_unsafe(dict); + DICTIONARY_ENTRIES_MINUS1(dict); + return true; +} -#ifdef NETDATA_INTERNAL_CHECKS - dict->creation_function = function; - dict->creation_file = file; - dict->creation_line = line; -#endif +static inline void dict_item_free_or_mark_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item) { + int rc = item_is_not_referenced_and_can_be_removed_advanced(dict, item); + switch(rc) { + case RC_ITEM_OK: + // the item is ours, refcount set to -100 + dict_item_shared_set_deleted(dict, item); + item_linked_list_remove(dict, item); + dict_item_free_with_hooks(dict, item); + break; - return (DICTIONARY *)dict; -} + case RC_ITEM_IS_REFERENCED: + case RC_ITEM_IS_CURRENTLY_BEING_CREATED: + // the item is currently referenced by others + dict_item_shared_set_deleted(dict, item); + dict_item_set_deleted(dict, item); + // after this point do not touch the item + break; -void *dictionary_scratchpad(DICTIONARY *dict) { - return &dict->scratchpad; -} + case RC_ITEM_IS_CURRENTLY_BEING_DELETED: + // an item that is currently being deleted by someone else - don't touch it + break; -size_t dictionary_destroy(DICTIONARY *dict) { - if(!dict) return 0; + default: + internal_error(true, "Hey dev! You forgot to add the new condition here!"); + break; + } +} - NAME_VALUE *nv; +// this is used by traversal functions to remove the current item +// if it is deleted and it has zero references. This will eliminate +// the need for the garbage collector to kick-in later. +// Most deletions happen during traversal, so this is a nice hack +// to speed up everything! +static inline void dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(DICTIONARY *dict, DICTIONARY_ITEM *item, char rw) { + if(rw == DICTIONARY_LOCK_WRITE) { + bool should_be_deleted = item_flag_check(item, ITEM_FLAG_DELETED); - debug(D_DICTIONARY, "Destroying dictionary."); + item_release(dict, item); - long referenced_items = 0; - size_t retries = 0; - do { - referenced_items = __atomic_load_n(&dict->referenced_items, __ATOMIC_SEQ_CST); - if (referenced_items) { - dictionary_lock(dict, DICTIONARY_LOCK_WRITE); - - // there are referenced items - // delete all items individually, so that only the referenced will remain - NAME_VALUE *nv_next; - for (nv = dict->first_item; nv; nv = nv_next) { - nv_next = nv->next; - size_t refcount = DICTIONARY_NAME_VALUE_REFCOUNT_GET(nv); - if (!refcount && !(nv->flags & NAME_VALUE_FLAG_DELETED)) - dictionary_del_unsafe(dict, namevalue_get_name(nv)); - } + if(should_be_deleted && item_is_not_referenced_and_can_be_removed(dict, item)) { + // this has to be before removing from the linked list, + // otherwise the garbage collector will also kick in! + DICTIONARY_PENDING_DELETES_MINUS1(dict); - internal_error( - retries == 0, - "DICTIONARY: waiting (try %zu) for destruction of dictionary created from %s() %zu@%s, because it has %ld referenced items in it (%ld total).", - retries + 1, - dict->creation_function, - dict->creation_line, - dict->creation_file, - referenced_items, - dict->entries); - - dictionary_unlock(dict, DICTIONARY_LOCK_WRITE); - sleep_usec(10000); + item_linked_list_remove(dict, item); + dict_item_free_with_hooks(dict, item); } - } while(referenced_items > 0 && ++retries < 10); - - if(referenced_items) { - dictionary_lock(dict, DICTIONARY_LOCK_WRITE); + } + else { + // we can't do anything under this mode + item_release(dict, item); + } +} - dict->flags |= DICTIONARY_FLAG_DESTROYED; +static bool dict_item_del(DICTIONARY *dict, const char *name, ssize_t name_len) { + if(unlikely(!name || !*name)) { internal_error( true, - "DICTIONARY: delaying destruction of dictionary created from %s() %zu@%s after %zu retries, because it has %ld referenced items in it (%ld total).", + "DICTIONARY: attempted to %s() without a name on a dictionary created from %s() %zu@%s.", + __FUNCTION__, dict->creation_function, dict->creation_line, - dict->creation_file, - retries, - referenced_items, - dict->entries); + dict->creation_file); + return false; + } - dictionary_unlock(dict, DICTIONARY_LOCK_WRITE); - return 0; + if(unlikely(is_dictionary_destroyed(dict))) { + internal_error(true, "DICTIONARY: attempted to dictionary_del() on a destroyed dictionary"); + return false; } - dictionary_lock(dict, DICTIONARY_LOCK_WRITE); + if(name_len == -1) + name_len = (ssize_t)strlen(name) + 1; // we need the terminating null too - size_t freed = 0; - nv = dict->first_item; - while (nv) { - // cache nv->next - // because we are going to free nv - NAME_VALUE *nv_next = nv->next; - freed += namevalue_destroy_unsafe(dict, nv); - nv = nv_next; - // to speed up destruction, we don't - // unlink nv from the linked-list here - } + debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name); - dict->first_item = NULL; - dict->last_item = NULL; + // Unfortunately, the JudyHSDel() does not return the value of the + // item that was deleted, so we have to find it before we delete it, + // since we need to release our structures too. - // destroy the dictionary - freed += hashtable_destroy_unsafe(dict); + dictionary_index_lock_wrlock(dict); - dictionary_unlock(dict, DICTIONARY_LOCK_WRITE); - freed += dictionary_lock_free(dict); - freed += reference_counter_free(dict); - freed += sizeof(DICTIONARY) + dict->scratchpad_size; - freez(dict); + int ret; + DICTIONARY_ITEM *item = hashtable_get_unsafe(dict, name, name_len); + if(unlikely(!item)) { + dictionary_index_wrlock_unlock(dict); + ret = false; + } + else { + if(hashtable_delete_unsafe(dict, name, name_len, item) == 0) + error("DICTIONARY: INTERNAL ERROR: tried to delete item with name '%s', name_len %zd that is not in the index", name, name_len - 1); + else + pointer_del(dict, item); - return freed; -} + dictionary_index_wrlock_unlock(dict); -// ---------------------------------------------------------------------------- -// helpers + dict_item_free_or_mark_deleted(dict, item); + ret = true; + } -static NAME_VALUE *dictionary_set_name_value_unsafe(DICTIONARY *dict, const char *name, void *value, size_t value_len) { - if(unlikely(!name)) { - internal_error(true, "DICTIONARY: attempted to dictionary_set() a dictionary item without a name"); + return ret; +} + +static DICTIONARY_ITEM *dict_item_add_or_reset_value_and_acquire(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data, DICTIONARY_ITEM *master_item) { + if(unlikely(!name || !*name)) { + internal_error( + true, + "DICTIONARY: attempted to %s() without a name on a dictionary created from %s() %zu@%s.", + __FUNCTION__, + dict->creation_function, + dict->creation_line, + dict->creation_file); return NULL; } - if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) { + if(unlikely(is_dictionary_destroyed(dict))) { internal_error(true, "DICTIONARY: attempted to dictionary_set() on a destroyed dictionary"); return NULL; } - internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: inserting dictionary item '%s' without exclusive access to dictionary", name); - - size_t name_len = strlen(name) + 1; // we need the terminating null too + if(name_len == -1) + name_len = (ssize_t)strlen(name) + 1; // we need the terminating null too debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name); @@ -979,264 +1581,618 @@ static NAME_VALUE *dictionary_set_name_value_unsafe(DICTIONARY *dict, const char // But the caller has the option to do this on his/her own. // So, let's do the fastest here and let the caller decide the flow of calls. - NAME_VALUE *nv, **pnv = hashtable_insert_unsafe(dict, name, name_len); - if(likely(*pnv == 0)) { - // a new item added to the index - nv = *pnv = namevalue_create_unsafe(dict, name, name_len, value, value_len); - hashtable_inserted_name_value_unsafe(dict, nv); - linkedlist_namevalue_link_unsafe(dict, nv); - nv->flags |= NAME_VALUE_FLAG_NEW_OR_UPDATED; - } - else { - // the item is already in the index - // so, either we will return the old one - // or overwrite the value, depending on dictionary flags + dictionary_index_lock_wrlock(dict); + + bool added_or_updated = false; + size_t spins = 0; + DICTIONARY_ITEM *item = NULL; + do { + DICTIONARY_ITEM **item_pptr = (DICTIONARY_ITEM **)hashtable_insert_unsafe(dict, name, name_len); + if (likely(*item_pptr == NULL)) { + // a new item added to the index - nv = *pnv; + // create the dictionary item + item = *item_pptr = + dict_item_create_with_hooks(dict, name, name_len, value, value_len, constructor_data, master_item); - if(!(dict->flags & DICTIONARY_FLAG_DONT_OVERWRITE_VALUE)) { - namevalue_reset_unsafe(dict, nv, value, value_len); - nv->flags |= NAME_VALUE_FLAG_NEW_OR_UPDATED; - } + pointer_add(dict, item); - else if(dict->conflict_callback) { - dict->conflict_callback(namevalue_get_name(nv), nv->value, value, dict->conflict_callback_data); - nv->flags |= NAME_VALUE_FLAG_NEW_OR_UPDATED; - } + // call the hashtable react + hashtable_inserted_item_unsafe(dict, item); + // unlock the index lock, before we add it to the linked list + // DONT DO IT THE OTHER WAY AROUND - DO NOT CROSS THE LOCKS! + dictionary_index_wrlock_unlock(dict); + + item_linked_list_add(dict, item); + + added_or_updated = true; + } else { - // make sure this flag is not set - nv->flags &= ~NAME_VALUE_FLAG_NEW_OR_UPDATED; + pointer_check(dict, *item_pptr); + + if(item_check_and_acquire_advanced(dict, *item_pptr, true) != RC_ITEM_OK) { + spins++; + continue; + } + + // the item is already in the index + // so, either we will return the old one + // or overwrite the value, depending on dictionary flags + + // We should not compare the values here! + // even if they are the same, we have to do the whole job + // so that the callbacks will be called. + + item = *item_pptr; + + if(is_view_dictionary(dict)) { + // view dictionary + // the item is already there and can be used + if(item->shared != master_item->shared) + error("DICTIONARY: changing the master item on a view is not supported. The previous item will remain. To change the key of an item in a view, delete it and add it again."); + } + else { + // master dictionary + // the user wants to reset its value + + if (!(dict->options & DICT_OPTION_DONT_OVERWRITE_VALUE)) { + dict_item_reset_value_with_hooks(dict, item, value, value_len, constructor_data); + added_or_updated = true; + } + + else if (dictionary_execute_conflict_callback(dict, item, value, constructor_data)) { + dictionary_version_increment(dict); + added_or_updated = true; + } + + else { + // conflict callback returned false + // we did really nothing! + ; + } + } + + dictionary_index_wrlock_unlock(dict); } - } + } while(!item); + - return nv; + if(unlikely(spins > 0 && dict->stats)) + DICTIONARY_STATS_INSERT_SPINS_PLUS(dict, spins); + + if(is_master_dictionary(dict) && added_or_updated) + dictionary_execute_react_callback(dict, item, constructor_data); + + return item; } -static NAME_VALUE *dictionary_get_name_value_unsafe(DICTIONARY *dict, const char *name) { - if(unlikely(!name)) { - internal_error(true, "attempted to dictionary_get() without a name"); +static DICTIONARY_ITEM *dict_item_find_and_acquire(DICTIONARY *dict, const char *name, ssize_t name_len) { + if(unlikely(!name || !*name)) { + internal_error( + true, + "DICTIONARY: attempted to %s() without a name on a dictionary created from %s() %zu@%s.", + __FUNCTION__, + dict->creation_function, + dict->creation_line, + dict->creation_file); return NULL; } - if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) { + if(unlikely(is_dictionary_destroyed(dict))) { internal_error(true, "DICTIONARY: attempted to dictionary_get() on a destroyed dictionary"); return NULL; } - size_t name_len = strlen(name) + 1; // we need the terminating null too + if(name_len == -1) + name_len = (ssize_t)strlen(name) + 1; // we need the terminating null too debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name); - NAME_VALUE *nv = hashtable_get_unsafe(dict, name, name_len); - if(unlikely(!nv)) { - debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name); - return NULL; + dictionary_index_lock_rdlock(dict); + + DICTIONARY_ITEM *item = hashtable_get_unsafe(dict, name, name_len); + if(unlikely(item && !item_check_and_acquire(dict, item))) { + item = NULL; + DICTIONARY_STATS_SEARCH_IGNORES_PLUS1(dict); } - debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name); - return nv; + dictionary_index_rdlock_unlock(dict); + + return item; } // ---------------------------------------------------------------------------- -// API - items management +// delayed destruction of dictionaries + +static bool dictionary_free_all_resources(DICTIONARY *dict, size_t *mem, bool force) { + if(mem) + *mem = 0; + + if(!force && dictionary_referenced_items(dict)) + return false; + + size_t dict_size = 0, counted_items = 0, item_size = 0, index_size = 0; + (void)counted_items; + +#ifdef NETDATA_INTERNAL_CHECKS + long int entries = dict->entries; + long int referenced_items = dict->referenced_items; + long int pending_deletion_items = dict->pending_deletion_items; + const char *creation_function = dict->creation_function; + const char *creation_file = dict->creation_file; + size_t creation_line = dict->creation_line; +#endif + + // destroy the index + dictionary_index_lock_wrlock(dict); + index_size += hashtable_destroy_unsafe(dict); + dictionary_index_wrlock_unlock(dict); + + ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE); + DICTIONARY_ITEM *item = dict->items.list; + while (item) { + // cache item->next + // because we are going to free item + DICTIONARY_ITEM *item_next = item->next; -void *dictionary_set_unsafe(DICTIONARY *dict, const char *name, void *value, size_t value_len) { - NAME_VALUE *nv = dictionary_set_name_value_unsafe(dict, name, value, value_len); + item_size += dict_item_free_with_hooks(dict, item); + item = item_next; - if(unlikely(dict->react_callback && nv && (nv->flags & NAME_VALUE_FLAG_NEW_OR_UPDATED))) { - // we need to call the react callback with a reference counter on nv - reference_counter_acquire(dict, nv); - dict->react_callback(namevalue_get_name(nv), nv->value, dict->react_callback_data); - reference_counter_release(dict, nv, false); + // to speed up destruction, we don't + // unlink item from the linked-list here + + counted_items++; } + dict->items.list = NULL; + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE); + + dict_size += dictionary_locks_destroy(dict); + dict_size += reference_counter_free(dict); + dict_size += dictionary_hooks_free(dict); + dict_size += sizeof(DICTIONARY); + DICTIONARY_STATS_MINUS_MEMORY(dict, 0, sizeof(DICTIONARY), 0); + + freez(dict); + + internal_error( + false, + "DICTIONARY: Freed dictionary created from %s() %zu@%s, having %ld (counted %zu) entries, %ld referenced, %ld pending deletion, total freed memory: %zu bytes (sizeof(dict) = %zu, sizeof(item) = %zu).", + creation_function, + creation_line, + creation_file, + entries, counted_items, referenced_items, pending_deletion_items, + dict_size + item_size, sizeof(DICTIONARY), sizeof(DICTIONARY_ITEM) + sizeof(DICTIONARY_ITEM_SHARED)); - return nv ? nv->value : NULL; + if(mem) + *mem = dict_size + item_size + index_size; + + return true; } -void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) { - dictionary_lock(dict, DICTIONARY_LOCK_WRITE); - NAME_VALUE *nv = dictionary_set_name_value_unsafe(dict, name, value, value_len); +netdata_mutex_t dictionaries_waiting_to_be_destroyed_mutex = NETDATA_MUTEX_INITIALIZER; +static DICTIONARY *dictionaries_waiting_to_be_destroyed = NULL; + +void dictionary_queue_for_destruction(DICTIONARY *dict) { + if(is_dictionary_destroyed(dict)) + return; - // we need to get a reference counter for the react callback - // before we unlock the dictionary - if(unlikely(dict->react_callback && nv && (nv->flags & NAME_VALUE_FLAG_NEW_OR_UPDATED))) - reference_counter_acquire(dict, nv); + DICTIONARY_STATS_DICT_DESTROY_QUEUED_PLUS1(dict); + dict_flag_set(dict, DICT_FLAG_DESTROYED); - dictionary_unlock(dict, DICTIONARY_LOCK_WRITE); + netdata_mutex_lock(&dictionaries_waiting_to_be_destroyed_mutex); - if(unlikely(dict->react_callback && nv && (nv->flags & NAME_VALUE_FLAG_NEW_OR_UPDATED))) { - // we got the reference counter we need, above - dict->react_callback(namevalue_get_name(nv), nv->value, dict->react_callback_data); - reference_counter_release(dict, nv, false); - } + dict->next = dictionaries_waiting_to_be_destroyed; + dictionaries_waiting_to_be_destroyed = dict; - return nv ? nv->value : NULL; + netdata_mutex_unlock(&dictionaries_waiting_to_be_destroyed_mutex); } -DICTIONARY_ITEM *dictionary_set_and_acquire_item_unsafe(DICTIONARY *dict, const char *name, void *value, size_t value_len) { - NAME_VALUE *nv = dictionary_set_name_value_unsafe(dict, name, value, value_len); +void cleanup_destroyed_dictionaries(void) { + if(!dictionaries_waiting_to_be_destroyed) + return; + + netdata_mutex_lock(&dictionaries_waiting_to_be_destroyed_mutex); - if(unlikely(!nv)) - return NULL; + DICTIONARY *dict, *last = NULL, *next = NULL; + for(dict = dictionaries_waiting_to_be_destroyed; dict ; dict = next) { + next = dict->next; + +#ifdef NETDATA_INTERNAL_CHECKS + size_t line = dict->creation_line; + const char *file = dict->creation_file; + const char *function = dict->creation_function; +#endif - reference_counter_acquire(dict, nv); + DICTIONARY_STATS_DICT_DESTROY_QUEUED_MINUS1(dict); + if(dictionary_free_all_resources(dict, NULL, false)) { - if(unlikely(dict->react_callback && (nv->flags & NAME_VALUE_FLAG_NEW_OR_UPDATED))) { - dict->react_callback(namevalue_get_name(nv), nv->value, dict->react_callback_data); + internal_error( + true, + "DICTIONARY: freed dictionary with delayed destruction, created from %s() %zu@%s.", + function, line, file); + + if(last) last->next = next; + else dictionaries_waiting_to_be_destroyed = next; + } + else { + DICTIONARY_STATS_DICT_DESTROY_QUEUED_PLUS1(dict); + last = dict; + } } - return (DICTIONARY_ITEM *)nv; + netdata_mutex_unlock(&dictionaries_waiting_to_be_destroyed_mutex); } -DICTIONARY_ITEM *dictionary_set_and_acquire_item(DICTIONARY *dict, const char *name, void *value, size_t value_len) { - dictionary_lock(dict, DICTIONARY_LOCK_WRITE); - NAME_VALUE *nv = dictionary_set_name_value_unsafe(dict, name, value, value_len); +// ---------------------------------------------------------------------------- +// API internal checks + +#ifdef NETDATA_INTERNAL_CHECKS +#define api_internal_check(dict, item, allow_null_dict, allow_null_item) api_internal_check_with_trace(dict, item, __FUNCTION__, allow_null_dict, allow_null_item) +static inline void api_internal_check_with_trace(DICTIONARY *dict, DICTIONARY_ITEM *item, const char *function, bool allow_null_dict, bool allow_null_item) { + if(!allow_null_dict && !dict) { + internal_error( + item, + "DICTIONARY: attempted to %s() with a NULL dictionary, passing an item created from %s() %zu@%s.", + function, + item->dict->creation_function, + item->dict->creation_line, + item->dict->creation_file); + fatal("DICTIONARY: attempted to %s() but dict is NULL", function); + } + + if(!allow_null_item && !item) { + internal_error( + true, + "DICTIONARY: attempted to %s() without an item on a dictionary created from %s() %zu@%s.", + function, + dict?dict->creation_function:"unknown", + dict?dict->creation_line:0, + dict?dict->creation_file:"unknown"); + fatal("DICTIONARY: attempted to %s() but item is NULL", function); + } + + if(dict && item && dict != item->dict) { + internal_error( + true, + "DICTIONARY: attempted to %s() an item on a dictionary created from %s() %zu@%s, but the item belongs to the dictionary created from %s() %zu@%s.", + function, + dict->creation_function, + dict->creation_line, + dict->creation_file, + item->dict->creation_function, + item->dict->creation_line, + item->dict->creation_file + ); + fatal("DICTIONARY: %s(): item does not belong to this dictionary.", function); + } - // we need to get the reference counter before we unlock - if(nv) reference_counter_acquire(dict, nv); + if(item) { + REFCOUNT refcount = DICTIONARY_ITEM_REFCOUNT_GET(dict, item); + if (unlikely(refcount <= 0)) { + internal_error( + true, + "DICTIONARY: attempted to %s() of an item with reference counter = %d on a dictionary created from %s() %zu@%s", + function, + refcount, + item->dict->creation_function, + item->dict->creation_line, + item->dict->creation_file); + fatal("DICTIONARY: attempted to %s but item is having refcount = %d", function, refcount); + } + } +} +#else +#define api_internal_check(dict, item, allow_null_dict, allow_null_item) debug_dummy() +#endif - dictionary_unlock(dict, DICTIONARY_LOCK_WRITE); +#define api_is_name_good(dict, name, name_len) api_is_name_good_with_trace(dict, name, name_len, __FUNCTION__) +static bool api_is_name_good_with_trace(DICTIONARY *dict __maybe_unused, const char *name, ssize_t name_len __maybe_unused, const char *function __maybe_unused) { + if(unlikely(!name)) { + internal_error( + true, + "DICTIONARY: attempted to %s() with name = NULL on a dictionary created from %s() %zu@%s.", + function, + dict?dict->creation_function:"unknown", + dict?dict->creation_line:0, + dict?dict->creation_file:"unknown"); + return false; + } - if(unlikely(dict->react_callback && nv && (nv->flags & NAME_VALUE_FLAG_NEW_OR_UPDATED))) { - // we already have a reference counter, for the caller, no need for another one - dict->react_callback(namevalue_get_name(nv), nv->value, dict->react_callback_data); + if(unlikely(!*name)) { + internal_error( + true, + "DICTIONARY: attempted to %s() with empty name on a dictionary created from %s() %zu@%s.", + function, + dict?dict->creation_function:"unknown", + dict?dict->creation_line:0, + dict?dict->creation_file:"unknown"); + return false; } - return (DICTIONARY_ITEM *)nv; + internal_error( + name_len > 0 && name_len != (ssize_t)(strlen(name) + 1), + "DICTIONARY: attempted to %s() with a name of '%s', having length of %zu (incl. '\\0'), but the supplied name_len = %ld, on a dictionary created from %s() %zu@%s.", + function, + name, + strlen(name) + 1, + (long int) name_len, + dict?dict->creation_function:"unknown", + dict?dict->creation_line:0, + dict?dict->creation_file:"unknown"); + + internal_error( + name_len <= 0 && name_len != -1, + "DICTIONARY: attempted to %s() with a name of '%s', having length of %zu (incl. '\\0'), but the supplied name_len = %ld, on a dictionary created from %s() %zu@%s.", + function, + name, + strlen(name) + 1, + (long int) name_len, + dict?dict->creation_function:"unknown", + dict?dict->creation_line:0, + dict?dict->creation_file:"unknown"); + + return true; } -void *dictionary_get_unsafe(DICTIONARY *dict, const char *name) { - NAME_VALUE *nv = dictionary_get_name_value_unsafe(dict, name); +// ---------------------------------------------------------------------------- +// API - dictionary management - if(unlikely(!nv)) - return NULL; +static DICTIONARY *dictionary_create_internal(DICT_OPTIONS options, struct dictionary_stats *stats) { + cleanup_destroyed_dictionaries(); + + DICTIONARY *dict = callocz(1, sizeof(DICTIONARY)); + dict->options = options; + dict->stats = stats; + + size_t dict_size = 0; + dict_size += sizeof(DICTIONARY); + dict_size += dictionary_locks_init(dict); + dict_size += reference_counter_init(dict); + dict_size += hashtable_init_unsafe(dict); + + pointer_index_init(dict); + + DICTIONARY_STATS_PLUS_MEMORY(dict, 0, dict_size, 0); - return nv->value; + return dict; } -void *dictionary_get(DICTIONARY *dict, const char *name) { - dictionary_lock(dict, DICTIONARY_LOCK_READ); - void *ret = dictionary_get_unsafe(dict, name); - dictionary_unlock(dict, DICTIONARY_LOCK_READ); - return ret; +#ifdef NETDATA_INTERNAL_CHECKS +DICTIONARY *dictionary_create_advanced_with_trace(DICT_OPTIONS options, struct dictionary_stats *stats, const char *function, size_t line, const char *file) { +#else +DICTIONARY *dictionary_create_advanced(DICT_OPTIONS options, struct dictionary_stats *stats) { +#endif + + DICTIONARY *dict = dictionary_create_internal(options, stats?stats:&dictionary_stats_category_other); + +#ifdef NETDATA_INTERNAL_CHECKS + dict->creation_function = function; + dict->creation_file = file; + dict->creation_line = line; +#endif + + DICTIONARY_STATS_DICT_CREATIONS_PLUS1(dict); + return dict; } -DICTIONARY_ITEM *dictionary_get_and_acquire_item_unsafe(DICTIONARY *dict, const char *name) { - NAME_VALUE *nv = dictionary_get_name_value_unsafe(dict, name); +#ifdef NETDATA_INTERNAL_CHECKS +DICTIONARY *dictionary_create_view_with_trace(DICTIONARY *master, const char *function, size_t line, const char *file) { +#else +DICTIONARY *dictionary_create_view(DICTIONARY *master) { +#endif - if(unlikely(!nv)) - return NULL; + DICTIONARY *dict = dictionary_create_internal(master->options, master->stats); + dict->master = master; + + dictionary_hooks_allocate(master); + + if(unlikely(__atomic_load_n(&master->hooks->links, __ATOMIC_SEQ_CST)) < 1) + fatal("DICTIONARY: attempted to create a view that has %d links", master->hooks->links); - reference_counter_acquire(dict, nv); - return (DICTIONARY_ITEM *)nv; + dict->hooks = master->hooks; + __atomic_add_fetch(&master->hooks->links, 1, __ATOMIC_SEQ_CST); + +#ifdef NETDATA_INTERNAL_CHECKS + dict->creation_function = function; + dict->creation_file = file; + dict->creation_line = line; +#endif + + DICTIONARY_STATS_DICT_CREATIONS_PLUS1(dict); + return dict; } -DICTIONARY_ITEM *dictionary_get_and_acquire_item(DICTIONARY *dict, const char *name) { - dictionary_lock(dict, DICTIONARY_LOCK_READ); - void *ret = dictionary_get_and_acquire_item_unsafe(dict, name); - dictionary_unlock(dict, DICTIONARY_LOCK_READ); - return ret; +void dictionary_flush(DICTIONARY *dict) { + if(unlikely(!dict)) + return; + + void *value; + dfe_start_write(dict, value) { + dictionary_del_advanced(dict, item_get_name(value_dfe.item), (ssize_t)item_get_name_len(value_dfe.item) + 1); + } + dfe_done(value); + + DICTIONARY_STATS_DICT_FLUSHES_PLUS1(dict); } -DICTIONARY_ITEM *dictionary_acquired_item_dup(DICTIONARY_ITEM *item) { - if(unlikely(!item)) return NULL; - reference_counter_increase((NAME_VALUE *)item); +size_t dictionary_destroy(DICTIONARY *dict) { + cleanup_destroyed_dictionaries(); + + if(!dict) return 0; + + ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE); + + dict_flag_set(dict, DICT_FLAG_DESTROYED); + DICTIONARY_STATS_DICT_DESTRUCTIONS_PLUS1(dict); + + size_t referenced_items = dictionary_referenced_items(dict); + if(referenced_items) { + dictionary_flush(dict); + dictionary_queue_for_destruction(dict); + + internal_error( + true, + "DICTIONARY: delaying destruction of dictionary created from %s() %zu@%s, because it has %ld referenced items in it (%ld total).", + dict->creation_function, + dict->creation_line, + dict->creation_file, + dict->referenced_items, + dict->entries); + + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE); + return 0; + } + + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE); + + size_t freed; + dictionary_free_all_resources(dict, &freed, true); + + return freed; +} + +// ---------------------------------------------------------------------------- +// SET an item to the dictionary + +DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_set_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data) { + if(unlikely(!api_is_name_good(dict, name, name_len))) + return NULL; + + api_internal_check(dict, NULL, false, true); + + if(unlikely(is_view_dictionary(dict))) + fatal("DICTIONARY: this dictionary is a view, you cannot add items other than the ones from the master dictionary."); + + DICTIONARY_ITEM *item = + dict_item_add_or_reset_value_and_acquire(dict, name, name_len, value, value_len, constructor_data, NULL); + api_internal_check(dict, item, false, false); return item; } -const char *dictionary_acquired_item_name(DICTIONARY_ITEM *item) { - if(unlikely(!item)) return NULL; - return namevalue_get_name((NAME_VALUE *)item); +void *dictionary_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data) { + DICTIONARY_ITEM *item = dictionary_set_and_acquire_item_advanced(dict, name, name_len, value, value_len, constructor_data); + + if(likely(item)) { + void *v = item->shared->value; + item_release(dict, item); + return v; + } + + return NULL; } -void *dictionary_acquired_item_value(DICTIONARY_ITEM *item) { - if(unlikely(!item)) return NULL; - return ((NAME_VALUE *)item)->value; +DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_view_set_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICTIONARY_ITEM *master_item) { + if(unlikely(!api_is_name_good(dict, name, name_len))) + return NULL; + + api_internal_check(dict, NULL, false, true); + + if(unlikely(is_master_dictionary(dict))) + fatal("DICTIONARY: this dictionary is a master, you cannot add items from other dictionaries."); + + dictionary_acquired_item_dup(dict->master, master_item); + DICTIONARY_ITEM *item = dict_item_add_or_reset_value_and_acquire(dict, name, name_len, NULL, 0, NULL, master_item); + dictionary_acquired_item_release(dict->master, master_item); + + api_internal_check(dict, item, false, false); + return item; } -void dictionary_acquired_item_release_unsafe(DICTIONARY *dict, DICTIONARY_ITEM *item) { - if(unlikely(!item)) return; +void *dictionary_view_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICTIONARY_ITEM *master_item) { + DICTIONARY_ITEM *item = dictionary_view_set_and_acquire_item_advanced(dict, name, name_len, master_item); -#ifdef NETDATA_INTERNAL_CHECKS - if(((NAME_VALUE *)item)->dict != dict) - fatal("DICTIONARY: %s(): name_value item with name '%s' does not belong to this dictionary", __FUNCTION__, namevalue_get_name((NAME_VALUE *)item)); -#endif + if(likely(item)) { + void *v = item->shared->value; + item_release(dict, item); + return v; + } - reference_counter_release(dict, (NAME_VALUE *)item, false); + return NULL; } -void dictionary_acquired_item_release(DICTIONARY *dict, DICTIONARY_ITEM *item) { - if(unlikely(!item)) return; +// ---------------------------------------------------------------------------- +// GET an item from the dictionary -#ifdef NETDATA_INTERNAL_CHECKS - if(((NAME_VALUE *)item)->dict != dict) - fatal("DICTIONARY: %s(): name_value item with name '%s' does not belong to this dictionary", __FUNCTION__, namevalue_get_name((NAME_VALUE *)item)); -#endif +DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_get_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len) { + if(unlikely(!api_is_name_good(dict, name, name_len))) + return NULL; - // no need to get a lock here - // we pass the last parameter to reference_counter_release() as true - // so that the release may get a write-lock if required to clean up + api_internal_check(dict, NULL, false, true); + DICTIONARY_ITEM *item = dict_item_find_and_acquire(dict, name, name_len); + api_internal_check(dict, item, false, true); + return item; +} - reference_counter_release(dict, (NAME_VALUE *)item, true); +void *dictionary_get_advanced(DICTIONARY *dict, const char *name, ssize_t name_len) { + DICTIONARY_ITEM *item = dictionary_get_and_acquire_item_advanced(dict, name, name_len); - if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) - dictionary_destroy(dict); + if(likely(item)) { + void *v = item->shared->value; + item_release(dict, item); + return v; + } + + return NULL; } -int dictionary_del_unsafe(DICTIONARY *dict, const char *name) { - if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) { - internal_error(true, "DICTIONARY: attempted to dictionary_del() on a destroyed dictionary"); - return -1; - } +// ---------------------------------------------------------------------------- +// DUP/REL an item (increase/decrease its reference counter) - if(unlikely(!name || !*name)) { - internal_error(true, "DICTIONARY: attempted to dictionary_del() without a name"); - return -1; +DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_acquired_item_dup(DICTIONARY *dict, DICT_ITEM_CONST DICTIONARY_ITEM *item) { + // we allow the item to be NULL here + api_internal_check(dict, item, false, true); + + if(likely(item)) { + item_acquire(dict, item); + api_internal_check(dict, item, false, false); } - internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: INTERNAL ERROR: deleting dictionary item '%s' without exclusive access to dictionary", name); + return item; +} - size_t name_len = strlen(name) + 1; // we need the terminating null too +void dictionary_acquired_item_release(DICTIONARY *dict, DICT_ITEM_CONST DICTIONARY_ITEM *item) { + // we allow the item to be NULL here + api_internal_check(dict, item, false, true); - debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name); + // no need to get a lock here + // we pass the last parameter to reference_counter_release() as true + // so that the release may get a write-lock if required to clean up - // Unfortunately, the JudyHSDel() does not return the value of the - // item that was deleted, so we have to find it before we delete it, - // since we need to release our structures too. + if(likely(item)) + item_release(dict, item); +} - int ret; - NAME_VALUE *nv = hashtable_get_unsafe(dict, name, name_len); - if(unlikely(!nv)) { - debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name); - ret = -1; - } - else { - debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name); +// ---------------------------------------------------------------------------- +// get the name/value of an item - if(hashtable_delete_unsafe(dict, name, name_len, nv) == 0) - error("DICTIONARY: INTERNAL ERROR: tried to delete item with name '%s' that is not in the index", name); +const char *dictionary_acquired_item_name(DICT_ITEM_CONST DICTIONARY_ITEM *item) { + return item_get_name(item); +} - if(name_value_can_be_deleted(dict, nv)) { - linkedlist_namevalue_unlink_unsafe(dict, nv); - namevalue_destroy_unsafe(dict, nv); - } - else - nv->flags |= NAME_VALUE_FLAG_DELETED; +void *dictionary_acquired_item_value(DICT_ITEM_CONST DICTIONARY_ITEM *item) { + if(likely(item)) + return item->shared->value; - ret = 0; + return NULL; +} - DICTIONARY_STATS_ENTRIES_MINUS1(dict); +size_t dictionary_acquired_item_references(DICT_ITEM_CONST DICTIONARY_ITEM *item) { + if(likely(item)) + return DICTIONARY_ITEM_REFCOUNT_GET_SOLE(item); - } - return ret; + return 0; } -int dictionary_del(DICTIONARY *dict, const char *name) { - dictionary_lock(dict, DICTIONARY_LOCK_WRITE); - int ret = dictionary_del_unsafe(dict, name); - dictionary_unlock(dict, DICTIONARY_LOCK_WRITE); - return ret; +// ---------------------------------------------------------------------------- +// DEL an item + +bool dictionary_del_advanced(DICTIONARY *dict, const char *name, ssize_t name_len) { + if(unlikely(!api_is_name_good(dict, name, name_len))) + return false; + + api_internal_check(dict, NULL, false, true); + return dict_item_del(dict, name, name_len); } // ---------------------------------------------------------------------------- @@ -1245,150 +2201,165 @@ int dictionary_del(DICTIONARY *dict, const char *name) { void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw) { if(unlikely(!dfe || !dict)) return NULL; - if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) { + if(unlikely(is_dictionary_destroyed(dict))) { internal_error(true, "DICTIONARY: attempted to dictionary_foreach_start_rw() on a destroyed dictionary"); - dfe->last_item = NULL; + dfe->counter = 0; + dfe->item = NULL; dfe->name = NULL; dfe->value = NULL; return NULL; } + dfe->counter = 0; dfe->dict = dict; dfe->rw = rw; - dfe->started_ut = now_realtime_usec(); - dictionary_lock(dict, dfe->rw); + ll_recursive_lock(dict, dfe->rw); - DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict); + DICTIONARY_STATS_TRAVERSALS_PLUS1(dict); // get the first item from the list - NAME_VALUE *nv = dict->first_item; + DICTIONARY_ITEM *item = dict->items.list; // skip all the deleted items - while(nv && (nv->flags & NAME_VALUE_FLAG_DELETED)) - nv = nv->next; + while(item && !item_check_and_acquire(dict, item)) + item = item->next; - if(likely(nv)) { - dfe->last_item = nv; - dfe->name = (char *)namevalue_get_name(nv); - dfe->value = nv->value; - reference_counter_acquire(dict, nv); + if(likely(item)) { + dfe->item = item; + dfe->name = (char *)item_get_name(item); + dfe->value = item->shared->value; } else { - dfe->last_item = NULL; + dfe->item = NULL; dfe->name = NULL; dfe->value = NULL; } + if(unlikely(dfe->rw == DICTIONARY_LOCK_REENTRANT)) + ll_recursive_unlock(dfe->dict, dfe->rw); + return dfe->value; } void *dictionary_foreach_next(DICTFE *dfe) { if(unlikely(!dfe || !dfe->dict)) return NULL; - if(unlikely(dfe->dict->flags & DICTIONARY_FLAG_DESTROYED)) { + if(unlikely(is_dictionary_destroyed(dfe->dict))) { internal_error(true, "DICTIONARY: attempted to dictionary_foreach_next() on a destroyed dictionary"); - dfe->last_item = NULL; + dfe->item = NULL; dfe->name = NULL; dfe->value = NULL; return NULL; } + if(unlikely(dfe->rw == DICTIONARY_LOCK_REENTRANT)) + ll_recursive_lock(dfe->dict, dfe->rw); + // the item we just did - NAME_VALUE *nv = (NAME_VALUE *)dfe->last_item; + DICTIONARY_ITEM *item = dfe->item; // get the next item from the list - NAME_VALUE *nv_next = (nv) ? nv->next : NULL; + DICTIONARY_ITEM *item_next = (item) ? item->next : NULL; - // skip all the deleted items - while(nv_next && (nv_next->flags & NAME_VALUE_FLAG_DELETED)) - nv_next = nv_next->next; + // skip all the deleted items until one that can be acquired is found + while(item_next && !item_check_and_acquire(dfe->dict, item_next)) + item_next = item_next->next; - // release the old, so that it can possibly be deleted - if(likely(nv)) - reference_counter_release(dfe->dict, nv, false); + if(likely(item)) { + dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dfe->dict, item, dfe->rw); + // item_release(dfe->dict, item); + } - if(likely(nv = nv_next)) { - dfe->last_item = nv; - dfe->name = (char *)namevalue_get_name(nv); - dfe->value = nv->value; - reference_counter_acquire(dfe->dict, nv); + item = item_next; + if(likely(item)) { + dfe->item = item; + dfe->name = (char *)item_get_name(item); + dfe->value = item->shared->value; + dfe->counter++; } else { - dfe->last_item = NULL; + dfe->item = NULL; dfe->name = NULL; dfe->value = NULL; } + if(unlikely(dfe->rw == DICTIONARY_LOCK_REENTRANT)) + ll_recursive_unlock(dfe->dict, dfe->rw); + return dfe->value; } -usec_t dictionary_foreach_done(DICTFE *dfe) { - if(unlikely(!dfe || !dfe->dict)) return 0; +void dictionary_foreach_done(DICTFE *dfe) { + if(unlikely(!dfe || !dfe->dict)) return; - if(unlikely(dfe->dict->flags & DICTIONARY_FLAG_DESTROYED)) { + if(unlikely(is_dictionary_destroyed(dfe->dict))) { internal_error(true, "DICTIONARY: attempted to dictionary_foreach_next() on a destroyed dictionary"); - return 0; + return; } // the item we just did - NAME_VALUE *nv = (NAME_VALUE *)dfe->last_item; + DICTIONARY_ITEM *item = dfe->item; // release it, so that it can possibly be deleted - if(likely(nv)) - reference_counter_release(dfe->dict, nv, false); + if(likely(item)) { + dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dfe->dict, item, dfe->rw); + // item_release(dfe->dict, item); + } + + if(likely(dfe->rw != DICTIONARY_LOCK_REENTRANT)) + ll_recursive_unlock(dfe->dict, dfe->rw); - dictionary_unlock(dfe->dict, dfe->rw); dfe->dict = NULL; - dfe->last_item = NULL; + dfe->item = NULL; dfe->name = NULL; dfe->value = NULL; - - usec_t usec = now_realtime_usec() - dfe->started_ut; - dfe->started_ut = 0; - - return usec; + dfe->counter = 0; } // ---------------------------------------------------------------------------- -// API - walk through the dictionary -// the dictionary is locked for reading while this happens +// API - walk through the dictionary. +// The dictionary is locked for reading while this happens // do not use other dictionary calls while walking the dictionary - deadlock! -int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const char *name, void *entry, void *data), void *data) { - if(unlikely(!dict)) return 0; +int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const DICTIONARY_ITEM *item, void *entry, void *data), void *data) { + if(unlikely(!dict || !callback)) return 0; - if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) { + if(unlikely(is_dictionary_destroyed(dict))) { internal_error(true, "DICTIONARY: attempted to dictionary_walkthrough_rw() on a destroyed dictionary"); return 0; } - dictionary_lock(dict, rw); + ll_recursive_lock(dict, rw); DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict); // written in such a way, that the callback can delete the active element int ret = 0; - NAME_VALUE *nv = dict->first_item, *nv_next; - while(nv) { + DICTIONARY_ITEM *item = dict->items.list, *item_next; + while(item) { // skip the deleted items - if(unlikely(nv->flags & NAME_VALUE_FLAG_DELETED)) { - nv = nv->next; + if(unlikely(!item_check_and_acquire(dict, item))) { + item = item->next; continue; } - // get a reference counter, so that our item will not be deleted - // while we are using it - reference_counter_acquire(dict, nv); + if(unlikely(rw == DICTIONARY_LOCK_REENTRANT)) + ll_recursive_unlock(dict, rw); - int r = callback(namevalue_get_name(nv), nv->value, data); + int r = callback(item, item->shared->value, data); + + if(unlikely(rw == DICTIONARY_LOCK_REENTRANT)) + ll_recursive_lock(dict, rw); // since we have a reference counter, this item cannot be deleted // until we release the reference counter, so the pointers are there - nv_next = nv->next; - reference_counter_release(dict, nv, false); + item_next = item->next; + + dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dict, item, rw); + // item_release(dict, item); if(unlikely(r < 0)) { ret = r; @@ -1397,10 +2368,10 @@ int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const c ret += r; - nv = nv_next; + item = item_next; } - dictionary_unlock(dict, rw); + ll_recursive_unlock(dict, rw); return ret; } @@ -1408,238 +2379,114 @@ int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const c // ---------------------------------------------------------------------------- // sorted walkthrough -static int dictionary_sort_compar(const void *nv1, const void *nv2) { - return strcmp(namevalue_get_name((*(NAME_VALUE **)nv1)), namevalue_get_name((*(NAME_VALUE **)nv2))); +typedef int (*qsort_compar)(const void *item1, const void *item2); + +static int dictionary_sort_compar(const void *item1, const void *item2) { + return strcmp(item_get_name((*(DICTIONARY_ITEM **)item1)), item_get_name((*(DICTIONARY_ITEM **)item2))); } -int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const char *name, void *entry, void *data), void *data) { - if(unlikely(!dict || !dict->entries)) return 0; +int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const DICTIONARY_ITEM *item, void *entry, void *data), void *data, dictionary_sorted_compar compar) { + if(unlikely(!dict || !callback)) return 0; - if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) { + if(unlikely(is_dictionary_destroyed(dict))) { internal_error(true, "DICTIONARY: attempted to dictionary_sorted_walkthrough_rw() on a destroyed dictionary"); return 0; } - dictionary_lock(dict, rw); - dictionary_defer_all_deletions_unsafe(dict, rw); - DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict); - size_t count = dict->entries; - NAME_VALUE **array = mallocz(sizeof(NAME_VALUE *) * count); + ll_recursive_lock(dict, rw); + size_t entries = __atomic_load_n(&dict->entries, __ATOMIC_SEQ_CST); + DICTIONARY_ITEM **array = mallocz(sizeof(DICTIONARY_ITEM *) * entries); size_t i; - NAME_VALUE *nv; - for(nv = dict->first_item, i = 0; nv && i < count ;nv = nv->next) { - if(likely(!(nv->flags & NAME_VALUE_FLAG_DELETED))) - array[i++] = nv; + DICTIONARY_ITEM *item; + for(item = dict->items.list, i = 0; item && i < entries; item = item->next) { + if(likely(item_check_and_acquire(dict, item))) + array[i++] = item; } + ll_recursive_unlock(dict, rw); - internal_error(nv != NULL, "DICTIONARY: during sorting expected to have %zu items in dictionary, but there are more. Sorted results may be incomplete. Dictionary fails to maintain an accurate number of the number of entries it has.", count); + if(unlikely(i != entries)) + entries = i; - if(unlikely(i != count)) { - internal_error(true, "DICTIONARY: during sorting expected to have %zu items in dictionary, but there are %zu. Sorted results may be incomplete. Dictionary fails to maintain an accurate number of the number of entries it has.", count, i); - count = i; - } + if(compar) + qsort(array, entries, sizeof(DICTIONARY_ITEM *), (qsort_compar)compar); + else + qsort(array, entries, sizeof(DICTIONARY_ITEM *), dictionary_sort_compar); - qsort(array, count, sizeof(NAME_VALUE *), dictionary_sort_compar); + bool callit = true; + int ret = 0, r; + for(i = 0; i < entries ;i++) { + item = array[i]; - int ret = 0; - for(i = 0; i < count ;i++) { - nv = array[i]; - if(likely(!(nv->flags & NAME_VALUE_FLAG_DELETED))) { - reference_counter_acquire(dict, nv); - int r = callback(namevalue_get_name(nv), nv->value, data); - reference_counter_release(dict, nv, false); - if (r < 0) { - ret = r; - break; - } - ret += r; + if(callit) + r = callback(item, item->shared->value, data); + + dict_item_release_and_check_if_it_is_deleted_and_can_be_removed_under_this_lock_mode(dict, item, rw); + // item_release(dict, item); + + if(r < 0) { + ret = r; + r = 0; + + // stop calling the callback, + // but we have to continue, to release all the reference counters + callit = false; } + else + ret += r; } - dictionary_restore_all_deletions_unsafe(dict, rw); - dictionary_unlock(dict, rw); freez(array); return ret; } // ---------------------------------------------------------------------------- -// STRING implementation - dedup all STRINGs - -typedef struct string_entry { -#ifdef DICTIONARY_WITH_AVL - avl_t avl_node; -#endif - uint32_t length; // the string length with the terminating '\0' - uint32_t refcount; // how many times this string is used - const char str[]; // the string itself -} STRING_ENTRY; - -#ifdef DICTIONARY_WITH_AVL -static int string_entry_compare(void* a, void* b) { - return strcmp(((STRING_ENTRY *)a)->str, ((STRING_ENTRY *)b)->str); -} - -static void *get_thread_static_string_entry(const char *name) { - static __thread size_t _length = 0; - static __thread STRING_ENTRY *_tmp = NULL; - - size_t size = sizeof(STRING_ENTRY) + strlen(name) + 1; - if(likely(_tmp && _length < size)) { - freez(_tmp); - _tmp = NULL; - _length = 0; - } - - if(unlikely(!_tmp)) { - _tmp = callocz(1, size); - _length = size; - } - - strcpy((char *)&_tmp->str[0], name); - return _tmp; -} -#endif - -DICTIONARY string_dictionary = { -#ifdef DICTIONARY_WITH_AVL - .values_index = { - .root = NULL, - .compar = string_entry_compare - }, - .get_thread_static_name_value = get_thread_static_string_entry, -#endif - - .flags = DICTIONARY_FLAG_EXCLUSIVE_ACCESS, - .rwlock = NETDATA_RWLOCK_INITIALIZER -}; - -static netdata_mutex_t string_mutex = NETDATA_MUTEX_INITIALIZER; +// THREAD_CACHE -STRING *string_dup(STRING *string) { - if(unlikely(!string)) return NULL; +static __thread Pvoid_t thread_cache_judy_array = NULL; - STRING_ENTRY *se = (STRING_ENTRY *)string; - netdata_mutex_lock(&string_mutex); - se->refcount++; - netdata_mutex_unlock(&string_mutex); - return string; -} - -STRING *string_strdupz(const char *str) { - if(unlikely(!str || !*str)) return NULL; +void *thread_cache_entry_get_or_set(void *key, + ssize_t key_length, + void *value, + void *(*transform_the_value_before_insert)(void *key, size_t key_length, void *value) + ) { + if(unlikely(!key || !key_length)) return NULL; - netdata_mutex_lock(&string_mutex); + if(key_length == -1) + key_length = (ssize_t)strlen((char *)key) + 1; - size_t length = strlen(str) + 1; - STRING_ENTRY *se; - STRING_ENTRY **ptr = (STRING_ENTRY **)hashtable_insert_unsafe(&string_dictionary, str, length); - if(unlikely(*ptr == 0)) { - // a new item added to the index - size_t mem_size = sizeof(STRING_ENTRY) + length; - se = mallocz(mem_size); - strcpy((char *)se->str, str); - se->length = length; - se->refcount = 1; - *ptr = se; - hashtable_inserted_name_value_unsafe(&string_dictionary, se); - string_dictionary.version++; - string_dictionary.inserts++; - string_dictionary.entries++; - string_dictionary.memory += (long)mem_size; - } - else { - // the item is already in the index - se = *ptr; - se->refcount++; - string_dictionary.searches++; + JError_t J_Error; + Pvoid_t *Rc = JudyHSIns(&thread_cache_judy_array, key, key_length, &J_Error); + if (unlikely(Rc == PJERR)) { + fatal("THREAD_CACHE: Cannot insert entry to JudyHS, JU_ERRNO_* == %u, ID == %d", + JU_ERRNO(&J_Error), JU_ERRID(&J_Error)); } - netdata_mutex_unlock(&string_mutex); - return (STRING *)se; -} - -void string_freez(STRING *string) { - if(unlikely(!string)) return; - netdata_mutex_lock(&string_mutex); - - STRING_ENTRY *se = (STRING_ENTRY *)string; - - if(se->refcount == 0) - fatal("STRING: tried to free string that has zero references."); - - se->refcount--; - if(unlikely(se->refcount == 0)) { - if(hashtable_delete_unsafe(&string_dictionary, se->str, se->length, se) == 0) - error("STRING: INTERNAL ERROR: tried to delete '%s' that is not in the index", se->str); + if(*Rc == 0) { + // new item added - size_t mem_size = sizeof(STRING_ENTRY) + se->length; - freez(se); - string_dictionary.version++; - string_dictionary.deletes++; - string_dictionary.entries--; - string_dictionary.memory -= (long)mem_size; + *Rc = (transform_the_value_before_insert) ? transform_the_value_before_insert(key, key_length, value) : value; } - netdata_mutex_unlock(&string_mutex); -} - -size_t string_length(STRING *string) { - if(unlikely(!string)) return 0; - return ((STRING_ENTRY *)string)->length - 1; -} - -const char *string2str(STRING *string) { - if(unlikely(!string)) return ""; - return ((STRING_ENTRY *)string)->str; + return *Rc; } -STRING *string_2way_merge(STRING *a, STRING *b) { - static STRING *X = NULL; +void thread_cache_destroy(void) { + if(unlikely(!thread_cache_judy_array)) return; - if(unlikely(!X)) { - X = string_strdupz("[x]"); + JError_t J_Error; + Word_t ret = JudyHSFreeArray(&thread_cache_judy_array, &J_Error); + if(unlikely(ret == (Word_t) JERR)) { + error("THREAD_CACHE: Cannot destroy JudyHS, JU_ERRNO_* == %u, ID == %d", + JU_ERRNO(&J_Error), JU_ERRID(&J_Error)); } - if(unlikely(a == b)) return string_dup(a); - if(unlikely(a == X)) return string_dup(a); - if(unlikely(b == X)) return string_dup(b); - if(unlikely(!a)) return string_dup(X); - if(unlikely(!b)) return string_dup(X); - - size_t alen = string_length(a); - size_t blen = string_length(b); - size_t length = alen + blen + string_length(X) + 1; - char buf1[length + 1], buf2[length + 1], *dst1; - const char *s1, *s2; - - s1 = string2str(a); - s2 = string2str(b); - dst1 = buf1; - for( ; *s1 && *s2 && *s1 == *s2 ;s1++, s2++) - *dst1++ = *s1; - - *dst1 = '\0'; - - if(*s1 != '\0' || *s2 != '\0') { - *dst1++ = '['; - *dst1++ = 'x'; - *dst1++ = ']'; - - s1 = &(string2str(a))[alen - 1]; - s2 = &(string2str(b))[blen - 1]; - char *dst2 = &buf2[length]; - *dst2 = '\0'; - for (; *s1 && *s2 && *s1 == *s2; s1--, s2--) - *(--dst2) = *s1; + internal_error(true, "THREAD_CACHE: hash table freed %lu bytes", ret); - strcpy(dst1, dst2); - } - - return string_strdupz(buf1); + thread_cache_judy_array = NULL; } // ---------------------------------------------------------------------------- @@ -1656,7 +2503,7 @@ static char **dictionary_unittest_generate_names(size_t entries) { char **names = mallocz(sizeof(char *) * entries); for(size_t i = 0; i < entries ;i++) { char buf[25 + 1] = ""; - snprintfz(buf, 25, "name.%zu.0123456789.%zu \t !@#$%%^&*(),./[]{}\\|~`", i, entries / 2 + i); + snprintfz(buf, 25, "name.%zu.0123456789.%zu!@#$%%^&*(),./[]{}\\|~`", i, entries / 2 + i); names[i] = strdupz(buf); } return names; @@ -1686,12 +2533,12 @@ static size_t dictionary_unittest_set_clone(DICTIONARY *dict, char **names, char static size_t dictionary_unittest_set_null(DICTIONARY *dict, char **names, char **values, size_t entries) { (void)values; size_t errors = 0; - long i = 0; - for(; i < (long)entries ;i++) { + size_t i = 0; + for(; i < entries ;i++) { void *val = dictionary_set(dict, names[i], NULL, 0); if(val != NULL) { fprintf(stderr, ">>> %s() returns a non NULL value\n", __FUNCTION__); errors++; } } - if(dictionary_stats_entries(dict) != i) { + if(dictionary_entries(dict) != i) { fprintf(stderr, ">>> %s() dictionary items do not match\n", __FUNCTION__); errors++; } @@ -1743,8 +2590,8 @@ static size_t dictionary_unittest_del_nonexisting(DICTIONARY *dict, char **names (void)names; size_t errors = 0; for(size_t i = 0; i < entries ;i++) { - int ret = dictionary_del(dict, values[i]); - if(ret != -1) { fprintf(stderr, ">>> %s() deleted non-existing item\n", __FUNCTION__); errors++; } + bool ret = dictionary_del(dict, values[i]); + if(ret) { fprintf(stderr, ">>> %s() deleted non-existing item\n", __FUNCTION__); errors++; } } return errors; } @@ -1758,18 +2605,18 @@ static size_t dictionary_unittest_del_existing(DICTIONARY *dict, char **names, c size_t backward_from = middle_to, backward_to = entries; for(size_t i = forward_from; i < forward_to ;i++) { - int ret = dictionary_del(dict, names[i]); - if(ret == -1) { fprintf(stderr, ">>> %s() didn't delete (forward) existing item\n", __FUNCTION__); errors++; } + bool ret = dictionary_del(dict, names[i]); + if(!ret) { fprintf(stderr, ">>> %s() didn't delete (forward) existing item\n", __FUNCTION__); errors++; } } for(size_t i = middle_to - 1; i >= middle_from ;i--) { - int ret = dictionary_del(dict, names[i]); - if(ret == -1) { fprintf(stderr, ">>> %s() didn't delete (middle) existing item\n", __FUNCTION__); errors++; } + bool ret = dictionary_del(dict, names[i]); + if(!ret) { fprintf(stderr, ">>> %s() didn't delete (middle) existing item\n", __FUNCTION__); errors++; } } for(size_t i = backward_to - 1; i >= backward_from ;i--) { - int ret = dictionary_del(dict, names[i]); - if(ret == -1) { fprintf(stderr, ">>> %s() didn't delete (backward) existing item\n", __FUNCTION__); errors++; } + bool ret = dictionary_del(dict, names[i]); + if(!ret) { fprintf(stderr, ">>> %s() didn't delete (backward) existing item\n", __FUNCTION__); errors++; } } return errors; @@ -1812,10 +2659,7 @@ static size_t dictionary_unittest_reset_dont_overwrite_nonclone(DICTIONARY *dict return errors; } -static int dictionary_unittest_walkthrough_callback(const char *name, void *value, void *data) { - (void)name; - (void)value; - (void)data; +static int dictionary_unittest_walkthrough_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *data __maybe_unused) { return 1; } @@ -1827,10 +2671,10 @@ static size_t dictionary_unittest_walkthrough(DICTIONARY *dict, char **names, ch else return sum - entries; } -static int dictionary_unittest_walkthrough_delete_this_callback(const char *name, void *value, void *data) { - (void)value; +static int dictionary_unittest_walkthrough_delete_this_callback(const DICTIONARY_ITEM *item, void *value __maybe_unused, void *data) { + const char *name = dictionary_acquired_item_name((DICTIONARY_ITEM *)item); - if(dictionary_del_having_write_lock((DICTIONARY *)data, name) == -1) + if(!dictionary_del((DICTIONARY *)data, name)) return 0; return 1; @@ -1844,10 +2688,7 @@ static size_t dictionary_unittest_walkthrough_delete_this(DICTIONARY *dict, char else return sum - entries; } -static int dictionary_unittest_walkthrough_stop_callback(const char *name, void *value, void *data) { - (void)name; - (void)value; - (void)data; +static int dictionary_unittest_walkthrough_stop_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *data __maybe_unused) { return -1; } @@ -1881,7 +2722,7 @@ static size_t dictionary_unittest_foreach_delete_this(DICTIONARY *dict, char **n size_t count = 0; char *item; dfe_start_write(dict, item) - if(dictionary_del_having_write_lock(dict, item_name) != -1) count++; + if(dictionary_del(dict, item_dfe.name)) count++; dfe_done(item); if(count > entries) return count - entries; @@ -1907,7 +2748,22 @@ static usec_t dictionary_unittest_run_and_measure_time(DICTIONARY *dict, char *m if(callback == dictionary_unittest_destroy) dict = NULL; - fprintf(stderr, " %zu errors, %ld items in dictionary, %llu usec \n", errs, dict? dictionary_stats_entries(dict):0, dt); + long int found_ok = 0, found_deleted = 0, found_referenced = 0; + if(dict) { + DICTIONARY_ITEM *item; + DOUBLE_LINKED_LIST_FOREACH_FORWARD(dict->items.list, item, prev, next) { + if(item->refcount >= 0 && !(item ->flags & ITEM_FLAG_DELETED)) + found_ok++; + else + found_deleted++; + + if(item->refcount > 0) + found_referenced++; + } + } + + fprintf(stderr, " %zu errors, %ld (found %ld) items in dictionary, %ld (found %ld) referenced, %ld (found %ld) deleted, %llu usec \n", + errs, dict?dict->entries:0, found_ok, dict?dict->referenced_items:0, found_referenced, dict?dict->pending_deletion_items:0, found_deleted, dt); *errors += errs; return dt; } @@ -1943,23 +2799,24 @@ static void dictionary_unittest_nonclone(DICTIONARY *dict, char **names, char ** } struct dictionary_unittest_sorting { - const char *oldname; - const char *oldvalue; + const char *old_name; + const char *old_value; size_t count; }; -static int dictionary_unittest_sorting_callback(const char *name, void *value, void *data) { +static int dictionary_unittest_sorting_callback(const DICTIONARY_ITEM *item, void *value, void *data) { + const char *name = dictionary_acquired_item_name((DICTIONARY_ITEM *)item); struct dictionary_unittest_sorting *t = (struct dictionary_unittest_sorting *)data; const char *v = (const char *)value; int ret = 0; - if(t->oldname && strcmp(t->oldname, name) > 0) { - fprintf(stderr, "name '%s' should be after '%s'\n", t->oldname, name); + if(t->old_name && strcmp(t->old_name, name) > 0) { + fprintf(stderr, "name '%s' should be after '%s'\n", t->old_name, name); ret = 1; } t->count++; - t->oldname = name; - t->oldvalue = v; + t->old_name = name; + t->old_value = v; return ret; } @@ -1967,7 +2824,7 @@ static int dictionary_unittest_sorting_callback(const char *name, void *value, v static size_t dictionary_unittest_sorted_walkthrough(DICTIONARY *dict, char **names, char **values, size_t entries) { (void)names; (void)values; - struct dictionary_unittest_sorting tmp = { .oldname = NULL, .oldvalue = NULL, .count = 0 }; + struct dictionary_unittest_sorting tmp = { .old_name = NULL, .old_value = NULL, .count = 0 }; size_t errors; errors = dictionary_sorted_walkthrough_read(dict, dictionary_unittest_sorting_callback, &tmp); @@ -1989,62 +2846,94 @@ static void dictionary_unittest_null_dfe(DICTIONARY *dict, char **names, char ** } -static int check_dictionary_callback(const char *name, void *value, void *data) { - (void)name; - (void)value; - (void)data; +static int unittest_check_dictionary_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *data __maybe_unused) { return 1; } -static size_t check_dictionary(DICTIONARY *dict, size_t entries, size_t linked_list_members) { +static size_t unittest_check_dictionary(const char *label, DICTIONARY *dict, size_t traversable, size_t active_items, size_t deleted_items, size_t referenced_items, size_t pending_deletion) { size_t errors = 0; - fprintf(stderr, "dictionary entries %ld, expected %zu...\t\t\t\t\t", dictionary_stats_entries(dict), entries); - if (dictionary_stats_entries(dict) != (long)entries) { + size_t ll = 0; + void *t; + dfe_start_read(dict, t) + ll++; + dfe_done(t); + + fprintf(stderr, "DICT %-20s: dictionary foreach entries %zu, expected %zu...\t\t\t\t\t", + label, ll, traversable); + if(ll != traversable) { fprintf(stderr, "FAILED\n"); errors++; } else fprintf(stderr, "OK\n"); - size_t ll = 0; - void *t; - dfe_start_read(dict, t) - ll++; - dfe_done(t); + ll = dictionary_walkthrough_read(dict, unittest_check_dictionary_callback, NULL); + fprintf(stderr, "DICT %-20s: dictionary walkthrough entries %zu, expected %zu...\t\t\t\t", + label, ll, traversable); + if(ll != traversable) { + fprintf(stderr, "FAILED\n"); + errors++; + } + else + fprintf(stderr, "OK\n"); - fprintf(stderr, "dictionary foreach entries %zu, expected %zu...\t\t\t\t", ll, entries); - if(ll != entries) { + ll = dictionary_sorted_walkthrough_read(dict, unittest_check_dictionary_callback, NULL); + fprintf(stderr, "DICT %-20s: dictionary sorted walkthrough entries %zu, expected %zu...\t\t\t", + label, ll, traversable); + if(ll != traversable) { fprintf(stderr, "FAILED\n"); errors++; } else fprintf(stderr, "OK\n"); - ll = dictionary_walkthrough_read(dict, check_dictionary_callback, NULL); - fprintf(stderr, "dictionary walkthrough entries %zu, expected %zu...\t\t\t\t", ll, entries); - if(ll != entries) { + DICTIONARY_ITEM *item; + size_t active = 0, deleted = 0, referenced = 0, pending = 0; + for(item = dict->items.list; item; item = item->next) { + if(!(item->flags & ITEM_FLAG_DELETED) && !(item->shared->flags & ITEM_FLAG_DELETED)) + active++; + else { + deleted++; + + if(item->refcount == 0) + pending++; + } + + if(item->refcount > 0) + referenced++; + } + + fprintf(stderr, "DICT %-20s: dictionary active items reported %ld, counted %zu, expected %zu...\t\t\t", + label, dict->entries, active, active_items); + if(active != active_items || active != (size_t)dict->entries) { fprintf(stderr, "FAILED\n"); errors++; } else fprintf(stderr, "OK\n"); - ll = dictionary_sorted_walkthrough_read(dict, check_dictionary_callback, NULL); - fprintf(stderr, "dictionary sorted walkthrough entries %zu, expected %zu...\t\t\t", ll, entries); - if(ll != entries) { + fprintf(stderr, "DICT %-20s: dictionary deleted items counted %zu, expected %zu...\t\t\t\t", + label, deleted, deleted_items); + if(deleted != deleted_items) { fprintf(stderr, "FAILED\n"); errors++; } else fprintf(stderr, "OK\n"); - NAME_VALUE *nv; - for(ll = 0, nv = dict->first_item; nv ;nv = nv->next) - ll++; + fprintf(stderr, "DICT %-20s: dictionary referenced items reported %ld, counted %zu, expected %zu...\t\t", + label, dict->referenced_items, referenced, referenced_items); + if(referenced != referenced_items || dict->referenced_items != (long int)referenced) { + fprintf(stderr, "FAILED\n"); + errors++; + } + else + fprintf(stderr, "OK\n"); - fprintf(stderr, "dictionary linked list entries %zu, expected %zu...\t\t\t\t", ll, linked_list_members); - if(ll != linked_list_members) { + fprintf(stderr, "DICT %-20s: dictionary pending deletion items reported %ld, counted %zu, expected %zu...\t", + label, dict->pending_deletion_items, pending, pending_deletion); + if(pending != pending_deletion || pending != (size_t)dict->pending_deletion_items) { fprintf(stderr, "FAILED\n"); errors++; } @@ -2054,40 +2943,44 @@ static size_t check_dictionary(DICTIONARY *dict, size_t entries, size_t linked_l return errors; } -static int check_name_value_callback(const char *name, void *value, void *data) { - (void)name; +static int check_item_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) { return value == data; } -static size_t check_name_value_deleted_flag(DICTIONARY *dict, NAME_VALUE *nv, const char *name, const char *value, unsigned refcount, NAME_VALUE_FLAGS deleted_flags, bool searchable, bool browsable, bool linked) { +static size_t unittest_check_item(const char *label, DICTIONARY *dict, + DICTIONARY_ITEM *item, const char *name, const char *value, int refcount, + ITEM_FLAGS deleted_flags, bool searchable, bool browsable, bool linked) { size_t errors = 0; - fprintf(stderr, "NAME_VALUE name is '%s', expected '%s'...\t\t\t\t", namevalue_get_name(nv), name); - if(strcmp(namevalue_get_name(nv), name) != 0) { + fprintf(stderr, "ITEM %-20s: name is '%s', expected '%s'...\t\t\t\t\t\t", label, item_get_name(item), name); + if(strcmp(item_get_name(item), name) != 0) { fprintf(stderr, "FAILED\n"); errors++; } else fprintf(stderr, "OK\n"); - fprintf(stderr, "NAME_VALUE value is '%s', expected '%s'...\t\t\t", (const char *)nv->value, value); - if(strcmp((const char *)nv->value, value) != 0) { + fprintf(stderr, "ITEM %-20s: value is '%s', expected '%s'...\t\t\t\t\t", label, (const char *)item->shared->value, value); + if(strcmp((const char *)item->shared->value, value) != 0) { fprintf(stderr, "FAILED\n"); errors++; } else fprintf(stderr, "OK\n"); - fprintf(stderr, "NAME_VALUE refcount is %u, expected %u...\t\t\t\t\t", nv->refcount, refcount); - if (nv->refcount != refcount) { + fprintf(stderr, "ITEM %-20s: refcount is %d, expected %d...\t\t\t\t\t\t\t", label, item->refcount, refcount); + if (item->refcount != refcount) { fprintf(stderr, "FAILED\n"); errors++; } else fprintf(stderr, "OK\n"); - fprintf(stderr, "NAME_VALUE deleted flag is %s, expected %s...\t\t\t", (nv->flags & NAME_VALUE_FLAG_DELETED)?"TRUE":"FALSE", (deleted_flags & NAME_VALUE_FLAG_DELETED)?"TRUE":"FALSE"); - if ((nv->flags & NAME_VALUE_FLAG_DELETED) != (deleted_flags & NAME_VALUE_FLAG_DELETED)) { + fprintf(stderr, "ITEM %-20s: deleted flag is %s, expected %s...\t\t\t\t\t", label, + (item->flags & ITEM_FLAG_DELETED || item->shared->flags & ITEM_FLAG_DELETED)?"true":"false", + (deleted_flags & ITEM_FLAG_DELETED)?"true":"false"); + + if ((item->flags & ITEM_FLAG_DELETED || item->shared->flags & ITEM_FLAG_DELETED) != (deleted_flags & ITEM_FLAG_DELETED)) { fprintf(stderr, "FAILED\n"); errors++; } @@ -2095,8 +2988,9 @@ static size_t check_name_value_deleted_flag(DICTIONARY *dict, NAME_VALUE *nv, co fprintf(stderr, "OK\n"); void *v = dictionary_get(dict, name); - bool found = v == nv->value; - fprintf(stderr, "NAME_VALUE searchable %5s, expected %5s...\t\t\t\t", found?"true":"false", searchable?"true":"false"); + bool found = v == item->shared->value; + fprintf(stderr, "ITEM %-20s: searchable %5s, expected %5s...\t\t\t\t\t\t", label, + found?"true":"false", searchable?"true":"false"); if(found != searchable) { fprintf(stderr, "FAILED\n"); errors++; @@ -2107,11 +3001,12 @@ static size_t check_name_value_deleted_flag(DICTIONARY *dict, NAME_VALUE *nv, co found = false; void *t; dfe_start_read(dict, t) { - if(t == nv->value) found = true; + if(t == item->shared->value) found = true; } dfe_done(t); - fprintf(stderr, "NAME_VALUE dfe browsable %5s, expected %5s...\t\t\t", found?"true":"false", browsable?"true":"false"); + fprintf(stderr, "ITEM %-20s: dfe browsable %5s, expected %5s...\t\t\t\t\t", label, + found?"true":"false", browsable?"true":"false"); if(found != browsable) { fprintf(stderr, "FAILED\n"); errors++; @@ -2119,8 +3014,9 @@ static size_t check_name_value_deleted_flag(DICTIONARY *dict, NAME_VALUE *nv, co else fprintf(stderr, "OK\n"); - found = dictionary_walkthrough_read(dict, check_name_value_callback, nv->value); - fprintf(stderr, "NAME_VALUE walkthrough browsable %5s, expected %5s...\t\t", found?"true":"false", browsable?"true":"false"); + found = dictionary_walkthrough_read(dict, check_item_callback, item->shared->value); + fprintf(stderr, "ITEM %-20s: walkthrough browsable %5s, expected %5s...\t\t\t\t", label, + found?"true":"false", browsable?"true":"false"); if(found != browsable) { fprintf(stderr, "FAILED\n"); errors++; @@ -2128,8 +3024,9 @@ static size_t check_name_value_deleted_flag(DICTIONARY *dict, NAME_VALUE *nv, co else fprintf(stderr, "OK\n"); - found = dictionary_sorted_walkthrough_read(dict, check_name_value_callback, nv->value); - fprintf(stderr, "NAME_VALUE sorted walkthrough browsable %5s, expected %5s...\t", found?"true":"false", browsable?"true":"false"); + found = dictionary_sorted_walkthrough_read(dict, check_item_callback, item->shared->value); + fprintf(stderr, "ITEM %-20s: sorted walkthrough browsable %5s, expected %5s...\t\t\t", label, + found?"true":"false", browsable?"true":"false"); if(found != browsable) { fprintf(stderr, "FAILED\n"); errors++; @@ -2138,11 +3035,12 @@ static size_t check_name_value_deleted_flag(DICTIONARY *dict, NAME_VALUE *nv, co fprintf(stderr, "OK\n"); found = false; - NAME_VALUE *n; - for(n = dict->first_item; n ;n = n->next) - if(n == nv) found = true; + DICTIONARY_ITEM *n; + for(n = dict->items.list; n ;n = n->next) + if(n == item) found = true; - fprintf(stderr, "NAME_VALUE linked %5s, expected %5s...\t\t\t\t", found?"true":"false", linked?"true":"false"); + fprintf(stderr, "ITEM %-20s: linked %5s, expected %5s...\t\t\t\t\t\t", label, + found?"true":"false", linked?"true":"false"); if(found != linked) { fprintf(stderr, "FAILED\n"); errors++; @@ -2153,6 +3051,423 @@ static size_t check_name_value_deleted_flag(DICTIONARY *dict, NAME_VALUE *nv, co return errors; } +struct thread_unittest { + int join; + DICTIONARY *dict; + int dups; +}; + +static void *unittest_dict_thread(void *arg) { + struct thread_unittest *tu = arg; + for(; 1 ;) { + if(__atomic_load_n(&tu->join, __ATOMIC_RELAXED)) + break; + + DICT_ITEM_CONST DICTIONARY_ITEM *item = + dictionary_set_and_acquire_item_advanced(tu->dict, "dict thread checking 1234567890", + -1, NULL, 0, NULL); + + + dictionary_get(tu->dict, dictionary_acquired_item_name(item)); + + void *t1; + dfe_start_write(tu->dict, t1) { + + // this should delete the referenced item + dictionary_del(tu->dict, t1_dfe.name); + + void *t2; + dfe_start_write(tu->dict, t2) { + // this should add another + dictionary_set(tu->dict, t2_dfe.name, NULL, 0); + + dictionary_get(tu->dict, dictionary_acquired_item_name(item)); + + // and this should delete it again + dictionary_del(tu->dict, t2_dfe.name); + } + dfe_done(t2); + + // this should fail to add it + dictionary_set(tu->dict, t1_dfe.name, NULL, 0); + dictionary_del(tu->dict, t1_dfe.name); + } + dfe_done(t1); + + for(int i = 0; i < tu->dups ; i++) { + dictionary_acquired_item_dup(tu->dict, item); + dictionary_get(tu->dict, dictionary_acquired_item_name(item)); + } + + for(int i = 0; i < tu->dups ; i++) { + dictionary_acquired_item_release(tu->dict, item); + dictionary_del(tu->dict, dictionary_acquired_item_name(item)); + } + + dictionary_acquired_item_release(tu->dict, item); + dictionary_del(tu->dict, "dict thread checking 1234567890"); + + // test concurrent deletions and flushes + { + if(gettid() % 2) { + char buf [256 + 1]; + + for (int i = 0; i < 1000; i++) { + snprintfz(buf, 256, "del/flush test %d", i); + dictionary_set(tu->dict, buf, NULL, 0); + } + + for (int i = 0; i < 1000; i++) { + snprintfz(buf, 256, "del/flush test %d", i); + dictionary_del(tu->dict, buf); + } + } + else { + for (int i = 0; i < 10; i++) { + dictionary_flush(tu->dict); + } + } + } + } + + return arg; +} + +static int dictionary_unittest_threads() { + + struct thread_unittest tu = { + .join = 0, + .dict = NULL, + .dups = 1, + }; + + // threads testing of dictionary + tu.dict = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + time_t seconds_to_run = 5; + int threads_to_create = 2; + fprintf( + stderr, + "\nChecking dictionary concurrency with %d threads for %lld seconds...\n", + threads_to_create, + (long long)seconds_to_run); + + netdata_thread_t threads[threads_to_create]; + tu.join = 0; + for (int i = 0; i < threads_to_create; i++) { + char buf[100 + 1]; + snprintf(buf, 100, "dict%d", i); + netdata_thread_create( + &threads[i], + buf, + NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, + unittest_dict_thread, + &tu); + } + sleep_usec(seconds_to_run * USEC_PER_SEC); + + __atomic_store_n(&tu.join, 1, __ATOMIC_RELAXED); + for (int i = 0; i < threads_to_create; i++) { + void *retval; + netdata_thread_join(threads[i], &retval); + } + + fprintf(stderr, + "inserts %zu" + ", deletes %zu" + ", searches %zu" + ", resets %zu" + ", flushes %zu" + ", entries %ld" + ", referenced_items %ld" + ", pending deletions %ld" + ", check spins %zu" + ", insert spins %zu" + ", delete spins %zu" + ", search ignores %zu" + "\n", + tu.dict->stats->ops.inserts, + tu.dict->stats->ops.deletes, + tu.dict->stats->ops.searches, + tu.dict->stats->ops.resets, + tu.dict->stats->ops.flushes, + tu.dict->entries, + tu.dict->referenced_items, + tu.dict->pending_deletion_items, + tu.dict->stats->spin_locks.use_spins, + tu.dict->stats->spin_locks.insert_spins, + tu.dict->stats->spin_locks.delete_spins, + tu.dict->stats->spin_locks.search_spins + ); + dictionary_destroy(tu.dict); + tu.dict = NULL; + + return 0; +} + +struct thread_view_unittest { + int join; + DICTIONARY *master; + DICTIONARY *view; + DICTIONARY_ITEM *item_master; + int dups; +}; + +static void *unittest_dict_master_thread(void *arg) { + struct thread_view_unittest *tv = arg; + + DICTIONARY_ITEM *item = NULL; + int loops = 0; + while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST)) { + + if(!item) + item = dictionary_set_and_acquire_item(tv->master, "ITEM1", "123", strlen("123") + 1); + + if(__atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST) != NULL) { + dictionary_acquired_item_release(tv->master, item); + dictionary_del(tv->master, "ITEM1"); + item = NULL; + loops++; + continue; + } + + dictionary_acquired_item_dup(tv->master, item); // for the view thread + __atomic_store_n(&tv->item_master, item, __ATOMIC_SEQ_CST); + dictionary_del(tv->master, "ITEM1"); + + + for(int i = 0; i < tv->dups + loops ; i++) { + dictionary_acquired_item_dup(tv->master, item); + } + + for(int i = 0; i < tv->dups + loops ; i++) { + dictionary_acquired_item_release(tv->master, item); + } + + dictionary_acquired_item_release(tv->master, item); + + item = NULL; + loops = 0; + } + + return arg; +} + +static void *unittest_dict_view_thread(void *arg) { + struct thread_view_unittest *tv = arg; + + DICTIONARY_ITEM *m_item = NULL; + + while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST)) { + if(!(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST))) + continue; + + DICTIONARY_ITEM *v_item = dictionary_view_set_and_acquire_item(tv->view, "ITEM2", m_item); + dictionary_acquired_item_release(tv->master, m_item); + __atomic_store_n(&tv->item_master, NULL, __ATOMIC_SEQ_CST); + + for(int i = 0; i < tv->dups ; i++) { + dictionary_acquired_item_dup(tv->view, v_item); + } + + for(int i = 0; i < tv->dups ; i++) { + dictionary_acquired_item_release(tv->view, v_item); + } + + dictionary_del(tv->view, "ITEM2"); + + while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST) && !(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST))) { + dictionary_acquired_item_dup(tv->view, v_item); + dictionary_acquired_item_release(tv->view, v_item); + } + + dictionary_acquired_item_release(tv->view, v_item); + } + + return arg; +} + +static int dictionary_unittest_view_threads() { + + struct thread_view_unittest tv = { + .join = 0, + .master = NULL, + .view = NULL, + .item_master = NULL, + .dups = 1, + }; + + // threads testing of dictionary + struct dictionary_stats stats_master = {}; + struct dictionary_stats stats_view = {}; + tv.master = dictionary_create_advanced(DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE, &stats_master); + tv.view = dictionary_create_view(tv.master); + tv.view->stats = &stats_view; + + time_t seconds_to_run = 5; + fprintf( + stderr, + "\nChecking dictionary concurrency with 1 master and 1 view threads for %lld seconds...\n", + (long long)seconds_to_run); + + netdata_thread_t master_thread, view_thread; + tv.join = 0; + + netdata_thread_create( + &master_thread, + "master", + NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, + unittest_dict_master_thread, + &tv); + + netdata_thread_create( + &view_thread, + "view", + NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, + unittest_dict_view_thread, + &tv); + + sleep_usec(seconds_to_run * USEC_PER_SEC); + + __atomic_store_n(&tv.join, 1, __ATOMIC_RELAXED); + void *retval; + netdata_thread_join(view_thread, &retval); + netdata_thread_join(master_thread, &retval); + + fprintf(stderr, + "MASTER: inserts %zu" + ", deletes %zu" + ", searches %zu" + ", resets %zu" + ", entries %ld" + ", referenced_items %ld" + ", pending deletions %ld" + ", check spins %zu" + ", insert spins %zu" + ", delete spins %zu" + ", search ignores %zu" + "\n", + stats_master.ops.inserts, + stats_master.ops.deletes, + stats_master.ops.searches, + stats_master.ops.resets, + tv.master->entries, + tv.master->referenced_items, + tv.master->pending_deletion_items, + stats_master.spin_locks.use_spins, + stats_master.spin_locks.insert_spins, + stats_master.spin_locks.delete_spins, + stats_master.spin_locks.search_spins + ); + fprintf(stderr, + "VIEW : inserts %zu" + ", deletes %zu" + ", searches %zu" + ", resets %zu" + ", entries %ld" + ", referenced_items %ld" + ", pending deletions %ld" + ", check spins %zu" + ", insert spins %zu" + ", delete spins %zu" + ", search ignores %zu" + "\n", + stats_view.ops.inserts, + stats_view.ops.deletes, + stats_view.ops.searches, + stats_view.ops.resets, + tv.view->entries, + tv.view->referenced_items, + tv.view->pending_deletion_items, + stats_view.spin_locks.use_spins, + stats_view.spin_locks.insert_spins, + stats_view.spin_locks.delete_spins, + stats_view.spin_locks.search_spins + ); + dictionary_destroy(tv.master); + dictionary_destroy(tv.view); + + return 0; +} + +size_t dictionary_unittest_views(void) { + size_t errors = 0; + struct dictionary_stats stats = {}; + DICTIONARY *master = dictionary_create_advanced(DICT_OPTION_NONE, &stats); + DICTIONARY *view = dictionary_create_view(master); + + fprintf(stderr, "\n\nChecking dictionary views...\n"); + + // Add an item to both master and view, then remove the view first and the master second + fprintf(stderr, "\nPASS 1: Adding 1 item to master:\n"); + DICTIONARY_ITEM *item1_on_master = dictionary_set_and_acquire_item(master, "KEY 1", "VALUE1", strlen("VALUE1") + 1); + errors += unittest_check_dictionary("master", master, 1, 1, 0, 1, 0); + errors += unittest_check_item("master", master, item1_on_master, "KEY 1", item1_on_master->shared->value, 1, ITEM_FLAG_NONE, true, true, true); + + fprintf(stderr, "\nPASS 1: Adding master item to view:\n"); + DICTIONARY_ITEM *item1_on_view = dictionary_view_set_and_acquire_item(view, "KEY 1 ON VIEW", item1_on_master); + errors += unittest_check_dictionary("view", view, 1, 1, 0, 1, 0); + errors += unittest_check_item("view", view, item1_on_view, "KEY 1 ON VIEW", item1_on_master->shared->value, 1, ITEM_FLAG_NONE, true, true, true); + + fprintf(stderr, "\nPASS 1: Deleting view item:\n"); + dictionary_del(view, "KEY 1 ON VIEW"); + errors += unittest_check_dictionary("master", master, 1, 1, 0, 1, 0); + errors += unittest_check_dictionary("view", view, 0, 0, 1, 1, 0); + errors += unittest_check_item("master", master, item1_on_master, "KEY 1", item1_on_master->shared->value, 1, ITEM_FLAG_NONE, true, true, true); + errors += unittest_check_item("view", view, item1_on_view, "KEY 1 ON VIEW", item1_on_master->shared->value, 1, ITEM_FLAG_DELETED, false, false, true); + + fprintf(stderr, "\nPASS 1: Releasing the deleted view item:\n"); + dictionary_acquired_item_release(view, item1_on_view); + errors += unittest_check_dictionary("master", master, 1, 1, 0, 1, 0); + errors += unittest_check_dictionary("view", view, 0, 0, 1, 0, 1); + errors += unittest_check_item("master", master, item1_on_master, "KEY 1", item1_on_master->shared->value, 1, ITEM_FLAG_NONE, true, true, true); + + fprintf(stderr, "\nPASS 1: Releasing the acquired master item:\n"); + dictionary_acquired_item_release(master, item1_on_master); + errors += unittest_check_dictionary("master", master, 1, 1, 0, 0, 0); + errors += unittest_check_dictionary("view", view, 0, 0, 1, 0, 1); + errors += unittest_check_item("master", master, item1_on_master, "KEY 1", item1_on_master->shared->value, 0, ITEM_FLAG_NONE, true, true, true); + + fprintf(stderr, "\nPASS 1: Deleting the released master item:\n"); + dictionary_del(master, "KEY 1"); + errors += unittest_check_dictionary("master", master, 0, 0, 0, 0, 0); + errors += unittest_check_dictionary("view", view, 0, 0, 1, 0, 1); + + // The other way now: + // Add an item to both master and view, then remove the master first and verify it is deleted on the view also + fprintf(stderr, "\nPASS 2: Adding 1 item to master:\n"); + item1_on_master = dictionary_set_and_acquire_item(master, "KEY 1", "VALUE1", strlen("VALUE1") + 1); + errors += unittest_check_dictionary("master", master, 1, 1, 0, 1, 0); + errors += unittest_check_item("master", master, item1_on_master, "KEY 1", item1_on_master->shared->value, 1, ITEM_FLAG_NONE, true, true, true); + + fprintf(stderr, "\nPASS 2: Adding master item to view:\n"); + item1_on_view = dictionary_view_set_and_acquire_item(view, "KEY 1 ON VIEW", item1_on_master); + errors += unittest_check_dictionary("view", view, 1, 1, 0, 1, 0); + errors += unittest_check_item("view", view, item1_on_view, "KEY 1 ON VIEW", item1_on_master->shared->value, 1, ITEM_FLAG_NONE, true, true, true); + + fprintf(stderr, "\nPASS 2: Deleting master item:\n"); + dictionary_del(master, "KEY 1"); + dictionary_version(view); + errors += unittest_check_dictionary("master", master, 0, 0, 1, 1, 0); + errors += unittest_check_dictionary("view", view, 0, 0, 1, 1, 0); + errors += unittest_check_item("master", master, item1_on_master, "KEY 1", item1_on_master->shared->value, 1, ITEM_FLAG_DELETED, false, false, true); + errors += unittest_check_item("view", view, item1_on_view, "KEY 1 ON VIEW", item1_on_master->shared->value, 1, ITEM_FLAG_DELETED, false, false, true); + + fprintf(stderr, "\nPASS 2: Releasing the acquired master item:\n"); + dictionary_acquired_item_release(master, item1_on_master); + errors += unittest_check_dictionary("master", master, 0, 0, 1, 0, 1); + errors += unittest_check_dictionary("view", view, 0, 0, 1, 1, 0); + errors += unittest_check_item("view", view, item1_on_view, "KEY 1 ON VIEW", item1_on_master->shared->value, 1, ITEM_FLAG_DELETED, false, false, true); + + fprintf(stderr, "\nPASS 2: Releasing the deleted view item:\n"); + dictionary_acquired_item_release(view, item1_on_view); + errors += unittest_check_dictionary("master", master, 0, 0, 1, 0, 1); + errors += unittest_check_dictionary("view", view, 0, 0, 1, 0, 1); + + dictionary_destroy(master); + dictionary_destroy(view); + return errors; +} + int dictionary_unittest(size_t entries) { if(entries < 10) entries = 10; @@ -2164,23 +3479,28 @@ int dictionary_unittest(size_t entries) { char **values = dictionary_unittest_generate_values(entries); fprintf(stderr, "\nCreating dictionary single threaded, clone, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + dict = dictionary_create(DICT_OPTION_SINGLE_THREADED); dictionary_unittest_clone(dict, names, values, entries, &errors); fprintf(stderr, "\nCreating dictionary multi threaded, clone, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_NONE); + dict = dictionary_create(DICT_OPTION_NONE); dictionary_unittest_clone(dict, names, values, entries, &errors); fprintf(stderr, "\nCreating dictionary single threaded, non-clone, add-in-front options, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_ADD_IN_FRONT); + dict = dictionary_create( + DICT_OPTION_SINGLE_THREADED | DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | + DICT_OPTION_ADD_IN_FRONT); dictionary_unittest_nonclone(dict, names, values, entries, &errors); fprintf(stderr, "\nCreating dictionary multi threaded, non-clone, add-in-front options, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_ADD_IN_FRONT); + dict = dictionary_create( + DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_ADD_IN_FRONT); dictionary_unittest_nonclone(dict, names, values, entries, &errors); fprintf(stderr, "\nCreating dictionary single-threaded, non-clone, don't overwrite options, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_DONT_OVERWRITE_VALUE); + dict = dictionary_create( + DICT_OPTION_SINGLE_THREADED | DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | + DICT_OPTION_DONT_OVERWRITE_VALUE); dictionary_unittest_run_and_measure_time(dict, "adding entries", names, values, entries, &errors, dictionary_unittest_set_nonclone); dictionary_unittest_run_and_measure_time(dict, "resetting non-overwrite entries", names, values, entries, &errors, dictionary_unittest_reset_dont_overwrite_nonclone); dictionary_unittest_run_and_measure_time(dict, "traverse foreach read loop", names, values, entries, &errors, dictionary_unittest_foreach); @@ -2189,13 +3509,15 @@ int dictionary_unittest(size_t entries) { dictionary_unittest_run_and_measure_time(dict, "destroying full dictionary", names, values, entries, &errors, dictionary_unittest_destroy); fprintf(stderr, "\nCreating dictionary multi-threaded, non-clone, don't overwrite options, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_DONT_OVERWRITE_VALUE); + dict = dictionary_create( + DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE); dictionary_unittest_run_and_measure_time(dict, "adding entries", names, values, entries, &errors, dictionary_unittest_set_nonclone); dictionary_unittest_run_and_measure_time(dict, "walkthrough write delete this", names, values, entries, &errors, dictionary_unittest_walkthrough_delete_this); dictionary_unittest_run_and_measure_time(dict, "destroying empty dictionary", names, values, entries, &errors, dictionary_unittest_destroy); fprintf(stderr, "\nCreating dictionary multi-threaded, non-clone, don't overwrite options, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_DONT_OVERWRITE_VALUE); + dict = dictionary_create( + DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE); dictionary_unittest_run_and_measure_time(dict, "adding entries", names, values, entries, &errors, dictionary_unittest_set_nonclone); dictionary_unittest_run_and_measure_time(dict, "foreach write delete this", names, values, entries, &errors, dictionary_unittest_foreach_delete_this); dictionary_unittest_run_and_measure_time(dict, "traverse foreach read loop empty", names, values, 0, &errors, dictionary_unittest_foreach); @@ -2203,208 +3525,100 @@ int dictionary_unittest(size_t entries) { dictionary_unittest_run_and_measure_time(dict, "destroying empty dictionary", names, values, entries, &errors, dictionary_unittest_destroy); fprintf(stderr, "\nCreating dictionary single threaded, clone, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + dict = dictionary_create(DICT_OPTION_SINGLE_THREADED); dictionary_unittest_sorting(dict, names, values, entries, &errors); dictionary_unittest_run_and_measure_time(dict, "destroying full dictionary", names, values, entries, &errors, dictionary_unittest_destroy); fprintf(stderr, "\nCreating dictionary single threaded, clone, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + dict = dictionary_create(DICT_OPTION_SINGLE_THREADED); dictionary_unittest_null_dfe(dict, names, values, entries, &errors); dictionary_unittest_run_and_measure_time(dict, "destroying full dictionary", names, values, entries, &errors, dictionary_unittest_destroy); fprintf(stderr, "\nCreating dictionary single threaded, noclone, %zu items\n", entries); - dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE); + dict = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_VALUE_LINK_DONT_CLONE); dictionary_unittest_null_dfe(dict, names, values, entries, &errors); dictionary_unittest_run_and_measure_time(dict, "destroying full dictionary", names, values, entries, &errors, dictionary_unittest_destroy); // check reference counters { fprintf(stderr, "\nTesting reference counters:\n"); - dict = dictionary_create(DICTIONARY_FLAG_NONE|DICTIONARY_FLAG_NAME_LINK_DONT_CLONE); - errors += check_dictionary(dict, 0, 0); + dict = dictionary_create(DICT_OPTION_NONE | DICT_OPTION_NAME_LINK_DONT_CLONE); + errors += unittest_check_dictionary("", dict, 0, 0, 0, 0, 0); fprintf(stderr, "\nAdding test item to dictionary and acquiring it\n"); dictionary_set(dict, "test", "ITEM1", 6); - NAME_VALUE *nv = (NAME_VALUE *)dictionary_get_and_acquire_item(dict, "test"); + DICTIONARY_ITEM *item = (DICTIONARY_ITEM *)dictionary_get_and_acquire_item(dict, "test"); - errors += check_dictionary(dict, 1, 1); - errors += check_name_value_deleted_flag(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_NONE, true, true, true); + errors += unittest_check_dictionary("", dict, 1, 1, 0, 1, 0); + errors += unittest_check_item("ACQUIRED", dict, item, "test", "ITEM1", 1, ITEM_FLAG_NONE, true, true, true); fprintf(stderr, "\nChecking that reference counters are increased:\n"); void *t; dfe_start_read(dict, t) { - errors += check_dictionary(dict, 1, 1); - errors += - check_name_value_deleted_flag(dict, nv, "test", "ITEM1", 2, NAME_VALUE_FLAG_NONE, true, true, true); + errors += unittest_check_dictionary("", dict, 1, 1, 0, 1, 0); + errors += unittest_check_item("ACQUIRED TRAVERSAL", dict, item, "test", "ITEM1", 2, ITEM_FLAG_NONE, true, true, true); } dfe_done(t); fprintf(stderr, "\nChecking that reference counters are decreased:\n"); - errors += check_dictionary(dict, 1, 1); - errors += check_name_value_deleted_flag(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_NONE, true, true, true); + errors += unittest_check_dictionary("", dict, 1, 1, 0, 1, 0); + errors += unittest_check_item("ACQUIRED TRAVERSAL 2", dict, item, "test", "ITEM1", 1, ITEM_FLAG_NONE, true, true, true); fprintf(stderr, "\nDeleting the item we have acquired:\n"); dictionary_del(dict, "test"); - errors += check_dictionary(dict, 0, 1); - errors += check_name_value_deleted_flag(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_DELETED, false, false, true); + errors += unittest_check_dictionary("", dict, 0, 0, 1, 1, 0); + errors += unittest_check_item("DELETED", dict, item, "test", "ITEM1", 1, ITEM_FLAG_DELETED, false, false, true); fprintf(stderr, "\nAdding another item with the same name of the item we deleted, while being acquired:\n"); dictionary_set(dict, "test", "ITEM2", 6); - errors += check_dictionary(dict, 1, 2); + errors += unittest_check_dictionary("", dict, 1, 1, 1, 1, 0); fprintf(stderr, "\nAcquiring the second item:\n"); - NAME_VALUE *nv2 = (NAME_VALUE *)dictionary_get_and_acquire_item(dict, "test"); - errors += check_name_value_deleted_flag(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_DELETED, false, false, true); - errors += check_name_value_deleted_flag(dict, nv2, "test", "ITEM2", 1, NAME_VALUE_FLAG_NONE, true, true, true); + DICTIONARY_ITEM *item2 = (DICTIONARY_ITEM *)dictionary_get_and_acquire_item(dict, "test"); + errors += unittest_check_item("FIRST", dict, item, "test", "ITEM1", 1, ITEM_FLAG_DELETED, false, false, true); + errors += unittest_check_item("SECOND", dict, item2, "test", "ITEM2", 1, ITEM_FLAG_NONE, true, true, true); + errors += unittest_check_dictionary("", dict, 1, 1, 1, 2, 0); fprintf(stderr, "\nReleasing the second item (the first is still acquired):\n"); - dictionary_acquired_item_release(dict, (DICTIONARY_ITEM *)nv2); - errors += check_dictionary(dict, 1, 2); - errors += check_name_value_deleted_flag(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_DELETED, false, false, true); - errors += check_name_value_deleted_flag(dict, nv2, "test", "ITEM2", 0, NAME_VALUE_FLAG_NONE, true, true, true); + dictionary_acquired_item_release(dict, (DICTIONARY_ITEM *)item2); + errors += unittest_check_dictionary("", dict, 1, 1, 1, 1, 0); + errors += unittest_check_item("FIRST", dict, item, "test", "ITEM1", 1, ITEM_FLAG_DELETED, false, false, true); + errors += unittest_check_item("SECOND RELEASED", dict, item2, "test", "ITEM2", 0, ITEM_FLAG_NONE, true, true, true); fprintf(stderr, "\nDeleting the second item (the first is still acquired):\n"); dictionary_del(dict, "test"); - errors += check_dictionary(dict, 0, 1); - errors += check_name_value_deleted_flag(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_DELETED, false, false, true); + errors += unittest_check_dictionary("", dict, 0, 0, 1, 1, 0); + errors += unittest_check_item("ACQUIRED DELETED", dict, item, "test", "ITEM1", 1, ITEM_FLAG_DELETED, false, false, true); fprintf(stderr, "\nReleasing the first item (which we have already deleted):\n"); - dictionary_acquired_item_release(dict, (DICTIONARY_ITEM *)nv); - errors += check_dictionary(dict, 0, 0); + dictionary_acquired_item_release(dict, (DICTIONARY_ITEM *)item); + dfe_start_write(dict, item) ; dfe_done(item); + errors += unittest_check_dictionary("", dict, 0, 0, 1, 0, 1); fprintf(stderr, "\nAdding again the test item to dictionary and acquiring it\n"); dictionary_set(dict, "test", "ITEM1", 6); - nv = (NAME_VALUE *)dictionary_get_and_acquire_item(dict, "test"); + item = (DICTIONARY_ITEM *)dictionary_get_and_acquire_item(dict, "test"); - errors += check_dictionary(dict, 1, 1); - errors += check_name_value_deleted_flag(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_NONE, true, true, true); + errors += unittest_check_dictionary("", dict, 1, 1, 0, 1, 0); + errors += unittest_check_item("RE-ADDITION", dict, item, "test", "ITEM1", 1, ITEM_FLAG_NONE, true, true, true); fprintf(stderr, "\nDestroying the dictionary while we have acquired an item\n"); dictionary_destroy(dict); fprintf(stderr, "Releasing the item (on a destroyed dictionary)\n"); - dictionary_acquired_item_release(dict, (DICTIONARY_ITEM *)nv); - nv = NULL; + dictionary_acquired_item_release(dict, (DICTIONARY_ITEM *)item); + item = NULL; dict = NULL; } - // check string - { - long string_entries_starting = dictionary_stats_entries(&string_dictionary); - - fprintf(stderr, "\nChecking strings...\n"); - - STRING *s1 = string_strdupz("hello unittest"); - STRING *s2 = string_strdupz("hello unittest"); - if(s1 != s2) { - errors++; - fprintf(stderr, "ERROR: duplicating strings are not deduplicated\n"); - } - else - fprintf(stderr, "OK: duplicating string are deduplicated\n"); - - STRING *s3 = string_dup(s1); - if(s3 != s1) { - errors++; - fprintf(stderr, "ERROR: cloning strings are not deduplicated\n"); - } - else - fprintf(stderr, "OK: cloning string are deduplicated\n"); - - STRING_ENTRY *se = (STRING_ENTRY *)s1; - if(se->refcount != 3) { - errors++; - fprintf(stderr, "ERROR: string refcount is not 3\n"); - } - else - fprintf(stderr, "OK: string refcount is 3\n"); - - STRING *s4 = string_strdupz("world unittest"); - if(s4 == s1) { - errors++; - fprintf(stderr, "ERROR: string is sharing pointers on different strings\n"); - } - else - fprintf(stderr, "OK: string is properly handling different strings\n"); - - usec_t start_ut, end_ut; - STRING **strings = mallocz(entries * sizeof(STRING *)); - - start_ut = now_realtime_usec(); - for(size_t i = 0; i < entries ;i++) { - strings[i] = string_strdupz(names[i]); - } - end_ut = now_realtime_usec(); - fprintf(stderr, "Created %zu strings in %llu usecs\n", entries, end_ut - start_ut); - - start_ut = now_realtime_usec(); - for(size_t i = 0; i < entries ;i++) { - strings[i] = string_dup(strings[i]); - } - end_ut = now_realtime_usec(); - fprintf(stderr, "Cloned %zu strings in %llu usecs\n", entries, end_ut - start_ut); - - start_ut = now_realtime_usec(); - for(size_t i = 0; i < entries ;i++) { - string_freez(strings[i]); - string_freez(strings[i]); - } - end_ut = now_realtime_usec(); - fprintf(stderr, "Freed %zu strings in %llu usecs\n", entries, end_ut - start_ut); - - freez(strings); - - if(dictionary_stats_entries(&string_dictionary) != string_entries_starting + 2) { - errors++; - fprintf(stderr, "ERROR: strings dictionary should have %ld items but it has %ld\n", string_entries_starting + 2, dictionary_stats_entries(&string_dictionary)); - } - else - fprintf(stderr, "OK: strings dictionary has 2 items\n"); - } - - // check 2-way merge - { - struct testcase { - char *src1; char *src2; char *expected; - } tests[] = { - { "", "", ""}, - { "a", "", "[x]"}, - { "", "a", "[x]"}, - { "a", "a", "a"}, - { "abcd", "abcd", "abcd"}, - { "foo_cs", "bar_cs", "[x]_cs"}, - { "cp_UNIQUE_INFIX_cs", "cp_unique_infix_cs", "cp_[x]_cs"}, - { "cp_UNIQUE_INFIX_ci_unique_infix_cs", "cp_unique_infix_ci_UNIQUE_INFIX_cs", "cp_[x]_cs"}, - { "foo[1234]", "foo[4321]", "foo[[x]]"}, - { NULL, NULL, NULL }, - }; - - for (struct testcase *tc = &tests[0]; tc->expected != NULL; tc++) { - STRING *src1 = string_strdupz(tc->src1); - STRING *src2 = string_strdupz(tc->src2); - STRING *expected = string_strdupz(tc->expected); - - STRING *result = string_2way_merge(src1, src2); - if (string_cmp(result, expected) != 0) { - fprintf(stderr, "string_2way_merge(\"%s\", \"%s\") -> \"%s\" (expected=\"%s\")\n", - string2str(src1), - string2str(src2), - string2str(result), - string2str(expected)); - errors++; - } - - string_freez(src1); - string_freez(src2); - string_freez(expected); - string_freez(result); - } - } - dictionary_unittest_free_char_pp(names, entries); dictionary_unittest_free_char_pp(values, entries); + errors += dictionary_unittest_views(); + errors += dictionary_unittest_threads(); + errors += dictionary_unittest_view_threads(); + fprintf(stderr, "\n%zu errors found\n", errors); return errors ? 1 : 0; } diff --git a/libnetdata/dictionary/dictionary.h b/libnetdata/dictionary/dictionary.h index fdb2088c0..0e7b3d39f 100644 --- a/libnetdata/dictionary/dictionary.h +++ b/libnetdata/dictionary/dictionary.h @@ -13,19 +13,19 @@ * Names and Values in the dictionary can be cloned or linked. * In clone mode, the dictionary does all the memory management. * The default is clone for both names and values. - * Set DICTIONARY_FLAG_NAME_LINK_DONT_CLONE to link names. - * Set DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE to link names. + * Set DICT_OPTION_NAME_LINK_DONT_CLONE to link names. + * Set DICT_OPTION_VALUE_LINK_DONT_CLONE to link names. * * ORDERED * Items are ordered in the order they are added (new items are appended at the end). - * You may reverse the order by setting the flag DICTIONARY_FLAG_ADD_IN_FRONT. + * You may reverse the order by setting the flag DICT_OPTION_ADD_IN_FRONT. * * LOOKUP * The dictionary uses JudyHS to maintain a very fast randomly accessible hash table. * * MULTI-THREADED and SINGLE-THREADED * Each dictionary may be single threaded (no locks), or multi-threaded (multiple readers or one writer). - * The default is multi-threaded. Add the flag DICTIONARY_FLAG_SINGLE_THREADED for single-threaded. + * The default is multi-threaded. Add the flag DICT_OPTION_SINGLE_THREADED for single-threaded. * * WALK-THROUGH and FOREACH traversal * The dictionary can be traversed on read or write mode, either with a callback (walkthrough) or with @@ -35,110 +35,186 @@ * */ +#ifdef DICTIONARY_INTERNALS +#define DICTFE_CONST +#define DICT_ITEM_CONST +#else +#define DICTFE_CONST const +#define DICT_ITEM_CONST const +#endif + typedef struct dictionary DICTIONARY; typedef struct dictionary_item DICTIONARY_ITEM; -typedef enum dictionary_flags { - DICTIONARY_FLAG_NONE = 0, // the default is the opposite of all below - DICTIONARY_FLAG_SINGLE_THREADED = (1 << 0), // don't use any locks (default: use locks) - DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE = (1 << 1), // don't copy the value, just point to the one provided (default: copy) - DICTIONARY_FLAG_NAME_LINK_DONT_CLONE = (1 << 2), // don't copy the name, just point to the one provided (default: copy) - DICTIONARY_FLAG_DONT_OVERWRITE_VALUE = (1 << 3), // don't overwrite values of dictionary items (default: overwrite) - DICTIONARY_FLAG_ADD_IN_FRONT = (1 << 4), // add dictionary items at the front of the linked list (default: at the end) - - // to change the value of the following, you also need to change the corresponding #defines in dictionary.c - DICTIONARY_FLAG_RESERVED1 = (1 << 29), // reserved for DICTIONARY_FLAG_EXCLUSIVE_ACCESS - DICTIONARY_FLAG_RESERVED2 = (1 << 30), // reserved for DICTIONARY_FLAG_DESTROYED - DICTIONARY_FLAG_RESERVED3 = (1 << 31), // reserved for DICTIONARY_FLAG_DEFER_ALL_DELETIONS -} DICTIONARY_FLAGS; +typedef enum dictionary_options { + DICT_OPTION_NONE = 0, // the default is the opposite of all below + DICT_OPTION_SINGLE_THREADED = (1 << 0), // don't use any locks (default: use locks) + DICT_OPTION_VALUE_LINK_DONT_CLONE = (1 << 1), // don't copy the value, just point to the one provided (default: copy) + DICT_OPTION_NAME_LINK_DONT_CLONE = (1 << 2), // don't copy the name, just point to the one provided (default: copy) + DICT_OPTION_DONT_OVERWRITE_VALUE = (1 << 3), // don't overwrite values of dictionary items (default: overwrite) + DICT_OPTION_ADD_IN_FRONT = (1 << 4), // add dictionary items at the front of the linked list (default: at the end) +} DICT_OPTIONS; + +struct dictionary_stats { + const char *name; // the name of the category + + struct { + size_t active; // the number of active dictionaries + size_t deleted; // the number of dictionaries queued for destruction + } dictionaries; + + struct { + long entries; // active items in the dictionary + long pending_deletion; // pending deletion items in the dictionary + long referenced; // referenced items in the dictionary + } items; + + struct { + size_t creations; // dictionary creations + size_t destructions; // dictionary destructions + size_t flushes; // dictionary flushes + size_t traversals; // dictionary foreach + size_t walkthroughs; // dictionary walkthrough + size_t garbage_collections; // dictionary garbage collections + size_t searches; // item searches + size_t inserts; // item inserts + size_t resets; // item resets + size_t deletes; // item deletes + } ops; + + struct { + size_t inserts; // number of times the insert callback is called + size_t conflicts; // number of times the conflict callback is called + size_t reacts; // number of times the react callback is called + size_t deletes; // number of times the delete callback is called + } callbacks; + + // memory + struct { + long indexed; // bytes of keys indexed (indication of the index size) + long values; // bytes of caller structures + long dict; // bytes of the structures dictionary needs + } memory; + + // spin locks + struct { + size_t use_spins; // number of times a reference to item had to spin to acquire it or ignore it + size_t search_spins; // number of times a successful search result had to be thrown away + size_t insert_spins; // number of times an insertion to the hash table had to be repeated + size_t delete_spins; // number of times a deletion had to spin to get a decision + } spin_locks; +}; // Create a dictionary #ifdef NETDATA_INTERNAL_CHECKS -#define dictionary_create(flags) dictionary_create_advanced_with_trace(flags, 0, __FUNCTION__, __LINE__, __FILE__); -#define dictionary_create_advanced(flags) dictionary_create_advanced_with_trace(flags, 0, __FUNCTION__, __LINE__, __FILE__); -extern DICTIONARY *dictionary_create_advanced_with_trace(DICTIONARY_FLAGS flags, size_t scratchpad_size, const char *function, size_t line, const char *file); +#define dictionary_create(options) dictionary_create_advanced_with_trace(options, NULL, __FUNCTION__, __LINE__, __FILE__) +#define dictionary_create_advanced(options, stats) dictionary_create_advanced_with_trace(options, stats, __FUNCTION__, __LINE__, __FILE__) +DICTIONARY *dictionary_create_advanced_with_trace(DICT_OPTIONS options, struct dictionary_stats *stats, const char *function, size_t line, const char *file); #else -#define dictionary_create(flags) dictionary_create_advanced(flags, 0); -extern DICTIONARY *dictionary_create_advanced(DICTIONARY_FLAGS flags, size_t scratchpad_size); +#define dictionary_create(options) dictionary_create_advanced(options, NULL); +DICTIONARY *dictionary_create_advanced(DICT_OPTIONS options, struct dictionary_stats *stats); #endif -extern void *dictionary_scratchpad(DICTIONARY *dict); +// Create a view on a dictionary +#ifdef NETDATA_INTERNAL_CHECKS +#define dictionary_create_view(master) dictionary_create_view_with_trace(master, __FUNCTION__, __LINE__, __FILE__) +DICTIONARY *dictionary_create_view_with_trace(DICTIONARY *master, const char *function, size_t line, const char *file); +#else +DICTIONARY *dictionary_create_view(DICTIONARY *master); +#endif // an insert callback to be called just after an item is added to the dictionary // this callback is called while the dictionary is write locked! -extern void dictionary_register_insert_callback(DICTIONARY *dict, void (*ins_callback)(const char *name, void *value, void *data), void *data); +void dictionary_register_insert_callback(DICTIONARY *dict, void (*ins_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data); // a delete callback to be called just before an item is deleted forever // this callback is called while the dictionary is write locked! -extern void dictionary_register_delete_callback(DICTIONARY *dict, void (*del_callback)(const char *name, void *value, void *data), void *data); +void dictionary_register_delete_callback(DICTIONARY *dict, void (*del_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data); -// a merge callback to be called when DICTIONARY_FLAG_DONT_OVERWRITE_VALUE +// a merge callback to be called when DICT_OPTION_DONT_OVERWRITE_VALUE // and an item is already found in the dictionary - the dictionary does nothing else in this case // the old_value will remain in the dictionary - the new_value is ignored -extern void dictionary_register_conflict_callback(DICTIONARY *dict, void (*conflict_callback)(const char *name, void *old_value, void *new_value, void *data), void *data); +// The callback should return true if the value has been updated (it increases the dictionary version). +void dictionary_register_conflict_callback(DICTIONARY *dict, bool (*conflict_callback)(const DICTIONARY_ITEM *item, void *old_value, void *new_value, void *data), void *data); // a reaction callback to be called after every item insertion or conflict // after the constructors have finished and the items are fully available for use // and the dictionary is not write locked anymore -extern void dictionary_register_react_callback(DICTIONARY *dict, void (*react_callback)(const char *name, void *value, void *data), void *data); +void dictionary_register_react_callback(DICTIONARY *dict, void (*react_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data); // Destroy a dictionary -// returns the number of bytes freed -// the returned value will not include name and value sizes if DICTIONARY_FLAG_WITH_STATISTICS is not set -extern size_t dictionary_destroy(DICTIONARY *dict); +// Returns the number of bytes freed +// The returned value will not include name/key sizes +// Registered delete callbacks will be run for each item in the dictionary. +size_t dictionary_destroy(DICTIONARY *dict); +// Empties a dictionary +// Referenced items will survive, but are not offered anymore. +// Registered delete callbacks will be run for each item in the dictionary. +void dictionary_flush(DICTIONARY *dict); + +void dictionary_version_increment(DICTIONARY *dict); + +// ---------------------------------------------------------------------------- // Set an item in the dictionary +// // - if an item with the same name does not exist, create one // - if an item with the same name exists, then: -// a) if DICTIONARY_FLAG_DONT_OVERWRITE_VALUE is set, just return the existing value (ignore the new value) +// a) if DICT_OPTION_DONT_OVERWRITE_VALUE is set, just return the existing value (ignore the new value) // else b) reset the value to the new value passed at the call // -// When DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE is set, the value is linked, otherwise it is copied -// When DICTIONARY_FLAG_NAME_LINK_DONT_CLONE is set, the name is linked, otherwise it is copied +// When DICT_OPTION_VALUE_LINK_DONT_CLONE is set, the value is linked, otherwise it is copied +// When DICT_OPTION_NAME_LINK_DONT_CLONE is set, the name is linked, otherwise it is copied // -// When neither DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE nor DICTIONARY_FLAG_NAME_LINK_DONT_CLONE are set, all the +// When neither DICT_OPTION_VALUE_LINK_DONT_CLONE nor DICT_OPTION_NAME_LINK_DONT_CLONE are set, all the // memory management for names and values is done by the dictionary. // // Passing NULL as value, the dictionary will callocz() the newly allocated value, otherwise it will copy it. // Passing 0 as value_len, the dictionary will set the value to NULL (no allocations for value will be made). -extern void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len); +#define dictionary_set(dict, name, value, value_len) dictionary_set_advanced(dict, name, -1, value, value_len, NULL) +void *dictionary_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data); + +#define dictionary_set_and_acquire_item(dict, name, value, value_len) dictionary_set_and_acquire_item_advanced(dict, name, -1, value, value_len, NULL) +DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_set_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data); +// set an item in a dictionary view +#define dictionary_view_set_and_acquire_item(dict, name, master_item) dictionary_view_set_and_acquire_item_advanced(dict, name, -1, master_item) +DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_view_set_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICTIONARY_ITEM *master_item); +#define dictionary_view_set(dict, name, master_item) dictionary_view_set_advanced(dict, name, -1, master_item) +void *dictionary_view_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICT_ITEM_CONST DICTIONARY_ITEM *master_item); + +// ---------------------------------------------------------------------------- // Get an item from the dictionary // If it returns NULL, the item is not found -extern void *dictionary_get(DICTIONARY *dict, const char *name); +#define dictionary_get(dict, name) dictionary_get_advanced(dict, name, -1) +void *dictionary_get_advanced(DICTIONARY *dict, const char *name, ssize_t name_len); + +#define dictionary_get_and_acquire_item(dict, name) dictionary_get_and_acquire_item_advanced(dict, name, -1) +DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_get_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len); + + +// ---------------------------------------------------------------------------- // Delete an item from the dictionary -// returns 0 if the item was found and has been deleted -// returns -1 if the item was not found in the index -extern int dictionary_del(DICTIONARY *dict, const char *name); +// returns true if the item was found and has been deleted +// returns false if the item was not found in the index -extern DICTIONARY_ITEM *dictionary_get_and_acquire_item_unsafe(DICTIONARY *dict, const char *name); -extern DICTIONARY_ITEM *dictionary_get_and_acquire_item(DICTIONARY *dict, const char *name); +#define dictionary_del(dict, name) dictionary_del_advanced(dict, name, -1) +bool dictionary_del_advanced(DICTIONARY *dict, const char *name, ssize_t name_len); -extern DICTIONARY_ITEM *dictionary_set_and_acquire_item_unsafe(DICTIONARY *dict, const char *name, void *value, size_t value_len); -extern DICTIONARY_ITEM *dictionary_set_and_acquire_item(DICTIONARY *dict, const char *name, void *value, size_t value_len); +// ---------------------------------------------------------------------------- +// reference counters management -extern void dictionary_acquired_item_release_unsafe(DICTIONARY *dict, DICTIONARY_ITEM *item); -extern void dictionary_acquired_item_release(DICTIONARY *dict, DICTIONARY_ITEM *item); +void dictionary_acquired_item_release(DICTIONARY *dict, DICT_ITEM_CONST DICTIONARY_ITEM *item); -extern DICTIONARY_ITEM *dictionary_acquired_item_dup(DICTIONARY_ITEM *item); -extern const char *dictionary_acquired_item_name(DICTIONARY_ITEM *item); -extern void *dictionary_acquired_item_value(DICTIONARY_ITEM *item); +DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_acquired_item_dup(DICTIONARY *dict, DICT_ITEM_CONST DICTIONARY_ITEM *item); -// UNSAFE functions, without locks -// to be used when the user is traversing with the right lock type -// Read lock is acquired by dictionary_walktrhough_read() and dfe_start_read() -// Write lock is acquired by dictionary_walktrhough_write() and dfe_start_write() -// For code readability, please use these macros: -#define dictionary_get_having_read_lock(dict, name) dictionary_get_unsafe(dict, name) -#define dictionary_get_having_write_lock(dict, name) dictionary_get_unsafe(dict, name) -#define dictionary_set_having_write_lock(dict, name, value, value_len) dictionary_set_unsafe(dict, name, value, value_len) -#define dictionary_del_having_write_lock(dict, name) dictionary_del_unsafe(dict, name) +const char *dictionary_acquired_item_name(DICT_ITEM_CONST DICTIONARY_ITEM *item); +void *dictionary_acquired_item_value(DICT_ITEM_CONST DICTIONARY_ITEM *item); -extern void *dictionary_get_unsafe(DICTIONARY *dict, const char *name); -extern void *dictionary_set_unsafe(DICTIONARY *dict, const char *name, void *value, size_t value_len); -extern int dictionary_del_unsafe(DICTIONARY *dict, const char *name); +size_t dictionary_acquired_item_references(DICT_ITEM_CONST DICTIONARY_ITEM *item); +// ---------------------------------------------------------------------------- // Traverse (walk through) the items of the dictionary. // The order of traversal is currently the order of insertion. // @@ -153,12 +229,15 @@ extern int dictionary_del_unsafe(DICTIONARY *dict, const char *name); // #define dictionary_walkthrough_read(dict, callback, data) dictionary_walkthrough_rw(dict, 'r', callback, data) #define dictionary_walkthrough_write(dict, callback, data) dictionary_walkthrough_rw(dict, 'w', callback, data) -extern int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const char *name, void *value, void *data), void *data); +int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data); -#define dictionary_sorted_walkthrough_read(dict, callback, data) dictionary_sorted_walkthrough_rw(dict, 'r', callback, data) -#define dictionary_sorted_walkthrough_write(dict, callback, data) dictionary_sorted_walkthrough_rw(dict, 'w', callback, data) -int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const char *name, void *entry, void *data), void *data); +typedef int (*dictionary_sorted_compar)(const DICTIONARY_ITEM **item1, const DICTIONARY_ITEM **item2); +#define dictionary_sorted_walkthrough_read(dict, callback, data) dictionary_sorted_walkthrough_rw(dict, 'r', callback, data, NULL) +#define dictionary_sorted_walkthrough_write(dict, callback, data) dictionary_sorted_walkthrough_rw(dict, 'w', callback, data, NULL) +int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const DICTIONARY_ITEM *item, void *entry, void *data), void *data, dictionary_sorted_compar compar); + +// ---------------------------------------------------------------------------- // Traverse with foreach // // Use like this: @@ -173,80 +252,72 @@ int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)( // You can only delete the current item from inside a dfe_start_write() - you can add as many as you want. // -#ifdef DICTIONARY_INTERNALS -#define DICTFE_CONST -#else -#define DICTFE_CONST const -#endif +#define DICTIONARY_LOCK_READ 'r' +#define DICTIONARY_LOCK_WRITE 'w' +#define DICTIONARY_LOCK_REENTRANT 'z' -#define DICTIONARY_LOCK_READ 'r' -#define DICTIONARY_LOCK_WRITE 'w' -#define DICTIONARY_LOCK_NONE 'u' +void dictionary_write_lock(DICTIONARY *dict); +void dictionary_write_unlock(DICTIONARY *dict); typedef DICTFE_CONST struct dictionary_foreach { + DICTIONARY *dict; // the dictionary upon we work + + DICTIONARY_ITEM *item; // the item we work on, to remember the position we are at + // this can be used with dictionary_acquired_item_dup() to + // acquire the currently working item. + DICTFE_CONST char *name; // the dictionary name of the last item used void *value; // the dictionary value of the last item used // same as the return value of dictfe_start() and dictfe_next() - // the following are for internal use only - to keep track of the point we are + size_t counter; // counts the number of iterations made, starting from zero + char rw; // the lock mode 'r' or 'w' - usec_t started_ut; // the time the caller started iterating (now_realtime_usec()) - DICTIONARY *dict; // the dictionary upon we work - void *last_item; // the item we work on, to remember the position we are at } DICTFE; #define dfe_start_read(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_READ) #define dfe_start_write(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_WRITE) -#define dfe_start_rw(dict, value, mode) \ - do { \ - DICTFE value ## _dfe = {}; \ - const char *value ## _name; (void)(value ## _name); (void)value; \ - for((value) = dictionary_foreach_start_rw(&value ## _dfe, (dict), (mode)), ( value ## _name ) = value ## _dfe.name; \ - (value ## _dfe.name) ;\ - (value) = dictionary_foreach_next(&value ## _dfe), ( value ## _name ) = value ## _dfe.name) \ +#define dfe_start_reentrant(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_REENTRANT) + +#define dfe_start_rw(dict, value, mode) \ + do { \ + DICTFE value ## _dfe = {}; \ + (void)(value); /* needed to avoid warning when looping without using this */ \ + for((value) = dictionary_foreach_start_rw(&value ## _dfe, (dict), (mode)); \ + (value ## _dfe.item) ; \ + (value) = dictionary_foreach_next(&value ## _dfe)) \ { -#define dfe_done(value) \ - } \ - dictionary_foreach_done(&value ## _dfe); \ +#define dfe_done(value) \ + } \ + dictionary_foreach_done(&value ## _dfe); \ } while(0) -extern void * dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw); -extern void * dictionary_foreach_next(DICTFE *dfe); -extern usec_t dictionary_foreach_done(DICTFE *dfe); +void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw); +void *dictionary_foreach_next(DICTFE *dfe); +void dictionary_foreach_done(DICTFE *dfe); +// ---------------------------------------------------------------------------- // Get statistics about the dictionary -extern long int dictionary_stats_allocated_memory(DICTIONARY *dict); -extern long int dictionary_stats_entries(DICTIONARY *dict); -extern size_t dictionary_stats_version(DICTIONARY *dict); -extern size_t dictionary_stats_inserts(DICTIONARY *dict); -extern size_t dictionary_stats_searches(DICTIONARY *dict); -extern size_t dictionary_stats_deletes(DICTIONARY *dict); -extern size_t dictionary_stats_resets(DICTIONARY *dict); -extern size_t dictionary_stats_walkthroughs(DICTIONARY *dict); -extern size_t dictionary_stats_referenced_items(DICTIONARY *dict); - -extern int dictionary_unittest(size_t entries); -// ---------------------------------------------------------------------------- -// STRING implementation +size_t dictionary_version(DICTIONARY *dict); +size_t dictionary_entries(DICTIONARY *dict); +size_t dictionary_referenced_items(DICTIONARY *dict); +long int dictionary_stats_for_registry(DICTIONARY *dict); -typedef struct netdata_string STRING; -extern STRING *string_strdupz(const char *str); -extern STRING *string_dup(STRING *string); -extern void string_freez(STRING *string); -extern size_t string_length(STRING *string); -extern const char *string2str(STRING *string) NEVERNULL; +// for all cases that the caller does not provide a stats structure, this is where they are accumulated. +extern struct dictionary_stats dictionary_stats_category_other; -// keep common prefix/suffix and replace everything else with [x] -extern STRING *string_2way_merge(STRING *a, STRING *b); +int dictionary_unittest(size_t entries); + +// ---------------------------------------------------------------------------- +// THREAD CACHE -static inline int string_cmp(STRING *s1, STRING *s2) { - // STRINGs are deduplicated, so the same strings have the same pointer - if(unlikely(s1 == s2)) return 0; +void *thread_cache_entry_get_or_set(void *key, + ssize_t key_length, + void *value, + void *(*transform_the_value_before_insert)(void *key, size_t key_length, void *value)); - // they differ, do the typical comparison - return strcmp(string2str(s1), string2str(s2)); -} +void thread_cache_destroy(void); #endif /* NETDATA_DICTIONARY_H */ diff --git a/libnetdata/ebpf/ebpf.c b/libnetdata/ebpf/ebpf.c index c48f2f24e..382485e5f 100644 --- a/libnetdata/ebpf/ebpf.c +++ b/libnetdata/ebpf/ebpf.c @@ -239,7 +239,7 @@ static int kernel_is_rejected() } fclose(kernel_reject_list); - freez(reject_string); + free(reject_string); return 0; } @@ -279,7 +279,8 @@ static char *ebpf_select_kernel_name(uint32_t selector) { static char *kernel_names[] = { NETDATA_IDX_STR_V3_10, NETDATA_IDX_STR_V4_14, NETDATA_IDX_STR_V4_16, NETDATA_IDX_STR_V4_18, NETDATA_IDX_STR_V5_4, NETDATA_IDX_STR_V5_10, - NETDATA_IDX_STR_V5_11, NETDATA_IDX_STR_V5_15, NETDATA_IDX_STR_V5_16 + NETDATA_IDX_STR_V5_11, NETDATA_IDX_STR_V5_14, NETDATA_IDX_STR_V5_15, + NETDATA_IDX_STR_V5_16 }; return kernel_names[selector]; @@ -298,7 +299,11 @@ static char *ebpf_select_kernel_name(uint32_t selector) static int ebpf_select_max_index(int is_rhf, uint32_t kver) { if (is_rhf > 0) { // Is Red Hat family - if (kver >= NETDATA_EBPF_KERNEL_4_11) + if (kver >= NETDATA_EBPF_KERNEL_5_14) + return NETDATA_IDX_V5_14; + else if (kver >= NETDATA_EBPF_KERNEL_5_4 && kver < NETDATA_EBPF_KERNEL_5_5) // For Oracle Linux + return NETDATA_IDX_V5_4; + else if (kver >= NETDATA_EBPF_KERNEL_4_11) return NETDATA_IDX_V4_18; } else { // Kernels from kernel.org if (kver >= NETDATA_EBPF_KERNEL_5_16) @@ -334,6 +339,9 @@ static uint32_t ebpf_select_index(uint32_t kernels, int is_rhf, uint32_t kver) uint32_t start = ebpf_select_max_index(is_rhf, kver); uint32_t idx; + if (is_rhf == -1) + kernels &= ~NETDATA_V5_14; + for (idx = start; idx; idx--) { if (kernels & 1 << idx) break; @@ -438,9 +446,9 @@ void ebpf_update_stats(ebpf_plugin_stats_t *report, ebpf_module_t *em) // In theory the `else if` is useless, because when this function is called, the module should not stay in // EBPF_LOAD_PLAY_DICE. We have this additional condition to detect errors from developers. - if (em->load == EBPF_LOAD_LEGACY) + if (em->load & EBPF_LOAD_LEGACY) report->legacy++; - else if (em->load == EBPF_LOAD_CORE) + else if (em->load & EBPF_LOAD_CORE) report->core++; ebpf_stats_targets(report, em->targets); @@ -463,26 +471,42 @@ void ebpf_update_pid_table(ebpf_local_maps_t *pid, ebpf_module_t *em) * @param em the structure with information about how the module/thread is working. * @param map_name the name of the file used to log. */ -void ebpf_update_map_size(struct bpf_map *map, ebpf_local_maps_t *lmap, ebpf_module_t *em, const char *map_name) +void ebpf_update_map_size(struct bpf_map *map, ebpf_local_maps_t *lmap, ebpf_module_t *em, const char *map_name __maybe_unused) { + uint32_t define_size = 0; uint32_t apps_type = NETDATA_EBPF_MAP_PID | NETDATA_EBPF_MAP_RESIZABLE; if (lmap->user_input && lmap->user_input != lmap->internal_input) { + define_size = lmap->internal_input; #ifdef NETDATA_INTERNAL_CHECKS info("Changing map %s from size %u to %u ", map_name, lmap->internal_input, lmap->user_input); -#endif -#ifdef LIBBPF_MAJOR_VERSION - bpf_map__set_max_entries(map, lmap->user_input); -#else - bpf_map__resize(map, lmap->user_input); #endif } else if (((lmap->type & apps_type) == apps_type) && (!em->apps_charts) && (!em->cgroup_charts)) { lmap->user_input = ND_EBPF_DEFAULT_MIN_PID; + } else if (((em->apps_charts) || (em->cgroup_charts)) && (em->apps_level != NETDATA_APPS_NOT_SET)) { + switch (em->apps_level) { + case NETDATA_APPS_LEVEL_ALL: { + define_size = lmap->user_input; + break; + } + case NETDATA_APPS_LEVEL_PARENT: { + define_size = ND_EBPF_DEFAULT_PID_SIZE / 2; + break; + } + case NETDATA_APPS_LEVEL_REAL_PARENT: + default: { + define_size = ND_EBPF_DEFAULT_PID_SIZE / 3; + } + } + } + + if (!define_size) + return; + #ifdef LIBBPF_MAJOR_VERSION - bpf_map__set_max_entries(map, lmap->user_input); + bpf_map__set_max_entries(map, define_size); #else - bpf_map__resize(map, lmap->user_input); + bpf_map__resize(map, define_size); #endif - } } /** @@ -607,11 +631,18 @@ static void ebpf_update_maps(ebpf_module_t *em, struct bpf_object *obj) */ void ebpf_update_controller(int fd, ebpf_module_t *em) { - uint32_t key = NETDATA_CONTROLLER_APPS_ENABLED; - uint32_t value = (em->apps_charts & NETDATA_EBPF_APPS_FLAG_YES) | em->cgroup_charts; - int ret = bpf_map_update_elem(fd, &key, &value, 0); - if (ret) - error("Add key(%u) for controller table failed.", key); + uint32_t values[NETDATA_CONTROLLER_END] = { + (em->apps_charts & NETDATA_EBPF_APPS_FLAG_YES) | em->cgroup_charts, + em->apps_level + }; + uint32_t key; + uint32_t end = (em->apps_level != NETDATA_APPS_NOT_SET) ? NETDATA_CONTROLLER_END : NETDATA_CONTROLLER_APPS_LEVEL; + + for (key = NETDATA_CONTROLLER_APPS_ENABLED; key < end; key++) { + int ret = bpf_map_update_elem(fd, &key, &values[key], 0); + if (ret) + error("Add key(%u) for controller table failed.", key); + } } /** @@ -667,6 +698,10 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kv ebpf_mount_name(lpath, 4095, plugins_dir, idx, em->thread_name, em->mode); + // When this function is called ebpf.plugin is using legacy code, so we should reset the variable + em->load &= ~ NETDATA_EBPF_LOAD_METHODS; + em->load |= EBPF_LOAD_LEGACY; + *obj = bpf_object__open_file(lpath, NULL); if (libbpf_get_error(obj)) { error("Cannot open BPF object %s", lpath); @@ -797,14 +832,52 @@ netdata_ebpf_load_mode_t epbf_convert_string_to_load_mode(char *str) */ static char *ebpf_convert_load_mode_to_string(netdata_ebpf_load_mode_t mode) { - if (mode == EBPF_LOAD_CORE) + if (mode & EBPF_LOAD_CORE) return EBPF_CFG_CORE_PROGRAM; - else if (mode == EBPF_LOAD_LEGACY) + else if (mode & EBPF_LOAD_LEGACY) return EBPF_CFG_LEGACY_PROGRAM; return EBPF_CFG_DEFAULT_PROGRAM; } +/** + * Convert collect pid to string + * + * @param level value that will select the string + * + * @return It returns the string associated to level. + */ +static char *ebpf_convert_collect_pid_to_string(netdata_apps_level_t level) +{ + if (level == NETDATA_APPS_LEVEL_REAL_PARENT) + return EBPF_CFG_PID_REAL_PARENT; + else if (level == NETDATA_APPS_LEVEL_PARENT) + return EBPF_CFG_PID_PARENT; + else if (level == NETDATA_APPS_LEVEL_ALL) + return EBPF_CFG_PID_ALL; + + return EBPF_CFG_PID_INTERNAL_USAGE; +} + +/** + * Convert string to apps level + * + * @param str the argument read from config files + * + * @return it returns the level associated to the string or default when it is a wrong value + */ +netdata_apps_level_t ebpf_convert_string_to_apps_level(char *str) +{ + if (!strcasecmp(str, EBPF_CFG_PID_REAL_PARENT)) + return NETDATA_APPS_LEVEL_REAL_PARENT; + else if (!strcasecmp(str, EBPF_CFG_PID_PARENT)) + return NETDATA_APPS_LEVEL_PARENT; + else if (!strcasecmp(str, EBPF_CFG_PID_ALL)) + return NETDATA_APPS_LEVEL_ALL; + + return NETDATA_APPS_NOT_SET; +} + /** * CO-RE type * @@ -836,9 +909,11 @@ netdata_ebpf_program_loaded_t ebpf_convert_core_type(char *str, netdata_run_mode void ebpf_adjust_thread_load(ebpf_module_t *mod, struct btf *file) { if (!file) { - mod->load = EBPF_LOAD_LEGACY; + mod->load &= ~EBPF_LOAD_CORE; + mod->load |= EBPF_LOAD_LEGACY; } else if (mod->load == EBPF_LOAD_PLAY_DICE && file) { - mod->load = EBPF_LOAD_CORE; + mod->load &= ~EBPF_LOAD_LEGACY; + mod->load |= EBPF_LOAD_CORE; } } @@ -951,14 +1026,46 @@ static void ebpf_update_target_with_conf(ebpf_module_t *em, netdata_ebpf_program } } +/** + * Select Load Mode + * + * Select the load mode according the given inputs. + * + * @param btf_file a pointer to the loaded btf file. + * @parma load current value. + * @param btf_file a pointer to the loaded btf file. + * @param is_rhf is Red Hat family? + * + * @return it returns the new load mode. + */ +static netdata_ebpf_load_mode_t ebpf_select_load_mode(struct btf *btf_file, netdata_ebpf_load_mode_t load, + int kver, int is_rh) +{ +#ifdef LIBBPF_MAJOR_VERSION + if ((load & EBPF_LOAD_CORE) || (load & EBPF_LOAD_PLAY_DICE)) { + // Quick fix for Oracle linux 8.x + load = (!btf_file || (is_rh && (kver >= NETDATA_EBPF_KERNEL_5_4 && kver < NETDATA_EBPF_KERNEL_5_5))) ? + EBPF_LOAD_LEGACY : EBPF_LOAD_CORE; + } +#else + load = EBPF_LOAD_LEGACY; +#endif + + return load; +} + /** * Update Module using config * * Update configuration for a specific thread. * * @param modules structure that will be updated + * @oaram origin specify the configuration file loaded + * @param btf_file a pointer to the loaded btf file. + * @param is_rhf is Red Hat family? */ -void ebpf_update_module_using_config(ebpf_module_t *modules) +void ebpf_update_module_using_config(ebpf_module_t *modules, netdata_ebpf_load_mode_t origin, struct btf *btf_file, + int kver, int is_rh) { char default_value[EBPF_MAX_MODE_LENGTH + 1]; ebpf_select_mode_string(default_value, EBPF_MAX_MODE_LENGTH, modules->mode); @@ -977,13 +1084,19 @@ void ebpf_update_module_using_config(ebpf_module_t *modules) modules->pid_map_size = (uint32_t)appconfig_get_number(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_PID_SIZE, modules->pid_map_size); - value = ebpf_convert_load_mode_to_string(modules->load); + value = ebpf_convert_load_mode_to_string(modules->load & NETDATA_EBPF_LOAD_METHODS); value = appconfig_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_TYPE_FORMAT, value); - modules->load = epbf_convert_string_to_load_mode(value); + netdata_ebpf_load_mode_t load = epbf_convert_string_to_load_mode(value); + load = ebpf_select_load_mode(btf_file, load, kver, is_rh); + modules->load = origin | load; value = appconfig_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_CORE_ATTACH, EBPF_CFG_ATTACH_TRAMPOLINE); netdata_ebpf_program_loaded_t fill_lm = ebpf_convert_core_type(value, modules->mode); ebpf_update_target_with_conf(modules, fill_lm); + + value = ebpf_convert_collect_pid_to_string(modules->apps_level); + value = appconfig_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_COLLECT_PID, value); + modules->apps_level = ebpf_convert_string_to_apps_level(value); } /** @@ -995,10 +1108,15 @@ void ebpf_update_module_using_config(ebpf_module_t *modules) * update the variables. * * @param em the module structure + * @param btf_file a pointer to the loaded btf file. + * @param is_rhf is Red Hat family? + * @param kver the kernel version */ -void ebpf_update_module(ebpf_module_t *em) +void ebpf_update_module(ebpf_module_t *em, struct btf *btf_file, int kver, int is_rh) { char filename[FILENAME_MAX+1]; + netdata_ebpf_load_mode_t origin; + ebpf_mount_config_name(filename, FILENAME_MAX, ebpf_user_config_dir, em->config_file); if (!ebpf_load_config(em->cfg, filename)) { ebpf_mount_config_name(filename, FILENAME_MAX, ebpf_stock_config_dir, em->config_file); @@ -1006,9 +1124,32 @@ void ebpf_update_module(ebpf_module_t *em) error("Cannot load the ebpf configuration file %s", em->config_file); return; } - } + // If user defined data globaly, we will have here EBPF_LOADED_FROM_USER, we need to consider this, to avoid + // forcing users to configure thread by thread. + origin = (!(em->load & NETDATA_EBPF_LOAD_SOURCE)) ? EBPF_LOADED_FROM_STOCK : em->load & NETDATA_EBPF_LOAD_SOURCE; + } else + origin = EBPF_LOADED_FROM_USER; - ebpf_update_module_using_config(em); + ebpf_update_module_using_config(em, origin, btf_file, kver, is_rh); +} + +/** + * Adjust Apps Cgroup + * + * Apps and cgroup has internal cleanup that needs attaching tracers to release_task, to avoid overload the function + * we will enable this integration by default, if and only if, we are running with trampolines. + * + * @param em a poiter to the main thread structure. + * @param mode is the mode used with different + */ +void ebpf_adjust_apps_cgroup(ebpf_module_t *em, netdata_ebpf_program_loaded_t mode) +{ + if ((em->load & EBPF_LOADED_FROM_STOCK) && + (em->apps_charts || em->cgroup_charts) && + mode != EBPF_LOAD_TRAMPOLINE) { + em->apps_charts = NETDATA_EBPF_APPS_FLAG_NO; + em->cgroup_charts = 0; + } } //---------------------------------------------------------------------------------------------------------------------- @@ -1282,4 +1423,5 @@ void ebpf_select_host_prefix(char *output, size_t length, char *syscall, int kve char *prefix = (arch == 32) ? "__ia32" : "__x64"; snprintfz(output, length, "%s_sys_%s", prefix, syscall); } -} \ No newline at end of file +} + diff --git a/libnetdata/ebpf/ebpf.h b/libnetdata/ebpf/ebpf.h index 55b68a51e..5cff5134f 100644 --- a/libnetdata/ebpf/ebpf.h +++ b/libnetdata/ebpf/ebpf.h @@ -26,6 +26,12 @@ #define EBPF_CFG_CORE_PROGRAM "CO-RE" #define EBPF_CFG_LEGACY_PROGRAM "legacy" +#define EBPF_CFG_COLLECT_PID "collect pid" +#define EBPF_CFG_PID_REAL_PARENT "real parent" +#define EBPF_CFG_PID_PARENT "parent" +#define EBPF_CFG_PID_ALL "all" +#define EBPF_CFG_PID_INTERNAL_USAGE "not used" + #define EBPF_CFG_CORE_ATTACH "ebpf co-re tracing" #define EBPF_CFG_ATTACH_TRAMPOLINE "trampoline" #define EBPF_CFG_ATTACH_TRACEPOINT "tracepoint" @@ -71,11 +77,15 @@ */ enum netdata_ebpf_kernel_versions { NETDATA_EBPF_KERNEL_4_11 = 264960, // 264960 = 4 * 65536 + 15 * 256 + NETDATA_EBPF_KERNEL_4_14 = 265728, // 264960 = 4 * 65536 + 14 * 256 NETDATA_EBPF_KERNEL_4_15 = 265984, // 265984 = 4 * 65536 + 15 * 256 NETDATA_EBPF_KERNEL_4_17 = 266496, // 266496 = 4 * 65536 + 17 * 256 NETDATA_EBPF_KERNEL_5_0 = 327680, // 327680 = 5 * 65536 + 0 * 256 + NETDATA_EBPF_KERNEL_5_4 = 328704, // 327680 = 5 * 65536 + 4 * 256 + NETDATA_EBPF_KERNEL_5_5 = 328960, // 327680 = 5 * 65536 + 5 * 256 NETDATA_EBPF_KERNEL_5_10 = 330240, // 330240 = 5 * 65536 + 10 * 256 NETDATA_EBPF_KERNEL_5_11 = 330496, // 330240 = 5 * 65536 + 11 * 256 + NETDATA_EBPF_KERNEL_5_14 = 331264, // 331264 = 5 * 65536 + 14 * 256 NETDATA_EBPF_KERNEL_5_15 = 331520, // 331520 = 5 * 65536 + 15 * 256 NETDATA_EBPF_KERNEL_5_16 = 331776 // 331776 = 5 * 65536 + 16 * 256 }; @@ -88,8 +98,9 @@ enum netdata_kernel_flag { NETDATA_V5_4 = 1 << 4, NETDATA_V5_10 = 1 << 5, NETDATA_V5_11 = 1 << 6, - NETDATA_V5_15 = 1 << 7, - NETDATA_V5_16 = 1 << 8 + NETDATA_V5_14 = 1 << 7, + NETDATA_V5_15 = 1 << 8, + NETDATA_V5_16 = 1 << 9 }; enum netdata_kernel_idx { @@ -100,6 +111,7 @@ enum netdata_kernel_idx { NETDATA_IDX_V5_4 , NETDATA_IDX_V5_10, NETDATA_IDX_V5_11, + NETDATA_IDX_V5_14, NETDATA_IDX_V5_15, NETDATA_IDX_V5_16 }; @@ -111,6 +123,7 @@ enum netdata_kernel_idx { #define NETDATA_IDX_STR_V5_4 "5.4" #define NETDATA_IDX_STR_V5_10 "5.10" #define NETDATA_IDX_STR_V5_11 "5.11" +#define NETDATA_IDX_STR_V5_14 "5.14" #define NETDATA_IDX_STR_V5_15 "5.15" #define NETDATA_IDX_STR_V5_16 "5.16" @@ -161,10 +174,21 @@ enum netdata_ebpf_map_type { enum netdata_controller { NETDATA_CONTROLLER_APPS_ENABLED, + NETDATA_CONTROLLER_APPS_LEVEL, NETDATA_CONTROLLER_END }; +// Control how Netdata will monitor PIDs (apps and cgroups) +typedef enum netdata_apps_level { + NETDATA_APPS_LEVEL_REAL_PARENT, + NETDATA_APPS_LEVEL_PARENT, + NETDATA_APPS_LEVEL_ALL, + + // Present only in user ring + NETDATA_APPS_NOT_SET +} netdata_apps_level_t; + typedef struct ebpf_local_maps { char *name; uint32_t internal_input; @@ -181,11 +205,14 @@ typedef struct ebpf_specify_name { } ebpf_specify_name_t; typedef enum netdata_ebpf_load_mode { - EBPF_LOAD_LEGACY, // Select legacy mode, this means we will load binaries - EBPF_LOAD_CORE, // When CO-RE is used, it is necessary to use the souce code - - EBPF_LOAD_PLAY_DICE // Take a look on environment and choose the best option + EBPF_LOAD_LEGACY = 1<<0, // Select legacy mode, this means we will load binaries + EBPF_LOAD_CORE = 1<<1, // When CO-RE is used, it is necessary to use the souce code + EBPF_LOAD_PLAY_DICE = 1<<2, // Take a look on environment and choose the best option + EBPF_LOADED_FROM_STOCK = 1<<3, // Configuration loaded from Stock file + EBPF_LOADED_FROM_USER = 1<<4 // Configuration loaded from user } netdata_ebpf_load_mode_t; +#define NETDATA_EBPF_LOAD_METHODS (EBPF_LOAD_LEGACY|EBPF_LOAD_CORE|EBPF_LOAD_PLAY_DICE) +#define NETDATA_EBPF_LOAD_SOURCE (EBPF_LOADED_FROM_STOCK|EBPF_LOADED_FROM_USER) typedef enum netdata_ebpf_program_loaded { EBPF_LOAD_PROBE, // Attach probes on targets @@ -227,6 +254,7 @@ typedef struct ebpf_module { int update_every; int global_charts; netdata_apps_integration_flags_t apps_charts; + netdata_apps_level_t apps_level; int cgroup_charts; netdata_run_mode_t mode; uint32_t thread_id; @@ -242,26 +270,28 @@ typedef struct ebpf_module { netdata_ebpf_targets_t *targets; struct bpf_link **probe_links; struct bpf_object *objects; + struct netdata_static_thread *thread; } ebpf_module_t; -extern int ebpf_get_kernel_version(); -extern int get_redhat_release(); -extern int has_condition_to_run(int version); -extern char *ebpf_kernel_suffix(int version, int isrh); -extern struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kver, int is_rhf, +int ebpf_get_kernel_version(); +int get_redhat_release(); +int has_condition_to_run(int version); +char *ebpf_kernel_suffix(int version, int isrh); +struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kver, int is_rhf, struct bpf_object **obj); -extern void ebpf_mount_config_name(char *filename, size_t length, char *path, const char *config); -extern int ebpf_load_config(struct config *config, char *filename); -extern void ebpf_update_module(ebpf_module_t *em); -extern void ebpf_update_names(ebpf_specify_name_t *opt, ebpf_module_t *em); -extern char *ebpf_find_symbol(char *search); -extern void ebpf_load_addresses(ebpf_addresses_t *fa, int fd); -extern void ebpf_fill_algorithms(int *algorithms, size_t length, int algorithm); -extern char **ebpf_fill_histogram_dimension(size_t maximum); -extern void ebpf_update_stats(ebpf_plugin_stats_t *report, ebpf_module_t *em); -extern void ebpf_update_controller(int fd, ebpf_module_t *em); -extern void ebpf_update_map_size(struct bpf_map *map, ebpf_local_maps_t *lmap, ebpf_module_t *em, const char *map_name); +void ebpf_mount_config_name(char *filename, size_t length, char *path, const char *config); +int ebpf_load_config(struct config *config, char *filename); +void ebpf_update_module(ebpf_module_t *em, struct btf *btf_file, int kver, int is_rh); +void ebpf_update_names(ebpf_specify_name_t *opt, ebpf_module_t *em); +void ebpf_adjust_apps_cgroup(ebpf_module_t *em, netdata_ebpf_program_loaded_t mode); +char *ebpf_find_symbol(char *search); +void ebpf_load_addresses(ebpf_addresses_t *fa, int fd); +void ebpf_fill_algorithms(int *algorithms, size_t length, int algorithm); +char **ebpf_fill_histogram_dimension(size_t maximum); +void ebpf_update_stats(ebpf_plugin_stats_t *report, ebpf_module_t *em); +void ebpf_update_controller(int fd, ebpf_module_t *em); +void ebpf_update_map_size(struct bpf_map *map, ebpf_local_maps_t *lmap, ebpf_module_t *em, const char *map_name); // Histogram #define NETDATA_EBPF_HIST_MAX_BINS 24UL @@ -312,13 +342,13 @@ typedef struct ebpf_sync_syscalls { #endif } ebpf_sync_syscalls_t; -extern void ebpf_histogram_dimension_cleanup(char **ptr, size_t length); +void ebpf_histogram_dimension_cleanup(char **ptr, size_t length); // Tracepoint helpers // For more information related to tracepoints read https://www.kernel.org/doc/html/latest/trace/tracepoints.html -extern int ebpf_is_tracepoint_enabled(char *subsys, char *eventname); -extern int ebpf_enable_tracing_values(char *subsys, char *eventname); -extern int ebpf_disable_tracing_values(char *subsys, char *eventname); +int ebpf_is_tracepoint_enabled(char *subsys, char *eventname); +int ebpf_enable_tracing_values(char *subsys, char *eventname); +int ebpf_disable_tracing_values(char *subsys, char *eventname); // BTF Section #define EBPF_DEFAULT_BTF_FILE "vmlinux" @@ -328,14 +358,14 @@ extern int ebpf_disable_tracing_values(char *subsys, char *eventname); // BTF helpers #define NETDATA_EBPF_MAX_SYSCALL_LENGTH 255 -extern netdata_ebpf_load_mode_t epbf_convert_string_to_load_mode(char *str); -extern netdata_ebpf_program_loaded_t ebpf_convert_core_type(char *str, netdata_run_mode_t lmode); -extern void ebpf_select_host_prefix(char *output, size_t length, char *syscall, int kver); +netdata_ebpf_load_mode_t epbf_convert_string_to_load_mode(char *str); +netdata_ebpf_program_loaded_t ebpf_convert_core_type(char *str, netdata_run_mode_t lmode); +void ebpf_select_host_prefix(char *output, size_t length, char *syscall, int kver); #ifdef LIBBPF_MAJOR_VERSION -extern void ebpf_adjust_thread_load(ebpf_module_t *mod, struct btf *file); -extern struct btf *ebpf_parse_btf_file(const char *filename); -extern struct btf *ebpf_load_btf_file(char *path, char *filename); -extern int ebpf_is_function_inside_btf(struct btf *file, char *function); +void ebpf_adjust_thread_load(ebpf_module_t *mod, struct btf *file); +struct btf *ebpf_parse_btf_file(const char *filename); +struct btf *ebpf_load_btf_file(char *path, char *filename); +int ebpf_is_function_inside_btf(struct btf *file, char *function); #endif #endif /* NETDATA_EBPF_H */ diff --git a/libnetdata/eval/eval.c b/libnetdata/eval/eval.c index e86cbd587..0e429a08c 100644 --- a/libnetdata/eval/eval.c +++ b/libnetdata/eval/eval.c @@ -62,24 +62,36 @@ static inline void print_parsed_as_constant(BUFFER *out, NETDATA_DOUBLE n); // evaluation of expressions static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) { - static uint32_t this_hash = 0, now_hash = 0, after_hash = 0, before_hash = 0, status_hash = 0, removed_hash = 0, uninitialized_hash = 0, undefined_hash = 0, clear_hash = 0, warning_hash = 0, critical_hash = 0; + static STRING + *this_string = NULL, + *now_string = NULL, + *after_string = NULL, + *before_string = NULL, + *status_string = NULL, + *removed_string = NULL, + *uninitialized_string = NULL, + *undefined_string = NULL, + *clear_string = NULL, + *warning_string = NULL, + *critical_string = NULL; + NETDATA_DOUBLE n; - if(unlikely(this_hash == 0)) { - this_hash = simple_hash("this"); - now_hash = simple_hash("now"); - after_hash = simple_hash("after"); - before_hash = simple_hash("before"); - status_hash = simple_hash("status"); - removed_hash = simple_hash("REMOVED"); - uninitialized_hash = simple_hash("UNINITIALIZED"); - undefined_hash = simple_hash("UNDEFINED"); - clear_hash = simple_hash("CLEAR"); - warning_hash = simple_hash("WARNING"); - critical_hash = simple_hash("CRITICAL"); - } - - if(unlikely(v->hash == this_hash && !strcmp(v->name, "this"))) { + if(unlikely(this_string == NULL)) { + this_string = string_strdupz("this"); + now_string = string_strdupz("now"); + after_string = string_strdupz("after"); + before_string = string_strdupz("before"); + status_string = string_strdupz("status"); + removed_string = string_strdupz("REMOVED"); + uninitialized_string = string_strdupz("UNINITIALIZED"); + undefined_string = string_strdupz("UNDEFINED"); + clear_string = string_strdupz("CLEAR"); + warning_string = string_strdupz("WARNING"); + critical_string = string_strdupz("CRITICAL"); + } + + if(unlikely(v->name == this_string)) { n = (exp->myself)?*exp->myself:NAN; buffer_strcat(exp->error_msg, "[ $this = "); print_parsed_as_constant(exp->error_msg, n); @@ -87,7 +99,7 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(unlikely(v->hash == after_hash && !strcmp(v->name, "after"))) { + if(unlikely(v->name == after_string)) { n = (exp->after && *exp->after)?*exp->after:NAN; buffer_strcat(exp->error_msg, "[ $after = "); print_parsed_as_constant(exp->error_msg, n); @@ -95,7 +107,7 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(unlikely(v->hash == before_hash && !strcmp(v->name, "before"))) { + if(unlikely(v->name == before_string)) { n = (exp->before && *exp->before)?*exp->before:NAN; buffer_strcat(exp->error_msg, "[ $before = "); print_parsed_as_constant(exp->error_msg, n); @@ -103,15 +115,15 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(unlikely(v->hash == now_hash && !strcmp(v->name, "now"))) { - n = now_realtime_sec(); + if(unlikely(v->name == now_string)) { + n = (NETDATA_DOUBLE)now_realtime_sec(); buffer_strcat(exp->error_msg, "[ $now = "); print_parsed_as_constant(exp->error_msg, n); buffer_strcat(exp->error_msg, " ] "); return n; } - if(unlikely(v->hash == status_hash && !strcmp(v->name, "status"))) { + if(unlikely(v->name == status_string)) { n = (exp->status)?*exp->status:RRDCALC_STATUS_UNINITIALIZED; buffer_strcat(exp->error_msg, "[ $status = "); print_parsed_as_constant(exp->error_msg, n); @@ -119,7 +131,7 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(unlikely(v->hash == removed_hash && !strcmp(v->name, "REMOVED"))) { + if(unlikely(v->name == removed_string)) { n = RRDCALC_STATUS_REMOVED; buffer_strcat(exp->error_msg, "[ $REMOVED = "); print_parsed_as_constant(exp->error_msg, n); @@ -127,7 +139,7 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(unlikely(v->hash == uninitialized_hash && !strcmp(v->name, "UNINITIALIZED"))) { + if(unlikely(v->name == uninitialized_string)) { n = RRDCALC_STATUS_UNINITIALIZED; buffer_strcat(exp->error_msg, "[ $UNINITIALIZED = "); print_parsed_as_constant(exp->error_msg, n); @@ -135,7 +147,7 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(unlikely(v->hash == undefined_hash && !strcmp(v->name, "UNDEFINED"))) { + if(unlikely(v->name == undefined_string)) { n = RRDCALC_STATUS_UNDEFINED; buffer_strcat(exp->error_msg, "[ $UNDEFINED = "); print_parsed_as_constant(exp->error_msg, n); @@ -143,7 +155,7 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(unlikely(v->hash == clear_hash && !strcmp(v->name, "CLEAR"))) { + if(unlikely(v->name == clear_string)) { n = RRDCALC_STATUS_CLEAR; buffer_strcat(exp->error_msg, "[ $CLEAR = "); print_parsed_as_constant(exp->error_msg, n); @@ -151,7 +163,7 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(unlikely(v->hash == warning_hash && !strcmp(v->name, "WARNING"))) { + if(unlikely(v->name == warning_string)) { n = RRDCALC_STATUS_WARNING; buffer_strcat(exp->error_msg, "[ $WARNING = "); print_parsed_as_constant(exp->error_msg, n); @@ -159,7 +171,7 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(unlikely(v->hash == critical_hash && !strcmp(v->name, "CRITICAL"))) { + if(unlikely(v->name == critical_string)) { n = RRDCALC_STATUS_CRITICAL; buffer_strcat(exp->error_msg, "[ $CRITICAL = "); print_parsed_as_constant(exp->error_msg, n); @@ -167,15 +179,15 @@ static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE * return n; } - if(exp->rrdcalc && health_variable_lookup(v->name, v->hash, exp->rrdcalc, &n)) { - buffer_sprintf(exp->error_msg, "[ ${%s} = ", v->name); + if(exp->rrdcalc && health_variable_lookup(v->name, exp->rrdcalc, &n)) { + buffer_sprintf(exp->error_msg, "[ ${%s} = ", string2str(v->name)); print_parsed_as_constant(exp->error_msg, n); buffer_strcat(exp->error_msg, " ] "); return n; } *error = EVAL_ERROR_UNKNOWN_VARIABLE; - buffer_sprintf(exp->error_msg, "[ undefined variable '%s' ] ", v->name); + buffer_sprintf(exp->error_msg, "[ undefined variable '%s' ] ", string2str(v->name)); return NAN; } @@ -357,7 +369,7 @@ static inline NETDATA_DOUBLE eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int static inline void print_parsed_as_variable(BUFFER *out, EVAL_VARIABLE *v, int *error) { (void)error; - buffer_sprintf(out, "${%s}", v->name); + buffer_sprintf(out, "${%s}", string2str(v->name)); } static inline void print_parsed_as_constant(BUFFER *out, NETDATA_DOUBLE n) { @@ -859,12 +871,11 @@ static inline void eval_node_set_value_to_variable(EVAL_NODE *op, int pos, const op->ops[pos].type = EVAL_VALUE_VARIABLE; op->ops[pos].variable = callocz(1, sizeof(EVAL_VARIABLE)); - op->ops[pos].variable->name = strdupz(variable); - op->ops[pos].variable->hash = simple_hash(op->ops[pos].variable->name); + op->ops[pos].variable->name = string_strdupz(variable); } static inline void eval_variable_free(EVAL_VARIABLE *v) { - freez(v->name); + string_freez(v->name); freez(v); } diff --git a/libnetdata/eval/eval.h b/libnetdata/eval/eval.h index 086d171aa..1633ec505 100644 --- a/libnetdata/eval/eval.h +++ b/libnetdata/eval/eval.h @@ -18,8 +18,7 @@ typedef enum rrdcalc_status { } RRDCALC_STATUS; typedef struct eval_variable { - char *name; - uint32_t hash; + STRING *name; struct eval_variable *next; } EVAL_VARIABLE; @@ -70,19 +69,19 @@ typedef struct eval_expression { // parse the given string as an expression and return: // a pointer to an expression if it parsed OK // NULL in which case the pointer to error has the error code -extern EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error); +EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error); // free all resources allocated for an expression -extern void expression_free(EVAL_EXPRESSION *expression); +void expression_free(EVAL_EXPRESSION *expression); // convert an error code to a message -extern const char *expression_strerror(int error); +const char *expression_strerror(int error); // evaluate an expression and return // 1 = OK, the result is in: expression->result // 2 = FAILED, the error message is in: buffer_tostring(expression->error_msg) -extern int expression_evaluate(EVAL_EXPRESSION *expression); +int expression_evaluate(EVAL_EXPRESSION *expression); -extern int health_variable_lookup(const char *variable, uint32_t hash, struct rrdcalc *rc, NETDATA_DOUBLE *result); +int health_variable_lookup(STRING *variable, struct rrdcalc *rc, NETDATA_DOUBLE *result); #endif //NETDATA_EVAL_H diff --git a/libnetdata/health/health.h b/libnetdata/health/health.h index f7580edab..6b8f9b384 100644 --- a/libnetdata/health/health.h +++ b/libnetdata/health/health.h @@ -46,10 +46,10 @@ typedef struct silencers { extern SILENCERS *silencers; -extern SILENCER *create_silencer(void); -extern int health_silencers_json_read_callback(JSON_ENTRY *e); -extern void health_silencers_add(SILENCER *silencer); -extern SILENCER * health_silencers_addparam(SILENCER *silencer, char *key, char *value); -extern int health_initialize_global_silencers(); +SILENCER *create_silencer(void); +int health_silencers_json_read_callback(JSON_ENTRY *e); +void health_silencers_add(SILENCER *silencer); +SILENCER * health_silencers_addparam(SILENCER *silencer, char *key, char *value); +int health_initialize_global_silencers(); #endif diff --git a/libnetdata/inlined.h b/libnetdata/inlined.h index 5c265fc01..aa7f3c213 100644 --- a/libnetdata/inlined.h +++ b/libnetdata/inlined.h @@ -42,24 +42,12 @@ static inline uint32_t simple_uhash(const char *name) { return hval; } -static inline int simple_hash_strcmp(const char *name, const char *b, uint32_t *hash) { - unsigned char *s = (unsigned char *) name; - uint32_t hval = 0x811c9dc5; - int ret = 0; - while (*s) { - if(!ret) ret = *s - *b++; - hval *= 16777619; - hval ^= (uint32_t) *s++; - } - *hash = hval; - return ret; -} - static inline int str2i(const char *s) { int n = 0; - char c, negative = (*s == '-'); + char c, negative = (char)(*s == '-'); + const char *e = s + 30; // max number of character to iterate - for(c = (negative)?*(++s):*s; c >= '0' && c <= '9' ; c = *(++s)) { + for(c = (char)((negative)?*(++s):*s); c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -73,8 +61,9 @@ static inline int str2i(const char *s) { static inline long str2l(const char *s) { long n = 0; char c, negative = (*s == '-'); + const char *e = &s[30]; // max number of character to iterate - for(c = (negative)?*(++s):*s; c >= '0' && c <= '9' ; c = *(++s)) { + for(c = (negative)?*(++s):*s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -88,7 +77,9 @@ static inline long str2l(const char *s) { static inline uint32_t str2uint32_t(const char *s) { uint32_t n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -98,7 +89,9 @@ static inline uint32_t str2uint32_t(const char *s) { static inline uint64_t str2uint64_t(const char *s) { uint64_t n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -108,7 +101,9 @@ static inline uint64_t str2uint64_t(const char *s) { static inline unsigned long str2ul(const char *s) { unsigned long n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -118,7 +113,9 @@ static inline unsigned long str2ul(const char *s) { static inline unsigned long long str2ull(const char *s) { unsigned long long n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -137,7 +134,9 @@ static inline long long str2ll(const char *s, char **endptr) { long long n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -182,6 +181,42 @@ static inline void sanitize_json_string(char *dst, const char *src, size_t dst_s *dst = '\0'; } +static inline bool sanitize_command_argument_string(char *dst, const char *src, size_t dst_size) { + // skip leading dashes + while (src[0] == '-') + src++; + + // escape single quotes + while (src[0] != '\0') { + if (src[0] == '\'') { + if (dst_size < 4) + return false; + + dst[0] = '\''; dst[1] = '\\'; dst[2] = '\''; dst[3] = '\''; + + dst += 4; + dst_size -= 4; + } else { + if (dst_size < 1) + return false; + + dst[0] = src[0]; + + dst += 1; + dst_size -= 1; + } + + src++; + } + + // make sure we have space to terminate the string + if (dst_size == 0) + return false; + *dst = '\0'; + + return true; +} + static inline int read_file(const char *filename, char *buffer, size_t size) { if(unlikely(!size)) return 3; diff --git a/libnetdata/json/json.c b/libnetdata/json/json.c index e03556b50..d5f62edaf 100644 --- a/libnetdata/json/json.c +++ b/libnetdata/json/json.c @@ -102,7 +102,7 @@ int json_callback_print(JSON_ENTRY *e) case JSON_ARRAY: e->callback_function = json_callback_print; - sprintf(txt,"ARRAY[%lu]", e->data.items); + sprintf(txt,"ARRAY[%lu]", (long unsigned int) e->data.items); buffer_strcat(wb, txt); break; @@ -553,4 +553,5 @@ int json_test(char *str) { return json_parse(str, NULL, json_callback_print); } - */ \ No newline at end of file + */ + diff --git a/libnetdata/libnetdata.c b/libnetdata/libnetdata.c index 5b6c541ed..cc04a97eb 100644 --- a/libnetdata/libnetdata.c +++ b/libnetdata/libnetdata.c @@ -30,128 +30,367 @@ const char *program_version = VERSION; // its lifetime), these can be used to override the default system allocation // routines. -#ifdef NETDATA_LOG_ALLOCATIONS -#warning NETDATA_LOG_ALLOCATIONS ENABLED - set log_thread_memory_allocations=1 on any thread to log all its allocations - or use log_allocations() to log them on demand - -static __thread struct memory_statistics { - volatile ssize_t malloc_calls_made; - volatile ssize_t calloc_calls_made; - volatile ssize_t realloc_calls_made; - volatile ssize_t strdup_calls_made; - volatile ssize_t free_calls_made; - volatile ssize_t memory_calls_made; - volatile ssize_t allocated_memory; - volatile ssize_t mmapped_memory; -} memory_statistics = { 0, 0, 0, 0, 0, 0, 0, 0 }; - -__thread size_t log_thread_memory_allocations = 0; - -inline void log_allocations_int(const char *file, const char *function, const unsigned long line) { - static __thread struct memory_statistics old = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - fprintf(stderr, "%s MEMORY ALLOCATIONS: (%04lu@%s:%s): Allocated %zd KiB (%+zd B), mmapped %zd KiB (%+zd B): : malloc %zd (%+zd), calloc %zd (%+zd), realloc %zd (%+zd), strdup %zd (%+zd), free %zd (%+zd)\n", - netdata_thread_tag(), - line, file, function, - (memory_statistics.allocated_memory + 512) / 1024, memory_statistics.allocated_memory - old.allocated_memory, - (memory_statistics.mmapped_memory + 512) / 1024, memory_statistics.mmapped_memory - old.mmapped_memory, - memory_statistics.malloc_calls_made, memory_statistics.malloc_calls_made - old.malloc_calls_made, - memory_statistics.calloc_calls_made, memory_statistics.calloc_calls_made - old.calloc_calls_made, - memory_statistics.realloc_calls_made, memory_statistics.realloc_calls_made - old.realloc_calls_made, - memory_statistics.strdup_calls_made, memory_statistics.strdup_calls_made - old.strdup_calls_made, - memory_statistics.free_calls_made, memory_statistics.free_calls_made - old.free_calls_made - ); - - memcpy(&old, &memory_statistics, sizeof(struct memory_statistics)); -} - -static inline void mmap_accounting(size_t size) { - if(log_thread_memory_allocations) { - memory_statistics.memory_calls_made++; - memory_statistics.mmapped_memory += size; +#ifdef NETDATA_TRACE_ALLOCATIONS +#warning NETDATA_TRACE_ALLOCATIONS ENABLED +#include "Judy.h" + +#if defined(HAVE_DLSYM) && defined(ENABLE_DLSYM) +#include + +typedef void (*libc_function_t)(void); + +static void *malloc_first_run(size_t size); +static void *(*libc_malloc)(size_t) = malloc_first_run; + +static void *calloc_first_run(size_t n, size_t size); +static void *(*libc_calloc)(size_t, size_t) = calloc_first_run; + +static void *realloc_first_run(void *ptr, size_t size); +static void *(*libc_realloc)(void *, size_t) = realloc_first_run; + +static void free_first_run(void *ptr); +static void (*libc_free)(void *) = free_first_run; + +static char *strdup_first_run(const char *s); +static char *(*libc_strdup)(const char *) = strdup_first_run; + +static size_t malloc_usable_size_first_run(void *ptr); +#ifdef HAVE_MALLOC_USABLE_SIZE +static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size_first_run; +#else +static size_t (*libc_malloc_usable_size)(void *) = NULL; +#endif + +static void link_system_library_function(libc_function_t *func_pptr, const char *name, bool required) { + *func_pptr = dlsym(RTLD_NEXT, name); + if(!*func_pptr && required) { + fprintf(stderr, "FATAL: Cannot find system's %s() function.\n", name); + abort(); } } -void *mallocz_int(const char *file, const char *function, const unsigned long line, size_t size) { - memory_statistics.memory_calls_made++; - memory_statistics.malloc_calls_made++; - memory_statistics.allocated_memory += size; +static void *malloc_first_run(size_t size) { + link_system_library_function((libc_function_t *) &libc_malloc, "malloc", true); + return libc_malloc(size); +} - if(log_thread_memory_allocations) - log_allocations_int(file, function, line); +static void *calloc_first_run(size_t n, size_t size) { + link_system_library_function((libc_function_t *) &libc_calloc, "calloc", true); + return libc_calloc(n, size); +} - size_t *n = (size_t *)malloc(sizeof(size_t) + size); - if (unlikely(!n)) fatal("mallocz() cannot allocate %zu bytes of memory.", size); - *n = size; - return (void *)&n[1]; +static void *realloc_first_run(void *ptr, size_t size) { + link_system_library_function((libc_function_t *) &libc_realloc, "realloc", true); + return libc_realloc(ptr, size); } -void *callocz_int(const char *file, const char *function, const unsigned long line, size_t nmemb, size_t size) { - size = nmemb * size; +static void free_first_run(void *ptr) { + link_system_library_function((libc_function_t *) &libc_free, "free", true); + libc_free(ptr); +} + +static char *strdup_first_run(const char *s) { + link_system_library_function((libc_function_t *) &libc_strdup, "strdup", true); + return libc_strdup(s); +} + +static size_t malloc_usable_size_first_run(void *ptr) { + link_system_library_function((libc_function_t *) &libc_malloc_usable_size, "malloc_usable_size", false); + + if(libc_malloc_usable_size) + return libc_malloc_usable_size(ptr); + else + return 0; +} + +void *malloc(size_t size) { + return mallocz(size); +} + +void *calloc(size_t n, size_t size) { + return callocz(n, size); +} + +void *realloc(void *ptr, size_t size) { + return reallocz(ptr, size); +} + +void *reallocarray(void *ptr, size_t n, size_t size) { + return reallocz(ptr, n * size); +} + +void free(void *ptr) { + freez(ptr); +} + +char *strdup(const char *s) { + return strdupz(s); +} + +size_t malloc_usable_size(void *ptr) { + return mallocz_usable_size(ptr); +} +#else // !HAVE_DLSYM + +static void *(*libc_malloc)(size_t) = malloc; +static void *(*libc_calloc)(size_t, size_t) = calloc; +static void *(*libc_realloc)(void *, size_t) = realloc; +static void (*libc_free)(void *) = free; + +#ifdef HAVE_MALLOC_USABLE_SIZE +static size_t (*libc_malloc_usable_size)(void *) = malloc_usable_size; +#else +static size_t (*libc_malloc_usable_size)(void *) = NULL; +#endif - memory_statistics.memory_calls_made++; - memory_statistics.calloc_calls_made++; - memory_statistics.allocated_memory += size; - if(log_thread_memory_allocations) - log_allocations_int(file, function, line); +#endif // HAVE_DLSYM - size_t *n = (size_t *)calloc(1, sizeof(size_t) + size); - if (unlikely(!n)) fatal("callocz() cannot allocate %zu bytes of memory.", size); - *n = size; - return (void *)&n[1]; + +void posix_memfree(void *ptr) { + libc_free(ptr); +} + +Word_t JudyMalloc(Word_t Words) { + Word_t Addr; + + Addr = (Word_t) mallocz(Words * sizeof(Word_t)); + return(Addr); +} +void JudyFree(void * PWord, Word_t Words) { + (void)Words; + freez(PWord); +} +Word_t JudyMallocVirtual(Word_t Words) { + Word_t Addr; + + Addr = (Word_t) mallocz(Words * sizeof(Word_t)); + return(Addr); +} +void JudyFreeVirtual(void * PWord, Word_t Words) { + (void)Words; + freez(PWord); +} + +#define MALLOC_ALIGNMENT (sizeof(uintptr_t) * 2) +#define size_t_atomic_count(op, var, size) __atomic_## op ##_fetch(&(var), size, __ATOMIC_RELAXED) +#define size_t_atomic_bytes(op, var, size) __atomic_## op ##_fetch(&(var), ((size) % MALLOC_ALIGNMENT)?((size) + MALLOC_ALIGNMENT - ((size) % MALLOC_ALIGNMENT)):(size), __ATOMIC_RELAXED) + +struct malloc_header_signature { + uint32_t magic; + uint32_t size; + struct malloc_trace *trace; +}; + +struct malloc_header { + struct malloc_header_signature signature; + uint8_t padding[(sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) ? MALLOC_ALIGNMENT - (sizeof(struct malloc_header_signature) % MALLOC_ALIGNMENT) : 0]; + uint8_t data[]; +}; + +static size_t malloc_header_size = sizeof(struct malloc_header); + +int malloc_trace_compare(void *A, void *B) { + struct malloc_trace *a = A; + struct malloc_trace *b = B; + return strcmp(a->function, b->function); +} + +static avl_tree_lock malloc_trace_index = { + .avl_tree = { + .root = NULL, + .compar = malloc_trace_compare}, + .rwlock = NETDATA_RWLOCK_INITIALIZER +}; + +int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data) { + return avl_traverse_lock(&malloc_trace_index, callback, data); } -void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size) { - if(!ptr) return mallocz_int(file, function, line, size); +NEVERNULL WARNUNUSED +static struct malloc_trace *malloc_trace_find_or_create(const char *file, const char *function, size_t line) { + struct malloc_trace tmp = { + .line = line, + .function = function, + .file = file, + }; + + struct malloc_trace *t = (struct malloc_trace *)avl_search_lock(&malloc_trace_index, (avl_t *)&tmp); + if(!t) { + t = libc_calloc(1, sizeof(struct malloc_trace)); + if(!t) fatal("No memory"); + t->line = line; + t->function = function; + t->file = file; + + struct malloc_trace *t2 = (struct malloc_trace *)avl_insert_lock(&malloc_trace_index, (avl_t *)t); + if(t2 != t) + free(t); + + t = t2; + } + + if(!t) + fatal("Cannot insert to AVL"); + + return t; +} + +void malloc_trace_mmap(size_t size) { + struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1); + size_t_atomic_count(add, p->mmap_calls, 1); + size_t_atomic_count(add, p->allocations, 1); + size_t_atomic_bytes(add, p->bytes, size); +} + +void malloc_trace_munmap(size_t size) { + struct malloc_trace *p = malloc_trace_find_or_create("unknown", "netdata_mmap", 1); + size_t_atomic_count(add, p->munmap_calls, 1); + size_t_atomic_count(sub, p->allocations, 1); + size_t_atomic_bytes(sub, p->bytes, size); +} + +void *mallocz_int(size_t size, const char *file, const char *function, size_t line) { + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line); - size_t *n = (size_t *)ptr; - n--; - size_t old_size = *n; + size_t_atomic_count(add, p->malloc_calls, 1); + size_t_atomic_count(add, p->allocations, 1); + size_t_atomic_bytes(add, p->bytes, size); - n = realloc(n, sizeof(size_t) + size); - if (unlikely(!n)) fatal("reallocz() cannot allocate %zu bytes of memory (from %zu bytes).", size, old_size); + struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size); + if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size); + t->signature.magic = 0x0BADCAFE; + t->signature.trace = p; + t->signature.size = size; - memory_statistics.memory_calls_made++; - memory_statistics.realloc_calls_made++; - memory_statistics.allocated_memory += (size - old_size); - if(log_thread_memory_allocations) - log_allocations_int(file, function, line); +#ifdef NETDATA_INTERNAL_CHECKS + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded + t->padding[i] = 0xFF; +#endif + + return (void *)&t->data; +} + +void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line) { + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line); + size = nmemb * size; - *n = size; - return (void *)&n[1]; + size_t_atomic_count(add, p->calloc_calls, 1); + size_t_atomic_count(add, p->allocations, 1); + size_t_atomic_bytes(add, p->bytes, size); + + struct malloc_header *t = (struct malloc_header *)libc_calloc(1, malloc_header_size + size); + if (unlikely(!t)) fatal("mallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size); + t->signature.magic = 0x0BADCAFE; + t->signature.trace = p; + t->signature.size = size; + +#ifdef NETDATA_INTERNAL_CHECKS + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded + t->padding[i] = 0xFF; +#endif + + return &t->data; } -char *strdupz_int(const char *file, const char *function, const unsigned long line, const char *s) { +char *strdupz_int(const char *s, const char *file, const char *function, size_t line) { + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line); size_t size = strlen(s) + 1; - memory_statistics.memory_calls_made++; - memory_statistics.strdup_calls_made++; - memory_statistics.allocated_memory += size; - if(log_thread_memory_allocations) - log_allocations_int(file, function, line); + size_t_atomic_count(add, p->strdup_calls, 1); + size_t_atomic_count(add, p->allocations, 1); + size_t_atomic_bytes(add, p->bytes, size); - size_t *n = (size_t *)malloc(sizeof(size_t) + size); - if (unlikely(!n)) fatal("strdupz() cannot allocate %zu bytes of memory.", size); + struct malloc_header *t = (struct malloc_header *)libc_malloc(malloc_header_size + size); + if (unlikely(!t)) fatal("strdupz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size); + t->signature.magic = 0x0BADCAFE; + t->signature.trace = p; + t->signature.size = size; + +#ifdef NETDATA_INTERNAL_CHECKS + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded + t->padding[i] = 0xFF; +#endif + + memcpy(&t->data, s, size); + return (char *)&t->data; +} + +static struct malloc_header *malloc_get_header(void *ptr, const char *caller, const char *file, const char *function, size_t line) { + uint8_t *ret = (uint8_t *)ptr - malloc_header_size; + struct malloc_header *t = (struct malloc_header *)ret; + + if(t->signature.magic != 0x0BADCAFE) { + error("pointer %p is not our pointer (called %s() from %zu@%s, %s()).", ptr, caller, line, file, function); + return NULL; + } - *n = size; - char *t = (char *)&n[1]; - strcpy(t, s); return t; } -void freez_int(const char *file, const char *function, const unsigned long line, void *ptr) { +void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line) { + if(!ptr) return mallocz_int(size, file, function, line); + + struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line); + if(!t) + return libc_realloc(ptr, size); + + if(t->signature.size == size) return ptr; + size_t_atomic_count(add, t->signature.trace->free_calls, 1); + size_t_atomic_count(sub, t->signature.trace->allocations, 1); + size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size); + + struct malloc_trace *p = malloc_trace_find_or_create(file, function, line); + size_t_atomic_count(add, p->realloc_calls, 1); + size_t_atomic_count(add, p->allocations, 1); + size_t_atomic_bytes(add, p->bytes, size); + + t = (struct malloc_header *)libc_realloc(t, malloc_header_size + size); + if (unlikely(!t)) fatal("reallocz() cannot allocate %zu bytes of memory (%zu with header).", size, malloc_header_size + size); + t->signature.magic = 0x0BADCAFE; + t->signature.trace = p; + t->signature.size = size; + +#ifdef NETDATA_INTERNAL_CHECKS + for(ssize_t i = 0; i < (ssize_t)sizeof(t->padding) ;i++) // signed to avoid compiler warning when zero-padded + t->padding[i] = 0xFF; +#endif + + return (void *)&t->data; +} + +size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line) { + if(unlikely(!ptr)) return 0; + + struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line); + if(!t) { + if(libc_malloc_usable_size) + return libc_malloc_usable_size(ptr); + else + return 0; + } + + return t->signature.size; +} + +void freez_int(void *ptr, const char *file, const char *function, size_t line) { if(unlikely(!ptr)) return; - size_t *n = (size_t *)ptr; - n--; - size_t size = *n; + struct malloc_header *t = malloc_get_header(ptr, __FUNCTION__, file, function, line); + if(!t) { + libc_free(ptr); + return; + } + + size_t_atomic_count(add, t->signature.trace->free_calls, 1); + size_t_atomic_count(sub, t->signature.trace->allocations, 1); + size_t_atomic_bytes(sub, t->signature.trace->bytes, t->signature.size); - memory_statistics.memory_calls_made++; - memory_statistics.free_calls_made++; - memory_statistics.allocated_memory -= size; - if(log_thread_memory_allocations) - log_allocations_int(file, function, line); +#ifdef NETDATA_INTERNAL_CHECKS + // it should crash if it is used after freeing it + memset(t, 0, malloc_header_size + t->signature.size); +#endif - free(n); + libc_free(t); } #else @@ -184,6 +423,10 @@ void *reallocz(void *ptr, size_t size) { return p; } +void posix_memfree(void *ptr) { + free(ptr); +} + #endif // -------------------------------------------------------------------------------------------------------------------- @@ -1031,8 +1274,8 @@ void *netdata_mmap(const char *filename, size_t size, int flags, int ksm) { mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd_for_mmap, 0); if (mem != MAP_FAILED) { -#ifdef NETDATA_LOG_ALLOCATIONS - mmap_accounting(size); +#ifdef NETDATA_TRACE_ALLOCATIONS + malloc_trace_mmap(size); #endif // if we have a file open, but we didn't give it to mmap(), @@ -1059,6 +1302,13 @@ cleanup: return mem; } +int netdata_munmap(void *ptr, size_t size) { +#ifdef NETDATA_TRACE_ALLOCATIONS + malloc_trace_munmap(size); +#endif + return munmap(ptr, size); +} + int memory_file_save(const char *filename, void *mem, size_t size) { char tmpfilename[FILENAME_MAX + 1]; @@ -1535,6 +1785,122 @@ char *find_and_replace(const char *src, const char *find, const char *replace, c return value; } +inline int pluginsd_space(char c) { + switch(c) { + case ' ': + case '\t': + case '\r': + case '\n': + case '=': + return 1; + + default: + return 0; + } +} + +inline int config_isspace(char c) +{ + switch (c) { + case ' ': + case '\t': + case '\r': + case '\n': + case ',': + return 1; + + default: + return 0; + } +} + +// split a text into words, respecting quotes +inline size_t quoted_strings_splitter(char *str, char **words, size_t max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover) +{ + char *s = str, quote = 0; + size_t i = 0; + int rec = 0; + char *recover = recover_input; + + // skip all white space + while (unlikely(custom_isspace(*s))) + s++; + + // check for quote + if (unlikely(*s == '\'' || *s == '"')) { + quote = *s; // remember the quote + s++; // skip the quote + } + + // store the first word + words[i++] = s; + + // while we have something + while (likely(*s)) { + // if it is escape + if (unlikely(*s == '\\' && s[1])) { + s += 2; + continue; + } + + // if it is quote + else if (unlikely(*s == quote)) { + quote = 0; + if (recover && rec < max_recover) { + recover_location[rec++] = s; + *recover++ = *s; + } + *s = ' '; + continue; + } + + // if it is a space + else if (unlikely(quote == 0 && custom_isspace(*s))) { + // terminate the word + if (recover && rec < max_recover) { + if (!rec || recover_location[rec-1] != s) { + recover_location[rec++] = s; + *recover++ = *s; + } + } + *s++ = '\0'; + + // skip all white space + while (likely(custom_isspace(*s))) + s++; + + // check for quote + if (unlikely(*s == '\'' || *s == '"')) { + quote = *s; // remember the quote + s++; // skip the quote + } + + // if we reached the end, stop + if (unlikely(!*s)) + break; + + // store the next word + if (likely(i < max_words)) + words[i++] = s; + else + break; + } + + // anything else + else + s++; + } + + if (i < max_words) + words[i] = NULL; + + return i; +} + +inline size_t pluginsd_split_words(char *str, char **words, size_t max_words, char *recover_input, char **recover_location, int max_recover) +{ + return quoted_strings_splitter(str, words, max_words, pluginsd_space, recover_input, recover_location, max_recover); +} bool bitmap256_get_bit(BITMAP256 *ptr, uint8_t idx) { if (unlikely(!ptr)) @@ -1550,3 +1916,21 @@ void bitmap256_set_bit(BITMAP256 *ptr, uint8_t idx, bool value) { else ptr->data[idx / 64] &= ~(1ULL << (idx % 64)); } + +bool run_command_and_copy_output_to_stdout(const char *command, int max_line_length) { + pid_t pid; + FILE *fp = netdata_popen(command, &pid, NULL); + + if(fp) { + char buffer[max_line_length + 1]; + while (fgets(buffer, max_line_length, fp)) + fprintf(stdout, "%s", buffer); + } + else { + error("Failed to execute command '%s'.", command); + return false; + } + + netdata_pclose(NULL, fp, pid); + return true; +} diff --git a/libnetdata/libnetdata.h b/libnetdata/libnetdata.h index 8cc6cce9f..58eaa9ded 100644 --- a/libnetdata/libnetdata.h +++ b/libnetdata/libnetdata.h @@ -11,6 +11,15 @@ extern "C" { #include #endif +#if defined(NETDATA_DEV_MODE) && !defined(NETDATA_INTERNAL_CHECKS) +#define NETDATA_INTERNAL_CHECKS 1 +#endif + +// NETDATA_TRACE_ALLOCATIONS does not work under musl libc, so don't enable it +//#if defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_TRACE_ALLOCATIONS) +//#define NETDATA_TRACE_ALLOCATIONS 1 +//#endif + #define OS_LINUX 1 #define OS_FREEBSD 2 #define OS_MACOS 3 @@ -214,68 +223,142 @@ extern "C" { #define GUID_LEN 36 -extern void netdata_fix_chart_id(char *s); -extern void netdata_fix_chart_name(char *s); - -extern void strreverse(char* begin, char* end); -extern char *mystrsep(char **ptr, char *s); -extern char *trim(char *s); // remove leading and trailing spaces; may return NULL -extern char *trim_all(char *buffer); // like trim(), but also remove duplicate spaces inside the string; may return NULL - -extern int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args); -extern int snprintfz(char *dst, size_t n, const char *fmt, ...) PRINTFLIKE(3, 4); +// --------------------------------------------------------------------------------------------- +// double linked list management + +#define DOUBLE_LINKED_LIST_PREPEND_UNSAFE(head, item, prev, next) \ + do { \ + (item)->next = (head); \ + \ + if(likely(head)) { \ + (item)->prev = (head)->prev; \ + (head)->prev = (item); \ + } \ + else \ + (item)->prev = (item); \ + \ + (head) = (item); \ + } while (0) + +#define DOUBLE_LINKED_LIST_APPEND_UNSAFE(head, item, prev, next) \ + do { \ + if(likely(head)) { \ + (item)->prev = (head)->prev; \ + (head)->prev->next = (item); \ + (head)->prev = (item); \ + (item)->next = NULL; \ + } \ + else { \ + (head) = (item); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ + \ + } while (0) + +#define DOUBLE_LINKED_LIST_REMOVE_UNSAFE(head, item, prev, next) \ + do { \ + fatal_assert((head) != NULL); \ + fatal_assert((item)->prev != NULL); \ + \ + if((item)->prev == (item)) { \ + /* it is the only item in the list */ \ + (head) = NULL; \ + } \ + else if((item) == (head)) { \ + /* it is the first item */ \ + (item)->next->prev = (item)->prev; \ + (head) = (item)->next; \ + } \ + else { \ + (item)->prev->next = (item)->next; \ + if ((item)->next) { \ + (item)->next->prev = (item)->prev; \ + } \ + else { \ + (head)->prev = (item)->prev; \ + } \ + } \ + \ + (item)->next = NULL; \ + (item)->prev = NULL; \ + } while (0) + +#define DOUBLE_LINKED_LIST_FOREACH_FORWARD(head, var, prev, next) \ + for ((var) = (head); (var) ; (var) = (var)->next) + +#define DOUBLE_LINKED_LIST_FOREACH_BACKWARD(head, var, prev, next) \ + for ((var) = (head)?(head)->prev:NULL; (var) && (var) != (head)->prev ; (var) = (var)->prev) + +// --------------------------------------------------------------------------------------------- + + +void netdata_fix_chart_id(char *s); +void netdata_fix_chart_name(char *s); + +void strreverse(char* begin, char* end); +char *mystrsep(char **ptr, char *s); +char *trim(char *s); // remove leading and trailing spaces; may return NULL +char *trim_all(char *buffer); // like trim(), but also remove duplicate spaces inside the string; may return NULL + +int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args); +int snprintfz(char *dst, size_t n, const char *fmt, ...) PRINTFLIKE(3, 4); // memory allocation functions that handle failures -#ifdef NETDATA_LOG_ALLOCATIONS -extern __thread size_t log_thread_memory_allocations; -#define strdupz(s) strdupz_int(__FILE__, __FUNCTION__, __LINE__, s) -#define callocz(nmemb, size) callocz_int(__FILE__, __FUNCTION__, __LINE__, nmemb, size) -#define mallocz(size) mallocz_int(__FILE__, __FUNCTION__, __LINE__, size) -#define reallocz(ptr, size) reallocz_int(__FILE__, __FUNCTION__, __LINE__, ptr, size) -#define freez(ptr) freez_int(__FILE__, __FUNCTION__, __LINE__, ptr) -#define log_allocations() log_allocations_int(__FILE__, __FUNCTION__, __LINE__) - -extern char *strdupz_int(const char *file, const char *function, const unsigned long line, const char *s); -extern void *callocz_int(const char *file, const char *function, const unsigned long line, size_t nmemb, size_t size); -extern void *mallocz_int(const char *file, const char *function, const unsigned long line, size_t size); -extern void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size); -extern void freez_int(const char *file, const char *function, const unsigned long line, void *ptr); -extern void log_allocations_int(const char *file, const char *function, const unsigned long line); - -#else // NETDATA_LOG_ALLOCATIONS -extern char *strdupz(const char *s) MALLOCLIKE NEVERNULL; -extern void *callocz(size_t nmemb, size_t size) MALLOCLIKE NEVERNULL; -extern void *mallocz(size_t size) MALLOCLIKE NEVERNULL; -extern void *reallocz(void *ptr, size_t size) MALLOCLIKE NEVERNULL; -extern void freez(void *ptr); -#endif // NETDATA_LOG_ALLOCATIONS - -extern void json_escape_string(char *dst, const char *src, size_t size); -extern void json_fix_string(char *s); - -extern void *netdata_mmap(const char *filename, size_t size, int flags, int ksm); -extern int memory_file_save(const char *filename, void *mem, size_t size); - -extern int fd_is_valid(int fd); +#ifdef NETDATA_TRACE_ALLOCATIONS +int malloc_trace_walkthrough(int (*callback)(void *item, void *data), void *data); + +#define strdupz(s) strdupz_int(s, __FILE__, __FUNCTION__, __LINE__) +#define callocz(nmemb, size) callocz_int(nmemb, size, __FILE__, __FUNCTION__, __LINE__) +#define mallocz(size) mallocz_int(size, __FILE__, __FUNCTION__, __LINE__) +#define reallocz(ptr, size) reallocz_int(ptr, size, __FILE__, __FUNCTION__, __LINE__) +#define freez(ptr) freez_int(ptr, __FILE__, __FUNCTION__, __LINE__) +#define mallocz_usable_size(ptr) mallocz_usable_size_int(ptr, __FILE__, __FUNCTION__, __LINE__) + +char *strdupz_int(const char *s, const char *file, const char *function, size_t line); +void *callocz_int(size_t nmemb, size_t size, const char *file, const char *function, size_t line); +void *mallocz_int(size_t size, const char *file, const char *function, size_t line); +void *reallocz_int(void *ptr, size_t size, const char *file, const char *function, size_t line); +void freez_int(void *ptr, const char *file, const char *function, size_t line); +size_t mallocz_usable_size_int(void *ptr, const char *file, const char *function, size_t line); + +#else // NETDATA_TRACE_ALLOCATIONS +char *strdupz(const char *s) MALLOCLIKE NEVERNULL; +void *callocz(size_t nmemb, size_t size) MALLOCLIKE NEVERNULL; +void *mallocz(size_t size) MALLOCLIKE NEVERNULL; +void *reallocz(void *ptr, size_t size) MALLOCLIKE NEVERNULL; +void freez(void *ptr); +#endif // NETDATA_TRACE_ALLOCATIONS + +void posix_memfree(void *ptr); + +void json_escape_string(char *dst, const char *src, size_t size); +void json_fix_string(char *s); + +void *netdata_mmap(const char *filename, size_t size, int flags, int ksm); +int netdata_munmap(void *ptr, size_t size); +int memory_file_save(const char *filename, void *mem, size_t size); + +int fd_is_valid(int fd); extern struct rlimit rlimit_nofile; extern int enable_ksm; -extern char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len); +char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len); -extern int verify_netdata_host_prefix(); +int verify_netdata_host_prefix(); -extern int recursively_delete_dir(const char *path, const char *reason); +int recursively_delete_dir(const char *path, const char *reason); extern volatile sig_atomic_t netdata_exit; extern const char *program_version; -extern char *strdupz_path_subpath(const char *path, const char *subpath); -extern int path_is_dir(const char *path, const char *subpath); -extern int path_is_file(const char *path, const char *subpath); -extern void recursive_config_double_dir_load( +char *strdupz_path_subpath(const char *path, const char *subpath); +int path_is_dir(const char *path, const char *subpath); +int path_is_file(const char *path, const char *subpath); +void recursive_config_double_dir_load( const char *user_path , const char *stock_path , const char *subpath @@ -283,8 +366,8 @@ extern void recursive_config_double_dir_load( , void *data , size_t depth ); -extern char *read_by_filename(char *filename, long *file_size); -extern char *find_and_replace(const char *src, const char *find, const char *replace, const char *where); +char *read_by_filename(char *filename, long *file_size); +char *find_and_replace(const char *src, const char *find, const char *replace, const char *where); /* fix for alpine linux */ #ifndef RUSAGE_THREAD @@ -315,12 +398,30 @@ typedef struct bitmap256 { uint64_t data[4]; } BITMAP256; -extern bool bitmap256_get_bit(BITMAP256 *ptr, uint8_t idx); -extern void bitmap256_set_bit(BITMAP256 *ptr, uint8_t idx, bool value); +bool bitmap256_get_bit(BITMAP256 *ptr, uint8_t idx); +void bitmap256_set_bit(BITMAP256 *ptr, uint8_t idx, bool value); -extern void netdata_cleanup_and_exit(int ret) NORETURN; -extern void send_statistics(const char *action, const char *action_result, const char *action_data); +#define COMPRESSION_MAX_MSG_SIZE 0x4000 +#define PLUGINSD_LINE_MAX (COMPRESSION_MAX_MSG_SIZE - 1024) +int config_isspace(char c); +int pluginsd_space(char c); + +size_t quoted_strings_splitter(char *str, char **words, size_t max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover); +size_t pluginsd_split_words(char *str, char **words, size_t max_words, char *recover_string, char **recover_location, int max_recover); + +static inline char *get_word(char **words, size_t num_words, size_t index) { + if (index >= num_words) + return NULL; + + return words[index]; +} + +bool run_command_and_copy_output_to_stdout(const char *command, int max_line_length); + +void netdata_cleanup_and_exit(int ret) NORETURN; +void send_statistics(const char *action, const char *action_result, const char *action_data); extern char *netdata_configured_host_prefix; +#include "libjudy/src/Judy.h" #include "os.h" #include "storage_number/storage_number.h" #include "threads/threads.h" @@ -340,6 +441,7 @@ extern char *netdata_configured_host_prefix; #include "config/appconfig.h" #include "log/log.h" #include "procfile/procfile.h" +#include "string/string.h" #include "dictionary/dictionary.h" #if defined(HAVE_LIBBPF) && !defined(__cplusplus) #include "ebpf/ebpf.h" @@ -356,7 +458,8 @@ extern char *netdata_configured_host_prefix; #include "worker_utilization/worker_utilization.h" // BEWARE: Outside of the C code this also exists in alarm-notify.sh -#define DEFAULT_CLOUD_BASE_URL "https://app.netdata.cloud" +#define DEFAULT_CLOUD_BASE_URL "https://api.netdata.cloud" +#define DEFAULT_CLOUD_UI_URL "https://app.netdata.cloud" #define RRD_STORAGE_TIERS 5 @@ -370,6 +473,33 @@ static inline size_t struct_natural_alignment(size_t size) { return size; } +#ifdef NETDATA_TRACE_ALLOCATIONS +struct malloc_trace { + avl_t avl; + + const char *function; + const char *file; + size_t line; + + size_t malloc_calls; + size_t calloc_calls; + size_t realloc_calls; + size_t strdup_calls; + size_t free_calls; + + size_t mmap_calls; + size_t munmap_calls; + + size_t allocations; + size_t bytes; + + struct rrddim *rd_bytes; + struct rrddim *rd_allocations; + struct rrddim *rd_avg_alloc; + struct rrddim *rd_ops; +}; +#endif // NETDATA_TRACE_ALLOCATIONS + # ifdef __cplusplus } # endif diff --git a/libnetdata/locks/README.md b/libnetdata/locks/README.md index 9ac96a8f6..9132edc43 100644 --- a/libnetdata/locks/README.md +++ b/libnetdata/locks/README.md @@ -49,7 +49,7 @@ The library maintains a linked-list of all the lock holders (one entry per threa If any call is expected to pause the caller (ie the caller is attempting a read lock while there is a write lock in place and vice versa), the library will log something like this: ``` -RW_LOCK ON LOCK 0x0x5651c9fcce20: 4190039 'HEALTH' (function init_pending_foreach_alarms() 661@health/health.c) WANTS a 'W' lock (while holding 1 rwlocks and 1 mutexes). +RW_LOCK ON LOCK 0x0x5651c9fcce20: 4190039 'HEALTH' (function health_execute_pending_updates() 661@health/health.c) WANTS a 'W' lock (while holding 1 rwlocks and 1 mutexes). There are 7 readers and 0 writers are holding the lock: => 1: RW_LOCK: process 4190091 'WEB_SERVER[static14]' (function web_client_api_request_v1_data() 526@web/api/web_api_v1.c) is having 1 'R' lock for 709847 usec. => 2: RW_LOCK: process 4190079 'WEB_SERVER[static6]' (function web_client_api_request_v1_data() 526@web/api/web_api_v1.c) is having 1 'R' lock for 709869 usec. @@ -63,9 +63,9 @@ There are 7 readers and 0 writers are holding the lock: And each of the above is paired with a `GOT` log, like this: ``` -RW_LOCK ON LOCK 0x0x5651c9fcce20: 4190039 'HEALTH' (function init_pending_foreach_alarms() 661@health/health.c) GOT a 'W' lock (while holding 2 rwlocks and 1 mutexes). +RW_LOCK ON LOCK 0x0x5651c9fcce20: 4190039 'HEALTH' (function health_execute_pending_updates() 661@health/health.c) GOT a 'W' lock (while holding 2 rwlocks and 1 mutexes). There are 0 readers and 1 writers are holding the lock: - => 1: RW_LOCK: process 4190039 'HEALTH' (function init_pending_foreach_alarms() 661@health/health.c) is having 1 'W' lock for 36 usec. + => 1: RW_LOCK: process 4190039 'HEALTH' (function health_execute_pending_updates() 661@health/health.c) is having 1 'W' lock for 36 usec. ``` Keep in mind that the lock and log are not atomic. The list of callers is indicative (and sometimes just empty because the original holders of the lock, unlocked it until we had the chance to print their names). diff --git a/libnetdata/locks/locks.c b/libnetdata/locks/locks.c index 8b5348678..f7191be52 100644 --- a/libnetdata/locks/locks.c +++ b/libnetdata/locks/locks.c @@ -278,6 +278,37 @@ int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock) { return ret; } +// ---------------------------------------------------------------------------- +// spinlock implementation +// https://www.youtube.com/watch?v=rmGJc9PXpuE&t=41s + +void netdata_spinlock_init(SPINLOCK *spinlock) { + *spinlock = NETDATA_SPINLOCK_INITIALIZER; +} + +void netdata_spinlock_lock(SPINLOCK *spinlock) { + static const struct timespec ns = { .tv_sec = 0, .tv_nsec = 1 }; + + netdata_thread_disable_cancelability(); + + for(int i = 1; + __atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) || + __atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE) + ; i++ + ) { + if(unlikely(i == 8)) { + i = 0; + nanosleep(&ns, NULL); + } + } + // we have the lock +} + +void netdata_spinlock_unlock(SPINLOCK *spinlock) { + __atomic_clear(&spinlock->locked, __ATOMIC_RELEASE); + netdata_thread_enable_cancelability(); +} + #ifdef NETDATA_TRACE_RWLOCKS // ---------------------------------------------------------------------------- diff --git a/libnetdata/locks/locks.h b/libnetdata/locks/locks.h index 796b53c6d..4d2d1655c 100644 --- a/libnetdata/locks/locks.h +++ b/libnetdata/locks/locks.h @@ -9,6 +9,14 @@ typedef pthread_mutex_t netdata_mutex_t; #define NETDATA_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +typedef struct netdata_spinlock { + bool locked; +} SPINLOCK; +#define NETDATA_SPINLOCK_INITIALIZER (SPINLOCK){ .locked = false } +void netdata_spinlock_init(SPINLOCK *spinlock); +void netdata_spinlock_lock(SPINLOCK *spinlock); +void netdata_spinlock_unlock(SPINLOCK *spinlock); + #ifdef NETDATA_TRACE_RWLOCKS typedef struct netdata_rwlock_locker { pid_t pid; @@ -51,38 +59,38 @@ typedef struct netdata_rwlock_t { #endif // NETDATA_TRACE_RWLOCKS -extern int __netdata_mutex_init(netdata_mutex_t *mutex); -extern int __netdata_mutex_destroy(netdata_mutex_t *mutex); -extern int __netdata_mutex_lock(netdata_mutex_t *mutex); -extern int __netdata_mutex_trylock(netdata_mutex_t *mutex); -extern int __netdata_mutex_unlock(netdata_mutex_t *mutex); +int __netdata_mutex_init(netdata_mutex_t *mutex); +int __netdata_mutex_destroy(netdata_mutex_t *mutex); +int __netdata_mutex_lock(netdata_mutex_t *mutex); +int __netdata_mutex_trylock(netdata_mutex_t *mutex); +int __netdata_mutex_unlock(netdata_mutex_t *mutex); -extern int __netdata_rwlock_destroy(netdata_rwlock_t *rwlock); -extern int __netdata_rwlock_init(netdata_rwlock_t *rwlock); -extern int __netdata_rwlock_rdlock(netdata_rwlock_t *rwlock); -extern int __netdata_rwlock_wrlock(netdata_rwlock_t *rwlock); -extern int __netdata_rwlock_unlock(netdata_rwlock_t *rwlock); -extern int __netdata_rwlock_tryrdlock(netdata_rwlock_t *rwlock); -extern int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock); +int __netdata_rwlock_destroy(netdata_rwlock_t *rwlock); +int __netdata_rwlock_init(netdata_rwlock_t *rwlock); +int __netdata_rwlock_rdlock(netdata_rwlock_t *rwlock); +int __netdata_rwlock_wrlock(netdata_rwlock_t *rwlock); +int __netdata_rwlock_unlock(netdata_rwlock_t *rwlock); +int __netdata_rwlock_tryrdlock(netdata_rwlock_t *rwlock); +int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock); -extern void netdata_thread_disable_cancelability(void); -extern void netdata_thread_enable_cancelability(void); +void netdata_thread_disable_cancelability(void); +void netdata_thread_enable_cancelability(void); #ifdef NETDATA_TRACE_RWLOCKS -extern int netdata_mutex_init_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); -extern int netdata_mutex_destroy_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); -extern int netdata_mutex_lock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); -extern int netdata_mutex_trylock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); -extern int netdata_mutex_unlock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); - -extern int netdata_rwlock_destroy_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); -extern int netdata_rwlock_init_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); -extern int netdata_rwlock_rdlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); -extern int netdata_rwlock_wrlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); -extern int netdata_rwlock_unlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); -extern int netdata_rwlock_tryrdlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); -extern int netdata_rwlock_trywrlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); +int netdata_mutex_init_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); +int netdata_mutex_destroy_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); +int netdata_mutex_lock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); +int netdata_mutex_trylock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); +int netdata_mutex_unlock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex); + +int netdata_rwlock_destroy_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); +int netdata_rwlock_init_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); +int netdata_rwlock_rdlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); +int netdata_rwlock_wrlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); +int netdata_rwlock_unlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); +int netdata_rwlock_tryrdlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); +int netdata_rwlock_trywrlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock); #define netdata_mutex_init(mutex) netdata_mutex_init_debug(__FILE__, __FUNCTION__, __LINE__, mutex) #define netdata_mutex_destroy(mutex) netdata_mutex_init_debug(__FILE__, __FUNCTION__, __LINE__, mutex) diff --git a/libnetdata/log/log.c b/libnetdata/log/log.c index d6793b69b..fb3b2d034 100644 --- a/libnetdata/log/log.c +++ b/libnetdata/log/log.c @@ -3,6 +3,10 @@ #include #include "../libnetdata.h" +#ifdef HAVE_BACKTRACE +#include +#endif + int web_server_is_multithreaded = 1; const char *program_name = ""; @@ -11,14 +15,19 @@ uint64_t debug_flags = 0; int access_log_syslog = 1; int error_log_syslog = 1; int output_log_syslog = 1; // debug log +int health_log_syslog = 1; int stdaccess_fd = -1; FILE *stdaccess = NULL; +int stdhealth_fd = -1; +FILE *stdhealth = NULL; + const char *stdaccess_filename = NULL; const char *stderr_filename = NULL; const char *stdout_filename = NULL; const char *facility_log = NULL; +const char *stdhealth_filename = NULL; #ifdef ENABLE_ACLK const char *aclklog_filename = NULL; @@ -451,16 +460,13 @@ void syslog_init() { } } -#define LOG_DATE_LENGTH 26 - -static inline void log_date(char *buffer, size_t len) { +void log_date(char *buffer, size_t len, time_t now) { if(unlikely(!buffer || !len)) return; - time_t t; + time_t t = now; struct tm *tmp, tmbuf; - t = now_realtime_sec(); tmp = localtime_r(&t, &tmbuf); if (tmp == NULL) { @@ -576,7 +582,10 @@ void reopen_all_log_files() { #endif if(stdaccess_filename) - stdaccess = open_log_file(stdaccess_fd, stdaccess, stdaccess_filename, &access_log_syslog, 1, &stdaccess_fd); + stdaccess = open_log_file(stdaccess_fd, stdaccess, stdaccess_filename, &access_log_syslog, 1, &stdaccess_fd); + + if(stdhealth_filename) + stdhealth = open_log_file(stdhealth_fd, stdhealth, stdhealth_filename, &health_log_syslog, 1, &stdhealth_fd); } void open_all_log_files() { @@ -592,6 +601,8 @@ void open_all_log_files() { #endif stdaccess = open_log_file(stdaccess_fd, stdaccess, stdaccess_filename, &access_log_syslog, 1, &stdaccess_fd); + + stdhealth = open_log_file(stdhealth_fd, stdhealth, stdhealth_filename, &health_log_syslog, 1, &stdhealth_fd); } // ---------------------------------------------------------------------------- @@ -625,7 +636,7 @@ int error_log_limit(int reset) { if(reset) { if(prevented) { char date[LOG_DATE_LENGTH]; - log_date(date, LOG_DATE_LENGTH); + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); fprintf( stderr, "%s: %s LOG FLOOD PROTECTION reset for process '%s' " @@ -648,7 +659,7 @@ int error_log_limit(int reset) { if(now - start > error_log_throttle_period) { if(prevented) { char date[LOG_DATE_LENGTH]; - log_date(date, LOG_DATE_LENGTH); + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); fprintf( stderr, "%s: %s LOG FLOOD PROTECTION resuming logging from process '%s' " @@ -672,7 +683,7 @@ int error_log_limit(int reset) { if(counter > error_log_errors_per_period) { if(!prevented) { char date[LOG_DATE_LENGTH]; - log_date(date, LOG_DATE_LENGTH); + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); fprintf( stderr, "%s: %s LOG FLOOD PROTECTION too many logs (%lu logs in %"PRId64" seconds, threshold is set to %lu logs " @@ -727,7 +738,7 @@ void debug_int( const char *file, const char *function, const unsigned long line va_list args; char date[LOG_DATE_LENGTH]; - log_date(date, LOG_DATE_LENGTH); + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); va_start( args, fmt ); printf("%s: %s DEBUG : %s : (%04lu@%-20.20s:%-15.15s): ", date, program_name, netdata_thread_tag(), line, file, function); @@ -747,7 +758,7 @@ void debug_int( const char *file, const char *function, const unsigned long line // ---------------------------------------------------------------------------- // info log -void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) +void info_int( const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) { va_list args; @@ -766,7 +777,7 @@ void info_int( const char *file, const char *function, const unsigned long line, } char date[LOG_DATE_LENGTH]; - log_date(date, LOG_DATE_LENGTH); + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); va_start( args, fmt ); #ifdef NETDATA_INTERNAL_CHECKS @@ -807,7 +818,10 @@ static const char *strerror_result_string(const char *a, const char *b) { (void) #error "cannot detect the format of function strerror_r()" #endif -void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) { +void error_limit_int(ERROR_LIMIT *erl, const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) { + if(erl->sleep_ut) + sleep_usec(erl->sleep_ut); + // save a copy of errno - just in case this function generates a new error int __errno = errno; @@ -815,6 +829,13 @@ void error_int( const char *prefix, const char *file, const char *function, cons log_lock(); + erl->count++; + time_t now = now_boottime_sec(); + if(now - erl->last_logged < erl->log_every) { + log_unlock(); + return; + } + // prevent logging too much if (error_log_limit(0)) { log_unlock(); @@ -828,7 +849,7 @@ void error_int( const char *prefix, const char *file, const char *function, cons } char date[LOG_DATE_LENGTH]; - log_date(date, LOG_DATE_LENGTH); + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); va_start( args, fmt ); #ifdef NETDATA_INTERNAL_CHECKS @@ -839,6 +860,12 @@ void error_int( const char *prefix, const char *file, const char *function, cons vfprintf( stderr, fmt, args ); va_end( args ); + if(erl->count > 1) + fprintf(stderr, " (similar messages repeated %zu times in the last %llu secs)", erl->count, (unsigned long long)(erl->last_logged ? now - erl->last_logged : 0)); + + if(erl->sleep_ut) + fprintf(stderr, " (sleeping for %llu microseconds every time this happens)", erl->sleep_ut); + if(__errno) { char buf[1024]; fprintf(stderr, " (errno %d, %s)\n", __errno, strerror_result(strerror_r(__errno, buf, 1023), buf)); @@ -847,9 +874,74 @@ void error_int( const char *prefix, const char *file, const char *function, cons else fputc('\n', stderr); + erl->last_logged = now; + erl->count = 0; + log_unlock(); } +void error_int(const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) { + // save a copy of errno - just in case this function generates a new error + int __errno = errno; + + va_list args; + + log_lock(); + + // prevent logging too much + if (error_log_limit(0)) { + log_unlock(); + return; + } + + if(error_log_syslog) { + va_start( args, fmt ); + vsyslog(LOG_ERR, fmt, args ); + va_end( args ); + } + + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); + + va_start( args, fmt ); +#ifdef NETDATA_INTERNAL_CHECKS + fprintf(stderr, "%s: %s %-5.5s : %s : (%04lu@%-20.20s:%-15.15s): ", date, program_name, prefix, netdata_thread_tag(), line, file, function); +#else + fprintf(stderr, "%s: %s %-5.5s : %s : ", date, program_name, prefix, netdata_thread_tag()); +#endif + vfprintf( stderr, fmt, args ); + va_end( args ); + + if(__errno) { + char buf[1024]; + fprintf(stderr, " (errno %d, %s)\n", __errno, strerror_result(strerror_r(__errno, buf, 1023), buf)); + errno = 0; + } + else + fputc('\n', stderr); + + log_unlock(); +} + +#ifdef NETDATA_INTERNAL_CHECKS +static void crash_netdata(void) { + // make Netdata core dump + abort(); +} +#endif + +#ifdef HAVE_BACKTRACE +#define BT_BUF_SIZE 100 +static void print_call_stack(void) { + int nptrs; + void *buffer[BT_BUF_SIZE]; + + nptrs = backtrace(buffer, BT_BUF_SIZE); + if(nptrs) + backtrace_symbols_fd(buffer, nptrs, fileno(stderr)); +} +#endif + void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) { // save a copy of errno - just in case this function generates a new error int __errno = errno; @@ -872,7 +964,7 @@ void fatal_int( const char *file, const char *function, const unsigned long line } char date[LOG_DATE_LENGTH]; - log_date(date, LOG_DATE_LENGTH); + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); log_lock(); @@ -897,6 +989,14 @@ void fatal_int( const char *file, const char *function, const unsigned long line snprintfz(action_result, 60, "%s:%s", program_name, strncmp(thread_tag, "STREAM_RECEIVER", strlen("STREAM_RECEIVER")) ? thread_tag : "[x]"); send_statistics("FATAL", action_result, action_data); +#ifdef HAVE_BACKTRACE + print_call_stack(); +#endif + +#ifdef NETDATA_INTERNAL_CHECKS + crash_netdata(); +#endif + netdata_cleanup_and_exit(1); } @@ -919,7 +1019,7 @@ void log_access( const char *fmt, ... ) { netdata_mutex_lock(&access_mutex); char date[LOG_DATE_LENGTH]; - log_date(date, LOG_DATE_LENGTH); + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); fprintf(stdaccess, "%s: ", date); va_start( args, fmt ); @@ -932,6 +1032,38 @@ void log_access( const char *fmt, ... ) { } } +// ---------------------------------------------------------------------------- +// health log + +void log_health( const char *fmt, ... ) { + va_list args; + + if(health_log_syslog) { + va_start( args, fmt ); + vsyslog(LOG_INFO, fmt, args ); + va_end( args ); + } + + if(stdhealth) { + static netdata_mutex_t health_mutex = NETDATA_MUTEX_INITIALIZER; + + if(web_server_is_multithreaded) + netdata_mutex_lock(&health_mutex); + + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); + fprintf(stdhealth, "%s: ", date); + + va_start( args, fmt ); + vfprintf( stdhealth, fmt, args ); + va_end( args ); + fputc('\n', stdhealth); + + if(web_server_is_multithreaded) + netdata_mutex_unlock(&health_mutex); + } +} + #ifdef ENABLE_ACLK void log_aclk_message_bin( const char *data, const size_t data_len, int tx, const char *mqtt_topic, const char *message_name) { if (aclklog) { @@ -939,7 +1071,7 @@ void log_aclk_message_bin( const char *data, const size_t data_len, int tx, cons netdata_mutex_lock(&aclklog_mutex); char date[LOG_DATE_LENGTH]; - log_date(date, LOG_DATE_LENGTH); + log_date(date, LOG_DATE_LENGTH, now_realtime_sec()); fprintf(aclklog, "%s: %s Msg:\"%s\", MQTT-topic:\"%s\": ", date, tx ? "OUTGOING" : "INCOMING", message_name, mqtt_topic); fwrite(data, data_len, 1, aclklog); diff --git a/libnetdata/log/log.h b/libnetdata/log/log.h index ae86720cb..11dab4c1d 100644 --- a/libnetdata/log/log.h +++ b/libnetdata/log/log.h @@ -45,6 +45,8 @@ extern "C" { #define D_ACLK 0x0000000200000000 #define D_METADATALOG 0x0000000400000000 #define D_ACLK_SYNC 0x0000000800000000 +#define D_META_SYNC 0x0000001000000000 +#define D_REPLICATION 0x0000002000000000 #define D_SYSTEM 0x8000000000000000 extern int web_server_is_multithreaded; @@ -56,9 +58,13 @@ extern const char *program_name; extern int stdaccess_fd; extern FILE *stdaccess; +extern int stdhealth_fd; +extern FILE *stdhealth; + extern const char *stdaccess_filename; extern const char *stderr_filename; extern const char *stdout_filename; +extern const char *stdhealth_filename; extern const char *facility_log; #ifdef ENABLE_ACLK @@ -71,42 +77,61 @@ extern int aclklog_enabled; extern int access_log_syslog; extern int error_log_syslog; extern int output_log_syslog; +extern int health_log_syslog; extern time_t error_log_throttle_period; extern unsigned long error_log_errors_per_period, error_log_errors_per_period_backup; -extern int error_log_limit(int reset); +int error_log_limit(int reset); + +void open_all_log_files(); +void reopen_all_log_files(); -extern void open_all_log_files(); -extern void reopen_all_log_files(); +#define LOG_DATE_LENGTH 26 +void log_date(char *buffer, size_t len, time_t now); static inline void debug_dummy(void) {} void error_log_limit_reset(void); void error_log_limit_unlimited(void); +typedef struct error_with_limit { + time_t log_every; + size_t count; + time_t last_logged; + usec_t sleep_ut; +} ERROR_LIMIT; + +#define error_limit_static_global_var(var, log_every_secs, sleep_usecs) static ERROR_LIMIT var = { .last_logged = 0, .count = 0, .log_every = (log_every_secs), .sleep_ut = (sleep_usecs) } +#define error_limit_static_thread_var(var, log_every_secs, sleep_usecs) static __thread ERROR_LIMIT var = { .last_logged = 0, .count = 0, .log_every = (log_every_secs), .sleep_ut = (sleep_usecs) } + #ifdef NETDATA_INTERNAL_CHECKS #define debug(type, args...) do { if(unlikely(debug_flags & type)) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0) #define internal_error(condition, args...) do { if(unlikely(condition)) error_int("IERR", __FILE__, __FUNCTION__, __LINE__, ##args); } while(0) +#define internal_fatal(condition, args...) do { if(unlikely(condition)) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0) #else #define debug(type, args...) debug_dummy() #define internal_error(args...) debug_dummy() +#define internal_fatal(args...) debug_dummy() #endif #define info(args...) info_int(__FILE__, __FUNCTION__, __LINE__, ##args) #define infoerr(args...) error_int("INFO", __FILE__, __FUNCTION__, __LINE__, ##args) #define error(args...) error_int("ERROR", __FILE__, __FUNCTION__, __LINE__, ##args) +#define error_limit(erl, args...) error_limit_int(erl, "ERROR", __FILE__, __FUNCTION__, __LINE__, ##args) #define fatal(args...) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args) #define fatal_assert(expr) ((expr) ? (void)(0) : fatal_int(__FILE__, __FUNCTION__, __LINE__, "Assertion `%s' failed", #expr)) -extern void send_statistics(const char *action, const char *action_result, const char *action_data); -extern void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5); -extern void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5); -extern void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(5, 6); -extern void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) NORETURN PRINTFLIKE(4, 5); -extern void log_access( const char *fmt, ... ) PRINTFLIKE(1, 2); +void send_statistics(const char *action, const char *action_result, const char *action_data); +void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5); +void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5); +void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(5, 6); +void error_limit_int(ERROR_LIMIT *erl, const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, unsigned long line __maybe_unused, const char *fmt, ... ) PRINTFLIKE(6, 7);; +void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) NORETURN PRINTFLIKE(4, 5); +void log_access( const char *fmt, ... ) PRINTFLIKE(1, 2); +void log_health( const char *fmt, ... ) PRINTFLIKE(1, 2); #ifdef ENABLE_ACLK -extern void log_aclk_message_bin( const char *data, const size_t data_len, int tx, const char *mqtt_topic, const char *message_name); +void log_aclk_message_bin( const char *data, const size_t data_len, int tx, const char *mqtt_topic, const char *message_name); #endif # ifdef __cplusplus diff --git a/libnetdata/onewayalloc/onewayalloc.h b/libnetdata/onewayalloc/onewayalloc.h index 9eb908bfb..e536e0542 100644 --- a/libnetdata/onewayalloc/onewayalloc.h +++ b/libnetdata/onewayalloc/onewayalloc.h @@ -5,15 +5,15 @@ typedef void ONEWAYALLOC; -extern ONEWAYALLOC *onewayalloc_create(size_t size_hint); -extern void onewayalloc_destroy(ONEWAYALLOC *owa); +ONEWAYALLOC *onewayalloc_create(size_t size_hint); +void onewayalloc_destroy(ONEWAYALLOC *owa); -extern void *onewayalloc_mallocz(ONEWAYALLOC *owa, size_t size); -extern void *onewayalloc_callocz(ONEWAYALLOC *owa, size_t nmemb, size_t size); -extern char *onewayalloc_strdupz(ONEWAYALLOC *owa, const char *s); -extern void *onewayalloc_memdupz(ONEWAYALLOC *owa, const void *src, size_t size); -extern void onewayalloc_freez(ONEWAYALLOC *owa, const void *ptr); +void *onewayalloc_mallocz(ONEWAYALLOC *owa, size_t size); +void *onewayalloc_callocz(ONEWAYALLOC *owa, size_t nmemb, size_t size); +char *onewayalloc_strdupz(ONEWAYALLOC *owa, const char *s); +void *onewayalloc_memdupz(ONEWAYALLOC *owa, const void *src, size_t size); +void onewayalloc_freez(ONEWAYALLOC *owa, const void *ptr); -extern void *onewayalloc_doublesize(ONEWAYALLOC *owa, const void *src, size_t oldsize); +void *onewayalloc_doublesize(ONEWAYALLOC *owa, const void *src, size_t oldsize); #endif // ONEWAYALLOC_H diff --git a/libnetdata/os.h b/libnetdata/os.h index 5e7746df8..67abf0be4 100644 --- a/libnetdata/os.h +++ b/libnetdata/os.h @@ -13,21 +13,21 @@ #include #define GETSYSCTL_BY_NAME(name, var) getsysctl_by_name(name, &(var), sizeof(var)) -extern int getsysctl_by_name(const char *name, void *ptr, size_t len); +int getsysctl_by_name(const char *name, void *ptr, size_t len); #define GETSYSCTL_MIB(name, mib) getsysctl_mib(name, mib, sizeof(mib)/sizeof(int)) -extern int getsysctl_mib(const char *name, int *mib, size_t len); +int getsysctl_mib(const char *name, int *mib, size_t len); #define GETSYSCTL_SIMPLE(name, mib, var) getsysctl_simple(name, mib, sizeof(mib)/sizeof(int), &(var), sizeof(var)) #define GETSYSCTL_WSIZE(name, mib, var, size) getsysctl_simple(name, mib, sizeof(mib)/sizeof(int), var, size) -extern int getsysctl_simple(const char *name, int *mib, size_t miblen, void *ptr, size_t len); +int getsysctl_simple(const char *name, int *mib, size_t miblen, void *ptr, size_t len); #define GETSYSCTL_SIZE(name, mib, size) getsysctl(name, mib, sizeof(mib)/sizeof(int), NULL, &(size)) #define GETSYSCTL(name, mib, var, size) getsysctl(name, mib, sizeof(mib)/sizeof(int), &(var), &(size)) -extern int getsysctl(const char *name, int *mib, size_t miblen, void *ptr, size_t *len); +int getsysctl(const char *name, int *mib, size_t miblen, void *ptr, size_t *len); #endif @@ -39,7 +39,7 @@ extern int getsysctl(const char *name, int *mib, size_t miblen, void *ptr, size_ #include #define GETSYSCTL_BY_NAME(name, var) getsysctl_by_name(name, &(var), sizeof(var)) -extern int getsysctl_by_name(const char *name, void *ptr, size_t len); +int getsysctl_by_name(const char *name, void *ptr, size_t len); #endif @@ -49,13 +49,13 @@ extern int getsysctl_by_name(const char *name, void *ptr, size_t len); extern const char *os_type; extern int processors; -extern long get_system_cpus(void); +long get_system_cpus(void); extern pid_t pid_max; -extern pid_t get_system_pid_max(void); +pid_t get_system_pid_max(void); extern unsigned int system_hz; -extern void get_system_HZ(void); +void get_system_HZ(void); #include #if defined(__FreeBSD__) || defined(__APPLE__) diff --git a/libnetdata/popen/popen.c b/libnetdata/popen/popen.c index eaeffd32d..57f957f63 100644 --- a/libnetdata/popen/popen.c +++ b/libnetdata/popen/popen.c @@ -2,81 +2,129 @@ #include "../libnetdata.h" -static pthread_mutex_t myp_lock; -static int myp_tracking = 0; +// ---------------------------------------------------------------------------- +// popen with tracking -struct mypopen { +static pthread_mutex_t netdata_popen_tracking_mutex; +static bool netdata_popen_tracking_enabled = false; + +struct netdata_popen { pid_t pid; - struct mypopen *next; - struct mypopen *prev; + struct netdata_popen *next; + struct netdata_popen *prev; }; -static struct mypopen *mypopen_root = NULL; +static struct netdata_popen *netdata_popen_root = NULL; // myp_add_lock takes the lock if we're tracking. -static void myp_add_lock(void) { - if (myp_tracking == 0) +static void netdata_popen_tracking_lock(void) { + if(!netdata_popen_tracking_enabled) return; - netdata_mutex_lock(&myp_lock); + netdata_mutex_lock(&netdata_popen_tracking_mutex); } // myp_add_unlock release the lock if we're tracking. -static void myp_add_unlock(void) { - if (myp_tracking == 0) +static void netdata_popen_tracking_unlock(void) { + if(!netdata_popen_tracking_enabled) return; - netdata_mutex_unlock(&myp_lock); + netdata_mutex_unlock(&netdata_popen_tracking_mutex); } // myp_add_locked adds pid if we're tracking. // myp_add_lock must have been called previously. -static void myp_add_locked(pid_t pid) { - struct mypopen *mp; - - if (myp_tracking == 0) +static void netdata_popen_tracking_add_pid_unsafe(pid_t pid) { + if(!netdata_popen_tracking_enabled) return; - mp = mallocz(sizeof(struct mypopen)); + struct netdata_popen *mp; + + mp = mallocz(sizeof(struct netdata_popen)); mp->pid = pid; - mp->next = mypopen_root; - mp->prev = NULL; - if (mypopen_root != NULL) - mypopen_root->prev = mp; - mypopen_root = mp; - netdata_mutex_unlock(&myp_lock); + DOUBLE_LINKED_LIST_PREPEND_UNSAFE(netdata_popen_root, mp, prev, next); } // myp_del deletes pid if we're tracking. -static void myp_del(pid_t pid) { - struct mypopen *mp; - - if (myp_tracking == 0) +static void netdata_popen_tracking_del_pid(pid_t pid) { + if(!netdata_popen_tracking_enabled) return; - netdata_mutex_lock(&myp_lock); - for (mp = mypopen_root; mp != NULL; mp = mp->next) { - if (mp->pid == pid) { - if (mp->next != NULL) - mp->next->prev = mp->prev; - if (mp->prev != NULL) - mp->prev->next = mp->next; - if (mypopen_root == mp) - mypopen_root = mp->next; - freez(mp); + struct netdata_popen *mp; + + netdata_mutex_lock(&netdata_popen_tracking_mutex); + + DOUBLE_LINKED_LIST_FOREACH_FORWARD(netdata_popen_root, mp, prev, next) { + if(unlikely(mp->pid == pid)) break; - } } - if (mp == NULL) + if(mp) { + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(netdata_popen_root, mp, prev, next); + freez(mp); + } + else error("Cannot find pid %d.", pid); - netdata_mutex_unlock(&myp_lock); + netdata_mutex_unlock(&netdata_popen_tracking_mutex); } -#define PIPE_READ 0 -#define PIPE_WRITE 1 +// netdata_popen_tracking_init() should be called by apps which act as init +// (pid 1) so that processes created by mypopen and mypopene +// are tracked. This enables the reaper to ignore processes +// which will be handled internally, by calling myp_reap, to +// avoid issues with already reaped processes during wait calls. +// +// Callers should call myp_free() to clean up resources. +void netdata_popen_tracking_init(void) { + info("process tracking enabled."); + netdata_popen_tracking_enabled = true; + + if (netdata_mutex_init(&netdata_popen_tracking_mutex) != 0) + fatal("netdata_popen_tracking_init() mutex init failed."); +} + +// myp_free cleans up any resources allocated for process +// tracking. +void netdata_popen_tracking_cleanup(void) { + if(!netdata_popen_tracking_enabled) + return; + + netdata_mutex_lock(&netdata_popen_tracking_mutex); + netdata_popen_tracking_enabled = false; + + while(netdata_popen_root) { + struct netdata_popen *mp = netdata_popen_root; + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(netdata_popen_root, mp, prev, next); + freez(mp); + } + + netdata_mutex_unlock(&netdata_popen_tracking_mutex); +} + +// myp_reap returns 1 if pid should be reaped, 0 otherwise. +int netdata_popen_tracking_pid_shoud_be_reaped(pid_t pid) { + if(!netdata_popen_tracking_enabled) + return 0; + + netdata_mutex_lock(&netdata_popen_tracking_mutex); + + int ret = 1; + struct netdata_popen *mp; + DOUBLE_LINKED_LIST_FOREACH_FORWARD(netdata_popen_root, mp, prev, next) { + if(unlikely(mp->pid == pid)) { + ret = 0; + break; + } + } + + netdata_mutex_unlock(&netdata_popen_tracking_mutex); + return ret; +} + +// ---------------------------------------------------------------------------- +// helpers static inline void convert_argv_to_string(char *dst, size_t size, const char *spawn_argv[]) { int i; @@ -89,118 +137,191 @@ static inline void convert_argv_to_string(char *dst, size_t size, const char *sp } } +// ---------------------------------------------------------------------------- +// the core of netdata popen + /* * Returns -1 on failure, 0 on success. When POPEN_FLAG_CREATE_PIPE is set, on success set the FILE *fp pointer. */ -static int custom_popene(volatile pid_t *pidptr, char **env, uint8_t flags, FILE **fpp, const char *command, const char *spawn_argv[]) { +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +static int popene_internal(volatile pid_t *pidptr, char **env, uint8_t flags, FILE **fpp_child_stdin, FILE **fpp_child_stdout, const char *command, const char *spawn_argv[]) { // create a string to be logged about the command we are running char command_to_be_logged[2048]; convert_argv_to_string(command_to_be_logged, sizeof(command_to_be_logged), spawn_argv); // info("custom_popene() running command: %s", command_to_be_logged); - FILE *fp = NULL; - int ret = 0; // success by default - int pipefd[2], error; + int ret = 0; // success by default + int attr_rc = 1; // failure by default + + FILE *fp_child_stdin = NULL, *fp_child_stdout = NULL; + int pipefd_stdin[2] = { -1, -1 }; + int pipefd_stdout[2] = { -1, -1 }; + pid_t pid; posix_spawnattr_t attr; posix_spawn_file_actions_t fa; - if (flags & POPEN_FLAG_CREATE_PIPE) { - if (pipe(pipefd) == -1) - return -1; - if ((fp = fdopen(pipefd[PIPE_READ], "r")) == NULL) { - goto error_after_pipe; + int stdin_fd_to_exclude_from_closing = -1; + int stdout_fd_to_exclude_from_closing = -1; + + if(posix_spawn_file_actions_init(&fa)) { + error("POPEN: posix_spawn_file_actions_init() failed."); + ret = -1; + goto set_return_values_and_return; + } + + if(fpp_child_stdin) { + if (pipe(pipefd_stdin) == -1) { + error("POPEN: stdin pipe() failed"); + ret = -1; + goto cleanup_and_return; + } + + if ((fp_child_stdin = fdopen(pipefd_stdin[PIPE_WRITE], "w")) == NULL) { + error("POPEN: fdopen() stdin failed"); + ret = -1; + goto cleanup_and_return; + } + + if(posix_spawn_file_actions_adddup2(&fa, pipefd_stdin[PIPE_READ], STDIN_FILENO)) { + error("POPEN: posix_spawn_file_actions_adddup2() on stdin failed."); + ret = -1; + goto cleanup_and_return; + } + } + else { + if (posix_spawn_file_actions_addopen(&fa, STDIN_FILENO, "/dev/null", O_RDONLY, 0)) { + error("POPEN: posix_spawn_file_actions_addopen() on stdin to /dev/null failed."); + // this is not a fatal error + stdin_fd_to_exclude_from_closing = STDIN_FILENO; } } - if (flags & POPEN_FLAG_CLOSE_FD) { - // Mark all files to be closed by the exec() stage of posix_spawn() - int i; - for (i = (int) (sysconf(_SC_OPEN_MAX) - 1); i >= 0; i--) { - if (i != STDIN_FILENO && i != STDERR_FILENO) - (void) fcntl(i, F_SETFD, FD_CLOEXEC); + if (fpp_child_stdout) { + if (pipe(pipefd_stdout) == -1) { + error("POPEN: stdout pipe() failed"); + ret = -1; + goto cleanup_and_return; + } + + if ((fp_child_stdout = fdopen(pipefd_stdout[PIPE_READ], "r")) == NULL) { + error("POPEN: fdopen() stdout failed"); + ret = -1; + goto cleanup_and_return; + } + + if(posix_spawn_file_actions_adddup2(&fa, pipefd_stdout[PIPE_WRITE], STDOUT_FILENO)) { + error("POPEN: posix_spawn_file_actions_adddup2() on stdout failed."); + ret = -1; + goto cleanup_and_return; + } + } + else { + if (posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, "/dev/null", O_WRONLY, 0)) { + error("POPEN: posix_spawn_file_actions_addopen() on stdout to /dev/null failed."); + // this is not a fatal error + stdout_fd_to_exclude_from_closing = STDOUT_FILENO; } } - if (!posix_spawn_file_actions_init(&fa)) { - if (flags & POPEN_FLAG_CREATE_PIPE) { - // move the pipe to stdout in the child - if (posix_spawn_file_actions_adddup2(&fa, pipefd[PIPE_WRITE], STDOUT_FILENO)) { - error("posix_spawn_file_actions_adddup2() failed"); - goto error_after_posix_spawn_file_actions_init; - } - } else { - // set stdout to /dev/null - if (posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, "/dev/null", O_WRONLY, 0)) { - error("posix_spawn_file_actions_addopen() failed"); - // this is not a fatal error - } + if(flags & POPEN_FLAG_CLOSE_FD) { + // Mark all files to be closed by the exec() stage of posix_spawn() + for(int i = (int)(sysconf(_SC_OPEN_MAX) - 1); i >= 0; i--) { + if(likely(i != STDERR_FILENO && i != stdin_fd_to_exclude_from_closing && i != stdout_fd_to_exclude_from_closing)) + (void)fcntl(i, F_SETFD, FD_CLOEXEC); } - } else { - error("posix_spawn_file_actions_init() failed."); - goto error_after_pipe; } - if (!(error = posix_spawnattr_init(&attr))) { + + attr_rc = posix_spawnattr_init(&attr); + if(attr_rc) { + // failed + error("POPEN: posix_spawnattr_init() failed."); + } + else { + // success // reset all signals in the child - sigset_t mask; if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF)) - error("posix_spawnattr_setflags() failed."); + error("POPEN: posix_spawnattr_setflags() failed."); + + sigset_t mask; sigemptyset(&mask); + if (posix_spawnattr_setsigmask(&attr, &mask)) - error("posix_spawnattr_setsigmask() failed."); - } else { - error("posix_spawnattr_init() failed."); + error("POPEN: posix_spawnattr_setsigmask() failed."); } // Take the lock while we fork to ensure we don't race with SIGCHLD // delivery on a process which exits quickly. - myp_add_lock(); + netdata_popen_tracking_lock(); if (!posix_spawn(&pid, command, &fa, &attr, (char * const*)spawn_argv, env)) { + // success *pidptr = pid; - myp_add_locked(pid); - debug(D_CHILDS, "Spawned command: \"%s\" on pid %d from parent pid %d.", command_to_be_logged, pid, getpid()); - } else { - myp_add_unlock(); - error("Failed to spawn command: \"%s\" from parent pid %d.", command_to_be_logged, getpid()); - if (flags & POPEN_FLAG_CREATE_PIPE) { - fclose(fp); - } - ret = -1; + netdata_popen_tracking_add_pid_unsafe(pid); + netdata_popen_tracking_unlock(); } - if (flags & POPEN_FLAG_CREATE_PIPE) { - close(pipefd[PIPE_WRITE]); - if (0 == ret) // on success set FILE * pointer - if(fpp) *fpp = fp; + else { + // failure + netdata_popen_tracking_unlock(); + error("POPEN: failed to spawn command: \"%s\" from parent pid %d.", command_to_be_logged, getpid()); + ret = -1; + goto cleanup_and_return; } - if (!error) { + // the normal cleanup will run + // but ret == 0 at this point + +cleanup_and_return: + if(!attr_rc) { // posix_spawnattr_init() succeeded if (posix_spawnattr_destroy(&attr)) - error("posix_spawnattr_destroy"); + error("POPEN: posix_spawnattr_destroy() failed"); } + if (posix_spawn_file_actions_destroy(&fa)) - error("posix_spawn_file_actions_destroy"); + error("POPEN: posix_spawn_file_actions_destroy() failed"); - return ret; + // the child end - close it + if(pipefd_stdin[PIPE_READ] != -1) + close(pipefd_stdin[PIPE_READ]); -error_after_posix_spawn_file_actions_init: - if (posix_spawn_file_actions_destroy(&fa)) - error("posix_spawn_file_actions_destroy"); + // our end + if(ret == -1 || !fpp_child_stdin) { + if (fp_child_stdin) + fclose(fp_child_stdin); + else if (pipefd_stdin[PIPE_WRITE] != -1) + close(pipefd_stdin[PIPE_WRITE]); + + fp_child_stdin = NULL; + } + + // the child end - close it + if (pipefd_stdout[PIPE_WRITE] != -1) + close(pipefd_stdout[PIPE_WRITE]); -error_after_pipe: - if (flags & POPEN_FLAG_CREATE_PIPE) { - if (fp) - fclose(fp); - else - close(pipefd[PIPE_READ]); + // our end + if (ret == -1 || !fpp_child_stdout) { + if (fp_child_stdout) + fclose(fp_child_stdout); + else if (pipefd_stdout[PIPE_READ] != -1) + close(pipefd_stdout[PIPE_READ]); - close(pipefd[PIPE_WRITE]); + fp_child_stdout = NULL; } - return -1; + +set_return_values_and_return: + if(fpp_child_stdin) + *fpp_child_stdin = fp_child_stdin; + + if(fpp_child_stdout) + *fpp_child_stdout = fp_child_stdout; + + return ret; } -int custom_popene_variadic_internal_dont_use_directly(volatile pid_t *pidptr, char **env, uint8_t flags, FILE **fpp, const char *command, ...) { +int netdata_popene_variadic_internal_dont_use_directly(volatile pid_t *pidptr, char **env, uint8_t flags, FILE **fpp_child_input, FILE **fpp_child_output, const char *command, ...) { // convert the variable list arguments into what posix_spawn() needs // all arguments are expected strings va_list args; @@ -232,88 +353,34 @@ int custom_popene_variadic_internal_dont_use_directly(volatile pid_t *pidptr, ch va_end(args); } - return custom_popene(pidptr, env, flags, fpp, command, spawn_argv); + return popene_internal(pidptr, env, flags, fpp_child_input, fpp_child_output, command, spawn_argv); } // See man environ extern char **environ; -// myp_init should be called by apps which act as init -// (pid 1) so that processes created by mypopen and mypopene -// are tracked. This enables the reaper to ignore processes -// which will be handled internally, by calling myp_reap, to -// avoid issues with already reaped processes during wait calls. -// -// Callers should call myp_free() to clean up resources. -void myp_init(void) { - info("process tracking enabled."); - myp_tracking = 1; - - if (netdata_mutex_init(&myp_lock) != 0) { - fatal("myp_init() mutex init failed."); - } -} - -// myp_free cleans up any resources allocated for process -// tracking. -void myp_free(void) { - struct mypopen *mp, *next; - - if (myp_tracking == 0) - return; - - netdata_mutex_lock(&myp_lock); - for (mp = mypopen_root; mp != NULL; mp = next) { - next = mp->next; - freez(mp); - } - - mypopen_root = NULL; - myp_tracking = 0; - netdata_mutex_unlock(&myp_lock); -} - -// myp_reap returns 1 if pid should be reaped, 0 otherwise. -int myp_reap(pid_t pid) { - struct mypopen *mp; - - if (myp_tracking == 0) - return 0; - - netdata_mutex_lock(&myp_lock); - for (mp = mypopen_root; mp != NULL; mp = mp->next) { - if (mp->pid == pid) { - netdata_mutex_unlock(&myp_lock); - return 0; - } - } - netdata_mutex_unlock(&myp_lock); - - return 1; -} - -FILE *mypopen(const char *command, volatile pid_t *pidptr) { - FILE *fp = NULL; +FILE *netdata_popen(const char *command, volatile pid_t *pidptr, FILE **fpp_child_input) { + FILE *fp_child_output = NULL; const char *spawn_argv[] = { "sh", "-c", command, NULL }; - (void)custom_popene(pidptr, environ, POPEN_FLAG_CREATE_PIPE|POPEN_FLAG_CLOSE_FD, &fp, "/bin/sh", spawn_argv); - return fp; + (void)popene_internal(pidptr, environ, POPEN_FLAG_CLOSE_FD, fpp_child_input, &fp_child_output, "/bin/sh", spawn_argv); + return fp_child_output; } -FILE *mypopene(const char *command, volatile pid_t *pidptr, char **env) { - FILE *fp = NULL; +FILE *netdata_popene(const char *command, volatile pid_t *pidptr, char **env, FILE **fpp_child_input) { + FILE *fp_child_output = NULL; const char *spawn_argv[] = { "sh", "-c", command, NULL }; - (void)custom_popene( pidptr, env, POPEN_FLAG_CREATE_PIPE|POPEN_FLAG_CLOSE_FD, &fp, "/bin/sh", spawn_argv); - return fp; + (void)popene_internal(pidptr, env, POPEN_FLAG_CLOSE_FD, fpp_child_input, &fp_child_output, "/bin/sh", spawn_argv); + return fp_child_output; } // returns 0 on success, -1 on failure @@ -324,29 +391,25 @@ int netdata_spawn(const char *command, volatile pid_t *pidptr) { command, NULL }; - return custom_popene( pidptr, environ, POPEN_FLAG_NONE, NULL, "/bin/sh", spawn_argv); + return popene_internal(pidptr, environ, POPEN_FLAG_NONE, NULL, NULL, "/bin/sh", spawn_argv); } -int custom_pclose(FILE *fp, pid_t pid) { +int netdata_pclose(FILE *fp_child_input, FILE *fp_child_output, pid_t pid) { int ret; siginfo_t info; - debug(D_EXIT, "Request to mypclose() on pid %d", pid); + debug(D_EXIT, "Request to netdata_pclose() on pid %d", pid); - if (fp) { - // close the pipe fd - // this is required in musl - // without it the childs do not exit - close(fileno(fp)); + if (fp_child_input) + fclose(fp_child_input); - // close the pipe file pointer - fclose(fp); - } + if (fp_child_output) + fclose(fp_child_output); errno = 0; ret = waitid(P_PID, (id_t) pid, &info, WEXITED); - myp_del(pid); + netdata_popen_tracking_del_pid(pid); if (ret != -1) { switch (info.si_code) { @@ -392,12 +455,6 @@ int custom_pclose(FILE *fp, pid_t pid) { return 0; } -int mypclose(FILE *fp, pid_t pid) -{ - return custom_pclose(fp, pid); -} - -int netdata_spawn_waitpid(pid_t pid) -{ - return custom_pclose(NULL, pid); +int netdata_spawn_waitpid(pid_t pid) { + return netdata_pclose(NULL, NULL, pid); } diff --git a/libnetdata/popen/popen.h b/libnetdata/popen/popen.h index 57eb9131e..c57a35a4e 100644 --- a/libnetdata/popen/popen.h +++ b/libnetdata/popen/popen.h @@ -10,31 +10,31 @@ /* custom_popene_variadic_internal_dont_use_directly flag definitions */ #define POPEN_FLAG_NONE 0 -#define POPEN_FLAG_CREATE_PIPE 1 // Create a pipe like popen() when set, otherwise set stdout to /dev/null -#define POPEN_FLAG_CLOSE_FD 2 // Close all file descriptors other than STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO +#define POPEN_FLAG_CLOSE_FD (1 << 0) // Close all file descriptors other than STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO // the flags to be used by default -#define POPEN_FLAGS_DEFAULT (POPEN_FLAG_CREATE_PIPE|POPEN_FLAG_CLOSE_FD) +#define POPEN_FLAGS_DEFAULT (POPEN_FLAG_CLOSE_FD) // mypopen_raw is the interface to use instead of custom_popene_variadic_internal_dont_use_directly() // mypopen_raw will add the terminating NULL at the arguments list // we append the parameter 'command' twice - this is because the underlying call needs the command to execute and the argv[0] to pass to it -#define mypopen_raw_default_flags_and_environment(pidptr, fpp, command, args...) custom_popene_variadic_internal_dont_use_directly(pidptr, environ, POPEN_FLAGS_DEFAULT, fpp, command, command, ##args, NULL) -#define mypopen_raw_default_flags(pidptr, env, fpp, command, args...) custom_popene_variadic_internal_dont_use_directly(pidptr, env, POPEN_FLAGS_DEFAULT, fpp, command, command, ##args, NULL) -#define mypopen_raw(pidptr, env, flags, fpp, command, args...) custom_popene_variadic_internal_dont_use_directly(pidptr, env, flags, fpp, command, command, ##args, NULL) - -extern int custom_popene_variadic_internal_dont_use_directly(volatile pid_t *pidptr, char **env, uint8_t flags, FILE **fpp, const char *command, ...); - -extern FILE *mypopen(const char *command, volatile pid_t *pidptr); -extern FILE *mypopene(const char *command, volatile pid_t *pidptr, char **env); -extern int mypclose(FILE *fp, pid_t pid); -extern int netdata_spawn(const char *command, volatile pid_t *pidptr); -extern int netdata_spawn_waitpid(pid_t pid); -extern void myp_init(void); -extern void myp_free(void); -extern int myp_reap(pid_t pid); - -extern void signals_unblock(void); -extern void signals_reset(void); +#define netdata_popen_raw_default_flags_and_environment(pidptr, fpp_child_input, fpp_child_output, command, args...) netdata_popene_variadic_internal_dont_use_directly(pidptr, environ, POPEN_FLAGS_DEFAULT, fpp_child_input, fpp_child_output, command, command, ##args, NULL) +#define netdata_popen_raw_default_flags(pidptr, env, fpp_child_input, fpp_child_output, command, args...) netdata_popene_variadic_internal_dont_use_directly(pidptr, env, POPEN_FLAGS_DEFAULT, fpp_child_input, fpp_child_output, command, command, ##args, NULL) +#define netdata_popen_raw(pidptr, env, flags, fpp_child_input, fpp_child_output, command, args...) netdata_popene_variadic_internal_dont_use_directly(pidptr, env, flags, fpp_child_input, fpp_child_output, command, command, ##args, NULL) + +FILE *netdata_popen(const char *command, volatile pid_t *pidptr, FILE **fp_child_input); +FILE *netdata_popene(const char *command, volatile pid_t *pidptr, char **env, FILE **fp_child_input); +int netdata_popene_variadic_internal_dont_use_directly(volatile pid_t *pidptr, char **env, uint8_t flags, FILE **fpp_child_input, FILE **fpp_child_output, const char *command, ...); +int netdata_pclose(FILE *fp_child_input, FILE *fp_child_output, pid_t pid); + +int netdata_spawn(const char *command, volatile pid_t *pidptr); +int netdata_spawn_waitpid(pid_t pid); + +void netdata_popen_tracking_init(void); +void netdata_popen_tracking_cleanup(void); +int netdata_popen_tracking_pid_shoud_be_reaped(pid_t pid); + +void signals_unblock(void); +void signals_reset(void); #endif /* NETDATA_POPEN_H */ diff --git a/libnetdata/procfile/procfile.c b/libnetdata/procfile/procfile.c index 19964da17..b4ca025ec 100644 --- a/libnetdata/procfile/procfile.c +++ b/libnetdata/procfile/procfile.c @@ -42,7 +42,7 @@ char *procfile_filename(procfile *ff) { // ---------------------------------------------------------------------------- // An array of words -static inline void pfwords_add(procfile *ff, char *str) { +static inline void procfile_words_add(procfile *ff, char *str) { // debug(D_PROCFILE, PF_PREFIX ": adding word No %d: '%s'", fw->len, str); pfwords *fw = ff->words; @@ -60,7 +60,7 @@ static inline void pfwords_add(procfile *ff, char *str) { } NEVERNULL -static inline pfwords *pfwords_new(void) { +static inline pfwords *procfile_words_create(void) { // debug(D_PROCFILE, PF_PREFIX ": initializing words"); size_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP; @@ -71,12 +71,12 @@ static inline pfwords *pfwords_new(void) { return new; } -static inline void pfwords_reset(pfwords *fw) { +static inline void procfile_words_reset(pfwords *fw) { // debug(D_PROCFILE, PF_PREFIX ": resetting words"); fw->len = 0; } -static inline void pfwords_free(pfwords *fw) { +static inline void procfile_words_free(pfwords *fw) { // debug(D_PROCFILE, PF_PREFIX ": freeing words"); freez(fw); @@ -87,7 +87,7 @@ static inline void pfwords_free(pfwords *fw) { // An array of lines NEVERNULL -static inline size_t *pflines_add(procfile *ff) { +static inline size_t *procfile_lines_add(procfile *ff) { // debug(D_PROCFILE, PF_PREFIX ": adding line %d at word %d", fl->len, first_word); pflines *fl = ff->lines; @@ -109,7 +109,7 @@ static inline size_t *pflines_add(procfile *ff) { } NEVERNULL -static inline pflines *pflines_new(void) { +static inline pflines *procfile_lines_create(void) { // debug(D_PROCFILE, PF_PREFIX ": initializing lines"); size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP; @@ -120,13 +120,13 @@ static inline pflines *pflines_new(void) { return new; } -static inline void pflines_reset(pflines *fl) { +static inline void procfile_lines_reset(pflines *fl) { // debug(D_PROCFILE, PF_PREFIX ": resetting lines"); fl->len = 0; } -static inline void pflines_free(pflines *fl) { +static inline void procfile_lines_free(pflines *fl) { // debug(D_PROCFILE, PF_PREFIX ": freeing lines"); freez(fl); @@ -141,8 +141,8 @@ void procfile_close(procfile *ff) { debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", procfile_filename(ff)); - if(likely(ff->lines)) pflines_free(ff->lines); - if(likely(ff->words)) pfwords_free(ff->words); + if(likely(ff->lines)) procfile_lines_free(ff->lines); + if(likely(ff->words)) procfile_words_free(ff->words); if(likely(ff->fd != -1)) close(ff->fd); freez(ff); @@ -162,7 +162,7 @@ static void procfile_parser(procfile *ff) { char quote = 0; // the quote character - only when in quoted string size_t opened = 0; // counts the number of open parenthesis - size_t *line_words = pflines_add(ff); + size_t *line_words = procfile_lines_add(ff); while(s < e) { PF_CHAR_TYPE ct = separators[(unsigned char)(*s)]; @@ -177,7 +177,7 @@ static void procfile_parser(procfile *ff) { if (s != t) { // separator, but we have word before it *s = '\0'; - pfwords_add(ff, t); + procfile_words_add(ff, t); (*line_words)++; t = ++s; } @@ -196,13 +196,13 @@ static void procfile_parser(procfile *ff) { // end of line *s = '\0'; - pfwords_add(ff, t); + procfile_words_add(ff, t); (*line_words)++; t = ++s; // debug(D_PROCFILE, PF_PREFIX ": ended line %d with %d words", l, ff->lines->lines[l].words); - line_words = pflines_add(ff); + line_words = procfile_lines_add(ff); } else if(likely(ct == PF_CHAR_IS_QUOTE)) { if(unlikely(!quote && s == t)) { @@ -215,7 +215,7 @@ static void procfile_parser(procfile *ff) { quote = 0; *s = '\0'; - pfwords_add(ff, t); + procfile_words_add(ff, t); (*line_words)++; t = ++s; } @@ -240,7 +240,7 @@ static void procfile_parser(procfile *ff) { if(!opened) { *s = '\0'; - pfwords_add(ff, t); + procfile_words_add(ff, t); (*line_words)++; t = ++s; } @@ -262,7 +262,7 @@ static void procfile_parser(procfile *ff) { } *s = '\0'; - pfwords_add(ff, t); + procfile_words_add(ff, t); (*line_words)++; // t = ++s; } @@ -305,8 +305,8 @@ procfile *procfile_readall(procfile *ff) { return NULL; } - pflines_reset(ff->lines); - pfwords_reset(ff->words); + procfile_lines_reset(ff->lines); + procfile_words_reset(ff->words); procfile_parser(ff); if(unlikely(procfile_adaptive_initial_allocation)) { @@ -423,8 +423,8 @@ procfile *procfile_open(const char *filename, const char *separators, uint32_t f ff->len = 0; ff->flags = flags; - ff->lines = pflines_new(); - ff->words = pfwords_new(); + ff->lines = procfile_lines_create(); + ff->words = procfile_words_create(); procfile_set_separators(ff, separators); diff --git a/libnetdata/procfile/procfile.h b/libnetdata/procfile/procfile.h index 5263ad770..5d45e4028 100644 --- a/libnetdata/procfile/procfile.h +++ b/libnetdata/procfile/procfile.h @@ -60,25 +60,25 @@ typedef struct { } procfile; // close the proc file and free all related memory -extern void procfile_close(procfile *ff); +void procfile_close(procfile *ff); // (re)read and parse the proc file -extern procfile *procfile_readall(procfile *ff); +procfile *procfile_readall(procfile *ff); // open a /proc or /sys file -extern procfile *procfile_open(const char *filename, const char *separators, uint32_t flags); +procfile *procfile_open(const char *filename, const char *separators, uint32_t flags); // re-open a file // if separators == NULL, the last separators are used -extern procfile *procfile_reopen(procfile *ff, const char *filename, const char *separators, uint32_t flags); +procfile *procfile_reopen(procfile *ff, const char *filename, const char *separators, uint32_t flags); // example walk-through a procfile parsed file -extern void procfile_print(procfile *ff); +void procfile_print(procfile *ff); -extern void procfile_set_quotes(procfile *ff, const char *quotes); -extern void procfile_set_open_close(procfile *ff, const char *open, const char *close); +void procfile_set_quotes(procfile *ff, const char *quotes); +void procfile_set_open_close(procfile *ff, const char *open, const char *close); -extern char *procfile_filename(procfile *ff); +char *procfile_filename(procfile *ff); // ---------------------------------------------------------------------------- diff --git a/libnetdata/required_dummies.h b/libnetdata/required_dummies.h index 6d51bfedd..ad1e8fb84 100644 --- a/libnetdata/required_dummies.h +++ b/libnetdata/required_dummies.h @@ -22,15 +22,20 @@ void signals_block(void){}; void signals_unblock(void){}; void signals_reset(void){}; +#ifndef UNIT_TESTING // callback required by eval() -int health_variable_lookup(const char *variable, uint32_t hash, struct rrdcalc *rc, NETDATA_DOUBLE *result) +int health_variable_lookup(STRING *variable, struct rrdcalc *rc, NETDATA_DOUBLE *result) { (void)variable; - (void)hash; (void)rc; (void)result; return 0; }; +#endif + +void rrdset_thread_rda_free(void){}; +void sender_thread_buffer_free(void){}; +void query_target_free(void){}; // required by get_system_cpus() char *netdata_configured_host_prefix = ""; diff --git a/libnetdata/simple_pattern/simple_pattern.c b/libnetdata/simple_pattern/simple_pattern.c index 70b06a22b..81c2ed0b8 100644 --- a/libnetdata/simple_pattern/simple_pattern.c +++ b/libnetdata/simple_pattern/simple_pattern.c @@ -333,9 +333,7 @@ extern int simple_pattern_is_potential_name(SIMPLE_PATTERN *p) } char *simple_pattern_trim_around_equal(char *src) { - char *store = mallocz(strlen(src) +1); - if(!store) - return NULL; + char *store = mallocz(strlen(src) + 1); char *dst = store; while (*src) { diff --git a/libnetdata/simple_pattern/simple_pattern.h b/libnetdata/simple_pattern/simple_pattern.h index 36fbbde7d..7282053e8 100644 --- a/libnetdata/simple_pattern/simple_pattern.h +++ b/libnetdata/simple_pattern/simple_pattern.h @@ -18,23 +18,25 @@ typedef void SIMPLE_PATTERN; // create a simple_pattern from the string given // default_mode is used in cases where EXACT matches, without an asterisk, // should be considered PREFIX matches. -extern SIMPLE_PATTERN *simple_pattern_create(const char *list, const char *separators, SIMPLE_PREFIX_MODE default_mode); +SIMPLE_PATTERN *simple_pattern_create(const char *list, const char *separators, SIMPLE_PREFIX_MODE default_mode); // test if string str is matched from the pattern and fill 'wildcarded' with the parts matched by '*' -extern int simple_pattern_matches_extract(SIMPLE_PATTERN *list, const char *str, char *wildcarded, size_t wildcarded_size); +int simple_pattern_matches_extract(SIMPLE_PATTERN *list, const char *str, char *wildcarded, size_t wildcarded_size); // test if string str is matched from the pattern #define simple_pattern_matches(list, str) simple_pattern_matches_extract(list, str, NULL, 0) // free a simple_pattern that was created with simple_pattern_create() // list can be NULL, in which case, this does nothing. -extern void simple_pattern_free(SIMPLE_PATTERN *list); +void simple_pattern_free(SIMPLE_PATTERN *list); -extern void simple_pattern_dump(uint64_t debug_type, SIMPLE_PATTERN *p) ; -extern int simple_pattern_is_potential_name(SIMPLE_PATTERN *p) ; -extern char *simple_pattern_iterate(SIMPLE_PATTERN **p); +void simple_pattern_dump(uint64_t debug_type, SIMPLE_PATTERN *p) ; +int simple_pattern_is_potential_name(SIMPLE_PATTERN *p) ; +char *simple_pattern_iterate(SIMPLE_PATTERN **p); -//Auxiliary function to create a pattern +// Auxiliary function to create a pattern char *simple_pattern_trim_around_equal(char *src); +#define is_valid_sp(x) ((x) && *(x) && !((x)[0] == '*' && (x)[1] == '\0')) + #endif //NETDATA_SIMPLE_PATTERN_H diff --git a/libnetdata/socket/security.c b/libnetdata/socket/security.c index 6ac512de5..f7b44049b 100644 --- a/libnetdata/socket/security.c +++ b/libnetdata/socket/security.c @@ -2,14 +2,14 @@ #ifdef ENABLE_HTTPS -SSL_CTX *netdata_exporting_ctx=NULL; -SSL_CTX *netdata_client_ctx=NULL; -SSL_CTX *netdata_srv_ctx=NULL; -const char *security_key=NULL; -const char *security_cert=NULL; +SSL_CTX *netdata_ssl_exporting_ctx =NULL; +SSL_CTX *netdata_ssl_client_ctx =NULL; +SSL_CTX *netdata_ssl_srv_ctx =NULL; +const char *netdata_ssl_security_key =NULL; +const char *netdata_ssl_security_cert =NULL; const char *tls_version=NULL; const char *tls_ciphers=NULL; -int netdata_validate_server = NETDATA_SSL_VALID_CERTIFICATE; +int netdata_ssl_validate_server = NETDATA_SSL_VALID_CERTIFICATE; /** * Info Callback @@ -161,7 +161,7 @@ static SSL_CTX * security_initialize_openssl_server() { return NULL; } - SSL_CTX_use_certificate_file(ctx, security_cert, SSL_FILETYPE_PEM); + SSL_CTX_use_certificate_file(ctx, netdata_ssl_security_cert, SSL_FILETYPE_PEM); #else ctx = SSL_CTX_new(TLS_server_method()); if (!ctx) { @@ -169,11 +169,11 @@ static SSL_CTX * security_initialize_openssl_server() { return NULL; } - SSL_CTX_use_certificate_chain_file(ctx, security_cert); + SSL_CTX_use_certificate_chain_file(ctx, netdata_ssl_security_cert); #endif security_openssl_common_options(ctx, 0); - SSL_CTX_use_PrivateKey_file(ctx,security_key,SSL_FILETYPE_PEM); + SSL_CTX_use_PrivateKey_file(ctx, netdata_ssl_security_key,SSL_FILETYPE_PEM); if (!SSL_CTX_check_private_key(ctx)) { ERR_error_string_n(ERR_get_error(),lerror,sizeof(lerror)); @@ -207,24 +207,25 @@ void security_start_ssl(int selector) { switch (selector) { case NETDATA_SSL_CONTEXT_SERVER: { struct stat statbuf; - if (stat(security_key, &statbuf) || stat(security_cert, &statbuf)) { + if (stat(netdata_ssl_security_key, &statbuf) || stat(netdata_ssl_security_cert, &statbuf)) { info("To use encryption it is necessary to set \"ssl certificate\" and \"ssl key\" in [web] !\n"); return; } - netdata_srv_ctx = security_initialize_openssl_server(); - SSL_CTX_set_mode(netdata_srv_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); + netdata_ssl_srv_ctx = security_initialize_openssl_server(); + SSL_CTX_set_mode(netdata_ssl_srv_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); break; } case NETDATA_SSL_CONTEXT_STREAMING: { - netdata_client_ctx = security_initialize_openssl_client(); + netdata_ssl_client_ctx = security_initialize_openssl_client(); //This is necessary for the stream, because it is working sometimes with nonblock socket. //It returns the bitmask after to change, there is not any description of errors in the documentation - SSL_CTX_set_mode(netdata_client_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode( + netdata_ssl_client_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |SSL_MODE_AUTO_RETRY); break; } case NETDATA_SSL_CONTEXT_EXPORTING: { - netdata_exporting_ctx = security_initialize_openssl_client(); + netdata_ssl_exporting_ctx = security_initialize_openssl_client(); break; } } @@ -237,16 +238,16 @@ void security_start_ssl(int selector) { */ void security_clean_openssl() { - if (netdata_srv_ctx) { - SSL_CTX_free(netdata_srv_ctx); + if (netdata_ssl_srv_ctx) { + SSL_CTX_free(netdata_ssl_srv_ctx); } - if (netdata_client_ctx) { - SSL_CTX_free(netdata_client_ctx); + if (netdata_ssl_client_ctx) { + SSL_CTX_free(netdata_ssl_client_ctx); } - if (netdata_exporting_ctx) { - SSL_CTX_free(netdata_exporting_ctx); + if (netdata_ssl_exporting_ctx) { + SSL_CTX_free(netdata_ssl_exporting_ctx); } #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110 @@ -355,32 +356,23 @@ int security_test_certificate(SSL *ssl) { * * @return It returns 0 on success and -1 otherwise. */ -int security_location_for_context(SSL_CTX *ctx, char *file, char *path) { - struct stat statbuf; - if (stat(file, &statbuf)) { - info("Netdata does not have the parent's SSL certificate, so it will use the default OpenSSL configuration to validate certificates!"); - return 0; - } - - ERR_clear_error(); - u_long err; - char buf[256]; - if(!SSL_CTX_load_verify_locations(ctx, file, path)) { - goto slfc; +int ssl_security_location_for_context(SSL_CTX *ctx, char *file, char *path) { + int load_custom = 1, load_default = 1; + if (file || path) { + if(!SSL_CTX_load_verify_locations(ctx, file, path)) { + info("Netdata can not verify custom CAfile or CApath for parent's SSL certificate, so it will use the default OpenSSL configuration to validate certificates!"); + load_custom = 0; + } } if(!SSL_CTX_set_default_verify_paths(ctx)) { - goto slfc; + info("Can not verify default OpenSSL configuration to validate certificates!"); + load_default = 0; } - return 0; + if (load_custom == 0 && load_default == 0) + return -1; -slfc: - while ((err = ERR_get_error()) != 0) { - ERR_error_string_n(err, buf, sizeof(buf)); - error("Cannot set the directory for the certificates and the parent SSL certificate: %s",buf); - } - return -1; + return 0; } - #endif diff --git a/libnetdata/socket/security.h b/libnetdata/socket/security.h index dbf71a6fe..ae7c595e3 100644 --- a/libnetdata/socket/security.h +++ b/libnetdata/socket/security.h @@ -37,20 +37,20 @@ #include #endif -struct netdata_ssl{ +struct netdata_ssl { SSL *conn; //SSL connection uint32_t flags; //The flags for SSL connection }; -extern SSL_CTX *netdata_exporting_ctx; -extern SSL_CTX *netdata_client_ctx; -extern SSL_CTX *netdata_srv_ctx; -extern const char *security_key; -extern const char *security_cert; +extern SSL_CTX *netdata_ssl_exporting_ctx; +extern SSL_CTX *netdata_ssl_client_ctx; +extern SSL_CTX *netdata_ssl_srv_ctx; +extern const char *netdata_ssl_security_key; +extern const char *netdata_ssl_security_cert; extern const char *tls_version; extern const char *tls_ciphers; -extern int netdata_validate_server; -extern int security_location_for_context(SSL_CTX *ctx,char *file,char *path); +extern int netdata_ssl_validate_server; +int ssl_security_location_for_context(SSL_CTX *ctx,char *file,char *path); void security_openssl_library(); void security_clean_openssl(); diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c index df6d3148b..40271b623 100644 --- a/libnetdata/socket/socket.c +++ b/libnetdata/socket/socket.c @@ -779,6 +779,10 @@ int connect_to_this(const char *definition, int default_port, struct timeval *ti char *path = host + 5; return connect_to_unix(path, timeout); } + else if(*host == '/') { + char *path = host; + return connect_to_unix(path, timeout); + } char *e = host; if(*e == '[') { @@ -826,43 +830,141 @@ int connect_to_this(const char *definition, int default_port, struct timeval *ti return connect_to_this_ip46(protocol, socktype, host, scope_id, service, timeout); } -int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) { - int sock = -1; - +void foreach_entry_in_connection_string(const char *destination, bool (*callback)(char *entry, void *data), void *data) { const char *s = destination; while(*s) { const char *e = s; - // skip path, moving both s(tart) and e(nd) - if(*e == '/') - while(!isspace(*e) && *e != ',') s = ++e; - // skip separators, moving both s(tart) and e(nd) while(isspace(*e) || *e == ',') s = ++e; // move e(nd) to the first separator - while(*e && !isspace(*e) && *e != ',' && *e != '/') e++; + while(*e && !isspace(*e) && *e != ',') e++; // is there anything? if(!*s || s == e) break; char buf[e - s + 1]; strncpyz(buf, s, e - s); - if(reconnects_counter) *reconnects_counter += 1; - sock = connect_to_this(buf, default_port, timeout); - if(sock != -1) { - if(connected_to && connected_to_size) { - strncpy(connected_to, buf, connected_to_size); - connected_to[connected_to_size - 1] = '\0'; - } - break; - } + + if(callback(buf, data)) break; + s = e; } +} - return sock; +struct connect_to_one_of_data { + int default_port; + struct timeval *timeout; + size_t *reconnects_counter; + char *connected_to; + size_t connected_to_size; + int sock; +}; + +static bool connect_to_one_of_callback(char *entry, void *data) { + struct connect_to_one_of_data *t = data; + + if(t->reconnects_counter) + t->reconnects_counter++; + + t->sock = connect_to_this(entry, t->default_port, t->timeout); + if(t->sock != -1) { + if(t->connected_to && t->connected_to_size) { + strncpyz(t->connected_to, entry, t->connected_to_size); + t->connected_to[t->connected_to_size - 1] = '\0'; + } + + return true; + } + + return false; +} + +int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) { + struct connect_to_one_of_data t = { + .default_port = default_port, + .timeout = timeout, + .reconnects_counter = reconnects_counter, + .connected_to = connected_to, + .connected_to_size = connected_to_size, + .sock = -1, + }; + + foreach_entry_in_connection_string(destination, connect_to_one_of_callback, &t); + + return t.sock; +} + +static bool connect_to_one_of_urls_callback(char *entry, void *data) { + char *s = strchr(entry, '/'); + if(s) *s = '\0'; + + return connect_to_one_of_callback(entry, data); } +int connect_to_one_of_urls(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) { + struct connect_to_one_of_data t = { + .default_port = default_port, + .timeout = timeout, + .reconnects_counter = reconnects_counter, + .connected_to = connected_to, + .connected_to_size = connected_to_size, + .sock = -1, + }; + + foreach_entry_in_connection_string(destination, connect_to_one_of_urls_callback, &t); + + return t.sock; +} + + +#ifdef ENABLE_HTTPS +ssize_t netdata_ssl_read(SSL *ssl, void *buf, size_t num) { + error_limit_static_thread_var(erl, 1, 0); + + int bytes, err, retries = 0; + + //do { + bytes = SSL_read(ssl, buf, (int)num); + err = SSL_get_error(ssl, bytes); + retries++; + //} while (bytes <= 0 && (err == SSL_ERROR_WANT_READ)); + + if(unlikely(bytes <= 0)) + error("SSL_read() returned %d bytes, SSL error %d", bytes, err); + + if(retries > 1) + error_limit(&erl, "SSL_read() retried %d times", retries); + + return bytes; +} + +ssize_t netdata_ssl_write(SSL *ssl, const void *buf, size_t num) { + error_limit_static_thread_var(erl, 1, 0); + + int bytes, err, retries = 0; + size_t total = 0; + + //do { + bytes = SSL_write(ssl, (uint8_t *)buf + total, (int)(num - total)); + err = SSL_get_error(ssl, bytes); + retries++; + + if(bytes > 0) + total += bytes; + + //} while ((bytes <= 0 && (err == SSL_ERROR_WANT_WRITE)) || (bytes > 0 && total < num)); + + if(unlikely(bytes <= 0)) + error("SSL_write() returned %d bytes, SSL error %d", bytes, err); + + if(retries > 1) + error_limit(&erl, "SSL_write() retried %d times", retries); + + return bytes; +} +#endif // -------------------------------------------------------------------------------------------------------------------- // helpers to send/receive data in one call, in blocking mode, with a timeout @@ -901,12 +1003,10 @@ ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) } #ifdef ENABLE_HTTPS - if (ssl->conn) { - if (!ssl->flags) { - return SSL_read(ssl->conn,buf,len); - } - } + if (ssl->conn && ssl->flags == NETDATA_SSL_HANDSHAKE_COMPLETE) + return netdata_ssl_read(ssl->conn, buf, len); #endif + return recv(sockfd, buf, len, flags); } @@ -945,8 +1045,12 @@ ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) #ifdef ENABLE_HTTPS if(ssl->conn) { - if (!ssl->flags) { - return SSL_write(ssl->conn, buf, len); + if (ssl->flags == NETDATA_SSL_HANDSHAKE_COMPLETE) { + return netdata_ssl_write(ssl->conn, buf, len); + } + else { + error("cannot write to SSL connection - connection is not ready."); + return -1; } } #endif @@ -1087,12 +1191,11 @@ int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *clien if (getnameinfo((struct sockaddr *)&sadr, addrlen, client_ip, (socklen_t)ipsize, client_port, (socklen_t)portsize, NI_NUMERICHOST | NI_NUMERICSERV) != 0) { error("LISTENER: cannot getnameinfo() on received client connection."); - strncpyz(client_ip, "UNKNOWN", ipsize - 1); - strncpyz(client_port, "UNKNOWN", portsize - 1); + strncpyz(client_ip, "UNKNOWN", ipsize); + strncpyz(client_port, "UNKNOWN", portsize); } if (!strcmp(client_ip, "127.0.0.1") || !strcmp(client_ip, "::1")) { - strncpy(client_ip, "localhost", ipsize); - client_ip[ipsize - 1] = '\0'; + strncpyz(client_ip, "localhost", ipsize); } #ifdef __FreeBSD__ @@ -1107,8 +1210,7 @@ int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *clien case AF_UNIX: debug(D_LISTENER, "New UNIX domain web client from %s on socket %d.", client_ip, fd); // set the port - certain versions of libc return garbage on unix sockets - strncpy(client_port, "UNIX", portsize); - client_port[portsize - 1] = '\0'; + strncpyz(client_port, "UNIX", portsize); break; case AF_INET: @@ -1490,8 +1592,9 @@ static int poll_process_new_tcp_connection(POLLJOB *p, POLLINFO *pi, struct poll debug(D_POLLFD, "POLLFD: LISTENER: accept4() slot %zu (fd %d) failed.", pi->slot, pf->fd); if(unlikely(errno == EMFILE)) { - error("POLLFD: LISTENER: too many open files - sleeping for 1ms - used by this thread %zu, max for this thread %zu", p->used, p->limit); - usleep(1000); // 1ms + error_limit_static_global_var(erl, 10, 1000); + error_limit(&erl, "POLLFD: LISTENER: too many open files - used by this thread %zu, max for this thread %zu", + p->used, p->limit); } else if(unlikely(errno != EWOULDBLOCK && errno != EAGAIN)) error("POLLFD: LISTENER: accept() failed."); diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h index a40d801dd..282324273 100644 --- a/libnetdata/socket/socket.h +++ b/libnetdata/socket/socket.h @@ -10,19 +10,22 @@ #endif typedef enum web_client_acl { - WEB_CLIENT_ACL_NONE = 0, - WEB_CLIENT_ACL_NOCHECK = 0, - WEB_CLIENT_ACL_DASHBOARD = 1 << 0, - WEB_CLIENT_ACL_REGISTRY = 1 << 1, - WEB_CLIENT_ACL_BADGE = 1 << 2, - WEB_CLIENT_ACL_MGMT = 1 << 3, - WEB_CLIENT_ACL_STREAMING = 1 << 4, - WEB_CLIENT_ACL_NETDATACONF = 1 << 5, + WEB_CLIENT_ACL_NONE = 0, + WEB_CLIENT_ACL_NOCHECK = 0, + WEB_CLIENT_ACL_DASHBOARD = 1 << 0, + WEB_CLIENT_ACL_REGISTRY = 1 << 1, + WEB_CLIENT_ACL_BADGE = 1 << 2, + WEB_CLIENT_ACL_MGMT = 1 << 3, + WEB_CLIENT_ACL_STREAMING = 1 << 4, + WEB_CLIENT_ACL_NETDATACONF = 1 << 5, WEB_CLIENT_ACL_SSL_OPTIONAL = 1 << 6, - WEB_CLIENT_ACL_SSL_FORCE = 1 << 7, - WEB_CLIENT_ACL_SSL_DEFAULT = 1 << 8 + WEB_CLIENT_ACL_SSL_FORCE = 1 << 7, + WEB_CLIENT_ACL_SSL_DEFAULT = 1 << 8, + WEB_CLIENT_ACL_ACLK = 1 << 9, } WEB_CLIENT_ACL; +#define WEB_CLIENT_ACL_ALL 0xFFFF + #define web_client_can_access_dashboard(w) ((w)->acl & WEB_CLIENT_ACL_DASHBOARD) #define web_client_can_access_registry(w) ((w)->acl & WEB_CLIENT_ACL_REGISTRY) #define web_client_can_access_badges(w) ((w)->acl & WEB_CLIENT_ACL_BADGE) @@ -49,37 +52,42 @@ typedef struct listen_sockets { WEB_CLIENT_ACL fds_acl_flags[MAX_LISTEN_FDS]; // the acl to apply to the open sockets (dashboard, badges, streaming, netdata.conf, management) } LISTEN_SOCKETS; -extern char *strdup_client_description(int family, const char *protocol, const char *ip, uint16_t port); +char *strdup_client_description(int family, const char *protocol, const char *ip, uint16_t port); -extern int listen_sockets_setup(LISTEN_SOCKETS *sockets); -extern void listen_sockets_close(LISTEN_SOCKETS *sockets); +int listen_sockets_setup(LISTEN_SOCKETS *sockets); +void listen_sockets_close(LISTEN_SOCKETS *sockets); -extern int connect_to_this(const char *definition, int default_port, struct timeval *timeout); -extern int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size); +void foreach_entry_in_connection_string(const char *destination, bool (*callback)(char *entry, void *data), void *data); int connect_to_this_ip46(int protocol, int socktype, const char *host, uint32_t scope_id, const char *service, struct timeval *timeout); +int connect_to_this(const char *definition, int default_port, struct timeval *timeout); +int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size); +int connect_to_one_of_urls(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size); + #ifdef ENABLE_HTTPS -extern ssize_t recv_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout); -extern ssize_t send_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout); +ssize_t recv_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout); +ssize_t send_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout); +ssize_t netdata_ssl_read(SSL *ssl, void *buf, size_t num); +ssize_t netdata_ssl_write(SSL *ssl, const void *buf, size_t num); #else -extern ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); -extern ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); +ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); +ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); #endif -extern int sock_setnonblock(int fd); -extern int sock_delnonblock(int fd); -extern int sock_setreuse(int fd, int reuse); -extern int sock_setreuse_port(int fd, int reuse); -extern int sock_enlarge_in(int fd); -extern int sock_enlarge_out(int fd); +int sock_setnonblock(int fd); +int sock_delnonblock(int fd); +int sock_setreuse(int fd, int reuse); +int sock_setreuse_port(int fd, int reuse); +int sock_enlarge_in(int fd); +int sock_enlarge_out(int fd); -extern int connection_allowed(int fd, char *client_ip, char *client_host, size_t hostsize, +int connection_allowed(int fd, char *client_ip, char *client_host, size_t hostsize, SIMPLE_PATTERN *access_list, const char *patname, int allow_dns); -extern int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *client_port, size_t portsize, +int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *client_port, size_t portsize, char *client_host, size_t hostsize, SIMPLE_PATTERN *access_list, int allow_dns); #ifndef HAVE_ACCEPT4 -extern int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags); +int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags); #ifndef SOCK_NONBLOCK #define SOCK_NONBLOCK 00004000 @@ -167,12 +175,12 @@ struct poll { #define pollinfo_from_slot(p, slot) (&((p)->inf[(slot)])) -extern int poll_default_snd_callback(POLLINFO *pi, short int *events); -extern int poll_default_rcv_callback(POLLINFO *pi, short int *events); -extern void poll_default_del_callback(POLLINFO *pi); -extern void *poll_default_add_callback(POLLINFO *pi, short int *events, void *data); +int poll_default_snd_callback(POLLINFO *pi, short int *events); +int poll_default_rcv_callback(POLLINFO *pi, short int *events); +void poll_default_del_callback(POLLINFO *pi); +void *poll_default_add_callback(POLLINFO *pi, short int *events, void *data); -extern POLLINFO *poll_add_fd(POLLJOB *p +POLLINFO *poll_add_fd(POLLJOB *p , int fd , int socktype , WEB_CLIENT_ACL port_acl @@ -186,9 +194,9 @@ extern POLLINFO *poll_add_fd(POLLJOB *p , int (*snd_callback)(POLLINFO *pi, short int *events) , void *data ); -extern void poll_close_fd(POLLINFO *pi); +void poll_close_fd(POLLINFO *pi); -extern void poll_events(LISTEN_SOCKETS *sockets +void poll_events(LISTEN_SOCKETS *sockets , void *(*add_callback)(POLLINFO *pi, short int *events, void *data) , void (*del_callback)(POLLINFO *pi) , int (*rcv_callback)(POLLINFO *pi, short int *events) diff --git a/libnetdata/statistical/statistical.h b/libnetdata/statistical/statistical.h index 9496e0e7f..f3ecfadb4 100644 --- a/libnetdata/statistical/statistical.h +++ b/libnetdata/statistical/statistical.h @@ -5,30 +5,30 @@ #include "../libnetdata.h" -extern void log_series_to_stderr(NETDATA_DOUBLE *series, size_t entries, NETDATA_DOUBLE result, const char *msg); +void log_series_to_stderr(NETDATA_DOUBLE *series, size_t entries, NETDATA_DOUBLE result, const char *msg); -extern NETDATA_DOUBLE average(const NETDATA_DOUBLE *series, size_t entries); -extern NETDATA_DOUBLE moving_average(const NETDATA_DOUBLE *series, size_t entries, size_t period); -extern NETDATA_DOUBLE median(const NETDATA_DOUBLE *series, size_t entries); -extern NETDATA_DOUBLE moving_median(const NETDATA_DOUBLE *series, size_t entries, size_t period); -extern NETDATA_DOUBLE running_median_estimate(const NETDATA_DOUBLE *series, size_t entries); -extern NETDATA_DOUBLE standard_deviation(const NETDATA_DOUBLE *series, size_t entries); -extern NETDATA_DOUBLE single_exponential_smoothing(const NETDATA_DOUBLE *series, size_t entries, NETDATA_DOUBLE alpha); +NETDATA_DOUBLE average(const NETDATA_DOUBLE *series, size_t entries); +NETDATA_DOUBLE moving_average(const NETDATA_DOUBLE *series, size_t entries, size_t period); +NETDATA_DOUBLE median(const NETDATA_DOUBLE *series, size_t entries); +NETDATA_DOUBLE moving_median(const NETDATA_DOUBLE *series, size_t entries, size_t period); +NETDATA_DOUBLE running_median_estimate(const NETDATA_DOUBLE *series, size_t entries); +NETDATA_DOUBLE standard_deviation(const NETDATA_DOUBLE *series, size_t entries); +NETDATA_DOUBLE single_exponential_smoothing(const NETDATA_DOUBLE *series, size_t entries, NETDATA_DOUBLE alpha); extern NETDATA_DOUBLE single_exponential_smoothing_reverse(const NETDATA_DOUBLE *series, size_t entries, NETDATA_DOUBLE alpha); -extern NETDATA_DOUBLE double_exponential_smoothing(const NETDATA_DOUBLE *series, size_t entries, +NETDATA_DOUBLE double_exponential_smoothing(const NETDATA_DOUBLE *series, size_t entries, NETDATA_DOUBLE alpha, NETDATA_DOUBLE beta, NETDATA_DOUBLE *forecast); -extern NETDATA_DOUBLE holtwinters(const NETDATA_DOUBLE *series, size_t entries, +NETDATA_DOUBLE holtwinters(const NETDATA_DOUBLE *series, size_t entries, NETDATA_DOUBLE alpha, NETDATA_DOUBLE beta, NETDATA_DOUBLE gamma, NETDATA_DOUBLE *forecast); -extern NETDATA_DOUBLE sum_and_count(const NETDATA_DOUBLE *series, size_t entries, size_t *count); -extern NETDATA_DOUBLE sum(const NETDATA_DOUBLE *series, size_t entries); -extern NETDATA_DOUBLE median_on_sorted_series(const NETDATA_DOUBLE *series, size_t entries); -extern NETDATA_DOUBLE *copy_series(const NETDATA_DOUBLE *series, size_t entries); -extern void sort_series(NETDATA_DOUBLE *series, size_t entries); +NETDATA_DOUBLE sum_and_count(const NETDATA_DOUBLE *series, size_t entries, size_t *count); +NETDATA_DOUBLE sum(const NETDATA_DOUBLE *series, size_t entries); +NETDATA_DOUBLE median_on_sorted_series(const NETDATA_DOUBLE *series, size_t entries); +NETDATA_DOUBLE *copy_series(const NETDATA_DOUBLE *series, size_t entries); +void sort_series(NETDATA_DOUBLE *series, size_t entries); #endif //NETDATA_STATISTICAL_H diff --git a/libnetdata/storage_number/storage_number.c b/libnetdata/storage_number/storage_number.c index 6a8b68c11..7511f3a79 100644 --- a/libnetdata/storage_number/storage_number.c +++ b/libnetdata/storage_number/storage_number.c @@ -176,7 +176,8 @@ int print_netdata_double(char *str, NETDATA_DOUBLE value) { #ifdef STORAGE_WITH_MATH fractional = modfndd(value, &integral) * 10000000.0; #else - fractional = ((unsigned long long)(value * 10000000ULL) % 10000000ULL); + integral = (NETDATA_DOUBLE)((unsigned long long)(value * 10000000ULL) / 10000000ULL); + fractional = (NETDATA_DOUBLE)((unsigned long long)(value * 10000000ULL) % 10000000ULL); #endif unsigned long long integral_int = (unsigned long long)integral; diff --git a/libnetdata/string/Makefile.am b/libnetdata/string/Makefile.am new file mode 100644 index 000000000..161784b8f --- /dev/null +++ b/libnetdata/string/Makefile.am @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +AUTOMAKE_OPTIONS = subdir-objects +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +dist_noinst_DATA = \ + README.md \ + $(NULL) diff --git a/libnetdata/string/README.md b/libnetdata/string/README.md new file mode 100644 index 000000000..e73ab2696 --- /dev/null +++ b/libnetdata/string/README.md @@ -0,0 +1,20 @@ + + +# STRING + +STRING provides a way to allocate and free text strings, while de-duplicating them. + +It can be used similarly to libc string functions: + + - `strdup()` and `strdupz()` become `string_strdupz()`. + - `strlen()` becomes `string_strlen()` (and it does not walkthrough the bytes of the string). + - `free()` and `freez()` become `string_freez()`. + +There is also a special `string_dup()` function that increases the reference counter of a STRING, avoiding the +index lookup to find it. + +Once there is a `STRING *`, the actual `const char *` can be accessed with `string2str()`. + +All STRING should be constant. Changing the contents of a `const char *` that has been acquired by `string2str()` should never happen. \ No newline at end of file diff --git a/libnetdata/string/string.c b/libnetdata/string/string.c new file mode 100644 index 000000000..a3f74b4ef --- /dev/null +++ b/libnetdata/string/string.c @@ -0,0 +1,595 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "../libnetdata.h" +#include + +typedef int32_t REFCOUNT; + +// ---------------------------------------------------------------------------- +// STRING implementation - dedup all STRING + +struct netdata_string { + uint32_t length; // the string length including the terminating '\0' + + REFCOUNT refcount; // how many times this string is used + // We use a signed number to be able to detect duplicate frees of a string. + // If at any point this goes below zero, we have a duplicate free. + + const char str[]; // the string itself, is appended to this structure +}; + +static struct string_hashtable { + Pvoid_t JudyHSArray; // the Judy array - hashtable + netdata_rwlock_t rwlock; // the R/W lock to protect the Judy array + + long int entries; // the number of entries in the index + long int active_references; // the number of active references alive + long int memory; // the memory used, without the JudyHS index + + size_t inserts; // the number of successful inserts to the index + size_t deletes; // the number of successful deleted from the index + size_t searches; // the number of successful searches in the index + size_t duplications; // when a string is referenced + size_t releases; // when a string is unreferenced + +#ifdef NETDATA_INTERNAL_CHECKS + // internal statistics + size_t found_deleted_on_search; + size_t found_available_on_search; + size_t found_deleted_on_insert; + size_t found_available_on_insert; + size_t spins; +#endif + +} string_base = { + .JudyHSArray = NULL, + .rwlock = NETDATA_RWLOCK_INITIALIZER, +}; + +#ifdef NETDATA_INTERNAL_CHECKS +#define string_internal_stats_add(var, val) __atomic_add_fetch(&string_base.var, val, __ATOMIC_RELAXED) +#else +#define string_internal_stats_add(var, val) do {;} while(0) +#endif + +#define string_stats_atomic_increment(var) __atomic_add_fetch(&string_base.var, 1, __ATOMIC_RELAXED) +#define string_stats_atomic_decrement(var) __atomic_sub_fetch(&string_base.var, 1, __ATOMIC_RELAXED) + +void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_t *entries, size_t *references, size_t *memory, size_t *duplications, size_t *releases) { + *inserts = string_base.inserts; + *deletes = string_base.deletes; + *searches = string_base.searches; + *entries = (size_t)string_base.entries; + *references = (size_t)string_base.active_references; + *memory = (size_t)string_base.memory; + *duplications = string_base.duplications; + *releases = string_base.releases; +} + +#define string_entry_acquire(se) __atomic_add_fetch(&((se)->refcount), 1, __ATOMIC_SEQ_CST); +#define string_entry_release(se) __atomic_sub_fetch(&((se)->refcount), 1, __ATOMIC_SEQ_CST); + +static inline bool string_entry_check_and_acquire(STRING *se) { + REFCOUNT expected, desired, count = 0; + do { + count++; + + expected = __atomic_load_n(&se->refcount, __ATOMIC_SEQ_CST); + + if(expected <= 0) { + // We cannot use this. + // The reference counter reached value zero, + // so another thread is deleting this. + string_internal_stats_add(spins, count - 1); + return false; + } + + desired = expected + 1; + } + while(!__atomic_compare_exchange_n(&se->refcount, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)); + + string_internal_stats_add(spins, count - 1); + + // statistics + // string_base.active_references is altered at the in string_strdupz() and string_freez() + string_stats_atomic_increment(duplications); + + return true; +} + +STRING *string_dup(STRING *string) { + if(unlikely(!string)) return NULL; + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(__atomic_load_n(&string->refcount, __ATOMIC_SEQ_CST) <= 0)) + fatal("STRING: tried to %s() a string that is freed (it has %d references).", __FUNCTION__, string->refcount); +#endif + + string_entry_acquire(string); + + // statistics + string_stats_atomic_increment(active_references); + string_stats_atomic_increment(duplications); + + return string; +} + +// Search the index and return an ACQUIRED string entry, or NULL +static inline STRING *string_index_search(const char *str, size_t length) { + STRING *string; + + // Find the string in the index + // With a read-lock so that multiple readers can use the index concurrently. + + netdata_rwlock_rdlock(&string_base.rwlock); + + Pvoid_t *Rc; + Rc = JudyHSGet(string_base.JudyHSArray, (void *)str, length); + if(likely(Rc)) { + // found in the hash table + string = *Rc; + + if(string_entry_check_and_acquire(string)) { + // we can use this entry + string_internal_stats_add(found_available_on_search, 1); + } + else { + // this entry is about to be deleted by another thread + // do not touch it, let it go... + string = NULL; + string_internal_stats_add(found_deleted_on_search, 1); + } + } + else { + // not found in the hash table + string = NULL; + } + + string_stats_atomic_increment(searches); + netdata_rwlock_unlock(&string_base.rwlock); + + return string; +} + +// Insert a string to the index and return an ACQUIRED string entry, +// or NULL if the call needs to be retried (a deleted entry with the same key is still in the index) +// The returned entry is ACQUIRED, and it can either be: +// 1. a new item inserted, or +// 2. an item found in the index that is not currently deleted +static inline STRING *string_index_insert(const char *str, size_t length) { + STRING *string; + + netdata_rwlock_wrlock(&string_base.rwlock); + + STRING **ptr; + { + JError_t J_Error; + Pvoid_t *Rc = JudyHSIns(&string_base.JudyHSArray, (void *)str, length, &J_Error); + if (unlikely(Rc == PJERR)) { + fatal( + "STRING: Cannot insert entry with name '%s' to JudyHS, JU_ERRNO_* == %u, ID == %d", + str, + JU_ERRNO(&J_Error), + JU_ERRID(&J_Error)); + } + ptr = (STRING **)Rc; + } + + if (likely(*ptr == 0)) { + // a new item added to the index + size_t mem_size = sizeof(STRING) + length; + string = mallocz(mem_size); + strcpy((char *)string->str, str); + string->length = length; + string->refcount = 1; + *ptr = string; + string_base.inserts++; + string_base.entries++; + string_base.memory += (long)mem_size; + } + else { + // the item is already in the index + string = *ptr; + + if(string_entry_check_and_acquire(string)) { + // we can use this entry + string_internal_stats_add(found_available_on_insert, 1); + } + else { + // this entry is about to be deleted by another thread + // do not touch it, let it go... + string = NULL; + string_internal_stats_add(found_deleted_on_insert, 1); + } + + string_stats_atomic_increment(searches); + } + + netdata_rwlock_unlock(&string_base.rwlock); + return string; +} + +// delete an entry from the index +static inline void string_index_delete(STRING *string) { + netdata_rwlock_wrlock(&string_base.rwlock); + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(__atomic_load_n(&string->refcount, __ATOMIC_SEQ_CST) != 0)) + fatal("STRING: tried to delete a string at %s() that is already freed (it has %d references).", __FUNCTION__, string->refcount); +#endif + + bool deleted = false; + + if (likely(string_base.JudyHSArray)) { + JError_t J_Error; + int ret = JudyHSDel(&string_base.JudyHSArray, (void *)string->str, string->length, &J_Error); + if (unlikely(ret == JERR)) { + error( + "STRING: Cannot delete entry with name '%s' from JudyHS, JU_ERRNO_* == %u, ID == %d", + string->str, + JU_ERRNO(&J_Error), + JU_ERRID(&J_Error)); + } else + deleted = true; + } + + if (unlikely(!deleted)) + error("STRING: tried to delete '%s' that is not in the index. Ignoring it.", string->str); + else { + size_t mem_size = sizeof(STRING) + string->length; + string_base.deletes++; + string_base.entries--; + string_base.memory -= (long)mem_size; + freez(string); + } + + netdata_rwlock_unlock(&string_base.rwlock); +} + +STRING *string_strdupz(const char *str) { + if(unlikely(!str || !*str)) return NULL; + + size_t length = strlen(str) + 1; + STRING *string = string_index_search(str, length); + + while(!string) { + // The search above did not find anything, + // We loop here, because during insert we may find an entry that is being deleted by another thread. + // So, we have to let it go and retry to insert it again. + + string = string_index_insert(str, length); + } + + // statistics + string_stats_atomic_increment(active_references); + + return string; +} + +void string_freez(STRING *string) { + if(unlikely(!string)) return; + + REFCOUNT refcount = string_entry_release(string); + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(refcount < 0)) + fatal("STRING: tried to %s() a string that is already freed (it has %d references).", __FUNCTION__, string->refcount); +#endif + + if(unlikely(refcount == 0)) + string_index_delete(string); + + // statistics + string_stats_atomic_decrement(active_references); + string_stats_atomic_increment(releases); +} + +size_t string_strlen(STRING *string) { + if(unlikely(!string)) return 0; + return string->length - 1; +} + +const char *string2str(STRING *string) { + if(unlikely(!string)) return ""; + return string->str; +} + +STRING *string_2way_merge(STRING *a, STRING *b) { + static STRING *X = NULL; + + if(unlikely(!X)) { + X = string_strdupz("[x]"); + } + + if(unlikely(a == b)) return string_dup(a); + if(unlikely(a == X)) return string_dup(a); + if(unlikely(b == X)) return string_dup(b); + if(unlikely(!a)) return string_dup(X); + if(unlikely(!b)) return string_dup(X); + + size_t alen = string_strlen(a); + size_t blen = string_strlen(b); + size_t length = alen + blen + string_strlen(X) + 1; + char buf1[length + 1], buf2[length + 1], *dst1; + const char *s1, *s2; + + s1 = string2str(a); + s2 = string2str(b); + dst1 = buf1; + for( ; *s1 && *s2 && *s1 == *s2 ;s1++, s2++) + *dst1++ = *s1; + + *dst1 = '\0'; + + if(*s1 != '\0' || *s2 != '\0') { + *dst1++ = '['; + *dst1++ = 'x'; + *dst1++ = ']'; + + s1 = &(string2str(a))[alen - 1]; + s2 = &(string2str(b))[blen - 1]; + char *dst2 = &buf2[length]; + *dst2 = '\0'; + for (; *s1 && *s2 && *s1 == *s2; s1--, s2--) + *(--dst2) = *s1; + + strcpy(dst1, dst2); + } + + return string_strdupz(buf1); +} + +// ---------------------------------------------------------------------------- +// STRING unit test + +struct thread_unittest { + int join; + int dups; +}; + +static void *string_thread(void *arg) { + struct thread_unittest *tu = arg; + + for(; 1 ;) { + if(__atomic_load_n(&tu->join, __ATOMIC_RELAXED)) + break; + + STRING *s = string_strdupz("string thread checking 1234567890"); + + for(int i = 0; i < tu->dups ; i++) + string_dup(s); + + for(int i = 0; i < tu->dups ; i++) + string_freez(s); + + string_freez(s); + } + + return arg; +} + +static char **string_unittest_generate_names(size_t entries) { + char **names = mallocz(sizeof(char *) * entries); + for(size_t i = 0; i < entries ;i++) { + char buf[25 + 1] = ""; + snprintfz(buf, 25, "name.%zu.0123456789.%zu \t !@#$%%^&*(),./[]{}\\|~`", i, entries / 2 + i); + names[i] = strdupz(buf); + } + return names; +} + +static void string_unittest_free_char_pp(char **pp, size_t entries) { + for(size_t i = 0; i < entries ;i++) + freez(pp[i]); + + freez(pp); +} + +int string_unittest(size_t entries) { + size_t errors = 0; + + fprintf(stderr, "Generating %zu names and values...\n", entries); + char **names = string_unittest_generate_names(entries); + + // check string + { + long int string_entries_starting = string_base.entries; + + fprintf(stderr, "\nChecking strings...\n"); + + STRING *s1 = string_strdupz("hello unittest"); + STRING *s2 = string_strdupz("hello unittest"); + if(s1 != s2) { + errors++; + fprintf(stderr, "ERROR: duplicating strings are not deduplicated\n"); + } + else + fprintf(stderr, "OK: duplicating string are deduplicated\n"); + + STRING *s3 = string_dup(s1); + if(s3 != s1) { + errors++; + fprintf(stderr, "ERROR: cloning strings are not deduplicated\n"); + } + else + fprintf(stderr, "OK: cloning string are deduplicated\n"); + + if(s1->refcount != 3) { + errors++; + fprintf(stderr, "ERROR: string refcount is not 3\n"); + } + else + fprintf(stderr, "OK: string refcount is 3\n"); + + STRING *s4 = string_strdupz("world unittest"); + if(s4 == s1) { + errors++; + fprintf(stderr, "ERROR: string is sharing pointers on different strings\n"); + } + else + fprintf(stderr, "OK: string is properly handling different strings\n"); + + usec_t start_ut, end_ut; + STRING **strings = mallocz(entries * sizeof(STRING *)); + + start_ut = now_realtime_usec(); + for(size_t i = 0; i < entries ;i++) { + strings[i] = string_strdupz(names[i]); + } + end_ut = now_realtime_usec(); + fprintf(stderr, "Created %zu strings in %llu usecs\n", entries, end_ut - start_ut); + + start_ut = now_realtime_usec(); + for(size_t i = 0; i < entries ;i++) { + strings[i] = string_dup(strings[i]); + } + end_ut = now_realtime_usec(); + fprintf(stderr, "Cloned %zu strings in %llu usecs\n", entries, end_ut - start_ut); + + start_ut = now_realtime_usec(); + for(size_t i = 0; i < entries ;i++) { + strings[i] = string_strdupz(string2str(strings[i])); + } + end_ut = now_realtime_usec(); + fprintf(stderr, "Found %zu existing strings in %llu usecs\n", entries, end_ut - start_ut); + + start_ut = now_realtime_usec(); + for(size_t i = 0; i < entries ;i++) { + string_freez(strings[i]); + } + end_ut = now_realtime_usec(); + fprintf(stderr, "Released %zu referenced strings in %llu usecs\n", entries, end_ut - start_ut); + + start_ut = now_realtime_usec(); + for(size_t i = 0; i < entries ;i++) { + string_freez(strings[i]); + } + end_ut = now_realtime_usec(); + fprintf(stderr, "Released (again) %zu referenced strings in %llu usecs\n", entries, end_ut - start_ut); + + start_ut = now_realtime_usec(); + for(size_t i = 0; i < entries ;i++) { + string_freez(strings[i]); + } + end_ut = now_realtime_usec(); + fprintf(stderr, "Freed %zu strings in %llu usecs\n", entries, end_ut - start_ut); + + freez(strings); + + if(string_base.entries != string_entries_starting + 2) { + errors++; + fprintf(stderr, "ERROR: strings dictionary should have %ld items but it has %ld\n", string_entries_starting + 2, string_base.entries); + } + else + fprintf(stderr, "OK: strings dictionary has 2 items\n"); + } + + // check 2-way merge + { + struct testcase { + char *src1; char *src2; char *expected; + } tests[] = { + { "", "", ""}, + { "a", "", "[x]"}, + { "", "a", "[x]"}, + { "a", "a", "a"}, + { "abcd", "abcd", "abcd"}, + { "foo_cs", "bar_cs", "[x]_cs"}, + { "cp_UNIQUE_INFIX_cs", "cp_unique_infix_cs", "cp_[x]_cs"}, + { "cp_UNIQUE_INFIX_ci_unique_infix_cs", "cp_unique_infix_ci_UNIQUE_INFIX_cs", "cp_[x]_cs"}, + { "foo[1234]", "foo[4321]", "foo[[x]]"}, + { NULL, NULL, NULL }, + }; + + for (struct testcase *tc = &tests[0]; tc->expected != NULL; tc++) { + STRING *src1 = string_strdupz(tc->src1); + STRING *src2 = string_strdupz(tc->src2); + STRING *expected = string_strdupz(tc->expected); + + STRING *result = string_2way_merge(src1, src2); + if (string_cmp(result, expected) != 0) { + fprintf(stderr, "string_2way_merge(\"%s\", \"%s\") -> \"%s\" (expected=\"%s\")\n", + string2str(src1), + string2str(src2), + string2str(result), + string2str(expected)); + errors++; + } + + string_freez(src1); + string_freez(src2); + string_freez(expected); + string_freez(result); + } + } + + // threads testing of string + { + struct thread_unittest tu = { + .dups = 1, + .join = 0, + }; + +#ifdef NETDATA_INTERNAL_CHECKS + size_t ofound_deleted_on_search = string_base.found_deleted_on_search, + ofound_available_on_search = string_base.found_available_on_search, + ofound_deleted_on_insert = string_base.found_deleted_on_insert, + ofound_available_on_insert = string_base.found_available_on_insert, + ospins = string_base.spins; +#endif + + size_t oinserts, odeletes, osearches, oentries, oreferences, omemory, oduplications, oreleases; + string_statistics(&oinserts, &odeletes, &osearches, &oentries, &oreferences, &omemory, &oduplications, &oreleases); + + time_t seconds_to_run = 5; + int threads_to_create = 2; + fprintf( + stderr, + "Checking string concurrency with %d threads for %lld seconds...\n", + threads_to_create, + (long long)seconds_to_run); + // check string concurrency + netdata_thread_t threads[threads_to_create]; + tu.join = 0; + for (int i = 0; i < threads_to_create; i++) { + char buf[100 + 1]; + snprintf(buf, 100, "string%d", i); + netdata_thread_create( + &threads[i], buf, NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, string_thread, &tu); + } + sleep_usec(seconds_to_run * USEC_PER_SEC); + + __atomic_store_n(&tu.join, 1, __ATOMIC_RELAXED); + for (int i = 0; i < threads_to_create; i++) { + void *retval; + netdata_thread_join(threads[i], &retval); + } + + size_t inserts, deletes, searches, sentries, references, memory, duplications, releases; + string_statistics(&inserts, &deletes, &searches, &sentries, &references, &memory, &duplications, &releases); + + fprintf(stderr, "inserts %zu, deletes %zu, searches %zu, entries %zu, references %zu, memory %zu, duplications %zu, releases %zu\n", + inserts - oinserts, deletes - odeletes, searches - osearches, sentries - oentries, references - oreferences, memory - omemory, duplications - oduplications, releases - oreleases); + +#ifdef NETDATA_INTERNAL_CHECKS + size_t found_deleted_on_search = string_base.found_deleted_on_search, + found_available_on_search = string_base.found_available_on_search, + found_deleted_on_insert = string_base.found_deleted_on_insert, + found_available_on_insert = string_base.found_available_on_insert, + spins = string_base.spins; + + fprintf(stderr, "on insert: %zu ok + %zu deleted\non search: %zu ok + %zu deleted\nspins: %zu\n", + found_available_on_insert - ofound_available_on_insert, + found_deleted_on_insert - ofound_deleted_on_insert, + found_available_on_search - ofound_available_on_search, + found_deleted_on_search - ofound_deleted_on_search, + spins - ospins + ); +#endif + } + + string_unittest_free_char_pp(names, entries); + + fprintf(stderr, "\n%zu errors found\n", errors); + return errors ? 1 : 0; +} diff --git a/libnetdata/string/string.h b/libnetdata/string/string.h new file mode 100644 index 000000000..cec44ebd9 --- /dev/null +++ b/libnetdata/string/string.h @@ -0,0 +1,30 @@ + +#ifndef NETDATA_STRING_H +#define NETDATA_STRING_H 1 + +#include "../libnetdata.h" + +// ---------------------------------------------------------------------------- +// STRING implementation + +typedef struct netdata_string STRING; +STRING *string_strdupz(const char *str); +STRING *string_dup(STRING *string); +void string_freez(STRING *string); +size_t string_strlen(STRING *string); +const char *string2str(STRING *string) NEVERNULL; + +// keep common prefix/suffix and replace everything else with [x] +STRING *string_2way_merge(STRING *a, STRING *b); + +static inline int string_cmp(STRING *s1, STRING *s2) { + // STRINGs are deduplicated, so the same strings have the same pointer + // when they differ, we do the typical strcmp() comparison + return (s1 == s2)?0:strcmp(string2str(s1), string2str(s2)); +} + +void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_t *entries, size_t *references, size_t *memory, size_t *duplications, size_t *releases); + +int string_unittest(size_t entries); + +#endif diff --git a/libnetdata/threads/threads.c b/libnetdata/threads/threads.c index 12007afff..5c3d2675c 100644 --- a/libnetdata/threads/threads.c +++ b/libnetdata/threads/threads.c @@ -29,26 +29,35 @@ const char *netdata_thread_tag(void) { // ---------------------------------------------------------------------------- // compatibility library functions +static __thread pid_t gettid_cached_tid = 0; pid_t gettid(void) { + pid_t tid = 0; + + if(likely(gettid_cached_tid > 0)) + return gettid_cached_tid; + #ifdef __FreeBSD__ - return (pid_t)pthread_getthreadid_np(); + tid = (pid_t)pthread_getthreadid_np(); #elif defined(__APPLE__) #if (defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) uint64_t curthreadid; pthread_threadid_np(NULL, &curthreadid); - return (pid_t)curthreadid; + tid = (pid_t)curthreadid; #else /* __MAC_OS_X_VERSION_MIN_REQUIRED */ - return (pid_t)pthread_self; + tid = (pid_t)pthread_self; #endif /* __MAC_OS_X_VERSION_MIN_REQUIRED */ #else /* __APPLE__*/ - return (pid_t)syscall(SYS_gettid); + tid = (pid_t)syscall(SYS_gettid); #endif /* __FreeBSD__, __APPLE__*/ + + gettid_cached_tid = tid; + return tid; } // ---------------------------------------------------------------------------- @@ -97,6 +106,10 @@ void netdata_threads_init_after_fork(size_t stacksize) { // ---------------------------------------------------------------------------- // netdata_thread_create +extern void rrdset_thread_rda_free(void); +extern void sender_thread_buffer_free(void); +extern void query_target_free(void); + static void thread_cleanup(void *ptr) { if(netdata_thread != ptr) { NETDATA_THREAD *info = (NETDATA_THREAD *)ptr; @@ -106,6 +119,11 @@ static void thread_cleanup(void *ptr) { if(!(netdata_thread->options & NETDATA_THREAD_OPTION_DONT_LOG_CLEANUP)) info("thread with task id %d finished", gettid()); + sender_thread_buffer_free(); + rrdset_thread_rda_free(); + query_target_free(); + thread_cache_destroy(); + freez((void *)netdata_thread->tag); netdata_thread->tag = NULL; @@ -213,11 +231,18 @@ int netdata_thread_create(netdata_thread_t *thread, const char *tag, NETDATA_THR // ---------------------------------------------------------------------------- // netdata_thread_cancel - +#ifdef NETDATA_INTERNAL_CHECKS +int netdata_thread_cancel_with_trace(netdata_thread_t thread, int line, const char *file, const char *function) { +#else int netdata_thread_cancel(netdata_thread_t thread) { +#endif int ret = pthread_cancel(thread); if(ret != 0) +#ifdef NETDATA_INTERNAL_CHECKS + error("cannot cancel thread. pthread_cancel() failed with code %d at %d@%s, function %s()", ret, line, file, function); +#else error("cannot cancel thread. pthread_cancel() failed with code %d.", ret); +#endif return ret; } diff --git a/libnetdata/threads/threads.h b/libnetdata/threads/threads.h index e7d79d328..ccc18aff0 100644 --- a/libnetdata/threads/threads.h +++ b/libnetdata/threads/threads.h @@ -5,7 +5,7 @@ #include "../libnetdata.h" -extern pid_t gettid(void); +pid_t gettid(void); typedef enum { NETDATA_THREAD_OPTION_DEFAULT = 0 << 0, @@ -21,20 +21,27 @@ typedef enum { typedef pthread_t netdata_thread_t; #define NETDATA_THREAD_TAG_MAX 100 -extern const char *netdata_thread_tag(void); -extern int netdata_thread_tag_exists(void); +const char *netdata_thread_tag(void); +int netdata_thread_tag_exists(void); -extern size_t netdata_threads_init(void); -extern void netdata_threads_init_after_fork(size_t stacksize); +size_t netdata_threads_init(void); +void netdata_threads_init_after_fork(size_t stacksize); -extern int netdata_thread_create(netdata_thread_t *thread, const char *tag, NETDATA_THREAD_OPTIONS options, void *(*start_routine) (void *), void *arg); -extern int netdata_thread_cancel(netdata_thread_t thread); -extern int netdata_thread_join(netdata_thread_t thread, void **retval); -extern int netdata_thread_detach(pthread_t thread); +int netdata_thread_create(netdata_thread_t *thread, const char *tag, NETDATA_THREAD_OPTIONS options, void *(*start_routine) (void *), void *arg); + +#ifdef NETDATA_INTERNAL_CHECKS +#define netdata_thread_cancel(thread) netdata_thread_cancel_with_trace(thread, __LINE__, __FILE__, __FUNCTION__) +int netdata_thread_cancel_with_trace(netdata_thread_t thread, int line, const char *file, const char *function); +#else +int netdata_thread_cancel(netdata_thread_t thread); +#endif + +int netdata_thread_join(netdata_thread_t thread, void **retval); +int netdata_thread_detach(pthread_t thread); #define NETDATA_THREAD_NAME_MAX 15 -extern void uv_thread_set_name_np(uv_thread_t ut, const char* name); -extern void os_thread_get_current_name_np(char threadname[NETDATA_THREAD_NAME_MAX + 1]); +void uv_thread_set_name_np(uv_thread_t ut, const char* name); +void os_thread_get_current_name_np(char threadname[NETDATA_THREAD_NAME_MAX + 1]); #define netdata_thread_self pthread_self #define netdata_thread_testcancel pthread_testcancel diff --git a/libnetdata/url/url.h b/libnetdata/url/url.h index 10f3fe176..da0f69ac1 100644 --- a/libnetdata/url/url.h +++ b/libnetdata/url/url.h @@ -10,26 +10,26 @@ // code from: http://www.geekhideout.com/urlcode.shtml /* Converts a hex character to its integer value */ -extern char from_hex(char ch); +char from_hex(char ch); /* Converts an integer value to its hex character*/ -extern char to_hex(char code); +char to_hex(char code); /* Returns a url-encoded version of str */ /* IMPORTANT: be sure to free() the returned string after use */ -extern char *url_encode(char *str); +char *url_encode(char *str); /* Returns a url-decoded version of str */ /* IMPORTANT: be sure to free() the returned string after use */ -extern char *url_decode(char *str); +char *url_decode(char *str); -extern char *url_decode_r(char *to, char *url, size_t size); +char *url_decode_r(char *to, char *url, size_t size); #define WEB_FIELDS_MAX 400 -extern int url_map_query_string(char **out, char *url); -extern int url_parse_query_string(char *output, size_t max, char **map, int total); +int url_map_query_string(char **out, char *url); +int url_parse_query_string(char *output, size_t max, char **map, int total); -extern int url_is_request_complete(char *begin,char *end,size_t length); -extern char *url_find_protocol(char *s); +int url_is_request_complete(char *begin,char *end,size_t length); +char *url_find_protocol(char *s); #endif /* NETDATA_URL_H */ diff --git a/libnetdata/worker_utilization/worker_utilization.c b/libnetdata/worker_utilization/worker_utilization.c index bd3ad60e0..14b8926e0 100644 --- a/libnetdata/worker_utilization/worker_utilization.c +++ b/libnetdata/worker_utilization/worker_utilization.c @@ -4,22 +4,26 @@ #define WORKER_BUSY 'B' struct worker_job_type { - char name[WORKER_UTILIZATION_MAX_JOB_NAME_LENGTH + 1]; + STRING *name; + STRING *units; // statistics controlled variables size_t statistics_last_jobs_started; usec_t statistics_last_busy_time; + NETDATA_DOUBLE statistics_last_custom_value; // worker controlled variables volatile size_t worker_jobs_started; volatile usec_t worker_busy_time; + + WORKER_METRIC_TYPE type; + NETDATA_DOUBLE custom_value; }; struct worker { pid_t pid; const char *tag; const char *workname; - uint32_t workname_hash; // statistics controlled variables volatile usec_t statistics_last_checkpoint; @@ -27,6 +31,7 @@ struct worker { usec_t statistics_last_busy_time; // the worker controlled variables + size_t worker_max_job_id; volatile size_t job_id; volatile size_t jobs_started; volatile usec_t busy_time; @@ -36,11 +41,12 @@ struct worker { struct worker_job_type per_job_type[WORKER_UTILIZATION_MAX_JOB_TYPES]; struct worker *next; + struct worker *prev; }; -static netdata_mutex_t base_lock = NETDATA_MUTEX_INITIALIZER; -static struct worker *base = NULL; +static netdata_mutex_t workers_base_lock = NETDATA_MUTEX_INITIALIZER; static __thread struct worker *worker = NULL; +static Pvoid_t workers_per_workname_JudyHS_array = NULL; void worker_register(const char *workname) { if(unlikely(worker)) return; @@ -49,47 +55,72 @@ void worker_register(const char *workname) { worker->pid = gettid(); worker->tag = strdupz(netdata_thread_tag()); worker->workname = strdupz(workname); - worker->workname_hash = simple_hash(worker->workname); - usec_t now = now_realtime_usec(); + usec_t now = now_monotonic_usec(); worker->statistics_last_checkpoint = now; worker->last_action_timestamp = now; worker->last_action = WORKER_IDLE; - netdata_mutex_lock(&base_lock); - worker->next = base; - base = worker; - netdata_mutex_unlock(&base_lock); + size_t workname_size = strlen(workname) + 1; + netdata_mutex_lock(&workers_base_lock); + + Pvoid_t *PValue = JudyHSGet(workers_per_workname_JudyHS_array, (void *)workname, workname_size); + if(!PValue) + PValue = JudyHSIns(&workers_per_workname_JudyHS_array, (void *)workname, workname_size, PJE0); + + struct worker *base = *PValue; + DOUBLE_LINKED_LIST_APPEND_UNSAFE(base, worker, prev, next); + *PValue = base; + + netdata_mutex_unlock(&workers_base_lock); } -void worker_register_job_name(size_t job_id, const char *name) { +void worker_register_job_custom_metric(size_t job_id, const char *name, const char *units, WORKER_METRIC_TYPE type) { if(unlikely(!worker)) return; if(unlikely(job_id >= WORKER_UTILIZATION_MAX_JOB_TYPES)) { error("WORKER_UTILIZATION: job_id %zu is too big. Max is %zu", job_id, (size_t)(WORKER_UTILIZATION_MAX_JOB_TYPES - 1)); return; } - if (*worker->per_job_type[job_id].name) { - error("WORKER_UTILIZATION: duplicate job registration: worker '%s' job id %zu is '%s', ignoring '%s'", worker->workname, job_id, worker->per_job_type[job_id].name, name); + + if(job_id > worker->worker_max_job_id) + worker->worker_max_job_id = job_id; + + if(worker->per_job_type[job_id].name) { + if(strcmp(string2str(worker->per_job_type[job_id].name), name) != 0 || worker->per_job_type[job_id].type != type || strcmp(string2str(worker->per_job_type[job_id].units), units) != 0) + error("WORKER_UTILIZATION: duplicate job registration: worker '%s' job id %zu is '%s', ignoring the later '%s'", worker->workname, job_id, string2str(worker->per_job_type[job_id].name), name); return; } - strncpy(worker->per_job_type[job_id].name, name, WORKER_UTILIZATION_MAX_JOB_NAME_LENGTH); + worker->per_job_type[job_id].name = string_strdupz(name); + worker->per_job_type[job_id].units = string_strdupz(units); + worker->per_job_type[job_id].type = type; +} + +void worker_register_job_name(size_t job_id, const char *name) { + worker_register_job_custom_metric(job_id, name, "", WORKER_METRIC_IDLE_BUSY); } void worker_unregister(void) { if(unlikely(!worker)) return; - netdata_mutex_lock(&base_lock); - if(base == worker) - base = worker->next; - else { - struct worker *p; - for(p = base; p && p->next && p->next != worker ;p = p->next); - if(p && p->next == worker) - p->next = worker->next; + size_t workname_size = strlen(worker->workname) + 1; + netdata_mutex_lock(&workers_base_lock); + Pvoid_t *PValue = JudyHSGet(workers_per_workname_JudyHS_array, (void *)worker->workname, workname_size); + if(PValue) { + struct worker *base = *PValue; + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(base, worker, prev, next); + *PValue = base; + + if(!base) + JudyHSDel(&workers_per_workname_JudyHS_array, (void *)worker->workname, workname_size, PJE0); + } + netdata_mutex_unlock(&workers_base_lock); + + for(int i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) { + string_freez(worker->per_job_type[i].name); + string_freez(worker->per_job_type[i].units); } - netdata_mutex_unlock(&base_lock); freez((void *)worker->tag); freez((void *)worker->workname); @@ -112,18 +143,16 @@ static inline void worker_is_idle_with_time(usec_t now) { } void worker_is_idle(void) { - if(unlikely(!worker)) return; - if(unlikely(worker->last_action != WORKER_BUSY)) return; + if(unlikely(!worker || worker->last_action != WORKER_BUSY)) return; - worker_is_idle_with_time(now_realtime_usec()); + worker_is_idle_with_time(now_monotonic_usec()); } void worker_is_busy(size_t job_id) { - if(unlikely(!worker)) return; - if(unlikely(job_id >= WORKER_UTILIZATION_MAX_JOB_TYPES)) - job_id = 0; + if(unlikely(!worker || job_id >= WORKER_UTILIZATION_MAX_JOB_TYPES)) + return; - usec_t now = now_realtime_usec(); + usec_t now = now_monotonic_usec(); if(worker->last_action == WORKER_BUSY) worker_is_idle_with_time(now); @@ -138,35 +167,112 @@ void worker_is_busy(size_t job_id) { worker->last_action = WORKER_BUSY; } +void worker_set_metric(size_t job_id, NETDATA_DOUBLE value) { + if(unlikely(!worker)) return; + if(unlikely(job_id >= WORKER_UTILIZATION_MAX_JOB_TYPES)) + return; + + switch(worker->per_job_type[job_id].type) { + case WORKER_METRIC_INCREMENT: + worker->per_job_type[job_id].custom_value += value; + break; + + case WORKER_METRIC_INCREMENTAL_TOTAL: + case WORKER_METRIC_ABSOLUTE: + default: + worker->per_job_type[job_id].custom_value = value; + break; + } +} // statistics interface -void workers_foreach(const char *workname, void (*callback)(void *data, pid_t pid, const char *thread_tag, size_t utilization_usec, size_t duration_usec, size_t jobs_started, size_t is_running, const char **job_types_names, size_t *job_types_jobs_started, usec_t *job_types_busy_time), void *data) { - netdata_mutex_lock(&base_lock); - uint32_t hash = simple_hash(workname); +void workers_foreach(const char *workname, void (*callback)( + void *data + , pid_t pid + , const char *thread_tag + , size_t max_job_id + , size_t utilization_usec + , size_t duration_usec + , size_t jobs_started, size_t is_running + , STRING **job_types_names + , STRING **job_types_units + , WORKER_METRIC_TYPE *job_metric_types + , size_t *job_types_jobs_started + , usec_t *job_types_busy_time + , NETDATA_DOUBLE *job_custom_values + ) + , void *data) { + netdata_mutex_lock(&workers_base_lock); usec_t busy_time, delta; size_t i, jobs_started, jobs_running; - struct worker *p; - for(p = base; p ; p = p->next) { - if(hash != p->workname_hash || strcmp(workname, p->workname)) continue; + size_t workname_size = strlen(workname) + 1; + struct worker *base = NULL; + Pvoid_t *PValue = JudyHSGet(workers_per_workname_JudyHS_array, (void *)workname, workname_size); + if(PValue) + base = *PValue; - usec_t now = now_realtime_usec(); + struct worker *p; + DOUBLE_LINKED_LIST_FOREACH_FORWARD(base, p, prev, next) { + usec_t now = now_monotonic_usec(); // find per job type statistics - const char *per_job_type_name[WORKER_UTILIZATION_MAX_JOB_TYPES]; + STRING *per_job_type_name[WORKER_UTILIZATION_MAX_JOB_TYPES]; + STRING *per_job_type_units[WORKER_UTILIZATION_MAX_JOB_TYPES]; + WORKER_METRIC_TYPE per_job_metric_type[WORKER_UTILIZATION_MAX_JOB_TYPES]; size_t per_job_type_jobs_started[WORKER_UTILIZATION_MAX_JOB_TYPES]; usec_t per_job_type_busy_time[WORKER_UTILIZATION_MAX_JOB_TYPES]; - for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) { - per_job_type_name[i] = p->per_job_type[i].name; - - size_t tmp_jobs_started = p->per_job_type[i].worker_jobs_started; - per_job_type_jobs_started[i] = tmp_jobs_started - p->per_job_type[i].statistics_last_jobs_started; - p->per_job_type[i].statistics_last_jobs_started = tmp_jobs_started; + NETDATA_DOUBLE per_job_custom_values[WORKER_UTILIZATION_MAX_JOB_TYPES]; - usec_t tmp_busy_time = p->per_job_type[i].worker_busy_time; - per_job_type_busy_time[i] = tmp_busy_time - p->per_job_type[i].statistics_last_busy_time; - p->per_job_type[i].statistics_last_busy_time = tmp_busy_time; + size_t max_job_id = p->worker_max_job_id; + for(i = 0; i <= max_job_id ;i++) { + per_job_type_name[i] = p->per_job_type[i].name; + per_job_type_units[i] = p->per_job_type[i].units; + per_job_metric_type[i] = p->per_job_type[i].type; + + switch(p->per_job_type[i].type) { + default: + case WORKER_METRIC_EMPTY: { + per_job_type_jobs_started[i] = 0; + per_job_type_busy_time[i] = 0; + per_job_custom_values[i] = NAN; + break; + } + + case WORKER_METRIC_IDLE_BUSY: { + size_t tmp_jobs_started = p->per_job_type[i].worker_jobs_started; + per_job_type_jobs_started[i] = tmp_jobs_started - p->per_job_type[i].statistics_last_jobs_started; + p->per_job_type[i].statistics_last_jobs_started = tmp_jobs_started; + + usec_t tmp_busy_time = p->per_job_type[i].worker_busy_time; + per_job_type_busy_time[i] = tmp_busy_time - p->per_job_type[i].statistics_last_busy_time; + p->per_job_type[i].statistics_last_busy_time = tmp_busy_time; + + per_job_custom_values[i] = NAN; + break; + } + + case WORKER_METRIC_ABSOLUTE: { + per_job_type_jobs_started[i] = 0; + per_job_type_busy_time[i] = 0; + + per_job_custom_values[i] = p->per_job_type[i].custom_value; + break; + } + + case WORKER_METRIC_INCREMENTAL_TOTAL: + case WORKER_METRIC_INCREMENT: { + per_job_type_jobs_started[i] = 0; + per_job_type_busy_time[i] = 0; + + NETDATA_DOUBLE tmp_custom_value = p->per_job_type[i].custom_value; + per_job_custom_values[i] = tmp_custom_value - p->per_job_type[i].statistics_last_custom_value; + p->per_job_type[i].statistics_last_custom_value = tmp_custom_value; + + break; + } + } } // get a copy of the worker variables @@ -203,8 +309,22 @@ void workers_foreach(const char *workname, void (*callback)(void *data, pid_t pi jobs_running = 1; } - callback(data, p->pid, p->tag, busy_time, delta, jobs_started, jobs_running, per_job_type_name, per_job_type_jobs_started, per_job_type_busy_time); + callback(data + , p->pid + , p->tag + , max_job_id + , busy_time + , delta + , jobs_started + , jobs_running + , per_job_type_name + , per_job_type_units + , per_job_metric_type + , per_job_type_jobs_started + , per_job_type_busy_time + , per_job_custom_values + ); } - netdata_mutex_unlock(&base_lock); + netdata_mutex_unlock(&workers_base_lock); } diff --git a/libnetdata/worker_utilization/worker_utilization.h b/libnetdata/worker_utilization/worker_utilization.h index 8f16fe054..04d24f1f7 100644 --- a/libnetdata/worker_utilization/worker_utilization.h +++ b/libnetdata/worker_utilization/worker_utilization.h @@ -6,17 +6,42 @@ // workers interfaces #define WORKER_UTILIZATION_MAX_JOB_TYPES 50 -#define WORKER_UTILIZATION_MAX_JOB_NAME_LENGTH 25 -extern void worker_register(const char *workname); -extern void worker_register_job_name(size_t job_id, const char *name); -extern void worker_unregister(void); +typedef enum { + WORKER_METRIC_EMPTY = 0, + WORKER_METRIC_IDLE_BUSY = 1, + WORKER_METRIC_ABSOLUTE = 2, + WORKER_METRIC_INCREMENT = 3, + WORKER_METRIC_INCREMENTAL_TOTAL = 4, +} WORKER_METRIC_TYPE; -extern void worker_is_idle(void); -extern void worker_is_busy(size_t job_id); +void worker_register(const char *workname); +void worker_register_job_name(size_t job_id, const char *name); +void worker_register_job_custom_metric(size_t job_id, const char *name, const char *units, WORKER_METRIC_TYPE type); +void worker_unregister(void); + +void worker_is_idle(void); +void worker_is_busy(size_t job_id); +void worker_set_metric(size_t job_id, NETDATA_DOUBLE value); // statistics interface -extern void workers_foreach(const char *workname, void (*callback)(void *data, pid_t pid, const char *thread_tag, size_t utilization_usec, size_t duration_usec, size_t jobs_started, size_t is_running, const char **job_types_names, size_t *job_types_jobs_started, usec_t *job_types_busy_time), void *data); +void workers_foreach(const char *workname, void (*callback)( + void *data + , pid_t pid + , const char *thread_tag + , size_t max_job_id + , size_t utilization_usec + , size_t duration_usec + , size_t jobs_started + , size_t is_running + , STRING **job_types_names + , STRING **job_types_units + , WORKER_METRIC_TYPE *job_metric_types + , size_t *job_types_jobs_started + , usec_t *job_types_busy_time + , NETDATA_DOUBLE *job_custom_values + ) + , void *data); #endif // WORKER_UTILIZATION_H diff --git a/ml/ADCharts.cc b/ml/ADCharts.cc new file mode 100644 index 000000000..00c593c0c --- /dev/null +++ b/ml/ADCharts.cc @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "ADCharts.h" +#include "Config.h" + +void ml::updateDimensionsChart(RRDHOST *RH, + collected_number NumTrainedDimensions, + collected_number NumNormalDimensions, + collected_number NumAnomalousDimensions) { + static thread_local RRDSET *RS = nullptr; + static thread_local RRDDIM *NumTotalDimensionsRD = nullptr; + static thread_local RRDDIM *NumTrainedDimensionsRD = nullptr; + static thread_local RRDDIM *NumNormalDimensionsRD = nullptr; + static thread_local RRDDIM *NumAnomalousDimensionsRD = nullptr; + + if (!RS) { + std::stringstream IdSS, NameSS; + + IdSS << "dimensions_on_" << localhost->machine_guid; + NameSS << "dimensions_on_" << localhost->hostname; + + RS = rrdset_create( + RH, + "anomaly_detection", // type + IdSS.str().c_str(), // id + NameSS.str().c_str(), // name + "dimensions", // family + "anomaly_detection.dimensions", // ctx + "Anomaly detection dimensions", // title + "dimensions", // units + "netdata", // plugin + "ml", // module + 39183, // priority + RH->rrd_update_every, // update_every + RRDSET_TYPE_LINE // chart_type + ); + rrdset_flag_set(RS, RRDSET_FLAG_ANOMALY_DETECTION); + + NumTotalDimensionsRD = rrddim_add(RS, "total", NULL, + 1, 1, RRD_ALGORITHM_ABSOLUTE); + NumTrainedDimensionsRD = rrddim_add(RS, "trained", NULL, + 1, 1, RRD_ALGORITHM_ABSOLUTE); + NumNormalDimensionsRD = rrddim_add(RS, "normal", NULL, + 1, 1, RRD_ALGORITHM_ABSOLUTE); + NumAnomalousDimensionsRD = rrddim_add(RS, "anomalous", NULL, + 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(RS, NumTotalDimensionsRD, NumNormalDimensions + NumAnomalousDimensions); + rrddim_set_by_pointer(RS, NumTrainedDimensionsRD, NumTrainedDimensions); + rrddim_set_by_pointer(RS, NumNormalDimensionsRD, NumNormalDimensions); + rrddim_set_by_pointer(RS, NumAnomalousDimensionsRD, NumAnomalousDimensions); + + rrdset_done(RS); +} + +void ml::updateHostAndDetectionRateCharts(RRDHOST *RH, collected_number AnomalyRate) { + static thread_local RRDSET *HostRateRS = nullptr; + static thread_local RRDDIM *AnomalyRateRD = nullptr; + + if (!HostRateRS) { + std::stringstream IdSS, NameSS; + + IdSS << "anomaly_rate_on_" << localhost->machine_guid; + NameSS << "anomaly_rate_on_" << localhost->hostname; + + HostRateRS = rrdset_create( + RH, + "anomaly_detection", // type + IdSS.str().c_str(), // id + NameSS.str().c_str(), // name + "anomaly_rate", // family + "anomaly_detection.anomaly_rate", // ctx + "Percentage of anomalous dimensions", // title + "percentage", // units + "netdata", // plugin + "ml", // module + 39184, // priority + RH->rrd_update_every, // update_every + RRDSET_TYPE_LINE // chart_type + ); + rrdset_flag_set(HostRateRS, RRDSET_FLAG_ANOMALY_DETECTION); + + AnomalyRateRD = rrddim_add(HostRateRS, "anomaly_rate", NULL, + 1, 100, RRD_ALGORITHM_ABSOLUTE); + } + + rrddim_set_by_pointer(HostRateRS, AnomalyRateRD, AnomalyRate); + rrdset_done(HostRateRS); + + static thread_local RRDSET *AnomalyDetectionRS = nullptr; + static thread_local RRDDIM *AboveThresholdRD = nullptr; + static thread_local RRDDIM *NewAnomalyEventRD = nullptr; + + if (!AnomalyDetectionRS) { + std::stringstream IdSS, NameSS; + + IdSS << "anomaly_detection_on_" << localhost->machine_guid; + NameSS << "anomaly_detection_on_" << localhost->hostname; + + AnomalyDetectionRS = rrdset_create( + RH, + "anomaly_detection", // type + IdSS.str().c_str(), // id + NameSS.str().c_str(), // name + "anomaly_detection", // family + "anomaly_detection.detector_events", // ctx + "Anomaly detection events", // title + "percentage", // units + "netdata", // plugin + "ml", // module + 39185, // priority + RH->rrd_update_every, // update_every + RRDSET_TYPE_LINE // chart_type + ); + rrdset_flag_set(AnomalyDetectionRS, RRDSET_FLAG_ANOMALY_DETECTION); + + AboveThresholdRD = rrddim_add(AnomalyDetectionRS, "above_threshold", NULL, + 1, 1, RRD_ALGORITHM_ABSOLUTE); + NewAnomalyEventRD = rrddim_add(AnomalyDetectionRS, "new_anomaly_event", NULL, + 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + /* + * Compute the values of the dimensions based on the host rate chart + */ + ONEWAYALLOC *OWA = onewayalloc_create(0); + time_t Now = now_realtime_sec(); + time_t Before = Now - RH->rrd_update_every; + time_t After = Before - Cfg.AnomalyDetectionQueryDuration; + RRDR_OPTIONS Options = static_cast(0x00000000); + + RRDR *R = rrd2rrdr_legacy( + OWA, HostRateRS, + 1 /* points wanted */, + After, + Before, + Cfg.AnomalyDetectionGroupingMethod, + 0 /* resampling time */, + Options, "anomaly_rate", + NULL /* group options */, + 0, /* timeout */ + 0, /* tier */ + QUERY_SOURCE_ML + ); + if(R) { + assert(R->d == 1 && R->n == 1 && R->rows == 1); + + static thread_local bool PrevAboveThreshold = false; + bool AboveThreshold = R->v[0] >= Cfg.HostAnomalyRateThreshold; + bool NewAnomalyEvent = AboveThreshold && !PrevAboveThreshold; + PrevAboveThreshold = AboveThreshold; + + rrddim_set_by_pointer(AnomalyDetectionRS, AboveThresholdRD, AboveThreshold); + rrddim_set_by_pointer(AnomalyDetectionRS, NewAnomalyEventRD, NewAnomalyEvent); + rrdset_done(AnomalyDetectionRS); + + rrdr_free(OWA, R); + } + onewayalloc_destroy(OWA); +} + +void ml::updateDetectionChart(RRDHOST *RH) { + static thread_local RRDSET *RS = nullptr; + static thread_local RRDDIM *UserRD, *SystemRD = nullptr; + + if (!RS) { + std::stringstream IdSS, NameSS; + + IdSS << "prediction_stats_" << RH->machine_guid; + NameSS << "prediction_stats_for_" << RH->hostname; + + RS = rrdset_create_localhost( + "netdata", // type + IdSS.str().c_str(), // id + NameSS.str().c_str(), // name + "ml", // family + "netdata.prediction_stats", // ctx + "Prediction thread CPU usage", // title + "milliseconds/s", // units + "netdata", // plugin + "ml", // module + 136000, // priority + RH->rrd_update_every, // update_every + RRDSET_TYPE_STACKED // chart_type + ); + + UserRD = rrddim_add(RS, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + SystemRD = rrddim_add(RS, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + } + + struct rusage TRU; + getrusage(RUSAGE_THREAD, &TRU); + + rrddim_set_by_pointer(RS, UserRD, TRU.ru_utime.tv_sec * 1000000ULL + TRU.ru_utime.tv_usec); + rrddim_set_by_pointer(RS, SystemRD, TRU.ru_stime.tv_sec * 1000000ULL + TRU.ru_stime.tv_usec); + rrdset_done(RS); +} + +void ml::updateTrainingChart(RRDHOST *RH, struct rusage *TRU) { + static thread_local RRDSET *RS = nullptr; + static thread_local RRDDIM *UserRD = nullptr; + static thread_local RRDDIM *SystemRD = nullptr; + + if (!RS) { + std::stringstream IdSS, NameSS; + + IdSS << "training_stats_" << RH->machine_guid; + NameSS << "training_stats_for_" << RH->hostname; + + RS = rrdset_create_localhost( + "netdata", // type + IdSS.str().c_str(), // id + NameSS.str().c_str(), // name + "ml", // family + "netdata.training_stats", // ctx + "Training thread CPU usage", // title + "milliseconds/s", // units + "netdata", // plugin + "ml", // module + 136001, // priority + RH->rrd_update_every, // update_every + RRDSET_TYPE_STACKED // chart_type + ); + + UserRD = rrddim_add(RS, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + SystemRD = rrddim_add(RS, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + } + + rrddim_set_by_pointer(RS, UserRD, TRU->ru_utime.tv_sec * 1000000ULL + TRU->ru_utime.tv_usec); + rrddim_set_by_pointer(RS, SystemRD, TRU->ru_stime.tv_sec * 1000000ULL + TRU->ru_stime.tv_usec); + rrdset_done(RS); +} diff --git a/ml/ADCharts.h b/ml/ADCharts.h new file mode 100644 index 000000000..0be324f7d --- /dev/null +++ b/ml/ADCharts.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef ML_ADCHARTS_H +#define ML_ADCHARTS_H + +#include "ml-private.h" + +namespace ml { + +void updateDimensionsChart(RRDHOST *RH, + collected_number NumTrainedDimensions, + collected_number NumNormalDimensions, + collected_number NumAnomalousDimensions); + +void updateHostAndDetectionRateCharts(RRDHOST *RH, collected_number AnomalyRate); + +void updateDetectionChart(RRDHOST *RH); + +void updateTrainingChart(RRDHOST *RH, struct rusage *TRU); + +} // namespace ml + +#endif /* ML_ADCHARTS_H */ diff --git a/ml/BitBufferCounter.cc b/ml/BitBufferCounter.cc deleted file mode 100644 index 5e1ab5aca..000000000 --- a/ml/BitBufferCounter.cc +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "BitBufferCounter.h" - -using namespace ml; - -std::vector BitBufferCounter::getBuffer() const { - std::vector Buffer; - - for (size_t Idx = start(); Idx != (start() + size()); Idx++) - Buffer.push_back(V[Idx % V.size()]); - - return Buffer; -} - -void BitBufferCounter::insert(bool Bit) { - if (N >= V.size()) - NumSetBits -= (V[start()] == true); - - NumSetBits += (Bit == true); - V[N++ % V.size()] = Bit; -} - -void BitBufferCounter::print(std::ostream &OS) const { - std::vector Buffer = getBuffer(); - - for (bool B : Buffer) - OS << B; -} diff --git a/ml/BitBufferCounter.h b/ml/BitBufferCounter.h deleted file mode 100644 index db924d776..000000000 --- a/ml/BitBufferCounter.h +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef BIT_BUFFER_COUNTER_H -#define BIT_BUFFER_COUNTER_H - -#include "ml-private.h" - -namespace ml { - -class BitBufferCounter { -public: - BitBufferCounter(size_t Capacity) : V(Capacity, 0), NumSetBits(0), N(0) {} - - std::vector getBuffer() const; - - void insert(bool Bit); - - void print(std::ostream &OS) const; - - bool isFilled() const { - return N >= V.size(); - } - - size_t numSetBits() const { - return NumSetBits; - } - -private: - inline size_t size() const { - return N < V.size() ? N : V.size(); - } - - inline size_t start() const { - if (N <= V.size()) - return 0; - - return N % V.size(); - } - -private: - std::vector V; - size_t NumSetBits; - - size_t N; -}; - -} // namespace ml - -inline std::ostream& operator<<(std::ostream &OS, const ml::BitBufferCounter &BBC) { - BBC.print(OS); - return OS; -} - -#endif /* BIT_BUFFER_COUNTER_H */ diff --git a/ml/BitRateWindow.cc b/ml/BitRateWindow.cc deleted file mode 100644 index c4c994c42..000000000 --- a/ml/BitRateWindow.cc +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "BitRateWindow.h" - -using namespace ml; - -std::pair BitRateWindow::insert(bool Bit) { - Edge E; - - BBC.insert(Bit); - switch (CurrState) { - case State::NotFilled: { - if (BBC.isFilled()) { - if (BBC.numSetBits() < SetBitsThreshold) { - CurrState = State::BelowThreshold; - } else { - CurrState = State::AboveThreshold; - } - } else { - CurrState = State::NotFilled; - } - - E = {State::NotFilled, CurrState}; - break; - } case State::BelowThreshold: { - if (BBC.numSetBits() >= SetBitsThreshold) { - CurrState = State::AboveThreshold; - } - - E = {State::BelowThreshold, CurrState}; - break; - } case State::AboveThreshold: { - if ((BBC.numSetBits() < SetBitsThreshold) || - (CurrLength == MaxLength)) { - CurrState = State::Idle; - } - - E = {State::AboveThreshold, CurrState}; - break; - } case State::Idle: { - if (CurrLength == IdleLength) { - CurrState = State::NotFilled; - } - - E = {State::Idle, CurrState}; - break; - } - } - - Action A = EdgeActions[E]; - size_t L = (this->*A)(E.first, Bit); - return {E, L}; -} - -void BitRateWindow::print(std::ostream &OS) const { - switch (CurrState) { - case State::NotFilled: - OS << "NotFilled"; - break; - case State::BelowThreshold: - OS << "BelowThreshold"; - break; - case State::AboveThreshold: - OS << "AboveThreshold"; - break; - case State::Idle: - OS << "Idle"; - break; - default: - OS << "UnknownState"; - break; - } - - OS << ": " << BBC << " (Current Length: " << CurrLength << ")"; -} diff --git a/ml/BitRateWindow.h b/ml/BitRateWindow.h deleted file mode 100644 index 0d99008b8..000000000 --- a/ml/BitRateWindow.h +++ /dev/null @@ -1,170 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef BIT_RATE_WINDOW_H -#define BIT_RATE_WINDOW_H - -#include "BitBufferCounter.h" -#include "ml-private.h" - -namespace ml { - -class BitRateWindow { -public: - enum class State { - NotFilled, - BelowThreshold, - AboveThreshold, - Idle - }; - - using Edge = std::pair; - using Action = size_t (BitRateWindow::*)(State PrevState, bool NewBit); - -private: - std::map EdgeActions = { - // From == To - { - Edge(State::NotFilled, State::NotFilled), - &BitRateWindow::onRoundtripNotFilled, - }, - { - Edge(State::BelowThreshold, State::BelowThreshold), - &BitRateWindow::onRoundtripBelowThreshold, - }, - { - Edge(State::AboveThreshold, State::AboveThreshold), - &BitRateWindow::onRoundtripAboveThreshold, - }, - { - Edge(State::Idle, State::Idle), - &BitRateWindow::onRoundtripIdle, - }, - - - // NotFilled => {BelowThreshold, AboveThreshold} - { - Edge(State::NotFilled, State::BelowThreshold), - &BitRateWindow::onNotFilledToBelowThreshold - }, - { - Edge(State::NotFilled, State::AboveThreshold), - &BitRateWindow::onNotFilledToAboveThreshold - }, - - // BelowThreshold => AboveThreshold - { - Edge(State::BelowThreshold, State::AboveThreshold), - &BitRateWindow::onBelowToAboveThreshold - }, - - // AboveThreshold => Idle - { - Edge(State::AboveThreshold, State::Idle), - &BitRateWindow::onAboveThresholdToIdle - }, - - // Idle => NotFilled - { - Edge(State::Idle, State::NotFilled), - &BitRateWindow::onIdleToNotFilled - }, - }; - -public: - BitRateWindow(size_t MinLength, size_t MaxLength, size_t IdleLength, - size_t SetBitsThreshold) : - MinLength(MinLength), MaxLength(MaxLength), IdleLength(IdleLength), - SetBitsThreshold(SetBitsThreshold), - CurrState(State::NotFilled), CurrLength(0), BBC(MinLength) {} - - std::pair insert(bool Bit); - - void print(std::ostream &OS) const; - -private: - size_t onRoundtripNotFilled(State PrevState, bool NewBit) { - (void) PrevState, (void) NewBit; - - CurrLength += 1; - return CurrLength; - } - - size_t onRoundtripBelowThreshold(State PrevState, bool NewBit) { - (void) PrevState, (void) NewBit; - - CurrLength = MinLength; - return CurrLength; - } - - size_t onRoundtripAboveThreshold(State PrevState, bool NewBit) { - (void) PrevState, (void) NewBit; - - CurrLength += 1; - return CurrLength; - } - - size_t onRoundtripIdle(State PrevState, bool NewBit) { - (void) PrevState, (void) NewBit; - - CurrLength += 1; - return CurrLength; - } - - size_t onNotFilledToBelowThreshold(State PrevState, bool NewBit) { - (void) PrevState, (void) NewBit; - - CurrLength = MinLength; - return CurrLength; - } - - size_t onNotFilledToAboveThreshold(State PrevState, bool NewBit) { - (void) PrevState, (void) NewBit; - - CurrLength += 1; - return CurrLength; - } - - size_t onBelowToAboveThreshold(State PrevState, bool NewBit) { - (void) PrevState, (void) NewBit; - - CurrLength = MinLength; - return CurrLength; - } - - size_t onAboveThresholdToIdle(State PrevState, bool NewBit) { - (void) PrevState, (void) NewBit; - - size_t PrevLength = CurrLength; - CurrLength = 1; - return PrevLength; - } - - size_t onIdleToNotFilled(State PrevState, bool NewBit) { - (void) PrevState, (void) NewBit; - - BBC = BitBufferCounter(MinLength); - BBC.insert(NewBit); - - CurrLength = 1; - return CurrLength; - } - -private: - size_t MinLength; - size_t MaxLength; - size_t IdleLength; - size_t SetBitsThreshold; - - State CurrState; - size_t CurrLength; - BitBufferCounter BBC; -}; - -} // namespace ml - -inline std::ostream& operator<<(std::ostream &OS, const ml::BitRateWindow BRW) { - BRW.print(OS); - return OS; -} - -#endif /* BIT_RATE_WINDOW_H */ diff --git a/ml/Config.cc b/ml/Config.cc index 65b05a34d..eedd8c29f 100644 --- a/ml/Config.cc +++ b/ml/Config.cc @@ -31,8 +31,7 @@ void Config::readMLConfig(void) { unsigned MaxTrainSamples = config_get_number(ConfigSectionML, "maximum num samples to train", 4 * 3600); unsigned MinTrainSamples = config_get_number(ConfigSectionML, "minimum num samples to train", 1 * 900); unsigned TrainEvery = config_get_number(ConfigSectionML, "train every", 1 * 3600); - - unsigned DBEngineAnomalyRateEvery = config_get_number(ConfigSectionML, "dbengine anomaly rate every", 30); + unsigned NumModelsToUse = config_get_number(ConfigSectionML, "number of models per dimension", 1 * 24); unsigned DiffN = config_get_number(ConfigSectionML, "num samples to diff", 1); unsigned SmoothN = config_get_number(ConfigSectionML, "num samples to smooth", 3); @@ -42,27 +41,19 @@ void Config::readMLConfig(void) { unsigned MaxKMeansIters = config_get_number(ConfigSectionML, "maximum number of k-means iterations", 1000); double DimensionAnomalyScoreThreshold = config_get_float(ConfigSectionML, "dimension anomaly score threshold", 0.99); - double HostAnomalyRateThreshold = config_get_float(ConfigSectionML, "host anomaly rate threshold", 0.01); - - double ADMinWindowSize = config_get_float(ConfigSectionML, "minimum window size", 30); - double ADMaxWindowSize = config_get_float(ConfigSectionML, "maximum window size", 600); - double ADIdleWindowSize = config_get_float(ConfigSectionML, "idle window size", 30); - double ADWindowRateThreshold = config_get_float(ConfigSectionML, "window minimum anomaly rate", 0.25); - double ADDimensionRateThreshold = config_get_float(ConfigSectionML, "anomaly event min dimension rate threshold", 0.05); - std::stringstream SS; - SS << netdata_configured_cache_dir << "/anomaly-detection.db"; - Cfg.AnomalyDBPath = SS.str(); + double HostAnomalyRateThreshold = config_get_float(ConfigSectionML, "host anomaly rate threshold", 1.0); + std::string AnomalyDetectionGroupingMethod = config_get(ConfigSectionML, "anomaly detection grouping method", "average"); + time_t AnomalyDetectionQueryDuration = config_get_number(ConfigSectionML, "anomaly detection grouping duration", 5 * 60); /* * Clamp */ - MaxTrainSamples = clamp(MaxTrainSamples, 1 * 3600u, 24 * 3600u); - MinTrainSamples = clamp(MinTrainSamples, 1 * 900u, 6 * 3600u); - TrainEvery = clamp(TrainEvery, 1 * 3600u, 6 * 3600u); - - DBEngineAnomalyRateEvery = clamp(DBEngineAnomalyRateEvery, 1 * 30u, 15 * 60u); + MaxTrainSamples = clamp(MaxTrainSamples, 1 * 3600, 24 * 3600); + MinTrainSamples = clamp(MinTrainSamples, 1 * 900, 6 * 3600); + TrainEvery = clamp(TrainEvery, 1 * 3600, 6 * 3600); + NumModelsToUse = clamp(TrainEvery, 1, 7 * 24); DiffN = clamp(DiffN, 0u, 1u); SmoothN = clamp(SmoothN, 0u, 5u); @@ -72,13 +63,9 @@ void Config::readMLConfig(void) { MaxKMeansIters = clamp(MaxKMeansIters, 500u, 1000u); DimensionAnomalyScoreThreshold = clamp(DimensionAnomalyScoreThreshold, 0.01, 5.00); - HostAnomalyRateThreshold = clamp(HostAnomalyRateThreshold, 0.01, 1.0); - ADMinWindowSize = clamp(ADMinWindowSize, 30.0, 300.0); - ADMaxWindowSize = clamp(ADMaxWindowSize, 60.0, 900.0); - ADIdleWindowSize = clamp(ADIdleWindowSize, 30.0, 900.0); - ADWindowRateThreshold = clamp(ADWindowRateThreshold, 0.01, 0.99); - ADDimensionRateThreshold = clamp(ADDimensionRateThreshold, 0.01, 0.99); + HostAnomalyRateThreshold = clamp(HostAnomalyRateThreshold, 0.1, 10.0); + AnomalyDetectionQueryDuration = clamp(AnomalyDetectionQueryDuration, 60, 15 * 60); /* * Validate @@ -91,13 +78,6 @@ void Config::readMLConfig(void) { MaxTrainSamples = 4 * 3600; } - if (ADMinWindowSize >= ADMaxWindowSize) { - error("invalid min/max anomaly window size found (%lf >= %lf)", ADMinWindowSize, ADMaxWindowSize); - - ADMinWindowSize = 30.0; - ADMaxWindowSize = 600.0; - } - /* * Assign to config instance */ @@ -107,8 +87,7 @@ void Config::readMLConfig(void) { Cfg.MaxTrainSamples = MaxTrainSamples; Cfg.MinTrainSamples = MinTrainSamples; Cfg.TrainEvery = TrainEvery; - - Cfg.DBEngineAnomalyRateEvery = DBEngineAnomalyRateEvery; + Cfg.NumModelsToUse = NumModelsToUse; Cfg.DiffN = DiffN; Cfg.SmoothN = SmoothN; @@ -118,13 +97,10 @@ void Config::readMLConfig(void) { Cfg.MaxKMeansIters = MaxKMeansIters; Cfg.DimensionAnomalyScoreThreshold = DimensionAnomalyScoreThreshold; - Cfg.HostAnomalyRateThreshold = HostAnomalyRateThreshold; - Cfg.ADMinWindowSize = ADMinWindowSize; - Cfg.ADMaxWindowSize = ADMaxWindowSize; - Cfg.ADIdleWindowSize = ADIdleWindowSize; - Cfg.ADWindowRateThreshold = ADWindowRateThreshold; - Cfg.ADDimensionRateThreshold = ADDimensionRateThreshold; + Cfg.HostAnomalyRateThreshold = HostAnomalyRateThreshold; + Cfg.AnomalyDetectionGroupingMethod = web_client_api_request_v1_data_group(AnomalyDetectionGroupingMethod.c_str(), RRDR_GROUPING_AVERAGE); + Cfg.AnomalyDetectionQueryDuration = AnomalyDetectionQueryDuration; Cfg.HostsToSkip = config_get(ConfigSectionML, "hosts to skip from training", "!*"); Cfg.SP_HostsToSkip = simple_pattern_create(Cfg.HostsToSkip.c_str(), NULL, SIMPLE_PATTERN_EXACT); diff --git a/ml/Config.h b/ml/Config.h index 595fd072b..d876d4aa4 100644 --- a/ml/Config.h +++ b/ml/Config.h @@ -14,6 +14,7 @@ public: unsigned MaxTrainSamples; unsigned MinTrainSamples; unsigned TrainEvery; + unsigned NumModelsToUse; unsigned DBEngineAnomalyRateEvery; @@ -25,13 +26,10 @@ public: unsigned MaxKMeansIters; double DimensionAnomalyScoreThreshold; - double HostAnomalyRateThreshold; - double ADMinWindowSize; - double ADMaxWindowSize; - double ADIdleWindowSize; - double ADWindowRateThreshold; - double ADDimensionRateThreshold; + double HostAnomalyRateThreshold; + RRDR_GROUPING AnomalyDetectionGroupingMethod; + time_t AnomalyDetectionQueryDuration; bool StreamADCharts; @@ -41,7 +39,6 @@ public: std::string ChartsToSkip; SIMPLE_PATTERN *SP_ChartsToSkip; - std::string AnomalyDBPath; std::vector RandomNums; void readMLConfig(); diff --git a/ml/Database.cc b/ml/Database.cc deleted file mode 100644 index 06d0cdecb..000000000 --- a/ml/Database.cc +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "Database.h" - -const char *ml::Database::SQL_CREATE_ANOMALIES_TABLE = - "CREATE TABLE IF NOT EXISTS anomaly_events( " - " anomaly_detector_name text NOT NULL, " - " anomaly_detector_version int NOT NULL, " - " host_id text NOT NULL, " - " after int NOT NULL, " - " before int NOT NULL, " - " anomaly_event_info text, " - " PRIMARY KEY( " - " anomaly_detector_name, anomaly_detector_version, " - " host_id, after, before " - " ) " - ");"; - -const char *ml::Database::SQL_INSERT_ANOMALY = - "INSERT INTO anomaly_events( " - " anomaly_detector_name, anomaly_detector_version, " - " host_id, after, before, anomaly_event_info) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6);"; - -const char *ml::Database::SQL_SELECT_ANOMALY = - "SELECT anomaly_event_info FROM anomaly_events WHERE" - " anomaly_detector_name == ?1 AND" - " anomaly_detector_version == ?2 AND" - " host_id == ?3 AND" - " after == ?4 AND" - " before == ?5;"; - -const char *ml::Database::SQL_SELECT_ANOMALY_EVENTS = - "SELECT after, before FROM anomaly_events WHERE" - " anomaly_detector_name == ?1 AND" - " anomaly_detector_version == ?2 AND" - " host_id == ?3 AND" - " after >= ?4 AND" - " before <= ?5;"; - -using namespace ml; - -bool Statement::prepare(sqlite3 *Conn) { - if (!Conn) - return false; - - if (ParsedStmt) - return true; - - int RC = sqlite3_prepare_v2(Conn, RawStmt, -1, &ParsedStmt, nullptr); - if (RC == SQLITE_OK) - return true; - - std::string Msg = "Statement \"%s\" preparation failed due to \"%s\""; - error(Msg.c_str(), RawStmt, sqlite3_errstr(RC)); - - return false; -} - -bool Statement::bindValue(size_t Pos, const std::string &Value) { - int RC = sqlite3_bind_text(ParsedStmt, Pos, Value.c_str(), -1, SQLITE_TRANSIENT); - if (RC == SQLITE_OK) - return true; - - error("Failed to bind text '%s' (pos = %zu) in statement '%s'.", Value.c_str(), Pos, RawStmt); - return false; -} - -bool Statement::bindValue(size_t Pos, const int Value) { - int RC = sqlite3_bind_int(ParsedStmt, Pos, Value); - if (RC == SQLITE_OK) - return true; - - error("Failed to bind integer %d (pos = %zu) in statement '%s'.", Value, Pos, RawStmt); - return false; -} - -bool Statement::resetAndClear(bool Ret) { - int RC = sqlite3_reset(ParsedStmt); - if (RC != SQLITE_OK) { - error("Could not reset statement: '%s'", RawStmt); - return false; - } - - RC = sqlite3_clear_bindings(ParsedStmt); - if (RC != SQLITE_OK) { - error("Could not clear bindings in statement: '%s'", RawStmt); - return false; - } - - return Ret; -} - -Database::Database(const std::string &Path) { - // Get sqlite3 connection handle. - int RC = sqlite3_open(Path.c_str(), &Conn); - if (RC != SQLITE_OK) { - std::string Msg = "Failed to initialize ML DB at %s, due to \"%s\""; - error(Msg.c_str(), Path.c_str(), sqlite3_errstr(RC)); - - sqlite3_close(Conn); - Conn = nullptr; - return; - } - - // Create anomaly events table if it does not exist. - char *ErrMsg; - RC = sqlite3_exec(Conn, SQL_CREATE_ANOMALIES_TABLE, nullptr, nullptr, &ErrMsg); - if (RC == SQLITE_OK) - return; - - error("SQLite error during database initialization, rc = %d (%s)", RC, ErrMsg); - error("SQLite failed statement: %s", SQL_CREATE_ANOMALIES_TABLE); - - sqlite3_free(ErrMsg); - sqlite3_close(Conn); - Conn = nullptr; -} - -Database::~Database() { - if (!Conn) - return; - - int RC = sqlite3_close(Conn); - if (RC != SQLITE_OK) - error("Could not close connection properly (rc=%d)", RC); -} diff --git a/ml/Database.h b/ml/Database.h deleted file mode 100644 index cc7b75872..000000000 --- a/ml/Database.h +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef ML_DATABASE_H -#define ML_DATABASE_H - -#include "Dimension.h" -#include "ml-private.h" - -#include "json/single_include/nlohmann/json.hpp" - -namespace ml { - -class Statement { -public: - using RowCallback = std::function; - -public: - Statement(const char *RawStmt) : RawStmt(RawStmt), ParsedStmt(nullptr) {} - - template - bool exec(sqlite3 *Conn, RowCallback RowCb, ArgTypes ...Args) { - if (!prepare(Conn)) - return false; - - switch (bind(1, Args...)) { - case 0: - return false; - case sizeof...(Args): - break; - default: - return resetAndClear(false); - } - - while (true) { - switch (int RC = sqlite3_step(ParsedStmt)) { - case SQLITE_BUSY: case SQLITE_LOCKED: - usleep(SQLITE_INSERT_DELAY * USEC_PER_MS); - continue; - case SQLITE_ROW: - RowCb(ParsedStmt); - continue; - case SQLITE_DONE: - return resetAndClear(true); - default: - error("Stepping through '%s' returned rc=%d", RawStmt, RC); - return resetAndClear(false); - } - } - } - - ~Statement() { - if (!ParsedStmt) - return; - - int RC = sqlite3_finalize(ParsedStmt); - if (RC != SQLITE_OK) - error("Could not properly finalize statement (rc=%d)", RC); - } - -private: - bool prepare(sqlite3 *Conn); - - bool bindValue(size_t Pos, const int Value); - bool bindValue(size_t Pos, const std::string &Value); - - template - size_t bind(size_t Pos, ArgType T) { - return bindValue(Pos, T); - } - - template - size_t bind(size_t Pos, ArgType T, ArgTypes ...Args) { - return bindValue(Pos, T) + bind(Pos + 1, Args...); - } - - bool resetAndClear(bool Ret); - -private: - const char *RawStmt; - sqlite3_stmt *ParsedStmt; -}; - -class Database { -private: - static const char *SQL_CREATE_ANOMALIES_TABLE; - static const char *SQL_INSERT_ANOMALY; - static const char *SQL_SELECT_ANOMALY; - static const char *SQL_SELECT_ANOMALY_EVENTS; - -public: - Database(const std::string &Path); - - ~Database(); - - template - bool insertAnomaly(ArgTypes... Args) { - Statement::RowCallback RowCb = [](sqlite3_stmt *Stmt) { (void) Stmt; }; - return InsertAnomalyStmt.exec(Conn, RowCb, Args...); - } - - template - bool getAnomalyInfo(nlohmann::json &Json, ArgTypes&&... Args) { - Statement::RowCallback RowCb = [&](sqlite3_stmt *Stmt) { - const char *Text = static_cast(sqlite3_column_blob(Stmt, 0)); - Json = nlohmann::json::parse(Text); - }; - return GetAnomalyInfoStmt.exec(Conn, RowCb, Args...); - } - - template - bool getAnomaliesInRange(std::vector> &V, ArgTypes&&... Args) { - Statement::RowCallback RowCb = [&](sqlite3_stmt *Stmt) { - V.push_back({ - sqlite3_column_int64(Stmt, 0), - sqlite3_column_int64(Stmt, 1) - }); - }; - return GetAnomaliesInRangeStmt.exec(Conn, RowCb, Args...); - } - -private: - sqlite3 *Conn; - - Statement InsertAnomalyStmt{SQL_INSERT_ANOMALY}; - Statement GetAnomalyInfoStmt{SQL_SELECT_ANOMALY}; - Statement GetAnomaliesInRangeStmt{SQL_SELECT_ANOMALY_EVENTS}; -}; - -} - -#endif /* ML_DATABASE_H */ diff --git a/ml/Dimension.cc b/ml/Dimension.cc index 0fe07530c..bf34abb72 100644 --- a/ml/Dimension.cc +++ b/ml/Dimension.cc @@ -6,8 +6,13 @@ using namespace ml; -std::pair -TrainableDimension::getCalculatedNumbers() { +bool Dimension::isActive() const { + bool SetObsolete = rrdset_flag_check(RD->rrdset, RRDSET_FLAG_OBSOLETE); + bool DimObsolete = rrddim_flag_check(RD, RRDDIM_FLAG_OBSOLETE); + return !SetObsolete && !DimObsolete; +} + +std::pair Dimension::getCalculatedNumbers() { size_t MinN = Cfg.MinTrainSamples; size_t MaxN = Cfg.MaxTrainSamples; @@ -68,7 +73,7 @@ TrainableDimension::getCalculatedNumbers() { return { CNs, TotalValues }; } -MLResult TrainableDimension::trainModel() { +MLResult Dimension::trainModel() { auto P = getCalculatedNumbers(); CalculatedNumber *CNs = P.first; unsigned N = P.second; @@ -81,7 +86,15 @@ MLResult TrainableDimension::trainModel() { SamplesBuffer SB = SamplesBuffer(CNs, N, 1, Cfg.DiffN, Cfg.SmoothN, Cfg.LagN, SamplingRatio, Cfg.RandomNums); - KM.train(SB, Cfg.MaxKMeansIters); + std::vector Samples = SB.preprocess(); + + KMeans KM; + KM.train(Samples, Cfg.MaxKMeansIters); + + { + std::lock_guard Lock(Mutex); + Models[0] = KM; + } Trained = true; ConstantModel = true; @@ -90,16 +103,25 @@ MLResult TrainableDimension::trainModel() { return MLResult::Success; } -void PredictableDimension::addValue(CalculatedNumber Value, bool Exists) { +bool Dimension::shouldTrain(const TimePoint &TP) const { + if (ConstantModel) + return false; + + return (LastTrainedAt + Seconds(Cfg.TrainEvery * updateEvery())) < TP; +} + +bool Dimension::predict(CalculatedNumber Value, bool Exists) { if (!Exists) { CNs.clear(); - return; + AnomalyBit = false; + return false; } unsigned N = Cfg.DiffN + Cfg.SmoothN + Cfg.LagN; if (CNs.size() < N) { CNs.push_back(Value); - return; + AnomalyBit = false; + return false; } std::rotate(std::begin(CNs), std::begin(CNs) + 1, std::end(CNs)); @@ -108,28 +130,44 @@ void PredictableDimension::addValue(CalculatedNumber Value, bool Exists) { ConstantModel = false; CNs[N - 1] = Value; -} -std::pair PredictableDimension::predict() { - unsigned N = Cfg.DiffN + Cfg.SmoothN + Cfg.LagN; - if (CNs.size() != N) { + if (!isTrained() || ConstantModel) { AnomalyBit = false; - return { MLResult::MissingData, AnomalyBit }; + return false; } CalculatedNumber *TmpCNs = new CalculatedNumber[N * (Cfg.LagN + 1)](); std::memcpy(TmpCNs, CNs.data(), N * sizeof(CalculatedNumber)); - - SamplesBuffer SB = SamplesBuffer(TmpCNs, N, 1, Cfg.DiffN, Cfg.SmoothN, Cfg.LagN, + SamplesBuffer SB = SamplesBuffer(TmpCNs, N, 1, + Cfg.DiffN, Cfg.SmoothN, Cfg.LagN, 1.0, Cfg.RandomNums); - AnomalyScore = computeAnomalyScore(SB); + const DSample Sample = SB.preprocess().back(); delete[] TmpCNs; - if (AnomalyScore == std::numeric_limits::quiet_NaN()) { + std::unique_lock Lock(Mutex, std::defer_lock); + if (!Lock.try_lock()) { AnomalyBit = false; - return { MLResult::NaN, AnomalyBit }; + return false; } - AnomalyBit = AnomalyScore >= (100 * Cfg.DimensionAnomalyScoreThreshold); - return { MLResult::Success, AnomalyBit }; + for (const auto &KM : Models) { + double AnomalyScore = KM.anomalyScore(Sample); + if (AnomalyScore == std::numeric_limits::quiet_NaN()) { + AnomalyBit = false; + continue; + } + + if (AnomalyScore < (100 * Cfg.DimensionAnomalyScoreThreshold)) { + AnomalyBit = false; + return false; + } + } + + AnomalyBit = true; + return true; +} + +std::array Dimension::getModels() { + std::unique_lock Lock(Mutex); + return Models; } diff --git a/ml/Dimension.h b/ml/Dimension.h index 4fbc09b98..3ec56e098 100644 --- a/ml/Dimension.h +++ b/ml/Dimension.h @@ -3,157 +3,92 @@ #ifndef ML_DIMENSION_H #define ML_DIMENSION_H -#include "BitBufferCounter.h" +#include "Query.h" #include "Config.h" #include "ml-private.h" namespace ml { -class RrdDimension { -public: - RrdDimension(RRDDIM *RD) : RD(RD), Ops(&RD->tiers[0]->query_ops) { } - - RRDDIM *getRD() const { return RD; } - - time_t latestTime() { return Ops->latest_time(RD->tiers[0]->db_metric_handle); } - - time_t oldestTime() { return Ops->oldest_time(RD->tiers[0]->db_metric_handle); } - - unsigned updateEvery() const { return RD->update_every; } - - const std::string getID() const { - RRDSET *RS = RD->rrdset; - - std::stringstream SS; - SS << RS->context << "|" << RS->id << "|" << RD->name; - return SS.str(); - } - - bool isActive() const { - if (rrdset_flag_check(RD->rrdset, RRDSET_FLAG_OBSOLETE)) - return false; - - if (rrddim_flag_check(RD, RRDDIM_FLAG_OBSOLETE)) - return false; - - return true; - } - - void setAnomalyRateRD(RRDDIM *ARRD) { AnomalyRateRD = ARRD; } - RRDDIM *getAnomalyRateRD() const { return AnomalyRateRD; } - - void setAnomalyRateRDName(const char *Name) const { - rrddim_set_name(AnomalyRateRD->rrdset, AnomalyRateRD, Name); - } - - virtual ~RrdDimension() { - rrddim_free(AnomalyRateRD->rrdset, AnomalyRateRD); - } - -private: - RRDDIM *RD; - RRDDIM *AnomalyRateRD; - - struct rrddim_query_ops *Ops; - - std::string ID; -}; - enum class MLResult { Success = 0, MissingData, NaN, }; -class TrainableDimension : public RrdDimension { -public: - TrainableDimension(RRDDIM *RD) : - RrdDimension(RD), TrainEvery(Cfg.TrainEvery * updateEvery()) {} +static inline std::string getMLDimensionID(RRDDIM *RD) { + RRDSET *RS = RD->rrdset; - MLResult trainModel(); + std::stringstream SS; + SS << rrdset_context(RS) << "|" << rrdset_id(RS) << "|" << rrddim_name(RD); + return SS.str(); +} - CalculatedNumber computeAnomalyScore(SamplesBuffer &SB) { - return Trained ? KM.anomalyScore(SB) : 0.0; +class Dimension { +public: + Dimension(RRDDIM *RD) : + RD(RD), + LastTrainedAt(Seconds(0)), + Trained(false), + ConstantModel(false), + AnomalyScore(0.0), + AnomalyBit(0) + { } + + RRDDIM *getRD() const { + return RD; } - bool shouldTrain(const TimePoint &TP) const { - if (ConstantModel) - return false; - - return (LastTrainedAt + TrainEvery) < TP; + unsigned updateEvery() const { + return RD->update_every; } - bool isTrained() const { return Trained; } - -private: - std::pair getCalculatedNumbers(); - -public: - TimePoint LastTrainedAt{Seconds{0}}; + time_t latestTime() const { + return Query(RD).latestTime(); + } -protected: - std::atomic ConstantModel{false}; + time_t oldestTime() const { + return Query(RD).oldestTime(); + } -private: - Seconds TrainEvery; - KMeans KM; + bool isTrained() const { + return Trained; + } - std::atomic Trained{false}; -}; + bool isAnomalous() const { + return AnomalyBit; + } -class PredictableDimension : public TrainableDimension { -public: - PredictableDimension(RRDDIM *RD) : TrainableDimension(RD) {} + bool shouldTrain(const TimePoint &TP) const; - std::pair predict(); + bool isActive() const; - void addValue(CalculatedNumber Value, bool Exists); + MLResult trainModel(); - bool isAnomalous() { return AnomalyBit; } + bool predict(CalculatedNumber Value, bool Exists); - void updateAnomalyBitCounter(RRDSET *RS, unsigned Elapsed, bool IsAnomalous) { - AnomalyBitCounter += IsAnomalous; + std::pair detect(size_t WindowLength, bool Reset); - if (Elapsed == Cfg.DBEngineAnomalyRateEvery) { - double AR = static_cast(AnomalyBitCounter) / Cfg.DBEngineAnomalyRateEvery; - rrddim_set_by_pointer(RS, getAnomalyRateRD(), AR * 1000); - AnomalyBitCounter = 0; - } - } + std::array getModels(); private: - CalculatedNumber AnomalyScore{0.0}; - std::atomic AnomalyBit{false}; - unsigned AnomalyBitCounter{0}; - - std::vector CNs; -}; + std::pair getCalculatedNumbers(); -class DetectableDimension : public PredictableDimension { public: - DetectableDimension(RRDDIM *RD) : PredictableDimension(RD) {} - - std::pair detect(size_t WindowLength, bool Reset) { - bool AnomalyBit = isAnomalous(); - - if (Reset) - NumSetBits = BBC.numSetBits(); + RRDDIM *RD; - NumSetBits += AnomalyBit; - BBC.insert(AnomalyBit); + TimePoint LastTrainedAt; + std::atomic Trained; + std::atomic ConstantModel; - double AnomalyRate = static_cast(NumSetBits) / WindowLength; - return { AnomalyBit, AnomalyRate }; - } + CalculatedNumber AnomalyScore; + std::atomic AnomalyBit; -private: - BitBufferCounter BBC{static_cast(Cfg.ADMinWindowSize)}; - size_t NumSetBits{0}; + std::vector CNs; + std::array Models; + std::mutex Mutex; }; -using Dimension = DetectableDimension; - } // namespace ml #endif /* ML_DIMENSION_H */ diff --git a/ml/Host.cc b/ml/Host.cc index f8cba9d64..4a57178c7 100644 --- a/ml/Host.cc +++ b/ml/Host.cc @@ -1,278 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#include - #include "Config.h" #include "Host.h" +#include "ADCharts.h" #include "json/single_include/nlohmann/json.hpp" using namespace ml; -static void updateDimensionsChart(RRDHOST *RH, - collected_number NumTrainedDimensions, - collected_number NumNormalDimensions, - collected_number NumAnomalousDimensions) { - static thread_local RRDSET *RS = nullptr; - static thread_local RRDDIM *NumTotalDimensionsRD = nullptr; - static thread_local RRDDIM *NumTrainedDimensionsRD = nullptr; - static thread_local RRDDIM *NumNormalDimensionsRD = nullptr; - static thread_local RRDDIM *NumAnomalousDimensionsRD = nullptr; - - if (!RS) { - std::stringstream IdSS, NameSS; - - IdSS << "dimensions_on_" << localhost->machine_guid; - NameSS << "dimensions_on_" << localhost->hostname; - - RS = rrdset_create( - RH, - "anomaly_detection", // type - IdSS.str().c_str(), // id - NameSS.str().c_str(), // name - "dimensions", // family - "anomaly_detection.dimensions", // ctx - "Anomaly detection dimensions", // title - "dimensions", // units - "netdata", // plugin - "ml", // module - 39183, // priority - RH->rrd_update_every, // update_every - RRDSET_TYPE_LINE // chart_type - ); - rrdset_flag_set(RS, RRDSET_FLAG_ANOMALY_DETECTION); - - NumTotalDimensionsRD = rrddim_add(RS, "total", NULL, - 1, 1, RRD_ALGORITHM_ABSOLUTE); - NumTrainedDimensionsRD = rrddim_add(RS, "trained", NULL, - 1, 1, RRD_ALGORITHM_ABSOLUTE); - NumNormalDimensionsRD = rrddim_add(RS, "normal", NULL, - 1, 1, RRD_ALGORITHM_ABSOLUTE); - NumAnomalousDimensionsRD = rrddim_add(RS, "anomalous", NULL, - 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(RS); - - rrddim_set_by_pointer(RS, NumTotalDimensionsRD, NumNormalDimensions + NumAnomalousDimensions); - rrddim_set_by_pointer(RS, NumTrainedDimensionsRD, NumTrainedDimensions); - rrddim_set_by_pointer(RS, NumNormalDimensionsRD, NumNormalDimensions); - rrddim_set_by_pointer(RS, NumAnomalousDimensionsRD, NumAnomalousDimensions); - - rrdset_done(RS); -} - -static void updateRateChart(RRDHOST *RH, collected_number AnomalyRate) { - static thread_local RRDSET *RS = nullptr; - static thread_local RRDDIM *AnomalyRateRD = nullptr; - - if (!RS) { - std::stringstream IdSS, NameSS; - - IdSS << "anomaly_rate_on_" << localhost->machine_guid; - NameSS << "anomaly_rate_on_" << localhost->hostname; - - RS = rrdset_create( - RH, - "anomaly_detection", // type - IdSS.str().c_str(), // id - NameSS.str().c_str(), // name - "anomaly_rate", // family - "anomaly_detection.anomaly_rate", // ctx - "Percentage of anomalous dimensions", // title - "percentage", // units - "netdata", // plugin - "ml", // module - 39184, // priority - RH->rrd_update_every, // update_every - RRDSET_TYPE_LINE // chart_type - ); - rrdset_flag_set(RS, RRDSET_FLAG_ANOMALY_DETECTION); - - AnomalyRateRD = rrddim_add(RS, "anomaly_rate", NULL, - 1, 100, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(RS); - - rrddim_set_by_pointer(RS, AnomalyRateRD, AnomalyRate); - - rrdset_done(RS); -} - -static void updateWindowLengthChart(RRDHOST *RH, collected_number WindowLength) { - static thread_local RRDSET *RS = nullptr; - static thread_local RRDDIM *WindowLengthRD = nullptr; - - if (!RS) { - std::stringstream IdSS, NameSS; - - IdSS << "detector_window_on_" << localhost->machine_guid; - NameSS << "detector_window_on_" << localhost->hostname; - - RS = rrdset_create( - RH, - "anomaly_detection", // type - IdSS.str().c_str(), // id - NameSS.str().c_str(), // name - "detector_window", // family - "anomaly_detection.detector_window", // ctx - "Anomaly detector window length", // title - "seconds", // units - "netdata", // plugin - "ml", // module - 39185, // priority - RH->rrd_update_every, // update_every - RRDSET_TYPE_LINE // chart_type - ); - rrdset_flag_set(RS, RRDSET_FLAG_ANOMALY_DETECTION); - - WindowLengthRD = rrddim_add(RS, "duration", NULL, - 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(RS); - - rrddim_set_by_pointer(RS, WindowLengthRD, WindowLength * RH->rrd_update_every); - rrdset_done(RS); -} - -static void updateEventsChart(RRDHOST *RH, - std::pair P, - bool ResetBitCounter, - bool NewAnomalyEvent) { - static thread_local RRDSET *RS = nullptr; - static thread_local RRDDIM *AboveThresholdRD = nullptr; - static thread_local RRDDIM *ResetBitCounterRD = nullptr; - static thread_local RRDDIM *NewAnomalyEventRD = nullptr; - - if (!RS) { - std::stringstream IdSS, NameSS; - - IdSS << "detector_events_on_" << localhost->machine_guid; - NameSS << "detector_events_on_" << localhost->hostname; - - RS = rrdset_create( - RH, - "anomaly_detection", // type - IdSS.str().c_str(), // id - NameSS.str().c_str(), // name - "detector_events", // family - "anomaly_detection.detector_events", // ctx - "Anomaly events triggered", // title - "boolean", // units - "netdata", // plugin - "ml", // module - 39186, // priority - RH->rrd_update_every, // update_every - RRDSET_TYPE_LINE // chart_type - ); - rrdset_flag_set(RS, RRDSET_FLAG_ANOMALY_DETECTION); - - AboveThresholdRD = rrddim_add(RS, "above_threshold", NULL, - 1, 1, RRD_ALGORITHM_ABSOLUTE); - ResetBitCounterRD = rrddim_add(RS, "reset_bit_counter", NULL, - 1, 1, RRD_ALGORITHM_ABSOLUTE); - NewAnomalyEventRD = rrddim_add(RS, "new_anomaly_event", NULL, - 1, 1, RRD_ALGORITHM_ABSOLUTE); - } else - rrdset_next(RS); - - BitRateWindow::Edge E = P.first; - bool AboveThreshold = E.second == BitRateWindow::State::AboveThreshold; - - rrddim_set_by_pointer(RS, AboveThresholdRD, AboveThreshold); - rrddim_set_by_pointer(RS, ResetBitCounterRD, ResetBitCounter); - rrddim_set_by_pointer(RS, NewAnomalyEventRD, NewAnomalyEvent); - - rrdset_done(RS); -} - -static void updateDetectionChart(RRDHOST *RH) { - static thread_local RRDSET *RS = nullptr; - static thread_local RRDDIM *UserRD, *SystemRD = nullptr; - - if (!RS) { - std::stringstream IdSS, NameSS; - - IdSS << "prediction_stats_" << RH->machine_guid; - NameSS << "prediction_stats_for_" << RH->hostname; - - RS = rrdset_create_localhost( - "netdata", // type - IdSS.str().c_str(), // id - NameSS.str().c_str(), // name - "ml", // family - "netdata.prediction_stats", // ctx - "Prediction thread CPU usage", // title - "milliseconds/s", // units - "netdata", // plugin - "ml", // module - 136000, // priority - RH->rrd_update_every, // update_every - RRDSET_TYPE_STACKED // chart_type - ); - - UserRD = rrddim_add(RS, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); - SystemRD = rrddim_add(RS, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(RS); - - struct rusage TRU; - getrusage(RUSAGE_THREAD, &TRU); - - rrddim_set_by_pointer(RS, UserRD, TRU.ru_utime.tv_sec * 1000000ULL + TRU.ru_utime.tv_usec); - rrddim_set_by_pointer(RS, SystemRD, TRU.ru_stime.tv_sec * 1000000ULL + TRU.ru_stime.tv_usec); - rrdset_done(RS); -} - -static void updateTrainingChart(RRDHOST *RH, struct rusage *TRU) -{ - static thread_local RRDSET *RS = nullptr; - static thread_local RRDDIM *UserRD = nullptr; - static thread_local RRDDIM *SystemRD = nullptr; - - if (!RS) { - std::stringstream IdSS, NameSS; - - IdSS << "training_stats_" << RH->machine_guid; - NameSS << "training_stats_for_" << RH->hostname; - - RS = rrdset_create_localhost( - "netdata", // type - IdSS.str().c_str(), // id - NameSS.str().c_str(), // name - "ml", // family - "netdata.training_stats", // ctx - "Training thread CPU usage", // title - "milliseconds/s", // units - "netdata", // plugin - "ml", // module - 136001, // priority - RH->rrd_update_every, // update_every - RRDSET_TYPE_STACKED // chart_type - ); - - UserRD = rrddim_add(RS, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); - SystemRD = rrddim_add(RS, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); - } else - rrdset_next(RS); - - rrddim_set_by_pointer(RS, UserRD, TRU->ru_utime.tv_sec * 1000000ULL + TRU->ru_utime.tv_usec); - rrddim_set_by_pointer(RS, SystemRD, TRU->ru_stime.tv_sec * 1000000ULL + TRU->ru_stime.tv_usec); - rrdset_done(RS); -} - void RrdHost::addDimension(Dimension *D) { - RRDDIM *AnomalyRateRD = rrddim_add(AnomalyRateRS, D->getID().c_str(), NULL, - 1, 1000, RRD_ALGORITHM_ABSOLUTE); - D->setAnomalyRateRD(AnomalyRateRD); - - { - std::lock_guard Lock(Mutex); + std::lock_guard Lock(Mutex); - DimensionsMap[D->getRD()] = D; + DimensionsMap[D->getRD()] = D; - // Default construct mutex for dimension - LocksMap[D]; - } + // Default construct mutex for dimension + LocksMap[D]; } void RrdHost::removeDimension(Dimension *D) { @@ -312,18 +54,33 @@ void RrdHost::getConfigAsJson(nlohmann::json &Json) const { Json["max-kmeans-iters"] = Cfg.MaxKMeansIters; Json["dimension-anomaly-score-threshold"] = Cfg.DimensionAnomalyScoreThreshold; - Json["host-anomaly-rate-threshold"] = Cfg.HostAnomalyRateThreshold; - Json["min-window-size"] = Cfg.ADMinWindowSize; - Json["max-window-size"] = Cfg.ADMaxWindowSize; - Json["idle-window-size"] = Cfg.ADIdleWindowSize; - Json["window-rate-threshold"] = Cfg.ADWindowRateThreshold; - Json["dimension-rate-threshold"] = Cfg.ADDimensionRateThreshold; + Json["host-anomaly-rate-threshold"] = Cfg.HostAnomalyRateThreshold; + Json["anomaly-detection-grouping-method"] = group_method2string(Cfg.AnomalyDetectionGroupingMethod); + Json["anomaly-detection-query-duration"] = Cfg.AnomalyDetectionQueryDuration; Json["hosts-to-skip"] = Cfg.HostsToSkip; Json["charts-to-skip"] = Cfg.ChartsToSkip; } +void TrainableHost::getModelsAsJson(nlohmann::json &Json) { + std::lock_guard Lock(Mutex); + + for (auto &DP : DimensionsMap) { + Dimension *D = DP.second; + + nlohmann::json JsonArray = nlohmann::json::array(); + for (const KMeans &KM : D->getModels()) { + nlohmann::json J; + KM.toJson(J); + JsonArray.push_back(J); + } + Json[getMLDimensionID(D->getRD())] = JsonArray; + } + + return; +} + std::pair> TrainableHost::findDimensionToTrain(const TimePoint &NowTP) { std::lock_guard Lock(Mutex); @@ -384,7 +141,12 @@ void TrainableHost::train() { worker_is_idle(); SleepFor = std::min(AllottedDuration - RealDuration, MaxSleepFor); - std::this_thread::sleep_for(SleepFor); + TimePoint Now = SteadyClock::now(); + auto Until = Now + SleepFor; + while (Now < Until && !netdata_exit) { + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + Now = SteadyClock::now(); + } worker_is_busy(0); } } @@ -393,78 +155,44 @@ void TrainableHost::train() { #define WORKER_JOB_UPDATE_DETECTION_CHART 1 #define WORKER_JOB_UPDATE_ANOMALY_RATES 2 #define WORKER_JOB_UPDATE_CHARTS 3 -#define WORKER_JOB_SAVE_ANOMALY_EVENT 4 #if WORKER_UTILIZATION_MAX_JOB_TYPES < 5 #error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 5 #endif void DetectableHost::detectOnce() { - auto P = BRW.insert(WindowAnomalyRate >= Cfg.HostAnomalyRateThreshold); - BitRateWindow::Edge Edge = P.first; - size_t WindowLength = P.second; - - bool ResetBitCounter = (Edge.first != BitRateWindow::State::AboveThreshold); - bool NewAnomalyEvent = (Edge.first == BitRateWindow::State::AboveThreshold) && - (Edge.second == BitRateWindow::State::Idle); - - std::vector> DimsOverThreshold; - size_t NumAnomalousDimensions = 0; size_t NumNormalDimensions = 0; size_t NumTrainedDimensions = 0; size_t NumActiveDimensions = 0; - bool CollectAnomalyRates = (++AnomalyRateTimer == Cfg.DBEngineAnomalyRateEvery); - if (CollectAnomalyRates) - rrdset_next(AnomalyRateRS); - { std::lock_guard Lock(Mutex); - DimsOverThreshold.reserve(DimensionsMap.size()); - for (auto &DP : DimensionsMap) { worker_is_busy(WORKER_JOB_DETECT_DIMENSION); Dimension *D = DP.second; - if (!D->isActive()) { - D->updateAnomalyBitCounter(AnomalyRateRS, AnomalyRateTimer, false); + if (!D->isActive()) continue; - } NumActiveDimensions++; - - auto P = D->detect(WindowLength, ResetBitCounter); - bool IsAnomalous = P.first; - double AnomalyScore = P.second; - NumTrainedDimensions += D->isTrained(); + bool IsAnomalous = D->isAnomalous(); if (IsAnomalous) NumAnomalousDimensions += 1; - - if (NewAnomalyEvent && (AnomalyScore >= Cfg.ADDimensionRateThreshold)) - DimsOverThreshold.push_back({ AnomalyScore, D->getID() }); - - D->updateAnomalyBitCounter(AnomalyRateRS, AnomalyRateTimer, IsAnomalous); } if (NumAnomalousDimensions) - WindowAnomalyRate = static_cast(NumAnomalousDimensions) / NumActiveDimensions; + HostAnomalyRate = static_cast(NumAnomalousDimensions) / NumActiveDimensions; else - WindowAnomalyRate = 0.0; + HostAnomalyRate = 0.0; NumNormalDimensions = NumActiveDimensions - NumAnomalousDimensions; } - if (CollectAnomalyRates) { - worker_is_busy(WORKER_JOB_UPDATE_ANOMALY_RATES); - AnomalyRateTimer = 0; - rrdset_done(AnomalyRateRS); - } - this->NumAnomalousDimensions = NumAnomalousDimensions; this->NumNormalDimensions = NumNormalDimensions; this->NumTrainedDimensions = NumTrainedDimensions; @@ -472,38 +200,11 @@ void DetectableHost::detectOnce() { worker_is_busy(WORKER_JOB_UPDATE_CHARTS); updateDimensionsChart(getRH(), NumTrainedDimensions, NumNormalDimensions, NumAnomalousDimensions); - updateRateChart(getRH(), WindowAnomalyRate * 10000.0); - updateWindowLengthChart(getRH(), WindowLength); - updateEventsChart(getRH(), P, ResetBitCounter, NewAnomalyEvent); + updateHostAndDetectionRateCharts(getRH(), HostAnomalyRate * 10000.0); struct rusage TRU; getResourceUsage(&TRU); updateTrainingChart(getRH(), &TRU); - - if (!NewAnomalyEvent || (DimsOverThreshold.size() == 0)) - return; - - worker_is_busy(WORKER_JOB_SAVE_ANOMALY_EVENT); - - std::sort(DimsOverThreshold.begin(), DimsOverThreshold.end()); - std::reverse(DimsOverThreshold.begin(), DimsOverThreshold.end()); - - // Make sure the JSON response won't grow beyond a specific number - // of dimensions. Log an error message if this happens, because it - // most likely means that the user specified a very-low anomaly rate - // threshold. - size_t NumMaxDimsOverThreshold = 2000; - if (DimsOverThreshold.size() > NumMaxDimsOverThreshold) { - error("Found %zu dimensions over threshold. Reducing JSON result to %zu dimensions.", - DimsOverThreshold.size(), NumMaxDimsOverThreshold); - DimsOverThreshold.resize(NumMaxDimsOverThreshold); - } - - nlohmann::json JsonResult = DimsOverThreshold; - - time_t Before = now_realtime_sec(); - time_t After = Before - (WindowLength * updateEvery()); - DB.insertAnomaly("AD1", 1, getUUID(), After, Before, JsonResult.dump(4)); } void DetectableHost::detect() { @@ -512,7 +213,6 @@ void DetectableHost::detect() { worker_register_job_name(WORKER_JOB_UPDATE_DETECTION_CHART, "detection chart"); worker_register_job_name(WORKER_JOB_UPDATE_ANOMALY_RATES, "anomaly rates"); worker_register_job_name(WORKER_JOB_UPDATE_CHARTS, "charts"); - worker_register_job_name(WORKER_JOB_SAVE_ANOMALY_EVENT, "anomaly event"); std::this_thread::sleep_for(Seconds{10}); diff --git a/ml/Host.h b/ml/Host.h index 2715008f0..52a0cd095 100644 --- a/ml/Host.h +++ b/ml/Host.h @@ -3,38 +3,17 @@ #ifndef ML_HOST_H #define ML_HOST_H -#include "BitRateWindow.h" #include "Config.h" -#include "Database.h" #include "Dimension.h" #include "ml-private.h" +#include "json/single_include/nlohmann/json.hpp" namespace ml { class RrdHost { public: - RrdHost(RRDHOST *RH) : RH(RH) { - AnomalyRateRS = rrdset_create( - RH, - "anomaly_detection", - "anomaly_rates", - NULL, // name - "anomaly_rates", - NULL, // ctx - "Average anomaly rate", - "anomaly rate", - "netdata", - "ml", - 39189, - Cfg.DBEngineAnomalyRateEvery, - RRDSET_TYPE_LINE - ); - - AnomalyRateRS->flags = static_cast( - static_cast(AnomalyRateRS->flags) | RRDSET_FLAG_HIDDEN - ); - } + RrdHost(RRDHOST *RH) : RH(RH) {}; RRDHOST *getRH() { return RH; } @@ -55,7 +34,6 @@ public: protected: RRDHOST *RH; - RRDSET *AnomalyRateRS; // Protect dimension and lock maps std::mutex Mutex; @@ -80,6 +58,8 @@ public: memcpy(RU, &ResourceUsage, sizeof(struct rusage)); } + void getModelsAsJson(nlohmann::json &Json); + private: std::pair> findDimensionToTrain(const TimePoint &NowTP); void trainDimension(Dimension *D, const TimePoint &NowTP); @@ -95,16 +75,6 @@ public: void startAnomalyDetectionThreads(); void stopAnomalyDetectionThreads(); - template - bool getAnomalyInfo(ArgTypes&&... Args) { - return DB.getAnomalyInfo(Args...); - } - - template - bool getAnomaliesInRange(ArgTypes&&... Args) { - return DB.getAnomaliesInRange(Args...); - } - void getDetectionInfoAsJson(nlohmann::json &Json) const; private: @@ -115,23 +85,12 @@ private: std::thread TrainingThread; std::thread DetectionThread; - BitRateWindow BRW{ - static_cast(Cfg.ADMinWindowSize), - static_cast(Cfg.ADMaxWindowSize), - static_cast(Cfg.ADIdleWindowSize), - static_cast(Cfg.ADMinWindowSize * Cfg.ADWindowRateThreshold) - }; - - CalculatedNumber WindowAnomalyRate{0.0}; + CalculatedNumber HostAnomalyRate{0.0}; size_t NumAnomalousDimensions{0}; size_t NumNormalDimensions{0}; size_t NumTrainedDimensions{0}; size_t NumActiveDimensions{0}; - - unsigned AnomalyRateTimer{0}; - - Database DB{Cfg.AnomalyDBPath}; }; using Host = DetectableHost; diff --git a/ml/KMeans.cc b/ml/KMeans.cc new file mode 100644 index 000000000..edc2ef49e --- /dev/null +++ b/ml/KMeans.cc @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "KMeans.h" +#include + +void KMeans::train(const std::vector &Samples, size_t MaxIterations) { + MinDist = std::numeric_limits::max(); + MaxDist = std::numeric_limits::min(); + + ClusterCenters.clear(); + + dlib::pick_initial_centers(NumClusters, ClusterCenters, Samples); + dlib::find_clusters_using_kmeans(Samples, ClusterCenters, MaxIterations); + + for (const auto &S : Samples) { + CalculatedNumber MeanDist = 0.0; + + for (const auto &KMCenter : ClusterCenters) + MeanDist += dlib::length(KMCenter - S); + + MeanDist /= NumClusters; + + if (MeanDist < MinDist) + MinDist = MeanDist; + + if (MeanDist > MaxDist) + MaxDist = MeanDist; + } +} + +CalculatedNumber KMeans::anomalyScore(const DSample &Sample) const { + CalculatedNumber MeanDist = 0.0; + for (const auto &CC: ClusterCenters) + MeanDist += dlib::length(CC - Sample); + + MeanDist /= NumClusters; + + if (MaxDist == MinDist) + return 0.0; + + CalculatedNumber AnomalyScore = 100.0 * std::abs((MeanDist - MinDist) / (MaxDist - MinDist)); + return (AnomalyScore > 100.0) ? 100.0 : AnomalyScore; +} diff --git a/ml/KMeans.h b/ml/KMeans.h new file mode 100644 index 000000000..0398eeb86 --- /dev/null +++ b/ml/KMeans.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef KMEANS_H +#define KMEANS_H + +#include +#include +#include +#include + +#include "SamplesBuffer.h" +#include "json/single_include/nlohmann/json.hpp" + +class KMeans { +public: + KMeans(size_t NumClusters = 2) : NumClusters(NumClusters) { + MinDist = std::numeric_limits::max(); + MaxDist = std::numeric_limits::min(); + }; + + void train(const std::vector &Samples, size_t MaxIterations); + CalculatedNumber anomalyScore(const DSample &Sample) const; + + void toJson(nlohmann::json &J) const { + J = nlohmann::json{ + {"CCs", ClusterCenters}, + {"MinDist", MinDist}, + {"MaxDist", MaxDist} + }; + } + +private: + size_t NumClusters; + + std::vector ClusterCenters; + + CalculatedNumber MinDist; + CalculatedNumber MaxDist; +}; + +#endif /* KMEANS_H */ diff --git a/ml/Makefile.am b/ml/Makefile.am deleted file mode 100644 index 27449d659..000000000 --- a/ml/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in - -SUBDIRS = \ - kmeans \ - $(NULL) diff --git a/ml/Query.h b/ml/Query.h index 24c5fa384..78d117003 100644 --- a/ml/Query.h +++ b/ml/Query.h @@ -7,8 +7,8 @@ namespace ml { class Query { public: - Query(RRDDIM *RD) : RD(RD) { - Ops = &RD->tiers[0]->query_ops; + Query(RRDDIM *RD) : RD(RD), Initialized(false) { + Ops = RD->tiers[0]->query_ops; } time_t latestTime() { @@ -20,27 +20,36 @@ public: } void init(time_t AfterT, time_t BeforeT) { - Ops->init(RD->tiers[0]->db_metric_handle, &Handle, AfterT, BeforeT, TIER_QUERY_FETCH_SUM); + Ops->init(RD->tiers[0]->db_metric_handle, &Handle, AfterT, BeforeT); + Initialized = true; + points_read = 0; } bool isFinished() { return Ops->is_finished(&Handle); } + ~Query() { + if (Initialized) { + Ops->finalize(&Handle); + global_statistics_ml_query_completed(points_read); + points_read = 0; + } + } + std::pair nextMetric() { + points_read++; STORAGE_POINT sp = Ops->next_metric(&Handle); return { sp.start_time, sp.sum / sp.count }; } - ~Query() { - Ops->finalize(&Handle); - } - private: RRDDIM *RD; + bool Initialized; + size_t points_read; - struct rrddim_query_ops *Ops; - struct rrddim_query_handle Handle; + struct storage_engine_query_ops *Ops; + struct storage_engine_query_handle Handle; }; } // namespace ml diff --git a/ml/README.md b/ml/README.md index 2578993e2..f6fd923ab 100644 --- a/ml/README.md +++ b/ml/README.md @@ -248,8 +248,6 @@ In terms of anomaly detection, the most interesting charts would be the `anomaly - `anomaly_detection.dimensions`: Percentage of anomalous dimensions. - `anomaly_detection.detector_window`: The length of the active window used by the detector. - `anomaly_detection.detector_events`: Flags (0 or 1) to show when an anomaly event has been triggered by the detector. -- `anomaly_detection.prediction_stats`: Diagnostic metrics relating to prediction time of anomaly detection. -- `anomaly_detection.training_stats`: Diagnostic metrics relating to training time of anomaly detection. Below is an example of how these charts may look in the presence of an anomaly event. diff --git a/ml/SamplesBuffer.cc b/ml/SamplesBuffer.cc new file mode 100644 index 000000000..d276c6e09 --- /dev/null +++ b/ml/SamplesBuffer.cc @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +#include "SamplesBuffer.h" + +#include +#include +#include + +void Sample::print(std::ostream &OS) const { + for (size_t Idx = 0; Idx != NumDims - 1; Idx++) + OS << CNs[Idx] << ", "; + + OS << CNs[NumDims - 1]; +} + +void SamplesBuffer::print(std::ostream &OS) const { + for (size_t Idx = Preprocessed ? (DiffN + (SmoothN - 1) + (LagN)) : 0; + Idx != NumSamples; Idx++) { + Sample S = Preprocessed ? getPreprocessedSample(Idx) : getSample(Idx); + OS << S << std::endl; + } +} + +std::vector SamplesBuffer::getPreprocessedSamples() const { + std::vector V; + + for (size_t Idx = Preprocessed ? (DiffN + (SmoothN - 1) + (LagN)) : 0; + Idx != NumSamples; Idx++) { + Sample S = Preprocessed ? getPreprocessedSample(Idx) : getSample(Idx); + V.push_back(S); + } + + return V; +} + +void SamplesBuffer::diffSamples() { + // Panda's DataFrame default behaviour is to subtract each element from + // itself. For us `DiffN = 0` means "disable diff-ing" when preprocessing + // the samples buffer. This deviation will make it easier for us to test + // the KMeans implementation. + if (DiffN == 0) + return; + + for (size_t Idx = 0; Idx != (NumSamples - DiffN); Idx++) { + size_t High = (NumSamples - 1) - Idx; + size_t Low = High - DiffN; + + Sample LHS = getSample(High); + Sample RHS = getSample(Low); + + LHS.diff(RHS); + } +} + +void SamplesBuffer::smoothSamples() { + // Holds the mean value of each window + CalculatedNumber *AccCNs = new CalculatedNumber[NumDimsPerSample](); + Sample Acc(AccCNs, NumDimsPerSample); + + // Used to avoid clobbering the accumulator when moving the window + CalculatedNumber *TmpCNs = new CalculatedNumber[NumDimsPerSample](); + Sample Tmp(TmpCNs, NumDimsPerSample); + + CalculatedNumber Factor = (CalculatedNumber) 1 / SmoothN; + + // Calculate the value of the 1st window + for (size_t Idx = 0; Idx != std::min(SmoothN, NumSamples); Idx++) { + Tmp.add(getSample(NumSamples - (Idx + 1))); + } + + Acc.add(Tmp); + Acc.scale(Factor); + + // Move the window and update the samples + for (size_t Idx = NumSamples; Idx != (DiffN + SmoothN - 1); Idx--) { + Sample S = getSample(Idx - 1); + + // Tmp <- Next window (if any) + if (Idx >= (SmoothN + 1)) { + Tmp.diff(S); + Tmp.add(getSample(Idx - (SmoothN + 1))); + } + + // S <- Acc + S.copy(Acc); + + // Acc <- Tmp + Acc.copy(Tmp); + Acc.scale(Factor); + } + + delete[] AccCNs; + delete[] TmpCNs; +} + +void SamplesBuffer::lagSamples() { + if (LagN == 0) + return; + + for (size_t Idx = NumSamples; Idx != LagN; Idx--) { + Sample PS = getPreprocessedSample(Idx - 1); + PS.lag(getSample(Idx - 1), LagN); + } +} + +std::vector SamplesBuffer::preprocess() { + assert(Preprocessed == false); + + std::vector DSamples; + size_t OutN = NumSamples; + + // Diff + if (DiffN >= OutN) + return DSamples; + OutN -= DiffN; + diffSamples(); + + // Smooth + if (SmoothN == 0 || SmoothN > OutN) + return DSamples; + OutN -= (SmoothN - 1); + smoothSamples(); + + // Lag + if (LagN >= OutN) + return DSamples; + OutN -= LagN; + lagSamples(); + + DSamples.reserve(OutN); + Preprocessed = true; + + uint32_t MaxMT = std::numeric_limits::max(); + uint32_t CutOff = static_cast(MaxMT) * SamplingRatio; + + for (size_t Idx = NumSamples - OutN; Idx != NumSamples; Idx++) { + if (RandNums[Idx] > CutOff) + continue; + + DSample DS; + DS.set_size(NumDimsPerSample * (LagN + 1)); + + const Sample PS = getPreprocessedSample(Idx); + PS.initDSample(DS); + + DSamples.push_back(DS); + } + + return DSamples; +} diff --git a/ml/SamplesBuffer.h b/ml/SamplesBuffer.h new file mode 100644 index 000000000..1c7215cca --- /dev/null +++ b/ml/SamplesBuffer.h @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SAMPLES_BUFFER_H +#define SAMPLES_BUFFER_H + +#include +#include + +#include +#include +#include + +#include + +typedef double CalculatedNumber; +typedef dlib::matrix DSample; + +class Sample { +public: + Sample(CalculatedNumber *Buf, size_t N) : CNs(Buf), NumDims(N) {} + + void initDSample(DSample &DS) const { + for (size_t Idx = 0; Idx != NumDims; Idx++) { + DS(Idx) = std::abs(CNs[Idx]); + } + } + + void add(const Sample &RHS) const { + assert(NumDims == RHS.NumDims); + + for (size_t Idx = 0; Idx != NumDims; Idx++) + CNs[Idx] += RHS.CNs[Idx]; + }; + + void diff(const Sample &RHS) const { + assert(NumDims == RHS.NumDims); + + for (size_t Idx = 0; Idx != NumDims; Idx++) + CNs[Idx] -= RHS.CNs[Idx]; + }; + + void copy(const Sample &RHS) const { + assert(NumDims == RHS.NumDims); + + std::memcpy(CNs, RHS.CNs, NumDims * sizeof(CalculatedNumber)); + } + + void scale(CalculatedNumber Factor) { + for (size_t Idx = 0; Idx != NumDims; Idx++) + CNs[Idx] *= Factor; + } + + void lag(const Sample &S, size_t LagN) { + size_t N = S.NumDims; + + for (size_t Idx = 0; Idx != (LagN + 1); Idx++) { + Sample Src(S.CNs - (Idx * N), N); + Sample Dst(CNs + (Idx * N), N); + Dst.copy(Src); + } + } + + const CalculatedNumber *getCalculatedNumbers() const { + return CNs; + }; + + void print(std::ostream &OS) const; + +private: + CalculatedNumber *CNs; + size_t NumDims; +}; + +inline std::ostream& operator<<(std::ostream &OS, const Sample &S) { + S.print(OS); + return OS; +} + +class SamplesBuffer { +public: + SamplesBuffer(CalculatedNumber *CNs, + size_t NumSamples, size_t NumDimsPerSample, + size_t DiffN, size_t SmoothN, size_t LagN, + double SamplingRatio, std::vector &RandNums) : + CNs(CNs), NumSamples(NumSamples), NumDimsPerSample(NumDimsPerSample), + DiffN(DiffN), SmoothN(SmoothN), LagN(LagN), + SamplingRatio(SamplingRatio), RandNums(RandNums), + BytesPerSample(NumDimsPerSample * sizeof(CalculatedNumber)), + Preprocessed(false) {}; + + std::vector preprocess(); + std::vector getPreprocessedSamples() const; + + size_t capacity() const { return NumSamples; } + void print(std::ostream &OS) const; + +private: + size_t getSampleOffset(size_t Index) const { + assert(Index < NumSamples); + return Index * NumDimsPerSample; + } + + size_t getPreprocessedSampleOffset(size_t Index) const { + assert(Index < NumSamples); + return getSampleOffset(Index) * (LagN + 1); + } + + void setSample(size_t Index, const Sample &S) const { + size_t Offset = getSampleOffset(Index); + std::memcpy(&CNs[Offset], S.getCalculatedNumbers(), BytesPerSample); + } + + const Sample getSample(size_t Index) const { + size_t Offset = getSampleOffset(Index); + return Sample(&CNs[Offset], NumDimsPerSample); + }; + + const Sample getPreprocessedSample(size_t Index) const { + size_t Offset = getPreprocessedSampleOffset(Index); + return Sample(&CNs[Offset], NumDimsPerSample * (LagN + 1)); + }; + + void diffSamples(); + void smoothSamples(); + void lagSamples(); + +private: + CalculatedNumber *CNs; + size_t NumSamples; + size_t NumDimsPerSample; + size_t DiffN; + size_t SmoothN; + size_t LagN; + double SamplingRatio; + std::vector &RandNums; + + size_t BytesPerSample; + bool Preprocessed; +}; + +inline std::ostream& operator<<(std::ostream& OS, const SamplesBuffer &SB) { + SB.print(OS); + return OS; +} + +#endif /* SAMPLES_BUFFER_H */ diff --git a/ml/SamplesBufferTests.cc b/ml/SamplesBufferTests.cc new file mode 100644 index 000000000..5997a2a15 --- /dev/null +++ b/ml/SamplesBufferTests.cc @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "ml/ml-private.h" +#include + +/* + * The SamplesBuffer class implements the functionality of the following python + * code: + * >> df = pd.DataFrame(data=samples) + * >> df = df.diff(diff_n).dropna() + * >> df = df.rolling(smooth_n).mean().dropna() + * >> df = pd.concat([df.shift(n) for n in range(lag_n + 1)], axis=1).dropna() + * + * Its correctness has been verified by automatically generating random + * data frames in Python and comparing them with the correspondent preprocessed + * SampleBuffers. + * + * The following tests are meant to catch unintended changes in the SamplesBuffer + * implementation. For development purposes, one should compare changes against + * the aforementioned python code. +*/ + +TEST(SamplesBufferTest, NS_8_NDPS_1_DN_1_SN_3_LN_1) { + size_t NumSamples = 8, NumDimsPerSample = 1; + size_t DiffN = 1, SmoothN = 3, LagN = 3; + + size_t N = NumSamples * NumDimsPerSample * (LagN + 1); + CalculatedNumber *CNs = new CalculatedNumber[N](); + + CNs[0] = 0.7568336679490107; + CNs[1] = 0.4814406581763254; + CNs[2] = 0.40073555156221874; + CNs[3] = 0.5973257298194408; + CNs[4] = 0.5334727814345868; + CNs[5] = 0.2632477193454843; + CNs[6] = 0.2684839023122384; + CNs[7] = 0.851332948637479; + + std::vector RandNums(NumSamples, std::numeric_limits::max()); + SamplesBuffer SB(CNs, NumSamples, NumDimsPerSample, DiffN, SmoothN, LagN, 1.0, RandNums); + SB.preprocess(); + + std::vector Samples = SB.getPreprocessedSamples(); + EXPECT_EQ(Samples.size(), 2); + + Sample S0 = Samples[0]; + const CalculatedNumber *S0_CNs = S0.getCalculatedNumbers(); + Sample S1 = Samples[1]; + const CalculatedNumber *S1_CNs = S1.getCalculatedNumbers(); + + EXPECT_NEAR(S0_CNs[0], -0.109614, 0.001); + EXPECT_NEAR(S0_CNs[1], -0.0458293, 0.001); + EXPECT_NEAR(S0_CNs[2], 0.017344, 0.001); + EXPECT_NEAR(S0_CNs[3], -0.0531693, 0.001); + + EXPECT_NEAR(S1_CNs[0], 0.105953, 0.001); + EXPECT_NEAR(S1_CNs[1], -0.109614, 0.001); + EXPECT_NEAR(S1_CNs[2], -0.0458293, 0.001); + EXPECT_NEAR(S1_CNs[3], 0.017344, 0.001); + + delete[] CNs; +} + +TEST(SamplesBufferTest, NS_8_NDPS_1_DN_2_SN_3_LN_2) { + size_t NumSamples = 8, NumDimsPerSample = 1; + size_t DiffN = 2, SmoothN = 3, LagN = 2; + + size_t N = NumSamples * NumDimsPerSample * (LagN + 1); + CalculatedNumber *CNs = new CalculatedNumber[N](); + + CNs[0] = 0.20511885291342846; + CNs[1] = 0.13151717360306558; + CNs[2] = 0.6017085062423134; + CNs[3] = 0.46256882933941545; + CNs[4] = 0.7887758447877941; + CNs[5] = 0.9237989080034406; + CNs[6] = 0.15552559051428083; + CNs[7] = 0.6309750314597955; + + std::vector RandNums(NumSamples, std::numeric_limits::max()); + SamplesBuffer SB(CNs, NumSamples, NumDimsPerSample, DiffN, SmoothN, LagN, 1.0, RandNums); + SB.preprocess(); + + std::vector Samples = SB.getPreprocessedSamples(); + EXPECT_EQ(Samples.size(), 2); + + Sample S0 = Samples[0]; + const CalculatedNumber *S0_CNs = S0.getCalculatedNumbers(); + Sample S1 = Samples[1]; + const CalculatedNumber *S1_CNs = S1.getCalculatedNumbers(); + + EXPECT_NEAR(S0_CNs[0], 0.005016, 0.001); + EXPECT_NEAR(S0_CNs[1], 0.326450, 0.001); + EXPECT_NEAR(S0_CNs[2], 0.304903, 0.001); + + EXPECT_NEAR(S1_CNs[0], -0.154948, 0.001); + EXPECT_NEAR(S1_CNs[1], 0.005016, 0.001); + EXPECT_NEAR(S1_CNs[2], 0.326450, 0.001); + + delete[] CNs; +} + +TEST(SamplesBufferTest, NS_8_NDPS_3_DN_2_SN_4_LN_1) { + size_t NumSamples = 8, NumDimsPerSample = 3; + size_t DiffN = 2, SmoothN = 4, LagN = 1; + + size_t N = NumSamples * NumDimsPerSample * (LagN + 1); + CalculatedNumber *CNs = new CalculatedNumber[N](); + + CNs[0] = 0.34310900399667765; CNs[1] = 0.14694315994488194; CNs[2] = 0.8246677800938796; + CNs[3] = 0.48249504592307835; CNs[4] = 0.23241087965531182; CNs[5] = 0.9595348555892567; + CNs[6] = 0.44281094035598334; CNs[7] = 0.5143142171362715; CNs[8] = 0.06391303014242555; + CNs[9] = 0.7460491027783901; CNs[10] = 0.43887217459032923; CNs[11] = 0.2814395025355999; + CNs[12] = 0.9231114281214198; CNs[13] = 0.326882401786898; CNs[14] = 0.26747939220376216; + CNs[15] = 0.7787571209969636; CNs[16] =0.5851700001235088; CNs[17] = 0.34410728945321567; + CNs[18] = 0.9394494507088997; CNs[19] =0.17567223681734334; CNs[20] = 0.42732886195446984; + CNs[21] = 0.9460522396152958; CNs[22] =0.23462747016780894; CNs[23] = 0.35983249900892145; + + std::vector RandNums(NumSamples, std::numeric_limits::max()); + SamplesBuffer SB(CNs, NumSamples, NumDimsPerSample, DiffN, SmoothN, LagN, 1.0, RandNums); + SB.preprocess(); + + std::vector Samples = SB.getPreprocessedSamples(); + EXPECT_EQ(Samples.size(), 2); + + Sample S0 = Samples[0]; + const CalculatedNumber *S0_CNs = S0.getCalculatedNumbers(); + Sample S1 = Samples[1]; + const CalculatedNumber *S1_CNs = S1.getCalculatedNumbers(); + + EXPECT_NEAR(S0_CNs[0], 0.198225, 0.001); + EXPECT_NEAR(S0_CNs[1], 0.003529, 0.001); + EXPECT_NEAR(S0_CNs[2], -0.063003, 0.001); + EXPECT_NEAR(S0_CNs[3], 0.219066, 0.001); + EXPECT_NEAR(S0_CNs[4], 0.133175, 0.001); + EXPECT_NEAR(S0_CNs[5], -0.293154, 0.001); + + EXPECT_NEAR(S1_CNs[0], 0.174160, 0.001); + EXPECT_NEAR(S1_CNs[1], -0.135722, 0.001); + EXPECT_NEAR(S1_CNs[2], 0.110452, 0.001); + EXPECT_NEAR(S1_CNs[3], 0.198225, 0.001); + EXPECT_NEAR(S1_CNs[4], 0.003529, 0.001); + EXPECT_NEAR(S1_CNs[5], -0.063003, 0.001); + + delete[] CNs; +} diff --git a/ml/Tests.cc b/ml/Tests.cc deleted file mode 100644 index 7d369d48d..000000000 --- a/ml/Tests.cc +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "BitBufferCounter.h" -#include "BitRateWindow.h" - -#include "gtest/gtest.h" - -using namespace ml; - -TEST(BitBufferCounterTest, Cap_4) { - size_t Capacity = 4; - BitBufferCounter BBC(Capacity); - - // No bits set - EXPECT_EQ(BBC.numSetBits(), 0); - - // All ones - for (size_t Idx = 0; Idx != (2 * Capacity); Idx++) { - BBC.insert(true); - - EXPECT_EQ(BBC.numSetBits(), std::min(Idx + 1, Capacity)); - } - - // All zeroes - for (size_t Idx = 0; Idx != Capacity; Idx++) { - BBC.insert(false); - - if (Idx < Capacity) - EXPECT_EQ(BBC.numSetBits(), Capacity - (Idx + 1)); - else - EXPECT_EQ(BBC.numSetBits(), 0); - } - - // Even ones/zeroes - for (size_t Idx = 0; Idx != (2 * Capacity); Idx++) - BBC.insert(Idx % 2 == 0); - EXPECT_EQ(BBC.numSetBits(), Capacity / 2); -} - -using State = BitRateWindow::State; -using Edge = BitRateWindow::Edge; -using Result = std::pair; - -TEST(BitRateWindowTest, Cycles) { - /* Test the FSM by going through its two cycles: - * 1) NotFilled -> AboveThreshold -> Idle -> NotFilled - * 2) NotFilled -> BelowThreshold -> AboveThreshold -> Idle -> NotFilled - * - * Check the window's length on every new state transition. - */ - - size_t MinLength = 4, MaxLength = 6, IdleLength = 5; - size_t SetBitsThreshold = 3; - - Result R; - BitRateWindow BRW(MinLength, MaxLength, IdleLength, SetBitsThreshold); - - /* - * 1st cycle - */ - - // NotFilled -> AboveThreshold - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::AboveThreshold)); - EXPECT_EQ(R.second, MinLength); - - // AboveThreshold -> Idle - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::AboveThreshold, State::AboveThreshold)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::AboveThreshold, State::AboveThreshold)); - - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::AboveThreshold, State::Idle)); - EXPECT_EQ(R.second, MaxLength); - - - // Idle -> NotFilled - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::Idle)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::Idle)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::Idle)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::Idle)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::NotFilled)); - EXPECT_EQ(R.second, 1); - - // NotFilled -> AboveThreshold - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::AboveThreshold)); - EXPECT_EQ(R.second, MinLength); - - /* - * 2nd cycle - */ - - BRW = BitRateWindow(MinLength, MaxLength, IdleLength, SetBitsThreshold); - - // NotFilled -> BelowThreshold - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::BelowThreshold)); - EXPECT_EQ(R.second, MinLength); - - // BelowThreshold -> BelowThreshold: - // Check the state's self loop by adding set bits that will keep the - // bit buffer below the specified threshold. - // - for (size_t Idx = 0; Idx != 2 * MaxLength; Idx++) { - R = BRW.insert(Idx % 2 == 0); - EXPECT_EQ(R.first, std::make_pair(State::BelowThreshold, State::BelowThreshold)); - EXPECT_EQ(R.second, MinLength); - } - - // Verify that at the end of the loop the internal bit buffer contains - // "1010". Do so by adding one set bit and checking that we remain below - // the specified threshold. - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::BelowThreshold, State::BelowThreshold)); - EXPECT_EQ(R.second, MinLength); - - // BelowThreshold -> AboveThreshold - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::BelowThreshold, State::AboveThreshold)); - EXPECT_EQ(R.second, MinLength); - - // AboveThreshold -> Idle: - // Do the transition without filling the max window size this time. - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::AboveThreshold, State::Idle)); - EXPECT_EQ(R.second, MinLength); - - // Idle -> NotFilled - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::Idle)); - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::Idle)); - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::Idle)); - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::Idle)); - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::NotFilled)); - EXPECT_EQ(R.second, 1); - - // NotFilled -> AboveThreshold - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::NotFilled)); - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::AboveThreshold)); - EXPECT_EQ(R.second, MinLength); -} - -TEST(BitRateWindowTest, ConsecutiveOnes) { - size_t MinLength = 120, MaxLength = 240, IdleLength = 30; - size_t SetBitsThreshold = 30; - - Result R; - BitRateWindow BRW(MinLength, MaxLength, IdleLength, SetBitsThreshold); - - for (size_t Idx = 0; Idx != MaxLength; Idx++) - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::BelowThreshold, State::BelowThreshold)); - EXPECT_EQ(R.second, MinLength); - - for (size_t Idx = 0; Idx != SetBitsThreshold; Idx++) { - EXPECT_EQ(R.first, std::make_pair(State::BelowThreshold, State::BelowThreshold)); - R = BRW.insert(true); - } - EXPECT_EQ(R.first, std::make_pair(State::BelowThreshold, State::AboveThreshold)); - EXPECT_EQ(R.second, MinLength); - - // At this point the window's buffer contains: - // (MinLength - SetBitsThreshold = 90) 0s, followed by - // (SetBitsThreshold = 30) 1s. - // - // To go below the threshold, we need to add (90 + 1) more 0s in the window's - // buffer. At that point, the the window's buffer will contain: - // (SetBitsThreshold = 29) 1s, followed by - // (MinLength - SetBitsThreshold = 91) 0s. - // - // Right before adding the last 0, we expect the window's length to be equal to 210, - // because the bit buffer has gone through these bits: - // (MinLength - SetBitsThreshold = 90) 0s, followed by - // (SetBitsThreshold = 30) 1s, followed by - // (MinLength - SetBitsThreshold = 90) 0s. - - for (size_t Idx = 0; Idx != (MinLength - SetBitsThreshold); Idx++) { - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::AboveThreshold, State::AboveThreshold)); - } - EXPECT_EQ(R.second, 2 * MinLength - SetBitsThreshold); - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::AboveThreshold, State::Idle)); - - // Continue with the Idle -> NotFilled edge. - for (size_t Idx = 0; Idx != IdleLength - 1; Idx++) { - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::Idle)); - } - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::Idle, State::NotFilled)); - EXPECT_EQ(R.second, 1); -} - -TEST(BitRateWindowTest, WithHoles) { - size_t MinLength = 120, MaxLength = 240, IdleLength = 30; - size_t SetBitsThreshold = 30; - - Result R; - BitRateWindow BRW(MinLength, MaxLength, IdleLength, SetBitsThreshold); - - for (size_t Idx = 0; Idx != MaxLength; Idx++) - R = BRW.insert(false); - - for (size_t Idx = 0; Idx != SetBitsThreshold / 3; Idx++) - R = BRW.insert(true); - for (size_t Idx = 0; Idx != SetBitsThreshold / 3; Idx++) - R = BRW.insert(false); - for (size_t Idx = 0; Idx != SetBitsThreshold / 3; Idx++) - R = BRW.insert(true); - for (size_t Idx = 0; Idx != SetBitsThreshold / 3; Idx++) - R = BRW.insert(false); - for (size_t Idx = 0; Idx != SetBitsThreshold / 3; Idx++) - R = BRW.insert(true); - - EXPECT_EQ(R.first, std::make_pair(State::BelowThreshold, State::AboveThreshold)); - EXPECT_EQ(R.second, MinLength); - - // The window's bit buffer contains: - // 70 0s, 10 1s, 10 0s, 10 1s, 10 0s, 10 1s. - // Where: 70 = MinLength - (5 / 3) * SetBitsThresholds, ie. we need - // to add (70 + 1) more zeros to make the bit buffer go below the - // threshold and then the window's length should be: - // 70 + 50 + 70 = 190. - - BitRateWindow::Edge E; - do { - R = BRW.insert(false); - E = R.first; - } while (E.first != State::AboveThreshold || E.second != State::Idle); - EXPECT_EQ(R.second, 2 * MinLength - (5 * SetBitsThreshold) / 3); -} - -TEST(BitRateWindowTest, MinWindow) { - size_t MinLength = 120, MaxLength = 240, IdleLength = 30; - size_t SetBitsThreshold = 30; - - Result R; - BitRateWindow BRW(MinLength, MaxLength, IdleLength, SetBitsThreshold); - - BRW.insert(true); - BRW.insert(false); - for (size_t Idx = 2; Idx != SetBitsThreshold; Idx++) - BRW.insert(true); - for (size_t Idx = SetBitsThreshold; Idx != MinLength - 1; Idx++) - BRW.insert(false); - - R = BRW.insert(true); - EXPECT_EQ(R.first, std::make_pair(State::NotFilled, State::AboveThreshold)); - EXPECT_EQ(R.second, MinLength); - - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::AboveThreshold, State::Idle)); -} - -TEST(BitRateWindowTest, MaxWindow) { - size_t MinLength = 100, MaxLength = 200, IdleLength = 30; - size_t SetBitsThreshold = 50; - - Result R; - BitRateWindow BRW(MinLength, MaxLength, IdleLength, SetBitsThreshold); - - for (size_t Idx = 0; Idx != MaxLength; Idx++) - R = BRW.insert(Idx % 2 == 0); - EXPECT_EQ(R.first, std::make_pair(State::AboveThreshold, State::AboveThreshold)); - EXPECT_EQ(R.second, MaxLength); - - R = BRW.insert(false); - EXPECT_EQ(R.first, std::make_pair(State::AboveThreshold, State::Idle)); -} diff --git a/ml/kmeans/KMeans.cc b/ml/kmeans/KMeans.cc deleted file mode 100644 index e66c66c16..000000000 --- a/ml/kmeans/KMeans.cc +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "KMeans.h" -#include - -void KMeans::train(SamplesBuffer &SB, size_t MaxIterations) { - std::vector Samples = SB.preprocess(); - - MinDist = std::numeric_limits::max(); - MaxDist = std::numeric_limits::min(); - - { - std::lock_guard Lock(Mutex); - - ClusterCenters.clear(); - - dlib::pick_initial_centers(NumClusters, ClusterCenters, Samples); - dlib::find_clusters_using_kmeans(Samples, ClusterCenters, MaxIterations); - - for (const auto &S : Samples) { - CalculatedNumber MeanDist = 0.0; - - for (const auto &KMCenter : ClusterCenters) - MeanDist += dlib::length(KMCenter - S); - - MeanDist /= NumClusters; - - if (MeanDist < MinDist) - MinDist = MeanDist; - - if (MeanDist > MaxDist) - MaxDist = MeanDist; - } - } -} - -CalculatedNumber KMeans::anomalyScore(SamplesBuffer &SB) { - std::vector DSamples = SB.preprocess(); - - std::unique_lock Lock(Mutex, std::defer_lock); - if (!Lock.try_lock()) - return std::numeric_limits::quiet_NaN(); - - CalculatedNumber MeanDist = 0.0; - for (const auto &CC: ClusterCenters) - MeanDist += dlib::length(CC - DSamples.back()); - - MeanDist /= NumClusters; - - if (MaxDist == MinDist) - return 0.0; - - CalculatedNumber AnomalyScore = 100.0 * std::abs((MeanDist - MinDist) / (MaxDist - MinDist)); - return (AnomalyScore > 100.0) ? 100.0 : AnomalyScore; -} diff --git a/ml/kmeans/KMeans.h b/ml/kmeans/KMeans.h deleted file mode 100644 index 4ea3b6a89..000000000 --- a/ml/kmeans/KMeans.h +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef KMEANS_H -#define KMEANS_H - -#include -#include -#include -#include - -#include "SamplesBuffer.h" - -class KMeans { -public: - KMeans(size_t NumClusters = 2) : NumClusters(NumClusters) { - MinDist = std::numeric_limits::max(); - MaxDist = std::numeric_limits::min(); - }; - - void train(SamplesBuffer &SB, size_t MaxIterations); - CalculatedNumber anomalyScore(SamplesBuffer &SB); - -private: - size_t NumClusters; - - std::vector ClusterCenters; - - CalculatedNumber MinDist; - CalculatedNumber MaxDist; - - std::mutex Mutex; -}; - -#endif /* KMEANS_H */ diff --git a/ml/kmeans/Makefile.am b/ml/kmeans/Makefile.am deleted file mode 100644 index babdcf0df..000000000 --- a/ml/kmeans/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in diff --git a/ml/kmeans/SamplesBuffer.cc b/ml/kmeans/SamplesBuffer.cc deleted file mode 100644 index d276c6e09..000000000 --- a/ml/kmeans/SamplesBuffer.cc +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// -#include "SamplesBuffer.h" - -#include -#include -#include - -void Sample::print(std::ostream &OS) const { - for (size_t Idx = 0; Idx != NumDims - 1; Idx++) - OS << CNs[Idx] << ", "; - - OS << CNs[NumDims - 1]; -} - -void SamplesBuffer::print(std::ostream &OS) const { - for (size_t Idx = Preprocessed ? (DiffN + (SmoothN - 1) + (LagN)) : 0; - Idx != NumSamples; Idx++) { - Sample S = Preprocessed ? getPreprocessedSample(Idx) : getSample(Idx); - OS << S << std::endl; - } -} - -std::vector SamplesBuffer::getPreprocessedSamples() const { - std::vector V; - - for (size_t Idx = Preprocessed ? (DiffN + (SmoothN - 1) + (LagN)) : 0; - Idx != NumSamples; Idx++) { - Sample S = Preprocessed ? getPreprocessedSample(Idx) : getSample(Idx); - V.push_back(S); - } - - return V; -} - -void SamplesBuffer::diffSamples() { - // Panda's DataFrame default behaviour is to subtract each element from - // itself. For us `DiffN = 0` means "disable diff-ing" when preprocessing - // the samples buffer. This deviation will make it easier for us to test - // the KMeans implementation. - if (DiffN == 0) - return; - - for (size_t Idx = 0; Idx != (NumSamples - DiffN); Idx++) { - size_t High = (NumSamples - 1) - Idx; - size_t Low = High - DiffN; - - Sample LHS = getSample(High); - Sample RHS = getSample(Low); - - LHS.diff(RHS); - } -} - -void SamplesBuffer::smoothSamples() { - // Holds the mean value of each window - CalculatedNumber *AccCNs = new CalculatedNumber[NumDimsPerSample](); - Sample Acc(AccCNs, NumDimsPerSample); - - // Used to avoid clobbering the accumulator when moving the window - CalculatedNumber *TmpCNs = new CalculatedNumber[NumDimsPerSample](); - Sample Tmp(TmpCNs, NumDimsPerSample); - - CalculatedNumber Factor = (CalculatedNumber) 1 / SmoothN; - - // Calculate the value of the 1st window - for (size_t Idx = 0; Idx != std::min(SmoothN, NumSamples); Idx++) { - Tmp.add(getSample(NumSamples - (Idx + 1))); - } - - Acc.add(Tmp); - Acc.scale(Factor); - - // Move the window and update the samples - for (size_t Idx = NumSamples; Idx != (DiffN + SmoothN - 1); Idx--) { - Sample S = getSample(Idx - 1); - - // Tmp <- Next window (if any) - if (Idx >= (SmoothN + 1)) { - Tmp.diff(S); - Tmp.add(getSample(Idx - (SmoothN + 1))); - } - - // S <- Acc - S.copy(Acc); - - // Acc <- Tmp - Acc.copy(Tmp); - Acc.scale(Factor); - } - - delete[] AccCNs; - delete[] TmpCNs; -} - -void SamplesBuffer::lagSamples() { - if (LagN == 0) - return; - - for (size_t Idx = NumSamples; Idx != LagN; Idx--) { - Sample PS = getPreprocessedSample(Idx - 1); - PS.lag(getSample(Idx - 1), LagN); - } -} - -std::vector SamplesBuffer::preprocess() { - assert(Preprocessed == false); - - std::vector DSamples; - size_t OutN = NumSamples; - - // Diff - if (DiffN >= OutN) - return DSamples; - OutN -= DiffN; - diffSamples(); - - // Smooth - if (SmoothN == 0 || SmoothN > OutN) - return DSamples; - OutN -= (SmoothN - 1); - smoothSamples(); - - // Lag - if (LagN >= OutN) - return DSamples; - OutN -= LagN; - lagSamples(); - - DSamples.reserve(OutN); - Preprocessed = true; - - uint32_t MaxMT = std::numeric_limits::max(); - uint32_t CutOff = static_cast(MaxMT) * SamplingRatio; - - for (size_t Idx = NumSamples - OutN; Idx != NumSamples; Idx++) { - if (RandNums[Idx] > CutOff) - continue; - - DSample DS; - DS.set_size(NumDimsPerSample * (LagN + 1)); - - const Sample PS = getPreprocessedSample(Idx); - PS.initDSample(DS); - - DSamples.push_back(DS); - } - - return DSamples; -} diff --git a/ml/kmeans/SamplesBuffer.h b/ml/kmeans/SamplesBuffer.h deleted file mode 100644 index 1c7215cca..000000000 --- a/ml/kmeans/SamplesBuffer.h +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef SAMPLES_BUFFER_H -#define SAMPLES_BUFFER_H - -#include -#include - -#include -#include -#include - -#include - -typedef double CalculatedNumber; -typedef dlib::matrix DSample; - -class Sample { -public: - Sample(CalculatedNumber *Buf, size_t N) : CNs(Buf), NumDims(N) {} - - void initDSample(DSample &DS) const { - for (size_t Idx = 0; Idx != NumDims; Idx++) { - DS(Idx) = std::abs(CNs[Idx]); - } - } - - void add(const Sample &RHS) const { - assert(NumDims == RHS.NumDims); - - for (size_t Idx = 0; Idx != NumDims; Idx++) - CNs[Idx] += RHS.CNs[Idx]; - }; - - void diff(const Sample &RHS) const { - assert(NumDims == RHS.NumDims); - - for (size_t Idx = 0; Idx != NumDims; Idx++) - CNs[Idx] -= RHS.CNs[Idx]; - }; - - void copy(const Sample &RHS) const { - assert(NumDims == RHS.NumDims); - - std::memcpy(CNs, RHS.CNs, NumDims * sizeof(CalculatedNumber)); - } - - void scale(CalculatedNumber Factor) { - for (size_t Idx = 0; Idx != NumDims; Idx++) - CNs[Idx] *= Factor; - } - - void lag(const Sample &S, size_t LagN) { - size_t N = S.NumDims; - - for (size_t Idx = 0; Idx != (LagN + 1); Idx++) { - Sample Src(S.CNs - (Idx * N), N); - Sample Dst(CNs + (Idx * N), N); - Dst.copy(Src); - } - } - - const CalculatedNumber *getCalculatedNumbers() const { - return CNs; - }; - - void print(std::ostream &OS) const; - -private: - CalculatedNumber *CNs; - size_t NumDims; -}; - -inline std::ostream& operator<<(std::ostream &OS, const Sample &S) { - S.print(OS); - return OS; -} - -class SamplesBuffer { -public: - SamplesBuffer(CalculatedNumber *CNs, - size_t NumSamples, size_t NumDimsPerSample, - size_t DiffN, size_t SmoothN, size_t LagN, - double SamplingRatio, std::vector &RandNums) : - CNs(CNs), NumSamples(NumSamples), NumDimsPerSample(NumDimsPerSample), - DiffN(DiffN), SmoothN(SmoothN), LagN(LagN), - SamplingRatio(SamplingRatio), RandNums(RandNums), - BytesPerSample(NumDimsPerSample * sizeof(CalculatedNumber)), - Preprocessed(false) {}; - - std::vector preprocess(); - std::vector getPreprocessedSamples() const; - - size_t capacity() const { return NumSamples; } - void print(std::ostream &OS) const; - -private: - size_t getSampleOffset(size_t Index) const { - assert(Index < NumSamples); - return Index * NumDimsPerSample; - } - - size_t getPreprocessedSampleOffset(size_t Index) const { - assert(Index < NumSamples); - return getSampleOffset(Index) * (LagN + 1); - } - - void setSample(size_t Index, const Sample &S) const { - size_t Offset = getSampleOffset(Index); - std::memcpy(&CNs[Offset], S.getCalculatedNumbers(), BytesPerSample); - } - - const Sample getSample(size_t Index) const { - size_t Offset = getSampleOffset(Index); - return Sample(&CNs[Offset], NumDimsPerSample); - }; - - const Sample getPreprocessedSample(size_t Index) const { - size_t Offset = getPreprocessedSampleOffset(Index); - return Sample(&CNs[Offset], NumDimsPerSample * (LagN + 1)); - }; - - void diffSamples(); - void smoothSamples(); - void lagSamples(); - -private: - CalculatedNumber *CNs; - size_t NumSamples; - size_t NumDimsPerSample; - size_t DiffN; - size_t SmoothN; - size_t LagN; - double SamplingRatio; - std::vector &RandNums; - - size_t BytesPerSample; - bool Preprocessed; -}; - -inline std::ostream& operator<<(std::ostream& OS, const SamplesBuffer &SB) { - SB.print(OS); - return OS; -} - -#endif /* SAMPLES_BUFFER_H */ diff --git a/ml/kmeans/Tests.cc b/ml/kmeans/Tests.cc deleted file mode 100644 index 0cb595945..000000000 --- a/ml/kmeans/Tests.cc +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "ml/ml-private.h" -#include - -/* - * The SamplesBuffer class implements the functionality of the following python - * code: - * >> df = pd.DataFrame(data=samples) - * >> df = df.diff(diff_n).dropna() - * >> df = df.rolling(smooth_n).mean().dropna() - * >> df = pd.concat([df.shift(n) for n in range(lag_n + 1)], axis=1).dropna() - * - * Its correctness has been verified by automatically generating random - * data frames in Python and comparing them with the correspondent preprocessed - * SampleBuffers. - * - * The following tests are meant to catch unintended changes in the SamplesBuffer - * implementation. For development purposes, one should compare changes against - * the aforementioned python code. -*/ - -TEST(SamplesBufferTest, NS_8_NDPS_1_DN_1_SN_3_LN_1) { - size_t NumSamples = 8, NumDimsPerSample = 1; - size_t DiffN = 1, SmoothN = 3, LagN = 3; - - size_t N = NumSamples * NumDimsPerSample * (LagN + 1); - CalculatedNumber *CNs = new CalculatedNumber[N](); - - CNs[0] = 0.7568336679490107; - CNs[1] = 0.4814406581763254; - CNs[2] = 0.40073555156221874; - CNs[3] = 0.5973257298194408; - CNs[4] = 0.5334727814345868; - CNs[5] = 0.2632477193454843; - CNs[6] = 0.2684839023122384; - CNs[7] = 0.851332948637479; - - SamplesBuffer SB(CNs, NumSamples, NumDimsPerSample, DiffN, SmoothN, LagN); - SB.preprocess(); - - std::vector Samples = SB.getPreprocessedSamples(); - EXPECT_EQ(Samples.size(), 2); - - Sample S0 = Samples[0]; - const CalculatedNumber *S0_CNs = S0.getCalculatedNumbers(); - Sample S1 = Samples[1]; - const CalculatedNumber *S1_CNs = S1.getCalculatedNumbers(); - - EXPECT_NEAR(S0_CNs[0], -0.109614, 0.001); - EXPECT_NEAR(S0_CNs[1], -0.0458293, 0.001); - EXPECT_NEAR(S0_CNs[2], 0.017344, 0.001); - EXPECT_NEAR(S0_CNs[3], -0.0531693, 0.001); - - EXPECT_NEAR(S1_CNs[0], 0.105953, 0.001); - EXPECT_NEAR(S1_CNs[1], -0.109614, 0.001); - EXPECT_NEAR(S1_CNs[2], -0.0458293, 0.001); - EXPECT_NEAR(S1_CNs[3], 0.017344, 0.001); - - delete[] CNs; -} - -TEST(SamplesBufferTest, NS_8_NDPS_1_DN_2_SN_3_LN_2) { - size_t NumSamples = 8, NumDimsPerSample = 1; - size_t DiffN = 2, SmoothN = 3, LagN = 2; - - size_t N = NumSamples * NumDimsPerSample * (LagN + 1); - CalculatedNumber *CNs = new CalculatedNumber[N](); - - CNs[0] = 0.20511885291342846; - CNs[1] = 0.13151717360306558; - CNs[2] = 0.6017085062423134; - CNs[3] = 0.46256882933941545; - CNs[4] = 0.7887758447877941; - CNs[5] = 0.9237989080034406; - CNs[6] = 0.15552559051428083; - CNs[7] = 0.6309750314597955; - - SamplesBuffer SB(CNs, NumSamples, NumDimsPerSample, DiffN, SmoothN, LagN); - SB.preprocess(); - - std::vector Samples = SB.getPreprocessedSamples(); - EXPECT_EQ(Samples.size(), 2); - - Sample S0 = Samples[0]; - const CalculatedNumber *S0_CNs = S0.getCalculatedNumbers(); - Sample S1 = Samples[1]; - const CalculatedNumber *S1_CNs = S1.getCalculatedNumbers(); - - EXPECT_NEAR(S0_CNs[0], 0.005016, 0.001); - EXPECT_NEAR(S0_CNs[1], 0.326450, 0.001); - EXPECT_NEAR(S0_CNs[2], 0.304903, 0.001); - - EXPECT_NEAR(S1_CNs[0], -0.154948, 0.001); - EXPECT_NEAR(S1_CNs[1], 0.005016, 0.001); - EXPECT_NEAR(S1_CNs[2], 0.326450, 0.001); - - delete[] CNs; -} - -TEST(SamplesBufferTest, NS_8_NDPS_3_DN_2_SN_4_LN_1) { - size_t NumSamples = 8, NumDimsPerSample = 3; - size_t DiffN = 2, SmoothN = 4, LagN = 1; - - size_t N = NumSamples * NumDimsPerSample * (LagN + 1); - CalculatedNumber *CNs = new CalculatedNumber[N](); - - CNs[0] = 0.34310900399667765; CNs[1] = 0.14694315994488194; CNs[2] = 0.8246677800938796; - CNs[3] = 0.48249504592307835; CNs[4] = 0.23241087965531182; CNs[5] = 0.9595348555892567; - CNs[6] = 0.44281094035598334; CNs[7] = 0.5143142171362715; CNs[8] = 0.06391303014242555; - CNs[9] = 0.7460491027783901; CNs[10] = 0.43887217459032923; CNs[11] = 0.2814395025355999; - CNs[12] = 0.9231114281214198; CNs[13] = 0.326882401786898; CNs[14] = 0.26747939220376216; - CNs[15] = 0.7787571209969636; CNs[16] =0.5851700001235088; CNs[17] = 0.34410728945321567; - CNs[18] = 0.9394494507088997; CNs[19] =0.17567223681734334; CNs[20] = 0.42732886195446984; - CNs[21] = 0.9460522396152958; CNs[22] =0.23462747016780894; CNs[23] = 0.35983249900892145; - - SamplesBuffer SB(CNs, NumSamples, NumDimsPerSample, DiffN, SmoothN, LagN); - SB.preprocess(); - - std::vector Samples = SB.getPreprocessedSamples(); - EXPECT_EQ(Samples.size(), 2); - - Sample S0 = Samples[0]; - const CalculatedNumber *S0_CNs = S0.getCalculatedNumbers(); - Sample S1 = Samples[1]; - const CalculatedNumber *S1_CNs = S1.getCalculatedNumbers(); - - EXPECT_NEAR(S0_CNs[0], 0.198225, 0.001); - EXPECT_NEAR(S0_CNs[1], 0.003529, 0.001); - EXPECT_NEAR(S0_CNs[2], -0.063003, 0.001); - EXPECT_NEAR(S0_CNs[3], 0.219066, 0.001); - EXPECT_NEAR(S0_CNs[4], 0.133175, 0.001); - EXPECT_NEAR(S0_CNs[5], -0.293154, 0.001); - - EXPECT_NEAR(S1_CNs[0], 0.174160, 0.001); - EXPECT_NEAR(S1_CNs[1], -0.135722, 0.001); - EXPECT_NEAR(S1_CNs[2], 0.110452, 0.001); - EXPECT_NEAR(S1_CNs[3], 0.198225, 0.001); - EXPECT_NEAR(S1_CNs[4], 0.003529, 0.001); - EXPECT_NEAR(S1_CNs[5], -0.063003, 0.001); - - delete[] CNs; -} diff --git a/ml/ml-dummy.c b/ml/ml-dummy.c index fad480f4b..492dfe2fc 100644 --- a/ml/ml-dummy.c +++ b/ml/ml-dummy.c @@ -24,38 +24,23 @@ char *ml_get_host_info(RRDHOST *RH) { return NULL; } -void ml_new_dimension(RRDDIM *RD) { (void) RD; } - -void ml_delete_dimension(RRDDIM *RD) { (void) RD; } - -bool ml_is_anomalous(RRDDIM *RD, double Value, bool Exists) { - (void) RD; (void) Value; (void) Exists; - return false; -} - -char *ml_get_anomaly_events(RRDHOST *RH, const char *AnomalyDetectorName, - int AnomalyDetectorVersion, time_t After, time_t Before) { - (void) RH; (void) AnomalyDetectorName; - (void) AnomalyDetectorVersion; (void) After; (void) Before; +char *ml_get_host_runtime_info(RRDHOST *RH) { + (void) RH; return NULL; } -char *ml_get_anomaly_event_info(RRDHOST *RH, const char *AnomalyDetectorName, - int AnomalyDetectorVersion, time_t After, time_t Before) { - (void) RH; (void) AnomalyDetectorName; - (void) AnomalyDetectorVersion; (void) After; (void) Before; +char *ml_get_host_models(RRDHOST *RH) { + (void) RH; return NULL; } -void ml_process_rrdr(RRDR *R, int MaxAnomalyRates) { - (void) R; - (void) MaxAnomalyRates; -} +void ml_new_dimension(RRDDIM *RD) { (void) RD; } -void ml_dimension_update_name(RRDSET *RS, RRDDIM *RD, const char *name) { - (void) RS; - (void) RD; - (void) name; +void ml_delete_dimension(RRDDIM *RD) { (void) RD; } + +bool ml_is_anomalous(RRDDIM *RD, double Value, bool Exists) { + (void) RD; (void) Value; (void) Exists; + return false; } bool ml_streaming_enabled() { diff --git a/ml/ml-private.h b/ml/ml-private.h index 7b3e00684..2bd72ac5a 100644 --- a/ml/ml-private.h +++ b/ml/ml-private.h @@ -3,7 +3,7 @@ #ifndef ML_PRIVATE_H #define ML_PRIVATE_H -#include "kmeans/KMeans.h" +#include "KMeans.h" #include "ml/ml.h" #include diff --git a/ml/ml.cc b/ml/ml.cc index 7275d88b8..1a7d6ae25 100644 --- a/ml/ml.cc +++ b/ml/ml.cc @@ -16,7 +16,7 @@ bool ml_enabled(RRDHOST *RH) { if (!Cfg.EnableAnomalyDetection) return false; - if (simple_pattern_matches(Cfg.SP_HostsToSkip, RH->hostname)) + if (simple_pattern_matches(Cfg.SP_HostsToSkip, rrdhost_hostname(RH))) return false; return true; @@ -76,7 +76,7 @@ void ml_new_dimension(RRDDIM *RD) { if (static_cast(RD->update_every) != H->updateEvery()) return; - if (simple_pattern_matches(Cfg.SP_ChartsToSkip, RS->name)) + if (simple_pattern_matches(Cfg.SP_ChartsToSkip, rrdset_name(RS))) return; Dimension *D = new Dimension(RD); @@ -108,7 +108,7 @@ char *ml_get_host_info(RRDHOST *RH) { ConfigJson["enabled"] = false; } - return strdup(ConfigJson.dump(2, '\t').c_str()); + return strdupz(ConfigJson.dump(2, '\t').c_str()); } char *ml_get_host_runtime_info(RRDHOST *RH) { @@ -124,97 +124,24 @@ char *ml_get_host_runtime_info(RRDHOST *RH) { return strdup(ConfigJson.dump(1, '\t').c_str()); } -bool ml_is_anomalous(RRDDIM *RD, double Value, bool Exists) { - Dimension *D = static_cast(RD->ml_dimension); - if (!D) - return false; - - D->addValue(Value, Exists); - bool Result = D->predict().second; - return Result; -} - -char *ml_get_anomaly_events(RRDHOST *RH, const char *AnomalyDetectorName, - int AnomalyDetectorVersion, time_t After, time_t Before) { - if (!RH || !RH->ml_host) { - error("No host"); - return nullptr; - } - - Host *H = static_cast(RH->ml_host); - std::vector> TimeRanges; - - bool Res = H->getAnomaliesInRange(TimeRanges, AnomalyDetectorName, - AnomalyDetectorVersion, - H->getUUID(), - After, Before); - if (!Res) { - error("DB result is empty"); - return nullptr; - } - - nlohmann::json Json = TimeRanges; - return strdup(Json.dump(4).c_str()); -} - -char *ml_get_anomaly_event_info(RRDHOST *RH, const char *AnomalyDetectorName, - int AnomalyDetectorVersion, time_t After, time_t Before) { - if (!RH || !RH->ml_host) { - error("No host"); - return nullptr; - } - - Host *H = static_cast(RH->ml_host); +char *ml_get_host_models(RRDHOST *RH) { + nlohmann::json ModelsJson; - nlohmann::json Json; - bool Res = H->getAnomalyInfo(Json, AnomalyDetectorName, - AnomalyDetectorVersion, - H->getUUID(), - After, Before); - if (!Res) { - error("DB result is empty"); - return nullptr; + if (RH && RH->ml_host) { + Host *H = static_cast(RH->ml_host); + H->getModelsAsJson(ModelsJson); + return strdup(ModelsJson.dump(2, '\t').c_str()); } - return strdup(Json.dump(4, '\t').c_str()); -} - -void ml_process_rrdr(RRDR *R, int MaxAnomalyRates) { - if (R->rows != 1) - return; - - if (MaxAnomalyRates < 1 || MaxAnomalyRates >= R->d) - return; - - NETDATA_DOUBLE *CNs = R->v; - RRDR_DIMENSION_FLAGS *DimFlags = R->od; - - std::vector> V; - - V.reserve(R->d); - for (int Idx = 0; Idx != R->d; Idx++) - V.emplace_back(CNs[Idx], Idx); - - std::sort(V.rbegin(), V.rend()); - - for (int Idx = MaxAnomalyRates; Idx != R->d; Idx++) { - int UnsortedIdx = V[Idx].second; - - int OldFlags = static_cast(DimFlags[UnsortedIdx]); - int NewFlags = OldFlags | RRDR_DIMENSION_HIDDEN; - - DimFlags[UnsortedIdx] = static_cast(NewFlags); - } + return nullptr; } -void ml_dimension_update_name(RRDSET *RS, RRDDIM *RD, const char *Name) { - (void) RS; - +bool ml_is_anomalous(RRDDIM *RD, double Value, bool Exists) { Dimension *D = static_cast(RD->ml_dimension); if (!D) - return; + return false; - D->setAnomalyRateRDName(Name); + return D->predict(Value, Exists); } bool ml_streaming_enabled() { diff --git a/ml/ml.h b/ml/ml.h index e07eb094e..8e62c4988 100644 --- a/ml/ml.h +++ b/ml/ml.h @@ -12,7 +12,7 @@ extern "C" { // This is a DBEngine function redeclared here so that we can free // the anomaly rate dimension, whenever its backing dimension is freed. -extern void rrddim_free(RRDSET *st, RRDDIM *rd); +void rrddim_free(RRDSET *st, RRDDIM *rd); typedef void* ml_host_t; typedef void* ml_dimension_t; @@ -28,22 +28,13 @@ void ml_delete_host(RRDHOST *RH); char *ml_get_host_info(RRDHOST *RH); char *ml_get_host_runtime_info(RRDHOST *RH); +char *ml_get_host_models(RRDHOST *RH); void ml_new_dimension(RRDDIM *RD); void ml_delete_dimension(RRDDIM *RD); bool ml_is_anomalous(RRDDIM *RD, double value, bool exists); -char *ml_get_anomaly_events(RRDHOST *RH, const char *AnomalyDetectorName, - int AnomalyDetectorVersion, time_t After, time_t Before); - -char *ml_get_anomaly_event_info(RRDHOST *RH, const char *AnomalyDetectorName, - int AnomalyDetectorVersion, time_t After, time_t Before); - -void ml_process_rrdr(RRDR *R, int MaxAnomalyRates); - -void ml_dimension_update_name(RRDSET *RS, RRDDIM *RD, const char *name); - bool ml_streaming_enabled(); #define ML_ANOMALY_RATES_CHART_ID "anomaly_detection.anomaly_rates" diff --git a/netdata-installer.sh b/netdata-installer.sh index 941cf13f3..94745a295 100755 --- a/netdata-installer.sh +++ b/netdata-installer.sh @@ -79,7 +79,7 @@ _cannot_use_tmpdir() { if [ -z "${TMPDIR}" ] || _cannot_use_tmpdir "${TMPDIR}"; then if _cannot_use_tmpdir /tmp; then if _cannot_use_tmpdir "${PWD}"; then - fatal "UUnable to find a usable temporary directory. Please set \$TMPDIR to a path that is both writable and allows execution of files and try again." I0000 + fatal "Unable to find a usable temporary directory. Please set \$TMPDIR to a path that is both writable and allows execution of files and try again." I0000 else TMPDIR="${PWD}" fi @@ -747,14 +747,11 @@ detect_libc() { echo >&2 " Detected musl" libc="musl" else - ls_path=$(command -v ls) - if [ -n "${ls_path}" ] ; then - cmd=$(ldd "$ls_path" | grep -w libc | cut -d" " -f 3) + cmd=$(ldd /bin/sh | grep -w libc | cut -d" " -f 3) if bash -c "${cmd}" 2>&1 | grep -q -i "GNU C Library"; then echo >&2 " Detected GLIBC" libc="glibc" fi - fi fi if [ -z "$libc" ]; then @@ -937,6 +934,34 @@ if [ "$have_autotools" ]; then fi fi +# function to extract values from the config file +config_option() { + section="${1}" + key="${2}" + value="${3}" + + if [ -x "${NETDATA_PREFIX}/usr/sbin/netdata" ] && [ -r "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ]; then + "${NETDATA_PREFIX}/usr/sbin/netdata" \ + -c "${NETDATA_PREFIX}/etc/netdata/netdata.conf" \ + -W get "${section}" "${key}" "${value}" || + echo "${value}" + else + echo "${value}" + fi +} + +# the user netdata will run as +if [ "$(id -u)" = "0" ]; then + NETDATA_USER="$(config_option "global" "run as user" "netdata")" + ROOT_USER="root" +else + NETDATA_USER="${USER}" + ROOT_USER="${USER}" +fi +NETDATA_GROUP="$(id -g -n "${NETDATA_USER}")" +[ -z "${NETDATA_GROUP}" ] && NETDATA_GROUP="${NETDATA_USER}" +echo >&2 "Netdata user and group set to: ${NETDATA_USER}/${NETDATA_GROUP}" + # shellcheck disable=SC2086 if ! run ./configure \ --prefix="${NETDATA_PREFIX}/usr" \ @@ -946,7 +971,7 @@ if ! run ./configure \ --libdir="${NETDATA_PREFIX}/usr/lib" \ --with-zlib \ --with-math \ - --with-user=netdata \ + --with-user="${NETDATA_USER}" \ ${NETDATA_CONFIGURE_OPTIONS} \ CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}"; then fatal "Failed to configure Netdata sources." I000A @@ -1080,34 +1105,6 @@ progress "Read installation options from netdata.conf" [ ! -f "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ] && touch "${NETDATA_PREFIX}/etc/netdata/netdata.conf" -# function to extract values from the config file -config_option() { - section="${1}" - key="${2}" - value="${3}" - - if [ -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ]; then - "${NETDATA_PREFIX}/usr/sbin/netdata" \ - -c "${NETDATA_PREFIX}/etc/netdata/netdata.conf" \ - -W get "${section}" "${key}" "${value}" || - echo "${value}" - else - echo "${value}" - fi -} - -# the user netdata will run as -if [ "$(id -u)" = "0" ]; then - NETDATA_USER="$(config_option "global" "run as user" "netdata")" - ROOT_USER="root" -else - NETDATA_USER="${USER}" - ROOT_USER="${USER}" -fi -NETDATA_GROUP="$(id -g -n "${NETDATA_USER}")" -[ -z "${NETDATA_GROUP}" ] && NETDATA_GROUP="${NETDATA_USER}" -echo >&2 "Netdata user and group is finally set to: ${NETDATA_USER}/${NETDATA_GROUP}" - # port defport=19999 NETDATA_PORT="$(config_option "web" "default port" ${defport})" @@ -1444,7 +1441,7 @@ install_go() { fi run chmod 0750 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/go.d.plugin" if command -v setcap 1>/dev/null 2>&1; then - run setcap cap_net_admin+epi "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/go.d.plugin" + run setcap "cap_net_admin+epi cap_net_raw=eip" "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/go.d.plugin" fi rm -rf "${tmp}" diff --git a/netdata.spec.in b/netdata.spec.in index a75e48dda..cbbe6ab73 100644 --- a/netdata.spec.in +++ b/netdata.spec.in @@ -516,7 +516,7 @@ rm -rf "${RPM_BUILD_ROOT}" %attr(4750,root,netdata) %{_libexecdir}/%{name}/plugins.d/freeipmi.plugin # go.d.plugin (the capability required for wireguard module) -%caps(cap_net_admin=epi) %{_libexecdir}/%{name}/plugins.d/go.d.plugin +%caps(cap_net_admin,cap_net_raw=eip) %{_libexecdir}/%{name}/plugins.d/go.d.plugin # Enforce 0644 for files and 0755 for directories # for the netdata web directory diff --git a/packaging/PLATFORM_SUPPORT.md b/packaging/PLATFORM_SUPPORT.md index 068f0aecc..106c80892 100644 --- a/packaging/PLATFORM_SUPPORT.md +++ b/packaging/PLATFORM_SUPPORT.md @@ -50,13 +50,14 @@ to work on these platforms with minimal user effort. | Platform | Version | Official Native Packages | Notes | | -------- | ------- | ------------------------ | ----- | -| Alpine Linux | 3.16 | No | The latest release of Alpine Linux is guaranteed to remain at **Core** tier due to usage for our Docker images | +| Alpine Linux | 3.17 | No | The latest release of Alpine Linux is guaranteed to remain at **Core** tier due to usage for our Docker images | | Alma Linux | 9.x | x86\_64, AArch64 | Also includes support for Rocky Linux and other ABI compatible RHEL derivatives | | Alma Linux | 8.x | x86\_64, AArch64 | Also includes support for Rocky Linux and other ABI compatible RHEL derivatives | | CentOS | 7.x | x86\_64 | | | Docker | 19.03 or newer | x86\_64, i386, ARMv7, AArch64, POWER8+ | See our [Docker documentation](/packaging/docker/README.md) for more info on using Netdata on Docker | | Debian | 11.x | x86\_64, i386, ARMv7, AArch64 | | | Debian | 10.x | x86\_64, i386, ARMv7, AArch64 | | +| Fedora | 37 | x86\_64, AArch64 | | | Fedora | 36 | x86\_64, ARMv7, AArch64 | | | Fedora | 35 | x86\_64, ARMv7, AArch64 | | | openSUSE | Leap 15.4 | x86\_64, AArch64 | | @@ -66,8 +67,9 @@ to work on these platforms with minimal user effort. | Red Hat Enterprise Linux | 9.x | x86\_64, AArch64 | | | Red Hat Enterprise Linux | 8.x | x86\_64, AArch64 | | | Red Hat Enterprise Linux | 7.x | x86\_64 | | +| Ubuntu | 22.10 | x86\_64, ARMv7, AArch64 | | | Ubuntu | 22.04 | x86\_64, ARMv7, AArch64 | | -| Ubuntu | 20.04 | x86\_64, i386, ARMv7, AArch64 | | +| Ubuntu | 20.04 | x86\_64, ARMv7, AArch64 | | | Ubuntu | 18.04 | x86\_64, i386, ARMv7, AArch64 | | ### Intermediate @@ -81,9 +83,9 @@ with minimal user effort. | Platform | Version | Official Native Packages | Notes | | -------- | ------- | ------------------------ | ----- | +| Alpine Linux | 3.16 | No | | | Alpine Linux | 3.15 | No | | | Alpine Linux | 3.14 | No | | -| Alpine Linux | 3.13 | No | | | Arch Linux | Latest | No | We officially recommend the community packages available for Arch Linux | | Manjaro Linux | Latest | No | We officially recommend the community packages available for Arch Linux | @@ -104,9 +106,9 @@ platforms, but may require some extra effort from users. | FreeBSD | 13-STABLE | No | Netdata is included in the FreeBSD Ports Tree, and this is the recommended installation method on FreeBSD | | FreeBSD | 12-STABLE | No | Netdata is included in the FreeBSD Ports Tree, and this is the recommended installation method on FreeBSD | | Gentoo | Latest | No | | -| macOS | 12 | No | Planned for **Core** tier support. Currently only works for Intel-based hardware. Requires Homebrew for dependencies | -| macOS | 11 | No | Planned for **Core** tier support. Currently only works for Intel-based hardware. Requires Homebrew for dependencies. | -| macOS | 10.15 | No | Planned for **Core** tier support. Requires Homebrew for dependencies. | +| macOS | 12 | No | Currently only works for Intel-based hardware. Requires Homebrew for dependencies | +| macOS | 11 | No | Currently only works for Intel-based hardware. Requires Homebrew for dependencies. | +| macOS | 10.15 | No | Requires Homebrew for dependencies. | | openSUSE | Tumbleweed | No | | ## Third-party supported platforms @@ -144,8 +146,8 @@ This is a list of platforms that we have supported in the recent past but no lon | Platform | Version | Notes | | -------- | ------- | ----- | +| Alpine Linux | 3.13 | EOL as of 2022-11-01 | | Alpine Linux | 3.12 | EOL as of 2022-05-01 | -| Alpine Linux | 3.11 | EOL as of 2021-11-01 | | Debian | 9.x | EOL as of 2022-06-30 | | Fedora | 34 | EOL as of 2022-06-07 | | Fedora | 33 | EOL as of 2021-11-30 | diff --git a/packaging/building-native-packages-locally.md b/packaging/building-native-packages-locally.md new file mode 100644 index 000000000..d4949cf52 --- /dev/null +++ b/packaging/building-native-packages-locally.md @@ -0,0 +1,106 @@ + + +# How to build native (DEB/RPM) packages locally for testing + +## Requirements + +To build native packages locally, you will need the following: + +* A working Docker or Podman host. +* A local copy of the source tree you want to build from. + +## Building the packages + +In the root of the source tree from which you want to build, clean up any existing files left over from a previous build +and then run: + +```bash +docker run -it --rm -e VERSION=0.1 -v $PWD:/netdata netdata/package-builders: +``` + +or + +```bash +podman run -it --rm -e VERSION=0.1 -v $PWD:/netdata netdata/package-builders: +``` + +The `` should be the lowercase distribution name with no spaces, followed by the +release of that distribution. For example, `centos7` to build on CentOS 7, or `ubuntu20.04` +to build on Ubuntu 20.04. Note that we use Alma Linux for builds on CentOS/RHEL 8 or newer. See +[netdata/package-builders](https://hub.docker.com/r/netdata/package-builders/tags) for all available tags. + +The value passed in the `VERSION` environment variable can be any version number accepted by the type of package +being built. As a general rule, it needs to start with a digit, and must include a `.` somewhere. + +Once it finishes, the built packages can be found under `artifacts/` in the source tree. + +If an error is encountered and the build is being run interactively, it will drop to a shell to allow you to +inspect the state of the container and look at build logs. + +### Detailed explanation + +The environments used for building our packages are fully self-contianed Docker images built from [Dockerfiles](https://github.com/netdata/helper-images/tree/master/package-builders) +These are published on Docker +Hub with the image name `netdata/package-builders`, and tagged using the name and version of the distribution +(with the tag corresponding to the suffix on the associated Dockerfile). + +The build code expects the following requirements to be met: + +- It expects the source tree it should build from to be located at `/netdata`, and expects that said source tree + is clean (no artifacts left over from previous builds). +- It expects an environment variable named `VERSION` to be defined, and uses this to control what version number + will be shown in the package metadata and filenames. + +Internally, the source tree gets copied to a temporary location for the build process so that the source tree can +be mounted directly from the host without worrying about leaving a dirty tree behind, any templating or file +movements required for the build to work are done, the package build command is invoked with the correct arguments, +and then the resultant packages are copied to the `artifacts/` directory in the original source tree so they are +accessible after the container exits. + +## Finding build logs after a failed build + +Build logs and artifacts can be found in the build directory, whose location varies by distribution. + +On DEB systems (Ubuntu and Debian), the build directory inside the container is located at `/usr/src/netdata` + +On RPM systems except openSUSE, the build directory inside the container is located under `/root/rpmbuild/BUILD/` +and varies based on the package version number. + +On openSUSE, the build directory inside the container is located under `/usr/src/packages/BUILD`and varies based +on the package version number. + +## Building for other architectures + +If you need to test a build for an architecture that does not match your host system, you can do so by setting up +QEMU user-mode emulation. This requires a Linux kernel with binfmt\_misc support (all modern distributions provide +this out of the box, but I’m not sure about WSL or Docker Desktop). + +The quick and easy way to do this is to run the following: + +```bash +docker run --rm --privileged multiarch/qemu-user-static --reset -p yes +``` + +or + +```bash +podman run --rm --privileged multiarch/qemu-user-static --reset -p yes +``` + +This will set up the required QEMU user-mode emulation until you reboot. Note that if using Podman, you will need +to run this as root and not as a rootless container (the package builds work fine in a rootless container though, +even if doing cross-architecture builds). + +Once you have that set up, the command to build the packages is the same as above, you just need to add a correct +`--platform` option to the `docker run` or `podman run` command. The current list of architectures we build for, +and the correct value for the `--platform` option is: + +- 32-bit ARMv7: `linux/arm/v7` +- 64-bit ARMv8: `linux/arm64/v8` +- 32-bit x86: `linux/i386` +- 64-bit x86: `linux/amd64` diff --git a/packaging/current_libbpf.checksums b/packaging/current_libbpf.checksums index e7e3985a8..9a8b8f8cf 100644 --- a/packaging/current_libbpf.checksums +++ b/packaging/current_libbpf.checksums @@ -1 +1 @@ -8909385c347848b5994d9daa33be12e35ffc529ba0e01b5c944e0216bf78f79b v0.8.0_netdata.tar.gz +63fe4ac3f6807e8ff4cd3af2ffae0091eb177fb7f6aca2f03d3f201a609b988c v1.0.1_netdata.tar.gz diff --git a/packaging/current_libbpf.version b/packaging/current_libbpf.version index 606129075..bb58ffc40 100644 --- a/packaging/current_libbpf.version +++ b/packaging/current_libbpf.version @@ -1 +1 @@ -0.8.0_netdata +1.0.1_netdata diff --git a/packaging/docker/README.md b/packaging/docker/README.md index e3697fdaa..d00262a1b 100644 --- a/packaging/docker/README.md +++ b/packaging/docker/README.md @@ -252,6 +252,11 @@ If you don't want to destroy and recreate your container, you can edit the Agent above section on [configuring Agent containers](#configure-agent-containers) to find the appropriate method based on how you created the container. +Alternatively, you can directly use the hostname from the node running the container by mounting +`/etc/hostname` from the host in the container. With `docker run`, this can be done by adding `--volume +/etc/hostname:/etc/hostname:ro` to the options. If you are using Docker Compose, you can add an entry to the +container's `volumes` section reading `- /etc/hostname:/etc/hostname:ro`. + ### Add or remove other volumes Some volumes are optional depending on how you use Netdata: diff --git a/packaging/ebpf-co-re.checksums b/packaging/ebpf-co-re.checksums index 041f6dfb2..da7cb0510 100644 --- a/packaging/ebpf-co-re.checksums +++ b/packaging/ebpf-co-re.checksums @@ -1 +1 @@ -9181063ec4a40f1b2ce5d7ac702eb9de233d127c6e21aac8a3d3f7c185502726 netdata-ebpf-co-re-glibc-v0.9.3.3.tar.xz +9fe4fccc160ca9e0fd0ffd8894dbf6e5fddcf9ca3a4ee04cb645bb7703e8cef2 netdata-ebpf-co-re-glibc-v1.0.1.tar.xz diff --git a/packaging/ebpf-co-re.version b/packaging/ebpf-co-re.version index 599038a08..b18d46540 100644 --- a/packaging/ebpf-co-re.version +++ b/packaging/ebpf-co-re.version @@ -1 +1 @@ -v0.9.3.3 +v1.0.1 diff --git a/packaging/ebpf.checksums b/packaging/ebpf.checksums index dc5ce6cf1..e5afcb860 100644 --- a/packaging/ebpf.checksums +++ b/packaging/ebpf.checksums @@ -1,3 +1,3 @@ -cee483b29e1fd3125d635d03f9266d267eab1ba13e02c99afcf6a84bdcb729f4 ./netdata-kernel-collector-glibc-v0.9.3.tar.xz -33aeae2cf7e9c0967c62f262bf6c6c44db49dc5dbb6d3a16e8bfde4433779069 ./netdata-kernel-collector-musl-v0.9.3.tar.xz -9eba23184be7c9b29d22271e439e3fac517d32d500bd796c4e21f653b6f4d337 ./netdata-kernel-collector-static-v0.9.3.tar.xz +e3836908a5cfd5a1b6c71463645ed7040be1a4890767844b744fe1054910b51f ./netdata-kernel-collector-glibc-v1.0.1.tar.xz +091382fece7a470e505df4c655d834a8a49e50aa2a2fec4a52a324ab0ccd9870 ./netdata-kernel-collector-musl-v1.0.1.tar.xz +11497ccb4f2cdac0d80b00bf97d2f1633c972e8720abdda2a63bd0de95859840 ./netdata-kernel-collector-static-v1.0.1.tar.xz diff --git a/packaging/ebpf.version b/packaging/ebpf.version index 188bef592..b18d46540 100644 --- a/packaging/ebpf.version +++ b/packaging/ebpf.version @@ -1 +1 @@ -v0.9.3 +v1.0.1 diff --git a/packaging/go.d.checksums b/packaging/go.d.checksums index 4c73aecd8..806a45f40 100644 --- a/packaging/go.d.checksums +++ b/packaging/go.d.checksums @@ -1,16 +1,17 @@ -c2ad4897acf97555c3874dd301bc6cc76e2e99fa0fdcad469c0869ea70cefaef *config.tar.gz -3d04b65ceb9925f5fac684d54bc6641c0b52314e25d73dae002cc102fe07cd2d *go.d.plugin-v0.35.0.darwin-amd64.tar.gz -7311c4900f2cf24c9f49e5a5dd26d113b2c7b19a5db68dc64643fea1e0f95d29 *go.d.plugin-v0.35.0.freebsd-386.tar.gz -bb9bc9f82cf80ef35df14f1426f91861f749287bccfe54bf8b50da9a28bc434a *go.d.plugin-v0.35.0.freebsd-amd64.tar.gz -e1c7d5670a59348b7adaa16da1445896f5714fef0b8e1d9036729779f6a35fc0 *go.d.plugin-v0.35.0.freebsd-arm.tar.gz -8e129fd490fcf2f6999131e5fe9a48feb50532c2f2dd811204761cc8d7e87649 *go.d.plugin-v0.35.0.freebsd-arm64.tar.gz -a2b01aff7e078aa1ecc1f8a291ea02e8cfdd5fab4c9eaa0d776b606901c1fc51 *go.d.plugin-v0.35.0.linux-386.tar.gz -4aefa432b75bf81ef3cd787740d2ce29ceb3293c749acdd67038ef5a30214a36 *go.d.plugin-v0.35.0.linux-amd64.tar.gz -fd4fb4ffff203235f62e3d668d8b6aabe084fd711c865c4dd0bed7a002b3a671 *go.d.plugin-v0.35.0.linux-arm.tar.gz -005dd629ce9045e8bfb4e710b7aed898f6a39017d37a3d16957665cde0a70a84 *go.d.plugin-v0.35.0.linux-arm64.tar.gz -3a80c16a35e76d7a620b5e7bba8eced78cc44cc9803b8f41814b6c8516d0bfbd *go.d.plugin-v0.35.0.linux-mips.tar.gz -c1da9fec36aefde1af3ac0d70fc64bdc2726e6f5c0434f636e8e114d83f155c4 *go.d.plugin-v0.35.0.linux-mips64.tar.gz -77c0ec5867a1bbe6cc2b718aa00be8b112ca90b951fdd47e1d93218a514863ce *go.d.plugin-v0.35.0.linux-mips64le.tar.gz -740ad96ba6089f82a711863f063f1b4228b279c16eb85638e93c427a410ae719 *go.d.plugin-v0.35.0.linux-mipsle.tar.gz -65fade14e33ccf7323a752a8dd6507cf5ec9db05bd66de8d09ffb24b21b300dd *go.d.plugin-v0.35.0.linux-ppc64.tar.gz -5b6d3b289fa3ec6a3276e896530ed7760b94105870bc52662ed2d8369122e599 *go.d.plugin-v0.35.0.linux-ppc64le.tar.gz +67335df67f1629d9dc6dc559f91d26499f023b43a3fb2cece22d7c2919653dae *config.tar.gz +cae0acc8c19112c35927ccd4ef53f594c8d805467f204db84305ca8ddfccfcfa *go.d.plugin-v0.45.0.darwin-amd64.tar.gz +dcaddcea32a26392db7f13867226c62ba87aeb00c93931d1fc10d0641eb04e3a *go.d.plugin-v0.45.0.darwin-arm64.tar.gz +e89153033d2b8d6b59f163f1419ebaaae8414054f1a33bca52f76651848b528d *go.d.plugin-v0.45.0.freebsd-386.tar.gz +da3ba485995a60879cb51819ac2f9ecf81d23f64a0afb584bdc134a1fe4a2251 *go.d.plugin-v0.45.0.freebsd-amd64.tar.gz +ca1e153ac90714c0956a7ed4bcf14bf263f2c65a1876e9c8c24dd1b2bcd5b74a *go.d.plugin-v0.45.0.freebsd-arm.tar.gz +e25fcd7c3fa8aed01efff8ea25c3276de65d2efacff70fa4670a970272ffbba3 *go.d.plugin-v0.45.0.freebsd-arm64.tar.gz +7bfd345bdc30292a9145e4397b15a9004d78049006702727cd5a52900531dfa1 *go.d.plugin-v0.45.0.linux-386.tar.gz +7a6dd92ab6892aedd0a257ffbded54a0efa7cf95fb7f738387c8d7394e57eb09 *go.d.plugin-v0.45.0.linux-amd64.tar.gz +834a26c486a579991d76dc07625e135bf13fe4387627076bdbbb14bcb55ff978 *go.d.plugin-v0.45.0.linux-arm.tar.gz +1dda73a062ba20d2996334d878fd74207ad49b9f164530a5ad2144c70947d5a8 *go.d.plugin-v0.45.0.linux-arm64.tar.gz +8b52e67cf1e4d11776d5c5c8a4e246412caa1cffd36bafa5a8cb38a8e1867dee *go.d.plugin-v0.45.0.linux-mips.tar.gz +9a95f9df3527693a0a92c7b8ef1bec537e84014c75f32604adce4540e924576e *go.d.plugin-v0.45.0.linux-mips64.tar.gz +4ce0ec6068edf8218f68785f6ce258ed76ffea50a5953f7f75b9a48e1d912a81 *go.d.plugin-v0.45.0.linux-mips64le.tar.gz +360812ea936768926b38031c9dff2ecda2c80733e875f1c317371dee9d56a76c *go.d.plugin-v0.45.0.linux-mipsle.tar.gz +d7b594d84d47a0d2c6265c8fa9317285a53821f3e9aac5d31bcce0c8706fb369 *go.d.plugin-v0.45.0.linux-ppc64.tar.gz +dc29665076eecfb05d63ac243f97bd4d09f8dedae8c5285b349bd227e34ce249 *go.d.plugin-v0.45.0.linux-ppc64le.tar.gz diff --git a/packaging/go.d.version b/packaging/go.d.version index ab4e51c67..67d3cf473 100644 --- a/packaging/go.d.version +++ b/packaging/go.d.version @@ -1 +1 @@ -v0.35.0 +v0.45.0 diff --git a/packaging/installer/README.md b/packaging/installer/README.md index 2854d0723..3a4237d52 100644 --- a/packaging/installer/README.md +++ b/packaging/installer/README.md @@ -3,9 +3,9 @@ title: "Installation guide" custom_edit_url: https://github.com/netdata/netdata/edit/master/packaging/installer/README.md --> -import { Install, InstallBox } from '../../../src/components/Install/' +import { Install, InstallBox } from '@site/src/components/Install/' -import { OneLineInstallWget, OneLineInstallCurl } from '../../../src/components/OneLineInstall/' +import { OneLineInstallWget, OneLineInstallCurl } from '@site/src/components/OneLineInstall/' # Installation guide diff --git a/packaging/installer/UNINSTALL.md b/packaging/installer/UNINSTALL.md index 54ca771f9..af2314f65 100644 --- a/packaging/installer/UNINSTALL.md +++ b/packaging/installer/UNINSTALL.md @@ -10,17 +10,41 @@ custom_edit_url: https://github.com/netdata/netdata/edit/master/packaging/instal > issues with your Netdata Agent installation, consider our [**reinstall Netdata** > doc](/packaging/installer/REINSTALL.md) instead of removing the Netdata Agent entirely. -Our self-contained uninstaller is able to remove Netdata installations created with shell installer. It doesn't need any -other Netdata repository files to be run. All it needs is an `.environment` file, which is created during installation -(with shell installer) and put in `${NETDATA_USER_CONFIG_DIR}/.environment` (by default `/etc/netdata/.environment`). -That file contains some parameters which are passed to our installer and which are needed during uninstallation process. -Mainly two parameters are needed: +The recommended method to uninstall Netdata on a system is to use our kickstart installer script with the `--uninstall` option like so: ```sh -NETDATA_PREFIX -NETDATA_ADDED_TO_GROUPS +wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && sh /tmp/netdata-kickstart.sh --uninstall ``` +Or (if you have curl but not wget): + +```sh +curl https://my-netdata.io/kickstart.sh > /tmp/netdata-kickstart.sh && sh /tmp/netdata-kickstart.sh --uninstall +``` + +This will work in most cases without you needing to do anything more other than accepting removal of configuration +and data files. You can confirm whether this approach will work for you by adding `--dry-run` to the list of +options. If that produces a line with a message like `Would attempt to uninstall existing install`, then this +method will work on your system. + +If you used a non-standard installation prefix, you may need to specify that prefix using the `--old-install-prefix` +option when uninstalling this way. + +## Unofficial installs + +If you used a third-party package to install Netdata, then the above method will usually not work, and you will +need to use whatever mechanism you used to originally install Netdata to uninstall it. + +## Uninstalling manually + +Most official installs of Netdata include an uninstaller script that can be manually invoked instead of using the +kickstart script (internally, the kickstart script also uses this uninstaller script, it just handles the process +outlined below for you). + +This uninstaller script is self-contained other than requiring a `.environment` file that was generated during +installation. In most cases, this will be found in `/etc/netdata/.environment`, though if you used a non-standard +installation prefix it will usually be located in a similar place under that prefix. + A workflow for uninstallation looks like this: 1. Find your `.environment` file, which is usually `/etc/netdata/.environment` in a default installation. @@ -35,7 +59,9 @@ NETDATA_ADDED_TO_GROUPS="" # Additional groups for a user ru 3.1 **Interactive mode (Default)** - The default mode in the uninstaller script is **interactive**. This means that the script provides the user the option to reply with "yes" (`y`/`Y`) or "no" (`n`/`N`) to control the removal of each Netdata asset in the filesystem. + The default mode in the uninstaller script is **interactive**. This means that the script provides you + the option to reply with "yes" (`y`/`Y`) or "no" (`n`/`N`) to control the removal of each Netdata asset in + the filesystem. ```sh ${NETDATA_PREFIX}/usr/libexec/netdata/netdata-uninstaller.sh --yes --env @@ -43,7 +69,9 @@ NETDATA_ADDED_TO_GROUPS="" # Additional groups for a user ru 3.2 **Non-interactive mode** - If you are sure and you know what you are doing, you can speed up the removal of the Netdata assets from the filesystem without any questions by using the force option (`-f`/`--force`). This option will remove all the Netdata assets in a **non-interactive** mode. + If you are sure and you know what you are doing, you can speed up the removal of the Netdata assets from the + filesystem without any questions by using the force option (`-f`/`--force`). This option will remove all the + Netdata assets in a **non-interactive** mode. ```sh ${NETDATA_PREFIX}/usr/libexec/netdata/netdata-uninstaller.sh --yes --force --env @@ -58,9 +86,7 @@ chmod +x ./netdata-uninstaller.sh ./netdata-uninstaller.sh --yes --env ``` -The default `environment_file` is `/etc/netdata/.environment`. +The default `environment_file` is `/etc/netdata/.environment`. > Note: This uninstallation method assumes previous installation with `netdata-installer.sh` or the kickstart script. -> Currently using it when Netdata was installed by a package manager can work or cause unexpected results. - - +> Using it when Netdata was installed in some other way will usually not work correctly, and may make it harder to uninstall Netdata. diff --git a/packaging/installer/dependencies/alpine.sh b/packaging/installer/dependencies/alpine.sh index cc3908d29..65999dc3b 100755 --- a/packaging/installer/dependencies/alpine.sh +++ b/packaging/installer/dependencies/alpine.sh @@ -22,7 +22,6 @@ package_tree=" tar curl gzip - netcat-openbsd libuv-dev lz4-dev openssl-dev diff --git a/packaging/installer/dependencies/arch.sh b/packaging/installer/dependencies/arch.sh index dc37bcb9a..cdda52733 100755 --- a/packaging/installer/dependencies/arch.sh +++ b/packaging/installer/dependencies/arch.sh @@ -16,7 +16,6 @@ declare -a package_tree=( automake libtool cmake - gnu-netcat zlib util-linux libmnl diff --git a/packaging/installer/dependencies/centos.sh b/packaging/installer/dependencies/centos.sh index f5f478ac1..a05bce8f0 100755 --- a/packaging/installer/dependencies/centos.sh +++ b/packaging/installer/dependencies/centos.sh @@ -15,7 +15,6 @@ declare -a package_tree=( libtool pkgconfig cmake - nmap-ncat zlib-devel libuuid-devel libmnl-devel diff --git a/packaging/installer/dependencies/debian.sh b/packaging/installer/dependencies/debian.sh index 61ff3e4aa..a2c421a92 100755 --- a/packaging/installer/dependencies/debian.sh +++ b/packaging/installer/dependencies/debian.sh @@ -23,7 +23,6 @@ package_tree=" tar curl gzip - netcat zlib1g-dev uuid-dev libmnl-dev diff --git a/packaging/installer/dependencies/fedora.sh b/packaging/installer/dependencies/fedora.sh index 4cfe7cbb1..a1c3a1df6 100755 --- a/packaging/installer/dependencies/fedora.sh +++ b/packaging/installer/dependencies/fedora.sh @@ -35,7 +35,6 @@ declare -a package_tree=( libatomic libtool cmake - nmap-ncat zlib-devel libuuid-devel libmnl-devel diff --git a/packaging/installer/dependencies/freebsd.sh b/packaging/installer/dependencies/freebsd.sh index 6afaca337..914513563 100755 --- a/packaging/installer/dependencies/freebsd.sh +++ b/packaging/installer/dependencies/freebsd.sh @@ -18,7 +18,6 @@ package_tree=" cmake curl gzip - netcat lzlib e2fsprogs-libuuid json-c diff --git a/packaging/installer/dependencies/gentoo.sh b/packaging/installer/dependencies/gentoo.sh index ae82cf3b3..e7ed64455 100755 --- a/packaging/installer/dependencies/gentoo.sh +++ b/packaging/installer/dependencies/gentoo.sh @@ -21,7 +21,6 @@ package_tree=" app-arch/tar net-misc/curl app-arch/gzip - net-analyzer/netcat sys-apps/util-linux net-libs/libmnl dev-libs/json-c diff --git a/packaging/installer/dependencies/ol.sh b/packaging/installer/dependencies/ol.sh index 020bf63cc..0f1f90e67 100755 --- a/packaging/installer/dependencies/ol.sh +++ b/packaging/installer/dependencies/ol.sh @@ -19,7 +19,6 @@ declare -a package_tree=( libtool pkgconfig cmake - nmap-ncat tar zlib-devel libuuid-devel diff --git a/packaging/installer/dependencies/opensuse.sh b/packaging/installer/dependencies/opensuse.sh index 51a6d909e..b1f0c2182 100755 --- a/packaging/installer/dependencies/opensuse.sh +++ b/packaging/installer/dependencies/opensuse.sh @@ -21,7 +21,6 @@ declare -a package_tree=( libtool pkg-config cmake - netcat-openbsd zlib-devel libuuid-devel libmnl-devel diff --git a/packaging/installer/dependencies/rockylinux.sh b/packaging/installer/dependencies/rockylinux.sh index 92050a457..63981df4b 100755 --- a/packaging/installer/dependencies/rockylinux.sh +++ b/packaging/installer/dependencies/rockylinux.sh @@ -19,7 +19,6 @@ declare -a package_tree=( libtool pkgconfig cmake - nmap-ncat zlib-devel libuuid-devel libmnl-devel diff --git a/packaging/installer/dependencies/ubuntu.sh b/packaging/installer/dependencies/ubuntu.sh index b99098821..295dbf013 100755 --- a/packaging/installer/dependencies/ubuntu.sh +++ b/packaging/installer/dependencies/ubuntu.sh @@ -23,7 +23,6 @@ package_tree=" tar curl gzip - netcat zlib1g-dev uuid-dev libmnl-dev diff --git a/packaging/installer/functions.sh b/packaging/installer/functions.sh index 8bf7fafcf..e354ac651 100644 --- a/packaging/installer/functions.sh +++ b/packaging/installer/functions.sh @@ -253,9 +253,17 @@ exit_reason() { EXIT_REASON="${1}" EXIT_CODE="${2}" if [ -n "${NETDATA_PROPAGATE_WARNINGS}" ]; then - export EXIT_REASON - export EXIT_CODE - export NETDATA_WARNINGS="${NETDATA_WARNINGS}${SAVED_WARNINGS}" + if [ -n "${NETDATA_SCRIPT_STATUS_PATH}" ]; then + { + echo "EXIT_REASON=\"${EXIT_REASON}\"" + echo "EXIT_CODE=\"${EXIT_CODE}\"" + echo "NETDATA_WARNINGS=\"${NETDATA_WARNINGS}${SAVED_WARNINGS}\"" + } >> "${NETDATA_SCRIPT_STATUS_PATH}" + else + export EXIT_REASON + export EXIT_CODE + export NETDATA_WARNINGS="${NETDATA_WARNINGS}${SAVED_WARNINGS}" + fi fi fi } @@ -475,92 +483,153 @@ install_non_systemd_init() { return 1 } -# This is used by netdata-installer.sh -# shellcheck disable=SC2034 -NETDATA_STOP_CMD="netdatacli shutdown-agent" - -NETDATA_START_CMD="netdata" -NETDATA_INSTALLER_START_CMD="" - -install_netdata_service() { - uname="$(uname 2> /dev/null)" - - if [ "${UID}" -eq 0 ]; then - if [ "${uname}" = "Darwin" ]; then +run_install_service_script() { + if [ -z "${tmpdir}" ]; then + tmpdir="${TMPDIR:-/tmp}" + fi - if [ -f "/Library/LaunchDaemons/com.github.netdata.plist" ]; then - echo >&2 "file '/Library/LaunchDaemons/com.github.netdata.plist' already exists." - return 0 - else - echo >&2 "Installing MacOS X plist file..." - # This is used by netdata-installer.sh - # shellcheck disable=SC2034 - run cp system/netdata.plist /Library/LaunchDaemons/com.github.netdata.plist && - run launchctl load /Library/LaunchDaemons/com.github.netdata.plist && - NETDATA_START_CMD="launchctl start com.github.netdata" && - NETDATA_STOP_CMD="launchctl stop com.github.netdata" - return 0 + # shellcheck disable=SC2154 + save_path="${tmpdir}/netdata-service-cmds" + # shellcheck disable=SC2068 + "${NETDATA_PREFIX}/usr/libexec/netdata/install-service.sh" --save-cmds "${save_path}" ${@} + + case $? in + 0) + if [ -r "${save_path}" ]; then + # shellcheck disable=SC1090 + . "${save_path}" fi - elif [ "${uname}" = "FreeBSD" ]; then - # This is used by netdata-installer.sh - # shellcheck disable=SC2034 - run cp system/netdata-freebsd /etc/rc.d/netdata && NETDATA_START_CMD="service netdata start" && - NETDATA_STOP_CMD="service netdata stop" && - NETDATA_INSTALLER_START_CMD="service netdata onestart" && - myret=$? - - echo >&2 "Note: To explicitly enable netdata automatic start, set 'netdata_enable' to 'YES' in /etc/rc.conf" - echo >&2 "" - - return ${myret} + if [ -z "${NETDATA_INSTALLER_START_CMD}" ]; then + if [ -n "${NETDATA_START_CMD}" ]; then + NETDATA_INSTALLER_START_CMD="${NETDATA_START_CMD}" + else + NETDATA_INSTALLER_START_CMD="netdata" + fi + fi + ;; + 1) + if [ -z "${NETDATA_SERVICE_WARNED_1}" ]; then + warning "Intenral error encountered while attempting to install or manage Netdata as a system service. This is probably a bug." + NETDATA_SERVICE_WARNED_1=1 + fi + ;; + 2) + if [ -z "${NETDATA_SERVICE_WARNED_2}" ]; then + warning "Failed to detect system service manager type. Cannot cleanly install or manage Netdata as a system service. If you are running this script in a container, this is expected and can safely be ignored." + NETDATA_SERVICE_WARNED_2=1 + fi + ;; + 3) + if [ -z "${NETDATA_SERVICE_WARNED_3}" ]; then + warning "Detected an unsupported system service manager. Manual setup will be required to manage Netdata as a system service." + NETDATA_SERVICE_WARNED_3=1 + fi + ;; + 4) + if [ -z "${NETDATA_SERVICE_WARNED_4}" ]; then + warning "Detected a supported system service manager, but failed to install Netdata as a system service. Usually this is a result of incorrect permissions. Manually running ${NETDATA_PREFIX}/usr/libexec/netdata/install-service.sh may provide more information about the exact issue." + NETDATA_SERVICE_WARNED_4=1 + fi + ;; + 5) + if [ -z "${NETDATA_SERVICE_WARNED_5}" ]; then + warning "We do not support managing Netdata as a system service on this platform. Manual setup will be required." + NETDATA_SERVICE_WARNED_5=1 + fi + ;; + esac +} - elif issystemd; then - # systemd is running on this system - NETDATA_START_CMD="systemctl start netdata" +install_netdata_service() { + if [ "${UID}" -eq 0 ]; then + if [ -x "${NETDATA_PREFIX}/usr/libexec/netdata/install-service.sh" ]; then + run_install_service_script && return 0 + else # This is used by netdata-installer.sh # shellcheck disable=SC2034 - NETDATA_STOP_CMD="systemctl stop netdata" - NETDATA_INSTALLER_START_CMD="${NETDATA_START_CMD}" + NETDATA_STOP_CMD="netdatacli shutdown-agent" - SYSTEMD_DIRECTORY="$(get_systemd_service_dir)" + NETDATA_START_CMD="netdata" + NETDATA_INSTALLER_START_CMD="" - if [ "${SYSTEMD_DIRECTORY}x" != "x" ]; then - ENABLE_NETDATA_IF_PREVIOUSLY_ENABLED="run systemctl enable netdata" - IS_NETDATA_ENABLED="$(systemctl is-enabled netdata 2> /dev/null || echo "Netdata not there")" - if [ "${IS_NETDATA_ENABLED}" = "disabled" ]; then - echo >&2 "Netdata was there and disabled, make sure we don't re-enable it ourselves" - ENABLE_NETDATA_IF_PREVIOUSLY_ENABLED="true" - fi + uname="$(uname 2> /dev/null)" - echo >&2 "Installing systemd service..." - run cp system/netdata.service "${SYSTEMD_DIRECTORY}/netdata.service" && - run systemctl daemon-reload && - ${ENABLE_NETDATA_IF_PREVIOUSLY_ENABLED} && + if [ "${uname}" = "Darwin" ]; then + if [ -f "/Library/LaunchDaemons/com.github.netdata.plist" ]; then + echo >&2 "file '/Library/LaunchDaemons/com.github.netdata.plist' already exists." return 0 - else - warning "Could not find a systemd service directory, unable to install Netdata systemd service." - fi - else - install_non_systemd_init - ret=$? - - if [ ${ret} -eq 0 ]; then - if [ -n "${service_cmd}" ]; then - NETDATA_START_CMD="service netdata start" + else + echo >&2 "Installing MacOS X plist file..." # This is used by netdata-installer.sh # shellcheck disable=SC2034 - NETDATA_STOP_CMD="service netdata stop" - elif [ -n "${rcservice_cmd}" ]; then - NETDATA_START_CMD="rc-service netdata start" - # This is used by netdata-installer.sh - # shellcheck disable=SC2034 - NETDATA_STOP_CMD="rc-service netdata stop" + run cp system/netdata.plist /Library/LaunchDaemons/com.github.netdata.plist && + run launchctl load /Library/LaunchDaemons/com.github.netdata.plist && + NETDATA_START_CMD="launchctl start com.github.netdata" && + NETDATA_STOP_CMD="launchctl stop com.github.netdata" + return 0 fi + + elif [ "${uname}" = "FreeBSD" ]; then + # This is used by netdata-installer.sh + # shellcheck disable=SC2034 + run cp system/netdata-freebsd /etc/rc.d/netdata && NETDATA_START_CMD="service netdata start" && + NETDATA_STOP_CMD="service netdata stop" && + NETDATA_INSTALLER_START_CMD="service netdata onestart" && + myret=$? + + echo >&2 "Note: To explicitly enable netdata automatic start, set 'netdata_enable' to 'YES' in /etc/rc.conf" + echo >&2 "" + + return ${myret} + + elif issystemd; then + # systemd is running on this system + NETDATA_START_CMD="systemctl start netdata" + # This is used by netdata-installer.sh + # shellcheck disable=SC2034 + NETDATA_STOP_CMD="systemctl stop netdata" NETDATA_INSTALLER_START_CMD="${NETDATA_START_CMD}" - fi - return ${ret} + SYSTEMD_DIRECTORY="$(get_systemd_service_dir)" + + if [ "${SYSTEMD_DIRECTORY}x" != "x" ]; then + ENABLE_NETDATA_IF_PREVIOUSLY_ENABLED="run systemctl enable netdata" + IS_NETDATA_ENABLED="$(systemctl is-enabled netdata 2> /dev/null || echo "Netdata not there")" + if [ "${IS_NETDATA_ENABLED}" = "disabled" ]; then + echo >&2 "Netdata was there and disabled, make sure we don't re-enable it ourselves" + ENABLE_NETDATA_IF_PREVIOUSLY_ENABLED="true" + fi + + echo >&2 "Installing systemd service..." + run cp system/netdata.service "${SYSTEMD_DIRECTORY}/netdata.service" && + run systemctl daemon-reload && + ${ENABLE_NETDATA_IF_PREVIOUSLY_ENABLED} && + return 0 + else + warning "Could not find a systemd service directory, unable to install Netdata systemd service." + fi + else + install_non_systemd_init + ret=$? + + if [ ${ret} -eq 0 ]; then + if [ -n "${service_cmd}" ]; then + NETDATA_START_CMD="service netdata start" + # This is used by netdata-installer.sh + # shellcheck disable=SC2034 + NETDATA_STOP_CMD="service netdata stop" + elif [ -n "${rcservice_cmd}" ]; then + NETDATA_START_CMD="rc-service netdata start" + # This is used by netdata-installer.sh + # shellcheck disable=SC2034 + NETDATA_STOP_CMD="rc-service netdata stop" + fi + NETDATA_INSTALLER_START_CMD="${NETDATA_START_CMD}" + fi + + return ${ret} + fi fi fi @@ -642,11 +711,21 @@ netdata_pids() { stop_all_netdata() { stop_success=0 + if [ -x "${NETDATA_PREFIX}/usr/libexec/netdata/install-service.sh" ]; then + run_install_service_script --cmds-only + fi + if [ "${UID}" -eq 0 ]; then + uname="$(uname 2>/dev/null)" # Any of these may fail, but we need to not bail if they do. - if issystemd; then + if [ -n "${NETDATA_STOP_CMD}" ]; then + if ${NETDATA_STOP_CMD}; then + stop_success=1 + sleep 5 + fi + elif issystemd; then if systemctl stop netdata; then stop_success=1 sleep 5 @@ -693,8 +772,16 @@ restart_netdata() { progress "Restarting netdata instance" + if [ -x "${NETDATA_PREFIX}/usr/libexec/netdata/install-service.sh" ]; then + run_install_service_script --cmds-only + fi + if [ -z "${NETDATA_INSTALLER_START_CMD}" ]; then - NETDATA_INSTALLER_START_CMD="${netdata}" + if [ -n "${NETDATA_START_CMD}" ]; then + NETDATA_INSTALLER_START_CMD="${NETDATA_START_CMD}" + else + NETDATA_INSTALLER_START_CMD="${netdata}" + fi fi if [ "${UID}" -eq 0 ]; then diff --git a/packaging/installer/install-required-packages.sh b/packaging/installer/install-required-packages.sh index da3cf5e4a..6547dd82e 100755 --- a/packaging/installer/install-required-packages.sh +++ b/packaging/installer/install-required-packages.sh @@ -20,7 +20,6 @@ fi PACKAGES_NETDATA=${PACKAGES_NETDATA-1} PACKAGES_NETDATA_PYTHON=${PACKAGES_NETDATA_PYTHON-0} PACKAGES_NETDATA_PYTHON3=${PACKAGES_NETDATA_PYTHON3-1} -PACKAGES_NETDATA_PYTHON_POSTGRES=${PACKAGES_NETDATA_PYTHON_POSTGRES-0} PACKAGES_NETDATA_PYTHON_MONGO=${PACKAGES_NETDATA_PYTHON_MONGO-0} PACKAGES_DEBUG=${PACKAGES_DEBUG-0} PACKAGES_IPRANGE=${PACKAGES_IPRANGE-0} @@ -97,8 +96,7 @@ Supported installers (IN): Supported packages (you can append many of them): - netdata-all all packages required to install netdata - including postgres client, - node.js, python, sensors, etc + including python, sensors, etc - netdata minimum packages required to install netdata (includes python) @@ -107,10 +105,6 @@ Supported packages (you can append many of them): - python3 install python3 - - python-postgres install psycopg2 - (for monitoring postgres, will install python3 version - if python3 is enabled or detected) - - python-pymongo install python-pymongo (or python3-pymongo for python3) - sensors install lm_sensors for monitoring h/w sensors @@ -884,26 +878,6 @@ declare -A pkg_make=( ['default']="make" ) -declare -A pkg_netcat=( - ['alpine']="netcat-openbsd" - ['arch']="netcat" - ['centos']="nmap-ncat" - ['debian']="netcat" - ['gentoo']="net-analyzer/netcat" - ['sabayon']="net-analyzer/gnu-netcat" - ['rhel']="nmap-ncat" - ['ol']="nmap-ncat" - ['suse']="netcat-openbsd" - ['clearlinux']="sysadmin-basic" - ['arch']="gnu-netcat" - ['macos']="NOTREQUIRED" - ['default']="netcat" - - # exceptions - ['centos-6']="nc" - ['rhel-6']="nc" -) - declare -A pkg_nginx=( ['gentoo']="www-servers/nginx" ['default']="nginx" @@ -941,42 +915,6 @@ declare -A pkg_python=( ['centos-8']="python2" ) -declare -A pkg_python_psycopg2=( - ['alpine']="py-psycopg2" - ['arch']="python2-psycopg2" - ['centos']="python-psycopg2" - ['debian']="python-psycopg2" - ['gentoo']="dev-python/psycopg" - ['sabayon']="dev-python/psycopg:2" - ['rhel']="python-psycopg2" - ['ol']="python-psycopg2" - ['suse']="python-psycopg2" - ['clearlinux']="WARNING|" - ['macos']="WARNING|" - ['default']="python-psycopg2" -) - -declare -A pkg_python3_psycopg2=( - ['alpine']="py3-psycopg2" - ['arch']="python-psycopg2" - ['centos']="WARNING|" - ['debian']="WARNING|" - ['gentoo']="dev-python/psycopg" - ['sabayon']="dev-python/psycopg:2" - ['rhel']="WARNING|" - ['ol']="WARNING|" - ['suse']="WARNING|" - ['clearlinux']="WARNING|" - ['macos']="WARNING|" - ['default']="WARNING|" - - ['centos-7']="python3-psycopg2" - ['centos-8']="python38-psycopg2" - ['rhel-7']="python3-psycopg2" - ['rhel-8']="python38-psycopg2" - ['ol-8']="python3-psycopg2" -) - declare -A pkg_python_pip=( ['alpine']="py-pip" ['gentoo']="dev-python/pip" @@ -1295,7 +1233,6 @@ packages() { require_cmd tar || suitable_package tar require_cmd curl || suitable_package curl require_cmd gzip || suitable_package gzip - require_cmd nc || suitable_package netcat fi # ------------------------------------------------------------------------- @@ -1359,8 +1296,6 @@ packages() { [ "${PACKAGES_NETDATA_PYTHON_MONGO}" -ne 0 ] && suitable_package python-pymongo # suitable_package python-requests # suitable_package python-pip - - [ "${PACKAGES_NETDATA_PYTHON_POSTGRES}" -ne 0 ] && suitable_package python-psycopg2 fi # ------------------------------------------------------------------------- @@ -1372,8 +1307,6 @@ packages() { [ "${PACKAGES_NETDATA_PYTHON_MONGO}" -ne 0 ] && suitable_package python3-pymongo # suitable_package python3-requests # suitable_package python3-pip - - [ "${PACKAGES_NETDATA_PYTHON_POSTGRES}" -ne 0 ] && suitable_package python3-psycopg2 fi # ------------------------------------------------------------------------- @@ -1913,7 +1846,7 @@ EOF remote_log() { # log success or failure on our system # to help us solve installation issues - curl > /dev/null 2>&1 -Ss --max-time 3 "https://registry.my-netdata.io/log/installer?status=${1}&error=${2}&distribution=${distribution}&version=${version}&installer=${package_installer}&tree=${tree}&detection=${detection}&netdata=${PACKAGES_NETDATA}&python=${PACKAGES_NETDATA_PYTHON}&python3=${PACKAGES_NETDATA_PYTHON3}&postgres=${PACKAGES_NETDATA_PYTHON_POSTGRES}&pymongo=${PACKAGES_NETDATA_PYTHON_MONGO}&sensors=${PACKAGES_NETDATA_SENSORS}&database=${PACKAGES_NETDATA_DATABASE}&ebpf=${PACKAGES_NETDATA_EBPF}&firehol=${PACKAGES_FIREHOL}&fireqos=${PACKAGES_FIREQOS}&iprange=${PACKAGES_IPRANGE}&update_ipsets=${PACKAGES_UPDATE_IPSETS}&demo=${PACKAGES_NETDATA_DEMO_SITE}" + curl > /dev/null 2>&1 -Ss --max-time 3 "https://registry.my-netdata.io/log/installer?status=${1}&error=${2}&distribution=${distribution}&version=${version}&installer=${package_installer}&tree=${tree}&detection=${detection}&netdata=${PACKAGES_NETDATA}&python=${PACKAGES_NETDATA_PYTHON}&python3=${PACKAGES_NETDATA_PYTHON3}&pymongo=${PACKAGES_NETDATA_PYTHON_MONGO}&sensors=${PACKAGES_NETDATA_SENSORS}&database=${PACKAGES_NETDATA_DATABASE}&ebpf=${PACKAGES_NETDATA_EBPF}&firehol=${PACKAGES_FIREHOL}&fireqos=${PACKAGES_FIREQOS}&iprange=${PACKAGES_IPRANGE}&update_ipsets=${PACKAGES_UPDATE_IPSETS}&demo=${PACKAGES_NETDATA_DEMO_SITE}" } if [ -z "${1}" ]; then @@ -1976,11 +1909,9 @@ while [ -n "${1}" ]; do PACKAGES_NETDATA=1 if [ "${pv}" -eq 2 ]; then PACKAGES_NETDATA_PYTHON=1 - PACKAGES_NETDATA_PYTHON_POSTGRES=1 PACKAGES_NETDATA_PYTHON_MONGO=1 else PACKAGES_NETDATA_PYTHON3=1 - PACKAGES_NETDATA_PYTHON3_POSTGRES=1 PACKAGES_NETDATA_PYTHON3_MONGO=1 fi PACKAGES_NETDATA_SENSORS=1 @@ -2003,16 +1934,6 @@ while [ -n "${1}" ]; do PACKAGES_NETDATA_PYTHON3=1 ;; - python-postgres | postgres-python | psycopg2 | netdata-postgres) - if [ "${pv}" -eq 2 ]; then - PACKAGES_NETDATA_PYTHON=1 - PACKAGES_NETDATA_PYTHON_POSTGRES=1 - else - PACKAGES_NETDATA_PYTHON3=1 - PACKAGES_NETDATA_PYTHON3_POSTGRES=1 - fi - ;; - python-pymongo) if [ "${pv}" -eq 2 ]; then PACKAGES_NETDATA_PYTHON=1 @@ -2042,11 +1963,9 @@ while [ -n "${1}" ]; do PACKAGES_NETDATA=1 if [ "${pv}" -eq 2 ]; then PACKAGES_NETDATA_PYTHON=1 - PACKAGES_NETDATA_PYTHON_POSTGRES=1 PACKAGES_NETDATA_PYTHON_MONGO=1 else PACKAGES_NETDATA_PYTHON3=1 - PACKAGES_NETDATA_PYTHON3_POSTGRES=1 PACKAGES_NETDATA_PYTHON3_MONGO=1 fi PACKAGES_DEBUG=1 diff --git a/packaging/installer/kickstart.sh b/packaging/installer/kickstart.sh index 10f6b9664..295fcdca0 100755 --- a/packaging/installer/kickstart.sh +++ b/packaging/installer/kickstart.sh @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: GPL-3.0-or-later # -# Next unused error code: F050A +# Next unused error code: F050D # ====================================================================== # Constants @@ -12,7 +12,6 @@ CLOUD_BUG_REPORT_URL="https://github.com/netdata/netdata-cloud/issues/new/choose DEFAULT_RELEASE_CHANNEL="nightly" DISCORD_INVITE="https://discord.gg/5ygS846fR6" DISCUSSIONS_URL="https://github.com/netdata/netdata/discussions" -DISCUSSIONS_URL="https://github.com/netdata/netdata/discussions" DOCS_URL="https://learn.netdata.cloud/docs/" FORUM_URL="https://community.netdata.cloud/" KICKSTART_OPTIONS="${*}" @@ -44,7 +43,8 @@ INSTALL_TYPE="unknown" INSTALL_PREFIX="" NETDATA_AUTO_UPDATES="default" NETDATA_CLAIM_ONLY=0 -NETDATA_CLAIM_URL="${PUBLIC_CLOUD_URL}" +NETDATA_CLAIM_URL="api.netdata.cloud" +NETDATA_COMMAND="default" NETDATA_DISABLE_CLOUD=0 NETDATA_ONLY_BUILD=0 NETDATA_ONLY_NATIVE=0 @@ -76,6 +76,13 @@ else INTERACTIVE=1 fi +# ====================================================================== +# Shared messages used in multiple places throughout the script. + +BADCACHE_MSG="Usually this is a result of an older copy of the file being cached somewhere upstream and can be resolved by retrying in an hour" +BADNET_MSG="This is usually a result of a networking issue" +ERROR_F0003="Could not find a usable HTTP client. Either curl or wget is required to proceed with installation." + # ====================================================================== # Core program logic @@ -241,10 +248,16 @@ telemetry_event() { TOTAL_RAM="$((TOTAL_RAM * 1024))" fi - if [ -f /etc/machine-id ]; then - DISTINCT_ID="$(cat /etc/machine-id)" + if [ "${KERNEL_NAME}" = Darwin ] && command -v ioreg >/dev/null 2>&1; then + DISTINCT_ID="macos-$(ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { split($0, line, "\""); printf("%s\n", line[4]); }')" + elif [ -f /etc/machine-id ]; then + DISTINCT_ID="machine-$(cat /etc/machine-id)" + elif [ -f /var/db/dbus/machine-id ]; then + DISTINCT_ID="dbus-$(cat /var/db/dbus/machine-id)" + elif [ -f /var/lib/dbus/machine-id ]; then + DISTINCT_ID="dbus-$(cat /var/lib/dbus/machine-id)" elif command -v uuidgen > /dev/null 2>&1; then - DISTINCT_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')" + DISTINCT_ID="uuid-$(uuidgen | tr '[:upper:]' '[:lower:]')" else DISTINCT_ID="null" fi @@ -266,6 +279,7 @@ telemetry_event() { "install_options": "${KICKSTART_OPTIONS}", "install_interactivity": "${INTERACTIVE}", "install_auto_updates": "${NETDATA_AUTO_UPDATES}", + "install_command": "${NETDATA_COMMAND}", "total_runtime": "${total_duration}", "selected_install_method": "${SELECTED_INSTALL_METHOD}", "netdata_release_channel": "${RELEASE_CHANNEL:-null}", @@ -473,6 +487,26 @@ run() { return ${ret} } +run_script() { + set_tmpdir + + export NETDATA_SCRIPT_STATUS_PATH="${tmpdir}/.script-status" + + export NETDATA_SAVE_WARNINGS=1 + export NETDATA_PROPAGATE_WARNINGS=1 + # shellcheck disable=SC2090 + export NETDATA_WARNINGS="${NETDATA_WARNINGS}" + + # shellcheck disable=SC2086 + run ${ROOTCMD} "${@}" + + if [ -r "${NETDATA_SCRIPT_STATUS_PATH}" ]; then + # shellcheck disable=SC1090 + . "${NETDATA_SCRIPT_STATUS_PATH}" + rm -f "${NETDATA_SCRIPT_STATUS_PATH}" + fi +} + warning() { printf >&2 "%s\n\n" "${TPUT_BGRED}${TPUT_WHITE}${TPUT_BOLD} WARNING ${TPUT_RESET} ${*}" NETDATA_WARNINGS="${NETDATA_WARNINGS}\n - ${*}" @@ -515,7 +549,7 @@ create_tmp_directory() { } set_tmpdir() { - if [ -z "${tmpdir}" ]; then + if [ -z "${tmpdir}" ] || [ ! -d "${tmpdir}" ]; then tmpdir="$(create_tmp_directory)" progress "Using ${tmpdir} as a temporary directory." cd "${tmpdir}" || fatal "Failed to change current working directory to ${tmpdir}." F000A @@ -532,7 +566,7 @@ check_for_remote_file() { elif command -v wget > /dev/null 2>&1; then wget -S --spider "${url}" 2>&1 | grep -q 'HTTP/1.1 200 OK' || return 1 else - fatal "I need curl or wget to proceed, but neither of them are available on this system." F0003 + fatal "${ERROR_F0003}" F0003 fi } @@ -547,7 +581,7 @@ download() { elif command -v wget > /dev/null 2>&1; then run wget -T 15 -O "${dest}" "${url}" || return 1 else - fatal "I need curl or wget to proceed, but neither of them are available on this system." F0003 + fatal "${ERROR_F0003}" F0003 fi } @@ -559,7 +593,7 @@ get_redirect() { elif command -v wget > /dev/null 2>&1; then run sh -c "wget --max-redirect=0 ${url} 2>&1 | grep Location | cut -d ' ' -f2 | grep -o '[^/]*$'" || return 1 else - fatal "I need curl or wget to proceed, but neither of them are available on this system." F0003 + fatal "${ERROR_F0003}" F0003 fi } @@ -571,7 +605,7 @@ safe_sha256sum() { elif command -v sha256sum > /dev/null 2>&1; then sha256sum "$@" else - fatal "I could not find a suitable checksum binary to use" F0004 + fatal "Could not find a usable checksum tool. Either sha256sum, or a version of shasum supporting SHA256 checksums is required to proceed with installation." F0004 fi } @@ -586,7 +620,7 @@ get_system_info() { elif [ -s "/usr/lib/os-release" ] && [ -r "/usr/lib/os-release" ]; then os_release_file="/usr/lib/os-release" else - warning "Cannot find an os-release file ..." + warning "Cannot find usable OS release information. Native packages will not be available for this install." fi if [ -n "${os_release_file}" ]; then @@ -598,7 +632,7 @@ get_system_info() { SYSCODENAME="${VERSION_CODENAME}" SYSARCH="$(uname -m)" - supported_compat_names="debian ubuntu centos fedora opensuse ol" + supported_compat_names="debian ubuntu centos fedora opensuse ol arch" if str_in_list "${DISTRO}" "${supported_compat_names}"; then DISTRO_COMPAT_NAME="${DISTRO}" @@ -607,9 +641,12 @@ get_system_info() { opensuse-leap) DISTRO_COMPAT_NAME="opensuse" ;; - almalinux|rocky|rhel) + cloudlinux|almalinux|rocky|rhel) DISTRO_COMPAT_NAME="centos" ;; + artix|manjaro|obarun) + DISTRO_COMPAT_NAME="arch" + ;; *) DISTRO_COMPAT_NAME="unknown" ;; @@ -673,7 +710,7 @@ confirm_root_support() { fi if [ -z "${ROOTCMD}" ]; then - fatal "We need root privileges to continue, but cannot find a way to gain them. Either re-run this script as root, or set \$ROOTCMD to a command that can be used to gain root privileges" F0201 + fatal "We need root privileges to continue, but cannot find a way to gain them (we support sudo, doas, and pkexec). Either re-run this script as root, or set \$ROOTCMD to a command that can be used to gain root privileges." F0201 fi fi } @@ -711,22 +748,18 @@ update() { opts="--interactive" fi - export NETDATA_SAVE_WARNINGS=1 - export NETDATA_PROPAGATE_WARNINGS=1 - # shellcheck disable=SC2090 - export NETDATA_WARNINGS="${NETDATA_WARNINGS}" - if run ${ROOTCMD} "${updater}" ${opts} --not-running-from-cron; then + if run_script "${updater}" ${opts} --not-running-from-cron; then progress "Updated existing install at ${ndprefix}" return 0 else if [ -n "${EXIT_REASON}" ]; then fatal "Failed to update existing Netdata install at ${ndprefix}: ${EXIT_REASON}" "${EXIT_CODE}" else - fatal "Failed to update existing Netdata install at ${ndprefix}." U0000 + fatal "Failed to update existing Netdata install at ${ndprefix}: Encountered an unhandled error in the updater. Further information about this error may be displayed above." U0000 fi fi else - warning "Could not find a usable copy of the updater script." + warning "Could not find a usable copy of the updater script. We are unable to update this system in place." return 1 fi } @@ -757,11 +790,7 @@ uninstall() { return 0 else progress "Found existing netdata-uninstaller. Running it.." - export NETDATA_SAVE_WARNINGS=1 - export NETDATA_PROPAGATE_WARNINGS=1 - # shellcheck disable=SC2090 - export NETDATA_WARNINGS="${NETDATA_WARNINGS}" - if ! run ${ROOTCMD} "${uninstaller}" $FLAGS; then + if ! run_script "${uninstaller}" ${FLAGS}; then warning "Uninstaller failed. Some parts of Netdata may still be present on the system." fi fi @@ -774,11 +803,7 @@ uninstall() { progress "Downloading netdata-uninstaller ..." download "${uninstaller_url}" "${tmpdir}/netdata-uninstaller.sh" chmod +x "${tmpdir}/netdata-uninstaller.sh" - export NETDATA_SAVE_WARNINGS=1 - export NETDATA_PROPAGATE_WARNINGS=1 - # shellcheck disable=SC2090 - export NETDATA_WARNINGS="${NETDATA_WARNINGS}" - if ! run ${ROOTCMD} "${tmpdir}/netdata-uninstaller.sh" $FLAGS; then + if ! run_script "${tmpdir}/netdata-uninstaller.sh" ${FLAGS}; then warning "Uninstaller failed. Some parts of Netdata may still be present on the system." fi fi @@ -792,7 +817,9 @@ detect_existing_install() { if pkg_installed netdata; then ndprefix="/" + EXISTING_INSTALL_IS_NATIVE="1" else + EXISTING_INSTALL_IS_NATIVE="0" if [ -n "${INSTALL_PREFIX}" ]; then searchpath="${INSTALL_PREFIX}/bin:${INSTALL_PREFIX}/sbin:${INSTALL_PREFIX}/usr/bin:${INSTALL_PREFIX}/usr/sbin:${PATH}" searchpath="${INSTALL_PREFIX}/netdata/bin:${INSTALL_PREFIX}/netdata/sbin:${INSTALL_PREFIX}/netdata/usr/bin:${INSTALL_PREFIX}/netdata/usr/sbin:${searchpath}" @@ -807,7 +834,10 @@ detect_existing_install() { fi if [ -n "${ndpath}" ]; then - ndprefix="$(dirname "$(dirname "${ndpath}")")" + case "${ndpath}" in + */usr/bin/netdata|*/usr/sbin/netdata) ndprefix="$(dirname "$(dirname "$(dirname "${ndpath}")")")" ;; + *) ndprefix="$(dirname "$(dirname "${ndpath}")")" ;; + esac fi if echo "${ndprefix}" | grep -Eq '^/usr$'; then @@ -854,8 +884,11 @@ handle_existing_install() { case "${INSTALL_TYPE}" in kickstart-*|legacy-*|binpkg-*|manual-static|unknown) if [ "${INSTALL_TYPE}" = "unknown" ]; then - warning "Found an existing netdata install at ${ndprefix}, but could not determine the install type." - warning "Usually this means you installed Netdata through your distribution’s regular package repositories or some other unsupported method." + if [ "${EXISTING_INSTALL_IS_NATIVE}" -eq 1 ]; then + warning "Found an existing netdata install managed by the system package manager, but could not determine the install type. Usually this means you installed an unsupported third-party netdata package." + else + warning "Found an existing netdata install at ${ndprefix}, but could not determine the install type. Usually this means you installed Netdata through your distribution’s regular package repositories or some other unsupported method." + fi else progress "Found an existing netdata install at ${ndprefix}, with installation type '${INSTALL_TYPE}'." fi @@ -873,7 +906,13 @@ handle_existing_install() { elif [ "${INTERACTIVE}" -eq 0 ]; then fatal "User requested reinstall, but we cannot safely reinstall over top of a ${INSTALL_TYPE} installation, exiting." F0104 else - if confirm "Reinstalling over top of a ${INSTALL_TYPE} installation may be unsafe, do you want to continue?"; then + if [ "${EXISTING_INSTALL_IS_NATIVE}" ]; then + reinstall_prompt="Reinstalling over top of an existing install managed by the system package manager is known to cause things to break, are you sure you want to continue?" + else + reinstall_prompt="Reinstalling over top of a ${INSTALL_TYPE} installation may be unsafe, do you want to continue?" + fi + + if confirm "${reinstall_prompt}"; then progress "OK, continuing." else fatal "Cancelling reinstallation at user request." F0105 @@ -884,7 +923,23 @@ handle_existing_install() { return 0 elif [ "${INSTALL_TYPE}" = "unknown" ]; then - fatal "We do not support trying to update or claim installations when we cannot determine the install type. You will need to uninstall the existing install using the same method you used to install it to proceed." F0106 + claimonly_notice="If you just want to claim this install, you should re-run this command with the --claim-only option instead." + if [ "${EXISTING_INSTALL_IS_NATIVE}" -eq 1 ]; then + failmsg="Attempting to update an installation managed by the system package manager is known to not work in most cases. If you are trying to install the latest version of Netdata, you will need to manually uninstall it through your system package manager. ${claimonly_notice}" + promptmsg="Attempting to update an installation managed by the system package manager is known to not work in most cases. If you are trying to install the latest version of Netdata, you will need to manually uninstall it through your system package manager. ${claimonly_notice} Are you sure you want to continue?" + else + failmsg="We do not support trying to update or claim installations when we cannot determine the install type. You will need to uninstall the existing install using the same method you used to install it to proceed. ${claimonly_notice}" + promptmsg="Attempting to update an existing install is not officially supported. It may work, but it also might break your system. ${claimonly_notice} Are you sure you want to continue?" + fi + if [ "${INTERACTIVE}" -eq 0 ] && [ "${NETDATA_CLAIM_ONLY}" -eq 0 ]; then + fatal "${failmsg}" F0106 + elif [ "${INTERACTIVE}" -eq 1 ] && [ "${NETDATA_CLAIM_ONLY}" -eq 0 ]; then + if confirm "${promptmsg}"; then + progress "OK, continuing" + else + fatal "Cancelling update of unknown installation type at user request." F050C + fi + fi fi ret=0 @@ -945,7 +1000,7 @@ handle_existing_install() { elif [ "${NETDATA_CLAIM_ONLY}" -eq 1 ]; then fatal "User asked to claim, but did not proide a claiming token." F0202 else - fatal "Found an existing netdata install at ${ndprefix}, but the install type is '${INSTALL_TYPE}', which is not supported, refusing to proceed." F0103 + fatal "Found an existing netdata install at ${ndprefix}, but the install type is '${INSTALL_TYPE}', which is not supported by this script, refusing to proceed." F0103 fi fi ;; @@ -989,7 +1044,7 @@ EOF confirm_install_prefix() { if [ -n "${INSTALL_PREFIX}" ] && [ "${NETDATA_ONLY_BUILD}" -ne 1 ]; then - fatal "The \`--install-prefix\` and \`--install\` options are only supported together with the \`--build-only\` option." F0204 + fatal "The --install-prefix and --install options are only supported together with the --build-only option." F0204 fi if [ -n "${INSTALL_PREFIX}" ]; then @@ -1071,7 +1126,7 @@ claim() { fi if [ ! -x "${NETDATA_CLAIM_PATH}" ]; then - fatal "Unable to find usable claiming script." F0106 + fatal "Unable to find usable claiming script. Reinstalling Netdata may resolve this." F050B fi if ! is_netdata_running; then @@ -1092,7 +1147,7 @@ claim() { warning "Unable to claim node due to issues creating the claiming directory or preparing the local claiming key. Make sure you have a working openssl command and that ${INSTALL_PREFIX}/var/lib/netdata/cloud.d exists, then try again." ;; 3) - warning "Unable to claim node due to missing dependencies. Usually this means that the Netdata Agent was built without support for Netdata Cloud. If you built the agent from source, please install all needed dependencies for Cloud support. If you used the regular installation script and see this error, please file a bug." + warning "Unable to claim node due to missing dependencies. Usually this means that the Netdata Agent was built without support for Netdata Cloud. If you built the agent from source, please install all needed dependencies for Cloud support. If you used the regular installation script and see this error, please file a bug report at ${AGENT_BUG_REPORT_URL}." ;; 4) warning "Failed to claim node due to inability to connect to ${NETDATA_CLAIM_URL}. Usually this either means that the specified claiming URL is wrong, or that you are having networking problems." @@ -1178,19 +1233,48 @@ set_auto_updates() { # Check for an already installed package with a given name. pkg_installed() { - case "${DISTRO_COMPAT_NAME}" in - debian|ubuntu) - # shellcheck disable=SC2016 - dpkg-query --show --showformat '${Status}' "${1}" 2>&1 | cut -f 1 -d ' ' | grep -q '^install$' - return $? + case "${SYSTYPE}" in + Linux) + case "${DISTRO_COMPAT_NAME}" in + debian|ubuntu) + # shellcheck disable=SC2016 + dpkg-query --show --showformat '${Status}' "${1}" 2>&1 | cut -f 1 -d ' ' | grep -q '^install$' + return $? + ;; + centos|fedora|opensuse|ol) + rpm -q "${1}" > /dev/null 2>&1 + return $? + ;; + alpine) + apk -e info "${1}" > /dev/null 2>&1 + return $? + ;; + arch) + pacman -Qi "${1}" > /dev/null 2>&1 + return $? + ;; + *) + return 1 + ;; + esac ;; - centos|fedora|opensuse|ol) - rpm -q "${1}" > /dev/null 2>&1 - return $? + Darwin) + if command -v brew > /dev/null 2>&1; then + brew list "${1}" > /dev/null 2>&1 + return $? + else + return 1 + fi ;; - *) - return 1 + FreeBSD) + if pkg -N > /dev/null 2>&1; then + pkg info "${1}" > /dev/null 2>&1 + return $? + else + return 1 + fi ;; + *) return 1 ;; esac } @@ -1230,7 +1314,7 @@ check_special_native_deps() { # shellcheck disable=SC2086 if ! run ${ROOTCMD} env ${env} ${pm_cmd} install ${pkg_install_opts} epel-release; then - warning "Failed to install EPEL." + warning "Failed to install EPEL, even though it is required to install native packages on this system." return 1 fi fi @@ -1241,6 +1325,8 @@ check_special_native_deps() { } try_package_install() { + failed_refresh_msg="Failed to refresh repository metadata. ${BADNET_MSG} or by misconfiguration of one or more rpackage repositories in the system package manager configuration." + if [ -z "${DISTRO}" ] || [ "${DISTRO}" = "unknown" ]; then warning "Unable to determine Linux distribution for native packages." return 2 @@ -1388,18 +1474,18 @@ try_package_install() { if ! pkg_installed "${repoconfig_name}"; then progress "Checking for availability of repository configuration package." if ! check_for_remote_file "${repoconfig_url}"; then - warning "No repository configuration package available for ${DISTRO} ${SYSVERSION}." + warning "No repository configuration package available for ${DISTRO} ${SYSVERSION}. Cannot install native packages on this system." return 2 fi if ! download "${repoconfig_url}" "${tmpdir}/${repoconfig_file}"; then - fatal "Failed to download repository configuration package." F0209 + fatal "Failed to download repository configuration package. ${BADNET_MSG}." F0209 fi if [ -n "${needs_early_refresh}" ]; then # shellcheck disable=SC2086 if ! run ${ROOTCMD} env ${env} ${pm_cmd} ${repo_subcmd} ${repo_update_opts}; then - warning "Failed to refresh repository metadata." + warning "${failed_refresh_msg}" return 2 fi fi @@ -1413,7 +1499,7 @@ try_package_install() { if [ -n "${repo_subcmd}" ]; then # shellcheck disable=SC2086 if ! run ${ROOTCMD} env ${env} ${pm_cmd} ${repo_subcmd} ${repo_update_opts}; then - fatal "Failed to refresh repository metadata." F0205 + fatal "${failed_refresh_msg}" F0205 fi fi else @@ -1530,16 +1616,16 @@ try_static_install() { netdata_agent="${NETDATA_STATIC_ARCHIVE_OLD_URL#"https://github.com/netdata/netdata/releases/download/v${INSTALL_VERSION}/"}" export NETDATA_STATIC_ARCHIVE_URL="${NETDATA_STATIC_ARCHIVE_OLD_URL}" else - warning "No static build available for ${SYSARCH} CPUs." + warning "There is no static build available for ${SYSARCH} CPUs. This usually means we simply do not currently provide static builds for ${SYSARCH} CPUs." return 2 fi if ! download "${NETDATA_STATIC_ARCHIVE_URL}" "${tmpdir}/${netdata_agent}"; then - fatal "Unable to download static build archive for ${SYSARCH}." F0208 + fatal "Unable to download static build archive for ${SYSARCH}. ${BADNET_MSG}." F0208 fi if ! download "${NETDATA_STATIC_ARCHIVE_CHECKSUM_URL}" "${tmpdir}/sha256sum.txt"; then - fatal "Unable to fetch checksums to verify static build archive." F0206 + fatal "Unable to fetch checksums to verify static build archive. ${BADNET_MSG}." F0206 fi if [ "${DRY_RUN}" -eq 1 ]; then @@ -1547,7 +1633,7 @@ try_static_install() { else if [ -z "${INSTALL_VERSION}" ]; then if ! grep "${netdata_agent}" "${tmpdir}/sha256sum.txt" | safe_sha256sum -c - > /dev/null 2>&1; then - fatal "Static binary checksum validation failed. Usually this is a result of an older copy of the file being cached somewhere upstream and can be resolved by retrying in an hour." F0207 + fatal "Static binary checksum validation failed. ${BADCACHE_MSG}." F0207 fi fi fi @@ -1615,7 +1701,7 @@ install_local_build_dependencies() { fi if ! download "${PACKAGES_SCRIPT}" "${tmpdir}/install-required-packages.sh"; then - fatal "Failed to download dependency handling script for local build." F000D + fatal "Failed to download dependency handling script for local build. ${BADNET_MSG}." F000D fi if [ "${DRY_RUN}" -eq 1 ]; then @@ -1665,19 +1751,15 @@ build_and_install() { opts="${opts} --disable-cloud" fi - export NETDATA_SAVE_WARNINGS=1 - export NETDATA_PROPAGATE_WARNINGS=1 - # shellcheck disable=SC2090 - export NETDATA_WARNINGS="${NETDATA_WARNINGS}" # shellcheck disable=SC2086 - run ${ROOTCMD} ./netdata-installer.sh ${opts} + run_script ./netdata-installer.sh ${opts} case $? in 1) if [ -n "${EXIT_REASON}" ]; then fatal "netdata-installer.sh failed to run: ${EXIT_REASON}" "${EXIT_CODE}" else - fatal "netdata-installer.sh failed to run correctly." I0000 + fatal "netdata-installer.sh failed to run: Encountered an unhandled error in the installer code." I0000 fi ;; 2) @@ -1703,14 +1785,14 @@ try_build_install() { if [ -n "${INSTALL_VERSION}" ]; then if ! download "${NETDATA_SOURCE_ARCHIVE_URL}" "${tmpdir}/netdata-v${INSTALL_VERSION}.tar.gz"; then - fatal "Failed to download source tarball for local build." F000B + fatal "Failed to download source tarball for local build. ${BADNET_MSG}." F000B fi elif ! download "${NETDATA_SOURCE_ARCHIVE_URL}" "${tmpdir}/netdata-latest.tar.gz"; then - fatal "Failed to download source tarball for local build." F000B + fatal "Failed to download source tarball for local build. ${BADNET_MSG}." F000B fi if ! download "${NETDATA_SOURCE_ARCHIVE_CHECKSUM_URL}" "${tmpdir}/sha256sum.txt"; then - fatal "Failed to download checksums for source tarball verification." F000C + fatal "Failed to download checksums for source tarball verification. ${BADNET_MSG}." F000C fi if [ "${DRY_RUN}" -eq 1 ]; then @@ -1719,7 +1801,7 @@ try_build_install() { if [ -z "${INSTALL_VERSION}" ]; then # shellcheck disable=SC2086 if ! grep netdata-latest.tar.gz "${tmpdir}/sha256sum.txt" | safe_sha256sum -c - > /dev/null 2>&1; then - fatal "Tarball checksum validation failed. Usually this is a result of an older copy of the file being cached somewhere upstream and can be resolved by retrying in an hour." F0005 + fatal "Tarball checksum validation failed. ${BADCACHE_MSG}." F0005 fi fi fi @@ -1771,7 +1853,7 @@ prepare_offline_install_source() { progress "Fetching ${NETDATA_STATIC_ARCHIVE_URL}" if ! download "${NETDATA_STATIC_ARCHIVE_URL}" "netdata-${arch}-latest.gz.run"; then - warning "Failed to download static installer archive for ${arch}." + warning "Failed to download static installer archive for ${arch}. ${BADNET_MSG}." fi done legacy=0 @@ -1781,13 +1863,13 @@ prepare_offline_install_source() { legacy=1 if ! download "${NETDATA_STATIC_ARCHIVE_OLD_URL}" "netdata-x86_64-latest.gz.run"; then - warning "Failed to download static installer archive for x86_64." + warning "Failed to download static installer archive for x86_64. ${BADNET_MSG}." fi fi progress "Fetching ${NETDATA_STATIC_ARCHIVE_CHECKSUM_URL}" if ! download "${NETDATA_STATIC_ARCHIVE_CHECKSUM_URL}" "sha256sums.txt"; then - fatal "Failed to download checksum file." F0506 + fatal "Failed to download checksum file. ${BADNET_MSG}." F0506 fi fi @@ -1799,7 +1881,7 @@ prepare_offline_install_source() { if [ "${DRY_RUN}" -ne 1 ]; then progress "Verifying checksums." if ! grep -e "$(find . -name '*.gz.run')" sha256sums.txt | safe_sha256sum -c -; then - fatal "Checksums for offline install files are incorrect. Usually this is a result of an older copy of the file being cached somewhere upstream and can be resolved by retrying in an hour." F0507 + fatal "Checksums for offline install files are incorrect. ${BADCACHE_MSG}." F0507 fi else progress "Would verify SHA256 checksums of downloaded installation files." @@ -2037,9 +2119,18 @@ parse_args() { ;; esac ;; - "--reinstall") NETDATA_REINSTALL=1 ;; - "--reinstall-even-if-unsafe") NETDATA_UNSAFE_REINSTALL=1 ;; - "--claim-only") NETDATA_CLAIM_ONLY=1 ;; + "--reinstall") + NETDATA_REINSTALL=1 + NETDATA_COMMAND="reinstall" + ;; + "--reinstall-even-if-unsafe") + NETDATA_UNSAFE_REINSTALL=1 + NETDATA_COMMAND="unsafe-reinstall" + ;; + "--claim-only") + NETDATA_CLAIM_ONLY=1 + NETDATA_COMMAND="claim-only" + ;; "--disable-cloud") NETDATA_DISABLE_CLOUD=1 NETDATA_REQUIRE_CLOUD=0 @@ -2076,12 +2167,15 @@ parse_args() { ;; "--uninstall") ACTION="uninstall" + NETDATA_COMMAND="uninstall" ;; "--reinstall-clean") ACTION="reinstall-clean" + NETDATA_COMMAND="reinstall-clean" ;; "--repositories-only") REPO_ACTION="repositories-only" + NETDATA_COMMAND="repositories" ;; "--native-only") NETDATA_ONLY_NATIVE=1 @@ -2139,6 +2233,7 @@ parse_args() { "--prepare-offline-install-source") if [ -n "${2}" ]; then ACTION="prepare-offline" + NETDATA_COMMAND="prepare-offline" OFFLINE_TARGET="${2}" shift 1 else diff --git a/packaging/installer/methods/kickstart.md b/packaging/installer/methods/kickstart.md index fc212ea2e..2555e4a83 100644 --- a/packaging/installer/methods/kickstart.md +++ b/packaging/installer/methods/kickstart.md @@ -3,7 +3,7 @@ title: "Install Netdata with kickstart.sh" description: "The kickstart.sh script installs Netdata from source, including all dependencies required to connect to Netdata Cloud, with a single command." custom_edit_url: https://github.com/netdata/netdata/edit/master/packaging/installer/methods/kickstart.md --> -import { OneLineInstallWget, OneLineInstallCurl } from '../../../../../src/components/OneLineInstall/' +import { OneLineInstallWget, OneLineInstallCurl } from '@site/src/components/OneLineInstall/' # Install Netdata with kickstart.sh @@ -107,7 +107,7 @@ To automatically claim nodes after installation: - `--claim-rooms`: Specify a comma-separated list of tokens for each War Room this node should appear in. - `--claim-proxy`: Specify a proxy to use when connecting to the cloud in the form of `http://[user:pass@]host:ip` for an HTTP(S) proxy. See [connecting through a proxy](/claim/README.md#connect-through-a-proxy) for details. -- `--claim-url`: Specify a URL to use when connecting to the cloud. Defaults to `https://app.netdata.cloud`. +- `--claim-url`: Specify a URL to use when connecting to the cloud. Defaults to `https://api.netdata.cloud`. For example: diff --git a/packaging/installer/methods/macos.md b/packaging/installer/methods/macos.md index c43d8dfb2..a1b5f60ce 100644 --- a/packaging/installer/methods/macos.md +++ b/packaging/installer/methods/macos.md @@ -44,11 +44,11 @@ area](https://learn.netdata.cloud/docs/cloud/spaces#manage-spaces). - `--claim-rooms`: Specify a comma-separated list of tokens for each War Room this node should appear in. - `--claim-proxy`: Specify a proxy to use when connecting to the cloud in the form of `http://[user:pass@]host:ip` for an HTTP(S) proxy. See [connecting through a proxy](/claim/README.md#connect-through-a-proxy) for details. -- `--claim-url`: Specify a URL to use when connecting to the cloud. Defaults to `https://app.netdata.cloud`. +- `--claim-url`: Specify a URL to use when connecting to the cloud. Defaults to `https://api.netdata.cloud`. For example: ```bash -curl https://my-netdata.io/kickstart.sh > /tmp/netdata-kickstart.sh && sh /tmp/netdata-kickstart.sh --install /usr/local/ --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://app.netdata.cloud +curl https://my-netdata.io/kickstart.sh > /tmp/netdata-kickstart.sh && sh /tmp/netdata-kickstart.sh --install /usr/local/ --claim-token TOKEN --claim-rooms ROOM1,ROOM2 --claim-url https://api.netdata.cloud ``` The Netdata Agent is installed under `/usr/local/netdata` on your machine. Your machine will also show up as a node in your Netdata Cloud. diff --git a/packaging/installer/methods/manual.md b/packaging/installer/methods/manual.md index c47c2e364..d32075394 100644 --- a/packaging/installer/methods/manual.md +++ b/packaging/installer/methods/manual.md @@ -41,7 +41,7 @@ and other operating systems and is regularly tested. You can find this tool [her - **SLE12** Must have your system registered with SUSE Customer Center or have the DVD. See [#1162](https://github.com/netdata/netdata/issues/1162) -Install the packages for having a **basic Netdata installation** (system monitoring and many applications, without `mysql` / `mariadb`, `postgres`, `named`, hardware sensors and `SNMP`): +Install the packages for having a **basic Netdata installation** (system monitoring and many applications, without `mysql` / `mariadb`, `named`, hardware sensors and `SNMP`): ```sh curl -Ss 'https://raw.githubusercontent.com/netdata/netdata/master/packaging/installer/install-required-packages.sh' >/tmp/install-required-packages.sh && bash /tmp/install-required-packages.sh -i netdata @@ -99,7 +99,6 @@ Netdata plugins and various aspects of Netdata can be enabled or benefit when th | `python-dnspython`|used for monitoring DNS query time| | `python-ipaddress`|used for monitoring **DHCPd**
this package is required only if the system has python v2. python v3 has this functionality embedded| | `python-mysqldb`
or
`python-pymysql`|used for monitoring **mysql** or **mariadb** databases
`python-mysqldb` is a lot faster and thus preferred| -| `python-psycopg2`|used for monitoring **postgresql** databases| | `python-pymongo`|used for monitoring **mongodb** databases| | `nodejs`|used for `node.js` plugins for monitoring **named** and **SNMP** devices| | `lm-sensors`|for monitoring **hardware sensors**| diff --git a/packaging/installer/netdata-updater.sh b/packaging/installer/netdata-updater.sh index 15b7deda8..d018d67d2 100755 --- a/packaging/installer/netdata-updater.sh +++ b/packaging/installer/netdata-updater.sh @@ -85,9 +85,17 @@ exit_reason() { EXIT_REASON="${1}" EXIT_CODE="${2}" if [ -n "${NETDATA_PROPAGATE_WARNINGS}" ]; then - export EXIT_REASON - export EXIT_CODE - export NETDATA_WARNINGS + if [ -n "${NETDATA_SCRIPT_STATUS_PATH}" ]; then + { + echo "EXIT_REASON=\"${EXIT_REASON}\"" + echo "EXIT_CODE=\"${EXIT_CODE}\"" + echo "NETDATA_WARNINGS=\"${NETDATA_WARNINGS}\"" + } >> "${NETDATA_SCRIPT_STATUS_PATH}" + else + export EXIT_REASON + export EXIT_CODE + export NETDATA_WARNINGS + fi fi fi } @@ -602,15 +610,20 @@ update_build() { export NETDATA_SAVE_WARNINGS=1 export NETDATA_PROPAGATE_WARNINGS=1 export NETDATA_WARNINGS="${NETDATA_WARNINGS}" + export NETDATA_SCRIPT_STATUS_PATH="${NETDATA_SCRIPT_STATUS_PATH}" # shellcheck disable=SC2086 if ! ${env} ./netdata-installer.sh ${REINSTALL_OPTIONS} --dont-wait ${do_not_start} >&3 2>&3; then + if [ -r "${NETDATA_SCRIPT_STATUS_PATH}" ]; then + # shellcheck disable=SC1090 + . "${NETDATA_SCRIPT_STATUS_PATH}" + rm -f "${NETDATA_SCRIPT_STATUS_PATH}" + fi if [ -n "${EXIT_REASON}" ]; then fatal "Failed to rebuild existing netdata install: ${EXIT_REASON}" "U${EXIT_CODE}" else fatal "Failed to rebuild existing netdata reinstall." UI0000 fi fi - eval "${env} ./netdata-installer.sh ${REINSTALL_OPTIONS} --dont-wait ${do_not_start}" >&3 2>&3 || fatal "FAILED TO COMPILE/INSTALL NETDATA" U0009 # We no longer store checksum info here. but leave this so that we clean up all environment files upon next update. sed -i '/NETDATA_TARBALL/d' "${ENVIRONMENT_FILE}" @@ -650,7 +663,7 @@ update_static() { # Do not pass any options other than the accept, for now # shellcheck disable=SC2086 - if sh "${ndtmpdir}/netdata-${sysarch}-latest.gz.run" --accept -- ${REINSTALL_OPTIONS}; then + if sh "${ndtmpdir}/netdata-${sysarch}-latest.gz.run" --accept -- ${REINSTALL_OPTIONS} >&3 2>&3; then rm -r "${ndtmpdir}" else info "NOTE: did not remove: ${ndtmpdir}" @@ -691,7 +704,7 @@ update_binpkg() { opensuse-leap) DISTRO_COMPAT_NAME="opensuse" ;; - almalinux|rocky|rhel) + cloudlinux|almalinux|rocky|rhel) DISTRO_COMPAT_NAME="centos" ;; *) diff --git a/packaging/libbpf.checksums b/packaging/libbpf.checksums index e7e3985a8..9a8b8f8cf 100644 --- a/packaging/libbpf.checksums +++ b/packaging/libbpf.checksums @@ -1 +1 @@ -8909385c347848b5994d9daa33be12e35ffc529ba0e01b5c944e0216bf78f79b v0.8.0_netdata.tar.gz +63fe4ac3f6807e8ff4cd3af2ffae0091eb177fb7f6aca2f03d3f201a609b988c v1.0.1_netdata.tar.gz diff --git a/packaging/libbpf.version b/packaging/libbpf.version index 606129075..bb58ffc40 100644 --- a/packaging/libbpf.version +++ b/packaging/libbpf.version @@ -1 +1 @@ -0.8.0_netdata +1.0.1_netdata diff --git a/packaging/makeself/install-or-update.sh b/packaging/makeself/install-or-update.sh index aef67a156..be2b2f75f 100755 --- a/packaging/makeself/install-or-update.sh +++ b/packaging/makeself/install-or-update.sh @@ -215,7 +215,7 @@ for x in apps.plugin freeipmi.plugin ioping cgroup-network ebpf.plugin perf.plug done if [ -f "usr/libexec/netdata/plugins.d/go.d.plugin" ] && command -v setcap 1>/dev/null 2>&1; then - run setcap cap_net_admin+epi "usr/libexec/netdata/plugins.d/go.d.plugin" + run setcap "cap_net_admin+epi cap_net_raw=eip" "usr/libexec/netdata/plugins.d/go.d.plugin" fi # fix the fping binary diff --git a/packaging/version b/packaging/version index b5a0280e2..b909b32cd 100644 --- a/packaging/version +++ b/packaging/version @@ -1 +1 @@ -v1.36.1 +v1.37.0 diff --git a/parser/parser.c b/parser/parser.c index c37d1e2c4..5b4c528de 100644 --- a/parser/parser.c +++ b/parser/parser.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "parser.h" +#include "collectors/plugins.d/pluginsd_parser.h" -static inline int find_keyword(char *str, char *keyword, int max_size, int (*custom_isspace)(char)) +inline int find_first_keyword(const char *str, char *keyword, int max_size, int (*custom_isspace)(char)) { - char *s = str, *keyword_start; + const char *s = str, *keyword_start; while (unlikely(custom_isspace(*s))) s++; keyword_start = s; @@ -28,16 +29,22 @@ static inline int find_keyword(char *str, char *keyword, int max_size, int (*cus * */ -PARSER *parser_init(RRDHOST *host, void *user, void *input, PARSER_INPUT_TYPE flags) +PARSER *parser_init(RRDHOST *host, void *user, FILE *fp_input, FILE *fp_output, int fd, PARSER_INPUT_TYPE flags, void *ssl __maybe_unused) { PARSER *parser; parser = callocz(1, sizeof(*parser)); - parser->plugins_action = callocz(1, sizeof(PLUGINSD_ACTION)); parser->user = user; - parser->input = input; + parser->fd = fd; + parser->fp_input = fp_input; + parser->fp_output = fp_output; +#ifdef ENABLE_HTTPS + parser->ssl_output = ssl; +#endif parser->flags = flags; parser->host = host; + parser->worker_job_next_id = WORKER_PARSER_FIRST_JOB; + inflight_functions_init(parser); #ifdef ENABLE_HTTPS parser->bytesleft = 0; @@ -45,18 +52,28 @@ PARSER *parser_init(RRDHOST *host, void *user, void *input, PARSER_INPUT_TYPE fl #endif if (unlikely(!(flags & PARSER_NO_PARSE_INIT))) { - parser_add_keyword(parser, PLUGINSD_KEYWORD_FLUSH, pluginsd_flush); - parser_add_keyword(parser, PLUGINSD_KEYWORD_CHART, pluginsd_chart); - parser_add_keyword(parser, PLUGINSD_KEYWORD_DIMENSION, pluginsd_dimension); - parser_add_keyword(parser, PLUGINSD_KEYWORD_DISABLE, pluginsd_disable); - parser_add_keyword(parser, PLUGINSD_KEYWORD_VARIABLE, pluginsd_variable); - parser_add_keyword(parser, PLUGINSD_KEYWORD_LABEL, pluginsd_label); - parser_add_keyword(parser, PLUGINSD_KEYWORD_OVERWRITE, pluginsd_overwrite); - parser_add_keyword(parser, PLUGINSD_KEYWORD_END, pluginsd_end); - parser_add_keyword(parser, "CLABEL_COMMIT", pluginsd_clabel_commit); - parser_add_keyword(parser, "CLABEL", pluginsd_clabel); - parser_add_keyword(parser, PLUGINSD_KEYWORD_BEGIN, pluginsd_begin); - parser_add_keyword(parser, "SET", pluginsd_set); + parser_add_keyword(parser, PLUGINSD_KEYWORD_FLUSH, pluginsd_flush); + parser_add_keyword(parser, PLUGINSD_KEYWORD_CHART, pluginsd_chart); + parser_add_keyword(parser, PLUGINSD_KEYWORD_CHART_DEFINITION_END, pluginsd_chart_definition_end); + parser_add_keyword(parser, PLUGINSD_KEYWORD_DIMENSION, pluginsd_dimension); + parser_add_keyword(parser, PLUGINSD_KEYWORD_DISABLE, pluginsd_disable); + parser_add_keyword(parser, PLUGINSD_KEYWORD_VARIABLE, pluginsd_variable); + parser_add_keyword(parser, PLUGINSD_KEYWORD_LABEL, pluginsd_label); + parser_add_keyword(parser, PLUGINSD_KEYWORD_OVERWRITE, pluginsd_overwrite); + parser_add_keyword(parser, PLUGINSD_KEYWORD_END, pluginsd_end); + parser_add_keyword(parser, PLUGINSD_KEYWORD_CLABEL_COMMIT, pluginsd_clabel_commit); + parser_add_keyword(parser, PLUGINSD_KEYWORD_CLABEL, pluginsd_clabel); + parser_add_keyword(parser, PLUGINSD_KEYWORD_BEGIN, pluginsd_begin); + parser_add_keyword(parser, PLUGINSD_KEYWORD_SET, pluginsd_set); + + parser_add_keyword(parser, PLUGINSD_KEYWORD_FUNCTION, pluginsd_function); + parser_add_keyword(parser, PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN, pluginsd_function_result_begin); + + parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_BEGIN, pluginsd_replay_rrdset_begin); + parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_SET, pluginsd_replay_set); + parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE, pluginsd_replay_rrddim_collection_state); + parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE, pluginsd_replay_rrdset_collection_state); + parser_add_keyword(parser, PLUGINSD_KEYWORD_REPLAY_END, pluginsd_replay_end); } return parser; @@ -133,7 +150,7 @@ int parser_add_keyword(PARSER *parser, char *keyword, keyword_function func) tmp_keyword = callocz(1, sizeof(*tmp_keyword)); - tmp_keyword->worker_job_id = parser->worker_job_ids++; + tmp_keyword->worker_job_id = parser->worker_job_next_id++; tmp_keyword->keyword = strdupz(keyword); tmp_keyword->keyword_hash = keyword_hash; tmp_keyword->func[tmp_keyword->func_no++] = (void *) func; @@ -154,6 +171,8 @@ void parser_destroy(PARSER *parser) if (unlikely(!parser)) return; + dictionary_destroy(parser->inflight.functions); + PARSER_KEYWORD *tmp_keyword, *tmp_keyword_next; PARSER_DATA *tmp_parser_data, *tmp_parser_data_next; @@ -175,7 +194,6 @@ void parser_destroy(PARSER *parser) tmp_parser_data = tmp_parser_data_next; } - freez(parser->plugins_action); freez(parser); } @@ -205,19 +223,21 @@ int parser_next(PARSER *parser) } if (unlikely(parser->read_function)) - tmp = parser->read_function(parser->buffer, PLUGINSD_LINE_MAX, parser->input); + tmp = parser->read_function(parser->buffer, PLUGINSD_LINE_MAX, parser->fp_input); + else if(likely(parser->fp_input)) + tmp = fgets(parser->buffer, PLUGINSD_LINE_MAX, (FILE *)parser->fp_input); else - tmp = fgets(parser->buffer, PLUGINSD_LINE_MAX, (FILE *)parser->input); + tmp = NULL; if (unlikely(!tmp)) { if (unlikely(parser->eof_function)) { - int rc = parser->eof_function(parser->input); + int rc = parser->eof_function(parser->fp_input); error("read failed: user defined function returned %d", rc); } else { - if (feof((FILE *)parser->input)) + if (feof((FILE *)parser->fp_input)) error("read failed: end of file"); - else if (ferror((FILE *)parser->input)) + else if (ferror((FILE *)parser->fp_input)) error("read failed: input error"); else error("read failed: unknown error"); @@ -236,64 +256,97 @@ int parser_next(PARSER *parser) inline int parser_action(PARSER *parser, char *input) { - PARSER_RC rc = PARSER_RC_OK; - char *words[PLUGINSD_MAX_WORDS] = { NULL }; + parser->line++; + + PARSER_RC rc = PARSER_RC_OK; + char *words[PLUGINSD_MAX_WORDS]; char command[PLUGINSD_LINE_MAX + 1]; keyword_function action_function; keyword_function *action_function_list = NULL; - if (unlikely(!parser)) + if (unlikely(!parser)) { + internal_error(true, "parser is NULL"); return 1; + } + parser->recover_location[0] = 0x0; // if not direct input check if we have reprocessed this if (unlikely(!input && parser->flags & PARSER_INPUT_PROCESSED)) return 0; - PARSER_KEYWORD *tmp_keyword = parser->keyword; + PARSER_KEYWORD *tmp_keyword = parser->keyword; if (unlikely(!tmp_keyword)) { + internal_error(true, "called without a keyword"); return 1; } if (unlikely(!input)) input = parser->buffer; - if (unlikely(!find_keyword(input, command, PLUGINSD_LINE_MAX, pluginsd_space))) + if(unlikely(parser->flags & PARSER_DEFER_UNTIL_KEYWORD)) { + bool has_keyword = find_first_keyword(input, command, PLUGINSD_LINE_MAX, pluginsd_space); + + if(!has_keyword || strcmp(command, parser->defer.end_keyword) != 0) { + if(parser->defer.response) { + buffer_strcat(parser->defer.response, input); + if(buffer_strlen(parser->defer.response) > 10 * 1024 * 1024) { + // more than 10MB of data + // a bad plugin that did not send the end_keyword + internal_error(true, "PLUGINSD: deferred response is too big (%zu bytes). Stopping this plugin.", buffer_strlen(parser->defer.response)); + return 1; + } + } + return 0; + } + else { + // call the action + parser->defer.action(parser, parser->defer.action_data); + + // empty everything + parser->defer.action = NULL; + parser->defer.action_data = NULL; + parser->defer.end_keyword = NULL; + parser->defer.response = NULL; + parser->flags &= ~PARSER_DEFER_UNTIL_KEYWORD; + } + return 0; + } + + if (unlikely(!find_first_keyword(input, command, PLUGINSD_LINE_MAX, pluginsd_space))) return 0; - if ((parser->flags & PARSER_INPUT_ORIGINAL) == PARSER_INPUT_ORIGINAL) - pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, parser->recover_input, parser->recover_location, PARSER_MAX_RECOVER_KEYWORDS); + size_t num_words = 0; + if ((parser->flags & PARSER_INPUT_KEEP_ORIGINAL) == PARSER_INPUT_KEEP_ORIGINAL) + num_words = pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, parser->recover_input, parser->recover_location, PARSER_MAX_RECOVER_KEYWORDS); else - pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, NULL, NULL, 0); + num_words = pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, NULL, NULL, 0); uint32_t command_hash = simple_hash(command); - size_t worker_job_id = 0; + size_t worker_job_id = WORKER_UTILIZATION_MAX_JOB_TYPES + 1; // set an invalid value by default while(tmp_keyword) { - if (command_hash == tmp_keyword->keyword_hash && - (!strcmp(command, tmp_keyword->keyword))) { - action_function_list = &tmp_keyword->func[0]; - worker_job_id = tmp_keyword->worker_job_id; - break; + if (command_hash == tmp_keyword->keyword_hash && (!strcmp(command, tmp_keyword->keyword))) { + action_function_list = &tmp_keyword->func[0]; + worker_job_id = tmp_keyword->worker_job_id; + break; } tmp_keyword = tmp_keyword->next; } if (unlikely(!action_function_list)) { if (unlikely(parser->unknown_function)) - rc = parser->unknown_function(words, parser->user, NULL); + rc = parser->unknown_function(words, num_words, parser->user); else rc = PARSER_RC_ERROR; -#ifdef NETDATA_INTERNAL_CHECKS - error("Unknown keyword [%s]", input); -#endif } else { worker_is_busy(worker_job_id); while ((action_function = *action_function_list) != NULL) { - rc = action_function(words, parser->user, parser->plugins_action); + rc = action_function(words, num_words, parser->user); if (unlikely(rc == PARSER_RC_ERROR || rc == PARSER_RC_STOP)) - break; + break; + action_function_list++; } worker_is_idle(); @@ -302,6 +355,25 @@ inline int parser_action(PARSER *parser, char *input) if (likely(input == parser->buffer)) parser->flags |= PARSER_INPUT_PROCESSED; +#ifdef NETDATA_INTERNAL_CHECKS + if(rc == PARSER_RC_ERROR) { + BUFFER *wb = buffer_create(PLUGINSD_LINE_MAX); + for(size_t i = 0; i < num_words ;i++) { + if(i) buffer_fast_strcat(wb, " ", 1); + + buffer_fast_strcat(wb, "\"", 1); + const char *s = get_word(words, num_words, i); + buffer_strcat(wb, s?s:""); + buffer_fast_strcat(wb, "\"", 1); + } + + internal_error(true, "PLUGINSD: parser_action('%s') failed on line %zu: { %s } (quotes added to show parsing)", + command, parser->line, buffer_tostring(wb)); + + buffer_free(wb); + } +#endif + return (rc == PARSER_RC_ERROR); } diff --git a/parser/parser.h b/parser/parser.h index 1887318fc..ad7488389 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -7,6 +7,10 @@ #define PARSER_MAX_CALLBACKS 20 #define PARSER_MAX_RECOVER_KEYWORDS 128 +#define WORKER_PARSER_FIRST_JOB 3 + +// this has to be in-sync with the same at receiver.c +#define WORKER_RECEIVER_JOB_REPLICATION_COMPLETION (WORKER_PARSER_FIRST_JOB - 3) // PARSER return codes typedef enum parser_rc { @@ -15,43 +19,18 @@ typedef enum parser_rc { PARSER_RC_ERROR // Callback failed (abort rest of callbacks) } PARSER_RC; -typedef struct pluginsd_action { - PARSER_RC (*set_action)(void *user, RRDSET *st, RRDDIM *rd, long long int value); - PARSER_RC (*begin_action)(void *user, RRDSET *st, usec_t microseconds, int trust_durations); - PARSER_RC (*end_action)(void *user, RRDSET *st); - PARSER_RC (*chart_action) - (void *user, char *type, char *id, char *name, char *family, char *context, char *title, char *units, char *plugin, - char *module, int priority, int update_every, RRDSET_TYPE chart_type, char *options); - PARSER_RC (*dimension_action) - (void *user, RRDSET *st, char *id, char *name, char *algorithm, long multiplier, long divisor, char *options, - RRD_ALGORITHM algorithm_type); - - PARSER_RC (*flush_action)(void *user, RRDSET *st); - PARSER_RC (*disable_action)(void *user); - PARSER_RC (*variable_action)(void *user, RRDHOST *host, RRDSET *st, char *name, int global, NETDATA_DOUBLE value); - PARSER_RC (*label_action)(void *user, char *key, char *value, RRDLABEL_SRC source); - PARSER_RC (*overwrite_action)(void *user, RRDHOST *host, DICTIONARY *new_labels); - PARSER_RC (*clabel_action)(void *user, char *key, char *value, RRDLABEL_SRC source); - PARSER_RC (*clabel_commit_action)(void *user, RRDHOST *host, DICTIONARY *new_labels); - - PARSER_RC (*guid_action)(void *user, uuid_t *uuid); - PARSER_RC (*context_action)(void *user, uuid_t *uuid); - PARSER_RC (*tombstone_action)(void *user, uuid_t *uuid); - PARSER_RC (*host_action)(void *user, char *machine_guid, char *hostname, char *registry_hostname, int update_every, char *os, - char *timezone, char *tags); -} PLUGINSD_ACTION; - typedef enum parser_input_type { - PARSER_INPUT_SPLIT = 1 << 1, - PARSER_INPUT_ORIGINAL = 1 << 2, - PARSER_INPUT_PROCESSED = 1 << 3, - PARSER_NO_PARSE_INIT = 1 << 4, - PARSER_NO_ACTION_INIT = 1 << 5, + PARSER_INPUT_SPLIT = (1 << 1), + PARSER_INPUT_KEEP_ORIGINAL = (1 << 2), + PARSER_INPUT_PROCESSED = (1 << 3), + PARSER_NO_PARSE_INIT = (1 << 4), + PARSER_NO_ACTION_INIT = (1 << 5), + PARSER_DEFER_UNTIL_KEYWORD = (1 << 6), } PARSER_INPUT_TYPE; #define PARSER_INPUT_FULL (PARSER_INPUT_SPLIT|PARSER_INPUT_ORIGINAL) -typedef PARSER_RC (*keyword_function)(char **, void *, PLUGINSD_ACTION *plugins_action); +typedef PARSER_RC (*keyword_function)(char **words, size_t num_words, void *user_data); typedef struct parser_keyword { size_t worker_job_id; @@ -68,15 +47,20 @@ typedef struct parser_data { } PARSER_DATA; typedef struct parser { - size_t worker_job_ids; + size_t worker_job_next_id; uint8_t version; // Parser version RRDHOST *host; - void *input; // Input source e.g. stream + int fd; // Socket + FILE *fp_input; // Input source e.g. stream + FILE *fp_output; // Stream to send commands to plugin +#ifdef ENABLE_HTTPS + struct netdata_ssl *ssl_output; +#endif PARSER_DATA *data; // extra input PARSER_KEYWORD *keyword; // List of parse keywords and functions - PLUGINSD_ACTION *plugins_action; void *user; // User defined structure to hold extra state between calls uint32_t flags; + size_t line; char *(*read_function)(char *buffer, long unsigned int, void *input); int (*eof_function)(void *input); @@ -89,9 +73,24 @@ typedef struct parser { char tmpbuffer[PLUGINSD_LINE_MAX]; char *readfrom; #endif + + struct { + const char *end_keyword; + BUFFER *response; + void (*action)(struct parser *parser, void *action_data); + void *action_data; + } defer; + + struct { + DICTIONARY *functions; + usec_t smaller_timeout; + } inflight; + } PARSER; -PARSER *parser_init(RRDHOST *host, void *user, void *input, PARSER_INPUT_TYPE flags); +int find_first_keyword(const char *str, char *keyword, int max_size, int (*custom_isspace)(char)); + +PARSER *parser_init(RRDHOST *host, void *user, FILE *fp_input, FILE *fp_output, int fd, PARSER_INPUT_TYPE flags, void *ssl); int parser_add_keyword(PARSER *working_parser, char *keyword, keyword_function func); int parser_next(PARSER *working_parser); int parser_action(PARSER *working_parser, char *input); @@ -99,22 +98,26 @@ int parser_push(PARSER *working_parser, char *line); void parser_destroy(PARSER *working_parser); int parser_recover_input(PARSER *working_parser); -extern size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations); - -extern PARSER_RC pluginsd_set(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_begin(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_end(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_chart(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_dimension(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_variable(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_flush(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_disable(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_label(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_overwrite(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_guid(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_context(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_tombstone(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_clabel_commit(char **words, void *user, PLUGINSD_ACTION *plugins_action); -extern PARSER_RC pluginsd_clabel(char **words, void *user, PLUGINSD_ACTION *plugins_action); +size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp_plugin_input, FILE *fp_plugin_output, int trust_durations); + +PARSER_RC pluginsd_set(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_begin(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_end(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_chart(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_dimension(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_variable(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_flush(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_disable(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_label(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_overwrite(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_clabel_commit(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_clabel(char **words, size_t num_words, void *user); + +PARSER_RC pluginsd_replay_rrdset_begin(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_replay_rrddim_collection_state(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_replay_set(char **words, size_t num_words, void *user); +PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user); #endif diff --git a/registry/registry.c b/registry/registry.c index a4f9c6de3..e6c2172b0 100644 --- a/registry/registry.c +++ b/registry/registry.c @@ -61,7 +61,7 @@ static inline void registry_json_header(RRDHOST *host, struct web_client *w, con buffer_flush(w->response.data); w->response.data->contenttype = CT_APPLICATION_JSON; buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"", - action, status, host->registry_hostname, host->machine_guid); + action, status, rrdhost_registry_hostname(host), host->machine_guid); } static inline void registry_json_footer(struct web_client *w) { @@ -108,9 +108,7 @@ static int registry_json_person_url_callback(void *entry, void *data) { } // callback for rendering MACHINE_URLs -static int registry_json_machine_url_callback(const char *name, void *entry, void *data) { - (void)name; - +static int registry_json_machine_url_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data) { REGISTRY_MACHINE_URL *mu = (REGISTRY_MACHINE_URL *)entry; struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data; struct web_client *w = c->w; @@ -166,10 +164,12 @@ void registry_update_cloud_base_url() int registry_request_hello_json(RRDHOST *host, struct web_client *w) { registry_json_header(host, w, "hello", REGISTRY_STATUS_OK); + const char *cloud_ui_url = appconfig_get(&cloud_config, CONFIG_SECTION_GLOBAL, "cloud ui url", DEFAULT_CLOUD_UI_URL); + buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\",\n\t\"cloud_base_url\": \"%s\",\n\t\"anonymous_statistics\": %s", registry.registry_to_announce, - registry.cloud_base_url, netdata_anonymous_statistics_enabled?"true":"false"); + cloud_ui_url, netdata_anonymous_statistics_enabled?"true":"false"); registry_json_footer(w); return 200; @@ -379,7 +379,6 @@ void registry_statistics(void) { rrddim_add(sts, "sessions", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(sts); rrddim_set(sts, "sessions", registry.usages_count); rrdset_done(sts); @@ -408,7 +407,6 @@ void registry_statistics(void) { rrddim_add(stc, "persons_urls", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); rrddim_add(stc, "machines_urls", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(stc); rrddim_set(stc, "persons", registry.persons_count); rrddim_set(stc, "machines", registry.machines_count); @@ -441,10 +439,9 @@ void registry_statistics(void) { rrddim_add(stm, "persons_urls", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); rrddim_add(stm, "machines_urls", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(stm); - rrddim_set(stm, "persons", registry.persons_memory + dictionary_stats_allocated_memory(registry.persons)); - rrddim_set(stm, "machines", registry.machines_memory + dictionary_stats_allocated_memory(registry.machines)); + rrddim_set(stm, "persons", registry.persons_memory + dictionary_stats_for_registry(registry.persons)); + rrddim_set(stm, "machines", registry.machines_memory + dictionary_stats_for_registry(registry.machines)); rrddim_set(stm, "urls", registry.urls_memory); rrddim_set(stm, "persons_urls", registry.persons_urls_memory); rrddim_set(stm, "machines_urls", registry.machines_urls_memory); diff --git a/registry/registry.h b/registry/registry.h index 5e274487d..6aa3f0a11 100644 --- a/registry/registry.h +++ b/registry/registry.h @@ -55,29 +55,29 @@ // initialize the registry // should only happen when netdata starts -extern int registry_init(void); +int registry_init(void); // free all data held by the registry // should only happen when netdata exits -extern void registry_free(void); +void registry_free(void); // HTTP requests handled by the registry -extern int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when); -extern int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); -extern int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when); -extern int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when); -extern int registry_request_hello_json(RRDHOST *host, struct web_client *w); +int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when); +int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); +int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when); +int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when); +int registry_request_hello_json(RRDHOST *host, struct web_client *w); // update the registry config -extern void registry_update_cloud_base_url(); +void registry_update_cloud_base_url(); // update the registry monitoring charts -extern void registry_statistics(void); +void registry_statistics(void); -extern char *registry_get_this_machine_guid(void); -extern char *registry_get_mgmt_api_key(void); -extern char *registry_get_this_machine_hostname(void); +char *registry_get_this_machine_guid(void); +char *registry_get_mgmt_api_key(void); +char *registry_get_this_machine_hostname(void); -extern int regenerate_guid(const char *guid, char *result); +int regenerate_guid(const char *guid, char *result); #endif /* NETDATA_REGISTRY_H */ diff --git a/registry/registry_db.c b/registry/registry_db.c index db53ff7e0..ae74aa530 100644 --- a/registry/registry_db.c +++ b/registry/registry_db.c @@ -11,9 +11,7 @@ int registry_db_should_be_saved(void) { // ---------------------------------------------------------------------------- // INTERNAL FUNCTIONS FOR SAVING REGISTRY OBJECTS -static int registry_machine_save_url(const char *name, void *entry, void *file) { - (void)name; - +static int registry_machine_save_url(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *file) { REGISTRY_MACHINE_URL *mu = entry; FILE *fp = file; @@ -32,8 +30,7 @@ static int registry_machine_save_url(const char *name, void *entry, void *file) return ret; } -static int registry_machine_save(const char *name, void *entry, void *file) { - (void)name; +static int registry_machine_save(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *file) { REGISTRY_MACHINE *m = entry; FILE *fp = file; @@ -79,9 +76,7 @@ static inline int registry_person_save_url(void *entry, void *file) { return ret; } -static inline int registry_person_save(const char *name, void *entry, void *file) { - (void)name; - +static inline int registry_person_save(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *file) { REGISTRY_PERSON *p = entry; FILE *fp = file; diff --git a/registry/registry_init.c b/registry/registry_init.c index bae2ac5c5..ba4250ef3 100644 --- a/registry/registry_init.c +++ b/registry/registry_init.c @@ -76,8 +76,8 @@ int registry_init(void) { netdata_mutex_init(®istry.lock); // create dictionaries - registry.persons = dictionary_create(REGISTRY_DICTIONARY_FLAGS); - registry.machines = dictionary_create(REGISTRY_DICTIONARY_FLAGS); + registry.persons = dictionary_create(REGISTRY_DICTIONARY_OPTIONS); + registry.machines = dictionary_create(REGISTRY_DICTIONARY_OPTIONS); avl_init(®istry.registry_urls_root_index, registry_url_compare); // load the registry database @@ -93,9 +93,7 @@ int registry_init(void) { return 0; } -static int machine_urls_delete_callback(const char *name, void *entry, void *data) { - (void)name; - +static int machine_urls_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data) { REGISTRY_MACHINE *m = (REGISTRY_MACHINE *)data; (void)m; @@ -110,10 +108,7 @@ static int machine_urls_delete_callback(const char *name, void *entry, void *dat return 1; } -static int machine_delete_callback(const char *name, void *entry, void *data) { - (void)name; - (void)data; - +static int machine_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data __maybe_unused) { REGISTRY_MACHINE *m = (REGISTRY_MACHINE *)entry; int ret = dictionary_walkthrough_read(m->machine_urls, machine_urls_delete_callback, m); @@ -122,10 +117,7 @@ static int machine_delete_callback(const char *name, void *entry, void *data) { return ret + 1; } -static int registry_person_del_callback(const char *name, void *entry, void *d) { - (void)name; - (void)d; - +static int registry_person_del_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *d __maybe_unused) { REGISTRY_PERSON *p = (REGISTRY_PERSON *)entry; debug(D_REGISTRY, "Registry: registry_person_del('%s'): deleting person", p->guid); diff --git a/registry/registry_internals.h b/registry/registry_internals.h index 9e0f11477..3456d788d 100644 --- a/registry/registry_internals.h +++ b/registry/registry_internals.h @@ -8,7 +8,7 @@ #define REGISTRY_URL_FLAGS_DEFAULT 0x00 #define REGISTRY_URL_FLAGS_EXPIRED 0x01 -#define REGISTRY_DICTIONARY_FLAGS (DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE | DICTIONARY_FLAG_NAME_LINK_DONT_CLONE | DICTIONARY_FLAG_SINGLE_THREADED) +#define REGISTRY_DICTIONARY_OPTIONS (DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_SINGLE_THREADED) // ---------------------------------------------------------------------------- // COMMON structures @@ -71,20 +71,20 @@ struct registry { extern struct registry registry; // REGISTRY LOW-LEVEL REQUESTS (in registry-internals.c) -extern REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when); -extern REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); -extern REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when); +REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when); +REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); +REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when); // REGISTRY LOG (in registry_log.c) -extern void registry_log(char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name); -extern int registry_log_open(void); -extern void registry_log_close(void); -extern void registry_log_recreate(void); -extern ssize_t registry_log_load(void); +void registry_log(char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name); +int registry_log_open(void); +void registry_log_close(void); +void registry_log_recreate(void); +ssize_t registry_log_load(void); // REGISTRY DB (in registry_db.c) -extern int registry_db_save(void); -extern size_t registry_db_load(void); -extern int registry_db_should_be_saved(void); +int registry_db_save(void); +size_t registry_db_load(void); +int registry_db_should_be_saved(void); #endif //NETDATA_REGISTRY_INTERNALS_H_H diff --git a/registry/registry_machine.c b/registry/registry_machine.c index fb345aea2..414cd16d9 100644 --- a/registry/registry_machine.c +++ b/registry/registry_machine.c @@ -25,9 +25,9 @@ REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTR debug(D_REGISTRY, "registry_machine_url_allocate('%s', '%s'): indexing URL in machine", m->guid, u->url); - registry.machines_urls_memory -= dictionary_stats_allocated_memory(m->machine_urls); + registry.machines_urls_memory -= dictionary_stats_for_registry(m->machine_urls); dictionary_set(m->machine_urls, u->url, mu, sizeof(REGISTRY_MACHINE_URL)); - registry.machines_urls_memory += dictionary_stats_allocated_memory(m->machine_urls); + registry.machines_urls_memory += dictionary_stats_for_registry(m->machine_urls); registry_url_link(u); @@ -42,7 +42,7 @@ REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t whe strncpyz(m->guid, machine_guid, GUID_LEN); debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid); - m->machine_urls = dictionary_create(REGISTRY_DICTIONARY_FLAGS); + m->machine_urls = dictionary_create(REGISTRY_DICTIONARY_OPTIONS); m->first_t = m->last_t = (uint32_t)when; m->usages = 0; @@ -50,9 +50,9 @@ REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t whe registry.machines_memory += sizeof(REGISTRY_MACHINE); registry.machines_count++; - registry.machines_urls_memory -= dictionary_stats_allocated_memory(m->machine_urls); + registry.machines_urls_memory -= dictionary_stats_for_registry(m->machine_urls); dictionary_set(registry.machines, m->guid, m, sizeof(REGISTRY_MACHINE)); - registry.machines_urls_memory += dictionary_stats_allocated_memory(m->machine_urls); + registry.machines_urls_memory += dictionary_stats_for_registry(m->machine_urls); return m; } diff --git a/registry/registry_machine.h b/registry/registry_machine.h index 77ab5aaf5..bc95ecf69 100644 --- a/registry/registry_machine.h +++ b/registry/registry_machine.h @@ -34,10 +34,10 @@ struct registry_machine { }; typedef struct registry_machine REGISTRY_MACHINE; -extern REGISTRY_MACHINE *registry_machine_find(const char *machine_guid); -extern REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when); -extern REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t when); -extern REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when); -extern REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when); +REGISTRY_MACHINE *registry_machine_find(const char *machine_guid); +REGISTRY_MACHINE_URL *registry_machine_url_allocate(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when); +REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t when); +REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when); +REGISTRY_MACHINE_URL *registry_machine_link_to_url(REGISTRY_MACHINE *m, REGISTRY_URL *u, time_t when); #endif //NETDATA_REGISTRY_MACHINE_H diff --git a/registry/registry_person.h b/registry/registry_person.h index 556c426f7..674596be6 100644 --- a/registry/registry_person.h +++ b/registry/registry_person.h @@ -42,20 +42,20 @@ struct registry_person { typedef struct registry_person REGISTRY_PERSON; // PERSON_URL -extern REGISTRY_PERSON_URL *registry_person_url_index_find(REGISTRY_PERSON *p, const char *url); -extern REGISTRY_PERSON_URL *registry_person_url_index_add(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) NEVERNULL WARNUNUSED; -extern REGISTRY_PERSON_URL *registry_person_url_index_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) WARNUNUSED; +REGISTRY_PERSON_URL *registry_person_url_index_find(REGISTRY_PERSON *p, const char *url); +REGISTRY_PERSON_URL *registry_person_url_index_add(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) NEVERNULL WARNUNUSED; +REGISTRY_PERSON_URL *registry_person_url_index_del(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu) WARNUNUSED; -extern REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when); -extern REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when, REGISTRY_PERSON_URL *pu); +REGISTRY_PERSON_URL *registry_person_url_allocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when); +REGISTRY_PERSON_URL *registry_person_url_reallocate(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when, REGISTRY_PERSON_URL *pu); // PERSON -extern REGISTRY_PERSON *registry_person_find(const char *person_guid); -extern REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when); -extern REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when); +REGISTRY_PERSON *registry_person_find(const char *person_guid); +REGISTRY_PERSON *registry_person_allocate(const char *person_guid, time_t when); +REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when); // LINKING PERSON -> PERSON_URL -extern REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when); -extern void registry_person_unlink_from_url(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu); +REGISTRY_PERSON_URL *registry_person_link_to_url(REGISTRY_PERSON *p, REGISTRY_MACHINE *m, REGISTRY_URL *u, char *name, size_t namelen, time_t when); +void registry_person_unlink_from_url(REGISTRY_PERSON *p, REGISTRY_PERSON_URL *pu); #endif //NETDATA_REGISTRY_PERSON_H diff --git a/registry/registry_url.h b/registry/registry_url.h index 0cc364fdf..8ba0391bc 100644 --- a/registry/registry_url.h +++ b/registry/registry_url.h @@ -23,13 +23,13 @@ struct registry_url { typedef struct registry_url REGISTRY_URL; // REGISTRY_URL INDEX -extern int registry_url_compare(void *a, void *b); -extern REGISTRY_URL *registry_url_index_del(REGISTRY_URL *u) WARNUNUSED; -extern REGISTRY_URL *registry_url_index_add(REGISTRY_URL *u) NEVERNULL WARNUNUSED; +int registry_url_compare(void *a, void *b); +REGISTRY_URL *registry_url_index_del(REGISTRY_URL *u) WARNUNUSED; +REGISTRY_URL *registry_url_index_add(REGISTRY_URL *u) NEVERNULL WARNUNUSED; // REGISTRY_URL MANAGEMENT -extern REGISTRY_URL *registry_url_get(const char *url, size_t urllen) NEVERNULL; -extern void registry_url_link(REGISTRY_URL *u); -extern void registry_url_unlink(REGISTRY_URL *u); +REGISTRY_URL *registry_url_get(const char *url, size_t urllen) NEVERNULL; +void registry_url_link(REGISTRY_URL *u); +void registry_url_unlink(REGISTRY_URL *u); #endif //NETDATA_REGISTRY_URL_H diff --git a/spawn/spawn.c b/spawn/spawn.c index 051955e88..f326f8881 100644 --- a/spawn/spawn.c +++ b/spawn/spawn.c @@ -8,7 +8,7 @@ int spawn_thread_shutdown; struct spawn_queue spawn_cmd_queue; -static struct spawn_cmd_info *create_spawn_cmd(char *command_to_run) +static struct spawn_cmd_info *create_spawn_cmd(const char *command_to_run) { struct spawn_cmd_info *cmdinfo; @@ -57,7 +57,7 @@ static void init_spawn_cmd_queue(void) /* * Returns serial number of the enqueued command */ -uint64_t spawn_enq_cmd(char *command_to_run) +uint64_t spawn_enq_cmd(const char *command_to_run) { unsigned queue_size; uint64_t serial; diff --git a/spawn/spawn.h b/spawn/spawn.h index a9f1a0744..6e9e51ef0 100644 --- a/spawn/spawn.h +++ b/spawn/spawn.h @@ -84,7 +84,7 @@ void spawn_init(void); void spawn_server(void); void spawn_client(void *arg); void destroy_spawn_cmd(struct spawn_cmd_info *cmdinfo); -uint64_t spawn_enq_cmd(char *command_to_run); +uint64_t spawn_enq_cmd(const char *command_to_run); void spawn_wait_cmd(uint64_t serial, int *exit_status, time_t *exec_run_timestamp); void spawn_deq_cmd(struct spawn_cmd_info *cmdinfo); struct spawn_cmd_info *spawn_get_unprocessed_cmd(void); diff --git a/streaming/README.md b/streaming/README.md index 57c392f40..58eb2cc1b 100644 --- a/streaming/README.md +++ b/streaming/README.md @@ -234,8 +234,7 @@ For Netdata v1.9+, streaming can also be monitored via `access.log`. Netdata does not activate TLS encryption by default. To encrypt streaming connections: 1. On the parent node (receiving node), [enable TLS support](/web/server/README.md#enabling-tls-support). -2. On the child node (sending node), [enable TLS support](/web/server/README.md#enabling-tls-support). -3. On the child's `stream.conf`, configure the destination as follows: +2. On the child's `stream.conf`, configure the destination as follows: ``` [stream] diff --git a/streaming/compression.c b/streaming/compression.c index d6178d6c3..7ba9dbf19 100644 --- a/streaming/compression.c +++ b/streaming/compression.c @@ -5,9 +5,7 @@ #define STREAM_COMPRESSION_MSG "STREAM_COMPRESSION" -#define LZ4_MAX_MSG_SIZE 0x4000 -#define LZ4_STREAM_BUFFER_SIZE (0x10000 + LZ4_MAX_MSG_SIZE) - +// signature MUST end with a newline #define SIGNATURE ((uint32_t)('z' | 0x80) | (0x80 << 8) | (0x80 << 16) | ('\n' << 24)) #define SIGNATURE_MASK ((uint32_t)0xff | (0x80 << 8) | (0x80 << 16) | (0xff << 24)) #define SIGNATURE_SIZE 4 @@ -18,8 +16,9 @@ */ struct compressor_data { LZ4_stream_t *stream; - char *stream_buffer; - size_t stream_buffer_pos; + char *input_ring_buffer; + size_t input_ring_buffer_size; + size_t input_ring_buffer_pos; }; @@ -31,9 +30,9 @@ static void lz4_compressor_reset(struct compressor_state *state) if (state->data) { if (state->data->stream) { LZ4_resetStream_fast(state->data->stream); - info("%s: Compressor Reset", STREAM_COMPRESSION_MSG); + internal_error(true, "%s: compressor reset", STREAM_COMPRESSION_MSG); } - state->data->stream_buffer_pos = 0; + state->data->input_ring_buffer_pos = 0; } } @@ -47,10 +46,10 @@ static void lz4_compressor_destroy(struct compressor_state **state) if (s->data) { if (s->data->stream) LZ4_freeStream(s->data->stream); - freez(s->data->stream_buffer); + freez(s->data->input_ring_buffer); freez(s->data); } - freez(s->buffer); + freez(s->compression_result_buffer); freez(s); *state = NULL; debug(D_STREAM, "%s: Compressor Destroyed.", STREAM_COMPRESSION_MSG); @@ -65,37 +64,53 @@ static void lz4_compressor_destroy(struct compressor_state **state) */ static size_t lz4_compressor_compress(struct compressor_state *state, const char *data, size_t size, char **out) { - if (!state || !size || !out) + if(unlikely(!state || !size || !out)) return 0; - if (size > LZ4_MAX_MSG_SIZE) { - error("%s: Compression Failed - Message size %lu above compression buffer limit: %d", STREAM_COMPRESSION_MSG, size, LZ4_MAX_MSG_SIZE); + + if(unlikely(size > COMPRESSION_MAX_MSG_SIZE)) { + error("%s: Compression Failed - Message size %lu above compression buffer limit: %d", STREAM_COMPRESSION_MSG, (long unsigned int)size, COMPRESSION_MAX_MSG_SIZE); return 0; } + size_t max_dst_size = LZ4_COMPRESSBOUND(size); size_t data_size = max_dst_size + SIGNATURE_SIZE; - if (!state->buffer) { - state->buffer = mallocz(data_size); - state->buffer_size = data_size; - } else if (state->buffer_size < data_size) { - state->buffer = reallocz(state->buffer, data_size); - state->buffer_size = data_size; + if (!state->compression_result_buffer) { + state->compression_result_buffer = mallocz(data_size); + state->compression_result_buffer_size = data_size; + } + else if(unlikely(state->compression_result_buffer_size < data_size)) { + state->compression_result_buffer = reallocz(state->compression_result_buffer, data_size); + state->compression_result_buffer_size = data_size; } - memcpy(state->data->stream_buffer + state->data->stream_buffer_pos, data, size); - long int compressed_data_size = LZ4_compress_fast_continue(state->data->stream, - state->data->stream_buffer + state->data->stream_buffer_pos, - state->buffer + SIGNATURE_SIZE, size, max_dst_size, 1); + // the ring buffer always has space for LZ4_MAX_MSG_SIZE + memcpy(state->data->input_ring_buffer + state->data->input_ring_buffer_pos, data, size); + + // this call needs the last 64K of our previous data + // they are available in the ring buffer + long int compressed_data_size = LZ4_compress_fast_continue( + state->data->stream, + state->data->input_ring_buffer + state->data->input_ring_buffer_pos, + state->compression_result_buffer + SIGNATURE_SIZE, + size, + max_dst_size, + 1); + if (compressed_data_size < 0) { error("Data compression error: %ld", compressed_data_size); return 0; } - state->data->stream_buffer_pos += size; - if (state->data->stream_buffer_pos >= LZ4_STREAM_BUFFER_SIZE - LZ4_MAX_MSG_SIZE) - state->data->stream_buffer_pos = 0; + + // update the next writing position of the ring buffer + state->data->input_ring_buffer_pos += size; + if(unlikely(state->data->input_ring_buffer_pos >= state->data->input_ring_buffer_size - COMPRESSION_MAX_MSG_SIZE)) + state->data->input_ring_buffer_pos = 0; + + // update the signature header uint32_t len = ((compressed_data_size & 0x7f) | 0x80 | (((compressed_data_size & (0x7f << 7)) << 1) | 0x8000)) << 8; - *(uint32_t *)state->buffer = len | SIGNATURE; - *out = state->buffer; + *(uint32_t *)state->compression_result_buffer = len | SIGNATURE; + *out = state->compression_result_buffer; debug(D_STREAM, "%s: Compressed data header: %ld", STREAM_COMPRESSION_MSG, compressed_data_size); return compressed_data_size + SIGNATURE_SIZE; } @@ -114,8 +129,9 @@ struct compressor_state *create_compressor() state->data = callocz(1, sizeof(struct compressor_data)); state->data->stream = LZ4_createStream(); - state->data->stream_buffer = callocz(1, LZ4_DECODER_RING_BUFFER_SIZE(LZ4_MAX_MSG_SIZE)); - state->buffer_size = LZ4_STREAM_BUFFER_SIZE; + state->data->input_ring_buffer_size = LZ4_DECODER_RING_BUFFER_SIZE(COMPRESSION_MAX_MSG_SIZE * 2); + state->data->input_ring_buffer = callocz(1, state->data->input_ring_buffer_size); + state->compression_result_buffer_size = 0; state->reset(state); debug(D_STREAM, "%s: Initialize streaming compression!", STREAM_COMPRESSION_MSG); return state; @@ -124,11 +140,12 @@ struct compressor_state *create_compressor() /* * LZ4 streaming API decompressor specific data */ -struct decompressor_data { - LZ4_streamDecode_t *stream; - char *stream_buffer; - size_t stream_buffer_size; - size_t stream_buffer_pos; +struct decompressor_stream { + LZ4_streamDecode_t *lz4_stream; + char *buffer; + size_t size; + size_t write_at; + size_t read_at; }; /* @@ -136,12 +153,12 @@ struct decompressor_data { */ static void lz4_decompressor_reset(struct decompressor_state *state) { - if (state->data) { - if (state->data->stream) - LZ4_setStreamDecode(state->data->stream, NULL, 0); - state->data->stream_buffer_pos = 0; - state->buffer_len = 0; - state->out_buffer_len = 0; + if (state->stream) { + if (state->stream->lz4_stream) + LZ4_setStreamDecode(state->stream->lz4_stream, NULL, 0); + + state->stream->write_at = 0; + state->stream->read_at = 0; } } @@ -152,173 +169,129 @@ static void lz4_decompressor_destroy(struct decompressor_state **state) { if (state && *state) { struct decompressor_state *s = *state; - if (s->data) { + if (s->stream) { debug(D_STREAM, "%s: Destroying decompressor.", STREAM_COMPRESSION_MSG); - if (s->data->stream) - LZ4_freeStreamDecode(s->data->stream); - freez(s->data->stream_buffer); - freez(s->data); + if (s->stream->lz4_stream) + LZ4_freeStreamDecode(s->stream->lz4_stream); + freez(s->stream->buffer); + freez(s->stream); } - freez(s->buffer); freez(s); *state = NULL; } } -static size_t decode_compress_header(const char *data, size_t data_size) -{ - if (!data || !data_size) +static size_t decode_compress_header(const char *data, size_t data_size) { + if (unlikely(!data || !data_size)) return 0; - if (data_size < SIGNATURE_SIZE) + + if (unlikely(data_size != SIGNATURE_SIZE)) return 0; + uint32_t sign = *(uint32_t *)data; - if ((sign & SIGNATURE_MASK) != SIGNATURE) + if (unlikely((sign & SIGNATURE_MASK) != SIGNATURE)) return 0; + size_t length = ((sign >> 8) & 0x7f) | ((sign >> 9) & (0x7f << 7)); return length; } -/* - * Check input data for the compression header - * Return the size of compressed data or 0 for uncompressed data - */ -size_t is_compressed_data(const char *data, size_t data_size) -{ - return decode_compress_header(data, data_size); -} - /* * Start the collection of compressed data in an internal buffer * Return the size of compressed data or 0 for uncompressed data */ -static size_t lz4_decompressor_start(struct decompressor_state *state, const char *header, size_t header_size) -{ - size_t length = decode_compress_header(header, header_size); - if (!length) - return 0; +static size_t lz4_decompressor_start(struct decompressor_state *state __maybe_unused, const char *header, size_t header_size) { + if(unlikely(state->stream->read_at != state->stream->write_at)) + fatal("%s: asked to decompress new data, while there are unread data in the decompression buffer!" + , STREAM_COMPRESSION_MSG); - if (!state->buffer) { - state->buffer = mallocz(length); - state->buffer_size = length; - } else if (state->buffer_size < length) { - state->buffer = reallocz(state->buffer, length); - state->buffer_size = length; - } - state->buffer_len = length; - state->buffer_pos = 0; - state->out_buffer_pos = 0; - state->out_buffer_len = 0; - return length; + return decode_compress_header(header, header_size); } /* - * Add a chunk of compressed data to the internal buffer - * Return the current size of compressed data or 0 for error + * Decompress the compressed data in the internal buffer + * Return the size of uncompressed data or 0 for error */ -static size_t lz4_decompressor_put(struct decompressor_state *state, const char *data, size_t size) -{ - if (!state || !size || !data) +static size_t lz4_decompressor_decompress(struct decompressor_state *state, const char *compressed_data, size_t compressed_size) { + if (unlikely(!state || !compressed_data || !compressed_size)) return 0; - if (!state->buffer) - fatal("STREAM: No decompressor buffer allocated"); - if (state->buffer_pos + size > state->buffer_len) { - error("STREAM: Decompressor buffer overflow %lu + %lu > %lu", - state->buffer_pos, size, state->buffer_len); - size = state->buffer_len - state->buffer_pos; + if(unlikely(state->stream->read_at != state->stream->write_at)) + fatal("%s: asked to decompress new data, while there are unread data in the decompression buffer!" + , STREAM_COMPRESSION_MSG); + + if (unlikely(state->stream->write_at >= state->stream->size / 2)) { + state->stream->write_at = 0; + state->stream->read_at = 0; } - memcpy(state->buffer + state->buffer_pos, data, size); - state->buffer_pos += size; - return state->buffer_pos; -} -static size_t saving_percent(size_t comp_len, size_t src_len) -{ - if (comp_len > src_len) - comp_len = src_len; - if (!src_len) - return 0; - return 100 - comp_len * 100 / src_len; -} + long int decompressed_size = LZ4_decompress_safe_continue( + state->stream->lz4_stream + , compressed_data + , state->stream->buffer + state->stream->write_at + , (int)compressed_size + , (int)(state->stream->size - state->stream->write_at) + ); -/* - * Decompress the compressed data in the internal buffer - * Return the size of uncompressed data or 0 for error - */ -static size_t lz4_decompressor_decompress(struct decompressor_state *state) -{ - if (!state) - return 0; - if (!state->buffer) { - error("%s: No decompressor buffer allocated", STREAM_COMPRESSION_MSG); - return 0; - } - - long int decompressed_size = LZ4_decompress_safe_continue(state->data->stream, state->buffer, - state->data->stream_buffer + state->data->stream_buffer_pos, - state->buffer_len, state->data->stream_buffer_size - state->data->stream_buffer_pos); - if (decompressed_size < 0) { - error("%s: Decompressor error %ld", STREAM_COMPRESSION_MSG, decompressed_size); + if (unlikely(decompressed_size < 0)) { + error("%s: decompressor returned negative decompressed bytes: %ld", STREAM_COMPRESSION_MSG, decompressed_size); return 0; } - state->out_buffer = state->data->stream_buffer + state->data->stream_buffer_pos; - state->data->stream_buffer_pos += decompressed_size; - if (state->data->stream_buffer_pos >= state->data->stream_buffer_size - LZ4_MAX_MSG_SIZE) - state->data->stream_buffer_pos = 0; - state->out_buffer_len = decompressed_size; - state->out_buffer_pos = 0; + if(unlikely(decompressed_size + state->stream->write_at > state->stream->size)) + fatal("%s: decompressor overflown the stream_buffer. size: %zu, pos: %zu, added: %ld, exceeding the buffer by %zu" + , STREAM_COMPRESSION_MSG + , state->stream->size + , state->stream->write_at + , decompressed_size + , state->stream->write_at + decompressed_size - state->stream->size + ); - // Some compression statistics - size_t old_avg_saving = saving_percent(state->total_compressed, state->total_uncompressed); - size_t old_avg_size = state->packet_count ? state->total_uncompressed / state->packet_count : 0; + state->stream->write_at += decompressed_size; - state->total_compressed += state->buffer_len + SIGNATURE_SIZE; + // statistics + state->total_compressed += compressed_size + SIGNATURE_SIZE; state->total_uncompressed += decompressed_size; state->packet_count++; - size_t saving = saving_percent(state->buffer_len, decompressed_size); - size_t avg_saving = saving_percent(state->total_compressed, state->total_uncompressed); - size_t avg_size = state->total_uncompressed / state->packet_count; - - if (old_avg_saving != avg_saving || old_avg_size != avg_size){ - debug(D_STREAM, "%s: Saving: %lu%% (avg. %lu%%), avg.size: %lu", STREAM_COMPRESSION_MSG, saving, avg_saving, avg_size); - } return decompressed_size; } /* * Return the size of uncompressed data left in the internal buffer or 0 for error */ -static size_t lz4_decompressor_decompressed_bytes_in_buffer(struct decompressor_state *state) -{ - return state->out_buffer_len ? - state->out_buffer_len - state->out_buffer_pos : 0; +static size_t lz4_decompressor_decompressed_bytes_in_buffer(struct decompressor_state *state) { + if(unlikely(state->stream->read_at > state->stream->write_at)) + fatal("%s: invalid read/write stream positions" + , STREAM_COMPRESSION_MSG); + + return state->stream->write_at - state->stream->read_at; } /* * Fill the buffer provided with uncompressed data from the internal buffer * Return the size of uncompressed data copied or 0 for error */ -static size_t lz4_decompressor_get(struct decompressor_state *state, char *data, size_t size) -{ - if (!state || !size || !data) +static size_t lz4_decompressor_get(struct decompressor_state *state, char *dst, size_t size) { + if (unlikely(!state || !size || !dst)) return 0; - if (!state->out_buffer) - fatal("%s: No decompressor output buffer allocated", STREAM_COMPRESSION_MSG); - if (state->out_buffer_pos + size > state->out_buffer_len) - size = state->out_buffer_len - state->out_buffer_pos; - - char *p = state->out_buffer + state->out_buffer_pos, *endp = p + size, *last_lf = NULL; - for (; p < endp; ++p) - if (*p == '\n' || *p == 0) - last_lf = p; - if (last_lf) - size = last_lf + 1 - (state->out_buffer + state->out_buffer_pos); - - memcpy(data, state->out_buffer + state->out_buffer_pos, size); - state->out_buffer_pos += size; - return size; + + size_t remaining = lz4_decompressor_decompressed_bytes_in_buffer(state); + if(unlikely(!remaining)) + return 0; + + size_t bytes_to_return = size; + if(bytes_to_return > remaining) + bytes_to_return = remaining; + + memcpy(dst, state->stream->buffer + state->stream->read_at, bytes_to_return); + state->stream->read_at += bytes_to_return; + + if(unlikely(state->stream->read_at > state->stream->write_at)) + fatal("%s: invalid read/write stream positions" + , STREAM_COMPRESSION_MSG); + + return bytes_to_return; } /* @@ -328,20 +301,20 @@ static size_t lz4_decompressor_get(struct decompressor_state *state, char *data, struct decompressor_state *create_decompressor() { struct decompressor_state *state = callocz(1, sizeof(struct decompressor_state)); + state->signature_size = SIGNATURE_SIZE; state->reset = lz4_decompressor_reset; state->start = lz4_decompressor_start; - state->put = lz4_decompressor_put; state->decompress = lz4_decompressor_decompress; state->get = lz4_decompressor_get; state->decompressed_bytes_in_buffer = lz4_decompressor_decompressed_bytes_in_buffer; state->destroy = lz4_decompressor_destroy; - state->data = callocz(1, sizeof(struct decompressor_data)); - fatal_assert(state->data); - state->data->stream = LZ4_createStreamDecode(); - state->data->stream_buffer_size = LZ4_decoderRingBufferSize(LZ4_MAX_MSG_SIZE); - state->data->stream_buffer = mallocz(state->data->stream_buffer_size); - fatal_assert(state->data->stream_buffer); + state->stream = callocz(1, sizeof(struct decompressor_stream)); + fatal_assert(state->stream); + state->stream->lz4_stream = LZ4_createStreamDecode(); + state->stream->size = LZ4_decoderRingBufferSize(COMPRESSION_MAX_MSG_SIZE) * 2; + state->stream->buffer = mallocz(state->stream->size); + fatal_assert(state->stream->buffer); state->reset(state); debug(D_STREAM, "%s: Initialize streaming decompression!", STREAM_COMPRESSION_MSG); return state; diff --git a/streaming/receiver.c b/streaming/receiver.c index 0890ebbcd..61ee33bc4 100644 --- a/streaming/receiver.c +++ b/streaming/receiver.c @@ -1,6 +1,18 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "rrdpush.h" +#include "parser/parser.h" + +// IMPORTANT: to add workers, you have to edit WORKER_PARSER_FIRST_JOB accordingly +#define WORKER_RECEIVER_JOB_BYTES_READ (WORKER_PARSER_FIRST_JOB - 1) +#define WORKER_RECEIVER_JOB_BYTES_UNCOMPRESSED (WORKER_PARSER_FIRST_JOB - 2) + +// this has to be the same at parser.h +#define WORKER_RECEIVER_JOB_REPLICATION_COMPLETION (WORKER_PARSER_FIRST_JOB - 3) + +#if WORKER_PARSER_FIRST_JOB < 1 +#error The define WORKER_PARSER_FIRST_JOB needs to be at least 1 +#endif extern struct config stream_config; @@ -58,105 +70,43 @@ static void rrdpush_receiver_thread_cleanup(void *ptr) { #include "collectors/plugins.d/pluginsd_parser.h" -PARSER_RC streaming_timestamp(char **words, void *user, PLUGINSD_ACTION *plugins_action) +PARSER_RC streaming_claimed_id(char **words, size_t num_words, void *user) { - UNUSED(plugins_action); - char *remote_time_txt = words[1]; - time_t remote_time = 0; - RRDHOST *host = ((PARSER_USER_OBJECT *)user)->host; - struct plugind *cd = ((PARSER_USER_OBJECT *)user)->cd; - if (cd->version < VERSION_GAP_FILLING ) { - error("STREAM %s from %s: Child negotiated version %u but sent TIMESTAMP!", host->hostname, cd->cmd, - cd->version); - return PARSER_RC_OK; // Ignore error and continue stream - } - if (remote_time_txt && *remote_time_txt) { - remote_time = str2ull(remote_time_txt); - time_t now = now_realtime_sec(), prev = rrdhost_last_entry_t(host); - time_t gap = 0; - if (prev == 0) - info( - "STREAM %s from %s: Initial connection (no gap to check), " - "remote=%"PRId64" local=%"PRId64" slew=%"PRId64"", - host->hostname, - cd->cmd, - (int64_t)remote_time, - (int64_t)now, - (int64_t)now - remote_time); - else { - gap = now - prev; - info( - "STREAM %s from %s: Checking for gaps... " - "remote=%"PRId64" local=%"PRId64"..%"PRId64" slew=%"PRId64" %"PRId64"-sec gap", - host->hostname, - cd->cmd, - (int64_t)remote_time, - (int64_t)prev, - (int64_t)now, - (int64_t)(remote_time - now), - (int64_t)gap); - } - char message[128]; - sprintf( - message, - "REPLICATE %"PRId64" %"PRId64"\n", - (int64_t)(remote_time - gap), - (int64_t)remote_time); - int ret; -#ifdef ENABLE_HTTPS - SSL *conn = host->stream_ssl.conn ; - if(conn && !host->stream_ssl.flags) { - ret = SSL_write(conn, message, strlen(message)); - } else { - ret = send(host->receiver->fd, message, strlen(message), MSG_DONTWAIT); - } -#else - ret = send(host->receiver->fd, message, strlen(message), MSG_DONTWAIT); -#endif - if (ret != (int)strlen(message)) - error("Failed to send initial timestamp - gaps may appear in charts"); - return PARSER_RC_OK; - } - return PARSER_RC_ERROR; -} + const char *host_uuid_str = get_word(words, num_words, 1); + const char *claim_id_str = get_word(words, num_words, 2); -#define CLAIMED_ID_MIN_WORDS 3 -PARSER_RC streaming_claimed_id(char **words, void *user, PLUGINSD_ACTION *plugins_action) -{ - UNUSED(plugins_action); + if (!host_uuid_str || !claim_id_str) { + error("Command CLAIMED_ID came malformed, uuid = '%s', claim_id = '%s'", + host_uuid_str ? host_uuid_str : "[unset]", + claim_id_str ? claim_id_str : "[unset]"); + return PARSER_RC_ERROR; + } - int i; uuid_t uuid; RRDHOST *host = ((PARSER_USER_OBJECT *)user)->host; - for (i = 0; words[i]; i++) ; - if (i != CLAIMED_ID_MIN_WORDS) { - error("Command CLAIMED_ID came malformed %d parameters are expected but %d received", CLAIMED_ID_MIN_WORDS - 1, i - 1); - return PARSER_RC_ERROR; - } - // We don't need the parsed UUID // just do it to check the format - if(uuid_parse(words[1], uuid)) { - error("1st parameter (host GUID) to CLAIMED_ID command is not valid GUID. Received: \"%s\".", words[1]); + if(uuid_parse(host_uuid_str, uuid)) { + error("1st parameter (host GUID) to CLAIMED_ID command is not valid GUID. Received: \"%s\".", host_uuid_str); return PARSER_RC_ERROR; } - if(uuid_parse(words[2], uuid) && strcmp(words[2], "NULL")) { - error("2nd parameter (Claim ID) to CLAIMED_ID command is not valid GUID. Received: \"%s\".", words[2]); + if(uuid_parse(claim_id_str, uuid) && strcmp(claim_id_str, "NULL")) { + error("2nd parameter (Claim ID) to CLAIMED_ID command is not valid GUID. Received: \"%s\".", claim_id_str); return PARSER_RC_ERROR; } - if(strcmp(words[1], host->machine_guid)) { - error("Claim ID is for host \"%s\" but it came over connection for \"%s\"", words[1], host->machine_guid); + if(strcmp(host_uuid_str, host->machine_guid)) { + error("Claim ID is for host \"%s\" but it came over connection for \"%s\"", host_uuid_str, host->machine_guid); return PARSER_RC_OK; //the message is OK problem must be somewhere else } rrdhost_aclk_state_lock(host); if (host->aclk_state.claimed_id) freez(host->aclk_state.claimed_id); - host->aclk_state.claimed_id = strcmp(words[2], "NULL") ? strdupz(words[2]) : NULL; + host->aclk_state.claimed_id = strcmp(claim_id_str, "NULL") ? strdupz(claim_id_str) : NULL; - store_claim_id(&host->host_uuid, host->aclk_state.claimed_id ? &uuid : NULL); + metaqueue_store_claim_id(&host->host_uuid, host->aclk_state.claimed_id ? &uuid : NULL); rrdhost_aclk_state_unlock(host); @@ -165,197 +115,242 @@ PARSER_RC streaming_claimed_id(char **words, void *user, PLUGINSD_ACTION *plugin return PARSER_RC_OK; } +static int read_stream(struct receiver_state *r, char* buffer, size_t size) { + if(unlikely(!size)) { + internal_error(true, "%s() asked to read zero bytes", __FUNCTION__); + return 0; + } -#ifndef ENABLE_COMPRESSION -/* The receiver socket is blocking, perform a single read into a buffer so that we can reassemble lines for parsing. - */ -static int receiver_read(struct receiver_state *r, FILE *fp) { #ifdef ENABLE_HTTPS - if (r->ssl.conn && !r->ssl.flags) { - ERR_clear_error(); - int desired = sizeof(r->read_buffer) - r->read_len - 1; - int ret = SSL_read(r->ssl.conn, r->read_buffer + r->read_len, desired); - if (ret > 0 ) { - r->read_len += ret; - return 0; - } - // Don't treat SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE differently on blocking socket - u_long err; - char buf[256]; - while ((err = ERR_get_error()) != 0) { - ERR_error_string_n(err, buf, sizeof(buf)); - error("STREAM %s [receive from %s] ssl error: %s", r->hostname, r->client_ip, buf); - } - return 1; + if (r->ssl.conn && r->ssl.flags == NETDATA_SSL_HANDSHAKE_COMPLETE) + return (int)netdata_ssl_read(r->ssl.conn, buffer, size); +#endif + + ssize_t bytes_read = read(r->fd, buffer, size); + if(bytes_read == 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS)) { + error("STREAM: %s(): timeout while waiting for data on socket!", __FUNCTION__); + bytes_read = -3; } + else if (bytes_read == 0) { + error("STREAM: %s(): EOF while reading data from socket!", __FUNCTION__); + bytes_read = -1; + } + else if (bytes_read < 0) { + error("STREAM: %s() failed to read from socket!", __FUNCTION__); + bytes_read = -2; + } + +// do { +// bytes_read = (int) fread(buffer, 1, size, fp); +// if (unlikely(bytes_read <= 0)) { +// if(feof(fp)) { +// internal_error(true, "%s(): fread() failed with EOF", __FUNCTION__); +// bytes_read = -2; +// } +// else if(ferror(fp)) { +// internal_error(true, "%s(): fread() failed with ERROR", __FUNCTION__); +// bytes_read = -3; +// } +// else bytes_read = 0; +// } +// else +// worker_set_metric(WORKER_RECEIVER_JOB_BYTES_READ, bytes_read); +// } while(bytes_read == 0); + + return (int)bytes_read; +} + +static bool receiver_read_uncompressed(struct receiver_state *r) { +#ifdef NETDATA_INTERNAL_CHECKS + if(r->read_buffer[r->read_len] != '\0') + fatal("%s(): read_buffer does not start with zero", __FUNCTION__ ); #endif - if (!fgets(r->read_buffer, sizeof(r->read_buffer), fp)) - return 1; - r->read_len = strlen(r->read_buffer); - return 0; + + int bytes_read = read_stream(r, r->read_buffer + r->read_len, sizeof(r->read_buffer) - r->read_len - 1); + if(unlikely(bytes_read <= 0)) + return false; + + worker_set_metric(WORKER_RECEIVER_JOB_BYTES_READ, (NETDATA_DOUBLE)bytes_read); + worker_set_metric(WORKER_RECEIVER_JOB_BYTES_UNCOMPRESSED, (NETDATA_DOUBLE)bytes_read); + + r->read_len += bytes_read; + r->read_buffer[r->read_len] = '\0'; + + return true; } -#else -/* - * The receiver socket is blocking, perform a single read into a buffer so that we can reassemble lines for parsing. - * if SSL encryption is on, then use SSL API for reading stream data. - * Use line oriented fgets() in buffer from receiver_state is provided. - * In other cases use fread to read binary data from socket. - * Return zero on success and the number of bytes were read using pointer in the last argument. - */ -static int read_stream(struct receiver_state *r, FILE *fp, char* buffer, size_t size, int* ret) { - if (!ret) - return 1; - *ret = 0; -#ifdef ENABLE_HTTPS - if (r->ssl.conn && !r->ssl.flags) { - ERR_clear_error(); - if (buffer != r->read_buffer + r->read_len) { - *ret = SSL_read(r->ssl.conn, buffer, size); - if (*ret > 0 ) - return 0; - } else { - // we need to receive data with LF to parse compression header - size_t ofs = 0; - int res = 0; - errno = 0; - while (ofs < size) { - do { - res = SSL_read(r->ssl.conn, buffer + ofs, 1); - // When either SSL_ERROR_SYSCALL (OpenSSL < 3.0) or SSL_ERROR_SSL(OpenSSL > 3.0) happens, - // the connection was lost https://www.openssl.org/docs/man3.0/man3/SSL_get_error.html, - // without the test we will have an infinite loop https://github.com/netdata/netdata/issues/13092 - int local_ssl_err = SSL_get_error(r->ssl.conn, res); - if (local_ssl_err == SSL_ERROR_SYSCALL || local_ssl_err == SSL_ERROR_SSL) { - error("The SSL connection has error SSL_ERROR_SYSCALL(%d) and system is registering errno = %d", - local_ssl_err, errno); - return 1; - } - } while (res == 0); - - if (res < 0) - break; - if (buffer[ofs] == '\n') - break; - ofs += res; - } - if (res > 0) { - ofs += res; - *ret = ofs; - buffer[ofs] = 0; - return 0; + +#ifdef ENABLE_COMPRESSION +static bool receiver_read_compressed(struct receiver_state *r) { + +#ifdef NETDATA_INTERNAL_CHECKS + if(r->read_buffer[r->read_len] != '\0') + fatal("%s: read_buffer does not start with zero #2", __FUNCTION__ ); +#endif + + // first use any available uncompressed data + if (r->decompressor->decompressed_bytes_in_buffer(r->decompressor)) { + size_t available = sizeof(r->read_buffer) - r->read_len - 1; + if (available) { + size_t len = r->decompressor->get(r->decompressor, r->read_buffer + r->read_len, available); + if (!len) { + internal_error(true, "decompressor returned zero length #1"); + return false; } + + r->read_len += (int)len; + r->read_buffer[r->read_len] = '\0'; } - // Don't treat SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE differently on blocking socket - u_long err; - char buf[256]; - while ((err = ERR_get_error()) != 0) { - ERR_error_string_n(err, buf, sizeof(buf)); - error("STREAM %s [receive from %s] ssl error: %s", r->hostname, r->client_ip, buf); - } - return 1; + else + internal_error(true, "The line to read is too big! Already have %d bytes in read_buffer.", r->read_len); + + return true; } -#endif - if (buffer != r->read_buffer + r->read_len) { - // read to external buffer - *ret = fread(buffer, 1, size, fp); - if (!*ret) - return 1; - } else { - if (!fgets(r->read_buffer, sizeof(r->read_buffer), fp)) - return 1; - *ret = strlen(r->read_buffer); + + // no decompressed data available + // read the compression signature of the next block + + if(unlikely(r->read_len + r->decompressor->signature_size > sizeof(r->read_buffer) - 1)) { + internal_error(true, "The last incomplete line does not leave enough room for the next compression header! Already have %d bytes in read_buffer.", r->read_len); + return false; } - return 0; -} -/* - * Get the next line of data for parsing. - * Return data from the decompressor buffer if available. - * Otherwise read next line from the socket and check for compression header. - * Return the line was read If no compression header was found. - * Otherwise read the entire block of compressed data, decompress it - * and return it in receiver_state buffer. - * Return zero on success. - */ -static int receiver_read(struct receiver_state *r, FILE *fp) { - // check any decompressed data present - if (r->decompressor && - r->decompressor->decompressed_bytes_in_buffer(r->decompressor)) { - size_t available = sizeof(r->read_buffer) - r->read_len; - if (available) { - size_t len = r->decompressor->get(r->decompressor, - r->read_buffer + r->read_len, available); - if (!len) - return 1; - r->read_len += len; - } - return 0; + // read the compression signature from the stream + // we have to do a loop here, because read_stream() may return less than the data we need + int bytes_read = 0; + do { + int ret = read_stream(r, r->read_buffer + r->read_len + bytes_read, r->decompressor->signature_size - bytes_read); + if (unlikely(ret <= 0)) + return false; + + bytes_read += ret; + } while(unlikely(bytes_read < (int)r->decompressor->signature_size)); + + worker_set_metric(WORKER_RECEIVER_JOB_BYTES_READ, (NETDATA_DOUBLE)bytes_read); + + if(unlikely(bytes_read != (int)r->decompressor->signature_size)) + fatal("read %d bytes, but expected compression signature of size %zu", bytes_read, r->decompressor->signature_size); + + size_t compressed_message_size = r->decompressor->start(r->decompressor, r->read_buffer + r->read_len, bytes_read); + if (unlikely(!compressed_message_size)) { + internal_error(true, "multiplexed uncompressed data in compressed stream!"); + r->read_len += bytes_read; + r->read_buffer[r->read_len] = '\0'; + return true; } - int ret = 0; - if (read_stream(r, fp, r->read_buffer + r->read_len, sizeof(r->read_buffer) - r->read_len - 1, &ret)) - return 1; - - if (!is_compressed_data(r->read_buffer, ret)) { - r->read_len += ret; - return 0; + + if(unlikely(compressed_message_size > COMPRESSION_MAX_MSG_SIZE)) { + error("received a compressed message of %zu bytes, which is bigger than the max compressed message size supported of %zu. Ignoring message.", + compressed_message_size, (size_t)COMPRESSION_MAX_MSG_SIZE); + return false; } - if (unlikely(!r->decompressor)) - r->decompressor = create_decompressor(); - - size_t bytes_to_read = r->decompressor->start(r->decompressor, - r->read_buffer, ret); + // delete compression header from our read buffer + r->read_buffer[r->read_len] = '\0'; - // Read the entire block of compressed data because - // we're unable to decompress incomplete block - char compressed[bytes_to_read]; + // Read the entire compressed block of compressed data + char compressed[compressed_message_size]; + size_t compressed_bytes_read = 0; do { - if (read_stream(r, fp, compressed, bytes_to_read, &ret)) - return 1; - // Send input data to decompressor - if (ret) - r->decompressor->put(r->decompressor, compressed, ret); - bytes_to_read -= ret; - } while (bytes_to_read > 0); - // Decompress - size_t bytes_to_parse = r->decompressor->decompress(r->decompressor); - if (!bytes_to_parse) - return 1; - // Fill read buffer with decompressed data - r->read_len = r->decompressor->get(r->decompressor, - r->read_buffer, sizeof(r->read_buffer)); - return 0; -} + size_t start = compressed_bytes_read; + size_t remaining = compressed_message_size - start; -#endif + int last_read_bytes = read_stream(r, &compressed[start], remaining); + if (unlikely(last_read_bytes <= 0)) { + internal_error(true, "read_stream() failed #2, with code %d", last_read_bytes); + return false; + } + + compressed_bytes_read += last_read_bytes; + + } while(unlikely(compressed_message_size > compressed_bytes_read)); + + worker_set_metric(WORKER_RECEIVER_JOB_BYTES_READ, (NETDATA_DOUBLE)compressed_bytes_read); + + // decompress the compressed block + size_t bytes_to_parse = r->decompressor->decompress(r->decompressor, compressed, compressed_bytes_read); + if (!bytes_to_parse) { + internal_error(true, "no bytes to parse."); + return false; + } + + worker_set_metric(WORKER_RECEIVER_JOB_BYTES_UNCOMPRESSED, (NETDATA_DOUBLE)bytes_to_parse); + + // fill read buffer with decompressed data + size_t len = (int)r->decompressor->get(r->decompressor, r->read_buffer + r->read_len, sizeof(r->read_buffer) - r->read_len - 1); + if (!len) { + internal_error(true, "decompressor returned zero length #2"); + return false; + } + r->read_len += (int)len; + r->read_buffer[r->read_len] = '\0'; + + return true; +} +#else // !ENABLE_COMPRESSION +static bool receiver_read_compressed(struct receiver_state *r) { + return receiver_read_uncompressed(r); +} +#endif // ENABLE_COMPRESSION /* Produce a full line if one exists, statefully return where we start next time. * When we hit the end of the buffer with a partial line move it to the beginning for the next fill. */ -static char *receiver_next_line(struct receiver_state *r, int *pos) { - int start = *pos, scan = *pos; - if (scan >= r->read_len) { +static char *receiver_next_line(struct receiver_state *r, char *buffer, size_t buffer_length, size_t *pos) { + size_t start = *pos; + + char *ss = &r->read_buffer[start]; + char *se = &r->read_buffer[r->read_len]; + char *ds = buffer; + char *de = &buffer[buffer_length - 2]; + + if(ss >= se) { + *ds = '\0'; + *pos = 0; r->read_len = 0; + r->read_buffer[r->read_len] = '\0'; return NULL; } - while (scan < r->read_len && r->read_buffer[scan] != '\n') - scan++; - if (scan < r->read_len && r->read_buffer[scan] == '\n') { - *pos = scan+1; - r->read_buffer[scan] = 0; - return &r->read_buffer[start]; + + // copy all bytes to buffer + while(ss < se && ds < de && *ss != '\n') + *ds++ = *ss++; + + // if we have a newline, return the buffer + if(ss < se && ds < de && *ss == '\n') { + // newline found in the r->read_buffer + + *ds++ = *ss++; // copy the newline too + *ds = '\0'; + + *pos = ss - r->read_buffer; + return buffer; } + + // if the destination is full, oops! + if(ds == de) { + error("STREAM: received line exceeds %d bytes. Truncating it.", PLUGINSD_LINE_MAX); + *ds = '\0'; + *pos = ss - r->read_buffer; + return buffer; + } + + // no newline found in the r->read_buffer + // move everything to the beginning memmove(r->read_buffer, &r->read_buffer[start], r->read_len - start); - r->read_len -= start; + r->read_len -= (int)start; + r->read_buffer[r->read_len] = '\0'; + *ds = '\0'; + *pos = 0; return NULL; } static void streaming_parser_thread_cleanup(void *ptr) { PARSER *parser = (PARSER *)ptr; + rrd_collector_finished(); parser_destroy(parser); } -size_t streaming_parser(struct receiver_state *rpt, struct plugind *cd, FILE *fp) { +static size_t streaming_parser(struct receiver_state *rpt, struct plugind *cd, int fd, void *ssl) { size_t result; PARSER_USER_OBJECT user = { @@ -366,49 +361,68 @@ size_t streaming_parser(struct receiver_state *rpt, struct plugind *cd, FILE *fp .trust_durations = 1 }; - PARSER *parser = parser_init(rpt->host, &user, fp, PARSER_INPUT_SPLIT); + PARSER *parser = parser_init(rpt->host, &user, NULL, NULL, fd, PARSER_INPUT_SPLIT, ssl); + + rrd_collector_started(); // this keeps the parser with its current value // so, parser needs to be allocated before pushing it netdata_thread_cleanup_push(streaming_parser_thread_cleanup, parser); - parser_add_keyword(parser, "TIMESTAMP", streaming_timestamp); parser_add_keyword(parser, "CLAIMED_ID", streaming_claimed_id); - parser->plugins_action->begin_action = &pluginsd_begin_action; - parser->plugins_action->flush_action = &pluginsd_flush_action; - parser->plugins_action->end_action = &pluginsd_end_action; - parser->plugins_action->disable_action = &pluginsd_disable_action; - parser->plugins_action->variable_action = &pluginsd_variable_action; - parser->plugins_action->dimension_action = &pluginsd_dimension_action; - parser->plugins_action->label_action = &pluginsd_label_action; - parser->plugins_action->overwrite_action = &pluginsd_overwrite_action; - parser->plugins_action->chart_action = &pluginsd_chart_action; - parser->plugins_action->set_action = &pluginsd_set_action; - parser->plugins_action->clabel_commit_action = &pluginsd_clabel_commit_action; - parser->plugins_action->clabel_action = &pluginsd_clabel_action; - user.parser = parser; + bool compressed_connection = false; #ifdef ENABLE_COMPRESSION - if (rpt->decompressor) - rpt->decompressor->reset(rpt->decompressor); + if(stream_has_capability(rpt, STREAM_CAP_COMPRESSION)) { + compressed_connection = true; + + if (!rpt->decompressor) + rpt->decompressor = create_decompressor(); + else + rpt->decompressor->reset(rpt->decompressor); + } #endif - do{ - if (receiver_read(rpt, fp)) + rpt->read_buffer[0] = '\0'; + rpt->read_len = 0; + + size_t read_buffer_start = 0; + char buffer[PLUGINSD_LINE_MAX + 2] = ""; + while(!netdata_exit) { + if(!receiver_next_line(rpt, buffer, PLUGINSD_LINE_MAX + 2, &read_buffer_start)) { + bool have_new_data; + if(compressed_connection) + have_new_data = receiver_read_compressed(rpt); + else + have_new_data = receiver_read_uncompressed(rpt); + + if(!have_new_data) + break; + + rpt->last_msg_t = now_realtime_sec(); + continue; + } + + if(unlikely(netdata_exit)) { + internal_error(true, "exiting..."); + goto done; + } + if(unlikely(rpt->shutdown)) { + internal_error(true, "parser shutdown..."); + goto done; + } + + if (unlikely(parser_action(parser, buffer))) { + internal_error(true, "parser_action() failed on keyword '%s'.", buffer); break; - int pos = 0; - char *line; - while ((line = receiver_next_line(rpt, &pos))) { - if (unlikely(netdata_exit || rpt->shutdown || parser_action(parser, line))) - goto done; } - rpt->last_msg_t = now_realtime_sec(); } - while(!netdata_exit); done: + internal_error(true, "Streaming receiver thread stopping..."); + result = user.count; // free parser with the pop function @@ -417,6 +431,15 @@ done: return result; } +static void rrdpush_receiver_replication_reset(struct receiver_state *rpt) { + RRDSET *st; + rrdset_foreach_read(st, rpt->host) { + rrdset_flag_clear(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS); + rrdset_flag_set(st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED); + } + rrdset_foreach_done(st); + rrdhost_receiver_replicating_charts_zero(rpt->host); +} static int rrdpush_receive(struct receiver_state *rpt) { @@ -427,6 +450,9 @@ static int rrdpush_receive(struct receiver_state *rpt) char *rrdpush_destination = default_rrdpush_destination; char *rrdpush_api_key = default_rrdpush_api_key; char *rrdpush_send_charts_matching = default_rrdpush_send_charts_matching; + bool rrdpush_enable_replication = default_rrdpush_enable_replication; + time_t rrdpush_seconds_to_replicate = default_rrdpush_seconds_to_replicate; + time_t rrdpush_replication_step = default_rrdpush_replication_step; time_t alarms_delay = 60; rpt->update_every = (int)appconfig_get_number(&stream_config, rpt->machine_guid, "update every", rpt->update_every); @@ -439,13 +465,10 @@ static int rrdpush_receive(struct receiver_state *rpt) mode = rrd_memory_mode_id(appconfig_get(&stream_config, rpt->key, "default memory mode", rrd_memory_mode_name(mode))); mode = rrd_memory_mode_id(appconfig_get(&stream_config, rpt->machine_guid, "memory mode", rrd_memory_mode_name(mode))); -#ifndef ENABLE_DBENGINE - if (unlikely(mode == RRD_MEMORY_MODE_DBENGINE)) { - close(rpt->fd); - log_stream_connection(rpt->client_ip, rpt->client_port, rpt->key, rpt->machine_guid, rpt->hostname, "REJECTED -- DBENGINE MEMORY MODE NOT SUPPORTED"); - return 1; + if (unlikely(mode == RRD_MEMORY_MODE_DBENGINE && !dbengine_enabled)) { + error("STREAM %s [receive from %s:%s]: dbengine is not enabled, falling back to default.", rpt->hostname, rpt->client_ip, rpt->client_port); + mode = default_rrd_memory_mode; } -#endif health_enabled = appconfig_get_boolean_ondemand(&stream_config, rpt->key, "health enabled by default", health_enabled); health_enabled = appconfig_get_boolean_ondemand(&stream_config, rpt->machine_guid, "health enabled", health_enabled); @@ -465,6 +488,15 @@ static int rrdpush_receive(struct receiver_state *rpt) rrdpush_send_charts_matching = appconfig_get(&stream_config, rpt->key, "default proxy send charts matching", rrdpush_send_charts_matching); rrdpush_send_charts_matching = appconfig_get(&stream_config, rpt->machine_guid, "proxy send charts matching", rrdpush_send_charts_matching); + rrdpush_enable_replication = appconfig_get_boolean(&stream_config, rpt->key, "enable replication", rrdpush_enable_replication); + rrdpush_enable_replication = appconfig_get_boolean(&stream_config, rpt->machine_guid, "enable replication", rrdpush_enable_replication); + + rrdpush_seconds_to_replicate = appconfig_get_number(&stream_config, rpt->key, "seconds to replicate", rrdpush_seconds_to_replicate); + rrdpush_seconds_to_replicate = appconfig_get_number(&stream_config, rpt->machine_guid, "seconds to replicate", rrdpush_seconds_to_replicate); + + rrdpush_replication_step = appconfig_get_number(&stream_config, rpt->key, "seconds per replication step", rrdpush_replication_step); + rrdpush_replication_step = appconfig_get_number(&stream_config, rpt->machine_guid, "seconds per replication step", rrdpush_replication_step); + #ifdef ENABLE_COMPRESSION unsigned int rrdpush_compression = default_compression_enabled; rrdpush_compression = appconfig_get_boolean(&stream_config, rpt->key, "enable compression", rrdpush_compression); @@ -480,14 +512,12 @@ static int rrdpush_receive(struct receiver_state *rpt) char initial_response[HTTP_HEADER_SIZE + 1]; snprintfz(initial_response, HTTP_HEADER_SIZE, "%s", START_STREAMING_ERROR_SAME_LOCALHOST); #ifdef ENABLE_HTTPS - rpt->host->stream_ssl.conn = rpt->ssl.conn; - rpt->host->stream_ssl.flags = rpt->ssl.flags; if(send_timeout(&rpt->ssl, rpt->fd, initial_response, strlen(initial_response), 0, 60) != (ssize_t)strlen(initial_response)) { #else if(send_timeout(rpt->fd, initial_response, strlen(initial_response), 0, 60) != strlen(initial_response)) { #endif - log_stream_connection(rpt->client_ip, rpt->client_port, rpt->key, rpt->host->machine_guid, rpt->host->hostname, "FAILED - CANNOT REPLY"); - error("STREAM %s [receive from [%s]:%s]: cannot send command.", rpt->host->hostname, rpt->client_ip, rpt->client_port); + log_stream_connection(rpt->client_ip, rpt->client_port, rpt->key, rpt->host->machine_guid, rrdhost_hostname(rpt->host), "FAILED - CANNOT REPLY"); + error("STREAM %s [receive from [%s]:%s]: cannot send command.", rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port); close(rpt->fd); return 0; } @@ -516,6 +546,9 @@ static int rrdpush_receive(struct receiver_state *rpt) , rrdpush_destination , rrdpush_api_key , rrdpush_send_charts_matching + , rrdpush_enable_replication + , rrdpush_seconds_to_replicate + , rrdpush_replication_step , rpt->system_info , 0 ); @@ -561,6 +594,9 @@ static int rrdpush_receive(struct receiver_state *rpt) rrdpush_destination, rrdpush_api_key, rrdpush_send_charts_matching, + rrdpush_enable_replication, + rrdpush_seconds_to_replicate, + rrdpush_replication_step, rpt->system_info); rrd_unlock(); } @@ -575,14 +611,14 @@ static int rrdpush_receive(struct receiver_state *rpt) , rpt->hostname , rpt->client_ip , rpt->client_port - , rpt->host->hostname + , rrdhost_hostname(rpt->host) , rpt->host->machine_guid , rpt->host->rrd_update_every , rpt->host->rrd_history_entries , rrd_memory_mode_name(rpt->host->rrd_memory_mode) , (health_enabled == CONFIG_BOOLEAN_NO)?"disabled":((health_enabled == CONFIG_BOOLEAN_YES)?"enabled":"auto") , ssl ? " SSL," : "" - , rpt->host->tags?rpt->host->tags:"" + , rrdhost_tags(rpt->host) ); #endif // NETDATA_INTERNAL_CHECKS @@ -596,7 +632,7 @@ static int rrdpush_receive(struct receiver_state *rpt) .obsolete = 0, .started_t = now_realtime_sec(), .next = NULL, - .version = 0, + .capabilities = 0, }; // put the client IP and port into the buffers used by plugins.d @@ -605,60 +641,50 @@ static int rrdpush_receive(struct receiver_state *rpt) snprintfz(cd.fullfilename, FILENAME_MAX, "%s:%s", rpt->client_ip, rpt->client_port); snprintfz(cd.cmd, PLUGINSD_CMD_MAX, "%s:%s", rpt->client_ip, rpt->client_port); - info("STREAM %s [receive from [%s]:%s]: initializing communication...", rpt->host->hostname, rpt->client_ip, rpt->client_port); - char initial_response[HTTP_HEADER_SIZE]; - if (rpt->stream_version > 1) { - if(rpt->stream_version >= STREAM_VERSION_COMPRESSION){ #ifdef ENABLE_COMPRESSION - if(!rpt->rrdpush_compression) - rpt->stream_version = STREAM_VERSION_CLABELS; -#else - if(STREAMING_PROTOCOL_CURRENT_VERSION < rpt->stream_version) { - rpt->stream_version = STREAMING_PROTOCOL_CURRENT_VERSION; - } + if (stream_has_capability(rpt, STREAM_CAP_COMPRESSION)) { + if (!rpt->rrdpush_compression) + rpt->capabilities &= ~STREAM_CAP_COMPRESSION; + } #endif - } - info("STREAM %s [receive from [%s]:%s]: Netdata is using the stream version %u.", rpt->host->hostname, rpt->client_ip, rpt->client_port, rpt->stream_version); - sprintf(initial_response, "%s%u", START_STREAMING_PROMPT_VN, rpt->stream_version); - } else if (rpt->stream_version == 1) { - info("STREAM %s [receive from [%s]:%s]: Netdata is using the stream version %u.", rpt->host->hostname, rpt->client_ip, rpt->client_port, rpt->stream_version); + + info("STREAM %s [receive from [%s]:%s]: initializing communication...", rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port); + char initial_response[HTTP_HEADER_SIZE]; + if (stream_has_capability(rpt, STREAM_CAP_VCAPS)) { + log_receiver_capabilities(rpt); + sprintf(initial_response, "%s%u", START_STREAMING_PROMPT_VN, rpt->capabilities); + } + else if (stream_has_capability(rpt, STREAM_CAP_VN)) { + log_receiver_capabilities(rpt); + sprintf(initial_response, "%s%d", START_STREAMING_PROMPT_VN, stream_capabilities_to_vn(rpt->capabilities)); + } else if (stream_has_capability(rpt, STREAM_CAP_V2)) { + log_receiver_capabilities(rpt); sprintf(initial_response, "%s", START_STREAMING_PROMPT_V2); - } else { - info("STREAM %s [receive from [%s]:%s]: Netdata is using first stream protocol.", rpt->host->hostname, rpt->client_ip, rpt->client_port); - sprintf(initial_response, "%s", START_STREAMING_PROMPT); + } else { // stream_has_capability(rpt, STREAM_CAP_V1) + log_receiver_capabilities(rpt); + sprintf(initial_response, "%s", START_STREAMING_PROMPT_V1); } debug(D_STREAM, "Initial response to %s: %s", rpt->client_ip, initial_response); - #ifdef ENABLE_HTTPS - rpt->host->stream_ssl.conn = rpt->ssl.conn; - rpt->host->stream_ssl.flags = rpt->ssl.flags; +#ifdef ENABLE_HTTPS if(send_timeout(&rpt->ssl, rpt->fd, initial_response, strlen(initial_response), 0, 60) != (ssize_t)strlen(initial_response)) { #else if(send_timeout(rpt->fd, initial_response, strlen(initial_response), 0, 60) != strlen(initial_response)) { #endif - log_stream_connection(rpt->client_ip, rpt->client_port, rpt->key, rpt->host->machine_guid, rpt->host->hostname, "FAILED - CANNOT REPLY"); - error("STREAM %s [receive from [%s]:%s]: cannot send ready command.", rpt->host->hostname, rpt->client_ip, rpt->client_port); + log_stream_connection(rpt->client_ip, rpt->client_port, rpt->key, rpt->host->machine_guid, rrdhost_hostname(rpt->host), "FAILED - CANNOT REPLY"); + error("STREAM %s [receive from [%s]:%s]: cannot send ready command.", rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port); close(rpt->fd); return 0; } // remove the non-blocking flag from the socket if(sock_delnonblock(rpt->fd) < 0) - error("STREAM %s [receive from [%s]:%s]: cannot remove the non-blocking flag from socket %d", rpt->host->hostname, rpt->client_ip, rpt->client_port, rpt->fd); + error("STREAM %s [receive from [%s]:%s]: cannot remove the non-blocking flag from socket %d", rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rpt->fd); struct timeval timeout; - timeout.tv_sec = 120; + timeout.tv_sec = 600; timeout.tv_usec = 0; if (unlikely(setsockopt(rpt->fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout) != 0)) - error("STREAM %s [receive from [%s]:%s]: cannot set timeout for socket %d", rpt->host->hostname, rpt->client_ip, rpt->client_port, rpt->fd); - - // convert the socket to a FILE * - FILE *fp = fdopen(rpt->fd, "r"); - if(!fp) { - log_stream_connection(rpt->client_ip, rpt->client_port, rpt->key, rpt->host->machine_guid, rpt->host->hostname, "FAILED - SOCKET ERROR"); - error("STREAM %s [receive from [%s]:%s]: failed to get a FILE for FD %d.", rpt->host->hostname, rpt->client_ip, rpt->client_port, rpt->fd); - close(rpt->fd); - return 0; - } + error("STREAM %s [receive from [%s]:%s]: cannot set timeout for socket %d", rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, rpt->fd); rrdhost_wrlock(rpt->host); /* if(rpt->host->connected_senders > 0) { @@ -671,34 +697,29 @@ static int rrdpush_receive(struct receiver_state *rpt) */ // rpt->host->connected_senders++; - if(rpt->stream_version > 0) { - rrdhost_flag_set(rpt->host, RRDHOST_FLAG_STREAM_LABELS_UPDATE); - rrdhost_flag_clear(rpt->host, RRDHOST_FLAG_STREAM_LABELS_STOP); - } - else { - rrdhost_flag_set(rpt->host, RRDHOST_FLAG_STREAM_LABELS_STOP); - rrdhost_flag_clear(rpt->host, RRDHOST_FLAG_STREAM_LABELS_UPDATE); - } - if(health_enabled != CONFIG_BOOLEAN_NO) { if(alarms_delay > 0) { rpt->host->health_delay_up_to = now_realtime_sec() + alarms_delay; - info( - "Postponing health checks for %" PRId64 " seconds, on host '%s', because it was just connected.", - (int64_t)alarms_delay, - rpt->host->hostname); + log_health( + "[%s]: Postponing health checks for %" PRId64 " seconds, because it was just connected.", + rrdhost_hostname(rpt->host), + (int64_t)alarms_delay); } } rpt->host->senders_connect_time = now_realtime_sec(); rpt->host->senders_last_chart_command = 0; rpt->host->trigger_chart_obsoletion_check = 1; + rrdhost_unlock(rpt->host); // call the plugins.d processor to receive the metrics - info("STREAM %s [receive from [%s]:%s]: receiving metrics...", rpt->host->hostname, rpt->client_ip, rpt->client_port); - log_stream_connection(rpt->client_ip, rpt->client_port, rpt->key, rpt->host->machine_guid, rpt->host->hostname, "CONNECTED"); + info("STREAM %s [receive from [%s]:%s]: receiving metrics...", + rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port); - cd.version = rpt->stream_version; + log_stream_connection(rpt->client_ip, rpt->client_port, + rpt->key, rpt->host->machine_guid, rrdhost_hostname(rpt->host), "CONNECTED"); + + cd.capabilities = rpt->capabilities; #ifdef ENABLE_ACLK // in case we have cloud connection we inform cloud @@ -707,24 +728,42 @@ static int rrdpush_receive(struct receiver_state *rpt) aclk_host_state_update(rpt->host, 1); #endif + rrdhost_set_is_parent_label(++localhost->senders_count); + + rrdpush_receiver_replication_reset(rpt); rrdcontext_host_child_connected(rpt->host); - size_t count = streaming_parser(rpt, &cd, fp); + rrdhost_flag_clear(rpt->host, RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED); - log_stream_connection(rpt->client_ip, rpt->client_port, rpt->key, rpt->host->machine_guid, rpt->hostname, + size_t count = streaming_parser(rpt, &cd, rpt->fd, +#ifdef ENABLE_HTTPS + (rpt->ssl.conn) ? &rpt->ssl : NULL +#else + NULL +#endif + ); + + rrdhost_flag_set(rpt->host, RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED); + + log_stream_connection(rpt->client_ip, rpt->client_port, + rpt->key, rpt->host->machine_guid, rpt->hostname, "DISCONNECTED"); - error("STREAM %s [receive from [%s]:%s]: disconnected (completed %zu updates).", rpt->hostname, rpt->client_ip, - rpt->client_port, count); + + error("STREAM %s [receive from [%s]:%s]: disconnected (completed %zu updates).", + rpt->hostname, rpt->client_ip, rpt->client_port, count); rrdcontext_host_child_disconnected(rpt->host); + rrdpush_receiver_replication_reset(rpt); #ifdef ENABLE_ACLK // in case we have cloud connection we inform cloud - // new child connected + // a child disconnected if (netdata_cloud_setting) aclk_host_state_update(rpt->host, 0); #endif + rrdhost_set_is_parent_label(--localhost->senders_count); + // During a shutdown there is cleanup code in rrdhost that will cancel the sender thread if (!netdata_exit && rpt->host) { rrd_rdlock(); @@ -747,7 +786,7 @@ static int rrdpush_receive(struct receiver_state *rpt) } // cleanup - fclose(fp); + close(rpt->fd); return (int)count; } @@ -758,6 +797,9 @@ void *rrdpush_receiver_thread(void *ptr) { info("STREAM %s [%s]:%s: receive thread created (task id %d)", rpt->hostname, rpt->client_ip, rpt->client_port, gettid()); worker_register("STREAMRCV"); + worker_register_job_custom_metric(WORKER_RECEIVER_JOB_BYTES_READ, "received bytes", "bytes/s", WORKER_METRIC_INCREMENT); + worker_register_job_custom_metric(WORKER_RECEIVER_JOB_BYTES_UNCOMPRESSED, "uncompressed bytes", "bytes/s", WORKER_METRIC_INCREMENT); + worker_register_job_custom_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION, "replication completion", "%", WORKER_METRIC_ABSOLUTE); rrdpush_receive(rpt); worker_unregister(); diff --git a/streaming/replication.c b/streaming/replication.c new file mode 100644 index 000000000..8fa501061 --- /dev/null +++ b/streaming/replication.c @@ -0,0 +1,1407 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "replication.h" +#include "Judy.h" + +#define STREAMING_START_MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED 50 +#define MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED 20 +#define MIN_SENDER_BUFFER_PERCENTAGE_ALLOWED 10 + +#define WORKER_JOB_FIND_NEXT 1 +#define WORKER_JOB_QUERYING 2 +#define WORKER_JOB_DELETE_ENTRY 3 +#define WORKER_JOB_FIND_CHART 4 +#define WORKER_JOB_CHECK_CONSISTENCY 5 +#define WORKER_JOB_BUFFER_COMMIT 6 +#define WORKER_JOB_CLEANUP 7 + +// master thread worker jobs +#define WORKER_JOB_STATISTICS 8 +#define WORKER_JOB_CUSTOM_METRIC_PENDING_REQUESTS 9 +#define WORKER_JOB_CUSTOM_METRIC_COMPLETION 10 +#define WORKER_JOB_CUSTOM_METRIC_ADDED 11 +#define WORKER_JOB_CUSTOM_METRIC_DONE 12 +#define WORKER_JOB_CUSTOM_METRIC_SKIPPED_NOT_CONNECTED 13 +#define WORKER_JOB_CUSTOM_METRIC_SKIPPED_NO_ROOM 14 +#define WORKER_JOB_CUSTOM_METRIC_WAITS 15 +#define WORKER_JOB_CUSTOM_METRIC_SENDER_RESETS 16 + +#define ITERATIONS_IDLE_WITHOUT_PENDING_TO_RUN_SENDER_VERIFICATION 30 +#define SECONDS_TO_RESET_POINT_IN_TIME 10 + +static struct replication_query_statistics replication_queries = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .queries_started = 0, + .queries_finished = 0, + .points_read = 0, + .points_generated = 0, +}; + +struct replication_query_statistics replication_get_query_statistics(void) { + netdata_spinlock_lock(&replication_queries.spinlock); + struct replication_query_statistics ret = replication_queries; + netdata_spinlock_unlock(&replication_queries.spinlock); + return ret; +} + +// ---------------------------------------------------------------------------- +// sending replication replies + +struct replication_dimension { + STORAGE_POINT sp; + struct storage_engine_query_handle handle; + bool enabled; + + DICTIONARY *dict; + const DICTIONARY_ITEM *rda; + RRDDIM *rd; +}; + +static time_t replicate_chart_timeframe(BUFFER *wb, RRDSET *st, time_t after, time_t before, bool enable_streaming, time_t wall_clock_time) { + size_t dimensions = rrdset_number_of_dimensions(st); + size_t points_read = 0, points_generated = 0; + + struct storage_engine_query_ops *ops = &st->rrdhost->db[0].eng->api.query_ops; + struct replication_dimension data[dimensions]; + memset(data, 0, sizeof(data)); + + if(enable_streaming && st->last_updated.tv_sec > before) { + internal_error(true, "STREAM_SENDER REPLAY: 'host:%s/chart:%s' has start_streaming = true, adjusting replication before timestamp from %llu to %llu", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + (unsigned long long)before, + (unsigned long long)st->last_updated.tv_sec + ); + before = st->last_updated.tv_sec; + } + + // prepare our array of dimensions + { + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if(unlikely(!rd || !rd_dfe.item || !rd->exposed)) + continue; + + if (unlikely(rd_dfe.counter >= dimensions)) { + internal_error(true, "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s' has more dimensions than the replicated ones", + rrdhost_hostname(st->rrdhost), rrdset_id(st)); + break; + } + + struct replication_dimension *d = &data[rd_dfe.counter]; + + d->dict = rd_dfe.dict; + d->rda = dictionary_acquired_item_dup(rd_dfe.dict, rd_dfe.item); + d->rd = rd; + + ops->init(rd->tiers[0]->db_metric_handle, &d->handle, after, before); + d->enabled = true; + } + rrddim_foreach_done(rd); + } + + time_t now = after + 1, actual_after = 0, actual_before = 0; (void)actual_before; + while(now <= before) { + time_t min_start_time = 0, min_end_time = 0; + for (size_t i = 0; i < dimensions ;i++) { + struct replication_dimension *d = &data[i]; + if(unlikely(!d->enabled)) continue; + + // fetch the first valid point for the dimension + int max_skip = 100; + while(d->sp.end_time < now && !ops->is_finished(&d->handle) && max_skip-- > 0) { + d->sp = ops->next_metric(&d->handle); + points_read++; + } + + internal_error(max_skip <= 0, + "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s/dim:%s': db does not advance the query beyond time %llu", + rrdhost_hostname(st->rrdhost), rrdset_id(st), rrddim_id(d->rd), (unsigned long long) now); + + if(unlikely(d->sp.end_time < now || storage_point_is_unset(d->sp) || storage_point_is_empty(d->sp))) + continue; + + if(unlikely(!min_start_time)) { + min_start_time = d->sp.start_time; + min_end_time = d->sp.end_time; + } + else { + min_start_time = MIN(min_start_time, d->sp.start_time); + min_end_time = MIN(min_end_time, d->sp.end_time); + } + } + + if(unlikely(min_start_time > wall_clock_time + 1 || min_end_time > wall_clock_time + st->update_every + 1)) { + internal_error(true, + "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s': db provided future start time %llu or end time %llu (now is %llu)", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + (unsigned long long)min_start_time, + (unsigned long long)min_end_time, + (unsigned long long)wall_clock_time); + break; + } + + if(unlikely(min_end_time < now)) { +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + internal_error(true, + "STREAM_SENDER REPLAY: 'host:%s/chart:%s': no data on any dimension beyond time %llu", + rrdhost_hostname(st->rrdhost), rrdset_id(st), (unsigned long long)now); +#endif // NETDATA_LOG_REPLICATION_REQUESTS + break; + } + + if(unlikely(min_end_time <= min_start_time)) + min_start_time = min_end_time - st->update_every; + + if(unlikely(!actual_after)) { + actual_after = min_end_time; + actual_before = min_end_time; + } + else + actual_before = min_end_time; + + buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_BEGIN " '' %llu %llu %llu\n" + , (unsigned long long)min_start_time + , (unsigned long long)min_end_time + , (unsigned long long)wall_clock_time + ); + + // output the replay values for this time + for (size_t i = 0; i < dimensions ;i++) { + struct replication_dimension *d = &data[i]; + if(unlikely(!d->enabled)) continue; + + if(likely(d->sp.start_time <= min_end_time && d->sp.end_time >= min_end_time)) + buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_SET " \"%s\" " NETDATA_DOUBLE_FORMAT " \"%s\"\n", + rrddim_id(d->rd), d->sp.sum, d->sp.flags & SN_FLAG_RESET ? "R" : ""); + + else + buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_SET " \"%s\" NAN \"E\"\n", + rrddim_id(d->rd)); + + points_generated++; + } + + now = min_end_time + 1; + } + +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + if(actual_after) { + char actual_after_buf[LOG_DATE_LENGTH + 1], actual_before_buf[LOG_DATE_LENGTH + 1]; + log_date(actual_after_buf, LOG_DATE_LENGTH, actual_after); + log_date(actual_before_buf, LOG_DATE_LENGTH, actual_before); + internal_error(true, + "STREAM_SENDER REPLAY: 'host:%s/chart:%s': sending data %llu [%s] to %llu [%s] (requested %llu [delta %lld] to %llu [delta %lld])", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + (unsigned long long)actual_after, actual_after_buf, (unsigned long long)actual_before, actual_before_buf, + (unsigned long long)after, (long long)(actual_after - after), (unsigned long long)before, (long long)(actual_before - before)); + } + else + internal_error(true, + "STREAM_SENDER REPLAY: 'host:%s/chart:%s': nothing to send (requested %llu to %llu)", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + (unsigned long long)after, (unsigned long long)before); +#endif // NETDATA_LOG_REPLICATION_REQUESTS + + // release all the dictionary items acquired + // finalize the queries + size_t queries = 0; + for(size_t i = 0; i < dimensions ;i++) { + struct replication_dimension *d = &data[i]; + if(unlikely(!d->enabled)) continue; + + ops->finalize(&d->handle); + + dictionary_acquired_item_release(d->dict, d->rda); + + // update global statistics + queries++; + } + + netdata_spinlock_lock(&replication_queries.spinlock); + replication_queries.queries_started += queries; + replication_queries.queries_finished += queries; + replication_queries.points_read += points_read; + replication_queries.points_generated += points_generated; + netdata_spinlock_unlock(&replication_queries.spinlock); + + return before; +} + +static void replicate_chart_collection_state(BUFFER *wb, RRDSET *st) { + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if(!rd->exposed) continue; + + buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE " \"%s\" %llu %lld " NETDATA_DOUBLE_FORMAT " " NETDATA_DOUBLE_FORMAT "\n", + rrddim_id(rd), + (usec_t)rd->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)rd->last_collected_time.tv_usec, + rd->last_collected_value, + rd->last_calculated_value, + rd->last_stored_value + ); + } + rrddim_foreach_done(rd); + + buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE " %llu %llu\n", + (usec_t)st->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)st->last_collected_time.tv_usec, + (usec_t)st->last_updated.tv_sec * USEC_PER_SEC + (usec_t)st->last_updated.tv_usec + ); +} + +bool replicate_chart_response(RRDHOST *host, RRDSET *st, bool start_streaming, time_t after, time_t before) { + time_t query_after = after; + time_t query_before = before; + time_t now = now_realtime_sec(); + time_t tolerance = 2; // sometimes from the time we get this value, to the time we check, + // a data collection has been made + // so, we give this tolerance to detect invalid timestamps + + // find the first entry we have + time_t first_entry_local = rrdset_first_entry_t(st); + if(first_entry_local > now + tolerance) { + internal_error(true, + "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s' db first time %llu is in the future (now is %llu)", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + (unsigned long long)first_entry_local, (unsigned long long)now); + first_entry_local = now; + } + + if (query_after < first_entry_local) + query_after = first_entry_local; + + // find the latest entry we have + time_t last_entry_local = st->last_updated.tv_sec; + if(!last_entry_local) { + internal_error(true, + "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s' RRDSET reports last updated time zero.", + rrdhost_hostname(st->rrdhost), rrdset_id(st)); + last_entry_local = rrdset_last_entry_t(st); + if(!last_entry_local) { + internal_error(true, + "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s' db reports last time zero.", + rrdhost_hostname(st->rrdhost), rrdset_id(st)); + last_entry_local = now; + } + } + + if(last_entry_local > now + tolerance) { + internal_error(true, + "STREAM_SENDER REPLAY ERROR: 'host:%s/chart:%s' last updated time %llu is in the future (now is %llu)", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + (unsigned long long)last_entry_local, (unsigned long long)now); + last_entry_local = now; + } + + if (query_before > last_entry_local) + query_before = last_entry_local; + + // if the parent asked us to start streaming, then fill the rest with the data that we have + if (start_streaming) + query_before = last_entry_local; + + if (query_after > query_before) { + time_t tmp = query_before; + query_before = query_after; + query_after = tmp; + } + + bool enable_streaming = (start_streaming || query_before == last_entry_local || !after || !before) ? true : false; + + // we might want to optimize this by filling a temporary buffer + // and copying the result to the host's buffer in order to avoid + // holding the host's buffer lock for too long + BUFFER *wb = sender_start(host->sender); + + buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_BEGIN " \"%s\"\n", rrdset_id(st)); + + if(after != 0 && before != 0) + before = replicate_chart_timeframe(wb, st, query_after, query_before, enable_streaming, now); + else { + after = 0; + before = 0; + enable_streaming = true; + } + + // get again the world clock time + time_t world_clock_time = now_realtime_sec(); + if(enable_streaming) { + if(now < world_clock_time) { + // we needed time to execute this request + // so, the parent will need to replicate more data + enable_streaming = false; + } + else + replicate_chart_collection_state(wb, st); + } + + // end with first/last entries we have, and the first start time and + // last end time of the data we sent + buffer_sprintf(wb, PLUGINSD_KEYWORD_REPLAY_END " %d %llu %llu %s %llu %llu %llu\n", + + // current chart update every + (int)st->update_every + + // child first db time, child end db time + , (unsigned long long)first_entry_local, (unsigned long long)last_entry_local + + // start streaming boolean + , enable_streaming ? "true" : "false" + + // after requested, before requested ('before' can be altered by the child when the request had enable_streaming true) + , (unsigned long long)after, (unsigned long long)before + + // child world clock time + , (unsigned long long)world_clock_time + ); + + worker_is_busy(WORKER_JOB_BUFFER_COMMIT); + sender_commit(host->sender, wb); + worker_is_busy(WORKER_JOB_CLEANUP); + + return enable_streaming; +} + +// ---------------------------------------------------------------------------- +// sending replication requests + +struct replication_request_details { + struct { + send_command callback; + void *data; + } caller; + + RRDHOST *host; + RRDSET *st; + + struct { + time_t first_entry_t; // the first entry time the child has + time_t last_entry_t; // the last entry time the child has + time_t world_time_t; // the current time of the child + } child_db; + + struct { + time_t first_entry_t; // the first entry time we have + time_t last_entry_t; // the last entry time we have + bool last_entry_t_adjusted_to_now; // true, if the last entry time was in the future, and we fixed + time_t now; // the current local world clock time + } local_db; + + struct { + time_t from; // the starting time of the entire gap we have + time_t to; // the ending time of the entire gap we have + } gap; + + struct { + time_t after; // the start time we requested previously from this child + time_t before; // the end time we requested previously from this child + } last_request; + + struct { + time_t after; // the start time of this replication request - the child will add 1 second + time_t before; // the end time of this replication request + bool start_streaming; // true when we want the child to send anything remaining and start streaming - the child will overwrite 'before' + } wanted; +}; + +static bool send_replay_chart_cmd(struct replication_request_details *r, const char *msg __maybe_unused) { + RRDSET *st = r->st; + + if(st->rrdhost->receiver && (!st->rrdhost->receiver->replication_first_time_t || r->wanted.after < st->rrdhost->receiver->replication_first_time_t)) + st->rrdhost->receiver->replication_first_time_t = r->wanted.after; + +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + st->replay.log_next_data_collection = true; + + char wanted_after_buf[LOG_DATE_LENGTH + 1] = "", wanted_before_buf[LOG_DATE_LENGTH + 1] = ""; + + if(r->wanted.after) + log_date(wanted_after_buf, LOG_DATE_LENGTH, r->wanted.after); + + if(r->wanted.before) + log_date(wanted_before_buf, LOG_DATE_LENGTH, r->wanted.before); + + internal_error(true, + "REPLAY: 'host:%s/chart:%s' sending replication request %ld [%s] to %ld [%s], start streaming '%s': %s: " + "last[%ld - %ld] child[%ld - %ld, now %ld %s] local[%ld - %ld %s, now %ld] gap[%ld - %ld %s] %s" + , rrdhost_hostname(r->host), rrdset_id(r->st) + , r->wanted.after, wanted_after_buf + , r->wanted.before, wanted_before_buf + , r->wanted.start_streaming ? "YES" : "NO" + , msg + , r->last_request.after, r->last_request.before + , r->child_db.first_entry_t, r->child_db.last_entry_t + , r->child_db.world_time_t, (r->child_db.world_time_t == r->local_db.now) ? "SAME" : (r->child_db.world_time_t < r->local_db.now) ? "BEHIND" : "AHEAD" + , r->local_db.first_entry_t, r->local_db.last_entry_t + , r->local_db.last_entry_t_adjusted_to_now?"FIXED":"RAW", r->local_db.now + , r->gap.from, r->gap.to + , (r->gap.from == r->wanted.after) ? "FULL" : "PARTIAL" + , (st->replay.after != 0 || st->replay.before != 0) ? "OVERLAPPING" : "" + ); + + st->replay.start_streaming = r->wanted.start_streaming; + st->replay.after = r->wanted.after; + st->replay.before = r->wanted.before; +#endif // NETDATA_LOG_REPLICATION_REQUESTS + + char buffer[2048 + 1]; + snprintfz(buffer, 2048, PLUGINSD_KEYWORD_REPLAY_CHART " \"%s\" \"%s\" %llu %llu\n", + rrdset_id(st), r->wanted.start_streaming ? "true" : "false", + (unsigned long long)r->wanted.after, (unsigned long long)r->wanted.before); + + int ret = r->caller.callback(buffer, r->caller.data); + if (ret < 0) { + error("REPLAY ERROR: 'host:%s/chart:%s' failed to send replication request to child (error %d)", + rrdhost_hostname(r->host), rrdset_id(r->st), ret); + return false; + } + + return true; +} + +bool replicate_chart_request(send_command callback, void *callback_data, RRDHOST *host, RRDSET *st, + time_t first_entry_child, time_t last_entry_child, time_t child_world_time, + time_t prev_first_entry_wanted, time_t prev_last_entry_wanted) +{ + struct replication_request_details r = { + .caller = { + .callback = callback, + .data = callback_data, + }, + + .host = host, + .st = st, + + .child_db = { + .first_entry_t = first_entry_child, + .last_entry_t = last_entry_child, + .world_time_t = child_world_time, + }, + + .local_db = { + .first_entry_t = rrdset_first_entry_t(st), + .last_entry_t = rrdset_last_entry_t(st), + .last_entry_t_adjusted_to_now = false, + .now = now_realtime_sec(), + }, + + .last_request = { + .after = prev_first_entry_wanted, + .before = prev_last_entry_wanted, + }, + + .wanted = { + .after = 0, + .before = 0, + .start_streaming = true, + }, + }; + + // check our local database retention + if(r.local_db.last_entry_t > r.local_db.now) { + r.local_db.last_entry_t = r.local_db.now; + r.local_db.last_entry_t_adjusted_to_now = true; + } + + // let's find the GAP we have + if(!r.last_request.after || !r.last_request.before) { + // there is no previous request + + if(r.local_db.last_entry_t) + // we have some data, let's continue from the last point we have + r.gap.from = r.local_db.last_entry_t; + else + // we don't have any data, the gap is the max timeframe we are allowed to replicate + r.gap.from = r.local_db.now - r.host->rrdpush_seconds_to_replicate; + + } + else { + // we had sent a request - let's continue at the point we left it + // for this we don't take into account the actual data in our db + // because the child may also have gaps, and we need to get over it + r.gap.from = r.last_request.before; + } + + // we want all the data up to now + r.gap.to = r.local_db.now; + + // The gap is now r.gap.from -> r.gap.to + + if (unlikely(!rrdhost_option_check(host, RRDHOST_OPTION_REPLICATION))) + return send_replay_chart_cmd(&r, "empty replication request, replication is disabled"); + + if (unlikely(!r.child_db.last_entry_t)) + return send_replay_chart_cmd(&r, "empty replication request, child has no stored data"); + + if (unlikely(!rrdset_number_of_dimensions(st))) + return send_replay_chart_cmd(&r, "empty replication request, chart has no dimensions"); + + if (r.child_db.first_entry_t <= 0) + return send_replay_chart_cmd(&r, "empty replication request, first entry of the child db first entry is invalid"); + + if (r.child_db.first_entry_t > r.child_db.last_entry_t) + return send_replay_chart_cmd(&r, "empty replication request, child timings are invalid (first entry > last entry)"); + + if (r.local_db.last_entry_t > r.child_db.last_entry_t) + return send_replay_chart_cmd(&r, "empty replication request, local last entry is later than the child one"); + + // let's find what the child can provide to fill that gap + + if(r.child_db.first_entry_t > r.gap.from) + // the child does not have all the data - let's get what it has + r.wanted.after = r.child_db.first_entry_t; + else + // ok, the child can fill the entire gap we have + r.wanted.after = r.gap.from; + + if(r.gap.to - r.wanted.after > host->rrdpush_replication_step) + // the duration is too big for one request - let's take the first step + r.wanted.before = r.wanted.after + host->rrdpush_replication_step; + else + // wow, we can do it in one request + r.wanted.before = r.gap.to; + + // don't ask from the child more than it has + if(r.wanted.before > r.child_db.last_entry_t) + r.wanted.before = r.child_db.last_entry_t; + + if(r.wanted.after > r.wanted.before) + r.wanted.after = r.wanted.before; + + // the child should start streaming immediately if the wanted duration is small, or we reached the last entry of the child + r.wanted.start_streaming = (r.local_db.now - r.wanted.after <= host->rrdpush_replication_step || r.wanted.before == r.child_db.last_entry_t); + + // the wanted timeframe is now r.wanted.after -> r.wanted.before + // send it + return send_replay_chart_cmd(&r, "OK"); +} + +// ---------------------------------------------------------------------------- +// replication thread + +// replication request in sender DICTIONARY +// used for de-duplicating the requests +struct replication_request { + struct sender_state *sender; // the sender we should put the reply at + STRING *chart_id; // the chart of the request + time_t after; // the start time of the query (maybe zero) key for sorting (JudyL) + time_t before; // the end time of the query (maybe zero) + bool start_streaming; // true, when the parent wants to send the rest of the data (before is overwritten) and enable normal streaming + + usec_t sender_last_flush_ut; // the timestamp of the sender, at the time we indexed this request + Word_t unique_id; // auto-increment, later requests have bigger + bool found; // used as a result boolean for the find call + bool indexed_in_judy; // true when the request is indexed in judy +}; + +// replication sort entry in JudyL array +// used for sorting all requests, across all nodes +struct replication_sort_entry { + struct replication_request *rq; + + size_t unique_id; // used as a key to identify the sort entry - we never access its contents +}; + +#define MAX_REPLICATION_THREADS 20 // + 1 for the main thread + +// the global variables for the replication thread +static struct replication_thread { + netdata_mutex_t mutex; + + struct { + size_t pending; // number of requests pending in the queue + Word_t unique_id; // the last unique id we gave to a request (auto-increment, starting from 1) + + // statistics + size_t added; // number of requests added to the queue + size_t removed; // number of requests removed from the queue + size_t skipped_not_connected; // number of requests skipped, because the sender is not connected to a parent + size_t skipped_no_room; // number of requests skipped, because the sender has no room for responses +// size_t skipped_no_room_since_last_reset; + size_t sender_resets; // number of times a sender reset our last position in the queue + time_t first_time_t; // the minimum 'after' we encountered + + struct { + Word_t after; + Word_t unique_id; + Pvoid_t JudyL_array; + } queue; + + } unsafe; // protected from replication_recursive_lock() + + struct { + size_t executed; // the number of replication requests executed + size_t latest_first_time; // the 'after' timestamp of the last request we executed + } atomic; // access should be with atomic operations + + struct { + size_t waits; + size_t last_executed; // caching of the atomic.executed to report number of requests executed since last time + + netdata_thread_t **threads_ptrs; + size_t threads; + } main_thread; // access is allowed only by the main thread + +} replication_globals = { + .mutex = NETDATA_MUTEX_INITIALIZER, + .unsafe = { + .pending = 0, + .unique_id = 0, + + .added = 0, + .removed = 0, + .skipped_not_connected = 0, + .skipped_no_room = 0, +// .skipped_no_room_since_last_reset = 0, + .sender_resets = 0, + + .first_time_t = 0, + + .queue = { + .after = 0, + .unique_id = 0, + .JudyL_array = NULL, + }, + }, + .atomic = { + .executed = 0, + .latest_first_time = 0, + }, + .main_thread = { + .waits = 0, + .last_executed = 0, + .threads = 0, + .threads_ptrs = NULL, + }, +}; + +#define replication_set_latest_first_time(t) __atomic_store_n(&replication_globals.atomic.latest_first_time, t, __ATOMIC_RELAXED) +#define replication_get_latest_first_time() __atomic_load_n(&replication_globals.atomic.latest_first_time, __ATOMIC_RELAXED) + +static inline bool replication_recursive_lock_mode(char mode) { + static __thread int recursions = 0; + + if(mode == 'L') { // (L)ock + if(++recursions == 1) + netdata_mutex_lock(&replication_globals.mutex); + } + else if(mode == 'U') { // (U)nlock + if(--recursions == 0) + netdata_mutex_unlock(&replication_globals.mutex); + } + else if(mode == 'C') { // (C)heck + if(recursions > 0) + return true; + else + return false; + } + else + fatal("REPLICATION: unknown lock mode '%c'", mode); + +#ifdef NETDATA_INTERNAL_CHECKS + if(recursions < 0) + fatal("REPLICATION: recursions is %d", recursions); +#endif + + return true; +} + +#define replication_recursive_lock() replication_recursive_lock_mode('L') +#define replication_recursive_unlock() replication_recursive_lock_mode('U') +#define fatal_when_replication_is_not_locked_for_me() do { \ + if(!replication_recursive_lock_mode('C')) \ + fatal("REPLICATION: reached %s, but replication is not locked by this thread.", __FUNCTION__); \ +} while(0) + +void replication_set_next_point_in_time(time_t after, size_t unique_id) { + replication_recursive_lock(); + replication_globals.unsafe.queue.after = after; + replication_globals.unsafe.queue.unique_id = unique_id; + replication_recursive_unlock(); +} + +// ---------------------------------------------------------------------------- +// replication sort entry management + +static struct replication_sort_entry *replication_sort_entry_create_unsafe(struct replication_request *rq) { + fatal_when_replication_is_not_locked_for_me(); + + struct replication_sort_entry *rse = mallocz(sizeof(struct replication_sort_entry)); + + rrdpush_sender_pending_replication_requests_plus_one(rq->sender); + + // copy the request + rse->rq = rq; + rse->unique_id = ++replication_globals.unsafe.unique_id; + + // save the unique id into the request, to be able to delete it later + rq->unique_id = rse->unique_id; + rq->indexed_in_judy = false; + return rse; +} + +static void replication_sort_entry_destroy(struct replication_sort_entry *rse) { + freez(rse); +} + +static struct replication_sort_entry *replication_sort_entry_add(struct replication_request *rq) { + replication_recursive_lock(); + + struct replication_sort_entry *rse = replication_sort_entry_create_unsafe(rq); + +// if(rq->after < (time_t)replication_globals.protected.queue.after && +// rq->sender->buffer_used_percentage <= MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED && +// !replication_globals.protected.skipped_no_room_since_last_reset) { +// +// // make it find this request first +// replication_set_next_point_in_time(rq->after, rq->unique_id); +// } + + replication_globals.unsafe.added++; + replication_globals.unsafe.pending++; + + Pvoid_t *inner_judy_ptr; + + // find the outer judy entry, using after as key + inner_judy_ptr = JudyLGet(replication_globals.unsafe.queue.JudyL_array, (Word_t) rq->after, PJE0); + if(!inner_judy_ptr) + inner_judy_ptr = JudyLIns(&replication_globals.unsafe.queue.JudyL_array, (Word_t) rq->after, PJE0); + + // add it to the inner judy, using unique_id as key + Pvoid_t *item = JudyLIns(inner_judy_ptr, rq->unique_id, PJE0); + *item = rse; + rq->indexed_in_judy = true; + + if(!replication_globals.unsafe.first_time_t || rq->after < replication_globals.unsafe.first_time_t) + replication_globals.unsafe.first_time_t = rq->after; + + replication_recursive_unlock(); + + return rse; +} + +static bool replication_sort_entry_unlink_and_free_unsafe(struct replication_sort_entry *rse, Pvoid_t **inner_judy_ppptr) { + fatal_when_replication_is_not_locked_for_me(); + + bool inner_judy_deleted = false; + + replication_globals.unsafe.removed++; + replication_globals.unsafe.pending--; + + rrdpush_sender_pending_replication_requests_minus_one(rse->rq->sender); + + rse->rq->indexed_in_judy = false; + + // delete it from the inner judy + JudyLDel(*inner_judy_ppptr, rse->rq->unique_id, PJE0); + + // if no items left, delete it from the outer judy + if(**inner_judy_ppptr == NULL) { + JudyLDel(&replication_globals.unsafe.queue.JudyL_array, rse->rq->after, PJE0); + inner_judy_deleted = true; + } + + // free memory + replication_sort_entry_destroy(rse); + + return inner_judy_deleted; +} + +static void replication_sort_entry_del(struct replication_request *rq) { + Pvoid_t *inner_judy_pptr; + struct replication_sort_entry *rse_to_delete = NULL; + + replication_recursive_lock(); + if(rq->indexed_in_judy) { + + inner_judy_pptr = JudyLGet(replication_globals.unsafe.queue.JudyL_array, rq->after, PJE0); + if (inner_judy_pptr) { + Pvoid_t *our_item_pptr = JudyLGet(*inner_judy_pptr, rq->unique_id, PJE0); + if (our_item_pptr) { + rse_to_delete = *our_item_pptr; + replication_sort_entry_unlink_and_free_unsafe(rse_to_delete, &inner_judy_pptr); + } + } + + if (!rse_to_delete) + fatal("REPLAY: 'host:%s/chart:%s' Cannot find sort entry to delete for time %ld.", + rrdhost_hostname(rq->sender->host), string2str(rq->chart_id), rq->after); + + } + + replication_recursive_unlock(); +} + +static inline PPvoid_t JudyLFirstOrNext(Pcvoid_t PArray, Word_t * PIndex, bool first) { + if(unlikely(first)) + return JudyLFirst(PArray, PIndex, PJE0); + + return JudyLNext(PArray, PIndex, PJE0); +} + +static struct replication_request replication_request_get_first_available() { + Pvoid_t *inner_judy_pptr; + + replication_recursive_lock(); + + struct replication_request rq_to_return = (struct replication_request){ .found = false }; + + if(unlikely(!replication_globals.unsafe.queue.after || !replication_globals.unsafe.queue.unique_id)) { + replication_globals.unsafe.queue.after = 0; + replication_globals.unsafe.queue.unique_id = 0; + } + + Word_t started_after = replication_globals.unsafe.queue.after; + + size_t round = 0; + while(!rq_to_return.found) { + round++; + + if(round > 2) + break; + + if(round == 2) { + if(started_after == 0) + break; + + replication_globals.unsafe.queue.after = 0; + replication_globals.unsafe.queue.unique_id = 0; + } + + bool find_same_after = true; + while (!rq_to_return.found && (inner_judy_pptr = JudyLFirstOrNext(replication_globals.unsafe.queue.JudyL_array, &replication_globals.unsafe.queue.after, find_same_after))) { + Pvoid_t *our_item_pptr; + + if(unlikely(round == 2 && replication_globals.unsafe.queue.after > started_after)) + break; + + while (!rq_to_return.found && (our_item_pptr = JudyLNext(*inner_judy_pptr, &replication_globals.unsafe.queue.unique_id, PJE0))) { + struct replication_sort_entry *rse = *our_item_pptr; + struct replication_request *rq = rse->rq; + struct sender_state *s = rq->sender; + + if (likely(rrdpush_sender_get_buffer_used_percent(s) <= MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED)) { + // there is room for this request in the sender buffer + + bool sender_is_connected = + rrdhost_flag_check(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED); + + bool sender_has_been_flushed_since_this_request = + rq->sender_last_flush_ut != rrdpush_sender_get_flush_time(s); + + if (unlikely(!sender_is_connected || sender_has_been_flushed_since_this_request)) { + // skip this request, the sender is not connected, or it has reconnected + + replication_globals.unsafe.skipped_not_connected++; + if (replication_sort_entry_unlink_and_free_unsafe(rse, &inner_judy_pptr)) + // we removed the item from the outer JudyL + break; + } + else { + // this request is good to execute + + // copy the request to return it + rq_to_return = *rq; + rq_to_return.chart_id = string_dup(rq_to_return.chart_id); + + // set the return result to found + rq_to_return.found = true; + + if (replication_sort_entry_unlink_and_free_unsafe(rse, &inner_judy_pptr)) + // we removed the item from the outer JudyL + break; + } + } + else { + replication_globals.unsafe.skipped_no_room++; +// replication_globals.protected.skipped_no_room_since_last_reset++; + } + } + + // call JudyLNext from now on + find_same_after = false; + + // prepare for the next iteration on the outer loop + replication_globals.unsafe.queue.unique_id = 0; + } + } + + replication_recursive_unlock(); + return rq_to_return; +} + +// ---------------------------------------------------------------------------- +// replication request management + +static void replication_request_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value __maybe_unused, void *sender_state __maybe_unused) { + struct sender_state *s = sender_state; (void)s; + struct replication_request *rq = value; + + // IMPORTANT: + // We use the react instead of the insert callback + // because we want the item to be atomically visible + // to our replication thread, immediately after. + + // If we put this at the insert callback, the item is not guaranteed + // to be atomically visible to others, so the replication thread + // may see the replication sort entry, but fail to find the dictionary item + // related to it. + + replication_sort_entry_add(rq); + + // this request is about a unique chart for this sender + rrdpush_sender_replicating_charts_plus_one(s); +} + +static bool replication_request_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *sender_state) { + struct sender_state *s = sender_state; (void)s; + struct replication_request *rq = old_value; (void)rq; + struct replication_request *rq_new = new_value; + + replication_recursive_lock(); + + if(!rq->indexed_in_judy) { + replication_sort_entry_add(rq); + internal_error( + true, + "STREAM %s [send to %s]: REPLAY: 'host:%s/chart:%s' adding duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])", + rrdhost_hostname(s->host), s->connected_to, rrdhost_hostname(s->host), dictionary_acquired_item_name(item), + (unsigned long long)rq->after, (unsigned long long)rq->before, rq->start_streaming ? "true" : "false", + (unsigned long long)rq_new->after, (unsigned long long)rq_new->before, rq_new->start_streaming ? "true" : "false"); + } + else { + internal_error( + true, + "STREAM %s [send to %s]: REPLAY: 'host:%s/chart:%s' ignoring duplicate replication command received (existing from %llu to %llu [%s], new from %llu to %llu [%s])", + rrdhost_hostname(s->host), s->connected_to, rrdhost_hostname(s->host), + dictionary_acquired_item_name(item), + (unsigned long long) rq->after, (unsigned long long) rq->before, rq->start_streaming ? "true" : "false", + (unsigned long long) rq_new->after, (unsigned long long) rq_new->before, rq_new->start_streaming ? "true" : "false"); + } + + replication_recursive_unlock(); + + string_freez(rq_new->chart_id); + return false; +} + +static void replication_request_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *sender_state __maybe_unused) { + struct replication_request *rq = value; + + // this request is about a unique chart for this sender + rrdpush_sender_replicating_charts_minus_one(rq->sender); + + if(rq->indexed_in_judy) + replication_sort_entry_del(rq); + + string_freez(rq->chart_id); +} + +static bool replication_execute_request(struct replication_request *rq, bool workers) { + bool ret = false; + + if(likely(workers)) + worker_is_busy(WORKER_JOB_FIND_CHART); + + RRDSET *st = rrdset_find(rq->sender->host, string2str(rq->chart_id)); + if(!st) { + internal_error(true, "REPLAY ERROR: 'host:%s/chart:%s' not found", + rrdhost_hostname(rq->sender->host), string2str(rq->chart_id)); + + goto cleanup; + } + + if(likely(workers)) + worker_is_busy(WORKER_JOB_QUERYING); + + netdata_thread_disable_cancelability(); + + // send the replication data + bool start_streaming = replicate_chart_response( + st->rrdhost, st, rq->start_streaming, rq->after, rq->before); + + netdata_thread_enable_cancelability(); + + if(start_streaming && rq->sender_last_flush_ut == rrdpush_sender_get_flush_time(rq->sender)) { + // enable normal streaming if we have to + // but only if the sender buffer has not been flushed since we started + + if(rrdset_flag_check(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS)) { + rrdset_flag_clear(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS); + rrdset_flag_set(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED); + rrdhost_sender_replicating_charts_minus_one(st->rrdhost); + +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + internal_error(true, "STREAM_SENDER REPLAY: 'host:%s/chart:%s' streaming starts", + rrdhost_hostname(st->rrdhost), rrdset_id(st)); +#endif + } + else + internal_error(true, "REPLAY ERROR: 'host:%s/chart:%s' received start streaming command, but the chart is not in progress replicating", + rrdhost_hostname(st->rrdhost), string2str(rq->chart_id)); + } + + __atomic_add_fetch(&replication_globals.atomic.executed, 1, __ATOMIC_RELAXED); + + ret = true; + +cleanup: + string_freez(rq->chart_id); + return ret; +} + +// ---------------------------------------------------------------------------- +// public API + +void replication_add_request(struct sender_state *sender, const char *chart_id, time_t after, time_t before, bool start_streaming) { + struct replication_request rq = { + .sender = sender, + .chart_id = string_strdupz(chart_id), + .after = after, + .before = before, + .start_streaming = start_streaming, + .sender_last_flush_ut = rrdpush_sender_get_flush_time(sender), + }; + + if(start_streaming && rrdpush_sender_get_buffer_used_percent(sender) <= STREAMING_START_MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED) + replication_execute_request(&rq, false); + + else + dictionary_set(sender->replication.requests, chart_id, &rq, sizeof(struct replication_request)); +} + +void replication_sender_delete_pending_requests(struct sender_state *sender) { + // allow the dictionary destructor to go faster on locks + replication_recursive_lock(); + dictionary_flush(sender->replication.requests); + replication_recursive_unlock(); +} + +void replication_init_sender(struct sender_state *sender) { + sender->replication.requests = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); + dictionary_register_react_callback(sender->replication.requests, replication_request_react_callback, sender); + dictionary_register_conflict_callback(sender->replication.requests, replication_request_conflict_callback, sender); + dictionary_register_delete_callback(sender->replication.requests, replication_request_delete_callback, sender); +} + +void replication_cleanup_sender(struct sender_state *sender) { + // allow the dictionary destructor to go faster on locks + replication_recursive_lock(); + dictionary_destroy(sender->replication.requests); + replication_recursive_unlock(); +} + +void replication_recalculate_buffer_used_ratio_unsafe(struct sender_state *s) { + size_t available = cbuffer_available_size_unsafe(s->host->sender->buffer); + size_t percentage = (s->buffer->max_size - available) * 100 / s->buffer->max_size; + + if(percentage > MAX_SENDER_BUFFER_PERCENTAGE_ALLOWED) + s->replication.unsafe.reached_max = true; + + if(s->replication.unsafe.reached_max && + percentage <= MIN_SENDER_BUFFER_PERCENTAGE_ALLOWED) { + s->replication.unsafe.reached_max = false; + replication_recursive_lock(); +// replication_set_next_point_in_time(0, 0); + replication_globals.unsafe.sender_resets++; + replication_recursive_unlock(); + } + + rrdpush_sender_set_buffer_used_percent(s, percentage); +} + +// ---------------------------------------------------------------------------- +// replication thread + +static size_t verify_host_charts_are_streaming_now(RRDHOST *host) { + internal_error( + host->sender && + !rrdpush_sender_pending_replication_requests(host->sender) && + dictionary_entries(host->sender->replication.requests) != 0, + "REPLICATION SUMMARY: 'host:%s' reports %zu pending replication requests, but its chart replication index says there are %zu charts pending replication", + rrdhost_hostname(host), + rrdpush_sender_pending_replication_requests(host->sender), + dictionary_entries(host->sender->replication.requests) + ); + + size_t ok = 0; + size_t errors = 0; + + RRDSET *st; + rrdset_foreach_read(st, host) { + RRDSET_FLAGS flags = rrdset_flag_check(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS | RRDSET_FLAG_SENDER_REPLICATION_FINISHED); + + bool is_error = false; + + if(!flags) { + internal_error( + true, + "REPLICATION SUMMARY: 'host:%s/chart:%s' is neither IN PROGRESS nor FINISHED", + rrdhost_hostname(host), rrdset_id(st) + ); + is_error = true; + } + + if(!(flags & RRDSET_FLAG_SENDER_REPLICATION_FINISHED) || (flags & RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS)) { + internal_error( + true, + "REPLICATION SUMMARY: 'host:%s/chart:%s' is IN PROGRESS although replication is finished", + rrdhost_hostname(host), rrdset_id(st) + ); + is_error = true; + } + + if(is_error) + errors++; + else + ok++; + } + rrdset_foreach_done(st); + + internal_error(errors, + "REPLICATION SUMMARY: 'host:%s' finished replicating %zu charts, but %zu charts are still in progress although replication finished", + rrdhost_hostname(host), ok, errors); + + return errors; +} + +static void verify_all_hosts_charts_are_streaming_now(void) { + worker_is_busy(WORKER_JOB_CHECK_CONSISTENCY); + + size_t errors = 0; + RRDHOST *host; + dfe_start_read(rrdhost_root_index, host) + errors += verify_host_charts_are_streaming_now(host); + dfe_done(host); + + size_t executed = __atomic_load_n(&replication_globals.atomic.executed, __ATOMIC_RELAXED); + info("REPLICATION SUMMARY: finished, executed %zu replication requests, %zu charts pending replication", + executed - replication_globals.main_thread.last_executed, errors); + replication_globals.main_thread.last_executed = executed; +} + +static void replication_initialize_workers(bool master) { + worker_register("REPLICATION"); + worker_register_job_name(WORKER_JOB_FIND_NEXT, "find next"); + worker_register_job_name(WORKER_JOB_QUERYING, "querying"); + worker_register_job_name(WORKER_JOB_DELETE_ENTRY, "dict delete"); + worker_register_job_name(WORKER_JOB_FIND_CHART, "find chart"); + worker_register_job_name(WORKER_JOB_CHECK_CONSISTENCY, "check consistency"); + worker_register_job_name(WORKER_JOB_BUFFER_COMMIT, "commit"); + worker_register_job_name(WORKER_JOB_CLEANUP, "cleanup"); + + if(master) { + worker_register_job_name(WORKER_JOB_STATISTICS, "statistics"); + worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_PENDING_REQUESTS, "pending requests", "requests", WORKER_METRIC_ABSOLUTE); + worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_COMPLETION, "completion", "%", WORKER_METRIC_ABSOLUTE); + worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_ADDED, "added requests", "requests/s", WORKER_METRIC_INCREMENTAL_TOTAL); + worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_DONE, "finished requests", "requests/s", WORKER_METRIC_INCREMENTAL_TOTAL); + worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_SKIPPED_NOT_CONNECTED, "not connected requests", "requests/s", WORKER_METRIC_INCREMENTAL_TOTAL); + worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_SKIPPED_NO_ROOM, "no room requests", "requests/s", WORKER_METRIC_INCREMENTAL_TOTAL); + worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_SENDER_RESETS, "sender resets", "resets/s", WORKER_METRIC_INCREMENTAL_TOTAL); + worker_register_job_custom_metric(WORKER_JOB_CUSTOM_METRIC_WAITS, "waits", "waits/s", WORKER_METRIC_INCREMENTAL_TOTAL); + } +} + +#define REQUEST_OK (0) +#define REQUEST_QUEUE_EMPTY (-1) +#define REQUEST_CHART_NOT_FOUND (-2) + +static int replication_execute_next_pending_request(void) { + worker_is_busy(WORKER_JOB_FIND_NEXT); + struct replication_request rq = replication_request_get_first_available(); + + if(unlikely(!rq.found)) + return REQUEST_QUEUE_EMPTY; + + // delete the request from the dictionary + worker_is_busy(WORKER_JOB_DELETE_ENTRY); + if(!dictionary_del(rq.sender->replication.requests, string2str(rq.chart_id))) + error("REPLAY ERROR: 'host:%s/chart:%s' failed to be deleted from sender pending charts index", + rrdhost_hostname(rq.sender->host), string2str(rq.chart_id)); + + replication_set_latest_first_time(rq.after); + + if(unlikely(!replication_execute_request(&rq, true))) + return REQUEST_CHART_NOT_FOUND; + + return REQUEST_OK; +} + +static void replication_worker_cleanup(void *ptr __maybe_unused) { + worker_unregister(); +} + +static void *replication_worker_thread(void *ptr) { + replication_initialize_workers(false); + + netdata_thread_cleanup_push(replication_worker_cleanup, ptr); + + while(!netdata_exit) { + if(unlikely(replication_execute_next_pending_request() == REQUEST_QUEUE_EMPTY)) { + worker_is_idle(); + sleep_usec(1 * USEC_PER_SEC); + } + } + + netdata_thread_cleanup_pop(1); + return NULL; +} + +static void replication_main_cleanup(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; + static_thread->enabled = NETDATA_MAIN_THREAD_EXITING; + + int threads = (int)replication_globals.main_thread.threads; + for(int i = 0; i < threads ;i++) { + netdata_thread_join(*replication_globals.main_thread.threads_ptrs[i], NULL); + freez(replication_globals.main_thread.threads_ptrs[i]); + } + freez(replication_globals.main_thread.threads_ptrs); + replication_globals.main_thread.threads_ptrs = NULL; + + // custom code + worker_unregister(); + + static_thread->enabled = NETDATA_MAIN_THREAD_EXITED; +} + +void *replication_thread_main(void *ptr __maybe_unused) { + replication_initialize_workers(true); + + int threads = config_get_number(CONFIG_SECTION_DB, "replication threads", 1); + if(threads < 1 || threads > MAX_REPLICATION_THREADS) { + error("replication threads given %d is invalid, resetting to 1", threads); + threads = 1; + } + + if(--threads) { + replication_globals.main_thread.threads = threads; + replication_globals.main_thread.threads_ptrs = mallocz(threads * sizeof(netdata_thread_t *)); + + for(int i = 0; i < threads ;i++) { + replication_globals.main_thread.threads_ptrs[i] = mallocz(sizeof(netdata_thread_t)); + netdata_thread_create(replication_globals.main_thread.threads_ptrs[i], "REPLICATION", + NETDATA_THREAD_OPTION_JOINABLE, replication_worker_thread, NULL); + } + } + + netdata_thread_cleanup_push(replication_main_cleanup, ptr); + + // start from 100% completed + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_COMPLETION, 100.0); + + long run_verification_countdown = LONG_MAX; // LONG_MAX to prevent an initial verification when no replication ever took place + bool slow = true; // control the time we sleep - it has to start with true! + usec_t last_now_mono_ut = now_monotonic_usec(); + time_t replication_reset_next_point_in_time_countdown = SECONDS_TO_RESET_POINT_IN_TIME; // restart from the beginning every 10 seconds + + size_t last_executed = 0; + size_t last_sender_resets = 0; + + while(!netdata_exit) { + + // statistics + usec_t now_mono_ut = now_monotonic_usec(); + if(unlikely(now_mono_ut - last_now_mono_ut > default_rrd_update_every * USEC_PER_SEC)) { + last_now_mono_ut = now_mono_ut; + + replication_recursive_lock(); + + size_t current_executed = __atomic_load_n(&replication_globals.atomic.executed, __ATOMIC_RELAXED); + if(last_executed != current_executed) { + run_verification_countdown = ITERATIONS_IDLE_WITHOUT_PENDING_TO_RUN_SENDER_VERIFICATION; + last_executed = current_executed; + slow = false; + } + + if(replication_reset_next_point_in_time_countdown-- == 0) { + // once per second, make it scan all the pending requests next time + replication_set_next_point_in_time(0, 0); +// replication_globals.protected.skipped_no_room_since_last_reset = 0; + replication_reset_next_point_in_time_countdown = SECONDS_TO_RESET_POINT_IN_TIME; + } + + if(!replication_globals.unsafe.pending && --run_verification_countdown == 0) { + // reset the statistics about completion percentage + replication_globals.unsafe.first_time_t = 0; + replication_set_latest_first_time(0); + + verify_all_hosts_charts_are_streaming_now(); + + run_verification_countdown = LONG_MAX; + slow = true; + } + + worker_is_busy(WORKER_JOB_STATISTICS); + + time_t latest_first_time_t = replication_get_latest_first_time(); + if(latest_first_time_t && replication_globals.unsafe.pending) { + // completion percentage statistics + time_t now = now_realtime_sec(); + time_t total = now - replication_globals.unsafe.first_time_t; + time_t done = latest_first_time_t - replication_globals.unsafe.first_time_t; + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_COMPLETION, + (NETDATA_DOUBLE) done * 100.0 / (NETDATA_DOUBLE) total); + } + else + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_COMPLETION, 100.0); + + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_PENDING_REQUESTS, (NETDATA_DOUBLE)replication_globals.unsafe.pending); + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_ADDED, (NETDATA_DOUBLE)replication_globals.unsafe.added); + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_DONE, (NETDATA_DOUBLE)__atomic_load_n(&replication_globals.atomic.executed, __ATOMIC_RELAXED)); + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_SKIPPED_NOT_CONNECTED, (NETDATA_DOUBLE)replication_globals.unsafe.skipped_not_connected); + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_SKIPPED_NO_ROOM, (NETDATA_DOUBLE)replication_globals.unsafe.skipped_no_room); + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_SENDER_RESETS, (NETDATA_DOUBLE)replication_globals.unsafe.sender_resets); + worker_set_metric(WORKER_JOB_CUSTOM_METRIC_WAITS, (NETDATA_DOUBLE)replication_globals.main_thread.waits); + + replication_recursive_unlock(); + } + + if(unlikely(replication_execute_next_pending_request() == REQUEST_QUEUE_EMPTY)) { + replication_recursive_lock(); + + // the timeout also defines now frequently we will traverse all the pending requests + // when the outbound buffers of all senders is full + usec_t timeout; + if(slow) + // no work to be done, wait for a request to come in + timeout = 1000 * USEC_PER_MS; + + else if(replication_globals.unsafe.pending > 0) { + if(replication_globals.unsafe.sender_resets == last_sender_resets) { + timeout = 1000 * USEC_PER_MS; + } + else { + // there are pending requests waiting to be executed, + // but none could be executed at this time. + // try again after this time. + timeout = 100 * USEC_PER_MS; + } + + last_sender_resets = replication_globals.unsafe.sender_resets; + } + else { + // no requests pending, but there were requests recently (run_verification_countdown) + // so, try in a short time. + // if this is big, one chart replicating will be slow to finish (ping - pong just one chart) + timeout = 10 * USEC_PER_MS; + last_sender_resets = replication_globals.unsafe.sender_resets; + } + + replication_globals.main_thread.waits++; + replication_recursive_unlock(); + + worker_is_idle(); + sleep_usec(timeout); + + // make it scan all the pending requests next time + replication_set_next_point_in_time(0, 0); + replication_reset_next_point_in_time_countdown = SECONDS_TO_RESET_POINT_IN_TIME; + + continue; + } + } + + netdata_thread_cleanup_pop(1); + return NULL; +} diff --git a/streaming/replication.h b/streaming/replication.h new file mode 100644 index 000000000..00462cc3a --- /dev/null +++ b/streaming/replication.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef REPLICATION_H +#define REPLICATION_H + +#include "daemon/common.h" + +struct replication_query_statistics { + SPINLOCK spinlock; + size_t queries_started; + size_t queries_finished; + size_t points_read; + size_t points_generated; +}; + +struct replication_query_statistics replication_get_query_statistics(void); + +bool replicate_chart_response(RRDHOST *rh, RRDSET *rs, bool start_streaming, time_t after, time_t before); + +typedef int (*send_command)(const char *txt, void *data); + +bool replicate_chart_request(send_command callback, void *callback_data, + RRDHOST *rh, RRDSET *rs, + time_t first_entry_child, time_t last_entry_child, time_t child_world_time, + time_t response_first_start_time, time_t response_last_end_time); + +void replication_init_sender(struct sender_state *sender); +void replication_cleanup_sender(struct sender_state *sender); +void replication_sender_delete_pending_requests(struct sender_state *sender); +void replication_add_request(struct sender_state *sender, const char *chart_id, time_t after, time_t before, bool start_streaming); +void replication_recalculate_buffer_used_ratio_unsafe(struct sender_state *s); + +#endif /* REPLICATION_H */ diff --git a/streaming/rrdpush.c b/streaming/rrdpush.c index b73f24633..a57f1b080 100644 --- a/streaming/rrdpush.c +++ b/streaming/rrdpush.c @@ -11,8 +11,8 @@ * 1. a random data collection thread, calling rrdset_done_push() * this is called for each chart. * - * the output of this work is kept in a BUFFER in RRDHOST - * the sender thread is signalled via a pipe (also in RRDHOST) + * the output of this work is kept in a thread BUFFER + * the sender thread is signalled via a pipe (in RRDHOST) * * 2. a sender thread running at the sending netdata * this is spawned automatically on the first chart to be pushed @@ -46,6 +46,9 @@ unsigned int default_compression_enabled = 1; char *default_rrdpush_destination = NULL; char *default_rrdpush_api_key = NULL; char *default_rrdpush_send_charts_matching = NULL; +bool default_rrdpush_enable_replication = true; +time_t default_rrdpush_seconds_to_replicate = 86400; +time_t default_rrdpush_replication_step = 600; #ifdef ENABLE_HTTPS int netdata_use_ssl_on_stream = NETDATA_SSL_OPTIONAL; char *netdata_ssl_ca_path = NULL; @@ -66,6 +69,31 @@ static void load_stream_conf() { freez(filename); } +bool rrdpush_receiver_needs_dbengine() { + struct section *co; + + for(co = stream_config.first_section; co; co = co->next) { + if(strcmp(co->name, "stream") == 0) + continue; // the first section is not relevant + + char *s; + + s = appconfig_get_by_section(co, "enabled", NULL); + if(!s || !appconfig_test_boolean_value(s)) + continue; + + s = appconfig_get_by_section(co, "default memory mode", NULL); + if(s && strcmp(s, "dbengine") == 0) + return true; + + s = appconfig_get_by_section(co, "memory mode", NULL); + if(s && strcmp(s, "dbengine") == 0) + return true; + } + + return false; +} + int rrdpush_init() { // -------------------------------------------------------------------- // load stream.conf @@ -75,6 +103,11 @@ int rrdpush_init() { default_rrdpush_destination = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "destination", ""); default_rrdpush_api_key = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "api key", ""); default_rrdpush_send_charts_matching = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "send charts matching", "*"); + + default_rrdpush_enable_replication = config_get_boolean(CONFIG_SECTION_DB, "enable replication", default_rrdpush_enable_replication); + default_rrdpush_seconds_to_replicate = config_get_number(CONFIG_SECTION_DB, "seconds to replicate", default_rrdpush_seconds_to_replicate); + default_rrdpush_replication_step = config_get_number(CONFIG_SECTION_DB, "seconds per replication step", default_rrdpush_replication_step); + rrdhost_free_orphan_time = config_get_number(CONFIG_SECTION_DB, "cleanup orphan hosts after secs", rrdhost_free_orphan_time); #ifdef ENABLE_COMPRESSION @@ -101,14 +134,14 @@ int rrdpush_init() { bool invalid_certificate = appconfig_get_boolean(&stream_config, CONFIG_SECTION_STREAM, "ssl skip certificate verification", CONFIG_BOOLEAN_NO); if(invalid_certificate == CONFIG_BOOLEAN_YES){ - if(netdata_validate_server == NETDATA_SSL_VALID_CERTIFICATE){ + if(netdata_ssl_validate_server == NETDATA_SSL_VALID_CERTIFICATE){ info("Netdata is configured to accept invalid SSL certificate."); - netdata_validate_server = NETDATA_SSL_INVALID_CERTIFICATE; + netdata_ssl_validate_server = NETDATA_SSL_INVALID_CERTIFICATE; } } - netdata_ssl_ca_path = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "CApath", "/etc/ssl/certs/"); - netdata_ssl_ca_file = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "CAfile", "/etc/ssl/certs/certs.pem"); + netdata_ssl_ca_path = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "CApath", NULL); + netdata_ssl_ca_file = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "CAfile", NULL); #endif return default_rrdpush_enabled; @@ -128,30 +161,31 @@ int rrdpush_init() { // this is for the first iterations of each chart unsigned int remote_clock_resync_iterations = 60; - -static inline int should_send_chart_matching(RRDSET *st) { - // Do not stream anomaly rates charts. - if (unlikely(st->state->is_ar_chart)) +static inline bool should_send_chart_matching(RRDSET *st, RRDSET_FLAGS flags) { + if(!(flags & RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED)) return false; - if (rrdset_flag_check(st, RRDSET_FLAG_ANOMALY_DETECTION)) - return ml_streaming_enabled(); - - if(!rrdset_flag_check(st, RRDSET_FLAG_UPSTREAM_SEND|RRDSET_FLAG_UPSTREAM_IGNORE)) { + if(unlikely(!(flags & (RRDSET_FLAG_UPSTREAM_SEND | RRDSET_FLAG_UPSTREAM_IGNORE)))) { RRDHOST *host = st->rrdhost; - if(simple_pattern_matches(host->rrdpush_send_charts_matching, st->id) || - simple_pattern_matches(host->rrdpush_send_charts_matching, st->name)) { - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_IGNORE); - rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_SEND); + if (flags & RRDSET_FLAG_ANOMALY_DETECTION) { + if(ml_streaming_enabled()) + rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_SEND); + else + rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_IGNORE); } - else { - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_SEND); + else if(simple_pattern_matches(host->rrdpush_send_charts_matching, rrdset_id(st)) || + simple_pattern_matches(host->rrdpush_send_charts_matching, rrdset_name(st))) + + rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_SEND); + else rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_IGNORE); - } + + // get the flags again, to know how to respond + flags = rrdset_flag_check(st, RRDSET_FLAG_UPSTREAM_SEND|RRDSET_FLAG_UPSTREAM_IGNORE); } - return(rrdset_flag_check(st, RRDSET_FLAG_UPSTREAM_SEND)); + return flags & RRDSET_FLAG_UPSTREAM_SEND; } int configured_as_parent() { @@ -173,42 +207,25 @@ int configured_as_parent() { return is_parent; } -// checks if the current chart definition has been sent -static inline int need_to_send_chart_definition(RRDSET *st) { - rrdset_check_rdlock(st); - - if(unlikely(!(rrdset_flag_check(st, RRDSET_FLAG_UPSTREAM_EXPOSED)))) - return 1; - - RRDDIM *rd; - rrddim_foreach_read(rd, st) { - if(unlikely(!rd->exposed)) { - #ifdef NETDATA_INTERNAL_CHECKS - info("host '%s', chart '%s', dimension '%s' flag 'exposed' triggered chart refresh to upstream", st->rrdhost->hostname, st->id, rd->id); - #endif - return 1; - } - } - - return 0; -} - // chart labels static int send_clabels_callback(const char *name, const char *value, RRDLABEL_SRC ls, void *data) { BUFFER *wb = (BUFFER *)data; buffer_sprintf(wb, "CLABEL \"%s\" \"%s\" %d\n", name, value, ls); return 1; } -void rrdpush_send_clabels(RRDHOST *host, RRDSET *st) { - if (st->state && st->state->chart_labels) { - if(rrdlabels_walkthrough_read(st->state->chart_labels, send_clabels_callback, host->sender->build) > 0) - buffer_sprintf(host->sender->build,"CLABEL_COMMIT\n"); + +static void rrdpush_send_clabels(BUFFER *wb, RRDSET *st) { + if (st->rrdlabels) { + if(rrdlabels_walkthrough_read(st->rrdlabels, send_clabels_callback, wb) > 0) + buffer_sprintf(wb, "CLABEL_COMMIT\n"); } } // Send the current chart definition. // Assumes that collector thread has already called sender_start for mutex / buffer state. -static inline void rrdpush_send_chart_definition_nolock(RRDSET *st) { +static inline bool rrdpush_send_chart_definition(BUFFER *wb, RRDSET *st) { + bool replication_progress = false; + RRDHOST *host = st->rrdhost; rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_EXPOSED); @@ -216,9 +233,9 @@ static inline void rrdpush_send_chart_definition_nolock(RRDSET *st) { // properly set the name for the remote end to parse it char *name = ""; if(likely(st->name)) { - if(unlikely(strcmp(st->id, st->name))) { + if(unlikely(st->id != st->name)) { // they differ - name = strchr(st->name, '.'); + name = strchr(rrdset_name(st), '.'); if(name) name++; else @@ -228,14 +245,14 @@ static inline void rrdpush_send_chart_definition_nolock(RRDSET *st) { // send the chart buffer_sprintf( - host->sender->build + wb , "CHART \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" %ld %d \"%s %s %s %s\" \"%s\" \"%s\"\n" - , st->id + , rrdset_id(st) , name - , st->title - , st->units - , st->family - , st->context + , rrdset_title(st) + , rrdset_units(st) + , rrdset_family(st) + , rrdset_context(st) , rrdset_type_name(st->chart_type) , st->priority , st->update_every @@ -243,120 +260,190 @@ static inline void rrdpush_send_chart_definition_nolock(RRDSET *st) { , rrdset_flag_check(st, RRDSET_FLAG_DETAIL)?"detail":"" , rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST)?"store_first":"" , rrdset_flag_check(st, RRDSET_FLAG_HIDDEN)?"hidden":"" - , (st->plugin_name)?st->plugin_name:"" - , (st->module_name)?st->module_name:"" + , rrdset_plugin_name(st) + , rrdset_module_name(st) ); // send the chart labels - if (host->sender->version >= STREAM_VERSION_CLABELS) - rrdpush_send_clabels(host, st); + if (stream_has_capability(host->sender, STREAM_CAP_CLABELS)) + rrdpush_send_clabels(wb, st); // send the dimensions RRDDIM *rd; rrddim_foreach_read(rd, st) { buffer_sprintf( - host->sender->build + wb , "DIMENSION \"%s\" \"%s\" \"%s\" " COLLECTED_NUMBER_FORMAT " " COLLECTED_NUMBER_FORMAT " \"%s %s %s\"\n" - , rd->id - , rd->name + , rrddim_id(rd) + , rrddim_name(rd) , rrd_algorithm_name(rd->algorithm) , rd->multiplier , rd->divisor , rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)?"obsolete":"" - , rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?"hidden":"" - , rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)?"noreset":"" + , rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN)?"hidden":"" + , rrddim_option_check(rd, RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS)?"noreset":"" ); rd->exposed = 1; } + rrddim_foreach_done(rd); + + // send the chart functions + if(stream_has_capability(host->sender, STREAM_CAP_FUNCTIONS)) + rrd_functions_expose_rrdpush(st, wb); // send the chart local custom variables - RRDSETVAR *rs; - for(rs = st->variables; rs ;rs = rs->next) { - if(unlikely(rs->type == RRDVAR_TYPE_CALCULATED && rs->options & RRDVAR_OPTION_CUSTOM_CHART_VAR)) { - NETDATA_DOUBLE *value = (NETDATA_DOUBLE *) rs->value; - - buffer_sprintf( - host->sender->build - , "VARIABLE CHART %s = " NETDATA_DOUBLE_FORMAT "\n" - , rs->variable - , *value - ); + rrdsetvar_print_to_streaming_custom_chart_variables(st, wb); + + if (stream_has_capability(host->sender, STREAM_CAP_REPLICATION)) { + time_t first_entry_local = rrdset_first_entry_t_of_tier(st, 0); + time_t last_entry_local = st->last_updated.tv_sec; + + if(unlikely(!last_entry_local)) + last_entry_local = rrdset_last_entry_t(st); + + time_t now = now_realtime_sec(); + if(unlikely(last_entry_local > now)) { + internal_error(true, + "RRDSET REPLAY ERROR: 'host:%s/chart:%s' last updated time %ld is in the future, adjusting it to now %ld", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + last_entry_local, now); + last_entry_local = now; + } + + if(unlikely(first_entry_local && last_entry_local && first_entry_local >= last_entry_local)) { + internal_error(true, + "RRDSET REPLAY ERROR: 'host:%s/chart:%s' first updated time %ld is equal or bigger than last updated time %ld, adjusting it last updated time - update every", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + first_entry_local, last_entry_local); + first_entry_local = last_entry_local - st->update_every; + } + + if(unlikely(!first_entry_local && last_entry_local)) { + internal_error(true, + "RRDSET REPLAY ERROR: 'host:%s/chart:%s' first time %ld, last time %ld, setting both to last time", + rrdhost_hostname(st->rrdhost), rrdset_id(st), + first_entry_local, last_entry_local); + first_entry_local = last_entry_local; } + + buffer_sprintf(wb, PLUGINSD_KEYWORD_CHART_DEFINITION_END " %llu %llu %llu\n", + (unsigned long long)first_entry_local, + (unsigned long long)last_entry_local, + (unsigned long long)now); + + rrdset_flag_set(st, RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS); + rrdset_flag_clear(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED); + rrdhost_sender_replicating_charts_plus_one(st->rrdhost); + replication_progress = true; + +#ifdef NETDATA_LOG_REPLICATION_REQUESTS + internal_error(true, "REPLAY: 'host:%s/chart:%s' replication starts", + rrdhost_hostname(st->rrdhost), rrdset_id(st)); +#endif } st->upstream_resync_time = st->last_collected_time.tv_sec + (remote_clock_resync_iterations * st->update_every); + return replication_progress; } // sends the current chart dimensions -static inline void rrdpush_send_chart_metrics_nolock(RRDSET *st, struct sender_state *s) { - RRDHOST *host = st->rrdhost; - buffer_sprintf(host->sender->build, "BEGIN \"%s\" %llu", st->id, (st->last_collected_time.tv_sec > st->upstream_resync_time)?st->usec_since_last_update:0); - if (s->version >= VERSION_GAP_FILLING) - buffer_sprintf(host->sender->build, " %"PRId64"\n", (int64_t)st->last_collected_time.tv_sec); +static void rrdpush_send_chart_metrics(BUFFER *wb, RRDSET *st, struct sender_state *s, RRDSET_FLAGS flags) { + buffer_fast_strcat(wb, "BEGIN \"", 7); + buffer_fast_strcat(wb, rrdset_id(st), string_strlen(st->id)); + buffer_fast_strcat(wb, "\" ", 2); + + if(stream_has_capability(s, STREAM_CAP_REPLICATION) || st->last_collected_time.tv_sec > st->upstream_resync_time) + buffer_print_llu(wb, st->usec_since_last_update); else - buffer_strcat(host->sender->build, "\n"); + buffer_fast_strcat(wb, "0", 1); + + buffer_fast_strcat(wb, "\n", 1); RRDDIM *rd; rrddim_foreach_read(rd, st) { - if(rd->updated && rd->exposed) - buffer_sprintf(host->sender->build - , "SET \"%s\" = " COLLECTED_NUMBER_FORMAT "\n" - , rd->id - , rd->collected_value - ); + if(unlikely(!rd->updated)) + continue; + + if(likely(rd->exposed)) { + buffer_fast_strcat(wb, "SET \"", 5); + buffer_fast_strcat(wb, rrddim_id(rd), string_strlen(rd->id)); + buffer_fast_strcat(wb, "\" = ", 4); + buffer_print_ll(wb, rd->collected_value); + buffer_fast_strcat(wb, "\n", 1); + } + else { + internal_error(true, "STREAM: 'host:%s/chart:%s/dim:%s' flag 'exposed' is updated but not exposed", + rrdhost_hostname(st->rrdhost), rrdset_id(st), rrddim_id(rd)); + // we will include it in the next iteration + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); + } } - buffer_strcat(host->sender->build, "END\n"); + rrddim_foreach_done(rd); + + if(unlikely(flags & RRDSET_FLAG_UPSTREAM_SEND_VARIABLES)) + rrdsetvar_print_to_streaming_custom_chart_variables(st, wb); + + buffer_fast_strcat(wb, "END\n", 4); } static void rrdpush_sender_thread_spawn(RRDHOST *host); // Called from the internal collectors to mark a chart obsolete. -void rrdset_push_chart_definition_now(RRDSET *st) { +bool rrdset_push_chart_definition_now(RRDSET *st) { RRDHOST *host = st->rrdhost; - if(unlikely(!host->rrdpush_send_enabled || !should_send_chart_matching(st))) - return; + if(unlikely(!rrdhost_can_send_definitions_to_parent(host) + || !should_send_chart_matching(st, __atomic_load_n(&st->flags, __ATOMIC_SEQ_CST)))) + return false; + + BUFFER *wb = sender_start(host->sender); + rrdpush_send_chart_definition(wb, st); + sender_commit(host->sender, wb); - rrdset_rdlock(st); - sender_start(host->sender); - rrdpush_send_chart_definition_nolock(st); - sender_commit(host->sender); - rrdset_unlock(st); + return true; } void rrdset_done_push(RRDSET *st) { - if(unlikely(!should_send_chart_matching(st))) - return; - RRDHOST *host = st->rrdhost; - if(unlikely(host->rrdpush_send_enabled && !host->rrdpush_sender_spawn)) - rrdpush_sender_thread_spawn(host); + // fetch the flags we need to check with one atomic operation + RRDHOST_FLAGS host_flags = __atomic_load_n(&host->flags, __ATOMIC_SEQ_CST); + + // check if we are not connected + if(unlikely(!(host_flags & RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS))) { + + if(unlikely(!(host_flags & (RRDHOST_FLAG_RRDPUSH_SENDER_SPAWN | RRDHOST_FLAG_RRDPUSH_RECEIVER_DISCONNECTED)))) + rrdpush_sender_thread_spawn(host); + + if(unlikely(!(host_flags & RRDHOST_FLAG_RRDPUSH_SENDER_LOGGED_STATUS))) { + rrdhost_flag_set(host, RRDHOST_FLAG_RRDPUSH_SENDER_LOGGED_STATUS); + error("STREAM %s [send]: not ready - collected metrics are not sent to parent.", rrdhost_hostname(host)); + } - // Handle non-connected case - if(unlikely(!__atomic_load_n(&host->rrdpush_sender_connected, __ATOMIC_SEQ_CST))) { - if(unlikely(!host->rrdpush_sender_error_shown)) - error("STREAM %s [send]: not ready - discarding collected metrics.", host->hostname); - host->rrdpush_sender_error_shown = 1; return; } - else if(unlikely(host->rrdpush_sender_error_shown)) { - info("STREAM %s [send]: sending metrics...", host->hostname); - host->rrdpush_sender_error_shown = 0; + else if(unlikely(host_flags & RRDHOST_FLAG_RRDPUSH_SENDER_LOGGED_STATUS)) { + info("STREAM %s [send]: sending metrics to parent...", rrdhost_hostname(host)); + rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_LOGGED_STATUS); } - sender_start(host->sender); + RRDSET_FLAGS rrdset_flags = __atomic_load_n(&st->flags, __ATOMIC_SEQ_CST); + bool exposed_upstream = (rrdset_flags & RRDSET_FLAG_UPSTREAM_EXPOSED); + bool replication_in_progress = !(rrdset_flags & RRDSET_FLAG_SENDER_REPLICATION_FINISHED); - if(need_to_send_chart_definition(st)) - rrdpush_send_chart_definition_nolock(st); + if(unlikely((exposed_upstream && replication_in_progress) || + !should_send_chart_matching(st, rrdset_flags))) + return; + + BUFFER *wb = sender_start(host->sender); - rrdpush_send_chart_metrics_nolock(st, host->sender); + if(unlikely(!exposed_upstream)) + replication_in_progress = rrdpush_send_chart_definition(wb, st); - // signal the sender there are more data - if(host->rrdpush_sender_pipe[PIPE_WRITE] != -1 && write(host->rrdpush_sender_pipe[PIPE_WRITE], " ", 1) == -1) - error("STREAM %s [send]: cannot write to internal pipe", host->hostname); + if (likely(!replication_in_progress)) + rrdpush_send_chart_metrics(wb, st, host->sender, rrdset_flags); - sender_commit(host->sender); + sender_commit(host->sender, wb); } // labels @@ -365,45 +452,38 @@ static int send_labels_callback(const char *name, const char *value, RRDLABEL_SR buffer_sprintf(wb, "LABEL \"%s\" = %d \"%s\"\n", name, ls, value); return 1; } -void rrdpush_send_labels(RRDHOST *host) { - if (!host->host_labels || !rrdhost_flag_check(host, RRDHOST_FLAG_STREAM_LABELS_UPDATE) || (rrdhost_flag_check(host, RRDHOST_FLAG_STREAM_LABELS_STOP))) +void rrdpush_send_host_labels(RRDHOST *host) { + if(unlikely(!rrdhost_can_send_definitions_to_parent(host) + || !stream_has_capability(host->sender, STREAM_CAP_HLABELS))) return; - sender_start(host->sender); - - rrdlabels_walkthrough_read(host->host_labels, send_labels_callback, host->sender->build); - buffer_sprintf(host->sender->build, "OVERWRITE %s\n", "labels"); - sender_commit(host->sender); + BUFFER *wb = sender_start(host->sender); - if(host->rrdpush_sender_pipe[PIPE_WRITE] != -1 && write(host->rrdpush_sender_pipe[PIPE_WRITE], " ", 1) == -1) - error("STREAM %s [send]: cannot write to internal pipe", host->hostname); + rrdlabels_walkthrough_read(host->rrdlabels, send_labels_callback, wb); + buffer_sprintf(wb, "OVERWRITE %s\n", "labels"); - rrdhost_flag_clear(host, RRDHOST_FLAG_STREAM_LABELS_UPDATE); + sender_commit(host->sender, wb); } void rrdpush_claimed_id(RRDHOST *host) { - if(unlikely(!host->rrdpush_send_enabled || !__atomic_load_n(&host->rrdpush_sender_connected, __ATOMIC_SEQ_CST))) - return; - - if(host->sender->version < STREAM_VERSION_CLAIM) + if(!stream_has_capability(host->sender, STREAM_CAP_CLAIM)) return; - sender_start(host->sender); + if(unlikely(!rrdhost_can_send_definitions_to_parent(host))) + return; + + BUFFER *wb = sender_start(host->sender); rrdhost_aclk_state_lock(host); - buffer_sprintf(host->sender->build, "CLAIMED_ID %s %s\n", host->machine_guid, (host->aclk_state.claimed_id ? host->aclk_state.claimed_id : "NULL") ); + buffer_sprintf(wb, "CLAIMED_ID %s %s\n", host->machine_guid, (host->aclk_state.claimed_id ? host->aclk_state.claimed_id : "NULL") ); rrdhost_aclk_state_unlock(host); - sender_commit(host->sender); - - // signal the sender there are more data - if(host->rrdpush_sender_pipe[PIPE_WRITE] != -1 && write(host->rrdpush_sender_pipe[PIPE_WRITE], " ", 1) == -1) - error("STREAM %s [send]: cannot write to internal pipe", host->hostname); + sender_commit(host->sender, wb); } int connect_to_one_of_destinations( - struct rrdpush_destinations *destinations, + RRDHOST *host, int default_port, struct timeval *timeout, size_t *reconnects_counter, @@ -413,28 +493,44 @@ int connect_to_one_of_destinations( { int sock = -1; - for (struct rrdpush_destinations *d = destinations; d; d = d->next) { - if (d->disabled_no_proper_reply) { - d->disabled_no_proper_reply = 0; - continue; - } else if (d->disabled_because_of_localhost) { - continue; - } else if (d->disabled_already_streaming && (d->disabled_already_streaming + 30 > now_realtime_sec())) { - continue; - } else if (d->disabled_because_of_denied_access) { - d->disabled_because_of_denied_access = 0; + for (struct rrdpush_destinations *d = host->destinations; d; d = d->next) { + time_t now = now_realtime_sec(); + + if(d->postpone_reconnection_until > now) { + info( + "STREAM %s: skipping destination '%s' (default port: %d) due to last error (code: %d, %s), will retry it in %d seconds", + rrdhost_hostname(host), + string2str(d->destination), + default_port, + d->last_handshake, d->last_error?d->last_error:"unset reason description", + (int)(d->postpone_reconnection_until - now)); + continue; } + info( + "STREAM %s: attempting to connect to '%s' (default port: %d)...", + rrdhost_hostname(host), + string2str(d->destination), + default_port); + if (reconnects_counter) *reconnects_counter += 1; - sock = connect_to_this(d->destination, default_port, timeout); + + sock = connect_to_this(string2str(d->destination), default_port, timeout); + if (sock != -1) { - if (connected_to && connected_to_size) { - strncpy(connected_to, d->destination, connected_to_size); - connected_to[connected_to_size - 1] = '\0'; - } + if (connected_to && connected_to_size) + strncpyz(connected_to, string2str(d->destination), connected_to_size); + *destination = d; + + // move the current item to the end of the list + // without this, this destination will break the loop again and again + // not advancing the destinations to find one that may work + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(host->destinations, d, prev, next); + DOUBLE_LINKED_LIST_APPEND_UNSAFE(host->destinations, d, prev, next); + break; } } @@ -442,44 +538,51 @@ int connect_to_one_of_destinations( return sock; } -struct rrdpush_destinations *destinations_init(const char *dests) { - const char *s = dests; - struct rrdpush_destinations *destinations = NULL, *prev = NULL; - while(*s) { - const char *e = s; - - // skip path, moving both s(tart) and e(nd) - if(*e == '/') - while(!isspace(*e) && *e != ',') s = ++e; - - // skip separators, moving both s(tart) and e(nd) - while(isspace(*e) || *e == ',') s = ++e; - - // move e(nd) to the first separator - while(*e && !isspace(*e) && *e != ',' && *e != '/') e++; - - // is there anything? - if(!*s || s == e) break; - - char buf[e - s + 1]; - strncpyz(buf, s, e - s); - struct rrdpush_destinations *d = callocz(1, sizeof(struct rrdpush_destinations)); - strncpyz(d->destination, buf, sizeof(d->destination)-1); - d->disabled_no_proper_reply = 0; - d->disabled_because_of_localhost = 0; - d->disabled_already_streaming = 0; - d->disabled_because_of_denied_access = 0; - d->next = NULL; - if (!destinations) { - destinations = d; - } else { - prev->next = d; - } - prev = d; +struct destinations_init_tmp { + RRDHOST *host; + struct rrdpush_destinations *list; + int count; +}; + +bool destinations_init_add_one(char *entry, void *data) { + struct destinations_init_tmp *t = data; + + struct rrdpush_destinations *d = callocz(1, sizeof(struct rrdpush_destinations)); + d->destination = string_strdupz(entry); + + DOUBLE_LINKED_LIST_APPEND_UNSAFE(t->list, d, prev, next); + + t->count++; + info("STREAM: added streaming destination No %d: '%s' to host '%s'", t->count, string2str(d->destination), rrdhost_hostname(t->host)); + + return false; // we return false, so that we will get all defined destinations +} + +void rrdpush_destinations_init(RRDHOST *host) { + if(!host->rrdpush_send_destination) return; + + rrdpush_destinations_free(host); + + struct destinations_init_tmp t = { + .host = host, + .list = NULL, + .count = 0, + }; + + foreach_entry_in_connection_string(host->rrdpush_send_destination, destinations_init_add_one, &t); - s = e; + host->destinations = t.list; +} + +void rrdpush_destinations_free(RRDHOST *host) { + while (host->destinations) { + struct rrdpush_destinations *tmp = host->destinations; + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(host->destinations, tmp, prev, next); + string_freez(tmp->destination); + freez(tmp); } - return destinations; + + host->destinations = NULL; } // ---------------------------------------------------------------------------- @@ -495,11 +598,13 @@ void rrdpush_sender_thread_stop(RRDHOST *host) { netdata_mutex_lock(&host->sender->mutex); netdata_thread_t thr = 0; - if(host->rrdpush_sender_spawn) { - info("STREAM %s [send]: signaling sending thread to stop...", host->hostname); + if(rrdhost_flag_check(host, RRDHOST_FLAG_RRDPUSH_SENDER_SPAWN)) { + rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_SPAWN); + + info("STREAM %s [send]: signaling sending thread to stop...", rrdhost_hostname(host)); // signal the thread that we want to join it - host->rrdpush_sender_join = 1; + rrdhost_flag_set(host, RRDHOST_FLAG_RRDPUSH_SENDER_JOIN); // copy the thread id, so that we will be waiting for the right one // even if a new one has been spawn @@ -512,10 +617,10 @@ void rrdpush_sender_thread_stop(RRDHOST *host) { netdata_mutex_unlock(&host->sender->mutex); if(thr != 0) { - info("STREAM %s [send]: waiting for the sending thread to stop...", host->hostname); + info("STREAM %s [send]: waiting for the sending thread to stop...", rrdhost_hostname(host)); void *result; netdata_thread_join(thr, &result); - info("STREAM %s [send]: sending thread has exited.", host->hostname); + info("STREAM %s [send]: sending thread has exited.", rrdhost_hostname(host)); } } @@ -531,15 +636,16 @@ void log_stream_connection(const char *client_ip, const char *client_port, const static void rrdpush_sender_thread_spawn(RRDHOST *host) { netdata_mutex_lock(&host->sender->mutex); - if(!host->rrdpush_sender_spawn) { + if(!rrdhost_flag_check(host, RRDHOST_FLAG_RRDPUSH_SENDER_SPAWN)) { char tag[NETDATA_THREAD_TAG_MAX + 1]; - snprintfz(tag, NETDATA_THREAD_TAG_MAX, "STREAM_SENDER[%s]", host->hostname); + snprintfz(tag, NETDATA_THREAD_TAG_MAX, "STREAM_SENDER[%s]", rrdhost_hostname(host)); if(netdata_thread_create(&host->rrdpush_sender_thread, tag, NETDATA_THREAD_OPTION_JOINABLE, rrdpush_sender_thread, (void *) host->sender)) - error("STREAM %s [send]: failed to create new thread for client.", host->hostname); + error("STREAM %s [send]: failed to create new thread for client.", rrdhost_hostname(host)); else - host->rrdpush_sender_spawn = 1; + rrdhost_flag_set(host, RRDHOST_FLAG_RRDPUSH_SENDER_SPAWN); } + netdata_mutex_unlock(&host->sender->mutex); } @@ -608,7 +714,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { else if(!strcmp(name, "tags")) tags = value; else if(!strcmp(name, "ver")) - stream_version = MIN((uint32_t) strtoul(value, NULL, 0), STREAMING_PROTOCOL_CURRENT_VERSION); + stream_version = convert_stream_version_to_capabilities(strtoul(value, NULL, 0)); else { // An old Netdata child does not have a compatible streaming protocol, map to something sane. if (!strcmp(name, "NETDATA_SYSTEM_OS_NAME")) @@ -624,7 +730,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { else if (!strcmp(name, "NETDATA_SYSTEM_OS_DETECTION")) name = "NETDATA_HOST_OS_DETECTION"; else if(!strcmp(name, "NETDATA_PROTOCOL_VERSION") && stream_version == UINT_MAX) { - stream_version = 1; + stream_version = convert_stream_version_to_capabilities(1); } if (unlikely(rrdhost_set_system_info_variable(system_info, name, value))) { @@ -635,7 +741,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { } if (stream_version == UINT_MAX) - stream_version = 0; + stream_version = convert_stream_version_to_capabilities(0); if(!key || !*key) { rrdhost_system_info_free(system_info); @@ -660,21 +766,30 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { if(regenerate_guid(key, buf) == -1) { rrdhost_system_info_free(system_info); - log_stream_connection(w->client_ip, w->client_port, (key && *key)?key:"-", (machine_guid && *machine_guid)?machine_guid:"-", (hostname && *hostname)?hostname:"-", "ACCESS DENIED - INVALID KEY"); + log_stream_connection(w->client_ip, w->client_port, key, machine_guid, hostname, "ACCESS DENIED - INVALID KEY"); error("STREAM [receive from [%s]:%s]: API key '%s' is not valid GUID (use the command uuidgen to generate one). Forbidding access.", w->client_ip, w->client_port, key); return rrdpush_receiver_permission_denied(w); } if(regenerate_guid(machine_guid, buf) == -1) { rrdhost_system_info_free(system_info); - log_stream_connection(w->client_ip, w->client_port, (key && *key)?key:"-", (machine_guid && *machine_guid)?machine_guid:"-", (hostname && *hostname)?hostname:"-", "ACCESS DENIED - INVALID MACHINE GUID"); + log_stream_connection(w->client_ip, w->client_port, key, machine_guid, hostname, "ACCESS DENIED - INVALID MACHINE GUID"); error("STREAM [receive from [%s]:%s]: machine GUID '%s' is not GUID. Forbidding access.", w->client_ip, w->client_port, machine_guid); return rrdpush_receiver_permission_denied(w); } + const char *api_key_type = appconfig_get(&stream_config, key, "type", "api"); + if(!api_key_type || !*api_key_type) api_key_type = "unknown"; + if(strcmp(api_key_type, "api") != 0) { + rrdhost_system_info_free(system_info); + log_stream_connection(w->client_ip, w->client_port, key, machine_guid, hostname, "ACCESS DENIED - API KEY GIVEN IS NOT API KEY"); + error("STREAM [receive from [%s]:%s]: API key '%s' is a %s GUID. Forbidding access.", w->client_ip, w->client_port, key, api_key_type); + return rrdpush_receiver_permission_denied(w); + } + if(!appconfig_get_boolean(&stream_config, key, "enabled", 0)) { rrdhost_system_info_free(system_info); - log_stream_connection(w->client_ip, w->client_port, (key && *key)?key:"-", (machine_guid && *machine_guid)?machine_guid:"-", (hostname && *hostname)?hostname:"-", "ACCESS DENIED - KEY NOT ENABLED"); + log_stream_connection(w->client_ip, w->client_port, key, machine_guid, hostname, "ACCESS DENIED - KEY NOT ENABLED"); error("STREAM [receive from [%s]:%s]: API key '%s' is not allowed. Forbidding access.", w->client_ip, w->client_port, key); return rrdpush_receiver_permission_denied(w); } @@ -685,7 +800,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { if(!simple_pattern_matches(key_allow_from, w->client_ip)) { simple_pattern_free(key_allow_from); rrdhost_system_info_free(system_info); - log_stream_connection(w->client_ip, w->client_port, (key && *key)?key:"-", (machine_guid && *machine_guid)?machine_guid:"-", (hostname && *hostname) ? hostname : "-", "ACCESS DENIED - KEY NOT ALLOWED FROM THIS IP"); + log_stream_connection(w->client_ip, w->client_port, key, machine_guid, hostname, "ACCESS DENIED - KEY NOT ALLOWED FROM THIS IP"); error("STREAM [receive from [%s]:%s]: API key '%s' is not permitted from this IP. Forbidding access.", w->client_ip, w->client_port, key); return rrdpush_receiver_permission_denied(w); } @@ -693,9 +808,18 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { } } + const char *machine_guid_type = appconfig_get(&stream_config, machine_guid, "type", "machine"); + if(!machine_guid_type || !*machine_guid_type) machine_guid_type = "unknown"; + if(strcmp(machine_guid_type, "machine") != 0) { + rrdhost_system_info_free(system_info); + log_stream_connection(w->client_ip, w->client_port, key, machine_guid, hostname, "ACCESS DENIED - MACHINE GUID GIVEN IS NOT A MACHINE GUID"); + error("STREAM [receive from [%s]:%s]: machine GUID '%s' is a %s GUID. Forbidding access.", w->client_ip, w->client_port, machine_guid, machine_guid_type); + return rrdpush_receiver_permission_denied(w); + } + if(!appconfig_get_boolean(&stream_config, machine_guid, "enabled", 1)) { rrdhost_system_info_free(system_info); - log_stream_connection(w->client_ip, w->client_port, (key && *key)?key:"-", (machine_guid && *machine_guid)?machine_guid:"-", (hostname && *hostname)?hostname:"-", "ACCESS DENIED - MACHINE GUID NOT ENABLED"); + log_stream_connection(w->client_ip, w->client_port, key, machine_guid, hostname, "ACCESS DENIED - MACHINE GUID NOT ENABLED"); error("STREAM [receive from [%s]:%s]: machine GUID '%s' is not allowed. Forbidding access.", w->client_ip, w->client_port, machine_guid); return rrdpush_receiver_permission_denied(w); } @@ -706,7 +830,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { if(!simple_pattern_matches(machine_allow_from, w->client_ip)) { simple_pattern_free(machine_allow_from); rrdhost_system_info_free(system_info); - log_stream_connection(w->client_ip, w->client_port, (key && *key)?key:"-", (machine_guid && *machine_guid)?machine_guid:"-", (hostname && *hostname) ? hostname : "-", "ACCESS DENIED - MACHINE GUID NOT ALLOWED FROM THIS IP"); + log_stream_connection(w->client_ip, w->client_port, key, machine_guid, hostname, "ACCESS DENIED - MACHINE GUID NOT ALLOWED FROM THIS IP"); error("STREAM [receive from [%s]:%s]: Machine GUID '%s' is not permitted from this IP. Forbidding access.", w->client_ip, w->client_port, machine_guid); return rrdpush_receiver_permission_denied(w); } @@ -746,7 +870,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { struct receiver_state *rpt = callocz(1, sizeof(*rpt)); rrd_rdlock(); - RRDHOST *host = rrdhost_find_by_guid(machine_guid, 0); + RRDHOST *host = rrdhost_find_by_guid(machine_guid); if (unlikely(host && rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))) /* Ignore archived hosts. */ host = NULL; if (host) { @@ -763,7 +887,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { info( "STREAM %s [receive from [%s]:%s]: multiple connections for same host detected - " "existing connection is dead (%"PRId64" sec), accepting new connection.", - host->hostname, + rrdhost_hostname(host), w->client_ip, w->client_port, (int64_t)age); @@ -772,12 +896,12 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { netdata_mutex_unlock(&host->receiver_lock); rrdhost_unlock(host); rrd_unlock(); - log_stream_connection(w->client_ip, w->client_port, key, host->machine_guid, host->hostname, + log_stream_connection(w->client_ip, w->client_port, key, host->machine_guid, rrdhost_hostname(host), "REJECTED - ALREADY CONNECTED"); info( "STREAM %s [receive from [%s]:%s]: multiple connections for same host detected - " "existing connection is active (within last %"PRId64" sec), rejecting new connection.", - host->hostname, + rrdhost_hostname(host), w->client_ip, w->client_port, (int64_t)age); @@ -811,7 +935,7 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { rpt->client_port = strdupz(w->client_port); rpt->update_every = update_every; rpt->system_info = system_info; - rpt->stream_version = stream_version; + rpt->capabilities = stream_version; #ifdef ENABLE_HTTPS rpt->ssl.conn = w->ssl.conn; rpt->ssl.flags = w->ssl.flags; @@ -855,3 +979,66 @@ int rrdpush_receiver_thread_spawn(struct web_client *w, char *url) { buffer_flush(w->response.data); return 200; } + +static void stream_capabilities_to_string(BUFFER *wb, STREAM_CAPABILITIES caps) { + if(caps & STREAM_CAP_V1) buffer_strcat(wb, "V1 "); + if(caps & STREAM_CAP_V2) buffer_strcat(wb, "V2 "); + if(caps & STREAM_CAP_VN) buffer_strcat(wb, "VN "); + if(caps & STREAM_CAP_VCAPS) buffer_strcat(wb, "VCAPS "); + if(caps & STREAM_CAP_HLABELS) buffer_strcat(wb, "HLABELS "); + if(caps & STREAM_CAP_CLAIM) buffer_strcat(wb, "CLAIM "); + if(caps & STREAM_CAP_CLABELS) buffer_strcat(wb, "CLABELS "); + if(caps & STREAM_CAP_COMPRESSION) buffer_strcat(wb, "COMPRESSION "); + if(caps & STREAM_CAP_FUNCTIONS) buffer_strcat(wb, "FUNCTIONS "); + if(caps & STREAM_CAP_REPLICATION) buffer_strcat(wb, "REPLICATION "); + if(caps & STREAM_CAP_BINARY) buffer_strcat(wb, "BINARY "); +} + +void log_receiver_capabilities(struct receiver_state *rpt) { + BUFFER *wb = buffer_create(100); + stream_capabilities_to_string(wb, rpt->capabilities); + + info("STREAM %s [receive from [%s]:%s]: established link with negotiated capabilities: %s", + rrdhost_hostname(rpt->host), rpt->client_ip, rpt->client_port, buffer_tostring(wb)); + + buffer_free(wb); +} + +void log_sender_capabilities(struct sender_state *s) { + BUFFER *wb = buffer_create(100); + stream_capabilities_to_string(wb, s->capabilities); + + info("STREAM %s [send to %s]: established link with negotiated capabilities: %s", + rrdhost_hostname(s->host), s->connected_to, buffer_tostring(wb)); + + buffer_free(wb); +} + +STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version) { + STREAM_CAPABILITIES caps = 0; + + if(version <= 1) caps = STREAM_CAP_V1; + else if(version < STREAM_OLD_VERSION_CLAIM) caps = STREAM_CAP_V2 | STREAM_CAP_HLABELS; + else if(version <= STREAM_OLD_VERSION_CLAIM) caps = STREAM_CAP_VN | STREAM_CAP_HLABELS | STREAM_CAP_CLAIM; + else if(version <= STREAM_OLD_VERSION_CLABELS) caps = STREAM_CAP_VN | STREAM_CAP_HLABELS | STREAM_CAP_CLAIM | STREAM_CAP_CLABELS; + else if(version <= STREAM_OLD_VERSION_COMPRESSION) caps = STREAM_CAP_VN | STREAM_CAP_HLABELS | STREAM_CAP_CLAIM | STREAM_CAP_CLABELS | STREAM_HAS_COMPRESSION; + else caps = version; + + if(caps & STREAM_CAP_VCAPS) + caps &= ~(STREAM_CAP_V1|STREAM_CAP_V2|STREAM_CAP_VN); + + if(caps & STREAM_CAP_VN) + caps &= ~(STREAM_CAP_V1|STREAM_CAP_V2); + + if(caps & STREAM_CAP_V2) + caps &= ~(STREAM_CAP_V1); + + return caps & STREAM_OUR_CAPABILITIES; +} + +int32_t stream_capabilities_to_vn(uint32_t caps) { + if(caps & STREAM_CAP_COMPRESSION) return STREAM_OLD_VERSION_COMPRESSION; + if(caps & STREAM_CAP_CLABELS) return STREAM_OLD_VERSION_CLABELS; + return STREAM_OLD_VERSION_CLAIM; // if(caps & STREAM_CAP_CLAIM) +} + diff --git a/streaming/rrdpush.h b/streaming/rrdpush.h index 1eb39cc6c..c5f7618c1 100644 --- a/streaming/rrdpush.h +++ b/streaming/rrdpush.h @@ -10,32 +10,83 @@ #define CONNECTED_TO_SIZE 100 -#define STREAM_VERSION_CLAIM 3 -#define STREAM_VERSION_CLABELS 4 -#define STREAM_VERSION_COMPRESSION 5 -#define VERSION_GAP_FILLING 6 +// ---------------------------------------------------------------------------- +// obsolete versions - do not use anymore + +#define STREAM_OLD_VERSION_CLAIM 3 +#define STREAM_OLD_VERSION_CLABELS 4 +#define STREAM_OLD_VERSION_COMPRESSION 5 // this is production + +// ---------------------------------------------------------------------------- +// capabilities negotiation + +typedef enum { + // do not use the first 3 bits + STREAM_CAP_V1 = (1 << 3), // v1 = the oldest protocol + STREAM_CAP_V2 = (1 << 4), // v2 = the second version of the protocol (with host labels) + STREAM_CAP_VN = (1 << 5), // version negotiation supported (for versions 3, 4, 5 of the protocol) + // v3 = claiming supported + // v4 = chart labels supported + // v5 = lz4 compression supported + STREAM_CAP_VCAPS = (1 << 6), // capabilities negotiation supported + STREAM_CAP_HLABELS = (1 << 7), // host labels supported + STREAM_CAP_CLAIM = (1 << 8), // claiming supported + STREAM_CAP_CLABELS = (1 << 9), // chart labels supported + STREAM_CAP_COMPRESSION = (1 << 10), // lz4 compression supported + STREAM_CAP_FUNCTIONS = (1 << 11), // plugin functions supported + STREAM_CAP_REPLICATION = (1 << 12), // replication supported + STREAM_CAP_BINARY = (1 << 13), // streaming supports binary data + + // this must be signed int, so don't use the last bit + // needed for negotiating errors between parent and child +} STREAM_CAPABILITIES; #ifdef ENABLE_COMPRESSION -#define STREAMING_PROTOCOL_CURRENT_VERSION (uint32_t)(STREAM_VERSION_COMPRESSION) +#define STREAM_HAS_COMPRESSION STREAM_CAP_COMPRESSION #else -#define STREAMING_PROTOCOL_CURRENT_VERSION (uint32_t)(STREAM_VERSION_CLABELS) -#endif //ENABLE_COMPRESSION +#define STREAM_HAS_COMPRESSION 0 +#endif // ENABLE_COMPRESSION + +#define STREAM_OUR_CAPABILITIES ( \ + STREAM_CAP_V1 | STREAM_CAP_V2 | STREAM_CAP_VN | STREAM_CAP_VCAPS | \ + STREAM_CAP_HLABELS | STREAM_CAP_CLAIM | STREAM_CAP_CLABELS | \ + STREAM_HAS_COMPRESSION | STREAM_CAP_FUNCTIONS | STREAM_CAP_REPLICATION | STREAM_CAP_BINARY ) + +#define stream_has_capability(rpt, capability) ((rpt) && ((rpt)->capabilities & (capability))) + +// ---------------------------------------------------------------------------- +// stream handshake + +#define HTTP_HEADER_SIZE 8192 #define STREAMING_PROTOCOL_VERSION "1.1" -#define START_STREAMING_PROMPT "Hit me baby, push them over..." -#define START_STREAMING_PROMPT_V2 "Hit me baby, push them over and bring the host labels..." +#define START_STREAMING_PROMPT_V1 "Hit me baby, push them over..." +#define START_STREAMING_PROMPT_V2 "Hit me baby, push them over and bring the host labels..." #define START_STREAMING_PROMPT_VN "Hit me baby, push them over with the version=" #define START_STREAMING_ERROR_SAME_LOCALHOST "Don't hit me baby, you are trying to stream my localhost back" #define START_STREAMING_ERROR_ALREADY_STREAMING "This GUID is already streaming to this server" #define START_STREAMING_ERROR_NOT_PERMITTED "You are not permitted to access this. Check the logs for more info." -#define HTTP_HEADER_SIZE 8192 - typedef enum { - RRDPUSH_MULTIPLE_CONNECTIONS_ALLOW, - RRDPUSH_MULTIPLE_CONNECTIONS_DENY_NEW -} RRDPUSH_MULTIPLE_CONNECTIONS_STRATEGY; + STREAM_HANDSHAKE_OK_V5 = 5, // COMPRESSION + STREAM_HANDSHAKE_OK_V4 = 4, // CLABELS + STREAM_HANDSHAKE_OK_V3 = 3, // CLAIM + STREAM_HANDSHAKE_OK_V2 = 2, // HLABELS + STREAM_HANDSHAKE_OK_V1 = 1, + STREAM_HANDSHAKE_ERROR_BAD_HANDSHAKE = -1, + STREAM_HANDSHAKE_ERROR_LOCALHOST = -2, + STREAM_HANDSHAKE_ERROR_ALREADY_CONNECTED = -3, + STREAM_HANDSHAKE_ERROR_DENIED = -4, + STREAM_HANDSHAKE_ERROR_SEND_TIMEOUT = -5, + STREAM_HANDSHAKE_ERROR_RECEIVE_TIMEOUT = -6, + STREAM_HANDSHAKE_ERROR_INVALID_CERTIFICATE = -7, + STREAM_HANDSHAKE_ERROR_SSL_ERROR = -8, + STREAM_HANDSHAKE_ERROR_CANT_CONNECT = -9 +} STREAM_HANDSHAKE; + + +// ---------------------------------------------------------------------------- typedef struct { char *os_name; @@ -47,8 +98,8 @@ typedef struct { #ifdef ENABLE_COMPRESSION struct compressor_state { - char *buffer; - size_t buffer_size; + char *compression_result_buffer; + size_t compression_result_buffer_size; struct compressor_data *data; // Compression API specific data void (*reset)(struct compressor_state *state); size_t (*compress)(struct compressor_state *state, const char *data, size_t size, char **buffer); @@ -56,21 +107,14 @@ struct compressor_state { }; struct decompressor_state { - char *buffer; - size_t buffer_size; - size_t buffer_len; - size_t buffer_pos; - char *out_buffer; - size_t out_buffer_len; - size_t out_buffer_pos; + size_t signature_size; size_t total_compressed; size_t total_uncompressed; size_t packet_count; - struct decompressor_data *data; // Decompression API specific data + struct decompressor_stream *stream; // Decompression API specific data void (*reset)(struct decompressor_state *state); size_t (*start)(struct decompressor_state *state, const char *header, size_t header_size); - size_t (*put)(struct decompressor_state *state, const char *data, size_t size); - size_t (*decompress)(struct decompressor_state *state); + size_t (*decompress)(struct decompressor_state *state, const char *compressed_data, size_t compressed_size); size_t (*decompressed_bytes_in_buffer)(struct decompressor_state *state); size_t (*get)(struct decompressor_state *state, char *data, size_t size); void (*destroy)(struct decompressor_state **state); @@ -80,11 +124,17 @@ struct decompressor_state { // Thread-local storage // Metric transmission: collector threads asynchronously fill the buffer, sender thread uses it. +typedef enum { + SENDER_FLAG_OVERFLOW = (1 << 0), // The buffer has been overflown + SENDER_FLAG_COMPRESSION = (1 << 1), // The stream needs to have and has compression +} SENDER_FLAGS; + struct sender_state { RRDHOST *host; - pid_t task_id; - unsigned int overflow:1; - int timeout, default_port; + pid_t tid; // the thread id of the sender, from gettid() + SENDER_FLAGS flags; + int timeout; + int default_port; usec_t reconnect_delay; char connected_to[CONNECTED_TO_SIZE + 1]; // We don't know which proxy we connect to, passed back from socket.c size_t begin; @@ -92,22 +142,62 @@ struct sender_state { size_t sent_bytes; size_t sent_bytes_on_this_connection; size_t send_attempts; - time_t last_sent_t; + time_t last_traffic_seen_t; size_t not_connected_loops; // Metrics are collected asynchronously by collector threads calling rrdset_done_push(). This can also trigger // the lazy creation of the sender thread - both cases (buffer access and thread creation) are guarded here. netdata_mutex_t mutex; struct circular_buffer *buffer; - BUFFER *build; - char read_buffer[512]; + char read_buffer[PLUGINSD_LINE_MAX + 1]; int read_len; - int32_t version; + STREAM_CAPABILITIES capabilities; + + int rrdpush_sender_pipe[2]; // collector to sender thread signaling + int rrdpush_sender_socket; + #ifdef ENABLE_COMPRESSION - unsigned int rrdpush_compression; struct compressor_state *compressor; #endif +#ifdef ENABLE_HTTPS + struct netdata_ssl ssl; // structure used to encrypt the connection +#endif + + struct { + DICTIONARY *requests; // de-duplication of replication requests, per chart + + struct { + size_t pending_requests; // the currently outstanding replication requests + size_t charts_replicating; // the number of unique charts having pending replication requests (on every request one is added and is removed when we finish it - it does not track completion of the replication for this chart) + } atomic; + + struct { + bool reached_max; // used to avoid resetting the replication thread too frequently + } unsafe; // protected by sender mutex + + } replication; + + struct { + size_t buffer_used_percentage; // the current utilization of the sending buffer + usec_t last_flush_time_ut; // the last time the sender flushed the sending buffer in USEC + } atomic; }; +#define rrdpush_sender_set_buffer_used_percent(sender, value) __atomic_store_n(&((sender)->atomic.buffer_used_percentage), value, __ATOMIC_RELAXED); +#define rrdpush_sender_get_buffer_used_percent(sender) __atomic_load_n(&((sender)->atomic.buffer_used_percentage), __ATOMIC_RELAXED) + +#define rrdpush_sender_set_flush_time(sender) __atomic_store_n(&((sender)->atomic.last_flush_time_ut), now_realtime_usec(), __ATOMIC_RELAXED); +#define rrdpush_sender_get_flush_time(sender) __atomic_load_n(&((sender)->atomic.last_flush_time_ut), __ATOMIC_RELAXED) + +#define rrdpush_sender_replicating_charts(sender) __atomic_load_n(&((sender)->replication.atomic.charts_replicating), __ATOMIC_RELAXED) +#define rrdpush_sender_replicating_charts_plus_one(sender) __atomic_add_fetch(&((sender)->replication.atomic.charts_replicating), 1, __ATOMIC_RELAXED) +#define rrdpush_sender_replicating_charts_minus_one(sender) __atomic_sub_fetch(&((sender)->replication.atomic.charts_replicating), 1, __ATOMIC_RELAXED) +#define rrdpush_sender_replicating_charts_zero(sender) __atomic_store_n(&((sender)->replication.atomic.charts_replicating), 0, __ATOMIC_RELAXED) + +#define rrdpush_sender_pending_replication_requests(sender) __atomic_load_n(&((sender)->replication.atomic.pending_requests), __ATOMIC_RELAXED) +#define rrdpush_sender_pending_replication_requests_plus_one(sender) __atomic_add_fetch(&((sender)->replication.atomic.pending_requests), 1, __ATOMIC_RELAXED) +#define rrdpush_sender_pending_replication_requests_minus_one(sender) __atomic_sub_fetch(&((sender)->replication.atomic.pending_requests), 1, __ATOMIC_RELAXED) +#define rrdpush_sender_pending_replication_requests_zero(sender) __atomic_store_n(&((sender)->replication.atomic.pending_requests), 0, __ATOMIC_RELAXED) + struct receiver_state { RRDHOST *host; netdata_thread_t thread; @@ -127,9 +217,9 @@ struct receiver_state { char *program_version; struct rrdhost_system_info *system_info; int update_every; - uint32_t stream_version; + STREAM_CAPABILITIES capabilities; time_t last_msg_t; - char read_buffer[1024]; // Need to allow RRD_ID_LENGTH_MAX * 4 + the other fields + char read_buffer[PLUGINSD_LINE_MAX + 1]; int read_len; unsigned int shutdown:1; // Tell the thread to exit unsigned int exited; // Indicates that the thread has exited (NOT A BITFIELD!) @@ -140,14 +230,18 @@ struct receiver_state { unsigned int rrdpush_compression; struct decompressor_state *decompressor; #endif + + time_t replication_first_time_t; }; struct rrdpush_destinations { - char destination[CONNECTED_TO_SIZE + 1]; - int disabled_no_proper_reply; - int disabled_because_of_localhost; - time_t disabled_already_streaming; - int disabled_because_of_denied_access; + STRING *destination; + + const char *last_error; + time_t postpone_reconnection_until; + STREAM_HANDSHAKE last_handshake; + + struct rrdpush_destinations *prev; struct rrdpush_destinations *next; }; @@ -158,27 +252,35 @@ extern unsigned int default_compression_enabled; extern char *default_rrdpush_destination; extern char *default_rrdpush_api_key; extern char *default_rrdpush_send_charts_matching; +extern bool default_rrdpush_enable_replication; +extern time_t default_rrdpush_seconds_to_replicate; +extern time_t default_rrdpush_replication_step; extern unsigned int remote_clock_resync_iterations; -extern void sender_init(RRDHOST *parent); -extern struct rrdpush_destinations *destinations_init(const char *destinations); -void sender_start(struct sender_state *s); -void sender_commit(struct sender_state *s); -extern int rrdpush_init(); -extern int configured_as_parent(); -extern void rrdset_done_push(RRDSET *st); -extern void rrdset_push_chart_definition_now(RRDSET *st); -extern void *rrdpush_sender_thread(void *ptr); -extern void rrdpush_send_labels(RRDHOST *host); -extern void rrdpush_claimed_id(RRDHOST *host); - -extern int rrdpush_receiver_thread_spawn(struct web_client *w, char *url); -extern void rrdpush_sender_thread_stop(RRDHOST *host); - -extern void rrdpush_sender_send_this_host_variable_now(RRDHOST *host, RRDVAR *rv); -extern void log_stream_connection(const char *client_ip, const char *client_port, const char *api_key, const char *machine_guid, const char *host, const char *msg); -extern int connect_to_one_of_destinations( - struct rrdpush_destinations *destinations, +void rrdpush_destinations_init(RRDHOST *host); +void rrdpush_destinations_free(RRDHOST *host); + +void sender_init(RRDHOST *host); + +BUFFER *sender_start(struct sender_state *s); +void sender_commit(struct sender_state *s, BUFFER *wb); +void sender_cancel(struct sender_state *s); +int rrdpush_init(); +bool rrdpush_receiver_needs_dbengine(); +int configured_as_parent(); +void rrdset_done_push(RRDSET *st); +bool rrdset_push_chart_definition_now(RRDSET *st); +void *rrdpush_sender_thread(void *ptr); +void rrdpush_send_host_labels(RRDHOST *host); +void rrdpush_claimed_id(RRDHOST *host); + +int rrdpush_receiver_thread_spawn(struct web_client *w, char *url); +void rrdpush_sender_thread_stop(RRDHOST *host); + +void rrdpush_sender_send_this_host_variable_now(RRDHOST *host, const RRDVAR_ACQUIRED *rva); +void log_stream_connection(const char *client_ip, const char *client_port, const char *api_key, const char *machine_guid, const char *host, const char *msg); +int connect_to_one_of_destinations( + RRDHOST *host, int default_port, struct timeval *timeout, size_t *reconnects_counter, @@ -186,10 +288,18 @@ extern int connect_to_one_of_destinations( size_t connected_to_size, struct rrdpush_destinations **destination); +void rrdpush_signal_sender_to_wake_up(struct sender_state *s); + #ifdef ENABLE_COMPRESSION struct compressor_state *create_compressor(); struct decompressor_state *create_decompressor(); -size_t is_compressed_data(const char *data, size_t data_size); #endif +void log_receiver_capabilities(struct receiver_state *rpt); +void log_sender_capabilities(struct sender_state *s); +STREAM_CAPABILITIES convert_stream_version_to_capabilities(int32_t version); +int32_t stream_capabilities_to_vn(uint32_t caps); + +#include "replication.h" + #endif //NETDATA_RRDPUSH_H diff --git a/streaming/sender.c b/streaming/sender.c index c4836aeaf..8e637d2bd 100644 --- a/streaming/sender.c +++ b/streaming/sender.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "rrdpush.h" +#include "parser/parser.h" #define WORKER_SENDER_JOB_CONNECT 0 #define WORKER_SENDER_JOB_PIPE_READ 1 @@ -17,9 +18,15 @@ #define WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR 12 #define WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR 13 #define WORKER_SENDER_JOB_DISCONNECT_NO_COMPRESSION 14 - -#if WORKER_UTILIZATION_MAX_JOB_TYPES < 15 -#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 15 +#define WORKER_SENDER_JOB_BUFFER_RATIO 15 +#define WORKER_SENDER_JOB_BYTES_RECEIVED 16 +#define WORKER_SENDER_JOB_BYTES_SENT 17 +#define WORKER_SENDER_JOB_REPLAY_REQUEST 18 +#define WORKER_SENDER_JOB_FUNCTION_REQUEST 19 +#define WORKER_SENDER_JOB_REPLAY_DICT_SIZE 20 + +#if WORKER_UTILIZATION_MAX_JOB_TYPES < 21 +#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 21 #endif extern struct config stream_config; @@ -27,10 +34,31 @@ extern int netdata_use_ssl_on_stream; extern char *netdata_ssl_ca_path; extern char *netdata_ssl_ca_file; +static __thread BUFFER *sender_thread_buffer = NULL; +static __thread bool sender_thread_buffer_used = false; + +void sender_thread_buffer_free(void) { + if(sender_thread_buffer) { + buffer_free(sender_thread_buffer); + sender_thread_buffer = NULL; + } +} + // Collector thread starting a transmission -void sender_start(struct sender_state *s) { - netdata_mutex_lock(&s->mutex); - buffer_flush(s->build); +BUFFER *sender_start(struct sender_state *s __maybe_unused) { + if(!sender_thread_buffer) + sender_thread_buffer = buffer_create(1024); + + if(sender_thread_buffer_used) + fatal("STREAMING: thread buffer is used multiple times concurrently."); + + sender_thread_buffer_used = true; + buffer_flush(sender_thread_buffer); + return sender_thread_buffer; +} + +void sender_cancel(struct sender_state *s __maybe_unused) { + sender_thread_buffer_used = false; } static inline void rrdpush_sender_thread_close_socket(RRDHOST *host); @@ -43,137 +71,218 @@ static inline void rrdpush_sender_thread_close_socket(RRDHOST *host); */ static inline void deactivate_compression(struct sender_state *s) { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_NO_COMPRESSION); - error("STREAM_COMPRESSION: Deactivating compression to avoid stream corruption"); - default_compression_enabled = 0; - s->rrdpush_compression = 0; - s->version = STREAM_VERSION_CLABELS; - error("STREAM_COMPRESSION %s [send to %s]: Restarting connection without compression", s->host->hostname, s->connected_to); + error("STREAM_COMPRESSION: Compression returned error, disabling it."); + s->flags &= ~SENDER_FLAG_COMPRESSION; + error("STREAM %s [send to %s]: Restarting connection without compression.", rrdhost_hostname(s->host), s->connected_to); rrdpush_sender_thread_close_socket(s->host); } #endif +#define SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE 3 + // Collector thread finishing a transmission -void sender_commit(struct sender_state *s) { - char *src = (char *)buffer_tostring(s->host->sender->build); - size_t src_len = s->host->sender->build->len; +void sender_commit(struct sender_state *s, BUFFER *wb) { + + if(unlikely(wb != sender_thread_buffer)) + fatal("STREAMING: sender is trying to commit a buffer that is not this thread's buffer."); + + if(unlikely(!sender_thread_buffer_used)) + fatal("STREAMING: sender is committing a buffer twice."); + + sender_thread_buffer_used = false; + + char *src = (char *)buffer_tostring(wb); + size_t src_len = buffer_strlen(wb); + + if(unlikely(!src || !src_len)) + return; + + netdata_mutex_lock(&s->mutex); + + if(unlikely(s->host->sender->buffer->max_size < (src_len + 1) * SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE)) { + info("STREAM %s [send to %s]: max buffer size of %zu is too small for a data message of size %zu. Increasing the max buffer size to %d times the max data message size.", + rrdhost_hostname(s->host), s->connected_to, s->host->sender->buffer->max_size, buffer_strlen(wb) + 1, SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE); + + s->host->sender->buffer->max_size = (src_len + 1) * SENDER_BUFFER_ADAPT_TO_TIMES_MAX_SIZE; + } + #ifdef ENABLE_COMPRESSION - if (src && src_len) { - if (s->compressor && s->rrdpush_compression) { - src_len = s->compressor->compress(s->compressor, src, src_len, &src); - if (!src_len) { - deactivate_compression(s); - buffer_flush(s->build); - netdata_mutex_unlock(&s->mutex); - return; + if (stream_has_capability(s, STREAM_CAP_COMPRESSION) && s->compressor) { + while(src_len) { + size_t size_to_compress = src_len; + + if(unlikely(size_to_compress > COMPRESSION_MAX_MSG_SIZE)) { + if (stream_has_capability(s, STREAM_CAP_BINARY)) + size_to_compress = COMPRESSION_MAX_MSG_SIZE; + else { + if (size_to_compress > COMPRESSION_MAX_MSG_SIZE) { + // we need to find the last newline + // so that the decompressor will have a whole line to work with + + const char *t = &src[COMPRESSION_MAX_MSG_SIZE]; + while (--t >= src) + if (unlikely(*t == '\n')) + break; + + if (t <= src) { + size_to_compress = COMPRESSION_MAX_MSG_SIZE; + } else + size_to_compress = t - src + 1; + } + } } + + char *dst; + size_t dst_len = s->compressor->compress(s->compressor, src, size_to_compress, &dst); + if (!dst_len) { + error("STREAM %s [send to %s]: COMPRESSION failed. Resetting compressor and re-trying", + rrdhost_hostname(s->host), s->connected_to); + + s->compressor->reset(s->compressor); + dst_len = s->compressor->compress(s->compressor, src, size_to_compress, &dst); + if(!dst_len) { + error("STREAM %s [send to %s]: COMPRESSION failed again. Deactivating compression", + rrdhost_hostname(s->host), s->connected_to); + + deactivate_compression(s); + netdata_mutex_unlock(&s->mutex); + return; + } + } + + if(cbuffer_add_unsafe(s->host->sender->buffer, dst, dst_len)) + s->flags |= SENDER_FLAG_OVERFLOW; + + src = src + size_to_compress; + src_len -= size_to_compress; } - if(cbuffer_add_unsafe(s->host->sender->buffer, src, src_len)) - s->overflow = 1; } + else if(cbuffer_add_unsafe(s->host->sender->buffer, src, src_len)) + s->flags |= SENDER_FLAG_OVERFLOW; #else if(cbuffer_add_unsafe(s->host->sender->buffer, src, src_len)) - s->overflow = 1; + s->flags |= SENDER_FLAG_OVERFLOW; #endif - buffer_flush(s->build); - netdata_mutex_unlock(&s->mutex); -} + replication_recalculate_buffer_used_ratio_unsafe(s); -static inline void rrdpush_sender_thread_close_socket(RRDHOST *host) { - __atomic_clear(&host->rrdpush_sender_connected, __ATOMIC_SEQ_CST); - - if(host->rrdpush_sender_socket != -1) { - close(host->rrdpush_sender_socket); - host->rrdpush_sender_socket = -1; - } + netdata_mutex_unlock(&s->mutex); + rrdpush_signal_sender_to_wake_up(s); } -static inline void rrdpush_sender_add_host_variable_to_buffer_nolock(RRDHOST *host, RRDVAR *rv) { - NETDATA_DOUBLE *value = (NETDATA_DOUBLE *)rv->value; - +static inline void rrdpush_sender_add_host_variable_to_buffer(BUFFER *wb, const RRDVAR_ACQUIRED *rva) { buffer_sprintf( - host->sender->build + wb , "VARIABLE HOST %s = " NETDATA_DOUBLE_FORMAT "\n" - , rv->name - , *value + , rrdvar_name(rva) + , rrdvar2number(rva) ); - debug(D_STREAM, "RRDVAR pushed HOST VARIABLE %s = " NETDATA_DOUBLE_FORMAT, rv->name, *value); + debug(D_STREAM, "RRDVAR pushed HOST VARIABLE %s = " NETDATA_DOUBLE_FORMAT, rrdvar_name(rva), rrdvar2number(rva)); } -void rrdpush_sender_send_this_host_variable_now(RRDHOST *host, RRDVAR *rv) { - if(host->rrdpush_send_enabled && host->rrdpush_sender_spawn && __atomic_load_n(&host->rrdpush_sender_connected, __ATOMIC_SEQ_CST)) { - sender_start(host->sender); - rrdpush_sender_add_host_variable_to_buffer_nolock(host, rv); - sender_commit(host->sender); +void rrdpush_sender_send_this_host_variable_now(RRDHOST *host, const RRDVAR_ACQUIRED *rva) { + if(rrdhost_can_send_definitions_to_parent(host)) { + BUFFER *wb = sender_start(host->sender); + rrdpush_sender_add_host_variable_to_buffer(wb, rva); + sender_commit(host->sender, wb); } } +struct custom_host_variables_callback { + BUFFER *wb; +}; -static int rrdpush_sender_thread_custom_host_variables_callback(void *rrdvar_ptr, void *host_ptr) { - RRDVAR *rv = (RRDVAR *)rrdvar_ptr; - RRDHOST *host = (RRDHOST *)host_ptr; +static int rrdpush_sender_thread_custom_host_variables_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdvar_ptr __maybe_unused, void *struct_ptr) { + const RRDVAR_ACQUIRED *rv = (const RRDVAR_ACQUIRED *)item; + struct custom_host_variables_callback *tmp = struct_ptr; + BUFFER *wb = tmp->wb; - if(unlikely(rv->options & RRDVAR_OPTION_CUSTOM_HOST_VAR && rv->type == RRDVAR_TYPE_CALCULATED)) { - rrdpush_sender_add_host_variable_to_buffer_nolock(host, rv); - - // return 1, so that the traversal will return the number of variables sent + if(unlikely(rrdvar_flags(rv) & RRDVAR_FLAG_CUSTOM_HOST_VAR && rrdvar_type(rv) == RRDVAR_TYPE_CALCULATED)) { + rrdpush_sender_add_host_variable_to_buffer(wb, rv); return 1; } - - // returning a negative number will break the traversal return 0; } static void rrdpush_sender_thread_send_custom_host_variables(RRDHOST *host) { - sender_start(host->sender); - int ret = rrdvar_callback_for_all_host_variables(host, rrdpush_sender_thread_custom_host_variables_callback, host); - (void)ret; - sender_commit(host->sender); - - debug(D_STREAM, "RRDVAR sent %d VARIABLES", ret); + if(rrdhost_can_send_definitions_to_parent(host)) { + BUFFER *wb = sender_start(host->sender); + struct custom_host_variables_callback tmp = { + .wb = wb + }; + int ret = rrdvar_walkthrough_read(host->rrdvars, rrdpush_sender_thread_custom_host_variables_callback, &tmp); + (void)ret; + sender_commit(host->sender, wb); + + debug(D_STREAM, "RRDVAR sent %d VARIABLES", ret); + } } // resets all the chart, so that their definitions // will be resent to the central netdata static void rrdpush_sender_thread_reset_all_charts(RRDHOST *host) { - rrdhost_rdlock(host); + error("Clearing stream_collected_metrics flag in charts of host %s", rrdhost_hostname(host)); RRDSET *st; rrdset_foreach_read(st, host) { - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED); + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_EXPOSED | RRDSET_FLAG_SENDER_REPLICATION_IN_PROGRESS); + rrdset_flag_set(st, RRDSET_FLAG_SENDER_REPLICATION_FINISHED); st->upstream_resync_time = 0; - rrdset_rdlock(st); - RRDDIM *rd; rrddim_foreach_read(rd, st) rd->exposed = 0; - - rrdset_unlock(st); + rrddim_foreach_done(rd); } + rrdset_foreach_done(st); - rrdhost_unlock(host); + rrdhost_sender_replicating_charts_zero(host); } -static inline void rrdpush_sender_thread_data_flush(RRDHOST *host) { +static void rrdpush_sender_cbuffer_flush(RRDHOST *host) { + rrdpush_sender_set_flush_time(host->sender); + netdata_mutex_lock(&host->sender->mutex); - size_t len = cbuffer_next_unsafe(host->sender->buffer, NULL); - if (len) - error("STREAM %s [send]: discarding %zu bytes of metrics already in the buffer.", host->hostname, len); + // flush the output buffer from any data it may have + cbuffer_flush(host->sender->buffer); + replication_recalculate_buffer_used_ratio_unsafe(host->sender); - cbuffer_remove_unsafe(host->sender->buffer, len); netdata_mutex_unlock(&host->sender->mutex); +} + +static void rrdpush_sender_charts_and_replication_reset(RRDHOST *host) { + rrdpush_sender_set_flush_time(host->sender); + + // stop all replication commands inflight + replication_sender_delete_pending_requests(host->sender); + // reset the state of all charts rrdpush_sender_thread_reset_all_charts(host); + + rrdpush_sender_replicating_charts_zero(host->sender); +} + +static void rrdpush_sender_on_connect(RRDHOST *host) { + rrdpush_sender_cbuffer_flush(host); + rrdpush_sender_charts_and_replication_reset(host); rrdpush_sender_thread_send_custom_host_variables(host); } -static inline void rrdpush_set_flags_to_newest_stream(RRDHOST *host) { - rrdhost_flag_set(host, RRDHOST_FLAG_STREAM_LABELS_UPDATE); - rrdhost_flag_clear(host, RRDHOST_FLAG_STREAM_LABELS_STOP); +static inline void rrdpush_sender_thread_close_socket(RRDHOST *host) { + if(host->sender->rrdpush_sender_socket != -1) { + close(host->sender->rrdpush_sender_socket); + host->sender->rrdpush_sender_socket = -1; + } + + rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS); + rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED); + + // do not flush the circular buffer here + // this function is called sometimes with the mutex lock, sometimes without the lock + rrdpush_sender_charts_and_replication_reset(host); } void rrdpush_encode_variable(stream_encoded_t *se, RRDHOST *host) @@ -203,52 +312,123 @@ void rrdpush_clean_encoded(stream_encoded_t *se) freez(se->kernel_version); } -static inline long int parse_stream_version_for_errors(char *http) -{ - if (!memcmp(http, START_STREAMING_ERROR_SAME_LOCALHOST, sizeof(START_STREAMING_ERROR_SAME_LOCALHOST))) - return -2; - else if (!memcmp(http, START_STREAMING_ERROR_ALREADY_STREAMING, sizeof(START_STREAMING_ERROR_ALREADY_STREAMING))) - return -3; - else if (!memcmp(http, START_STREAMING_ERROR_NOT_PERMITTED, sizeof(START_STREAMING_ERROR_NOT_PERMITTED))) - return -4; - else - return -1; -} +struct { + const char *response; + size_t length; + int32_t version; + bool dynamic; + const char *error; + int worker_job_id; + time_t postpone_reconnect_seconds; +} stream_responses[] = { + { + .response = START_STREAMING_PROMPT_VN, + .length = sizeof(START_STREAMING_PROMPT_VN) - 1, + .version = STREAM_HANDSHAKE_OK_V3, // and above + .dynamic = true, // dynamic = we will parse the version / capabilities + .error = NULL, + .worker_job_id = 0, + .postpone_reconnect_seconds = 0, + }, + { + .response = START_STREAMING_PROMPT_V2, + .length = sizeof(START_STREAMING_PROMPT_V2) - 1, + .version = STREAM_HANDSHAKE_OK_V2, + .dynamic = false, + .error = NULL, + .worker_job_id = 0, + .postpone_reconnect_seconds = 0, + }, + { + .response = START_STREAMING_PROMPT_V1, + .length = sizeof(START_STREAMING_PROMPT_V1) - 1, + .version = STREAM_HANDSHAKE_OK_V1, + .dynamic = false, + .error = NULL, + .worker_job_id = 0, + .postpone_reconnect_seconds = 0, + }, + { + .response = START_STREAMING_ERROR_SAME_LOCALHOST, + .length = sizeof(START_STREAMING_ERROR_SAME_LOCALHOST) - 1, + .version = STREAM_HANDSHAKE_ERROR_LOCALHOST, + .dynamic = false, + .error = "remote server rejected this stream, the host we are trying to stream is its localhost", + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE, + .postpone_reconnect_seconds = 60 * 60, // the IP may change, try it every hour + }, + { + .response = START_STREAMING_ERROR_ALREADY_STREAMING, + .length = sizeof(START_STREAMING_ERROR_ALREADY_STREAMING) - 1, + .version = STREAM_HANDSHAKE_ERROR_ALREADY_CONNECTED, + .dynamic = false, + .error = "remote server rejected this stream, the host we are trying to stream is already streamed to it", + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE, + .postpone_reconnect_seconds = 2 * 60, // 2 minutes + }, + { + .response = START_STREAMING_ERROR_NOT_PERMITTED, + .length = sizeof(START_STREAMING_ERROR_NOT_PERMITTED) - 1, + .version = STREAM_HANDSHAKE_ERROR_DENIED, + .dynamic = false, + .error = "remote server denied access, probably we don't have the right API key?", + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE, + .postpone_reconnect_seconds = 1 * 60, // 1 minute + }, + + // terminator + { + .response = NULL, + .length = 0, + .version = STREAM_HANDSHAKE_ERROR_BAD_HANDSHAKE, + .dynamic = false, + .error = "remote node response is not understood, is it Netdata?", + .worker_job_id = WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE, + .postpone_reconnect_seconds = 1 * 60, // 1 minute + } +}; -static inline long int parse_stream_version(RRDHOST *host, char *http) -{ - long int stream_version = -1; - int answer = -1; - char *stream_version_start = strchr(http, '='); - if (stream_version_start) { - stream_version_start++; - stream_version = strtol(stream_version_start, NULL, 10); - answer = memcmp(http, START_STREAMING_PROMPT_VN, (size_t)(stream_version_start - http)); - if (!answer) { - rrdpush_set_flags_to_newest_stream(host); +static inline bool rrdpush_sender_validate_response(RRDHOST *host, struct sender_state *s, char *http, size_t http_length) { + int32_t version = STREAM_HANDSHAKE_ERROR_BAD_HANDSHAKE; + + int i; + for(i = 0; stream_responses[i].response ; i++) { + if(stream_responses[i].dynamic && + http_length > stream_responses[i].length && http_length < (stream_responses[i].length + 30) && + strncmp(http, stream_responses[i].response, stream_responses[i].length) == 0) { + + version = str2i(&http[stream_responses[i].length]); + break; } - } else { - answer = memcmp(http, START_STREAMING_PROMPT_V2, strlen(START_STREAMING_PROMPT_V2)); - if (!answer) { - stream_version = 1; - rrdpush_set_flags_to_newest_stream(host); - } else { - answer = memcmp(http, START_STREAMING_PROMPT, strlen(START_STREAMING_PROMPT)); - if (!answer) { - stream_version = 0; - rrdhost_flag_set(host, RRDHOST_FLAG_STREAM_LABELS_STOP); - rrdhost_flag_clear(host, RRDHOST_FLAG_STREAM_LABELS_UPDATE); - } - else { - stream_version = parse_stream_version_for_errors(http); - } + else if(http_length == stream_responses[i].length && strcmp(http, stream_responses[i].response) == 0) { + version = stream_responses[i].version; + + break; } } - return stream_version; + const char *error = stream_responses[i].error; + int worker_job_id = stream_responses[i].worker_job_id; + time_t delay = stream_responses[i].postpone_reconnect_seconds; + + if(version >= STREAM_HANDSHAKE_OK_V1) { + host->destination->last_error = NULL; + host->destination->last_handshake = version; + host->destination->postpone_reconnection_until = 0; + s->capabilities = convert_stream_version_to_capabilities(version); + return true; + } + + error("STREAM %s [send to %s]: %s.", rrdhost_hostname(host), s->connected_to, error); + + worker_is_busy(worker_job_id); + rrdpush_sender_thread_close_socket(host); + host->destination->last_error = error; + host->destination->last_handshake = version; + host->destination->postpone_reconnection_until = now_realtime_sec() + delay; + return false; } -static int rrdpush_sender_thread_connect_to_parent(RRDHOST *host, int default_port, int timeout, - struct sender_state *s) { +static bool rrdpush_sender_thread_connect_to_parent(RRDHOST *host, int default_port, int timeout, struct sender_state *s) { struct timeval tv = { .tv_sec = timeout, @@ -258,11 +438,8 @@ static int rrdpush_sender_thread_connect_to_parent(RRDHOST *host, int default_po // make sure the socket is closed rrdpush_sender_thread_close_socket(host); - debug(D_STREAM, "STREAM: Attempting to connect..."); - info("STREAM %s [send to %s]: connecting...", host->hostname, host->rrdpush_send_destination); - - host->rrdpush_sender_socket = connect_to_one_of_destinations( - host->destinations + s->rrdpush_sender_socket = connect_to_one_of_destinations( + host , default_port , &tv , &s->reconnects_counter @@ -271,48 +448,50 @@ static int rrdpush_sender_thread_connect_to_parent(RRDHOST *host, int default_po , &host->destination ); - if(unlikely(host->rrdpush_sender_socket == -1)) { - error("STREAM %s [send to %s]: failed to connect", host->hostname, host->rrdpush_send_destination); - return 0; + if(unlikely(s->rrdpush_sender_socket == -1)) { + error("STREAM %s [send to %s]: could not connect to parent node at this time.", rrdhost_hostname(host), host->rrdpush_send_destination); + return false; } - info("STREAM %s [send to %s]: initializing communication...", host->hostname, s->connected_to); + info("STREAM %s [send to %s]: initializing communication...", rrdhost_hostname(host), s->connected_to); #ifdef ENABLE_HTTPS - if( netdata_client_ctx ){ - host->ssl.flags = NETDATA_SSL_START; - if (!host->ssl.conn){ - host->ssl.conn = SSL_new(netdata_client_ctx); - if(!host->ssl.conn){ + if(netdata_ssl_client_ctx){ + host->sender->ssl.flags = NETDATA_SSL_START; + if (!host->sender->ssl.conn){ + host->sender->ssl.conn = SSL_new(netdata_ssl_client_ctx); + if(!host->sender->ssl.conn){ error("Failed to allocate SSL structure."); - host->ssl.flags = NETDATA_SSL_NO_HANDSHAKE; + host->sender->ssl.flags = NETDATA_SSL_NO_HANDSHAKE; } } else{ - SSL_clear(host->ssl.conn); + SSL_clear(host->sender->ssl.conn); } - if (host->ssl.conn) + if (host->sender->ssl.conn) { - if (SSL_set_fd(host->ssl.conn, host->rrdpush_sender_socket) != 1) { - error("Failed to set the socket to the SSL on socket fd %d.", host->rrdpush_sender_socket); - host->ssl.flags = NETDATA_SSL_NO_HANDSHAKE; + if (SSL_set_fd(host->sender->ssl.conn, s->rrdpush_sender_socket) != 1) { + error("Failed to set the socket to the SSL on socket fd %d.", s->rrdpush_sender_socket); + host->sender->ssl.flags = NETDATA_SSL_NO_HANDSHAKE; } else{ - host->ssl.flags = NETDATA_SSL_HANDSHAKE_COMPLETE; + host->sender->ssl.flags = NETDATA_SSL_HANDSHAKE_COMPLETE; } } } else { - host->ssl.flags = NETDATA_SSL_NO_HANDSHAKE; + host->sender->ssl.flags = NETDATA_SSL_NO_HANDSHAKE; } #endif + // reset our capabilities to default + s->capabilities = STREAM_OUR_CAPABILITIES; + #ifdef ENABLE_COMPRESSION -// Negotiate stream VERSION_CLABELS if stream compression is not supported -s->rrdpush_compression = (default_compression_enabled && (s->version >= STREAM_VERSION_COMPRESSION)); -if(!s->rrdpush_compression) - s->version = STREAM_VERSION_CLABELS; -#endif //ENABLE_COMPRESSION + // If we don't want compression, remove it from our capabilities + if(!(s->flags & SENDER_FLAG_COMPRESSION)) + s->capabilities &= ~STREAM_CAP_COMPRESSION; +#endif // ENABLE_COMPRESSION /* TODO: During the implementation of #7265 switch the set of variables to HOST_* and CONTAINER_* if the version negotiation resulted in a high enough version. @@ -337,7 +516,7 @@ if(!s->rrdpush_compression) "&ml_enabled=%d" "&mc_version=%d" "&tags=%s" - "&ver=%d" + "&ver=%u" "&NETDATA_INSTANCE_CLOUD_TYPE=%s" "&NETDATA_INSTANCE_CLOUD_INSTANCE_TYPE=%s" "&NETDATA_INSTANCE_CLOUD_INSTANCE_REGION=%s" @@ -370,20 +549,20 @@ if(!s->rrdpush_compression) "User-Agent: %s/%s\r\n" "Accept: */*\r\n\r\n" , host->rrdpush_send_api_key - , host->hostname - , host->registry_hostname + , rrdhost_hostname(host) + , rrdhost_registry_hostname(host) , host->machine_guid , default_rrd_update_every - , host->os - , host->timezone - , host->abbrev_timezone + , rrdhost_os(host) + , rrdhost_timezone(host) + , rrdhost_abbrev_timezone(host) , host->utc_offset , host->system_info->hops + 1 , host->system_info->ml_capable , host->system_info->ml_enabled , host->system_info->mc_version - , (host->tags) ? host->tags : "" - , s->version + , rrdhost_tags(host) + , s->capabilities , (host->system_info->cloud_provider_type) ? host->system_info->cloud_provider_type : "" , (host->system_info->cloud_instance_type) ? host->system_info->cloud_instance_type : "" , (host->system_info->cloud_instance_region) ? host->system_info->cloud_instance_region : "" @@ -412,146 +591,128 @@ if(!s->rrdpush_compression) , (host->system_info->host_ram_total) ? host->system_info->host_ram_total : "" , (host->system_info->host_disk_space) ? host->system_info->host_disk_space : "" , STREAMING_PROTOCOL_VERSION - , host->program_name - , host->program_version + , rrdhost_program_name(host) + , rrdhost_program_version(host) ); http[eol] = 0x00; rrdpush_clean_encoded(&se); #ifdef ENABLE_HTTPS - if (!host->ssl.flags) { + if (!host->sender->ssl.flags) { ERR_clear_error(); - SSL_set_connect_state(host->ssl.conn); - int err = SSL_connect(host->ssl.conn); + SSL_set_connect_state(host->sender->ssl.conn); + int err = SSL_connect(host->sender->ssl.conn); if (err != 1){ - err = SSL_get_error(host->ssl.conn, err); - error("SSL cannot connect with the server: %s ",ERR_error_string((long)SSL_get_error(host->ssl.conn,err),NULL)); + err = SSL_get_error(host->sender->ssl.conn, err); + error("SSL cannot connect with the server: %s ",ERR_error_string((long)SSL_get_error(host->sender->ssl.conn,err),NULL)); if (netdata_use_ssl_on_stream == NETDATA_SSL_FORCE) { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR); rrdpush_sender_thread_close_socket(host); - if (host->destination->next) - host->destination->disabled_no_proper_reply = 1; - return 0; - }else { - host->ssl.flags = NETDATA_SSL_NO_HANDSHAKE; + host->destination->last_error = "SSL error"; + host->destination->last_handshake = STREAM_HANDSHAKE_ERROR_SSL_ERROR; + host->destination->postpone_reconnection_until = now_realtime_sec() + 5 * 60; + return false; + } + else { + host->sender->ssl.flags = NETDATA_SSL_NO_HANDSHAKE; } } else { if (netdata_use_ssl_on_stream == NETDATA_SSL_FORCE) { - if (netdata_validate_server == NETDATA_SSL_VALID_CERTIFICATE) { - if ( security_test_certificate(host->ssl.conn)) { + if (netdata_ssl_validate_server == NETDATA_SSL_VALID_CERTIFICATE) { + if ( security_test_certificate(host->sender->ssl.conn)) { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR); error("Closing the stream connection, because the server SSL certificate is not valid."); rrdpush_sender_thread_close_socket(host); - if (host->destination->next) - host->destination->disabled_no_proper_reply = 1; - return 0; + host->destination->last_error = "invalid SSL certificate"; + host->destination->last_handshake = STREAM_HANDSHAKE_ERROR_INVALID_CERTIFICATE; + host->destination->postpone_reconnection_until = now_realtime_sec() + 5 * 60; + return false; } } } } } - if(send_timeout(&host->ssl,host->rrdpush_sender_socket, http, strlen(http), 0, timeout) == -1) { -#else - if(send_timeout(host->rrdpush_sender_socket, http, strlen(http), 0, timeout) == -1) { #endif + + ssize_t bytes; + + bytes = send_timeout( +#ifdef ENABLE_HTTPS + &host->sender->ssl, +#endif + s->rrdpush_sender_socket, + http, + strlen(http), + 0, + timeout); + + if(bytes <= 0) { // timeout is 0 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT); - error("STREAM %s [send to %s]: failed to send HTTP header to remote netdata.", host->hostname, s->connected_to); rrdpush_sender_thread_close_socket(host); - return 0; + error("STREAM %s [send to %s]: failed to send HTTP header to remote netdata.", rrdhost_hostname(host), s->connected_to); + host->destination->last_error = "timeout while sending request"; + host->destination->last_handshake = STREAM_HANDSHAKE_ERROR_SEND_TIMEOUT; + host->destination->postpone_reconnection_until = now_realtime_sec() + 1 * 60; + return false; } - info("STREAM %s [send to %s]: waiting response from remote netdata...", host->hostname, s->connected_to); + info("STREAM %s [send to %s]: waiting response from remote netdata...", rrdhost_hostname(host), s->connected_to); - ssize_t received; + bytes = recv_timeout( #ifdef ENABLE_HTTPS - received = recv_timeout(&host->ssl,host->rrdpush_sender_socket, http, HTTP_HEADER_SIZE, 0, timeout); - if(received == -1) { -#else - received = recv_timeout(host->rrdpush_sender_socket, http, HTTP_HEADER_SIZE, 0, timeout); - if(received == -1) { + &host->sender->ssl, #endif + s->rrdpush_sender_socket, + http, + HTTP_HEADER_SIZE, + 0, + timeout); + + if(bytes <= 0) { // timeout is 0 worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT); - error("STREAM %s [send to %s]: remote netdata does not respond.", host->hostname, s->connected_to); rrdpush_sender_thread_close_socket(host); - return 0; + error("STREAM %s [send to %s]: remote netdata does not respond.", rrdhost_hostname(host), s->connected_to); + host->destination->last_error = "timeout while expecting first response"; + host->destination->last_handshake = STREAM_HANDSHAKE_ERROR_RECEIVE_TIMEOUT; + host->destination->postpone_reconnection_until = now_realtime_sec() + 30; + return false; } - http[received] = '\0'; + http[bytes] = '\0'; debug(D_STREAM, "Response to sender from far end: %s", http); - int32_t version = (int32_t)parse_stream_version(host, http); - if(version == -1) { - worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE); - error("STREAM %s [send to %s]: server is not replying properly (is it a netdata?).", host->hostname, s->connected_to); - rrdpush_sender_thread_close_socket(host); - //catch other reject reasons and force to check other destinations - if (host->destination->next) - host->destination->disabled_no_proper_reply = 1; - return 0; - } - else if(version == -2) { - error("STREAM %s [send to %s]: remote server is the localhost for [%s].", host->hostname, s->connected_to, host->hostname); - rrdpush_sender_thread_close_socket(host); - host->destination->disabled_because_of_localhost = 1; - return 0; - } - else if(version == -3) { - error("STREAM %s [send to %s]: remote server already receives metrics for [%s].", host->hostname, s->connected_to, host->hostname); - rrdpush_sender_thread_close_socket(host); - host->destination->disabled_already_streaming = now_realtime_sec(); - return 0; - } - else if(version == -4) { - error("STREAM %s [send to %s]: remote server denied access for [%s].", host->hostname, s->connected_to, host->hostname); - rrdpush_sender_thread_close_socket(host); - if (host->destination->next) - host->destination->disabled_because_of_denied_access = 1; - return 0; - } - s->version = version; + if(!rrdpush_sender_validate_response(host, s, http, bytes)) + return false; #ifdef ENABLE_COMPRESSION - s->rrdpush_compression = (s->rrdpush_compression && (s->version >= STREAM_VERSION_COMPRESSION)); - if(s->rrdpush_compression) - { - // parent supports compression - if(s->compressor) + if(stream_has_capability(s, STREAM_CAP_COMPRESSION)) { + if(!s->compressor) + s->compressor = create_compressor(); + else s->compressor->reset(s->compressor); } - else { - //parent does not support compression or has compression disabled - debug(D_STREAM, "Stream is uncompressed! One of the agents (%s <-> %s) does not support compression OR compression is disabled.", s->connected_to, s->host->hostname); - infoerr("Stream is uncompressed! One of the agents (%s <-> %s) does not support compression OR compression is disabled.", s->connected_to, s->host->hostname); - s->version = STREAM_VERSION_CLABELS; - } #endif //ENABLE_COMPRESSION + log_sender_capabilities(s); - info("STREAM %s [send to %s]: established communication with a parent using protocol version %d - ready to send metrics..." - , host->hostname - , s->connected_to - , s->version); - - if(sock_setnonblock(host->rrdpush_sender_socket) < 0) - error("STREAM %s [send to %s]: cannot set non-blocking mode for socket.", host->hostname, s->connected_to); + if(sock_setnonblock(s->rrdpush_sender_socket) < 0) + error("STREAM %s [send to %s]: cannot set non-blocking mode for socket.", rrdhost_hostname(host), s->connected_to); - if(sock_enlarge_out(host->rrdpush_sender_socket) < 0) - error("STREAM %s [send to %s]: cannot enlarge the socket buffer.", host->hostname, s->connected_to); + if(sock_enlarge_out(s->rrdpush_sender_socket) < 0) + error("STREAM %s [send to %s]: cannot enlarge the socket buffer.", rrdhost_hostname(host), s->connected_to); - debug(D_STREAM, "STREAM: Connected on fd %d...", host->rrdpush_sender_socket); + debug(D_STREAM, "STREAM: Connected on fd %d...", s->rrdpush_sender_socket); - return 1; + return true; } -static void attempt_to_connect(struct sender_state *state) +static bool attempt_to_connect(struct sender_state *state) { state->send_attempts = 0; if(rrdpush_sender_thread_connect_to_parent(state->host, state->default_port, state->timeout, state)) { - state->last_sent_t = now_monotonic_sec(); - // reset the buffer, to properly send charts and metrics - rrdpush_sender_thread_data_flush(state->host); + rrdpush_sender_on_connect(state->host); // send from the beginning state->begin = 0; @@ -563,372 +724,628 @@ static void attempt_to_connect(struct sender_state *state) state->sent_bytes_on_this_connection = 0; // let the data collection threads know we are ready - __atomic_test_and_set(&state->host->rrdpush_sender_connected, __ATOMIC_SEQ_CST); + rrdhost_flag_set(state->host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED); + + return true; } - else { - // increase the failed connections counter - state->not_connected_loops++; - // reset the number of bytes sent - state->sent_bytes_on_this_connection = 0; + // we couldn't connect - // slow re-connection on repeating errors - sleep_usec(USEC_PER_SEC * state->reconnect_delay); // seconds - } + // increase the failed connections counter + state->not_connected_loops++; + + // reset the number of bytes sent + state->sent_bytes_on_this_connection = 0; + + // slow re-connection on repeating errors + sleep_usec(USEC_PER_SEC * state->reconnect_delay); // seconds + + return false; } // TCP window is open and we have data to transmit. -void attempt_to_send(struct sender_state *s) { - - rrdpush_send_labels(s->host); +static ssize_t attempt_to_send(struct sender_state *s) { + ssize_t ret = 0; #ifdef NETDATA_INTERNAL_CHECKS struct circular_buffer *cb = s->buffer; #endif - netdata_thread_disable_cancelability(); netdata_mutex_lock(&s->mutex); char *chunk; size_t outstanding = cbuffer_next_unsafe(s->buffer, &chunk); debug(D_STREAM, "STREAM: Sending data. Buffer r=%zu w=%zu s=%zu, next chunk=%zu", cb->read, cb->write, cb->size, outstanding); - ssize_t ret; + #ifdef ENABLE_HTTPS - SSL *conn = s->host->ssl.conn ; - if(conn && !s->host->ssl.flags) { - ret = SSL_write(conn, chunk, outstanding); - } else { - ret = send(s->host->rrdpush_sender_socket, chunk, outstanding, MSG_DONTWAIT); - } + SSL *conn = s->host->sender->ssl.conn ; + if(conn && s->host->sender->ssl.flags == NETDATA_SSL_HANDSHAKE_COMPLETE) + ret = netdata_ssl_write(conn, chunk, outstanding); + else + ret = send(s->rrdpush_sender_socket, chunk, outstanding, MSG_DONTWAIT); #else - ret = send(s->host->rrdpush_sender_socket, chunk, outstanding, MSG_DONTWAIT); + ret = send(s->rrdpush_sender_socket, chunk, outstanding, MSG_DONTWAIT); #endif + if (likely(ret > 0)) { cbuffer_remove_unsafe(s->buffer, ret); s->sent_bytes_on_this_connection += ret; s->sent_bytes += ret; - debug(D_STREAM, "STREAM %s [send to %s]: Sent %zd bytes", s->host->hostname, s->connected_to, ret); - s->last_sent_t = now_monotonic_sec(); + debug(D_STREAM, "STREAM %s [send to %s]: Sent %zd bytes", rrdhost_hostname(s->host), s->connected_to, ret); } else if (ret == -1 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) - debug(D_STREAM, "STREAM %s [send to %s]: unavailable after polling POLLOUT", s->host->hostname, s->connected_to); + debug(D_STREAM, "STREAM %s [send to %s]: unavailable after polling POLLOUT", rrdhost_hostname(s->host), s->connected_to); else if (ret == -1) { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR); debug(D_STREAM, "STREAM: Send failed - closing socket..."); - error("STREAM %s [send to %s]: failed to send metrics - closing connection - we have sent %zu bytes on this connection.", s->host->hostname, s->connected_to, s->sent_bytes_on_this_connection); + error("STREAM %s [send to %s]: failed to send metrics - closing connection - we have sent %zu bytes on this connection.", rrdhost_hostname(s->host), s->connected_to, s->sent_bytes_on_this_connection); rrdpush_sender_thread_close_socket(s->host); } - else { + else debug(D_STREAM, "STREAM: send() returned 0 -> no error but no transmission"); - } + replication_recalculate_buffer_used_ratio_unsafe(s); netdata_mutex_unlock(&s->mutex); - netdata_thread_enable_cancelability(); + + return ret; } -void attempt_read(struct sender_state *s) { -int ret; +static ssize_t attempt_read(struct sender_state *s) { + ssize_t ret = 0; + #ifdef ENABLE_HTTPS - if (s->host->ssl.conn && !s->host->stream_ssl.flags) { - ERR_clear_error(); - int desired = sizeof(s->read_buffer) - s->read_len - 1; - ret = SSL_read(s->host->ssl.conn, s->read_buffer, desired); + if (s->host->sender->ssl.conn && s->host->sender->ssl.flags == NETDATA_SSL_HANDSHAKE_COMPLETE) { + size_t desired = sizeof(s->read_buffer) - s->read_len - 1; + ret = netdata_ssl_read(s->host->sender->ssl.conn, s->read_buffer, desired); if (ret > 0 ) { - s->read_len += ret; - return; + s->read_len += (int)ret; + return ret; } - int sslerrno = SSL_get_error(s->host->ssl.conn, desired); - if (sslerrno == SSL_ERROR_WANT_READ || sslerrno == SSL_ERROR_WANT_WRITE) - return; worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR); - u_long err; - char buf[256]; - while ((err = ERR_get_error()) != 0) { - ERR_error_string_n(err, buf, sizeof(buf)); - error("STREAM %s [send to %s] ssl error: %s", s->host->hostname, s->connected_to, buf); - } - error("Restarting connection"); rrdpush_sender_thread_close_socket(s->host); - return; + return ret; } #endif - ret = recv(s->host->rrdpush_sender_socket, s->read_buffer + s->read_len, sizeof(s->read_buffer) - s->read_len - 1,MSG_DONTWAIT); - if (ret>0) { + ret = recv(s->rrdpush_sender_socket, s->read_buffer + s->read_len, sizeof(s->read_buffer) - s->read_len - 1,MSG_DONTWAIT); + if (ret > 0) { s->read_len += ret; - return; + return ret; } - debug(D_STREAM, "Socket was POLLIN, but req %zu bytes gave %d", sizeof(s->read_buffer) - s->read_len - 1, ret); - - if (ret<0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) - return; + if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) + return ret; - if (ret==0) { + if (ret == 0 || errno == ECONNRESET) { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_PARENT_CLOSED); - error("STREAM %s [send to %s]: connection closed by far end. Restarting connection", s->host->hostname, s->connected_to); + error("STREAM %s [send to %s]: connection closed by far end.", rrdhost_hostname(s->host), s->connected_to); } else { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR); - error("STREAM %s [send to %s]: error during receive (%d). Restarting connection", s->host->hostname, s->connected_to, ret); + error("STREAM %s [send to %s]: error during receive (%zd) - closing connection.", rrdhost_hostname(s->host), s->connected_to, ret); } rrdpush_sender_thread_close_socket(s->host); + + return ret; +} + +struct inflight_stream_function { + struct sender_state *sender; + STRING *transaction; + usec_t received_ut; +}; + +void stream_execute_function_callback(BUFFER *func_wb, int code, void *data) { + struct inflight_stream_function *tmp = data; + + struct sender_state *s = tmp->sender; + + if(rrdhost_can_send_definitions_to_parent(s->host)) { + BUFFER *wb = sender_start(s); + + pluginsd_function_result_begin_to_buffer(wb + , string2str(tmp->transaction) + , code + , functions_content_type_to_format(func_wb->contenttype) + , func_wb->expires); + + buffer_fast_strcat(wb, buffer_tostring(func_wb), buffer_strlen(func_wb)); + pluginsd_function_result_end_to_buffer(wb); + + sender_commit(s, wb); + + internal_error(true, "STREAM %s [send to %s] FUNCTION transaction %s sending back response (%zu bytes, %llu usec).", + rrdhost_hostname(s->host), s->connected_to, + string2str(tmp->transaction), + buffer_strlen(func_wb), + now_realtime_usec() - tmp->received_ut); + } + string_freez(tmp->transaction); + buffer_free(func_wb); + freez(tmp); } // This is just a placeholder until the gap filling state machine is inserted void execute_commands(struct sender_state *s) { + worker_is_busy(WORKER_SENDER_JOB_EXECUTE); + char *start = s->read_buffer, *end = &s->read_buffer[s->read_len], *newline; *end = 0; - while( starthost->hostname, s->connected_to, start); - start = newline+1; + while( start < end && (newline = strchr(start, '\n')) ) { + *newline = '\0'; + + log_access("STREAM: %d from '%s' for host '%s': %s", + gettid(), s->connected_to, rrdhost_hostname(s->host), start); + + internal_error(true, "STREAM %s [send to %s] received command over connection: %s", rrdhost_hostname(s->host), s->connected_to, start); + + char *words[PLUGINSD_MAX_WORDS] = { NULL }; + size_t num_words = pluginsd_split_words(start, words, PLUGINSD_MAX_WORDS, NULL, NULL, 0); + + const char *keyword = get_word(words, num_words, 0); + + if(keyword && strcmp(keyword, PLUGINSD_KEYWORD_FUNCTION) == 0) { + worker_is_busy(WORKER_SENDER_JOB_FUNCTION_REQUEST); + + char *transaction = get_word(words, num_words, 1); + char *timeout_s = get_word(words, num_words, 2); + char *function = get_word(words, num_words, 3); + + if(!transaction || !*transaction || !timeout_s || !*timeout_s || !function || !*function) { + error("STREAM %s [send to %s] %s execution command is incomplete (transaction = '%s', timeout = '%s', function = '%s'). Ignoring it.", + rrdhost_hostname(s->host), s->connected_to, + keyword, + transaction?transaction:"(unset)", + timeout_s?timeout_s:"(unset)", + function?function:"(unset)"); + } + else { + int timeout = str2i(timeout_s); + if(timeout <= 0) timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT; + + struct inflight_stream_function *tmp = callocz(1, sizeof(struct inflight_stream_function)); + tmp->received_ut = now_realtime_usec(); + tmp->sender = s; + tmp->transaction = string_strdupz(transaction); + BUFFER *wb = buffer_create(PLUGINSD_LINE_MAX + 1); + + int code = rrd_call_function_async(s->host, wb, timeout, function, stream_execute_function_callback, tmp); + if(code != HTTP_RESP_OK) { + rrd_call_function_error(wb, "Failed to route request to collector", code); + stream_execute_function_callback(wb, code, tmp); + } + } + } + else if (keyword && strcmp(keyword, PLUGINSD_KEYWORD_REPLAY_CHART) == 0) { + worker_is_busy(WORKER_SENDER_JOB_REPLAY_REQUEST); + + const char *chart_id = get_word(words, num_words, 1); + const char *start_streaming = get_word(words, num_words, 2); + const char *after = get_word(words, num_words, 3); + const char *before = get_word(words, num_words, 4); + + if (!chart_id || !start_streaming || !after || !before) { + error("STREAM %s [send to %s] %s command is incomplete" + " (chart=%s, start_streaming=%s, after=%s, before=%s)", + rrdhost_hostname(s->host), s->connected_to, + keyword, + chart_id ? chart_id : "(unset)", + start_streaming ? start_streaming : "(unset)", + after ? after : "(unset)", + before ? before : "(unset)"); + } + else { + replication_add_request(s, chart_id, + strtoll(after, NULL, 0), + strtoll(before, NULL, 0), + !strcmp(start_streaming, "true") + ); + } + } + else { + error("STREAM %s [send to %s] received unknown command over connection: %s", rrdhost_hostname(s->host), s->connected_to, words[0]?words[0]:"(unset)"); + } + + worker_is_busy(WORKER_SENDER_JOB_EXECUTE); + start = newline + 1; } - if (startread_buffer, start, end-start); - s->read_len = end-start; + s->read_len = end - start; + } + else { + s->read_buffer[0] = '\0'; + s->read_len = 0; } } +struct rrdpush_sender_thread_data { + struct sender_state *sender_state; + RRDHOST *host; + char *pipe_buffer; +}; -static void rrdpush_sender_thread_cleanup_callback(void *ptr) { - worker_unregister(); +static bool rrdpush_sender_pipe_close(RRDHOST *host, int *pipe_fds, bool reopen) { + static netdata_mutex_t mutex = NETDATA_MUTEX_INITIALIZER; - RRDHOST *host = (RRDHOST *)ptr; + bool ret = true; - netdata_mutex_lock(&host->sender->mutex); + netdata_mutex_lock(&mutex); - info("STREAM %s [send]: sending thread cleans up...", host->hostname); + int new_pipe_fds[2]; + if(reopen) { + if(pipe(new_pipe_fds) != 0) { + error("STREAM %s [send]: cannot create required pipe.", rrdhost_hostname(host)); + new_pipe_fds[PIPE_READ] = -1; + new_pipe_fds[PIPE_WRITE] = -1; + ret = false; + } + } - rrdpush_sender_thread_close_socket(host); + int old_pipe_fds[2]; + old_pipe_fds[PIPE_READ] = pipe_fds[PIPE_READ]; + old_pipe_fds[PIPE_WRITE] = pipe_fds[PIPE_WRITE]; - // close the pipe - if(host->rrdpush_sender_pipe[PIPE_READ] != -1) { - close(host->rrdpush_sender_pipe[PIPE_READ]); - host->rrdpush_sender_pipe[PIPE_READ] = -1; + if(reopen) { + pipe_fds[PIPE_READ] = new_pipe_fds[PIPE_READ]; + pipe_fds[PIPE_WRITE] = new_pipe_fds[PIPE_WRITE]; + } + else { + pipe_fds[PIPE_READ] = -1; + pipe_fds[PIPE_WRITE] = -1; } - if(host->rrdpush_sender_pipe[PIPE_WRITE] != -1) { - close(host->rrdpush_sender_pipe[PIPE_WRITE]); - host->rrdpush_sender_pipe[PIPE_WRITE] = -1; + if(old_pipe_fds[PIPE_READ] > 2) + close(old_pipe_fds[PIPE_READ]); + + if(old_pipe_fds[PIPE_WRITE] > 2) + close(old_pipe_fds[PIPE_WRITE]); + + netdata_mutex_unlock(&mutex); + return ret; +} + +void rrdpush_signal_sender_to_wake_up(struct sender_state *s) { + if(unlikely(s->tid == gettid())) + return; + + RRDHOST *host = s->host; + + int pipe_fd = s->rrdpush_sender_pipe[PIPE_WRITE]; + + // signal the sender there are more data + if (pipe_fd != -1 && write(pipe_fd, " ", 1) == -1) { + error("STREAM %s [send]: cannot write to internal pipe.", rrdhost_hostname(host)); + rrdpush_sender_pipe_close(host, s->rrdpush_sender_pipe, true); } +} + +static void rrdpush_sender_thread_cleanup_callback(void *ptr) { + struct rrdpush_sender_thread_data *data = ptr; + worker_unregister(); + + RRDHOST *host = data->host; - if(!host->rrdpush_sender_join) { - info("STREAM %s [send]: sending thread detaches itself.", host->hostname); + netdata_mutex_lock(&host->sender->mutex); + + info("STREAM %s [send]: sending thread cleans up...", rrdhost_hostname(host)); + + rrdpush_sender_thread_close_socket(host); + rrdpush_sender_pipe_close(host, host->sender->rrdpush_sender_pipe, false); + + if(!rrdhost_flag_check(host, RRDHOST_FLAG_RRDPUSH_SENDER_JOIN)) { + info("STREAM %s [send]: sending thread detaches itself.", rrdhost_hostname(host)); netdata_thread_detach(netdata_thread_self()); } - host->rrdpush_sender_spawn = 0; + rrdhost_flag_clear(host, RRDHOST_FLAG_RRDPUSH_SENDER_SPAWN); - info("STREAM %s [send]: sending thread now exits.", host->hostname); + info("STREAM %s [send]: sending thread now exits.", rrdhost_hostname(host)); netdata_mutex_unlock(&host->sender->mutex); + + freez(data->pipe_buffer); + freez(data); } -void sender_init(RRDHOST *parent) +void sender_init(RRDHOST *host) { - if (parent->sender) + if (host->sender) return; - parent->sender = callocz(1, sizeof(*parent->sender)); - parent->sender->host = parent; - parent->sender->buffer = cbuffer_new(1024, 1024*1024); - parent->sender->build = buffer_create(1); + host->sender = callocz(1, sizeof(*host->sender)); + host->sender->host = host; + host->sender->buffer = cbuffer_new(1024, 1024 * 1024); + host->sender->capabilities = STREAM_OUR_CAPABILITIES; + + host->sender->rrdpush_sender_pipe[PIPE_READ] = -1; + host->sender->rrdpush_sender_pipe[PIPE_WRITE] = -1; + host->sender->rrdpush_sender_socket = -1; + #ifdef ENABLE_COMPRESSION - parent->sender->rrdpush_compression = default_compression_enabled; - if (default_compression_enabled) - parent->sender->compressor = create_compressor(); + if(default_compression_enabled) { + host->sender->flags |= SENDER_FLAG_COMPRESSION; + host->sender->compressor = create_compressor(); + } + else + host->sender->flags &= ~SENDER_FLAG_COMPRESSION; #endif - netdata_mutex_init(&parent->sender->mutex); + + netdata_mutex_init(&host->sender->mutex); + replication_init_sender(host->sender); } void *rrdpush_sender_thread(void *ptr) { + worker_register("STREAMSND"); + worker_register_job_name(WORKER_SENDER_JOB_CONNECT, "connect"); + worker_register_job_name(WORKER_SENDER_JOB_PIPE_READ, "pipe read"); + worker_register_job_name(WORKER_SENDER_JOB_SOCKET_RECEIVE, "receive"); + worker_register_job_name(WORKER_SENDER_JOB_EXECUTE, "execute"); + worker_register_job_name(WORKER_SENDER_JOB_SOCKET_SEND, "send"); + + // disconnection reasons + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT, "disconnect timeout"); + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_POLL_ERROR, "disconnect poll error"); + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SOCKER_ERROR, "disconnect socket error"); + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_OVERFLOW, "disconnect overflow"); + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR, "disconnect ssl error"); + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_PARENT_CLOSED, "disconnect parent closed"); + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR, "disconnect receive error"); + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR, "disconnect send error"); + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_NO_COMPRESSION, "disconnect no compression"); + worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE, "disconnect bad handshake"); + + worker_register_job_name(WORKER_SENDER_JOB_REPLAY_REQUEST, "replay request"); + worker_register_job_name(WORKER_SENDER_JOB_FUNCTION_REQUEST, "function"); + + worker_register_job_custom_metric(WORKER_SENDER_JOB_BUFFER_RATIO, "used buffer ratio", "%", WORKER_METRIC_ABSOLUTE); + worker_register_job_custom_metric(WORKER_SENDER_JOB_BYTES_RECEIVED, "bytes received", "bytes/s", WORKER_METRIC_INCREMENT); + worker_register_job_custom_metric(WORKER_SENDER_JOB_BYTES_SENT, "bytes sent", "bytes/s", WORKER_METRIC_INCREMENT); + worker_register_job_custom_metric(WORKER_SENDER_JOB_REPLAY_DICT_SIZE, "replication dict entries", "entries", WORKER_METRIC_ABSOLUTE); + struct sender_state *s = ptr; - s->task_id = gettid(); + s->tid = gettid(); - if(!s->host->rrdpush_send_enabled || !s->host->rrdpush_send_destination || + if(!rrdhost_has_rrdpush_sender_enabled(s->host) || !s->host->rrdpush_send_destination || !*s->host->rrdpush_send_destination || !s->host->rrdpush_send_api_key || !*s->host->rrdpush_send_api_key) { error("STREAM %s [send]: thread created (task id %d), but host has streaming disabled.", - s->host->hostname, s->task_id); + rrdhost_hostname(s->host), s->tid); return NULL; } #ifdef ENABLE_HTTPS if (netdata_use_ssl_on_stream & NETDATA_SSL_FORCE ){ security_start_ssl(NETDATA_SSL_CONTEXT_STREAMING); - security_location_for_context(netdata_client_ctx, netdata_ssl_ca_file, netdata_ssl_ca_path); + ssl_security_location_for_context(netdata_ssl_client_ctx, netdata_ssl_ca_file, netdata_ssl_ca_path); } #endif - info("STREAM %s [send]: thread created (task id %d)", s->host->hostname, s->task_id); + info("STREAM %s [send]: thread created (task id %d)", rrdhost_hostname(s->host), s->tid); + + s->timeout = (int)appconfig_get_number( + &stream_config, CONFIG_SECTION_STREAM, "timeout seconds", 600); + + s->default_port = (int)appconfig_get_number( + &stream_config, CONFIG_SECTION_STREAM, "default port", 19999); + + s->buffer->max_size = (size_t)appconfig_get_number( + &stream_config, CONFIG_SECTION_STREAM, "buffer size bytes", 1024 * 1024 * 10); + + s->reconnect_delay = (unsigned int)appconfig_get_number( + &stream_config, CONFIG_SECTION_STREAM, "reconnect delay seconds", 5); - s->timeout = (int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "timeout seconds", 60); - s->default_port = (int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "default port", 19999); - s->buffer->max_size = - (size_t)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "buffer size bytes", 1024 * 1024 * 10); - s->reconnect_delay = - (unsigned int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "reconnect delay seconds", 5); remote_clock_resync_iterations = (unsigned int)appconfig_get_number( &stream_config, CONFIG_SECTION_STREAM, "initial clock resync iterations", remote_clock_resync_iterations); // TODO: REMOVE FOR SLEW / GAPFILLING // initialize rrdpush globals - __atomic_clear(&s->host->rrdpush_sender_connected, __ATOMIC_SEQ_CST); - if(pipe(s->host->rrdpush_sender_pipe) == -1) { - error("STREAM %s [send]: cannot create required pipe. DISABLING STREAMING THREAD", s->host->hostname); + rrdhost_flag_clear(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS); + rrdhost_flag_clear(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED); + + int pipe_buffer_size = 10 * 1024; +#ifdef F_GETPIPE_SZ + pipe_buffer_size = fcntl(s->rrdpush_sender_pipe[PIPE_READ], F_GETPIPE_SZ); +#endif + if(pipe_buffer_size < 10 * 1024) + pipe_buffer_size = 10 * 1024; + + if(!rrdpush_sender_pipe_close(s->host, s->rrdpush_sender_pipe, true)) { + error("STREAM %s [send]: cannot create inter-thread communication pipe. Disabling streaming.", + rrdhost_hostname(s->host)); return NULL; } - s->version = STREAMING_PROTOCOL_CURRENT_VERSION; - enum { - Collector, - Socket - }; - struct pollfd fds[2]; - fds[Collector].fd = s->host->rrdpush_sender_pipe[PIPE_READ]; - fds[Collector].events = POLLIN; + struct rrdpush_sender_thread_data *thread_data = callocz(1, sizeof(struct rrdpush_sender_thread_data)); + thread_data->pipe_buffer = mallocz(pipe_buffer_size); + thread_data->sender_state = s; + thread_data->host = s->host; - worker_register("STREAMSND"); - worker_register_job_name(WORKER_SENDER_JOB_CONNECT, "connect"); - worker_register_job_name(WORKER_SENDER_JOB_PIPE_READ, "pipe read"); - worker_register_job_name(WORKER_SENDER_JOB_SOCKET_RECEIVE, "receive"); - worker_register_job_name(WORKER_SENDER_JOB_EXECUTE, "execute"); - worker_register_job_name(WORKER_SENDER_JOB_SOCKET_SEND, "send"); + // reset our cleanup flags + rrdhost_flag_clear(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_JOIN); - // disconnection reasons - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT, "disconnect timeout"); - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_POLL_ERROR, "disconnect poll error"); - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SOCKER_ERROR, "disconnect socket error"); - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_OVERFLOW, "disconnect overflow"); - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SSL_ERROR, "disconnect ssl error"); - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_PARENT_CLOSED, "disconnect parent closed"); - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_RECEIVE_ERROR, "disconnect receive error"); - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_SEND_ERROR, "disconnect send error"); - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_NO_COMPRESSION, "disconnect no compression"); - worker_register_job_name(WORKER_SENDER_JOB_DISCONNECT_BAD_HANDSHAKE, "disconnect bad handshake"); + netdata_thread_cleanup_push(rrdpush_sender_thread_cleanup_callback, thread_data); - netdata_thread_cleanup_push(rrdpush_sender_thread_cleanup_callback, s->host); - for(; s->host->rrdpush_send_enabled && !netdata_exit ;) { + for(; rrdhost_has_rrdpush_sender_enabled(s->host) && !netdata_exit ;) { // check for outstanding cancellation requests netdata_thread_testcancel(); // The connection attempt blocks (after which we use the socket in nonblocking) - if(unlikely(s->host->rrdpush_sender_socket == -1)) { + if(unlikely(s->rrdpush_sender_socket == -1)) { worker_is_busy(WORKER_SENDER_JOB_CONNECT); - s->overflow = 0; + rrdhost_flag_clear(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS); + s->flags &= ~SENDER_FLAG_OVERFLOW; s->read_len = 0; s->buffer->read = 0; s->buffer->write = 0; - attempt_to_connect(s); - if (s->version >= VERSION_GAP_FILLING) { - time_t now = now_realtime_sec(); - sender_start(s); - buffer_sprintf(s->build, "TIMESTAMP %"PRId64"", (int64_t)now); - sender_commit(s); - } + + if(unlikely(!attempt_to_connect(s))) + continue; + + s->last_traffic_seen_t = now_monotonic_sec(); rrdpush_claimed_id(s->host); + rrdpush_send_host_labels(s->host); + + rrdhost_flag_set(s->host, RRDHOST_FLAG_RRDPUSH_SENDER_READY_4_METRICS); + info("STREAM %s [send to %s]: enabling metrics streaming...", rrdhost_hostname(s->host), s->connected_to); + continue; } // If the TCP window never opened then something is wrong, restart connection - if(unlikely(now_monotonic_sec() - s->last_sent_t > s->timeout)) { + if(unlikely(now_monotonic_sec() - s->last_traffic_seen_t > s->timeout && + !rrdpush_sender_pending_replication_requests(s) && + !rrdpush_sender_replicating_charts(s) + )) { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_TIMEOUT); - error("STREAM %s [send to %s]: could not send metrics for %d seconds - closing connection - we have sent %zu bytes on this connection via %zu send attempts.", s->host->hostname, s->connected_to, s->timeout, s->sent_bytes_on_this_connection, s->send_attempts); + error("STREAM %s [send to %s]: could not send metrics for %d seconds - closing connection - we have sent %zu bytes on this connection via %zu send attempts.", rrdhost_hostname(s->host), s->connected_to, s->timeout, s->sent_bytes_on_this_connection, s->send_attempts); rrdpush_sender_thread_close_socket(s->host); continue; } - worker_is_idle(); - - // Wait until buffer opens in the socket or a rrdset_done_push wakes us - fds[Collector].revents = 0; - fds[Socket].revents = 0; - fds[Socket].fd = s->host->rrdpush_sender_socket; - netdata_mutex_lock(&s->mutex); - char *chunk; - size_t outstanding = cbuffer_next_unsafe(s->host->sender->buffer, &chunk); - chunk = NULL; // Do not cache pointer outside of region - could be invalidated + size_t outstanding = cbuffer_next_unsafe(s->host->sender->buffer, NULL); + size_t available = cbuffer_available_size_unsafe(s->host->sender->buffer); netdata_mutex_unlock(&s->mutex); - if(outstanding) { + + worker_set_metric(WORKER_SENDER_JOB_BUFFER_RATIO, (NETDATA_DOUBLE)(s->host->sender->buffer->max_size - available) * 100.0 / (NETDATA_DOUBLE)s->host->sender->buffer->max_size); + + if(outstanding) s->send_attempts++; - fds[Socket].events = POLLIN | POLLOUT; - } - else { - fds[Socket].events = POLLIN; + + if(unlikely(s->rrdpush_sender_pipe[PIPE_READ] == -1)) { + if(!rrdpush_sender_pipe_close(s->host, s->rrdpush_sender_pipe, true)) { + error("STREAM %s [send]: cannot create inter-thread communication pipe. Disabling streaming.", + rrdhost_hostname(s->host)); + rrdpush_sender_thread_close_socket(s->host); + break; + } } - int retval = poll(fds, 2, 1000); + worker_is_idle(); + + // Wait until buffer opens in the socket or a rrdset_done_push wakes us + enum { + Collector = 0, + Socket = 1, + }; + struct pollfd fds[2] = { + [Collector] = { + .fd = s->rrdpush_sender_pipe[PIPE_READ], + .events = POLLIN, + .revents = 0, + }, + [Socket] = { + .fd = s->rrdpush_sender_socket, + .events = POLLIN | (outstanding ? POLLOUT : 0 ), + .revents = 0, + } + }; + int poll_rc = poll(fds, 2, 1000); + debug(D_STREAM, "STREAM: poll() finished collector=%d socket=%d (current chunk %zu bytes)...", fds[Collector].revents, fds[Socket].revents, outstanding); if(unlikely(netdata_exit)) break; + internal_error(fds[Collector].fd != s->rrdpush_sender_pipe[PIPE_READ], + "STREAM %s [send to %s]: pipe changed after poll().", rrdhost_hostname(s->host), s->connected_to); + + internal_error(fds[Socket].fd != s->rrdpush_sender_socket, + "STREAM %s [send to %s]: socket changed after poll().", rrdhost_hostname(s->host), s->connected_to); + // Spurious wake-ups without error - loop again - if (retval == 0 || ((retval == -1) && (errno == EAGAIN || errno == EINTR))) { + if (poll_rc == 0 || ((poll_rc == -1) && (errno == EAGAIN || errno == EINTR))) { debug(D_STREAM, "Spurious wakeup"); continue; } // Only errors from poll() are internal, but try restarting the connection - if(unlikely(retval == -1)) { + if(unlikely(poll_rc == -1)) { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_POLL_ERROR); - error("STREAM %s [send to %s]: failed to poll(). Closing socket.", s->host->hostname, s->connected_to); + error("STREAM %s [send to %s]: failed to poll(). Closing socket.", rrdhost_hostname(s->host), s->connected_to); + rrdpush_sender_pipe_close(s->host, s->rrdpush_sender_pipe, true); rrdpush_sender_thread_close_socket(s->host); continue; } + // If we have data and have seen the TCP window open then try to close it by a transmission. + if(likely(outstanding && (fds[Socket].revents & POLLOUT))) { + worker_is_busy(WORKER_SENDER_JOB_SOCKET_SEND); + ssize_t bytes = attempt_to_send(s); + if(bytes > 0) { + s->last_traffic_seen_t = now_monotonic_sec(); + worker_set_metric(WORKER_SENDER_JOB_BYTES_SENT, (NETDATA_DOUBLE)bytes); + } + } + // If the collector woke us up then empty the pipe to remove the signal - if (fds[Collector].revents & POLLIN || fds[Collector].revents & POLLPRI) { + if (fds[Collector].revents & (POLLIN|POLLPRI)) { worker_is_busy(WORKER_SENDER_JOB_PIPE_READ); debug(D_STREAM, "STREAM: Data added to send buffer (current buffer chunk %zu bytes)...", outstanding); - char buffer[1000 + 1]; - if (read(s->host->rrdpush_sender_pipe[PIPE_READ], buffer, 1000) == -1) - error("STREAM %s [send to %s]: cannot read from internal pipe.", s->host->hostname, s->connected_to); + if (read(fds[Collector].fd, thread_data->pipe_buffer, pipe_buffer_size) == -1) + error("STREAM %s [send to %s]: cannot read from internal pipe.", rrdhost_hostname(s->host), s->connected_to); } // Read as much as possible to fill the buffer, split into full lines for execution. if (fds[Socket].revents & POLLIN) { worker_is_busy(WORKER_SENDER_JOB_SOCKET_RECEIVE); - attempt_read(s); + ssize_t bytes = attempt_read(s); + if(bytes > 0) { + s->last_traffic_seen_t = now_monotonic_sec(); + worker_set_metric(WORKER_SENDER_JOB_BYTES_RECEIVED, (NETDATA_DOUBLE)bytes); + } } - worker_is_busy(WORKER_SENDER_JOB_EXECUTE); - execute_commands(s); + if(unlikely(s->read_len)) + execute_commands(s); - // If we have data and have seen the TCP window open then try to close it by a transmission. - if (outstanding && fds[Socket].revents & POLLOUT) { - worker_is_busy(WORKER_SENDER_JOB_SOCKET_SEND); - attempt_to_send(s); + if(unlikely(fds[Collector].revents & (POLLERR|POLLHUP|POLLNVAL))) { + char *error = NULL; + + if (unlikely(fds[Collector].revents & POLLERR)) + error = "pipe reports errors (POLLERR)"; + else if (unlikely(fds[Collector].revents & POLLHUP)) + error = "pipe closed (POLLHUP)"; + else if (unlikely(fds[Collector].revents & POLLNVAL)) + error = "pipe is invalid (POLLNVAL)"; + + if(error) { + rrdpush_sender_pipe_close(s->host, s->rrdpush_sender_pipe, true); + error("STREAM %s [send to %s]: restarting internal pipe: %s.", + rrdhost_hostname(s->host), s->connected_to, error); + } } - // TODO-GAPS - why do we only check this on the socket, not the pipe? - if (outstanding) { + if(unlikely(fds[Socket].revents & (POLLERR|POLLHUP|POLLNVAL))) { char *error = NULL; + if (unlikely(fds[Socket].revents & POLLERR)) error = "socket reports errors (POLLERR)"; else if (unlikely(fds[Socket].revents & POLLHUP)) error = "connection closed by remote end (POLLHUP)"; else if (unlikely(fds[Socket].revents & POLLNVAL)) error = "connection is invalid (POLLNVAL)"; + if(unlikely(error)) { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_SOCKER_ERROR); - error("STREAM %s [send to %s]: restart stream because %s - %zu bytes transmitted.", s->host->hostname, - s->connected_to, error, s->sent_bytes_on_this_connection); + error("STREAM %s [send to %s]: restarting connection: %s - %zu bytes transmitted.", + rrdhost_hostname(s->host), s->connected_to, error, s->sent_bytes_on_this_connection); rrdpush_sender_thread_close_socket(s->host); } } // protection from overflow - if (s->overflow) { + if(unlikely(s->flags & SENDER_FLAG_OVERFLOW)) { worker_is_busy(WORKER_SENDER_JOB_DISCONNECT_OVERFLOW); errno = 0; - error("STREAM %s [send to %s]: buffer full (%zu-bytes) after %zu bytes. Restarting connection", - s->host->hostname, s->connected_to, s->buffer->size, s->sent_bytes_on_this_connection); + error("STREAM %s [send to %s]: buffer full (allocated %zu bytes) after sending %zu bytes. Restarting connection", + rrdhost_hostname(s->host), s->connected_to, s->buffer->size, s->sent_bytes_on_this_connection); rrdpush_sender_thread_close_socket(s->host); } + + worker_set_metric(WORKER_SENDER_JOB_REPLAY_DICT_SIZE, (NETDATA_DOUBLE) dictionary_entries(s->replication.requests)); } netdata_thread_cleanup_pop(1); diff --git a/streaming/stream.conf b/streaming/stream.conf index 33172bbcb..7c9ccc9b8 100644 --- a/streaming/stream.conf +++ b/streaming/stream.conf @@ -33,36 +33,31 @@ destination = # Skip Certificate verification? - # # The netdata child is configurated to avoid invalid SSL/TLS certificate, # so certificates that are self-signed or expired will stop the streaming. # Case the server certificate is not valid, you can enable the use of # 'bad' certificates setting the next option as 'yes'. - # #ssl skip certificate verification = yes # Certificate Authority Path + # OpenSSL has a default directory where the known certificates are stored. + # In case it is necessary, it is possible to change this rule using the variable + # "CApath", e.g. CApath = /etc/ssl/certs/ # - # OpenSSL has a default directory where the known certificates are stored, - # case it is necessary it is possible to change this rule using the variable - # "CApath" - # - #CApath = /etc/ssl/certs/ + #CApath = # Certificate Authority file + # When the Netdata parent has a certificate that is not recognized as valid, + # we can add it to the list of known certificates in "CApath" and give it to + # Netdata as an argument, e.g. CAfile = /etc/ssl/certs/cert.pem # - # When the Netdata parent has certificate, that is not recognized as valid, - # we can add this certificate in the list of known certificates in CApath - # and give for Netdata as argument. - # - #CAfile = /etc/ssl/certs/cert.pem + #CAfile = # The API_KEY to use (as the sender) api key = # Stream Compression - # - # The netdata child is configurated to enable stream compression by default. + # The default is enabled # You can control stream compression in this agent with options: yes | no #enable compression = yes @@ -91,6 +86,7 @@ reconnect delay seconds = 5 # Sync the clock of the charts for that many iterations, when starting. + # It is ignored when replication is enabled initial clock resync iterations = 60 # ----------------------------------------------------------------------------- @@ -115,6 +111,11 @@ [API_KEY] # Default settings for this API key + # This GUID is to be used as an API key from remote agents connecting + # to this machine. Failure to match such a key, denies access. + # YOU MUST SET THIS FIELD ON ALL API KEYS. + type = api + # You can disable the API key, by setting this to: no # The default (for unknown API keys) is: no enabled = no @@ -127,9 +128,8 @@ # The default history in entries, for all hosts using this API key. # You can also set it per host below. - # If you don't set it here, the history size of the central netdata - # will be used. - default history = 3600 + # For the default db mode (dbengine), this is ignored. + #default history = 3600 # The default memory mode to be used for all hosts using this API key. # You can also set it per host below. @@ -140,7 +140,7 @@ # ram keep it in RAM, don't touch the disk # none no database at all (use this on headless proxies) # dbengine like a traditional database - default memory mode = ram + #default memory mode = dbengine # Shall we enable health monitoring for the hosts using this API key? # 3 possible values: @@ -150,7 +150,7 @@ # ensure that the netdata process on the child is gracefully stopped, to prevent invalid last_collected alarms # You can also set it per host, below. # The default is taken from [health].enabled of netdata.conf - health enabled by default = auto + #health enabled by default = auto # postpone alarms for a short period after the sender is connected default postpone alarms on connect seconds = 60 @@ -163,11 +163,19 @@ #default proxy send charts matching = * # Stream Compression - # - # The stream with the child can be configurated to enable stream compression. + # By default it is enabled. # You can control stream compression in this parent agent stream with options: yes | no #enable compression = yes + # Replication + # Enable replication for all hosts using this api key. Default: enabled + #enable replication = yes + + # How many seconds to replicate from each child. Default: a day + #seconds to replicate = 86400 + + # The duration we want to replicate per each step. + #replication_step = 600 # ----------------------------------------------------------------------------- # 3. PER SENDING HOST SETTINGS, ON PARENT NETDATA @@ -184,6 +192,11 @@ # you can give settings for each sending host here. [MACHINE_GUID] + # This GUID is to be used as a MACHINE GUID from remote agents connecting + # to this machine, not an API key. + # YOU MUST SET THIS FIELD ON ALL MACHINE GUIDs. + type = machine + # enable this host: yes | no # When disabled, the parent will not receive metrics for this host. # THIS IS NOT A SECURITY MECHANISM - AN ATTACKER CAN SET ANY OTHER GUID. @@ -197,14 +210,15 @@ # and at stream.conf [API_KEY].allow from allow from = * - # The number of entries in the database - history = 3600 + # The number of entries in the database. + # This is ignored for db mode dbengine. + #history = 3600 # The memory mode of the database: save | map | ram | none | dbengine - memory mode = save + #memory mode = dbengine # Health / alarms control: yes | no | auto - health enabled = yes + #health enabled = auto # postpone alarms when the sender connects postpone alarms on connect seconds = 60 @@ -217,8 +231,16 @@ #proxy send charts matching = * # Stream Compression - # - # The stream with the child can be configurated to enable stream compression. + # By default, enabled. # You can control stream compression in this parent agent stream with options: yes | no #enable compression = yes - \ No newline at end of file + + # Replication + # Enable replication for all hosts using this api key. + #enable replication = yes + + # How many seconds to replicate from each child. + #seconds to replicate = 86400 + + # The duration we want to replicate per each step. + #replication_step = 600 diff --git a/system/Makefile.am b/system/Makefile.am index a88ccab65..72d123daa 100644 --- a/system/Makefile.am +++ b/system/Makefile.am @@ -30,8 +30,14 @@ dist_config_DATA = \ # Explicitly install directories to avoid permission issues due to umask install-exec-local: $(INSTALL) -d $(DESTDIR)$(configdir) + $(INSTALL) -d $(DESTDIR)$(libsysdir) -nodist_noinst_DATA = \ +libexecnetdatadir=$(libexecdir)/netdata +nodist_libexecnetdata_SCRIPTS = \ + install-service.sh \ + $(NULL) + +nodist_libsys_DATA = \ netdata-openrc \ netdata.logrotate \ netdata.service \ @@ -44,8 +50,13 @@ nodist_noinst_DATA = \ netdata-updater.service \ $(NULL) +dist_libsys_DATA = \ + netdata-updater.timer \ + $(NULL) + dist_noinst_DATA = \ edit-config.in \ + install-service.sh.in \ netdata-openrc.in \ netdata.logrotate.in \ netdata.service.in \ @@ -57,5 +68,4 @@ dist_noinst_DATA = \ netdata.conf \ netdata.crontab.in \ netdata-updater.service.in \ - netdata-updater.timer \ $(NULL) diff --git a/system/install-service.sh.in b/system/install-service.sh.in new file mode 100755 index 000000000..59cff0a08 --- /dev/null +++ b/system/install-service.sh.in @@ -0,0 +1,786 @@ +#!/usr/bin/env sh + +# SPDX-License-Identifier: GPL-3.0-or-later + +# Handle installation of the Netdata agent as a system service. +# +# Exit codes: +# 0 - Successfully installed service. +# 1 - Invalid arguments or other internal error. +# 2 - Unable to detect system service type. +# 3 - Detected system service type, but type not supported. +# 4 - Detected system service type, but could not install due to other issues. +# 5 - Platform not supported. + +set -e + +SCRIPT_SOURCE="$( + self=${0} + while [ -L "${self}" ] + do + cd "${self%/*}" || exit 1 + self=$(readlink "${self}") + done + cd "${self%/*}" || exit 1 + echo "$(pwd -P)/${self##*/}" +)" + +DUMP_CMDS=0 +ENABLE="auto" +EXPORT_CMDS=0 +INSTALL=1 +LINUX_INIT_TYPES="systemd openrc lsb initd runit" +PLATFORM="$(uname -s)" +SHOW_SVC_TYPE=0 +SVC_SOURCE="@libsysdir_POST@" +SVC_TYPE="detect" +WSL_ERROR_MSG="We appear to be running in WSL and were unable to find a usable service manager. We currently support systemd, LSB init scripts, and traditional init.d style init scripts when running under WSL." + +# ===================================================================== +# Utility functions + +cleanup() { + ec="${?}" + + if [ -n "${NETDATA_SAVE_WARNINGS}" ]; then + if [ -n "${NETDATA_PROPAGATE_WARNINGS}" ]; then + export NETDATA_WARNINGS="${NETDATA_WARNINGS}${SAVED_WARNINGS}" + fi + fi + + trap - EXIT + + exit "${ec}" +} + +info() { + printf >&2 "%s\n" "${*}" +} + +warning() { + if [ -n "${NETDATA_SAVE_WARNINGS}" ]; then + SAVED_WARNINGS="${SAVED_WARNINGS}\n - ${*}" + fi + printf >&2 "WARNING: %s\n" "${*}" +} + +error() { + if [ -n "${NETDATA_SAVE_WARNINGS}" ]; then + SAVED_WARNINGS="${SAVED_WARNINGS}\n - ${*}" + fi + printf >&2 "ERROR: %s\n" "${*}" +} + +get_os_key() { + if [ -f /etc/os-release ]; then + # shellcheck disable=SC1091 + . /etc/os-release || return 1 + echo "${ID}-${VERSION_ID}" + + elif [ -f /etc/redhat-release ]; then + cat /etc/redhat-release + else + echo "unknown" + fi +} + +valid_types() { + case "${PLATFORM}" in + Linux) + echo "detect systemd openrc lsb initd" + ;; + FreeBSD) + echo "detect freebsd" + ;; + Darwin) + echo "detect launchd" + ;; + *) + echo "detect" + ;; + esac +} + +install_generic_service() { + svc_type="${1}" + svc_type_name="${2}" + svc_file="${3}" + svc_enable_hook="${4}" + svc_disable_hook="${5}" + + info "Installing ${svc_type_name} service file." + if [ ! -f "${svc_file}" ] && [ "${ENABLE}" = "auto" ]; then + ENABLE="enable" + fi + + if ! install -p -m 0755 -o 0 -g 0 "${SVC_SOURCE}/netdata-${svc_type}" "${svc_file}"; then + error "Failed to install service file." + exit 4 + fi + + case "${ENABLE}" in + auto) true ;; + disable) + info "Disabling Netdata service." + ${svc_disable_hook} + ;; + enable) + info "Enabling Netdata service." + ${svc_enable_hook} + ;; + esac +} + +dump_cmds() { + [ -n "${NETDATA_START_CMD}" ] && echo "NETDATA_START_CMD='${NETDATA_START_CMD}'" + [ -n "${NETDATA_STOP_CMD}" ] && echo "NETDATA_STOP_CMD='${NETDATA_STOP_CMD}'" + [ -n "${NETDATA_INSTALLER_START_CMD}" ] && echo "NETDATA_INSTALLER_START_CMD='${NETDATA_INSTALLER_START_CMD}'" + return 0 +} + +export_cmds() { + [ -n "${NETDATA_START_CMD}" ] && export NETDATA_START_CMD="${NETDATA_START_CMD}" + [ -n "${NETDATA_STOP_CMD}" ] && export NETDATA_STOP_CMD="${NETDATA_STOP_CMD}" + [ -n "${NETDATA_INSTALLER_START_CMD}" ] && export NETDATA_INSTALLER_START_CMD="${NETDATA_INSTALLER_START_COMMAND}" + return 0 +} + +save_cmds() { + dump_cmds > "${SAVE_CMDS_PATH}" +} + +# ===================================================================== +# Help functions + +usage() { + cat << HEREDOC +USAGE: install-service.sh [options] + where options include: + + --source Specify where to find the service files to install (default ${SVC_SOURCE}). + --type Specify the type of service file to install. Specify a type of 'help' to get a list of valid types for your platform. + --show-type Display information about what service managers are detected. + --cmds Additionally print a list of commands for starting and stopping the agent with the detected service type. + --export-cmds Export the variables that would be printed by the --cmds option. + --cmds-only Don't install, just handle the --cmds or --export-cmds option. + --enable Explicitly enable the service on install (default is to enable if not already installed). + --disable Explicitly disable the service on install. + --help Print this help information. +HEREDOC +} + +help_types() { + cat << HEREDOC +Valid service types for ${PLATFORM} are: +$(valid_types) +HEREDOC +} + +# ===================================================================== +# systemd support functions + +_check_systemd() { + pids='' + p='' + myns='' + ns='' + + # if the directory /lib/systemd/system OR /usr/lib/systemd/system (SLES 12.x) does not exit, it is not systemd + if [ ! -d /lib/systemd/system ] && [ ! -d /usr/lib/systemd/system ]; then + echo "NO" && return 0 + fi + + # if there is no systemctl command, it is not systemd + [ -z "$(command -v systemctl 2>/dev/null || true)" ] && echo "NO" && return 0 + + # if pid 1 is systemd, it is systemd + [ "$(basename "$(readlink /proc/1/exe)" 2> /dev/null)" = "systemd" ] && echo "YES" && return 0 + + # it ‘is’ systemd at this point, but systemd might not be running + # if not, return 2 to indicate ‘systemd, but not running’ + pids=$(safe_pidof systemd 2> /dev/null) + [ -z "${pids}" ] && echo "OFFLINE" && return 0 + + # check if the running systemd processes are not in our namespace + myns="$(readlink /proc/self/ns/pid 2> /dev/null)" + for p in ${pids}; do + ns="$(readlink "/proc/${p}/ns/pid" 2> /dev/null)" + + # if pid of systemd is in our namespace, it is systemd + [ -n "${myns}" ] && [ "${myns}" = "${ns}" ] && echo "YES" && return 0 + done + + # else, it is not systemd + echo "NO" +} + +check_systemd() { + if [ -z "${IS_SYSTEMD}" ]; then + IS_SYSTEMD="$(_check_systemd)" + fi + + echo "${IS_SYSTEMD}" +} + +get_systemd_service_dir() { + if [ -w "/lib/systemd/system" ]; then + echo "/lib/systemd/system" + elif [ -w "/usr/lib/systemd/system" ]; then + echo "/usr/lib/systemd/system" + elif [ -w "/etc/systemd/system" ]; then + echo "/etc/systemd/system" + else + error "Unable to detect systemd service directory." + exit 4 + fi +} + +install_systemd_service() { + SRCFILE="${SVC_SOURCE}/netdata.service" + + if [ "$(systemctl --version | head -n 1 | cut -f 2 -d ' ')" -le 235 ]; then + SRCFILE="${SVC_SOURCE}/netdata.service.v235" + fi + + if [ "${ENABLE}" = "auto" ]; then + if [ "$(check_systemd)" = "YES" ]; then + IS_NETDATA_ENABLED="$(systemctl is-enabled netdata 2> /dev/null || echo "Netdata not there")" + fi + + if [ "${IS_NETDATA_ENABLED}" = "disabled" ]; then + ENABLE="disable" + else + ENABLE="enable" + fi + fi + + info "Installing systemd service..." + if ! install -p -m 0644 -o 0 -g 0 "${SRCFILE}" "$(get_systemd_service_dir)/netdata.service"; then + error "Failed to install systemd service file." + exit 4 + fi + + if [ "$(check_systemd)" = "YES" ]; then + if ! systemctl daemon-reload; then + warning "Failed to reload systemd unit files." + fi + + if ! systemctl ${ENABLE} netdata; then + warning "Failed to ${ENABLE} Netdata service." + fi + fi +} + +systemd_cmds() { + if [ "$(check_systemd)" = "YES" ]; then + NETDATA_START_CMD='systemctl start netdata' + NETDATA_STOP_CMD='systemctl stop netdata' + else # systemd is not running, use external defaults by providing no commands + warning "Detected systemd, but not booted using systemd. Unable to provide commands to start or stop Netdata using the service manager." + fi +} + +# ===================================================================== +# OpenRC support functions + +_check_openrc() { + # if /lib/rc/sh/functions.sh does not exist, it's not OpenRC + [ ! -f /lib/rc/sh/functions.sh ] && echo "NO" && return 0 + + # if there is no /etc/init.d, it's not OpenRC + [ ! -d /etc/init.d ] && echo "NO" && return 0 + + # if there is no rc-update command, it's not OpenRC + [ -z "$(command -v rc-update 2>/dev/null || true)" ] && echo "NO" && return 0 + + # If /run/openrc/softlevel exists, it's OpenRC + [ -f /run/openrc/softlevel ] && echo "YES" && return 0 + + # if PID 1 is openrc-init, it's OpenRC + [ "$(basename "$(readlink /proc/1/exe)" 2> /dev/null)" = "openrc-init" ] && echo "YES" && return 0 + + # if there is an openrc command, it's OpenRC, but not booted as such + [ -n "$(command -v openrc 2>/dev/null || true)" ] && echo "OFFLINE" && return 0 + + # if /etc/init.d/local exists and has `openrc-run` in it's shebang line, it’s OpenRC, but not booted as such + [ -r /etc/init.d/local ] && head -n 1 /etc/init.d/local | grep -q openrc-run && echo "OFFLINE" && return 0 + + # Otherwise, it’s not OpenRC + echo "NO" && return 0 +} + +check_openrc() { + if [ -z "${IS_OPENRC}" ]; then + IS_OPENRC="$(_check_openrc)" + fi + + echo "${IS_OPENRC}" +} + +enable_openrc() { + if [ "$(check_openrc)" = "YES" ]; then + runlevel="$(rc-status -r)" + fi + + runlevel="${runlevel:-default}" + + if ! rc-update add netdata "${runlevel}"; then + warning "Failed to enable Netdata service in runlevel ${runlevel}." + fi +} + +disable_openrc() { + for runlevel in /etc/runlevels/*; do + if [ -e "${runlevel}/netdata" ]; then + runlevel="$(basename "${runlevel}")" + if ! rc-update del netdata "${runlevel}"; then + warning "Failed to disable Netdata service in runlevel ${runlevel}." + fi + fi + done +} + +install_openrc_service() { + install_generic_service openrc OpenRC /etc/init.d/netdata enable_openrc disable_openrc +} + +openrc_cmds() { + if [ "$(check_openrc)" = "YES" ]; then + NETDATA_START_CMD='rc-service netdata start' + NETDATA_STOP_CMD='rc-service netdata stop' + else # Not booted using OpenRC, use external defaults by not providing commands. + warning "Detected OpenRC, but the system is not booted using OpenRC. Unable to provide commands to start or stop Netdata using the service manager." + fi +} + +# ===================================================================== +# LSB init script support functions + +_check_lsb_ignore_systemd() { + # if there is no /etc/init.d directory, it’s not an LSB system + [ ! -d /etc/init.d ] && echo "NO" && return 0 + + # If it's an OpenRC system, then it's not an LSB system + [ "$(check_openrc)" != "NO" ] && echo "NO" && return 0 + + # If /lib/lsb/init-functions exists, it’s an LSB system + [ -f /lib/lsb/init-functions ] && echo "YES" && return 0 + + echo "NO" && return 0 +} + +_check_lsb() { + # if there is _any_ systemd, it’s not an LSB system + [ "$(check_systemd)" != "NO" ] && echo "NO" && return 0 + + _check_lsb_ignore_systemd +} + +check_lsb() { + if [ -z "${IS_LSB}" ]; then + IS_LSB="$(_check_lsb)" + fi + + echo "${IS_LSB}" +} + +enable_lsb() { + if ! update-rc.d netdata defaults; then + warning "Failed to enable Netdata service." + elif ! update-rc.d netdata defaults-disabled; then + warning "Failed to fully enable Netdata service." + fi +} + +disable_lsb() { + if ! update-rc.d netdata remove; then + warning "Failed to disable Netdata service." + fi +} + +install_lsb_service() { + install_generic_service lsb LSB /etc/init.d/netdata enable_lsb disable_lsb +} + +lsb_cmds() { + NETDATA_START_CMD='/etc/init.d/netdata start' + NETDATA_STOP_CMD='/etc/init.d/netdata stop' +} + +# ===================================================================== +# init.d init script support functions + +_check_initd_ignore_systemd() { + # if there is no /etc/init.d directory, it’s not an init.d system + [ ! -d /etc/init.d ] && echo "NO" && return 1 + + # if there is no chkconfig command, it's not a (usable) init.d system + [ -z "$(command -v chkconfig 2>/dev/null || true)" ] && echo "NO" && return 0 + + # if there is _any_ openrc, it’s not init.d + [ "$(check_openrc)" != "NO" ] && echo "NO" && return 0 + + # if it's not an LSB setup, it’s init.d + [ "$(check_lsb)" != "NO" ] && echo "NO" && return 0 + + echo "YES" && return 0 +} + +_check_initd() { + # if there is _any_ systemd, it’s not init.d + [ "$(check_systemd)" != "NO" ] && echo "NO" && return 0 + + _check_initd_ignore_systemd +} + +check_initd() { + if [ -z "${IS_INITD}" ]; then + IS_INITD="$(_check_initd)" + fi + + echo "${IS_INITD}" +} + +enable_initd() { + if ! chkconfig netdata on; then + warning "Failed to enable Netdata service." + fi +} + +disable_initd() { + if ! chkconfig netdata off; then + warning "Failed to disable Netdata service." + fi +} + +install_initd_service() { + install_generic_service init-d init.d /etc/init.d/netdata enable_initd disable_initd +} + +initd_cmds() { + NETDATA_START_CMD='/etc/init.d/netdata start' + NETDATA_STOP_CMD='/etc/init.d/netdata stop' +} + +# ===================================================================== +# runit support functions +# +# Currently not supported, this exists to provide useful error messages. + +_check_runit() { + # if there is no runsvdir command, then it's not runit + [ -z "$(command -v runsvdir 2>/dev/null || true)" ] && echo "NO" && return 0 + + # if there is no runit command, then it's not runit + [ -z "$(command -v runit 2>/dev/null || true)" ] && echo "NO" && return 0 + + # if /run/runit exists, then it's runit + [ -d /run/runit ] && echo "YES" && return 0 + + # if /etc/runit/1 exists and is executable, then it's runit + [ -x /etc/runit/1 ] && echo "YES" && return 0 + + echo "NO" && return 0 +} + +check_runit() { + if [ -z "${IS_RUNIT}" ]; then + IS_RUNIT="$(_check_runit)" + fi + + echo "${IS_RUNIT}" +} + +install_runit_service() { + error "Detected runit, which we do not currently support." + exit 3 +} + +runit_cmds() { + error "Detected runit, which we do not currently support." + exit 3 +} + +# ===================================================================== +# WSL support functions +# +# Cannot be supported, this exists to provide useful error messages. + +_check_wsl() { + # If uname -r contains the string WSL, then it's WSL. + uname -r | grep -q 'WSL' && echo "YES" && return 0 + + # If uname -r contains the string Microsoft, then it's WSL. + # This probably throws a false positive on CBL-Mariner, but it's part of what MS officially recommends for + # detecting if you're running under WSL. + uname -r | grep -q "Microsoft" && echo "YES" && return 0 + + echo "NO" && return 0 +} + +check_wsl() { + if [ -z "${IS_WSL}" ]; then + IS_WSL="$(_check_wsl)" + fi + + echo "${IS_WSL}" +} + +install_wsl_service() { + error "${WSL_ERROR_MSG}" + exit 3 +} + +wsl_cmds() { + error "${WSL_ERROR_MSG}" + exit 3 +} + +# ===================================================================== +# FreeBSD support functions + +enable_freebsd() { + if ! sysrc netdata_enable=YES; then + warning "Failed to enable netdata service." + fi +} + +disable_freebsd() { + if ! sysrc netdata_enable=NO; then + warning "Failed to disable netdata service." + fi +} + +install_freebsd_service() { + install_generic_service freebsd "FreeBSD rc.d" /usr/local/etc/rc.d/netdata enable_freebsd disable_freebsd +} + +freebsd_cmds() { + NETDATA_START_CMD='service netdata start' + NETDATA_STOP_CMD='service netdata stop' + NETDATA_INSTALLER_START_CMD='service netdata onestart' +} + +# ===================================================================== +# macOS support functions + +install_darwin_service() { + info "Installing macOS plist file for launchd." + if ! install -C -S -p -m 0644 -o 0 -g 0 system/netdata.plist /Library/LaunchDaemons/com.github.netdata.plist; then + error "Failed to copy plist file." + exit 4 + fi + + if ! launchctl load /Library/LaunchDaemons/com.github.netdata.plist; then + error "Failed to load plist file." + exit 4 + fi +} + +darwin_cmds() { + NETDATA_START_CMD='launchctl start com.github.netdata' + NETDATA_STOP_CMD='launchctl stop com.github.netdata' +} + +# ===================================================================== +# Linux support functions + +detect_linux_svc_type() { + if [ "${SVC_TYPE}" = "detect" ]; then + found_types='' + + for t in wsl ${LINUX_INIT_TYPES}; do + case "$("check_${t}")" in + YES) + SVC_TYPE="${t}" + break + ;; + NO) continue ;; + OFFLINE) + if [ -z "${found_types}" ]; then + found_types="${t}" + else + found_types="${found_types} ${t}" + fi + ;; + esac + done + + if [ "${SVC_TYPE}" = "detect" ]; then + if [ -z "${found_types}" ]; then + error "Failed to detect what type of service manager is in use." + else + SVC_TYPE="$(echo "${found_types}" | cut -f 1 -d ' ')" + warning "Failed to detect a running service manager, using detected (but not running) ${SVC_TYPE}." + fi + elif [ "${SVC_TYPE}" = "wsl" ]; then + if [ "$(check_systemd)" = "YES" ]; then + # Support for systemd in WSL. + SVC_TYPE="systemd" + elif [ "$(_check_lsb_ignore_systemd)" = "YES" ]; then + # Support for LSB init.d in WSL. + SVC_TYPE="lsb" + elif [ "$(_check_initd_ignore_systemd)" = "YES" ]; then + # Support for ‘generic’ init.d in WSL. + SVC_TYPE="initd" + fi + fi + fi + + echo "${SVC_TYPE}" +} + +install_linux_service() { + t="$(detect_linux_svc_type)" + + if [ -z "${t}" ]; then + exit 2 + fi + + "install_${t}_service" +} + +linux_cmds() { + t="$(detect_linux_svc_type)" + + if [ -z "${t}" ]; then + exit 2 + fi + + "${t}_cmds" +} + +# ===================================================================== +# Service type display function + +show_service_type() { + info "Detected platform: ${PLATFORM}" + + case "${PLATFORM}" in + FreeBSD) + info "Detected service managers:" + info " - freebsd: YES" + info "Would use freebsd service management." + ;; + Darwin) + info "Detected service managers:" + info " - launchd: YES" + info "Would use launchd service management." + ;; + Linux) + [ "$(check_wsl)" = "YES" ] && info "Detected WSL environment." + info "Detected service managers:" + for t in ${LINUX_INIT_TYPES}; do + info " - ${t}: $("check_${t}")" + done + info "Would use $(detect_linux_svc_type) service management." + ;; + *) + info "${PLATFORM} is not supported by this script. No service file would be installed." + esac + + exit 0 +} + +# ===================================================================== +# Argument handling + +parse_args() { + while [ -n "${1}" ]; do + case "${1}" in + "--source" | "-s") + SVC_SOURCE="${2}" + shift 1 + ;; + "--type" | "-t") + if [ "${2}" = "help" ]; then + help_types + exit 0 + else + SVC_TYPE="${2}" + shift 1 + fi + ;; + "--show-type") SHOW_SVC_TYPE=1 ; INSTALL=0 ;; + "--save-cmds") + if [ -z "${2}" ]; then + info "No path specified to save command variables." + exit 1 + else + SAVE_CMDS_PATH="${2}" + shift 1 + fi + ;; + "--cmds" | "-c") DUMP_CMDS=1 ;; + "--cmds-only") INSTALL=0 ;; + "--export-cmds") EXPORT_CMDS=1 ;; + "--enable" | "-e") ENABLE="enable" ;; + "--disable" | "-d") ENABLE="disable" ;; + "--help" | "-h") + usage + exit 0 + ;; + *) + info "Unrecognized option '${1}'." + exit 1 + ;; + esac + shift 1 + done + + if [ "${SVC_SOURCE#@}" = "libsysdir_POST@" ]; then + SVC_SOURCE="$(dirname "${SCRIPT_SOURCE}")/../../lib/netdata/system" + warning "SVC_SOURCE not templated, using ${SVC_SOURCE} as source directory." + fi + + if [ ! -d "${SVC_SOURCE}" ] && [ "${INSTALL}" -eq 1 ]; then + error "${SVC_SOURCE} does not appear to be a directory. Please specify a valid source for service files with the --source option." + exit 1 + fi + + if valid_types | grep -vw "${SVC_TYPE}"; then + error "${SVC_TYPE} is not supported on this platform." + help_types + exit 1 + fi +} + +# ===================================================================== +# Core logic + +main() { + trap "cleanup" EXIT + + parse_args "${@}" + + if [ "${SHOW_SVC_TYPE}" -eq 1 ]; then + show_service_type + else + case "${PLATFORM}" in + FreeBSD) + [ "${INSTALL}" -eq 1 ] && install_freebsd_service + freebsd_cmds + ;; + Darwin) + [ "${INSTALL}" -eq 1 ] && install_darwin_service + darwin_cmds + ;; + Linux) + [ "${INSTALL}" -eq 1 ] && install_linux_service + linux_cmds + ;; + *) + error "${PLATFORM} is not supported by this script." + exit 5 + ;; + esac + + [ "${DUMP_CMDS}" -eq 1 ] && dump_cmds + [ "${EXPORT_CMDS}" -eq 1 ] && export_cmds + [ -n "${SAVE_CMDS_PATH}" ] && save_cmds + fi + + exit 0 +} + +main "${@}" diff --git a/system/netdata-freebsd.in b/system/netdata-freebsd.in index 2d4f457e2..fd544c86c 100644 --- a/system/netdata-freebsd.in +++ b/system/netdata-freebsd.in @@ -25,6 +25,7 @@ savedb_cmd="netdata_savedb" netdata_prestart() { [ ! -d "${piddir}" ] && mkdir -p "${piddir}" + chown @netdata_user_POST@:@netdata_user_POST@ "${piddir}" return 0 } diff --git a/system/netdata-init-d.in b/system/netdata-init-d.in index 9ac510196..c0257ffab 100644 --- a/system/netdata-init-d.in +++ b/system/netdata-init-d.in @@ -27,6 +27,7 @@ service_start() { [ -x $DAEMON_PATH ] || exit 5 [ ! -d $PIDFILE_PATH ] && mkdir -p $PIDFILE_PATH + chown @netdata_user_POST@:@netdata_user_POST@ $PIDFILE_PATH echo -n "Starting $DAEMON..." daemon $DAEMON_PATH/$DAEMON $DAEMONOPTS RETVAL=$? diff --git a/system/netdata-lsb.in b/system/netdata-lsb.in index ca197a520..e429ad1c9 100644 --- a/system/netdata-lsb.in +++ b/system/netdata-lsb.in @@ -45,6 +45,8 @@ service_start() { mkdir -p $PIDFILE_PATH fi + chown @netdata_user_POST@:@netdata_user_POST@ $PIDFILE_PATH + log_daemon_msg "Starting real-time performance monitoring" "netdata" start_daemon -p $PIDFILE $DAEMON_PATH/$DAEMON $DAEMONOPTS RETVAL=$? diff --git a/system/netdata-openrc.in b/system/netdata-openrc.in index 2acf282e6..158878929 100644 --- a/system/netdata-openrc.in +++ b/system/netdata-openrc.in @@ -4,7 +4,7 @@ # The user netdata is configured to run as. # If you edit its configuration file to set a different # user, set it here too, to have its files switch ownership -: "${NETDATA_OWNER:=netdata:netdata}" +: "${NETDATA_OWNER:=@netdata_user_POST@:@netdata_user_POST@}" # The timeout in seconds to wait for netdata # to save its database on disk and exit. @@ -15,16 +15,18 @@ # to exit. : "${NETDATA_FORCE_EXIT:=0}" -# Netdata will use these services, only if they -# are enabled to start. -: "${NETDATA_START_AFTER_SERVICES:=apache2 squid nginx mysql named opensips upsd hostapd postfix lm_sensors}" +# When set to 1, we use netdatacli for reload/rotate/save commands instead of s-s-d. +: "${NETDATA_USE_NETDATACLI:=0}" + +# Specifies the pidfile to use when running in the background. +: "${NETDATA_PIDFILE:=@localstatedir_POST@/run/netdata/netdata.pid}" extra_started_commands="reload rotate save" -pidfile="@localstatedir_POST@/run/netdata/netdata.pid" -command="@sbindir_POST@/netdata" -command_args="-P ${pidfile} ${NETDATA_EXTRA_ARGS}" +command_prefix="@sbindir_POST@" +command="${command_prefix}/netdata" +command_args="-P ${NETDATA_PIDFILE} ${NETDATA_EXTRA_ARGS}" +command_args_foreground="-D" start_stop_daemon_args="-u ${NETDATA_OWNER}" -required_files="/etc/netdata/netdata.conf" if [ "${NETDATA_FORCE_EXIT}" -eq 1 ]; then retry="TERM/${NETDATA_WAIT_EXIT_TIMEOUT}/KILL/1" else @@ -34,27 +36,53 @@ fi depend() { use logger need net - after ${NETDATA_START_AFTER_SERVICES} + after apache2 squid nginx mysql named opensips upsd hostapd postfix lm_sensors } start_pre() { checkpath -o ${NETDATA_OWNER} -d @localstatedir_POST@/cache/netdata @localstatedir_POST@/run/netdata + + if [ -z "${supervisor}" ]; then + pidfile="${NETDATA_PIDFILE}" + fi +} + +run_cmd() { + cmd="${1}" + msg="${2}" + failmsg="${3}" + signal="${4}" + + ebegin "${msg}" + if [ "${NETDATA_USE_NETDATACLI}" = 1 ]; then + "${command_prefix}/netdatacli" "${cmd}" >/dev/null + eend $? "${failmsg}" + elif [ "${supervisor}" = "supervise-daemon" ]; then + supervise-daemon "${RC_SVCNAME}" --signal "${signal}" + eend $? "${failmsg}" + else + start-stop-daemon --signal "${signal}" --pidfile "${pidfile}" + eend $? "${failmsg}" + fi } reload() { - ebegin "Reloading Netdata" - start-stop-daemon --signal SIGUSR2 --pidfile "${pidfile}" - eend $? "Failed to reload Netdata" + run_cmd reload-health \ + "Reloading Netdata health configuration" \ + "Failed to reload Netdata health configuration" \ + SIGUSR2 } rotate() { - ebegin "Logrotating Netdata" - start-stop-daemon --signal SIGHUP --pidfile "${pidfile}" - eend $? "Failed to logrotate Netdata" + run_cmd reopen-logs \ + "Reopening Netdata log files" \ + "Failed to reopen Netdata log files" \ + SIGHUP } save() { - ebegin "Saving Netdata database" - start-stop-daemon --signal SIGUSR1 --pidfile "${pidfile}" - eend $? "Failed to save Netdata database" + run_cmd save-database \ + "Saving Netdata database" \ + "Failed to save Netdata database" \ + SIGUSR1 } diff --git a/system/netdata.service.in b/system/netdata.service.in index 5affe4fef..3947392f4 100644 --- a/system/netdata.service.in +++ b/system/netdata.service.in @@ -7,16 +7,16 @@ After=network.target httpd.service squid.service nfs-server.service mysqld.servi [Service] Type=simple -User=netdata +User=@netdata_user_POST@ Group=netdata RuntimeDirectory=netdata RuntimeDirectoryMode=0775 -PIDFile=@localstatedir_POST@/run/netdata/netdata.pid -ExecStart=@sbindir_POST@/netdata -P @localstatedir_POST@/run/netdata/netdata.pid -D +PIDFile=/run/netdata/netdata.pid +ExecStart=@sbindir_POST@/netdata -P /run/netdata/netdata.pid -D ExecStartPre=/bin/mkdir -p @localstatedir_POST@/cache/netdata -ExecStartPre=/bin/chown -R netdata:netdata @localstatedir_POST@/cache/netdata -ExecStartPre=/bin/mkdir -p @localstatedir_POST@/run/netdata -ExecStartPre=/bin/chown -R netdata:netdata @localstatedir_POST@/run/netdata +ExecStartPre=/bin/chown -R @netdata_user_POST@ @localstatedir_POST@/cache/netdata +ExecStartPre=/bin/mkdir -p /run/netdata +ExecStartPre=/bin/chown -R @netdata_user_POST@ /run/netdata PermissionsStartOnly=true # saving a big db on slow disks may need some time diff --git a/tests/Makefile.am b/tests/Makefile.am index eb8b5e26e..eb19512df 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,32 +4,32 @@ AUTOMAKE_OPTIONS = subdir-objects MAINTAINERCLEANFILES = $(srcdir)/Makefile.in CLEANFILES = \ - health_mgmtapi/health-cmdapi-test.sh \ - acls/acl.sh \ - urls/request.sh \ - alarm_repetition/alarm.sh \ - template_dimension/template_dim.sh \ + $(srcdir)/health_mgmtapi/health-cmdapi-test.sh \ + $(srcdir)/acls/acl.sh \ + $(srcdir)/urls/request.sh \ + $(srcdir)/alarm_repetition/alarm.sh \ + $(srcdir)/template_dimension/template_dim.sh \ $(NULL) include $(top_srcdir)/build/subst.inc SUFFIXES = .in dist_noinst_DATA = \ - health_mgmtapi/health-cmdapi-test.sh.in \ - acls/acl.sh.in \ - urls/request.sh.in \ - alarm_repetition/alarm.sh.in \ - template_dimension/template_dim.sh.in \ + $(srcdir)/health_mgmtapi/health-cmdapi-test.sh.in \ + $(srcdir)/acls/acl.sh.in \ + $(srcdir)/urls/request.sh.in \ + $(srcdir)/alarm_repetition/alarm.sh.in \ + $(srcdir)/template_dimension/template_dim.sh.in \ $(NULL) dist_plugins_SCRIPTS = \ - health_mgmtapi/health-cmdapi-test.sh \ - acls/acl.sh \ - urls/request.sh \ - alarm_repetition/alarm.sh \ - template_dimension/template_dim.sh \ + $(srcdir)/health_mgmtapi/health-cmdapi-test.sh \ + $(srcdir)/acls/acl.sh \ + $(srcdir)/urls/request.sh \ + $(srcdir)/alarm_repetition/alarm.sh \ + $(srcdir)/template_dimension/template_dim.sh \ $(NULL) dist_noinst_SCRIPTS = \ - stress.sh \ + $(srcdir)/stress.sh \ $(NULL) diff --git a/tests/alarm_repetition/netdata.conf_with_repetition b/tests/alarm_repetition/netdata.conf_with_repetition index dc3aef39b..ddee852ff 100644 --- a/tests/alarm_repetition/netdata.conf_with_repetition +++ b/tests/alarm_repetition/netdata.conf_with_repetition @@ -52,4 +52,4 @@ allow from = * [cloud] - cloud base url = https://app.netdata.cloud + cloud base url = https://api.netdata.cloud diff --git a/tests/alarm_repetition/netdata.conf_without_repetition b/tests/alarm_repetition/netdata.conf_without_repetition index 27c7b86f7..7add03282 100644 --- a/tests/alarm_repetition/netdata.conf_without_repetition +++ b/tests/alarm_repetition/netdata.conf_without_repetition @@ -52,4 +52,4 @@ allow from = * [cloud] - cloud base url = https://app.netdata.cloud + cloud base url = https://api.netdata.cloud diff --git a/tests/profile/benchmark-dictionary.c b/tests/profile/benchmark-dictionary.c index 30c098d5d..7cc9ab0ad 100644 --- a/tests/profile/benchmark-dictionary.c +++ b/tests/profile/benchmark-dictionary.c @@ -19,8 +19,8 @@ void netdata_cleanup_and_exit(int ret) { exit(ret); } int main(int argc, char **argv) { if(argc || argv) {;} -// DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_WITH_STATISTICS); - DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_WITH_STATISTICS); +// DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_WITH_STATISTICS); + DICTIONARY *dict = dictionary_create(DICT_OPTION_STATS); if(!dict) fatal("Cannot create dictionary."); struct rusage start, end; diff --git a/web/api/badges/web_buffer_svg.c b/web/api/badges/web_buffer_svg.c index 00b4ad650..080f2240f 100644 --- a/web/api/badges/web_buffer_svg.c +++ b/web/api/badges/web_buffer_svg.c @@ -893,6 +893,10 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u int group = RRDR_GROUPING_AVERAGE; uint32_t options = 0x00000000; + const RRDCALC_ACQUIRED *rca = NULL; + RRDCALC *rc = NULL; + RRDSET *st = NULL; + while(url) { char *value = mystrsep(&url, "&"); if(!value || !*value) continue; @@ -957,7 +961,7 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u int scale = (scale_str && *scale_str)?str2i(scale_str):100; - RRDSET *st = rrdset_find(host, chart); + st = rrdset_find(host, chart); if(!st) st = rrdset_find_byname(host, chart); if(!st) { buffer_no_cacheable(w->response.data); @@ -967,9 +971,10 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u } st->last_accessed_time = now_realtime_sec(); - RRDCALC *rc = NULL; if(alarm) { - rc = rrdcalc_find(st, alarm); + rca = rrdcalc_from_rrdset_get(st, alarm); + rc = rrdcalc_acquired_to_rrdcalc(rca); + if (!rc) { buffer_no_cacheable(w->response.data); buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1, scale, 0, -1, -1, NULL, NULL); @@ -1020,19 +1025,19 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u label = dim; } else - label = st->name; + label = rrdset_name(st); } if(!units) { if(alarm) { if(rc->units) - units = rc->units; + units = rrdcalc_units(rc); else units = ""; } else if(options & RRDR_OPTION_PERCENTAGE) units = "%"; else - units = st->units; + units = rrdset_units(st); } debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'" @@ -1111,7 +1116,8 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u points, after, before, group, group_options, 0, options, NULL, &latest_timestamp, NULL, NULL, NULL, - &value_is_null, NULL, 0, 0); + &value_is_null, NULL, 0, 0, + QUERY_SOURCE_API_BADGE); // if the value cannot be calculated, show empty badge if (ret != HTTP_RESP_OK) { @@ -1143,7 +1149,8 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u ); } - cleanup: +cleanup: + rrdcalc_from_rrdset_release(st, rca); buffer_free(dimensions); return ret; } diff --git a/web/api/badges/web_buffer_svg.h b/web/api/badges/web_buffer_svg.h index 4853a8864..71857811f 100644 --- a/web/api/badges/web_buffer_svg.h +++ b/web/api/badges/web_buffer_svg.h @@ -6,12 +6,12 @@ #include "libnetdata/libnetdata.h" #include "web/server/web_client.h" -extern void buffer_svg(BUFFER *wb, const char *label, +void buffer_svg(BUFFER *wb, const char *label, NETDATA_DOUBLE value, const char *units, const char *label_color, const char *value_color, int precision, int scale, uint32_t options, int fixed_width_lbl, int fixed_width_val, const char* text_color_lbl, const char* text_color_val); -extern char *format_value_and_unit(char *value_string, size_t value_string_len, +char *format_value_and_unit(char *value_string, size_t value_string_len, NETDATA_DOUBLE value, const char *units, int precision); -extern int web_client_api_request_v1_badge(struct rrdhost *host, struct web_client *w, char *url); +int web_client_api_request_v1_badge(struct rrdhost *host, struct web_client *w, char *url); #include "web/api/web_api_v1.h" diff --git a/web/api/exporters/allmetrics.h b/web/api/exporters/allmetrics.h index f076ff0d5..3afc42e28 100644 --- a/web/api/exporters/allmetrics.h +++ b/web/api/exporters/allmetrics.h @@ -7,6 +7,6 @@ #include "shell/allmetrics_shell.h" #include "web/server/web_client.h" -extern int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url); #endif //NETDATA_API_ALLMETRICS_H diff --git a/web/api/exporters/shell/allmetrics_shell.c b/web/api/exporters/shell/allmetrics_shell.c index 615aab43c..0ffbac67b 100644 --- a/web/api/exporters/shell/allmetrics_shell.c +++ b/web/api/exporters/shell/allmetrics_shell.c @@ -25,73 +25,71 @@ static inline size_t shell_name_copy(char *d, const char *s, size_t usable) { void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, const char *filter_string, BUFFER *wb) { analytics_log_shell(); SIMPLE_PATTERN *filter = simple_pattern_create(filter_string, NULL, SIMPLE_PATTERN_EXACT); - rrdhost_rdlock(host); // for each chart RRDSET *st; rrdset_foreach_read(st, host) { - if (filter && !simple_pattern_matches(filter, st->name)) + if (filter && !simple_pattern_matches(filter, rrdset_name(st))) continue; NETDATA_DOUBLE total = 0.0; char chart[SHELL_ELEMENT_MAX + 1]; - shell_name_copy(chart, st->name?st->name:st->id, SHELL_ELEMENT_MAX); + shell_name_copy(chart, st->name?rrdset_name(st):rrdset_id(st), SHELL_ELEMENT_MAX); - buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", st->id, st->name); + buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", rrdset_id(st), rrdset_name(st)); if(rrdset_is_available_for_viewers(st)) { - rrdset_rdlock(st); - // for each dimension RRDDIM *rd; rrddim_foreach_read(rd, st) { if(rd->collections_counter && !rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) { char dimension[SHELL_ELEMENT_MAX + 1]; - shell_name_copy(dimension, rd->name?rd->name:rd->id, SHELL_ELEMENT_MAX); + shell_name_copy(dimension, rd->name?rrddim_name(rd):rrddim_id(rd), SHELL_ELEMENT_MAX); NETDATA_DOUBLE n = rd->last_stored_value; if(isnan(n) || isinf(n)) - buffer_sprintf(wb, "NETDATA_%s_%s=\"\" # %s\n", chart, dimension, st->units); + buffer_sprintf(wb, "NETDATA_%s_%s=\"\" # %s\n", chart, dimension, rrdset_units(st)); else { if(rd->multiplier < 0 || rd->divisor < 0) n = -n; n = roundndd(n); - if(!rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) total += n; - buffer_sprintf(wb, "NETDATA_%s_%s=\"" NETDATA_DOUBLE_FORMAT_ZERO "\" # %s\n", chart, dimension, n, st->units); + if(!rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN)) total += n; + buffer_sprintf(wb, "NETDATA_%s_%s=\"" NETDATA_DOUBLE_FORMAT_ZERO "\" # %s\n", chart, dimension, n, rrdset_units(st)); } } } + rrddim_foreach_done(rd); total = roundndd(total); - buffer_sprintf(wb, "NETDATA_%s_VISIBLETOTAL=\"" NETDATA_DOUBLE_FORMAT_ZERO "\" # %s\n", chart, total, st->units); - rrdset_unlock(st); + buffer_sprintf(wb, "NETDATA_%s_VISIBLETOTAL=\"" NETDATA_DOUBLE_FORMAT_ZERO "\" # %s\n", chart, total, rrdset_units(st)); } } + rrdset_foreach_done(st); buffer_strcat(wb, "\n# NETDATA ALARMS RUNNING\n"); RRDCALC *rc; - for(rc = host->alarms; rc ;rc = rc->next) { + foreach_rrdcalc_in_rrdhost_read(host, rc) { if(!rc->rrdset) continue; char chart[SHELL_ELEMENT_MAX + 1]; - shell_name_copy(chart, rc->rrdset->name?rc->rrdset->name:rc->rrdset->id, SHELL_ELEMENT_MAX); + shell_name_copy(chart, rc->rrdset->name?rrdset_name(rc->rrdset):rrdset_id(rc->rrdset), SHELL_ELEMENT_MAX); char alarm[SHELL_ELEMENT_MAX + 1]; - shell_name_copy(alarm, rc->name, SHELL_ELEMENT_MAX); + shell_name_copy(alarm, rrdcalc_name(rc), SHELL_ELEMENT_MAX); NETDATA_DOUBLE n = rc->value; if(isnan(n) || isinf(n)) - buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"\" # %s\n", chart, alarm, rc->units); + buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"\" # %s\n", chart, alarm, rrdcalc_units(rc)); else { n = roundndd(n); - buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"" NETDATA_DOUBLE_FORMAT_ZERO "\" # %s\n", chart, alarm, n, rc->units); + buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"" NETDATA_DOUBLE_FORMAT_ZERO "\" # %s\n", chart, alarm, n, rrdcalc_units(rc)); } buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_STATUS=\"%s\"\n", chart, alarm, rrdcalc_status2string(rc->status)); } + foreach_rrdcalc_in_rrdhost_done(rc); - rrdhost_unlock(host); simple_pattern_free(filter); } @@ -100,7 +98,6 @@ void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, const char *filter_ void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, const char *filter_string, BUFFER *wb) { analytics_log_json(); SIMPLE_PATTERN *filter = simple_pattern_create(filter_string, NULL, SIMPLE_PATTERN_EXACT); - rrdhost_rdlock(host); buffer_strcat(wb, "{"); @@ -110,12 +107,10 @@ void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, const char *filter_s // for each chart RRDSET *st; rrdset_foreach_read(st, host) { - if (filter && !(simple_pattern_matches(filter, st->id) || simple_pattern_matches(filter, st->name))) + if (filter && !(simple_pattern_matches(filter, rrdset_id(st)) || simple_pattern_matches(filter, rrdset_name(st)))) continue; if(rrdset_is_available_for_viewers(st)) { - rrdset_rdlock(st); - buffer_sprintf( wb, "%s\n" @@ -127,12 +122,12 @@ void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, const char *filter_s "\t\t\"last_updated\": %"PRId64",\n" "\t\t\"dimensions\": {", chart_counter ? "," : "", - st->id, - st->name, - st->family, - st->context, - st->units, - (int64_t)rrdset_last_entry_t_nolock(st)); + rrdset_id(st), + rrdset_name(st), + rrdset_family(st), + rrdset_context(st), + rrdset_units(st), + (int64_t)rrdset_last_entry_t(st)); chart_counter++; dimension_counter = 0; @@ -148,8 +143,8 @@ void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, const char *filter_s "\t\t\t\t\"name\": \"%s\",\n" "\t\t\t\t\"value\": ", dimension_counter ? "," : "", - rd->id, - rd->name); + rrddim_id(rd), + rrddim_name(rd)); if(isnan(rd->last_stored_value)) buffer_strcat(wb, "null"); @@ -161,14 +156,14 @@ void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, const char *filter_s dimension_counter++; } } + rrddim_foreach_done(rd); buffer_strcat(wb, "\n\t\t}\n\t}"); - rrdset_unlock(st); } } + rrdset_foreach_done(st); buffer_strcat(wb, "\n}"); - rrdhost_unlock(host); simple_pattern_free(filter); } diff --git a/web/api/exporters/shell/allmetrics_shell.h b/web/api/exporters/shell/allmetrics_shell.h index 1ee9aa717..d6598e08d 100644 --- a/web/api/exporters/shell/allmetrics_shell.h +++ b/web/api/exporters/shell/allmetrics_shell.h @@ -15,7 +15,7 @@ #define ALLMETRICS_JSON 3 #define ALLMETRICS_PROMETHEUS_ALL_HOSTS 4 -extern void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, const char *filter_string, BUFFER *wb); -extern void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, const char *filter_string, BUFFER *wb); +void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, const char *filter_string, BUFFER *wb); +void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, const char *filter_string, BUFFER *wb); #endif //NETDATA_API_ALLMETRICS_SHELL_H diff --git a/web/api/formatters/charts2json.c b/web/api/formatters/charts2json.c index 4325b6530..1fc20b493 100644 --- a/web/api/formatters/charts2json.c +++ b/web/api/formatters/charts2json.c @@ -57,11 +57,11 @@ void charts2json(RRDHOST *host, BUFFER *wb, int skip_volatile, int show_archived ",\n\t\"memory_mode\": \"%s\"" ",\n\t\"custom_info\": \"%s\"" ",\n\t\"charts\": {" - , host->hostname - , host->program_version + , rrdhost_hostname(host) + , rrdhost_program_version(host) , get_release_channel() - , host->os - , host->timezone + , rrdhost_os(host) + , rrdhost_timezone(host) , host->rrd_update_every , host->rrd_history_entries , rrd_memory_mode_name(host->rrd_memory_mode) @@ -69,12 +69,11 @@ void charts2json(RRDHOST *host, BUFFER *wb, int skip_volatile, int show_archived ); c = 0; - rrdhost_rdlock(host); rrdset_foreach_read(st, host) { if ((!show_archived && rrdset_is_available_for_viewers(st)) || (show_archived && rrdset_is_archived(st))) { if(c) buffer_strcat(wb, ","); buffer_strcat(wb, "\n\t\t\""); - buffer_strcat(wb, st->id); + buffer_strcat(wb, rrdset_id(st)); buffer_strcat(wb, "\": "); rrdset2json(st, wb, &dimensions, &memory, skip_volatile); @@ -82,13 +81,14 @@ void charts2json(RRDHOST *host, BUFFER *wb, int skip_volatile, int show_archived st->last_accessed_time = now; } } + rrdset_foreach_done(st); RRDCALC *rc; - for(rc = host->alarms; rc ; rc = rc->next) { + foreach_rrdcalc_in_rrdhost_read(host, rc) { if(rc->rrdset) alarms++; } - rrdhost_unlock(host); + foreach_rrdcalc_in_rrdhost_done(rc); buffer_sprintf(wb , "\n\t}" @@ -117,7 +117,7 @@ void charts2json(RRDHOST *host, BUFFER *wb, int skip_volatile, int show_archived "\n\t\t\t\"hostname\": \"%s\"" "\n\t\t}" , (found > 0) ? "," : "" - , h->hostname + , rrdhost_hostname(h) ); found++; @@ -131,7 +131,7 @@ void charts2json(RRDHOST *host, BUFFER *wb, int skip_volatile, int show_archived , "\n\t\t{" "\n\t\t\t\"hostname\": \"%s\"" "\n\t\t}" - , host->hostname + , rrdhost_hostname(host) ); } @@ -141,8 +141,8 @@ void charts2json(RRDHOST *host, BUFFER *wb, int skip_volatile, int show_archived // generate collectors list for the api/v1/info call struct collector { - char *plugin; - char *module; + const char *plugin; + const char *module; }; struct array_printer { @@ -150,9 +150,7 @@ struct array_printer { BUFFER *wb; }; -static int print_collector_callback(const char *name, void *entry, void *data) { - (void)name; - +static int print_collector_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data) { struct array_printer *ap = (struct array_printer *)data; BUFFER *wb = ap->wb; struct collector *col=(struct collector *) entry; @@ -167,24 +165,23 @@ static int print_collector_callback(const char *name, void *entry, void *data) { } void chartcollectors2json(RRDHOST *host, BUFFER *wb) { - DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED); RRDSET *st; char name[500]; time_t now = now_realtime_sec(); - rrdhost_rdlock(host); rrdset_foreach_read(st, host) { if (rrdset_is_available_for_viewers(st)) { struct collector col = { - .plugin = st->plugin_name ? st->plugin_name : "", - .module = st->module_name ? st->module_name : "" + .plugin = rrdset_plugin_name(st), + .module = rrdset_module_name(st) }; sprintf(name, "%s:%s", col.plugin, col.module); dictionary_set(dict, name, &col, sizeof(struct collector)); st->last_accessed_time = now; } } - rrdhost_unlock(host); + rrdset_foreach_done(st); struct array_printer ap = { .c = 0, .wb = wb diff --git a/web/api/formatters/charts2json.h b/web/api/formatters/charts2json.h index 2d8cce310..d4b04af58 100644 --- a/web/api/formatters/charts2json.h +++ b/web/api/formatters/charts2json.h @@ -5,8 +5,8 @@ #include "rrd2json.h" -extern void charts2json(RRDHOST *host, BUFFER *wb, int skip_volatile, int show_archived); -extern void chartcollectors2json(RRDHOST *host, BUFFER *wb); -extern const char* get_release_channel(); +void charts2json(RRDHOST *host, BUFFER *wb, int skip_volatile, int show_archived); +void chartcollectors2json(RRDHOST *host, BUFFER *wb); +const char* get_release_channel(); #endif //NETDATA_API_FORMATTER_CHARTS2JSON_H diff --git a/web/api/formatters/csv/csv.c b/web/api/formatters/csv/csv.c index 6d87ca374..603a17169 100644 --- a/web/api/formatters/csv/csv.c +++ b/web/api/formatters/csv/csv.c @@ -3,15 +3,14 @@ #include "libnetdata/libnetdata.h" #include "csv.h" -void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines, RRDDIM *temp_rd) { - rrdset_check_rdlock(r->st); - +void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines) { //info("RRD2CSV(): %s: BEGIN", r->st->id); + QUERY_TARGET *qt = r->internal.qt; long c, i; - RRDDIM *d; + const long used = qt->query.used; // print the csv header - for(c = 0, i = 0, d = temp_rd?temp_rd:r->st->dimensions; d && c < r->d ;c++, d = d->next) { + for(c = 0, i = 0; c < used ; c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; @@ -23,7 +22,7 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const } buffer_strcat(wb, separator); if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\""); - buffer_strcat(wb, d->name); + buffer_strcat(wb, string2str(qt->query.array[c].dimension.name)); if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\""); i++; } @@ -31,7 +30,7 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const if(format == DATASOURCE_CSV_MARKDOWN) { // print the --- line after header - for(c = 0, i = 0, d = temp_rd?temp_rd:r->st->dimensions; d && c < r->d ;c++, d = d->next) { + for(c = 0, i = 0; c < used ;c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; @@ -89,7 +88,7 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const int set_min_max = 0; if(unlikely(options & RRDR_OPTION_PERCENTAGE)) { total = 0; - for(c = 0, d = temp_rd?temp_rd:r->st->dimensions; d && c < r->d ;c++, d = d->next) { + for(c = 0; c < used ;c++) { NETDATA_DOUBLE n = cn[c]; if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0)) @@ -103,7 +102,7 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const } // for each dimension - for(c = 0, d = temp_rd?temp_rd:r->st->dimensions; d && c < r->d ;c++, d = d->next) { + for(c = 0; c < used ;c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; diff --git a/web/api/formatters/csv/csv.h b/web/api/formatters/csv/csv.h index cf6020de4..666d4c660 100644 --- a/web/api/formatters/csv/csv.h +++ b/web/api/formatters/csv/csv.h @@ -5,7 +5,7 @@ #include "web/api/queries/rrdr.h" -extern void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines, RRDDIM *temp_rd); +void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines); #include "../rrd2json.h" diff --git a/web/api/formatters/json/json.c b/web/api/formatters/json/json.c index 6f07b9aa4..608150cba 100644 --- a/web/api/formatters/json/json.c +++ b/web/api/formatters/json/json.c @@ -5,15 +5,7 @@ #define JSON_DATES_JS 1 #define JSON_DATES_TIMESTAMP 2 -void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct context_param *context_param_list) -{ - RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL; - - int should_lock = (!context_param_list || !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE)); - - if (should_lock) - rrdset_check_rdlock(r->st); - +void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) { //info("RRD2JSON(): %s: BEGIN", r->st->id); int row_annotations = 0, dates, dates_with_new = 0; char kq[2] = "", // key quote @@ -112,21 +104,21 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct // ------------------------------------------------------------------------- // print the JSON header + QUERY_TARGET *qt = r->internal.qt; long c, i; - RRDDIM *rd; + const long used = qt->query.used; // print the header lines - for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) { + for(c = 0, i = 0; c < used ; c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; buffer_fast_strcat(wb, pre_label, pre_label_len); - buffer_strcat(wb, rd->name); -// buffer_strcat(wb, "."); -// buffer_strcat(wb, rd->rrdset->name); + buffer_strcat(wb, string2str(qt->query.array[c].dimension.name)); buffer_fast_strcat(wb, post_label, post_label_len); i++; } + if(!i) { buffer_fast_strcat(wb, pre_label, pre_label_len); buffer_fast_strcat(wb, "no data", 7); @@ -134,7 +126,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct } size_t total_number_of_dimensions = i; - // print the begin of row data + // print the beginning of row data buffer_strcat(wb, data_begin); // if all dimensions are hidden, print a null @@ -187,7 +179,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct if(unlikely(row_annotations)) { // google supports one annotation per row int annotation_found = 0; - for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd ;c++, rd = rd->next) { + for(c = 0; c < used ; c++) { if(unlikely(!(r->od[c] & RRDR_DIMENSION_SELECTED))) continue; if(unlikely(co[c] & RRDR_VALUE_RESET)) { @@ -222,7 +214,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct int set_min_max = 0; if(unlikely(options & RRDR_OPTION_PERCENTAGE)) { total = 0; - for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) { + for(c = 0; c < used ;c++) { NETDATA_DOUBLE n; if(unlikely(options & RRDR_OPTION_INTERNAL_AR)) n = ar[c]; @@ -240,7 +232,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct } // for each dimension - for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) { + for(c = 0; c < used ;c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; @@ -253,7 +245,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct buffer_fast_strcat(wb, pre_value, pre_value_len); if(unlikely( options & RRDR_OPTION_OBJECTSROWS )) - buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq); + buffer_sprintf(wb, "%s%s%s: ", kq, string2str(qt->query.array[c].dimension.name), kq); if(co[c] & RRDR_VALUE_EMPTY && !(options & RRDR_OPTION_INTERNAL_AR)) { if(unlikely(options & RRDR_OPTION_NULL2ZERO)) diff --git a/web/api/formatters/json/json.h b/web/api/formatters/json/json.h index 5c4e11371..fb59e5c9a 100644 --- a/web/api/formatters/json/json.h +++ b/web/api/formatters/json/json.h @@ -5,6 +5,6 @@ #include "../rrd2json.h" -extern void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct context_param *context_param_list); +void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable); #endif //NETDATA_API_FORMATTER_JSON_H diff --git a/web/api/formatters/json_wrapper.c b/web/api/formatters/json_wrapper.c index 04cace2fb..8b9b7522c 100644 --- a/web/api/formatters/json_wrapper.c +++ b/web/api/formatters/json_wrapper.c @@ -7,9 +7,7 @@ struct value_output { BUFFER *wb; }; -static int value_list_output(const char *name, void *entry, void *data) { - (void)name; - +static int value_list_output_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data) { struct value_output *ap = (struct value_output *)data; BUFFER *wb = ap->wb; char *output = (char *) entry; @@ -35,25 +33,17 @@ static int fill_formatted_callback(const char *name, const char *value, RRDLABEL } void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, - RRDR_GROUPING group_method, QUERY_PARAMS *rrdset_query_data) + RRDR_GROUPING group_method) { - struct context_param *context_param_list = rrdset_query_data->context_param_list; - char *chart_label_key = rrdset_query_data->chart_label_key; - - RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL; - int should_lock = (!context_param_list || !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE)); - uint8_t context_mode = (!context_param_list || (context_param_list->flags & CONTEXT_FLAGS_CONTEXT)); - - if (should_lock) - rrdset_check_rdlock(r->st); + QUERY_TARGET *qt = r->internal.qt; long rows = rrdr_rows(r); long c, i; - RRDDIM *rd; + const long query_used = qt->query.used; //info("JSONWRAPPER(): %s: BEGIN", r->st->id); char kq[2] = "", // key quote - sq[2] = ""; // string quote + sq[2] = ""; // string quote if( options & RRDR_OPTION_GOOGLE_JSON ) { kq[0] = '\0'; @@ -64,52 +54,47 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS sq[0] = '"'; } - if (should_lock) - rrdset_rdlock(r->st); buffer_sprintf(wb, "{\n" " %sapi%s: 1,\n" " %sid%s: %s%s%s,\n" " %sname%s: %s%s%s,\n" - " %sview_update_every%s: %d,\n" - " %supdate_every%s: %d,\n" - " %sfirst_entry%s: %u,\n" - " %slast_entry%s: %u,\n" - " %sbefore%s: %u,\n" - " %safter%s: %u,\n" + " %sview_update_every%s: %lld,\n" + " %supdate_every%s: %lld,\n" + " %sfirst_entry%s: %lld,\n" + " %slast_entry%s: %lld,\n" + " %sbefore%s: %lld,\n" + " %safter%s: %lld,\n" " %sgroup%s: %s%s%s,\n" " %soptions%s: %s" , kq, kq - , kq, kq, sq, context_mode && temp_rd?r->st->context:r->st->id, sq - , kq, kq, sq, context_mode && temp_rd?r->st->context:r->st->name, sq - , kq, kq, r->update_every - , kq, kq, r->st->update_every - , kq, kq, (uint32_t) (context_param_list ? context_param_list->first_entry_t : rrdset_first_entry_t_nolock(r->st)) - , kq, kq, (uint32_t) (context_param_list ? context_param_list->last_entry_t : rrdset_last_entry_t_nolock(r->st)) - , kq, kq, (uint32_t)r->before - , kq, kq, (uint32_t)r->after + , kq, kq, sq, qt->id, sq + , kq, kq, sq, qt->id, sq + , kq, kq, (long long)r->update_every + , kq, kq, (long long)qt->db.minimum_latest_update_every + , kq, kq, (long long)qt->db.first_time_t + , kq, kq, (long long)qt->db.last_time_t + , kq, kq, (long long)r->before + , kq, kq, (long long)r->after , kq, kq, sq, web_client_api_request_v1_data_group_to_string(group_method), sq , kq, kq, sq); - web_client_api_request_v1_data_options_to_string(wb, r->internal.query_options); + web_client_api_request_v1_data_options_to_buffer(wb, r->internal.query_options); buffer_sprintf(wb, "%s,\n %sdimension_names%s: [", sq, kq, kq); - if (should_lock) - rrdset_unlock(r->st); - - for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) { + for(c = 0, i = 0; c < query_used ; c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; if(i) buffer_strcat(wb, ", "); buffer_strcat(wb, sq); - buffer_strcat(wb, rd->name); + buffer_strcat(wb, string2str(qt->query.array[c].dimension.name)); buffer_strcat(wb, sq); i++; } if(!i) { #ifdef NETDATA_INTERNAL_CHECKS - error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options); + error("QUERY: '%s', RRDR is empty, %zu dimensions, options is 0x%08x", qt->id, r->d, options); #endif rows = 0; buffer_strcat(wb, sq); @@ -121,13 +106,13 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS " %sdimension_ids%s: [" , kq, kq); - for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) { + for(c = 0, i = 0; c < query_used ; c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; if(i) buffer_strcat(wb, ", "); buffer_strcat(wb, sq); - buffer_strcat(wb, rd->id); + buffer_strcat(wb, string2str(qt->query.array[c].dimension.id)); buffer_strcat(wb, sq); i++; } @@ -139,7 +124,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS } buffer_strcat(wb, "],\n"); - if (rrdset_query_data->show_dimensions) { + if (r->internal.query_options & RRDR_OPTION_ALL_DIMENSIONS) { buffer_sprintf(wb, " %sfull_dimension_list%s: [", kq, kq); char name[RRD_ID_LENGTH_MAX * 2 + 2]; @@ -147,58 +132,97 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS struct value_output co = {.c = 0, .wb = wb}; - DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); - for (i = 0, rd = temp_rd ? temp_rd : r->st->dimensions; rd; rd = rd->next) { - snprintfz(name, RRD_ID_LENGTH_MAX * 2, "%s:%s", rd->id, rd->name); - int len = snprintfz(output, RRD_ID_LENGTH_MAX * 2 + 7, "[\"%s\",\"%s\"]", rd->id, rd->name); - dictionary_set(dict, name, output, len+1); + DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE); + for (c = 0; c < (long)qt->metrics.used ;c++) { + snprintfz(name, RRD_ID_LENGTH_MAX * 2 + 1, "%s:%s", + rrdmetric_acquired_id(qt->metrics.array[c]), + rrdmetric_acquired_name(qt->metrics.array[c])); + + int len = snprintfz(output, RRD_ID_LENGTH_MAX * 2 + 7, "[\"%s\",\"%s\"]", + rrdmetric_acquired_id(qt->metrics.array[c]), + rrdmetric_acquired_name(qt->metrics.array[c])); + + dictionary_set(dict, name, output, len + 1); } - dictionary_walkthrough_read(dict, value_list_output, &co); + dictionary_walkthrough_read(dict, value_list_output_callback, &co); dictionary_destroy(dict); co.c = 0; buffer_sprintf(wb, "],\n %sfull_chart_list%s: [", kq, kq); - dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); - for (i = 0, rd = temp_rd ? temp_rd : r->st->dimensions; rd; rd = rd->next) { - int len = snprintfz(output, RRD_ID_LENGTH_MAX * 2 + 7, "[\"%s\",\"%s\"]", rd->rrdset->id, rd->rrdset->name); - snprintfz(name, RRD_ID_LENGTH_MAX * 2, "%s:%s", rd->rrdset->id, rd->rrdset->name); + dict = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE); + for (c = 0; c < (long)qt->instances.used ; c++) { + RRDINSTANCE_ACQUIRED *ria = qt->instances.array[c]; + + snprintfz(name, RRD_ID_LENGTH_MAX * 2 + 1, "%s:%s", + rrdinstance_acquired_id(ria), + rrdinstance_acquired_name(ria)); + + int len = snprintfz(output, RRD_ID_LENGTH_MAX * 2 + 7, "[\"%s\",\"%s\"]", + rrdinstance_acquired_id(ria), + rrdinstance_acquired_name(ria)); + dictionary_set(dict, name, output, len + 1); } - - dictionary_walkthrough_read(dict, value_list_output, &co); + dictionary_walkthrough_read(dict, value_list_output_callback, &co); dictionary_destroy(dict); - RRDSET *st; co.c = 0; buffer_sprintf(wb, "],\n %sfull_chart_labels%s: [", kq, kq); - dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); - for (i = 0, rd = temp_rd ? temp_rd : r->st->dimensions; rd; rd = rd->next) { - st = rd->rrdset; - if (st->state && st->state->chart_labels) - rrdlabels_walkthrough_read(st->state->chart_labels, fill_formatted_callback, dict); + dict = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE); + for (c = 0; c < (long)qt->instances.used ; c++) { + RRDINSTANCE_ACQUIRED *ria = qt->instances.array[c]; + rrdlabels_walkthrough_read(rrdinstance_acquired_labels(ria), fill_formatted_callback, dict); } - dictionary_walkthrough_read(dict, value_list_output, &co); + dictionary_walkthrough_read(dict, value_list_output_callback, &co); dictionary_destroy(dict); buffer_strcat(wb, "],\n"); } - // Composite charts - if (context_mode && temp_rd) { + // functions + { + DICTIONARY *funcs = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE); + RRDINSTANCE_ACQUIRED *ria = NULL; + for (c = 0; c < query_used ; c++) { + QUERY_METRIC *qm = &qt->query.array[c]; + if(qm->link.ria == ria) + continue; + + ria = qm->link.ria; + chart_functions_to_dict(rrdinstance_acquired_functions(ria), funcs); + } + + buffer_sprintf(wb, " %sfunctions%s: [", kq, kq); + void *t; (void)t; + dfe_start_read(funcs, t) { + const char *comma = ""; + if(t_dfe.counter) comma = ", "; + buffer_sprintf(wb, "%s%s%s%s", comma, sq, t_dfe.name, sq); + } + dfe_done(t); + dictionary_destroy(funcs); + buffer_strcat(wb, "],\n"); + } + + // context query + if (!qt->request.st) { buffer_sprintf( wb, " %schart_ids%s: [", kq, kq); - for (c = 0, i = 0, rd = temp_rd ; rd && c < r->d; c++, rd = rd->next) { + for (c = 0, i = 0; c < query_used; c++) { + QUERY_METRIC *qm = &qt->query.array[c]; + if (unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; + if (unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; if (i) buffer_strcat(wb, ", "); buffer_strcat(wb, sq); - buffer_strcat(wb, rd->rrdset->id); + buffer_strcat(wb, string2str(qm->chart.id)); buffer_strcat(wb, sq); i++; } @@ -209,29 +233,29 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS buffer_strcat(wb, sq); } buffer_strcat(wb, "],\n"); - if (chart_label_key) { + if (qt->instances.chart_label_key_pattern) { buffer_sprintf(wb, " %schart_labels%s: { ", kq, kq); - SIMPLE_PATTERN *pattern = simple_pattern_create(chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); - SIMPLE_PATTERN *original_pattern = pattern; + SIMPLE_PATTERN *pattern = qt->instances.chart_label_key_pattern; char *label_key = NULL; int keys = 0; while (pattern && (label_key = simple_pattern_iterate(&pattern))) { - if (keys) buffer_strcat(wb, ", "); buffer_sprintf(wb, "%s%s%s : [", kq, label_key, kq); keys++; - for (c = 0, i = 0, rd = temp_rd; rd && c < r->d; c++, rd = rd->next) { + for (c = 0, i = 0; c < query_used; c++) { + QUERY_METRIC *qm = &qt->query.array[c]; + if (unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if (unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; + if (i) buffer_strcat(wb, ", "); - - rrdlabels_get_value_to_buffer_or_null(rd->rrdset->state->chart_labels, wb, label_key, sq, "null"); + rrdlabels_get_value_to_buffer_or_null(rrdinstance_acquired_labels(qm->link.ria), wb, label_key, sq, "null"); i++; } if (!i) { @@ -243,33 +267,26 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS buffer_strcat(wb, "]"); } buffer_strcat(wb, "},\n"); - simple_pattern_free(original_pattern); } } buffer_sprintf(wb, " %slatest_values%s: [" , kq, kq); - for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) { + for(c = 0, i = 0; c < query_used ;c++) { + QUERY_METRIC *qm = &qt->query.array[c]; + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; if(i) buffer_strcat(wb, ", "); i++; - NETDATA_DOUBLE value = rd->last_stored_value; + NETDATA_DOUBLE value = rrdmetric_acquired_last_stored_value(qm->link.rma); if (NAN == value) buffer_strcat(wb, "null"); else buffer_rrd_value(wb, value); - /* - storage_number n = rd->values[rrdset_last_slot(r->st)]; - - if(!does_storage_number_exist(n)) - buffer_strcat(wb, "null"); - else - buffer_rrd_value(wb, unpack_storage_number(n)); - */ } if(!i) { rows = 0; @@ -286,7 +303,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS if(unlikely(options & RRDR_OPTION_PERCENTAGE)) { total = 0; - for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) { + for(c = 0; c < query_used ;c++) { NETDATA_DOUBLE *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ]; NETDATA_DOUBLE n = cn[c]; @@ -299,7 +316,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS if(total == 0) total = 1; } - for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) { + for(c = 0, i = 0; c < query_used ;c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; @@ -349,16 +366,11 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS , kq, kq ); - for(int tier = 0; tier < storage_tiers ; tier++) + for(size_t tier = 0; tier < storage_tiers ; tier++) buffer_sprintf(wb, "%s%zu", tier>0?", ":"", r->internal.tier_points_read[tier]); buffer_strcat(wb, " ]"); - if((options & RRDR_OPTION_CUSTOM_VARS) && (options & RRDR_OPTION_JSON_WRAP)) { - buffer_sprintf(wb, ",\n %schart_variables%s: ", kq, kq); - health_api_v1_chart_custom_variables2json(r->st, wb); - } - buffer_sprintf(wb, ",\n %sresult%s: ", kq, kq); if(string_value) buffer_strcat(wb, sq); diff --git a/web/api/formatters/json_wrapper.h b/web/api/formatters/json_wrapper.h index bfadc883e..91c1475c5 100644 --- a/web/api/formatters/json_wrapper.h +++ b/web/api/formatters/json_wrapper.h @@ -7,9 +7,9 @@ #include "web/api/queries/query.h" -extern void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, - RRDR_GROUPING group_method, QUERY_PARAMS *query_params); -extern void rrdr_json_wrapper_anomaly_rates(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value); -extern void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value); +void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, + RRDR_GROUPING group_method); +void rrdr_json_wrapper_anomaly_rates(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value); +void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value); #endif //NETDATA_API_FORMATTER_JSON_WRAPPER_H diff --git a/web/api/formatters/rrd2json.c b/web/api/formatters/rrd2json.c index 7aa478d95..8bf547192 100644 --- a/web/api/formatters/rrd2json.c +++ b/web/api/formatters/rrd2json.c @@ -3,129 +3,6 @@ #include "web/api/web_api_v1.h" #include "database/storage_engine.h" -static inline void free_single_rrdrim(ONEWAYALLOC *owa, RRDDIM *temp_rd, int archive_mode) -{ - if (unlikely(!temp_rd)) - return; - - onewayalloc_freez(owa, (char *)temp_rd->id); - - if (unlikely(archive_mode)) { - temp_rd->rrdset->counter--; - if (!temp_rd->rrdset->counter) { - onewayalloc_freez(owa, (char *)temp_rd->rrdset->name); - onewayalloc_freez(owa, temp_rd->rrdset->context); - onewayalloc_freez(owa, temp_rd->rrdset); - } - } - - for(int tier = 0; tier < storage_tiers ;tier++) { - if(!temp_rd->tiers[tier]) continue; - - if(archive_mode) { - STORAGE_ENGINE *eng = storage_engine_get(temp_rd->tiers[tier]->mode); - if (eng) - eng->api.free(temp_rd->tiers[tier]->db_metric_handle); - } - - onewayalloc_freez(owa, temp_rd->tiers[tier]); - } - - onewayalloc_freez(owa, temp_rd); -} - -static inline void free_rrddim_list(ONEWAYALLOC *owa, RRDDIM *temp_rd, int archive_mode) -{ - if (unlikely(!temp_rd)) - return; - - RRDDIM *t; - while (temp_rd) { - t = temp_rd->next; - free_single_rrdrim(owa, temp_rd, archive_mode); - temp_rd = t; - } -} - -void free_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list) -{ - if (unlikely(!param_list || !*param_list)) - return; - - free_rrddim_list(owa, ((*param_list)->rd), (*param_list)->flags & CONTEXT_FLAGS_ARCHIVE); - onewayalloc_freez(owa, (*param_list)); - *param_list = NULL; -} - -void rebuild_context_param_list(ONEWAYALLOC *owa, struct context_param *context_param_list, time_t after_requested) -{ - RRDDIM *temp_rd = context_param_list->rd; - RRDDIM *new_rd_list = NULL, *t; - int is_archived = (context_param_list->flags & CONTEXT_FLAGS_ARCHIVE); - - RRDSET *st = temp_rd->rrdset; - RRDSET *last_st = st; - time_t last_entry_t = is_archived ? st->last_entry_t : rrdset_last_entry_t(st); - time_t last_last_entry_t = last_entry_t; - while (temp_rd) { - t = temp_rd->next; - - st = temp_rd->rrdset; - if (st == last_st) { - last_entry_t = last_last_entry_t; - }else { - last_entry_t = is_archived ? st->last_entry_t : rrdset_last_entry_t(st); - last_last_entry_t = last_entry_t; - last_st = st; - } - - if (last_entry_t >= after_requested) { - temp_rd->next = new_rd_list; - new_rd_list = temp_rd; - } else - free_single_rrdrim(owa, temp_rd, is_archived); - temp_rd = t; - } - context_param_list->rd = new_rd_list; -}; - -void build_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list, RRDSET *st) -{ - if (unlikely(!param_list || !st)) - return; - - if (unlikely(!(*param_list))) { - *param_list = onewayalloc_mallocz(owa, sizeof(struct context_param)); - (*param_list)->first_entry_t = LONG_MAX; - (*param_list)->last_entry_t = 0; - (*param_list)->flags = CONTEXT_FLAGS_CONTEXT; - (*param_list)->rd = NULL; - } - - RRDDIM *rd1; - st->last_accessed_time = now_realtime_sec(); - rrdset_rdlock(st); - - (*param_list)->first_entry_t = MIN((*param_list)->first_entry_t, rrdset_first_entry_t_nolock(st)); - (*param_list)->last_entry_t = MAX((*param_list)->last_entry_t, rrdset_last_entry_t_nolock(st)); - - rrddim_foreach_read(rd1, st) { - RRDDIM *rd = onewayalloc_memdupz(owa, rd1, sizeof(RRDDIM)); - rd->id = onewayalloc_strdupz(owa, rd1->id); - rd->name = onewayalloc_strdupz(owa, rd1->name); - for(int tier = 0; tier < storage_tiers ;tier++) { - if(rd1->tiers[tier]) - rd->tiers[tier] = onewayalloc_memdupz(owa, rd1->tiers[tier], sizeof(*rd->tiers[tier])); - else - rd->tiers[tier] = NULL; - } - rd->next = (*param_list)->rd; - (*param_list)->rd = rd; - } - - rrdset_unlock(st); -} - void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) { rrdset2json(st, wb, NULL, NULL, 0); } @@ -183,12 +60,12 @@ int rrdset2value_api_v1( , BUFFER *wb , NETDATA_DOUBLE *n , const char *dimensions - , long points - , long long after - , long long before - , int group_method + , size_t points + , time_t after + , time_t before + , RRDR_GROUPING group_method , const char *group_options - , long group_time + , time_t resampling_time , uint32_t options , time_t *db_after , time_t *db_before @@ -197,16 +74,27 @@ int rrdset2value_api_v1( , size_t *result_points_generated , int *value_is_null , NETDATA_DOUBLE *anomaly_rate - , int timeout - , int tier + , time_t timeout + , size_t tier + , QUERY_SOURCE query_source ) { int ret = HTTP_RESP_INTERNAL_SERVER_ERROR; ONEWAYALLOC *owa = onewayalloc_create(0); - - RRDR *r = rrd2rrdr(owa, st, points, after, before, - group_method, group_time, options, dimensions, NULL, - group_options, timeout, tier); + RRDR *r = rrd2rrdr_legacy( + owa, + st, + points, + after, + before, + group_method, + resampling_time, + options, + dimensions, + group_options, + timeout, + tier, + query_source); if(!r) { if(value_is_null) *value_is_null = 1; @@ -218,7 +106,7 @@ int rrdset2value_api_v1( *db_points_read += r->internal.db_points_read; if(db_points_per_tier) { - for(int t = 0; t < storage_tiers ;t++) + for(size_t t = 0; t < storage_tiers ;t++) db_points_per_tier[t] += r->internal.tier_points_read[t]; } @@ -244,50 +132,19 @@ int rrdset2value_api_v1( if(db_after) *db_after = r->after; if(db_before) *db_before = r->before; - long i = (!(options & RRDR_OPTION_REVERSED))?rrdr_rows(r) - 1:0; - *n = rrdr2value(r, i, options, value_is_null, anomaly_rate, NULL); + long i = (!(options & RRDR_OPTION_REVERSED))?(long)rrdr_rows(r) - 1:0; + *n = rrdr2value(r, i, options, value_is_null, anomaly_rate); ret = HTTP_RESP_OK; cleanup: - if(r) rrdr_free(owa, r); + rrdr_free(owa, r); onewayalloc_destroy(owa); return ret; } -int rrdset2anything_api_v1( - ONEWAYALLOC *owa - , RRDSET *st - , QUERY_PARAMS *query_params - , BUFFER *dimensions - , uint32_t format - , long points - , long long after - , long long before - , int group_method - , const char *group_options - , long group_time - , uint32_t options - , time_t *latest_timestamp - , int tier -) -{ - BUFFER *wb = query_params->wb; - if (query_params->context_param_list && !(query_params->context_param_list->flags & CONTEXT_FLAGS_ARCHIVE)) - st->last_accessed_time = now_realtime_sec(); - - RRDR *r = rrd2rrdr( - owa, - st, - points, - after, - before, - group_method, - group_time, - options, - dimensions ? buffer_tostring(dimensions) : NULL, - query_params->context_param_list, - group_options, - query_params->timeout, tier); +int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *latest_timestamp) { + + RRDR *r = rrd2rrdr(owa, qt); if(!r) { buffer_strcat(wb, "Cannot generate output with these parameters on this chart."); return HTTP_RESP_INTERNAL_SERVER_ERROR; @@ -298,11 +155,6 @@ int rrdset2anything_api_v1( return HTTP_RESP_BACKEND_FETCH_FAILED; } - if (st->state && st->state->is_ar_chart) - ml_process_rrdr(r, query_params->max_anomaly_rates); - - RRDDIM *temp_rd = query_params->context_param_list ? query_params->context_param_list->rd : NULL; - if(r->result_options & RRDR_RESULT_OPTION_RELATIVE) buffer_no_cacheable(wb); else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE) @@ -311,85 +163,89 @@ int rrdset2anything_api_v1( if(latest_timestamp && rrdr_rows(r) > 0) *latest_timestamp = r->before; + DATASOURCE_FORMAT format = qt->request.format; + RRDR_OPTIONS options = qt->request.options; + RRDR_GROUPING group_method = qt->request.group_method; + switch(format) { case DATASOURCE_SSV: if(options & RRDR_OPTION_JSON_WRAP) { wb->contenttype = CT_APPLICATION_JSON; - rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method, query_params); - rrdr2ssv(r, wb, options, "", " ", "", temp_rd); + rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method); + rrdr2ssv(r, wb, options, "", " ", ""); rrdr_json_wrapper_end(r, wb, format, options, 1); } else { wb->contenttype = CT_TEXT_PLAIN; - rrdr2ssv(r, wb, options, "", " ", "", temp_rd); + rrdr2ssv(r, wb, options, "", " ", ""); } break; case DATASOURCE_SSV_COMMA: if(options & RRDR_OPTION_JSON_WRAP) { wb->contenttype = CT_APPLICATION_JSON; - rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method, query_params); - rrdr2ssv(r, wb, options, "", ",", "", temp_rd); + rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method); + rrdr2ssv(r, wb, options, "", ",", ""); rrdr_json_wrapper_end(r, wb, format, options, 1); } else { wb->contenttype = CT_TEXT_PLAIN; - rrdr2ssv(r, wb, options, "", ",", "", temp_rd); + rrdr2ssv(r, wb, options, "", ",", ""); } break; case DATASOURCE_JS_ARRAY: if(options & RRDR_OPTION_JSON_WRAP) { wb->contenttype = CT_APPLICATION_JSON; - rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method, query_params); - rrdr2ssv(r, wb, options, "[", ",", "]", temp_rd); + rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method); + rrdr2ssv(r, wb, options, "[", ",", "]"); rrdr_json_wrapper_end(r, wb, format, options, 0); } else { wb->contenttype = CT_APPLICATION_JSON; - rrdr2ssv(r, wb, options, "[", ",", "]", temp_rd); + rrdr2ssv(r, wb, options, "[", ",", "]"); } break; case DATASOURCE_CSV: if(options & RRDR_OPTION_JSON_WRAP) { wb->contenttype = CT_APPLICATION_JSON; - rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method, query_params); - rrdr2csv(r, wb, format, options, "", ",", "\\n", "", temp_rd); + rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method); + rrdr2csv(r, wb, format, options, "", ",", "\\n", ""); rrdr_json_wrapper_end(r, wb, format, options, 1); } else { wb->contenttype = CT_TEXT_PLAIN; - rrdr2csv(r, wb, format, options, "", ",", "\r\n", "", temp_rd); + rrdr2csv(r, wb, format, options, "", ",", "\r\n", ""); } break; case DATASOURCE_CSV_MARKDOWN: if(options & RRDR_OPTION_JSON_WRAP) { wb->contenttype = CT_APPLICATION_JSON; - rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method, query_params); - rrdr2csv(r, wb, format, options, "", "|", "\\n", "", temp_rd); + rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method); + rrdr2csv(r, wb, format, options, "", "|", "\\n", ""); rrdr_json_wrapper_end(r, wb, format, options, 1); } else { wb->contenttype = CT_TEXT_PLAIN; - rrdr2csv(r, wb, format, options, "", "|", "\r\n", "", temp_rd); + rrdr2csv(r, wb, format, options, "", "|", "\r\n", ""); } break; case DATASOURCE_CSV_JSON_ARRAY: wb->contenttype = CT_APPLICATION_JSON; if(options & RRDR_OPTION_JSON_WRAP) { - rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method, query_params); + rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method); buffer_strcat(wb, "[\n"); - rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n", temp_rd); + rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n"); buffer_strcat(wb, "\n]"); rrdr_json_wrapper_end(r, wb, format, options, 0); } else { wb->contenttype = CT_APPLICATION_JSON; buffer_strcat(wb, "[\n"); - rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n", temp_rd); + rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n"); buffer_strcat(wb, "\n]"); } break; @@ -397,29 +253,29 @@ int rrdset2anything_api_v1( case DATASOURCE_TSV: if(options & RRDR_OPTION_JSON_WRAP) { wb->contenttype = CT_APPLICATION_JSON; - rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method, query_params); - rrdr2csv(r, wb, format, options, "", "\t", "\\n", "", temp_rd); + rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method); + rrdr2csv(r, wb, format, options, "", "\t", "\\n", ""); rrdr_json_wrapper_end(r, wb, format, options, 1); } else { wb->contenttype = CT_TEXT_PLAIN; - rrdr2csv(r, wb, format, options, "", "\t", "\r\n", "", temp_rd); + rrdr2csv(r, wb, format, options, "", "\t", "\r\n", ""); } break; case DATASOURCE_HTML: if(options & RRDR_OPTION_JSON_WRAP) { wb->contenttype = CT_APPLICATION_JSON; - rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method, query_params); + rrdr_json_wrapper_begin(r, wb, format, options, 1, group_method); buffer_strcat(wb, "\\n
\\n\\n"); - rrdr2csv(r, wb, format, options, "\\n", "", temp_rd); + rrdr2csv(r, wb, format, options, "\\n", ""); buffer_strcat(wb, "
", "", "
", "", "
\\n
\\n\\n"); rrdr_json_wrapper_end(r, wb, format, options, 1); } else { wb->contenttype = CT_TEXT_HTML; buffer_strcat(wb, "\n
\n\n"); - rrdr2csv(r, wb, format, options, "\n", "", temp_rd); + rrdr2csv(r, wb, format, options, "\n", ""); buffer_strcat(wb, "
", "", "
", "", "
\n
\n\n"); } break; @@ -428,9 +284,9 @@ int rrdset2anything_api_v1( wb->contenttype = CT_APPLICATION_X_JAVASCRIPT; if(options & RRDR_OPTION_JSON_WRAP) - rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method, query_params); + rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method); - rrdr2json(r, wb, options, 1, query_params->context_param_list); + rrdr2json(r, wb, options, 1); if(options & RRDR_OPTION_JSON_WRAP) rrdr_json_wrapper_end(r, wb, format, options, 0); @@ -440,9 +296,9 @@ int rrdset2anything_api_v1( wb->contenttype = CT_APPLICATION_JSON; if(options & RRDR_OPTION_JSON_WRAP) - rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method, query_params); + rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method); - rrdr2json(r, wb, options, 1, query_params->context_param_list); + rrdr2json(r, wb, options, 1); if(options & RRDR_OPTION_JSON_WRAP) rrdr_json_wrapper_end(r, wb, format, options, 0); @@ -451,9 +307,9 @@ int rrdset2anything_api_v1( case DATASOURCE_JSONP: wb->contenttype = CT_APPLICATION_X_JAVASCRIPT; if(options & RRDR_OPTION_JSON_WRAP) - rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method, query_params); + rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method); - rrdr2json(r, wb, options, 0, query_params->context_param_list); + rrdr2json(r, wb, options, 0); if(options & RRDR_OPTION_JSON_WRAP) rrdr_json_wrapper_end(r, wb, format, options, 0); @@ -464,14 +320,14 @@ int rrdset2anything_api_v1( wb->contenttype = CT_APPLICATION_JSON; if(options & RRDR_OPTION_JSON_WRAP) - rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method, query_params); + rrdr_json_wrapper_begin(r, wb, format, options, 0, group_method); - rrdr2json(r, wb, options, 0, query_params->context_param_list); + rrdr2json(r, wb, options, 0); if(options & RRDR_OPTION_JSON_WRAP) { if(options & RRDR_OPTION_RETURN_JWAR) { rrdr_json_wrapper_anomaly_rates(r, wb, format, options, 0); - rrdr2json(r, wb, options | RRDR_OPTION_INTERNAL_AR, 0, query_params->context_param_list); + rrdr2json(r, wb, options | RRDR_OPTION_INTERNAL_AR, 0); } rrdr_json_wrapper_end(r, wb, format, options, 0); } diff --git a/web/api/formatters/rrd2json.h b/web/api/formatters/rrd2json.h index 6be53ff8a..048281d7e 100644 --- a/web/api/formatters/rrd2json.h +++ b/web/api/formatters/rrd2json.h @@ -5,16 +5,6 @@ #include "web/api/web_api_v1.h" -typedef struct query_params { - struct context_param *context_param_list; - BUFFER *wb; - char *chart_label_key; - int max_anomaly_rates; - int timeout; - int show_dimensions; -} QUERY_PARAMS; - - #include "web/api/exporters/allmetrics.h" #include "web/api/queries/rrdr.h" @@ -34,19 +24,20 @@ typedef struct query_params { #define API_RELATIVE_TIME_MAX (3 * 365 * 86400) // type of JSON generations -#define DATASOURCE_INVALID (-1) -#define DATASOURCE_JSON 0 -#define DATASOURCE_DATATABLE_JSON 1 -#define DATASOURCE_DATATABLE_JSONP 2 -#define DATASOURCE_SSV 3 -#define DATASOURCE_CSV 4 -#define DATASOURCE_JSONP 5 -#define DATASOURCE_TSV 6 -#define DATASOURCE_HTML 7 -#define DATASOURCE_JS_ARRAY 8 -#define DATASOURCE_SSV_COMMA 9 -#define DATASOURCE_CSV_JSON_ARRAY 10 -#define DATASOURCE_CSV_MARKDOWN 11 +typedef enum { + DATASOURCE_JSON = 0, + DATASOURCE_DATATABLE_JSON = 1, + DATASOURCE_DATATABLE_JSONP = 2, + DATASOURCE_SSV = 3, + DATASOURCE_CSV = 4, + DATASOURCE_JSONP = 5, + DATASOURCE_TSV = 6, + DATASOURCE_HTML = 7, + DATASOURCE_JS_ARRAY = 8, + DATASOURCE_SSV_COMMA = 9, + DATASOURCE_CSV_JSON_ARRAY = 10, + DATASOURCE_CSV_MARKDOWN = 11, +} DATASOURCE_FORMAT; #define DATASOURCE_FORMAT_JSON "json" #define DATASOURCE_FORMAT_DATATABLE_JSON "datatable" @@ -61,37 +52,22 @@ typedef struct query_params { #define DATASOURCE_FORMAT_CSV_JSON_ARRAY "csvjsonarray" #define DATASOURCE_FORMAT_CSV_MARKDOWN "markdown" -extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb); -extern void rrdr_buffer_print_format(BUFFER *wb, uint32_t format); +void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb); +void rrdr_buffer_print_format(BUFFER *wb, uint32_t format); -extern int rrdset2anything_api_v1( - ONEWAYALLOC *owa - , RRDSET *st - , QUERY_PARAMS *query_params - , BUFFER *dimensions - , uint32_t format - , long points - , long long after - , long long before - , int group_method - , const char *group_options - , long group_time - , uint32_t options - , time_t *latest_timestamp - , int tier -); +int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, struct query_target *qt, time_t *latest_timestamp); -extern int rrdset2value_api_v1( +int rrdset2value_api_v1( RRDSET *st , BUFFER *wb , NETDATA_DOUBLE *n , const char *dimensions - , long points - , long long after - , long long before - , int group_method + , size_t points + , time_t after + , time_t before + , RRDR_GROUPING group_method , const char *group_options - , long group_time + , time_t resampling_time , uint32_t options , time_t *db_after , time_t *db_before @@ -100,12 +76,9 @@ extern int rrdset2value_api_v1( , size_t *result_points_generated , int *value_is_null , NETDATA_DOUBLE *anomaly_rate - , int timeout - , int tier + , time_t timeout + , size_t tier + , QUERY_SOURCE query_source ); -extern void build_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list, RRDSET *st); -extern void rebuild_context_param_list(ONEWAYALLOC *owa, struct context_param *context_param_list, time_t after_requested); -extern void free_context_param_list(ONEWAYALLOC *owa, struct context_param **param_list); - #endif /* NETDATA_RRD2JSON_H */ diff --git a/web/api/formatters/rrdset2json.c b/web/api/formatters/rrdset2json.c index de8d87bae..1e8106335 100644 --- a/web/api/formatters/rrdset2json.c +++ b/web/api/formatters/rrdset2json.c @@ -4,7 +4,7 @@ void chart_labels2json(RRDSET *st, BUFFER *wb, size_t indentation) { - if(unlikely(!st->state || !st->state->chart_labels)) + if(unlikely(!st->rrdlabels)) return; char tabs[11]; @@ -18,17 +18,15 @@ void chart_labels2json(RRDSET *st, BUFFER *wb, size_t indentation) indentation--; } - rrdlabels_to_buffer(st->state->chart_labels, wb, tabs, ":", "\"", ",\n", NULL, NULL, NULL, NULL); + rrdlabels_to_buffer(st->rrdlabels, wb, tabs, ":", "\"", ",\n", NULL, NULL, NULL, NULL); buffer_strcat(wb, "\n"); } // generate JSON for the /api/v1/chart API call void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used, int skip_volatile) { - rrdset_rdlock(st); - - time_t first_entry_t = rrdset_first_entry_t_nolock(st); - time_t last_entry_t = rrdset_last_entry_t_nolock(st); + time_t first_entry_t = rrdset_first_entry_t(st); + time_t last_entry_t = rrdset_last_entry_t(st); buffer_sprintf( wb, @@ -45,18 +43,18 @@ void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memor "\t\t\t\"units\": \"%s\",\n" "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n" "\t\t\t\"chart_type\": \"%s\",\n", - st->id, - st->name, - st->type, - st->family, - st->context, - st->title, - st->name, + rrdset_id(st), + rrdset_name(st), + rrdset_parts_type(st), + rrdset_family(st), + rrdset_context(st), + rrdset_title(st), + rrdset_name(st), st->priority, - st->plugin_name ? st->plugin_name : "", - st->module_name ? st->module_name : "", - st->units, - st->name, + rrdset_plugin_name(st), + rrdset_module_name(st), + rrdset_units(st), + rrdset_name(st), rrdset_type_name(st->chart_type)); if (likely(!skip_volatile)) @@ -90,7 +88,7 @@ void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memor size_t dimensions = 0; RRDDIM *rd; rrddim_foreach_read(rd, st) { - if(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) continue; + if(rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) continue; memory += sizeof(RRDDIM) + rd->memsize; @@ -98,13 +96,14 @@ void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memor buffer_strcat(wb, ",\n\t\t\t\t\""); else buffer_strcat(wb, "\t\t\t\t\""); - buffer_strcat_jsonescape(wb, rd->id); + buffer_strcat_jsonescape(wb, rrddim_id(rd)); buffer_strcat(wb, "\": { \"name\": \""); - buffer_strcat_jsonescape(wb, rd->name); + buffer_strcat_jsonescape(wb, rrddim_name(rd)); buffer_strcat(wb, "\" }"); dimensions++; } + rrddim_foreach_done(rd); if(dimensions_count) *dimensions_count += dimensions; if(memory_used) *memory_used += memory; @@ -121,7 +120,8 @@ void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memor buffer_strcat(wb, ",\n\t\t\t\"alarms\": {\n"); size_t alarms = 0; RRDCALC *rc; - for (rc = st->alarms; rc; rc = rc->rrdset_next) { + netdata_rwlock_rdlock(&st->alerts.rwlock); + DOUBLE_LINKED_LIST_FOREACH_FORWARD(st->alerts.base, rc, prev, next) { buffer_sprintf( wb, "%s" @@ -131,23 +131,25 @@ void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memor "\t\t\t\t\t\"units\": \"%s\",\n" "\t\t\t\t\t\"update_every\": %d\n" "\t\t\t\t}", - (alarms) ? ",\n" : "", rc->name, rc->id, rrdcalc_status2string(rc->status), rc->units, + (alarms) ? ",\n" : "", rrdcalc_name(rc), rc->id, rrdcalc_status2string(rc->status), rrdcalc_units(rc), rc->update_every); alarms++; } + netdata_rwlock_unlock(&st->alerts.rwlock); buffer_sprintf(wb, "\n\t\t\t}" ); } buffer_strcat(wb, ",\n\t\t\t\"chart_labels\": {\n"); chart_labels2json(st, wb, 2); - buffer_strcat(wb, "\t\t\t}\n"); + buffer_strcat(wb, "\t\t\t}"); + buffer_strcat(wb, ",\n\t\t\t\"functions\": {\n"); + chart_functions2json(st, wb, 4, "\"", "\""); + buffer_strcat(wb, "\t\t\t}"); buffer_sprintf(wb, "\n\t\t}" ); - - rrdset_unlock(st); } diff --git a/web/api/formatters/rrdset2json.h b/web/api/formatters/rrdset2json.h index 697c84634..b2908e225 100644 --- a/web/api/formatters/rrdset2json.h +++ b/web/api/formatters/rrdset2json.h @@ -5,6 +5,6 @@ #include "rrd2json.h" -extern void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used, int skip_volatile); +void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used, int skip_volatile); #endif //NETDATA_API_FORMATTER_RRDSET2JSON_H diff --git a/web/api/formatters/ssv/ssv.c b/web/api/formatters/ssv/ssv.c index 850182da1..d561980d9 100644 --- a/web/api/formatters/ssv/ssv.c +++ b/web/api/formatters/ssv/ssv.c @@ -2,7 +2,7 @@ #include "ssv.h" -void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix, RRDDIM *temp_rd) { +void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix) { //info("RRD2SSV(): %s: BEGIN", r->st->id); long i; @@ -17,7 +17,7 @@ void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, con // for each line in the array for(i = start; i != end ;i += step) { int all_values_are_null = 0; - NETDATA_DOUBLE v = rrdr2value(r, i, options, &all_values_are_null, NULL, temp_rd); + NETDATA_DOUBLE v = rrdr2value(r, i, options, &all_values_are_null, NULL); if(likely(i != start)) { if(r->min > v) r->min = v; diff --git a/web/api/formatters/ssv/ssv.h b/web/api/formatters/ssv/ssv.h index 66716b9c9..f7d4a9548 100644 --- a/web/api/formatters/ssv/ssv.h +++ b/web/api/formatters/ssv/ssv.h @@ -5,6 +5,6 @@ #include "../rrd2json.h" -extern void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix, RRDDIM *temp_rd); +void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix); #endif //NETDATA_API_FORMATTER_SSV_H diff --git a/web/api/formatters/value/value.c b/web/api/formatters/value/value.c index 30e00c068..46a71303e 100644 --- a/web/api/formatters/value/value.c +++ b/web/api/formatters/value/value.c @@ -3,9 +3,10 @@ #include "value.h" -inline NETDATA_DOUBLE rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null, NETDATA_DOUBLE *anomaly_rate, RRDDIM *temp_rd) { +inline NETDATA_DOUBLE rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null, NETDATA_DOUBLE *anomaly_rate) { + QUERY_TARGET *qt = r->internal.qt; long c; - RRDDIM *d; + const long used = qt->query.used; NETDATA_DOUBLE *cn = &r->v[ i * r->d ]; RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ]; @@ -20,7 +21,7 @@ inline NETDATA_DOUBLE rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all int set_min_max = 0; if(unlikely(options & RRDR_OPTION_PERCENTAGE)) { total = 0; - for (c = 0, d = temp_rd ? temp_rd : r->st->dimensions; d && c < r->d; c++, d = d->next) { + for (c = 0; c < used; c++) { NETDATA_DOUBLE n = cn[c]; if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0)) @@ -34,7 +35,7 @@ inline NETDATA_DOUBLE rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all } // for each dimension - for (c = 0, d = temp_rd ? temp_rd : r->st->dimensions; d && c < r->d; c++, d = d->next) { + for (c = 0; c < used; c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue; @@ -80,7 +81,7 @@ inline NETDATA_DOUBLE rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all if(anomaly_rate) { if(!r->d) *anomaly_rate = 0; - else *anomaly_rate = total_anomaly_rate / r->d; + else *anomaly_rate = total_anomaly_rate / (NETDATA_DOUBLE)r->d; } if(unlikely(all_null)) { @@ -100,3 +101,62 @@ inline NETDATA_DOUBLE rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all return v; } + +QUERY_VALUE rrdmetric2value(RRDHOST *host, + struct rrdcontext_acquired *rca, struct rrdinstance_acquired *ria, struct rrdmetric_acquired *rma, + time_t after, time_t before, + RRDR_OPTIONS options, RRDR_GROUPING group_method, const char *group_options, + size_t tier, time_t timeout, QUERY_SOURCE query_source +) { + QUERY_TARGET_REQUEST qtr = { + .host = host, + .rca = rca, + .ria = ria, + .rma = rma, + .after = after, + .before = before, + .points = 1, + .options = options, + .group_method = group_method, + .group_options = group_options, + .tier = tier, + .timeout = timeout, + .query_source = query_source, + }; + + ONEWAYALLOC *owa = onewayalloc_create(16 * 1024); + RRDR *r = rrd2rrdr(owa, query_target_create(&qtr)); + + QUERY_VALUE qv; + + if(!r || rrdr_rows(r) == 0) { + qv = (QUERY_VALUE) { + .value = NAN, + .anomaly_rate = NAN, + }; + } + else { + qv = (QUERY_VALUE) { + .after = r->after, + .before = r->before, + .points_read = r->internal.db_points_read, + .result_points = r->internal.result_points_generated, + }; + + for(size_t t = 0; t < storage_tiers ;t++) + qv.storage_points_per_tier[t] = r->internal.tier_points_read[t]; + + long i = (!(options & RRDR_OPTION_REVERSED))?(long)rrdr_rows(r) - 1:0; + int all_values_are_null = 0; + qv.value = rrdr2value(r, i, options, &all_values_are_null, &qv.anomaly_rate); + if(all_values_are_null) { + qv.value = NAN; + qv.anomaly_rate = NAN; + } + } + + rrdr_free(owa, r); + onewayalloc_destroy(owa); + + return qv; +} diff --git a/web/api/formatters/value/value.h b/web/api/formatters/value/value.h index fc1c7bf08..76b1869f3 100644 --- a/web/api/formatters/value/value.h +++ b/web/api/formatters/value/value.h @@ -5,6 +5,27 @@ #include "../rrd2json.h" -extern NETDATA_DOUBLE rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null, NETDATA_DOUBLE *anomaly_rate, RRDDIM *temp_rd); +typedef struct storage_value { + NETDATA_DOUBLE value; + NETDATA_DOUBLE anomaly_rate; + time_t after; + time_t before; + size_t points_read; + size_t storage_points_per_tier[RRD_STORAGE_TIERS]; + size_t result_points; +} QUERY_VALUE; + +struct rrdmetric_acquired; +struct rrdinstance_acquired; +struct rrdcontext_acquired; + +QUERY_VALUE rrdmetric2value(RRDHOST *host, + struct rrdcontext_acquired *rca, struct rrdinstance_acquired *ria, struct rrdmetric_acquired *rma, + time_t after, time_t before, + RRDR_OPTIONS options, RRDR_GROUPING group_method, const char *group_options, + size_t tier, time_t timeout, QUERY_SOURCE query_source +); + +NETDATA_DOUBLE rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null, NETDATA_DOUBLE *anomaly_rate); #endif //NETDATA_API_FORMATTER_VALUE_H diff --git a/web/api/health/health_cmdapi.h b/web/api/health/health_cmdapi.h index d8ec6aaa0..d5309c73f 100644 --- a/web/api/health/health_cmdapi.h +++ b/web/api/health/health_cmdapi.h @@ -24,7 +24,7 @@ #define HEALTH_CMDAPI_MSG_STYPEWARNING "WARNING: Added alarm selector to silence/disable alarms without a SILENCE or DISABLE command.\n" #define HEALTH_CMDAPI_MSG_NOSELECTORWARNING "WARNING: SILENCE or DISABLE command is ineffective without defining any alarm selectors.\n" -extern int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url); #include "web/api/web_api_v1.h" diff --git a/web/api/netdata-swagger.json b/web/api/netdata-swagger.json index 029783b55..cb2b4809c 100644 --- a/web/api/netdata-swagger.json +++ b/web/api/netdata-swagger.json @@ -573,7 +573,6 @@ "unaligned", "match-ids", "match-names", - "showcustomvars", "allow_past", "anomaly-bit" ] @@ -1629,6 +1628,68 @@ } } }, + "/function": { + "get": { + "summary": "Execute a collector function.", + "parameters": [ + { + "name": "function", + "in": "query", + "description": "The name of the function, as returned by the collector.", + "required": true, + "allowEmptyValue": false, + "schema": { + "type": "string" + } + }, + { + "name": "timeout", + "in": "query", + "description": "The timeout in seconds to wait for the function to complete.", + "required": false, + "schema": { + "type": "number", + "format": "integer", + "default": 10 + } + } + ], + "responses": { + "200": { + "description": "The collector function has been executed successfully. Each collector may return a different type of content." + }, + "400": { + "description": "The request was rejected by the collector." + }, + "404": { + "description": "The requested function is not found." + }, + "500": { + "description": "Other internal error, getting this error means there is a bug in Netdata." + }, + "503": { + "description": "The collector to execute the function is not currently available." + }, + "504": { + "description": "Timeout while waiting for the collector to execute the function." + }, + "591": { + "description": "The collector sent a response, but it was invalid or corrupted." + } + } + } + }, + "/functions": { + "get": { + "summary": "Get a list of all registered collector functions.", + "description": "Collector functions are programs that can be executed on demand.", + "responses": { + "200": { + "description": "A JSON object containing one object per supported function." + } + } + } + }, "/weights": { "get": { "summary": "Analyze all the metrics using an algorithm and score them accordingly", diff --git a/web/api/netdata-swagger.yaml b/web/api/netdata-swagger.yaml index 2e04e9f20..fced6544f 100644 --- a/web/api/netdata-swagger.yaml +++ b/web/api/netdata-swagger.yaml @@ -471,7 +471,6 @@ paths: - unaligned - match-ids - match-names - - showcustomvars - allow_past - anomaly-bit default: @@ -1351,6 +1350,47 @@ paths: that correlated the metrics did not produce any result. "504": description: Timeout - the query took too long and has been cancelled. + /function: + get: + summary: "Execute a collector function." + parameters: + - name: function + in: query + description: The name of the function, as returned by the collector. + required: true + allowEmptyValue: false + schema: + type: string + - name: timeout + in: query + description: The timeout in seconds to wait for the function to complete. + required: false + schema: + type: number + format: integer + default: 10 + responses: + "200": + description: The collector function has been executed successfully. Each collector may return a different type of content. + "400": + description: The request was rejected by the collector. + "404": + description: The requested function is not found. + "500": + description: Other internal error, getting this error means there is a bug in Netdata. + "503": + description: The collector to execute the function is not currently available. + "504": + description: Timeout while waiting for the collector to execute the function. + "591": + description: The collector sent a response, but it was invalid or corrupted. + /functions: + get: + summary: Get a list of all registered collector functions. + description: Collector functions are programs that can be executed on demand. + responses: + "200": + description: A JSON object containing one object per supported function. /weights: get: summary: "Analyze all the metrics using an algorithm and score them accordingly" diff --git a/web/api/queries/average/average.h b/web/api/queries/average/average.h index 55c51722c..b31966886 100644 --- a/web/api/queries/average/average.h +++ b/web/api/queries/average/average.h @@ -6,10 +6,10 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_average(RRDR *r, const char *options __maybe_unused); -extern void grouping_reset_average(RRDR *r); -extern void grouping_free_average(RRDR *r); -extern void grouping_add_average(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_average(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_average(RRDR *r, const char *options __maybe_unused); +void grouping_reset_average(RRDR *r); +void grouping_free_average(RRDR *r); +void grouping_add_average(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_average(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERY_AVERAGE_H diff --git a/web/api/queries/countif/countif.h b/web/api/queries/countif/countif.h index 0c7d2d7d1..dfe805658 100644 --- a/web/api/queries/countif/countif.h +++ b/web/api/queries/countif/countif.h @@ -6,10 +6,10 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_countif(RRDR *r, const char *options __maybe_unused); -extern void grouping_reset_countif(RRDR *r); -extern void grouping_free_countif(RRDR *r); -extern void grouping_add_countif(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_countif(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_countif(RRDR *r, const char *options __maybe_unused); +void grouping_reset_countif(RRDR *r); +void grouping_free_countif(RRDR *r); +void grouping_add_countif(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_countif(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERY_COUNTIF_H diff --git a/web/api/queries/des/des.h b/web/api/queries/des/des.h index 8906d14eb..05fa01b34 100644 --- a/web/api/queries/des/des.h +++ b/web/api/queries/des/des.h @@ -6,12 +6,12 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_init_des(void); +void grouping_init_des(void); -extern void grouping_create_des(RRDR *r, const char *options __maybe_unused); -extern void grouping_reset_des(RRDR *r); -extern void grouping_free_des(RRDR *r); -extern void grouping_add_des(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_des(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_des(RRDR *r, const char *options __maybe_unused); +void grouping_reset_des(RRDR *r); +void grouping_free_des(RRDR *r); +void grouping_add_des(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_des(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERIES_DES_H diff --git a/web/api/queries/incremental_sum/incremental_sum.h b/web/api/queries/incremental_sum/incremental_sum.h index 6d908cef6..c24507fcf 100644 --- a/web/api/queries/incremental_sum/incremental_sum.h +++ b/web/api/queries/incremental_sum/incremental_sum.h @@ -6,10 +6,10 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_incremental_sum(RRDR *r, const char *options __maybe_unused); -extern void grouping_reset_incremental_sum(RRDR *r); -extern void grouping_free_incremental_sum(RRDR *r); -extern void grouping_add_incremental_sum(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_incremental_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_incremental_sum(RRDR *r, const char *options __maybe_unused); +void grouping_reset_incremental_sum(RRDR *r); +void grouping_free_incremental_sum(RRDR *r); +void grouping_add_incremental_sum(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_incremental_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERY_INCREMENTAL_SUM_H diff --git a/web/api/queries/max/max.h b/web/api/queries/max/max.h index 28913686b..e2427d26d 100644 --- a/web/api/queries/max/max.h +++ b/web/api/queries/max/max.h @@ -6,10 +6,10 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_max(RRDR *r, const char *options __maybe_unused); -extern void grouping_reset_max(RRDR *r); -extern void grouping_free_max(RRDR *r); -extern void grouping_add_max(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_max(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_max(RRDR *r, const char *options __maybe_unused); +void grouping_reset_max(RRDR *r); +void grouping_free_max(RRDR *r); +void grouping_add_max(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_max(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERY_MAX_H diff --git a/web/api/queries/median/median.h b/web/api/queries/median/median.h index dd1b3de61..9fc159db4 100644 --- a/web/api/queries/median/median.h +++ b/web/api/queries/median/median.h @@ -6,18 +6,18 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_median(RRDR *r, const char *options); -extern void grouping_create_trimmed_median1(RRDR *r, const char *options); -extern void grouping_create_trimmed_median2(RRDR *r, const char *options); -extern void grouping_create_trimmed_median3(RRDR *r, const char *options); -extern void grouping_create_trimmed_median5(RRDR *r, const char *options); -extern void grouping_create_trimmed_median10(RRDR *r, const char *options); -extern void grouping_create_trimmed_median15(RRDR *r, const char *options); -extern void grouping_create_trimmed_median20(RRDR *r, const char *options); -extern void grouping_create_trimmed_median25(RRDR *r, const char *options); -extern void grouping_reset_median(RRDR *r); -extern void grouping_free_median(RRDR *r); -extern void grouping_add_median(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_median(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_median(RRDR *r, const char *options); +void grouping_create_trimmed_median1(RRDR *r, const char *options); +void grouping_create_trimmed_median2(RRDR *r, const char *options); +void grouping_create_trimmed_median3(RRDR *r, const char *options); +void grouping_create_trimmed_median5(RRDR *r, const char *options); +void grouping_create_trimmed_median10(RRDR *r, const char *options); +void grouping_create_trimmed_median15(RRDR *r, const char *options); +void grouping_create_trimmed_median20(RRDR *r, const char *options); +void grouping_create_trimmed_median25(RRDR *r, const char *options); +void grouping_reset_median(RRDR *r); +void grouping_free_median(RRDR *r); +void grouping_add_median(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_median(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERIES_MEDIAN_H diff --git a/web/api/queries/min/min.h b/web/api/queries/min/min.h index b8627f667..dcdfe252f 100644 --- a/web/api/queries/min/min.h +++ b/web/api/queries/min/min.h @@ -6,10 +6,10 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_min(RRDR *r, const char *options __maybe_unused); -extern void grouping_reset_min(RRDR *r); -extern void grouping_free_min(RRDR *r); -extern void grouping_add_min(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_min(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_min(RRDR *r, const char *options __maybe_unused); +void grouping_reset_min(RRDR *r); +void grouping_free_min(RRDR *r); +void grouping_add_min(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_min(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERY_MIN_H diff --git a/web/api/queries/percentile/percentile.h b/web/api/queries/percentile/percentile.h index 709717ebd..65e335c11 100644 --- a/web/api/queries/percentile/percentile.h +++ b/web/api/queries/percentile/percentile.h @@ -6,18 +6,18 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_percentile25(RRDR *r, const char *options); -extern void grouping_create_percentile50(RRDR *r, const char *options); -extern void grouping_create_percentile75(RRDR *r, const char *options); -extern void grouping_create_percentile80(RRDR *r, const char *options); -extern void grouping_create_percentile90(RRDR *r, const char *options); -extern void grouping_create_percentile95(RRDR *r, const char *options); -extern void grouping_create_percentile97(RRDR *r, const char *options); -extern void grouping_create_percentile98(RRDR *r, const char *options); -extern void grouping_create_percentile99(RRDR *r, const char *options ); -extern void grouping_reset_percentile(RRDR *r); -extern void grouping_free_percentile(RRDR *r); -extern void grouping_add_percentile(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_percentile(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_percentile25(RRDR *r, const char *options); +void grouping_create_percentile50(RRDR *r, const char *options); +void grouping_create_percentile75(RRDR *r, const char *options); +void grouping_create_percentile80(RRDR *r, const char *options); +void grouping_create_percentile90(RRDR *r, const char *options); +void grouping_create_percentile95(RRDR *r, const char *options); +void grouping_create_percentile97(RRDR *r, const char *options); +void grouping_create_percentile98(RRDR *r, const char *options); +void grouping_create_percentile99(RRDR *r, const char *options ); +void grouping_reset_percentile(RRDR *r); +void grouping_free_percentile(RRDR *r); +void grouping_add_percentile(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_percentile(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERIES_PERCENTILE_H diff --git a/web/api/queries/query.c b/web/api/queries/query.c index d776f6d11..ccd195135 100644 --- a/web/api/queries/query.c +++ b/web/api/queries/query.c @@ -657,71 +657,6 @@ static void rrdr_set_grouping_function(RRDR *r, RRDR_GROUPING group_method) { } } -// ---------------------------------------------------------------------------- - -static void rrdr_disable_not_selected_dimensions(RRDR *r, RRDR_OPTIONS options, const char *dims, - struct context_param *context_param_list) -{ - RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL; - int should_lock = (!context_param_list || !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE)); - - if (should_lock) - rrdset_check_rdlock(r->st); - - if(unlikely(!dims || !*dims || (dims[0] == '*' && dims[1] == '\0'))) return; - - int match_ids = 0, match_names = 0; - - if(unlikely(options & RRDR_OPTION_MATCH_IDS)) - match_ids = 1; - if(unlikely(options & RRDR_OPTION_MATCH_NAMES)) - match_names = 1; - - if(likely(!match_ids && !match_names)) - match_ids = match_names = 1; - - SIMPLE_PATTERN *pattern = simple_pattern_create(dims, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); - - RRDDIM *d; - long c, dims_selected = 0, dims_not_hidden_not_zero = 0; - for(c = 0, d = temp_rd?temp_rd:r->st->dimensions; d ;c++, d = d->next) { - if( (match_ids && simple_pattern_matches(pattern, d->id)) - || (match_names && simple_pattern_matches(pattern, d->name)) - ) { - r->od[c] |= RRDR_DIMENSION_SELECTED; - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) r->od[c] &= ~RRDR_DIMENSION_HIDDEN; - dims_selected++; - - // since the user needs this dimension - // make it appear as NONZERO, to return it - // even if the dimension has only zeros - // unless option non_zero is set - if(unlikely(!(options & RRDR_OPTION_NONZERO))) - r->od[c] |= RRDR_DIMENSION_NONZERO; - - // count the visible dimensions - if(likely(r->od[c] & RRDR_DIMENSION_NONZERO)) - dims_not_hidden_not_zero++; - } - else { - r->od[c] |= RRDR_DIMENSION_HIDDEN; - if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED)) r->od[c] &= ~RRDR_DIMENSION_SELECTED; - } - } - simple_pattern_free(pattern); - - // check if all dimensions are hidden - if(unlikely(!dims_not_hidden_not_zero && dims_selected)) { - // there are a few selected dimensions, - // but they are all zero - // enable the selected ones - // to avoid returning an empty chart - for(c = 0, d = temp_rd?temp_rd:r->st->dimensions; d ;c++, d = d->next) - if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED)) - r->od[c] |= RRDR_DIMENSION_NONZERO; - } -} - // ---------------------------------------------------------------------------- // helpers to find our way in RRDR @@ -736,13 +671,13 @@ static inline NETDATA_DOUBLE *UNUSED_FUNCTION(rrdr_line_values)(RRDR *r, long rr static inline long rrdr_line_init(RRDR *r, time_t t, long rrdr_line) { rrdr_line++; - internal_error(rrdr_line >= r->n, - "QUERY: requested to step above RRDR size for chart '%s'", - r->st->name); + internal_error(rrdr_line >= (long)r->n, + "QUERY: requested to step above RRDR size for query '%s'", + r->internal.qt->id); internal_error(r->t[rrdr_line] != 0 && r->t[rrdr_line] != t, - "QUERY: overwriting the timestamp of RRDR line %zu from %zu to %zu, of chart '%s'", - (size_t)rrdr_line, (size_t)r->t[rrdr_line], (size_t)t, r->st->name); + "QUERY: overwriting the timestamp of RRDR line %zu from %zu to %zu, of query '%s'", + (size_t)rrdr_line, (size_t)r->t[rrdr_line], (size_t)t, r->internal.qt->id); // save the time r->t[rrdr_line] = t; @@ -758,68 +693,137 @@ static inline void rrdr_done(RRDR *r, long rrdr_line) { // ---------------------------------------------------------------------------- // tier management -static int rrddim_find_best_tier_for_timeframe(RRDDIM *rd, time_t after_wanted, time_t before_wanted, long points_wanted) { +static bool query_metric_is_valid_tier(QUERY_METRIC *qm, size_t tier) { + if(!qm->tiers[tier].db_metric_handle || !qm->tiers[tier].db_first_time_t || !qm->tiers[tier].db_last_time_t || !qm->tiers[tier].db_update_every) + return false; + + return true; +} + +static size_t query_metric_first_working_tier(QUERY_METRIC *qm) { + for(size_t tier = 0; tier < storage_tiers ; tier++) { + + // find the db time-range for this tier for all metrics + STORAGE_METRIC_HANDLE *db_metric_handle = qm->tiers[tier].db_metric_handle; + time_t first_t = qm->tiers[tier].db_first_time_t; + time_t last_t = qm->tiers[tier].db_last_time_t; + time_t update_every = qm->tiers[tier].db_update_every; + + if(!db_metric_handle || !first_t || !last_t || !update_every) + continue; + + return tier; + } + + return 0; +} + +static long query_plan_points_coverage_weight(time_t db_first_t, time_t db_last_t, time_t db_update_every, time_t after_wanted, time_t before_wanted, size_t points_wanted, size_t tier __maybe_unused) { + if(db_first_t == 0 || db_last_t == 0 || db_update_every == 0) + return -LONG_MAX; + + time_t common_first_t = MAX(db_first_t, after_wanted); + time_t common_last_t = MIN(db_last_t, before_wanted); + + long time_coverage = (common_last_t - common_first_t) * 1000000 / (before_wanted - after_wanted); + size_t points_wanted_in_coverage = points_wanted * time_coverage / 1000000; + + long points_available = (common_last_t - common_first_t) / db_update_every; + long points_delta = (long)(points_available - points_wanted_in_coverage); + long points_coverage = (points_delta < 0) ? (long)(points_available * time_coverage / points_wanted_in_coverage) : time_coverage; + + // a way to benefit higher tiers + // points_coverage += (long)tier * 10000; + + if(points_available <= 0) + return -LONG_MAX; + + return points_coverage; +} + +static size_t query_metric_best_tier_for_timeframe(QUERY_METRIC *qm, time_t after_wanted, time_t before_wanted, size_t points_wanted) { if(unlikely(storage_tiers < 2)) return 0; - if(unlikely(after_wanted == before_wanted || points_wanted <= 0 || !rd || !rd->rrdset)) { + if(unlikely(after_wanted == before_wanted || points_wanted <= 0)) + return query_metric_first_working_tier(qm); - if(!rd) - internal_error(true, "QUERY: NULL dimension - invalid params to tier calculation"); - else - internal_error(true, "QUERY: chart '%s' dimension '%s' invalid params to tier calculation", - (rd->rrdset)?rd->rrdset->name:"unknown", rd->name); + long weight[storage_tiers]; - return 0; + for(size_t tier = 0; tier < storage_tiers ; tier++) { + + // find the db time-range for this tier for all metrics + STORAGE_METRIC_HANDLE *db_metric_handle = qm->tiers[tier].db_metric_handle; + time_t first_t = qm->tiers[tier].db_first_time_t; + time_t last_t = qm->tiers[tier].db_last_time_t; + time_t update_every = qm->tiers[tier].db_update_every; + + if(!db_metric_handle || !first_t || !last_t || !update_every) { + weight[tier] = -LONG_MAX; + continue; + } + + weight[tier] = query_plan_points_coverage_weight(first_t, last_t, update_every, after_wanted, before_wanted, points_wanted, tier); } - //BUFFER *wb = buffer_create(1000); - //buffer_sprintf(wb, "Best tier for chart '%s', dim '%s', from %ld to %ld (dur %ld, every %d), points %ld", - // rd->rrdset->name, rd->name, after_wanted, before_wanted, before_wanted - after_wanted, rd->update_every, points_wanted); + size_t best_tier = 0; + for(size_t tier = 1; tier < storage_tiers ; tier++) { + if(weight[tier] >= weight[best_tier]) + best_tier = tier; + } + + return best_tier; +} + +static size_t rrddim_find_best_tier_for_timeframe(QUERY_TARGET *qt, time_t after_wanted, time_t before_wanted, size_t points_wanted) { + if(unlikely(storage_tiers < 2)) + return 0; + + if(unlikely(after_wanted == before_wanted || points_wanted <= 0)) { + internal_error(true, "QUERY: '%s' has invalid params to tier calculation", qt->id); + return 0; + } long weight[storage_tiers]; - for(int tier = 0; tier < storage_tiers ; tier++) { - if(unlikely(!rd->tiers[tier])) { - internal_error(true, "QUERY: tier %d of chart '%s' dimension '%s' not initialized", - tier, rd->rrdset->name, rd->name); - // buffer_free(wb); - return 0; - } + for(size_t tier = 0; tier < storage_tiers ; tier++) { - time_t first_t = rd->tiers[tier]->query_ops.oldest_time(rd->tiers[tier]->db_metric_handle); - time_t last_t = rd->tiers[tier]->query_ops.latest_time(rd->tiers[tier]->db_metric_handle); + time_t common_first_t = 0; + time_t common_last_t = 0; + time_t common_update_every = 0; - time_t common_after = MAX(first_t, after_wanted); - time_t common_before = MIN(last_t, before_wanted); + // find the db time-range for this tier for all metrics + for(size_t i = 0, used = qt->query.used; i < used ; i++) { + QUERY_METRIC *qm = &qt->query.array[i]; - long time_coverage = (common_before - common_after) * 1000 / (before_wanted - after_wanted); - if(time_coverage < 0) time_coverage = 0; + time_t first_t = qm->tiers[tier].db_first_time_t; + time_t last_t = qm->tiers[tier].db_last_time_t; + time_t update_every = qm->tiers[tier].db_update_every; - int update_every = (int)rd->tiers[tier]->tier_grouping * (int)rd->update_every; - if(unlikely(update_every == 0)) { - internal_error(true, "QUERY: update_every of tier %d for chart '%s' dimension '%s' is zero. tg = %d, ue = %d", - tier, rd->rrdset->name, rd->name, rd->tiers[tier]->tier_grouping, rd->update_every); - // buffer_free(wb); - return 0; - } + if(!first_t || !last_t || !update_every) + continue; - long points_available = (before_wanted - after_wanted) / update_every; - long points_delta = points_available - points_wanted; - long points_coverage = (points_delta < 0) ? points_available * 1000 / points_wanted: 1000; + if(!common_first_t) + common_first_t = first_t; + else + common_first_t = MIN(first_t, common_first_t); - if(points_available <= 0) - weight[tier] = -LONG_MAX; - else - weight[tier] = points_coverage; + if(!common_last_t) + common_last_t = last_t; + else + common_last_t = MAX(last_t, common_last_t); - // buffer_sprintf(wb, ": tier %d, first %ld, last %ld (dur %ld, tg %d, every %d), points %ld, tcoverage %ld, pcoverage %ld, weight %ld", - // tier, first_t, last_t, last_t - first_t, rd->tiers[tier]->tier_grouping, update_every, - // points_available, time_coverage, points_coverage, weight[tier]); + if(!common_update_every) + common_update_every = update_every; + else + common_update_every = MIN(update_every, common_update_every); + } + + weight[tier] = query_plan_points_coverage_weight(common_first_t, common_last_t, common_update_every, after_wanted, before_wanted, points_wanted, tier); } - int best_tier = 0; - for(int tier = 1; tier < storage_tiers ; tier++) { + size_t best_tier = 0; + for(size_t tier = 1; tier < storage_tiers ; tier++) { if(weight[tier] >= weight[best_tier]) best_tier = tier; } @@ -827,47 +831,30 @@ static int rrddim_find_best_tier_for_timeframe(RRDDIM *rd, time_t after_wanted, if(weight[best_tier] == -LONG_MAX) best_tier = 0; - //buffer_sprintf(wb, ": final best tier %d", best_tier); - //internal_error(true, "%s", buffer_tostring(wb)); - //buffer_free(wb); - return best_tier; } -static int rrdset_find_natural_update_every_for_timeframe(RRDSET *st, time_t after_wanted, time_t before_wanted, long points_wanted, RRDR_OPTIONS options, int tier) { - int ret = st->update_every; - - if(unlikely(!st->dimensions)) - return ret; - - rrdset_rdlock(st); - int best_tier; - - if(options & RRDR_OPTION_SELECTED_TIER && tier >= 0 && tier < storage_tiers) +static time_t rrdset_find_natural_update_every_for_timeframe(QUERY_TARGET *qt, time_t after_wanted, time_t before_wanted, size_t points_wanted, RRDR_OPTIONS options, size_t tier) { + size_t best_tier; + if((options & RRDR_OPTION_SELECTED_TIER) && tier < storage_tiers) best_tier = tier; else - best_tier = rrddim_find_best_tier_for_timeframe(st->dimensions, after_wanted, before_wanted, points_wanted); + best_tier = rrddim_find_best_tier_for_timeframe(qt, after_wanted, before_wanted, points_wanted); - if(!st->dimensions->tiers[best_tier]) { - internal_error( - true, - "QUERY: tier %d on chart '%s', is not initialized", best_tier, st->name); - } - else { - ret = (int)st->dimensions->tiers[best_tier]->tier_grouping * (int)st->update_every; - if(unlikely(!ret)) { - internal_error( - true, - "QUERY: update_every calculated to be zero on chart '%s', tier_grouping %d, update_every %d", - st->name, st->dimensions->tiers[best_tier]->tier_grouping, st->update_every); - - ret = st->update_every; - } - } + // find the db minimum update every for this tier for all metrics + time_t common_update_every = default_rrd_update_every; + for(size_t i = 0, used = qt->query.used; i < used ; i++) { + QUERY_METRIC *qm = &qt->query.array[i]; - rrdset_unlock(st); + time_t update_every = qm->tiers[best_tier].db_update_every; - return ret; + if(!i) + common_update_every = update_every; + else + common_update_every = MIN(update_every, common_update_every); + } + + return common_update_every; } // ---------------------------------------------------------------------------- @@ -915,7 +902,7 @@ typedef struct query_plan { typedef struct query_engine_ops { // configuration RRDR *r; - RRDDIM *rd; + QUERY_METRIC *qm; time_t view_update_every; time_t query_granularity; TIER_QUERY_FETCH tier_query_fetch; @@ -927,11 +914,11 @@ typedef struct query_engine_ops { // storage queries size_t tier; - struct rrddim_tier *tier_ptr; - struct rrddim_query_handle handle; - STORAGE_POINT (*next_metric)(struct rrddim_query_handle *handle); - int (*is_finished)(struct rrddim_query_handle *handle); - void (*finalize)(struct rrddim_query_handle *handle); + struct query_metric_tier *tier_ptr; + struct storage_engine_query_handle handle; + STORAGE_POINT (*next_metric)(struct storage_engine_query_handle *handle); + int (*is_finished)(struct storage_engine_query_handle *handle); + void (*finalize)(struct storage_engine_query_handle *handle); // aggregating points over time void (*grouping_add)(struct rrdresult *r, NETDATA_DOUBLE value); @@ -963,11 +950,11 @@ static void query_planer_activate_plan(QUERY_ENGINE_OPS *ops, size_t plan_id, ti after = overwrite_after; ops->tier = ops->plan.data[plan_id].tier; - ops->tier_ptr = ops->rd->tiers[ops->tier]; - ops->tier_ptr->query_ops.init(ops->tier_ptr->db_metric_handle, &ops->handle, after, before, ops->r->internal.tier_query_fetch); - ops->next_metric = ops->tier_ptr->query_ops.next_metric; - ops->is_finished = ops->tier_ptr->query_ops.is_finished; - ops->finalize = ops->tier_ptr->query_ops.finalize; + ops->tier_ptr = &ops->qm->tiers[ops->tier]; + ops->tier_ptr->eng->api.query_ops.init(ops->tier_ptr->db_metric_handle, &ops->handle, after, before); + ops->next_metric = ops->tier_ptr->eng->api.query_ops.next_metric; + ops->is_finished = ops->tier_ptr->eng->api.query_ops.is_finished; + ops->finalize = ops->tier_ptr->eng->api.query_ops.finalize; ops->current_plan = plan_id; ops->current_plan_expire_time = ops->plan.data[plan_id].before; } @@ -976,26 +963,38 @@ static void query_planer_next_plan(QUERY_ENGINE_OPS *ops, time_t now, time_t las internal_error(now < ops->current_plan_expire_time && now < ops->plan.data[ops->current_plan].before, "QUERY: switching query plan too early!"); + size_t old_plan = ops->current_plan; + time_t next_plan_before_time; do { ops->current_plan++; if (ops->current_plan >= ops->plan.entries) { - ops->current_plan = ops->plan.entries - 1; + ops->current_plan = old_plan; + ops->current_plan_expire_time = ops->r->internal.qt->window.before; + // let the query run with current plan + // we will not switch it return; } next_plan_before_time = ops->plan.data[ops->current_plan].before; } while(now >= next_plan_before_time || last_point_end_time >= next_plan_before_time); + if(!query_metric_is_valid_tier(ops->qm, ops->plan.data[ops->current_plan].tier)) { + ops->current_plan = old_plan; + ops->current_plan_expire_time = ops->r->internal.qt->window.before; + return; + } + if(ops->finalize) { ops->finalize(&ops->handle); ops->finalize = NULL; + ops->is_finished = NULL; } - query_planer_activate_plan(ops, ops->current_plan, MIN(now, last_point_end_time)); - // internal_error(true, "QUERY: switched plan to %zu (all is %zu), previous expiration was %ld, this starts at %ld, now is %ld, last_point_end_time %ld", ops->current_plan, ops->plan.entries, ops->plan.data[ops->current_plan-1].before, ops->plan.data[ops->current_plan].after, now, last_point_end_time); + + query_planer_activate_plan(ops, ops->current_plan, MIN(now, last_point_end_time)); } static int compare_query_plan_entries_on_start_time(const void *a, const void *b) { @@ -1004,21 +1003,20 @@ static int compare_query_plan_entries_on_start_time(const void *a, const void *b return (p1->after < p2->after)?-1:1; } -static void query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before_wanted, long points_wanted) { - RRDDIM *rd = ops->rd; - +static bool query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before_wanted, size_t points_wanted) { //BUFFER *wb = buffer_create(1000); //buffer_sprintf(wb, "QUERY PLAN for chart '%s' dimension '%s', from %ld to %ld:", rd->rrdset->name, rd->name, after_wanted, before_wanted); // put our selected tier as the first plan size_t selected_tier; - if(ops->r->internal.query_options & RRDR_OPTION_SELECTED_TIER && ops->r->internal.query_tier >= 0 && ops->r->internal.query_tier < storage_tiers) { - selected_tier = ops->r->internal.query_tier; + if(ops->r->internal.query_options & RRDR_OPTION_SELECTED_TIER + && ops->r->internal.qt->window.tier < storage_tiers + && query_metric_is_valid_tier(ops->qm, ops->r->internal.qt->window.tier)) { + selected_tier = ops->r->internal.qt->window.tier; } else { - - selected_tier = rrddim_find_best_tier_for_timeframe(rd, after_wanted, before_wanted, points_wanted); + selected_tier = query_metric_best_tier_for_timeframe(ops->qm, after_wanted, before_wanted, points_wanted); if(ops->r->internal.query_options & RRDR_OPTION_SELECTED_TIER) ops->r->internal.query_options &= ~RRDR_OPTION_SELECTED_TIER; @@ -1026,8 +1024,8 @@ static void query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before ops->plan.entries = 1; ops->plan.data[0].tier = selected_tier; - ops->plan.data[0].after = rd->tiers[selected_tier]->query_ops.oldest_time(rd->tiers[selected_tier]->db_metric_handle); - ops->plan.data[0].before = rd->tiers[selected_tier]->query_ops.latest_time(rd->tiers[selected_tier]->db_metric_handle); + ops->plan.data[0].after = ops->qm->tiers[selected_tier].db_first_time_t; + ops->plan.data[0].before = ops->qm->tiers[selected_tier].db_last_time_t; if(!(ops->r->internal.query_options & RRDR_OPTION_SELECTED_TIER)) { // the selected tier @@ -1039,9 +1037,12 @@ static void query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before // check if our selected tier can start the query if (selected_tier_first_time_t > after_wanted) { // we need some help from other tiers - for (int tr = (int)selected_tier + 1; tr < storage_tiers; tr++) { + for (size_t tr = (int)selected_tier + 1; tr < storage_tiers; tr++) { + if(!query_metric_is_valid_tier(ops->qm, tr)) + continue; + // find the first time of this tier - time_t first_time_t = rd->tiers[tr]->query_ops.oldest_time(rd->tiers[tr]->db_metric_handle); + time_t first_time_t = ops->qm->tiers[tr].db_first_time_t; //buffer_sprintf(wb, ": EVAL AFTER tier %d, %ld", tier, first_time_t); @@ -1067,8 +1068,11 @@ static void query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before if (selected_tier_last_time_t < before_wanted) { // we need some help from other tiers for (int tr = (int)selected_tier - 1; tr >= 0; tr--) { + if(!query_metric_is_valid_tier(ops->qm, tr)) + continue; + // find the last time of this tier - time_t last_time_t = rd->tiers[tr]->query_ops.latest_time(rd->tiers[tr]->db_metric_handle); + time_t last_time_t = ops->qm->tiers[tr].db_last_time_t; //buffer_sprintf(wb, ": EVAL BEFORE tier %d, %ld", tier, last_time_t); @@ -1096,8 +1100,11 @@ static void query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before qsort(&ops->plan.data, ops->plan.entries, sizeof(QUERY_PLAN_ENTRY), compare_query_plan_entries_on_start_time); // make sure it has the whole timeframe we need - ops->plan.data[0].after = after_wanted; - ops->plan.data[ops->plan.entries - 1].before = before_wanted; + if(ops->plan.data[0].after < after_wanted) + ops->plan.data[0].after = after_wanted; + + if(ops->plan.data[ops->plan.entries - 1].before > before_wanted) + ops->plan.data[ops->plan.entries - 1].before = before_wanted; //buffer_sprintf(wb, ": FINAL STEPS %zu", ops->plan.entries); @@ -1106,7 +1113,12 @@ static void query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before //internal_error(true, "%s", buffer_tostring(wb)); + if(!query_metric_is_valid_tier(ops->qm, ops->plan.data[0].tier)) + return false; + query_planer_activate_plan(ops, 0, 0); + + return true; } @@ -1146,14 +1158,17 @@ static void query_plan(QUERY_ENGINE_OPS *ops, time_t after_wanted, time_t before (ops).group_anomaly_rate += (point).anomaly; \ } while(0) -static inline void rrd2rrdr_do_dimension( - RRDR *r - , long points_wanted - , RRDDIM *rd - , long dim_id_in_rrdr - , time_t after_wanted - , time_t before_wanted -){ +static inline void rrd2rrdr_do_dimension(RRDR *r, size_t dim_id_in_rrdr) { + QUERY_TARGET *qt = r->internal.qt; + QUERY_METRIC *qm = &qt->query.array[dim_id_in_rrdr]; + size_t points_wanted = qt->window.points; + time_t after_wanted = qt->window.after; + time_t before_wanted = qt->window.before; + +// bool debug_this = false; +// if(strcmp("user", string2str(rd->id)) == 0 && strcmp("system.cpu", string2str(rd->rrdset->id)) == 0) +// debug_this = true; + time_t max_date = 0, min_date = 0; @@ -1161,19 +1176,20 @@ static inline void rrd2rrdr_do_dimension( QUERY_ENGINE_OPS ops = { .r = r, - .rd = rd, + .qm = qm, .grouping_add = r->internal.grouping_add, .grouping_flush = r->internal.grouping_flush, .tier_query_fetch = r->internal.tier_query_fetch, .view_update_every = r->update_every, - .query_granularity = r->update_every / r->group, + .query_granularity = (time_t)(r->update_every / r->group), .group_value_flags = RRDR_VALUE_NOTHING }; long rrdr_line = -1; bool use_anomaly_bit_as_value = (r->internal.query_options & RRDR_OPTION_ANOMALY_BIT) ? true : false; - query_plan(&ops, after_wanted, before_wanted, points_wanted); + if(!query_plan(&ops, after_wanted, before_wanted, points_wanted)) + return; NETDATA_DOUBLE min = r->min, max = r->max; @@ -1184,15 +1200,18 @@ static inline void rrd2rrdr_do_dimension( time_t now_start_time = after_wanted - ops.query_granularity; time_t now_end_time = after_wanted + ops.view_update_every - ops.query_granularity; + size_t db_points_read_since_plan_switch = 0; (void)db_points_read_since_plan_switch; + // The main loop, based on the query granularity we need - for( ; (long)points_added < points_wanted ; now_start_time = now_end_time, now_end_time += ops.view_update_every) { + for( ; points_added < points_wanted ; now_start_time = now_end_time, now_end_time += ops.view_update_every) { - if(query_plan_should_switch_plan(ops, now_end_time)) + if(unlikely(query_plan_should_switch_plan(ops, now_end_time))) { query_planer_next_plan(&ops, now_end_time, new_point.end_time); + db_points_read_since_plan_switch = 0; + } // read all the points of the db, prior to the time we need (now_end_time) - size_t count_same_end_time = 0; while(count_same_end_time < 100) { if(likely(count_same_end_time == 0)) { @@ -1208,11 +1227,15 @@ static inline void rrd2rrdr_do_dimension( new_point = QUERY_POINT_EMPTY; new_point.start_time = last1_point.end_time; new_point.end_time = now_end_time; +// +// if(debug_this) info("QUERY: is finished() returned true"); +// break; } // fetch the new point { + db_points_read_since_plan_switch++; STORAGE_POINT sp = ops.next_metric(&ops.handle); ops.db_points_read_per_tier[ops.tier]++; @@ -1223,6 +1246,10 @@ static inline void rrd2rrdr_do_dimension( new_point.anomaly = sp.count ? (NETDATA_DOUBLE)sp.anomaly_count * 100.0 / (NETDATA_DOUBLE)sp.count : 0.0; query_point_set_id(new_point, ops.db_total_points_read); +// if(debug_this) +// info("QUERY: got point %zu, from time %ld to %ld // now from %ld to %ld // query from %ld to %ld", +// new_point.id, new_point.start_time, new_point.end_time, now_start_time, now_end_time, after_wanted, before_wanted); +// // set the right value to the point we got if(likely(!storage_point_is_unset(sp) && !storage_point_is_empty(sp))) { @@ -1258,17 +1285,18 @@ static inline void rrd2rrdr_do_dimension( // check if the db is giving us zero duration points if(unlikely(new_point.start_time == new_point.end_time)) { - internal_error(true, "QUERY: next_metric(%s, %s) returned point %zu start time %ld, end time %ld, that are both equal", - rd->rrdset->name, rd->name, new_point.id, new_point.start_time, new_point.end_time); + internal_error(true, "QUERY: '%s', dimension '%s' next_metric() returned point %zu start time %ld, end time %ld, that are both equal", + qt->id, string2str(qm->dimension.id), new_point.id, new_point.start_time, new_point.end_time); - new_point.start_time = new_point.end_time - ((time_t)ops.tier_ptr->tier_grouping * (time_t)ops.rd->update_every); + new_point.start_time = new_point.end_time - ops.tier_ptr->db_update_every; } // check if the db is advancing the query if(unlikely(new_point.end_time <= last1_point.end_time)) { - internal_error(true, "QUERY: next_metric(%s, %s) returned point %zu from %ld time %ld, before the last point %zu end time %ld, now is %ld to %ld", - rd->rrdset->name, rd->name, new_point.id, new_point.start_time, new_point.end_time, - last1_point.id, last1_point.end_time, now_start_time, now_end_time); + internal_error(db_points_read_since_plan_switch > 1, + "QUERY: '%s', dimension '%s' next_metric() returned point %zu from %ld to %ld, before the last point %zu from %ld to %ld, now is %ld to %ld", + qt->id, string2str(qm->dimension.id), new_point.id, new_point.start_time, new_point.end_time, + last1_point.id, last1_point.start_time, last1_point.end_time, now_start_time, now_end_time); count_same_end_time++; continue; @@ -1294,8 +1322,8 @@ static inline void rrd2rrdr_do_dimension( // we only log if this is not point 1 internal_error(new_point.end_time < after_wanted && new_point.id > 1, - "QUERY: next_metric(%s, %s) returned point %zu from %ld time %ld, which is entirely before our current timeframe %ld to %ld (and before the entire query, after %ld, before %ld)", - rd->rrdset->name, rd->name, + "QUERY: '%s', dimension '%s' next_metric() returned point %zu from %ld time %ld, which is entirely before our current timeframe %ld to %ld (and before the entire query, after %ld, before %ld)", + qt->id, string2str(qm->dimension.id), new_point.id, new_point.start_time, new_point.end_time, now_start_time, now_end_time, after_wanted, before_wanted); @@ -1311,8 +1339,8 @@ static inline void rrd2rrdr_do_dimension( if(unlikely(count_same_end_time)) { internal_error(true, - "QUERY: the database does not advance the query, it returned an end time less or equal to the end time of the last point we got %ld, %zu times", - last1_point.end_time, count_same_end_time); + "QUERY: '%s', dimension '%s', the database does not advance the query, it returned an end time less or equal to the end time of the last point we got %ld, %zu times", + qt->id, string2str(qm->dimension.id), last1_point.end_time, count_same_end_time); if(unlikely(new_point.end_time <= last1_point.end_time)) new_point.end_time = now_end_time; @@ -1323,7 +1351,7 @@ static inline void rrd2rrdr_do_dimension( // we select the one to use based on their timestamps size_t iterations = 0; - for ( ; now_end_time <= new_point.end_time && (long)points_added < points_wanted ; + for ( ; now_end_time <= new_point.end_time && points_added < points_wanted ; now_end_time += ops.view_update_every, iterations++) { // now_start_time is wrong in this loop @@ -1336,22 +1364,35 @@ static inline void rrd2rrdr_do_dimension( current_point = new_point; query_interpolate_point(current_point, last1_point, now_end_time); - internal_error(current_point.id > 0 && last1_point.id == 0 && current_point.end_time > after_wanted && current_point.end_time > now_end_time, - "QUERY: on '%s', dim '%s', after %ld, before %ld, view update every %ld, query granularity %ld," - " interpolating point %zu (from %ld to %ld) at %ld, but we could really favor by having last_point1 in this query.", - rd->rrdset->name, rd->name, after_wanted, before_wanted, ops.view_update_every, ops.query_granularity, - current_point.id, current_point.start_time, current_point.end_time, now_end_time); +// internal_error(current_point.id > 0 +// && last1_point.id == 0 +// && current_point.end_time > after_wanted +// && current_point.end_time > now_end_time, +// "QUERY: '%s', dimension '%s', after %ld, before %ld, view update every %ld," +// " query granularity %ld, interpolating point %zu (from %ld to %ld) at %ld," +// " but we could really favor by having last_point1 in this query.", +// qt->id, string2str(qm->dimension.id), +// after_wanted, before_wanted, +// ops.view_update_every, ops.query_granularity, +// current_point.id, current_point.start_time, current_point.end_time, +// now_end_time); } else if(likely(now_end_time <= last1_point.end_time)) { // our LAST point is still valid current_point = last1_point; query_interpolate_point(current_point, last2_point, now_end_time); - internal_error(current_point.id > 0 && last2_point.id == 0 && current_point.end_time > after_wanted && current_point.end_time > now_end_time, - "QUERY: on '%s', dim '%s', after %ld, before %ld, view update every %ld, query granularity %ld," - " interpolating point %zu (from %ld to %ld) at %ld, but we could really favor by having last_point2 in this query.", - rd->rrdset->name, rd->name, after_wanted, before_wanted, ops.view_update_every, ops.query_granularity, - current_point.id, current_point.start_time, current_point.end_time, now_end_time); +// internal_error(current_point.id > 0 +// && last2_point.id == 0 +// && current_point.end_time > after_wanted +// && current_point.end_time > now_end_time, +// "QUERY: '%s', dimension '%s', after %ld, before %ld, view update every %ld," +// " query granularity %ld, interpolating point %zu (from %ld to %ld) at %ld," +// " but we could really favor by having last_point2 in this query.", +// qt->id, string2str(qm->dimension.id), +// after_wanted, before_wanted, ops.view_update_every, ops.query_granularity, +// current_point.id, current_point.start_time, current_point.end_time, +// now_end_time); } else { // a GAP, we don't have a value this time @@ -1414,7 +1455,7 @@ static inline void rrd2rrdr_do_dimension( r->internal.result_points_generated += points_added; r->internal.db_points_read += ops.db_total_points_read; - for(int tr = 0; tr < storage_tiers ; tr++) + for(size_t tr = 0; tr < storage_tiers ; tr++) r->internal.tier_points_read[tr] += ops.db_points_read_per_tier[tr]; r->min = min; @@ -1423,24 +1464,26 @@ static inline void rrd2rrdr_do_dimension( r->after = min_date - ops.view_update_every + ops.query_granularity; rrdr_done(r, rrdr_line); - internal_error((long)points_added != points_wanted, - "QUERY: query on %s/%s requested %zu points, but RRDR added %zu (%zu db points read).", - r->st->name, rd->name, (size_t)points_wanted, (size_t)points_added, ops.db_total_points_read); + internal_error(points_added != points_wanted, + "QUERY: '%s', dimension '%s', requested %zu points, but RRDR added %zu (%zu db points read).", + qt->id, string2str(qm->dimension.id), + (size_t)points_wanted, (size_t)points_added, ops.db_total_points_read); } // ---------------------------------------------------------------------------- // fill the gap of a tier -extern void store_metric_at_tier(RRDDIM *rd, struct rrddim_tier *t, STORAGE_POINT sp, usec_t now_ut); +void store_metric_at_tier(RRDDIM *rd, size_t tier, struct rrddim_tier *t, STORAGE_POINT sp, usec_t now_ut); +void store_metric_collection_completed(void); -void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, int tier, time_t now) { - if(unlikely(tier < 0 || tier >= storage_tiers)) return; +void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, size_t tier, time_t now) { + if(unlikely(tier >= storage_tiers)) return; if(storage_tiers_backfill[tier] == RRD_BACKFILL_NONE) return; struct rrddim_tier *t = rd->tiers[tier]; if(unlikely(!t)) return; - time_t latest_time_t = t->query_ops.latest_time(t->db_metric_handle); + time_t latest_time_t = t->query_ops->latest_time(t->db_metric_handle); time_t granularity = (time_t)t->tier_grouping * (time_t)rd->update_every; time_t time_diff = now - latest_time_t; @@ -1450,43 +1493,40 @@ void rrdr_fill_tier_gap_from_smaller_tiers(RRDDIM *rd, int tier, time_t now) { // there is really nothing we can do if(now <= latest_time_t || time_diff < granularity) return; - struct rrddim_query_handle handle; - - size_t all_points_read = 0; + struct storage_engine_query_handle handle; // for each lower tier - for(int tr = tier - 1; tr >= 0 ;tr--){ - time_t smaller_tier_first_time = rd->tiers[tr]->query_ops.oldest_time(rd->tiers[tr]->db_metric_handle); - time_t smaller_tier_last_time = rd->tiers[tr]->query_ops.latest_time(rd->tiers[tr]->db_metric_handle); + for(int tr = (int)tier - 1; tr >= 0 ;tr--){ + time_t smaller_tier_first_time = rd->tiers[tr]->query_ops->oldest_time(rd->tiers[tr]->db_metric_handle); + time_t smaller_tier_last_time = rd->tiers[tr]->query_ops->latest_time(rd->tiers[tr]->db_metric_handle); if(smaller_tier_last_time <= latest_time_t) continue; // it is as bad as we are long after_wanted = (latest_time_t < smaller_tier_first_time) ? smaller_tier_first_time : latest_time_t; long before_wanted = smaller_tier_last_time; struct rrddim_tier *tmp = rd->tiers[tr]; - tmp->query_ops.init(tmp->db_metric_handle, &handle, after_wanted, before_wanted, TIER_QUERY_FETCH_AVERAGE); + tmp->query_ops->init(tmp->db_metric_handle, &handle, after_wanted, before_wanted); - size_t points = 0; + size_t points_read = 0; - while(!tmp->query_ops.is_finished(&handle)) { + while(!tmp->query_ops->is_finished(&handle)) { - STORAGE_POINT sp = tmp->query_ops.next_metric(&handle); + STORAGE_POINT sp = tmp->query_ops->next_metric(&handle); + points_read++; if(sp.end_time > latest_time_t) { latest_time_t = sp.end_time; - store_metric_at_tier(rd, t, sp, sp.end_time * USEC_PER_SEC); - points++; + store_metric_at_tier(rd, tr, t, sp, sp.end_time * USEC_PER_SEC); } } - all_points_read += points; - tmp->query_ops.finalize(&handle); + tmp->query_ops->finalize(&handle); + store_metric_collection_completed(); + global_statistics_backfill_query_completed(points_read); //internal_error(true, "DBENGINE: backfilled chart '%s', dimension '%s', tier %d, from %ld to %ld, with %zu points from tier %d", // rd->rrdset->name, rd->name, tier, after_wanted, before_wanted, points, tr); } - - rrdr_query_completed(all_points_read, all_points_read); } // ---------------------------------------------------------------------------- @@ -1497,29 +1537,33 @@ static void rrd2rrdr_log_request_response_metadata(RRDR *r , RRDR_OPTIONS options __maybe_unused , RRDR_GROUPING group_method , bool aligned - , long group - , long resampling_time - , long resampling_group + , size_t group + , time_t resampling_time + , size_t resampling_group , time_t after_wanted , time_t after_requested , time_t before_wanted , time_t before_requested - , long points_requested - , long points_wanted + , size_t points_requested + , size_t points_wanted //, size_t after_slot //, size_t before_slot , const char *msg ) { - netdata_rwlock_rdlock(&r->st->rrdset_rwlock); - info("INTERNAL ERROR: rrd2rrdr() on %s update every %d with %s grouping %s (group: %ld, resampling_time: %ld, resampling_group: %ld), " - "after (got: %zu, want: %zu, req: %ld, db: %zu), " - "before (got: %zu, want: %zu, req: %ld, db: %zu), " - "duration (got: %zu, want: %zu, req: %ld, db: %zu), " - //"slot (after: %zu, before: %zu, delta: %zu), " - "points (got: %ld, want: %ld, req: %ld, db: %ld), " + + time_t first_entry_t = r->internal.qt->db.first_time_t; + time_t last_entry_t = r->internal.qt->db.last_time_t; + + internal_error( + true, + "rrd2rrdr() on %s update every %ld with %s grouping %s (group: %zu, resampling_time: %ld, resampling_group: %zu), " + "after (got: %ld, want: %ld, req: %ld, db: %ld), " + "before (got: %ld, want: %ld, req: %ld, db: %ld), " + "duration (got: %ld, want: %ld, req: %ld, db: %ld), " + "points (got: %zu, want: %zu, req: %zu), " "%s" - , r->st->name - , r->st->update_every + , r->internal.qt->id + , r->internal.qt->window.query_granularity // grouping , (aligned) ? "aligned" : "unaligned" @@ -1529,45 +1573,36 @@ static void rrd2rrdr_log_request_response_metadata(RRDR *r , resampling_group // after - , (size_t)r->after - , (size_t)after_wanted + , r->after + , after_wanted , after_requested - , (size_t)rrdset_first_entry_t_nolock(r->st) + , first_entry_t // before - , (size_t)r->before - , (size_t)before_wanted + , r->before + , before_wanted , before_requested - , (size_t)rrdset_last_entry_t_nolock(r->st) + , last_entry_t // duration - , (size_t)(r->before - r->after + r->st->update_every) - , (size_t)(before_wanted - after_wanted + r->st->update_every) - , before_requested - after_requested - , (size_t)((rrdset_last_entry_t_nolock(r->st) - rrdset_first_entry_t_nolock(r->st)) + r->st->update_every) - - // slot - /* - , after_slot - , before_slot - , (after_slot > before_slot) ? (r->st->entries - after_slot + before_slot) : (before_slot - after_slot) - */ + , (long)(r->before - r->after + r->internal.qt->window.query_granularity) + , (long)(before_wanted - after_wanted + r->internal.qt->window.query_granularity) + , (long)before_requested - after_requested + , (long)((last_entry_t - first_entry_t) + r->internal.qt->window.query_granularity) // points , r->rows , points_wanted , points_requested - , r->st->entries // message , msg ); - netdata_rwlock_unlock(&r->st->rrdset_rwlock); } #endif // NETDATA_INTERNAL_CHECKS // Returns 1 if an absolute period was requested or 0 if it was a relative period -int rrdr_relative_window_to_absolute(long long *after, long long *before) { +bool rrdr_relative_window_to_absolute(time_t *after, time_t *before) { time_t now = now_realtime_sec() - 1; int absolute_period_requested = -1; @@ -1624,10 +1659,25 @@ int rrdr_relative_window_to_absolute(long long *after, long long *before) { after_requested -= delta; } + time_t absolute_minimum_time = now - (10 * 365 * 86400); + time_t absolute_maximum_time = now + (1 * 365 * 86400); + + if (after_requested < absolute_minimum_time && !unittest_running) + after_requested = absolute_minimum_time; + + if (after_requested > absolute_maximum_time && !unittest_running) + after_requested = absolute_maximum_time; + + if (before_requested < absolute_minimum_time && !unittest_running) + before_requested = absolute_minimum_time; + + if (before_requested > absolute_maximum_time && !unittest_running) + before_requested = absolute_maximum_time; + *before = before_requested; *after = after_requested; - return absolute_period_requested; + return (absolute_period_requested != 1); } // #define DEBUG_QUERY_LOGIC 1 @@ -1636,7 +1686,7 @@ int rrdr_relative_window_to_absolute(long long *after, long long *before) { #define query_debug_log_init() BUFFER *debug_log = buffer_create(1000) #define query_debug_log(args...) buffer_sprintf(debug_log, ##args) #define query_debug_log_fin() { \ - info("QUERY: chart '%s', after:%lld, before:%lld, duration:%lld, points:%ld, res:%ld - wanted => after:%lld, before:%lld, points:%ld, group:%ld, granularity:%ld, resgroup:%ld, resdiv:" NETDATA_DOUBLE_FORMAT_AUTO " %s", st->name, after_requested, before_requested, before_requested - after_requested, points_requested, resampling_time_requested, after_wanted, before_wanted, points_wanted, group, query_granularity, resampling_group, resampling_divisor, buffer_tostring(debug_log)); \ + info("QUERY: '%s', after:%ld, before:%ld, duration:%ld, points:%zu, res:%ld - wanted => after:%ld, before:%ld, points:%zu, group:%zu, granularity:%ld, resgroup:%ld, resdiv:" NETDATA_DOUBLE_FORMAT_AUTO " %s", qt->id, after_requested, before_requested, before_requested - after_requested, points_requested, resampling_time_requested, after_wanted, before_wanted, points_wanted, group, query_granularity, resampling_group, resampling_divisor, buffer_tostring(debug_log)); \ buffer_free(debug_log); \ debug_log = NULL; \ } @@ -1648,21 +1698,18 @@ int rrdr_relative_window_to_absolute(long long *after, long long *before) { #define query_debug_log_free() debug_dummy() #endif -RRDR *rrd2rrdr( - ONEWAYALLOC *owa - , RRDSET *st - , long points_requested - , long long after_requested - , long long before_requested - , RRDR_GROUPING group_method - , long resampling_time_requested - , RRDR_OPTIONS options - , const char *dimensions - , struct context_param *context_param_list - , const char *group_options - , int timeout - , int tier -) { +bool query_target_calculate_window(QUERY_TARGET *qt) { + if (unlikely(!qt)) return false; + + size_t points_requested = (long)qt->request.points; + time_t after_requested = qt->request.after; + time_t before_requested = qt->request.before; + RRDR_GROUPING group_method = qt->request.group_method; + time_t resampling_time_requested = qt->request.resampling_time; + RRDR_OPTIONS options = qt->request.options; + size_t tier = qt->request.tier; + time_t update_every = qt->db.minimum_latest_update_every; + // RULES // points_requested = 0 // the user wants all the natural points the database has @@ -1676,10 +1723,9 @@ RRDR *rrd2rrdr( // when natural points are wanted, the query has to be aligned to the update_every // of the database - long points_wanted = points_requested; - long long after_wanted = after_requested; - long long before_wanted = before_requested; - int update_every = st->update_every; + size_t points_wanted = points_requested; + time_t after_wanted = after_requested; + time_t before_wanted = before_requested; bool aligned = !(options & RRDR_OPTION_NOT_ALIGNED); bool automatic_natural_points = (points_wanted == 0); @@ -1689,13 +1735,7 @@ RRDR *rrd2rrdr( query_debug_log_init(); - // make sure points_wanted is positive - if(points_wanted < 0) { - points_wanted = -points_wanted; - query_debug_log(":-points_wanted %ld", points_wanted); - } - - if(ABS(before_requested) <= API_RELATIVE_TIME_MAX || ABS(after_requested) <= API_RELATIVE_TIME_MAX) { + if (ABS(before_requested) <= API_RELATIVE_TIME_MAX || ABS(after_requested) <= API_RELATIVE_TIME_MAX) { relative_period_requested = true; natural_points = true; options |= RRDR_OPTION_NATURAL_POINTS; @@ -1703,105 +1743,93 @@ RRDR *rrd2rrdr( } // if the user wants virtual points, make sure we do it - if(options & RRDR_OPTION_VIRTUAL_POINTS) + if (options & RRDR_OPTION_VIRTUAL_POINTS) natural_points = false; // set the right flag about natural and virtual points - if(natural_points) { + if (natural_points) { options |= RRDR_OPTION_NATURAL_POINTS; - if(options & RRDR_OPTION_VIRTUAL_POINTS) + if (options & RRDR_OPTION_VIRTUAL_POINTS) options &= ~RRDR_OPTION_VIRTUAL_POINTS; } else { options |= RRDR_OPTION_VIRTUAL_POINTS; - if(options & RRDR_OPTION_NATURAL_POINTS) + if (options & RRDR_OPTION_NATURAL_POINTS) options &= ~RRDR_OPTION_NATURAL_POINTS; } - if(after_wanted == 0 || before_wanted == 0) { - // for non-context queries we have to find the duration of the database - // for context queries we will assume 600 seconds duration - - if(!context_param_list) { - relative_period_requested = true; - - rrdset_rdlock(st); - time_t first_entry_t = rrdset_first_entry_t_nolock(st); - time_t last_entry_t = rrdset_last_entry_t_nolock(st); - rrdset_unlock(st); - - if(first_entry_t == 0 || last_entry_t == 0) { - internal_error(true, "QUERY: chart without data detected on '%s'", st->name); - query_debug_log_free(); - return NULL; - } + if (after_wanted == 0 || before_wanted == 0) { + relative_period_requested = true; - query_debug_log(":first_entry_t %ld, last_entry_t %ld", first_entry_t, last_entry_t); + time_t first_entry_t = qt->db.first_time_t; + time_t last_entry_t = qt->db.last_time_t; - if (after_wanted == 0) { - after_wanted = first_entry_t; - query_debug_log(":zero after_wanted %lld", after_wanted); - } + if (first_entry_t == 0 || last_entry_t == 0) { + internal_error(true, "QUERY: no data detected on query '%s' (db first_entry_t = %ld, last_entry_t = %ld", qt->id, first_entry_t, last_entry_t); + query_debug_log_free(); + return false; + } - if (before_wanted == 0) { - before_wanted = last_entry_t; - before_is_aligned_to_db_end = true; - query_debug_log(":zero before_wanted %lld", before_wanted); - } + query_debug_log(":first_entry_t %ld, last_entry_t %ld", first_entry_t, last_entry_t); - if(points_wanted == 0) { - points_wanted = (last_entry_t - first_entry_t) / update_every; - query_debug_log(":zero points_wanted %ld", points_wanted); - } + if (after_wanted == 0) { + after_wanted = first_entry_t; + query_debug_log(":zero after_wanted %ld", after_wanted); } - // if they are still zero, assume 600 + if (before_wanted == 0) { + before_wanted = last_entry_t; + before_is_aligned_to_db_end = true; + query_debug_log(":zero before_wanted %ld", before_wanted); + } - if(after_wanted == 0) { - after_wanted = -600; - query_debug_log(":zero600 after_wanted %lld", after_wanted); + if (points_wanted == 0) { + points_wanted = (last_entry_t - first_entry_t) / update_every; + query_debug_log(":zero points_wanted %zu", points_wanted); } } - if(points_wanted == 0) { + if (points_wanted == 0) { points_wanted = 600; - query_debug_log(":zero600 points_wanted %ld", points_wanted); + query_debug_log(":zero600 points_wanted %zu", points_wanted); } // convert our before_wanted and after_wanted to absolute rrdr_relative_window_to_absolute(&after_wanted, &before_wanted); - query_debug_log(":relative2absolute after %lld, before %lld", after_wanted, before_wanted); + query_debug_log(":relative2absolute after %ld, before %ld", after_wanted, before_wanted); - if(natural_points && (options & RRDR_OPTION_SELECTED_TIER) && tier > 0 && storage_tiers > 1) { - update_every = rrdset_find_natural_update_every_for_timeframe(st, after_wanted, before_wanted, points_wanted, options, tier); - if(update_every <= 0) update_every = st->update_every; - query_debug_log(":natural update every %d", update_every); + if (natural_points && (options & RRDR_OPTION_SELECTED_TIER) && tier > 0 && storage_tiers > 1) { + update_every = rrdset_find_natural_update_every_for_timeframe( + qt, after_wanted, before_wanted, points_wanted, options, tier); + + if (update_every <= 0) update_every = qt->db.minimum_latest_update_every; + query_debug_log(":natural update every %ld", update_every); } // this is the update_every of the query // it may be different to the update_every of the database - time_t query_granularity = (natural_points)?update_every:1; - if(query_granularity <= 0) query_granularity = 1; + time_t query_granularity = (natural_points) ? update_every : 1; + if (query_granularity <= 0) query_granularity = 1; query_debug_log(":query_granularity %ld", query_granularity); // align before_wanted and after_wanted to query_granularity if (before_wanted % query_granularity) { before_wanted -= before_wanted % query_granularity; - query_debug_log(":granularity align before_wanted %lld", before_wanted); + query_debug_log(":granularity align before_wanted %ld", before_wanted); } if (after_wanted % query_granularity) { after_wanted -= after_wanted % query_granularity; - query_debug_log(":granularity align after_wanted %lld", after_wanted); + query_debug_log(":granularity align after_wanted %ld", after_wanted); } // automatic_natural_points is set when the user wants all the points available in the database - if(automatic_natural_points) { + if (automatic_natural_points) { points_wanted = (before_wanted - after_wanted + 1) / query_granularity; - if(unlikely(points_wanted <= 0)) points_wanted = 1; - query_debug_log(":auto natural points_wanted %ld", points_wanted); + if (unlikely(points_wanted <= 0)) points_wanted = 1; + query_debug_log(":auto natural points_wanted %zu", points_wanted); } time_t duration = before_wanted - after_wanted; @@ -1810,42 +1838,47 @@ RRDR *rrd2rrdr( if (unlikely(resampling_time_requested > duration)) { after_wanted = before_wanted - resampling_time_requested; duration = before_wanted - after_wanted; - query_debug_log(":resampling after_wanted %lld", after_wanted); + query_debug_log(":resampling after_wanted %ld", after_wanted); } // if the duration is not aligned to resampling time // extend the duration to the past, to avoid a gap at the chart // only when the missing duration is above 1/10th of a point - if(resampling_time_requested > query_granularity && duration % resampling_time_requested) { + if (resampling_time_requested > query_granularity && duration % resampling_time_requested) { time_t delta = duration % resampling_time_requested; - if(delta > resampling_time_requested / 10) { + if (delta > resampling_time_requested / 10) { after_wanted -= resampling_time_requested - delta; duration = before_wanted - after_wanted; - query_debug_log(":resampling2 after_wanted %lld", after_wanted); + query_debug_log(":resampling2 after_wanted %ld", after_wanted); } } // the available points of the query - long points_available = (duration + 1) / query_granularity; - if(unlikely(points_available <= 0)) points_available = 1; - query_debug_log(":points_available %ld", points_available); + size_t points_available = (duration + 1) / query_granularity; + if (unlikely(points_available <= 0)) points_available = 1; + query_debug_log(":points_available %zu", points_available); - if(points_wanted > points_available) { + if (points_wanted > points_available) { points_wanted = points_available; - query_debug_log(":max points_wanted %ld", points_wanted); + query_debug_log(":max points_wanted %zu", points_wanted); + } + + if(points_wanted > 86400 && !unittest_running) { + points_wanted = 86400; + query_debug_log(":absolute max points_wanted %zu", points_wanted); } // calculate the desired grouping of source data points - long group = points_available / points_wanted; - if(group <= 0) group = 1; + size_t group = points_available / points_wanted; + if (group == 0) group = 1; // round "group" to the closest integer - if(points_available % points_wanted > points_wanted / 2) + if (points_available % points_wanted > points_wanted / 2) group++; - query_debug_log(":group %ld", group); + query_debug_log(":group %zu", group); - if(points_wanted * group * query_granularity < duration) { + if (points_wanted * group * query_granularity < (size_t)duration) { // the grouping we are going to do, is not enough // to cover the entire duration requested, so // we have to change the number of points, to make sure we will @@ -1854,162 +1887,186 @@ RRDR *rrd2rrdr( // let's see how many points are the optimal points_wanted = points_available / group; - if(points_wanted * group < points_available) + if (points_wanted * group < points_available) points_wanted++; - if(unlikely(points_wanted <= 0)) + if (unlikely(points_wanted == 0)) points_wanted = 1; - query_debug_log(":optimal points %ld", points_wanted); + query_debug_log(":optimal points %zu", points_wanted); } // resampling_time_requested enforces a certain grouping multiple NETDATA_DOUBLE resampling_divisor = 1.0; - long resampling_group = 1; - if(unlikely(resampling_time_requested > query_granularity)) { + size_t resampling_group = 1; + if (unlikely(resampling_time_requested > query_granularity)) { // the points we should group to satisfy gtime resampling_group = resampling_time_requested / query_granularity; - if(unlikely(resampling_time_requested % query_granularity)) + if (unlikely(resampling_time_requested % query_granularity)) resampling_group++; - query_debug_log(":resampling group %ld", resampling_group); + query_debug_log(":resampling group %zu", resampling_group); // adapt group according to resampling_group - if(unlikely(group < resampling_group)) { - group = resampling_group; // do not allow grouping below the desired one - query_debug_log(":group less res %ld", group); + if (unlikely(group < resampling_group)) { + group = resampling_group; // do not allow grouping below the desired one + query_debug_log(":group less res %zu", group); } - if(unlikely(group % resampling_group)) { + if (unlikely(group % resampling_group)) { group += resampling_group - (group % resampling_group); // make sure group is multiple of resampling_group - query_debug_log(":group mod res %ld", group); + query_debug_log(":group mod res %zu", group); } // resampling_divisor = group / resampling_group; - resampling_divisor = (NETDATA_DOUBLE)(group * query_granularity) / (NETDATA_DOUBLE)resampling_time_requested; + resampling_divisor = (NETDATA_DOUBLE) (group * query_granularity) / (NETDATA_DOUBLE) resampling_time_requested; query_debug_log(":resampling divisor " NETDATA_DOUBLE_FORMAT, resampling_divisor); } // now that we have group, align the requested timeframe to fit it. - if(aligned && before_wanted % (group * query_granularity)) { - if(before_is_aligned_to_db_end) - before_wanted -= before_wanted % (group * query_granularity); + if (aligned && before_wanted % (group * query_granularity)) { + if (before_is_aligned_to_db_end) + before_wanted -= before_wanted % (time_t)(group * query_granularity); else - before_wanted += (group * query_granularity) - before_wanted % (group * query_granularity); - query_debug_log(":align before_wanted %lld", before_wanted); + before_wanted += (time_t)(group * query_granularity) - before_wanted % (time_t)(group * query_granularity); + query_debug_log(":align before_wanted %ld", before_wanted); } - after_wanted = before_wanted - (points_wanted * group * query_granularity) + query_granularity; - query_debug_log(":final after_wanted %lld", after_wanted); + after_wanted = before_wanted - (time_t)(points_wanted * group * query_granularity) + query_granularity; + query_debug_log(":final after_wanted %ld", after_wanted); duration = before_wanted - after_wanted; query_debug_log(":final duration %ld", duration + 1); - // check the context query based on the starting time of the query - if (context_param_list && !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE)) { - rebuild_context_param_list(owa, context_param_list, after_wanted); - st = context_param_list->rd ? context_param_list->rd->rrdset : NULL; - - if(unlikely(!st)) - return NULL; - } + query_debug_log_fin(); internal_error(points_wanted != duration / (query_granularity * group) + 1, - "QUERY: points_wanted %ld is not points %ld", - points_wanted, duration / (query_granularity * group) + 1); + "QUERY: points_wanted %zu is not points %zu", + points_wanted, (size_t)(duration / (query_granularity * group) + 1)); internal_error(group < resampling_group, - "QUERY: group %ld is less than the desired group points %ld", + "QUERY: group %zu is less than the desired group points %zu", group, resampling_group); internal_error(group > resampling_group && group % resampling_group, - "QUERY: group %ld is not a multiple of the desired group points %ld", + "QUERY: group %zu is not a multiple of the desired group points %zu", group, resampling_group); // ------------------------------------------------------------------------- - // initialize our result set - // this also locks the chart for us + // update QUERY_TARGET with our calculations + + qt->window.after = after_wanted; + qt->window.before = before_wanted; + qt->window.relative = relative_period_requested; + qt->window.points = points_wanted; + qt->window.group = group; + qt->window.group_method = group_method; + qt->window.group_options = qt->request.group_options; + qt->window.query_granularity = query_granularity; + qt->window.resampling_group = resampling_group; + qt->window.resampling_divisor = resampling_divisor; + qt->window.options = options; + qt->window.tier = tier; + qt->window.aligned = aligned; + + return true; +} + +RRDR *rrd2rrdr_legacy( + ONEWAYALLOC *owa, + RRDSET *st, size_t points, time_t after, time_t before, + RRDR_GROUPING group_method, time_t resampling_time, RRDR_OPTIONS options, const char *dimensions, + const char *group_options, time_t timeout, size_t tier, QUERY_SOURCE query_source) { + + QUERY_TARGET_REQUEST qtr = { + .st = st, + .points = points, + .after = after, + .before = before, + .group_method = group_method, + .resampling_time = resampling_time, + .options = options, + .dimensions = dimensions, + .group_options = group_options, + .timeout = timeout, + .tier = tier, + .query_source = query_source, + }; + + return rrd2rrdr(owa, query_target_create(&qtr)); +} - RRDR *r = rrdr_create(owa, st, points_wanted, context_param_list); +RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) { + if(!qt) + return NULL; + + if(!owa) { + query_target_release(qt); + return NULL; + } + + // qt.window members are the WANTED ones. + // qt.request members are the REQUESTED ones. + + RRDR *r = rrdr_create(owa, qt); if(unlikely(!r)) { - internal_error(true, "QUERY: cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", - st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (uint32_t)duration, points_wanted); + internal_error(true, "QUERY: cannot create RRDR for %s, after=%ld, before=%ld, points=%zu", + qt->id, qt->window.after, qt->window.before, qt->window.points); return NULL; } - if(unlikely(!r->d || !points_wanted)) { - internal_error(true, "QUERY: returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%zu, points=%ld", - st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (size_t)duration, points_wanted); + if(unlikely(!r->d || !qt->window.points)) { + internal_error(true, "QUERY: returning empty RRDR (no dimensions in RRDSET) for %s, after=%ld, before=%ld, points=%zu", + qt->id, qt->window.after, qt->window.before, qt->window.points); return r; } - if(relative_period_requested) + if(qt->window.relative) r->result_options |= RRDR_RESULT_OPTION_RELATIVE; else r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE; - // find how many dimensions we have - long dimensions_count = r->d; - // ------------------------------------------------------------------------- // initialize RRDR - r->group = group; - r->update_every = (int)(group * query_granularity); - r->before = before_wanted; - r->after = after_wanted; - r->internal.points_wanted = points_wanted; - r->internal.resampling_group = resampling_group; - r->internal.resampling_divisor = resampling_divisor; - r->internal.query_options = options; - r->internal.query_tier = tier; + r->group = qt->window.group; + r->update_every = (int) (qt->window.group * qt->window.query_granularity); + r->before = qt->window.before; + r->after = qt->window.after; + r->internal.points_wanted = qt->window.points; + r->internal.resampling_group = qt->window.resampling_group; + r->internal.resampling_divisor = qt->window.resampling_divisor; + r->internal.query_options = qt->window.options; // ------------------------------------------------------------------------- // assign the processor functions - rrdr_set_grouping_function(r, group_method); + rrdr_set_grouping_function(r, qt->window.group_method); // allocate any memory required by the grouping method - r->internal.grouping_create(r, group_options); - - - // ------------------------------------------------------------------------- - // disable the not-wanted dimensions - - if (context_param_list && !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE)) - rrdset_check_rdlock(st); - - if(dimensions) - rrdr_disable_not_selected_dimensions(r, options, dimensions, context_param_list); - - - query_debug_log_fin(); + r->internal.grouping_create(r, qt->window.group_options); // ------------------------------------------------------------------------- // do the work for each dimension time_t max_after = 0, min_before = 0; - long max_rows = 0; + size_t max_rows = 0; - RRDDIM *first_rd = context_param_list ? context_param_list->rd : st->dimensions; - RRDDIM *rd; - long c, dimensions_used = 0, dimensions_nonzero = 0; + long dimensions_used = 0, dimensions_nonzero = 0; struct timeval query_start_time; struct timeval query_current_time; - if (timeout) now_realtime_timeval(&query_start_time); + if (qt->request.timeout) + now_realtime_timeval(&query_start_time); - for(rd = first_rd, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) { + for(size_t c = 0, max = qt->query.used; c < max ; c++) { + // set the query target dimension options to rrdr + r->od[c] = qt->query.array[c].dimension.options; - // if we need a percentage, we need to calculate all dimensions - if(unlikely(!(options & RRDR_OPTION_PERCENTAGE) && (r->od[c] & RRDR_DIMENSION_HIDDEN))) { - if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED)) r->od[c] &= ~RRDR_DIMENSION_SELECTED; - continue; - } r->od[c] |= RRDR_DIMENSION_SELECTED; // reset the grouping for the new dimension r->internal.grouping_reset(r); - rrd2rrdr_do_dimension(r, points_wanted, rd, c, after_wanted, before_wanted); - if (timeout) + rrd2rrdr_do_dimension(r, c); + if (qt->request.timeout) now_realtime_timeval(&query_current_time); if(r->od[c] & RRDR_DIMENSION_NONZERO) @@ -2024,30 +2081,30 @@ RRDR *rrd2rrdr( else { if(r->after != max_after) { internal_error(true, "QUERY: 'after' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu", - st->name, (size_t)max_after, rd->name, (size_t)r->after); + string2str(qt->query.array[c].dimension.id), (size_t)max_after, string2str(qt->query.array[c].dimension.name), (size_t)r->after); r->after = (r->after > max_after) ? r->after : max_after; } if(r->before != min_before) { internal_error(true, "QUERY: 'before' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu", - st->name, (size_t)min_before, rd->name, (size_t)r->before); + string2str(qt->query.array[c].dimension.id), (size_t)min_before, string2str(qt->query.array[c].dimension.name), (size_t)r->before); r->before = (r->before < min_before) ? r->before : min_before; } if(r->rows != max_rows) { internal_error(true, "QUERY: 'rows' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu", - st->name, (size_t)max_rows, rd->name, (size_t)r->rows); + string2str(qt->query.array[c].dimension.id), (size_t)max_rows, string2str(qt->query.array[c].dimension.name), (size_t)r->rows); r->rows = (r->rows > max_rows) ? r->rows : max_rows; } } dimensions_used++; - if (timeout && ((NETDATA_DOUBLE)dt_usec(&query_start_time, &query_current_time) / 1000.0) > timeout) { - log_access("QUERY CANCELED RUNTIME EXCEEDED %0.2f ms (LIMIT %d ms)", - (NETDATA_DOUBLE)dt_usec(&query_start_time, &query_current_time) / 1000.0, timeout); + if (qt->request.timeout && ((NETDATA_DOUBLE)dt_usec(&query_start_time, &query_current_time) / 1000.0) > (NETDATA_DOUBLE)qt->request.timeout) { + log_access("QUERY CANCELED RUNTIME EXCEEDED %0.2f ms (LIMIT %lld ms)", + (NETDATA_DOUBLE)dt_usec(&query_start_time, &query_current_time) / 1000.0, (long long)qt->request.timeout); r->result_options |= RRDR_RESULT_OPTION_CANCEL; break; } @@ -2056,44 +2113,44 @@ RRDR *rrd2rrdr( #ifdef NETDATA_INTERNAL_CHECKS if (dimensions_used) { if(r->internal.log) - rrd2rrdr_log_request_response_metadata(r, options, group_method, aligned, group, resampling_time_requested, resampling_group, - after_wanted, after_requested, before_wanted, before_requested, - points_requested, points_wanted, /*after_slot, before_slot,*/ + rrd2rrdr_log_request_response_metadata(r, qt->window.options, qt->window.group_method, qt->window.aligned, qt->window.group, qt->request.resampling_time, qt->window.resampling_group, + qt->window.after, qt->request.after, qt->window.before, qt->request.before, + qt->request.points, qt->window.points, /*after_slot, before_slot,*/ r->internal.log); - if(r->rows != points_wanted) - rrd2rrdr_log_request_response_metadata(r, options, group_method, aligned, group, resampling_time_requested, resampling_group, - after_wanted, after_requested, before_wanted, before_requested, - points_requested, points_wanted, /*after_slot, before_slot,*/ + if(r->rows != qt->window.points) + rrd2rrdr_log_request_response_metadata(r, qt->window.options, qt->window.group_method, qt->window.aligned, qt->window.group, qt->request.resampling_time, qt->window.resampling_group, + qt->window.after, qt->request.after, qt->window.before, qt->request.before, + qt->request.points, qt->window.points, /*after_slot, before_slot,*/ "got 'points' is not wanted 'points'"); - if(aligned && (r->before % (group * query_granularity)) != 0) - rrd2rrdr_log_request_response_metadata(r, options, group_method, aligned, group, resampling_time_requested, resampling_group, - after_wanted, after_requested, before_wanted,before_wanted, - points_requested, points_wanted, /*after_slot, before_slot,*/ + if(qt->window.aligned && (r->before % (qt->window.group * qt->window.query_granularity)) != 0) + rrd2rrdr_log_request_response_metadata(r, qt->window.options, qt->window.group_method, qt->window.aligned, qt->window.group, qt->request.resampling_time, qt->window.resampling_group, + qt->window.after, qt->request.after, qt->window.before,qt->request.before, + qt->request.points, qt->window.points, /*after_slot, before_slot,*/ "'before' is not aligned but alignment is required"); // 'after' should not be aligned, since we start inside the first group - //if(aligned && (r->after % group) != 0) - // rrd2rrdr_log_request_response_metadata(r, options, group_method, aligned, group, resampling_time_requested, resampling_group, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, "'after' is not aligned but alignment is required"); + //if(qt->window.aligned && (r->after % group) != 0) + // rrd2rrdr_log_request_response_metadata(r, qt->window.options, qt->window.group_method, qt->window.aligned, qt->window.group, qt->request.resampling_time, qt->window.resampling_group, qt->window.after, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, "'after' is not aligned but alignment is required"); - if(r->before != before_wanted) - rrd2rrdr_log_request_response_metadata(r, options, group_method, aligned, group, resampling_time_requested, resampling_group, - after_wanted, after_requested, before_wanted, before_requested, - points_requested, points_wanted, /*after_slot, before_slot,*/ + if(r->before != qt->window.before) + rrd2rrdr_log_request_response_metadata(r, qt->window.options, qt->window.group_method, qt->window.aligned, qt->window.group, qt->request.resampling_time, qt->window.resampling_group, + qt->window.after, qt->request.after, qt->window.before, qt->request.before, + qt->request.points, qt->window.points, /*after_slot, before_slot,*/ "chart is not aligned to requested 'before'"); - if(r->before != before_wanted) - rrd2rrdr_log_request_response_metadata(r, options, group_method, aligned, group, resampling_time_requested, resampling_group, - after_wanted, after_requested, before_wanted, before_requested, - points_requested, points_wanted, /*after_slot, before_slot,*/ + if(r->before != qt->window.before) + rrd2rrdr_log_request_response_metadata(r, qt->window.options, qt->window.group_method, qt->window.aligned, qt->window.group, qt->request.resampling_time, qt->window.resampling_group, + qt->window.after, qt->request.after, qt->window.before, qt->request.before, + qt->request.points, qt->window.points, /*after_slot, before_slot,*/ "got 'before' is not wanted 'before'"); // reported 'after' varies, depending on group - if(r->after != after_wanted) - rrd2rrdr_log_request_response_metadata(r, options, group_method, aligned, group, resampling_time_requested, resampling_group, - after_wanted, after_requested, before_wanted, before_requested, - points_requested, points_wanted, /*after_slot, before_slot,*/ + if(r->after != qt->window.after) + rrd2rrdr_log_request_response_metadata(r, qt->window.options, qt->window.group_method, qt->window.aligned, qt->window.group, qt->request.resampling_time, qt->window.resampling_group, + qt->window.after, qt->request.after, qt->window.before, qt->request.before, + qt->request.points, qt->window.points, /*after_slot, before_slot,*/ "got 'after' is not wanted 'after'"); } @@ -2103,15 +2160,16 @@ RRDR *rrd2rrdr( r->internal.grouping_free(r); // when all the dimensions are zero, we should return all of them - if(unlikely(options & RRDR_OPTION_NONZERO && !dimensions_nonzero && !(r->result_options & RRDR_RESULT_OPTION_CANCEL))) { + if(unlikely((qt->window.options & RRDR_OPTION_NONZERO) && !dimensions_nonzero && !(r->result_options & RRDR_RESULT_OPTION_CANCEL))) { // all the dimensions are zero // mark them as NONZERO to send them all - for(rd = first_rd, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) { + for(size_t c = 0, max = qt->query.used; c < max ; c++) { if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue; r->od[c] |= RRDR_DIMENSION_NONZERO; } } - rrdr_query_completed(r->internal.db_points_read, r->internal.result_points_generated); + global_statistics_rrdr_query_completed(dimensions_used, r->internal.db_points_read, + r->internal.result_points_generated, qt->request.query_source); return r; } diff --git a/web/api/queries/query.h b/web/api/queries/query.h index df876d9ac..ebad5a1f8 100644 --- a/web/api/queries/query.h +++ b/web/api/queries/query.h @@ -47,10 +47,10 @@ typedef enum rrdr_grouping { RRDR_GROUPING_COUNTIF, } RRDR_GROUPING; -extern const char *group_method2string(RRDR_GROUPING group); -extern void web_client_api_v1_init_grouping(void); -extern RRDR_GROUPING web_client_api_request_v1_data_group(const char *name, RRDR_GROUPING def); -extern const char *web_client_api_request_v1_data_group_to_string(RRDR_GROUPING group); +const char *group_method2string(RRDR_GROUPING group); +void web_client_api_v1_init_grouping(void); +RRDR_GROUPING web_client_api_request_v1_data_group(const char *name, RRDR_GROUPING def); +const char *web_client_api_request_v1_data_group_to_string(RRDR_GROUPING group); #ifdef __cplusplus } diff --git a/web/api/queries/rrdr.c b/web/api/queries/rrdr.c index ecf4ca2ac..676224c9d 100644 --- a/web/api/queries/rrdr.c +++ b/web/api/queries/rrdr.c @@ -61,9 +61,7 @@ static void rrdr_dump(RRDR *r) inline void rrdr_free(ONEWAYALLOC *owa, RRDR *r) { if(unlikely(!r)) return; - if(likely(r->st_locked_by_rrdr_create)) - rrdset_unlock(r->st); - + query_target_release(r->internal.qt); onewayalloc_freez(owa, r->t); onewayalloc_freez(owa, r->v); onewayalloc_freez(owa, r->o); @@ -72,12 +70,23 @@ inline void rrdr_free(ONEWAYALLOC *owa, RRDR *r) { onewayalloc_freez(owa, r); } -RRDR *rrdr_create_for_x_dimensions(ONEWAYALLOC *owa, int dimensions, long points) { +RRDR *rrdr_create(ONEWAYALLOC *owa, QUERY_TARGET *qt) { + if(unlikely(!qt || !qt->query.used || !qt->window.points)) + return NULL; + + size_t dimensions = qt->query.used; + size_t points = qt->window.points; + + // create the rrdr RRDR *r = onewayalloc_callocz(owa, 1, sizeof(RRDR)); r->internal.owa = owa; + r->internal.qt = qt; - r->d = dimensions; - r->n = points; + r->before = qt->window.before; + r->after = qt->window.after; + r->internal.points_wanted = qt->window.points; + r->d = (int)dimensions; + r->n = (int)points; r->t = onewayalloc_callocz(owa, points, sizeof(time_t)); r->v = onewayalloc_mallocz(owa, points * dimensions * sizeof(NETDATA_DOUBLE)); @@ -90,42 +99,3 @@ RRDR *rrdr_create_for_x_dimensions(ONEWAYALLOC *owa, int dimensions, long points return r; } - -RRDR *rrdr_create(ONEWAYALLOC *owa, struct rrdset *st, long n, struct context_param *context_param_list) { - if (unlikely(!st)) return NULL; - - bool st_locked_by_rrdr_create = false; - if (!context_param_list || !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE)) { - rrdset_rdlock(st); - st_locked_by_rrdr_create = true; - } - - // count the number of dimensions - int dimensions = 0; - RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL; - RRDDIM *rd; - if (temp_rd) { - RRDDIM *t = temp_rd; - while (t) { - dimensions++; - t = t->next; - } - } else - rrddim_foreach_read(rd, st) dimensions++; - - // create the rrdr - RRDR *r = rrdr_create_for_x_dimensions(owa, dimensions, n); - r->st = st; - r->st_locked_by_rrdr_create = st_locked_by_rrdr_create; - - // set the hidden flag on hidden dimensions - int c; - for (c = 0, rd = temp_rd ? temp_rd : st->dimensions; rd; c++, rd = rd->next) { - if (unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN))) - r->od[c] = RRDR_DIMENSION_HIDDEN; - else - r->od[c] = RRDR_DIMENSION_DEFAULT; - } - - return r; -} diff --git a/web/api/queries/rrdr.h b/web/api/queries/rrdr.h index 1c80e103f..6151cddc7 100644 --- a/web/api/queries/rrdr.h +++ b/web/api/queries/rrdr.h @@ -18,32 +18,33 @@ typedef enum tier_query_fetch { } TIER_QUERY_FETCH; typedef enum rrdr_options { - RRDR_OPTION_NONZERO = 0x00000001, // don't output dimensions with just zero values - RRDR_OPTION_REVERSED = 0x00000002, // output the rows in reverse order (oldest to newest) - RRDR_OPTION_ABSOLUTE = 0x00000004, // values positive, for DATASOURCE_SSV before summing - RRDR_OPTION_MIN2MAX = 0x00000008, // when adding dimensions, use max - min, instead of sum - RRDR_OPTION_SECONDS = 0x00000010, // output seconds, instead of dates - RRDR_OPTION_MILLISECONDS = 0x00000020, // output milliseconds, instead of dates - RRDR_OPTION_NULL2ZERO = 0x00000040, // do not show nulls, convert them to zeros - RRDR_OPTION_OBJECTSROWS = 0x00000080, // each row of values should be an object, not an array - RRDR_OPTION_GOOGLE_JSON = 0x00000100, // comply with google JSON/JSONP specs - RRDR_OPTION_JSON_WRAP = 0x00000200, // wrap the response in a JSON header with info about the result - RRDR_OPTION_LABEL_QUOTES = 0x00000400, // in CSV output, wrap header labels in double quotes - RRDR_OPTION_PERCENTAGE = 0x00000800, // give values as percentage of total - RRDR_OPTION_NOT_ALIGNED = 0x00001000, // do not align charts for persistent timeframes - RRDR_OPTION_DISPLAY_ABS = 0x00002000, // for badges, display the absolute value, but calculate colors with sign - RRDR_OPTION_MATCH_IDS = 0x00004000, // when filtering dimensions, match only IDs - RRDR_OPTION_MATCH_NAMES = 0x00008000, // when filtering dimensions, match only names - RRDR_OPTION_CUSTOM_VARS = 0x00010000, // when wrapping response in a JSON, return custom variables in response - RRDR_OPTION_NATURAL_POINTS = 0x00020000, // return the natural points of the database - RRDR_OPTION_VIRTUAL_POINTS = 0x00040000, // return virtual points - RRDR_OPTION_ANOMALY_BIT = 0x00080000, // Return the anomaly bit stored in each collected_number - RRDR_OPTION_RETURN_RAW = 0x00100000, // Return raw data for aggregating across multiple nodes - RRDR_OPTION_RETURN_JWAR = 0x00200000, // Return anomaly rates in jsonwrap - RRDR_OPTION_SELECTED_TIER = 0x00400000, // Use the selected tier for the query + RRDR_OPTION_NONZERO = 0x00000001, // don't output dimensions with just zero values + RRDR_OPTION_REVERSED = 0x00000002, // output the rows in reverse order (oldest to newest) + RRDR_OPTION_ABSOLUTE = 0x00000004, // values positive, for DATASOURCE_SSV before summing + RRDR_OPTION_MIN2MAX = 0x00000008, // when adding dimensions, use max - min, instead of sum + RRDR_OPTION_SECONDS = 0x00000010, // output seconds, instead of dates + RRDR_OPTION_MILLISECONDS = 0x00000020, // output milliseconds, instead of dates + RRDR_OPTION_NULL2ZERO = 0x00000040, // do not show nulls, convert them to zeros + RRDR_OPTION_OBJECTSROWS = 0x00000080, // each row of values should be an object, not an array + RRDR_OPTION_GOOGLE_JSON = 0x00000100, // comply with google JSON/JSONP specs + RRDR_OPTION_JSON_WRAP = 0x00000200, // wrap the response in a JSON header with info about the result + RRDR_OPTION_LABEL_QUOTES = 0x00000400, // in CSV output, wrap header labels in double quotes + RRDR_OPTION_PERCENTAGE = 0x00000800, // give values as percentage of total + RRDR_OPTION_NOT_ALIGNED = 0x00001000, // do not align charts for persistent timeframes + RRDR_OPTION_DISPLAY_ABS = 0x00002000, // for badges, display the absolute value, but calculate colors with sign + RRDR_OPTION_MATCH_IDS = 0x00004000, // when filtering dimensions, match only IDs + RRDR_OPTION_MATCH_NAMES = 0x00008000, // when filtering dimensions, match only names + RRDR_OPTION_NATURAL_POINTS = 0x00020000, // return the natural points of the database + RRDR_OPTION_VIRTUAL_POINTS = 0x00040000, // return virtual points + RRDR_OPTION_ANOMALY_BIT = 0x00080000, // Return the anomaly bit stored in each collected_number + RRDR_OPTION_RETURN_RAW = 0x00100000, // Return raw data for aggregating across multiple nodes + RRDR_OPTION_RETURN_JWAR = 0x00200000, // Return anomaly rates in jsonwrap + RRDR_OPTION_SELECTED_TIER = 0x00400000, // Use the selected tier for the query + RRDR_OPTION_ALL_DIMENSIONS = 0x00800000, // Return the full dimensions list // internal ones - not to be exposed to the API - RRDR_OPTION_INTERNAL_AR = 0x10000000, // internal use only, to let the formatters we want to render the anomaly rate + RRDR_OPTION_INTERNAL_AR = 0x10000000, // internal use only, to let the formatters we want to render the anomaly rate + RRDR_OPTION_HEALTH_RSRVD1 = 0x80000000, // reserved for RRDCALC_OPTION_NO_CLEAR_NOTIFICATION } RRDR_OPTIONS; typedef enum rrdr_value_flag { @@ -67,16 +68,14 @@ typedef enum rrdr_result_flags { // (should not to be cached by browsers and proxies) RRDR_RESULT_OPTION_VARIABLE_STEP = 0x00000004, // the query uses variable-step time-frames RRDR_RESULT_OPTION_CANCEL = 0x00000008, // the query needs to be cancelled -} RRDR_RESULT_FLAGS; +} RRDR_RESULT_OPTIONS; typedef struct rrdresult { - struct rrdset *st; // the chart this result refers to + RRDR_RESULT_OPTIONS result_options; // RRDR_RESULT_OPTION_* - RRDR_RESULT_FLAGS result_options; // RRDR_RESULT_OPTION_* - - int d; // the number of dimensions - long n; // the number of values in the arrays - long rows; // the number of rows used + size_t d; // the number of dimensions + size_t n; // the number of values in the arrays + size_t rows; // the number of rows used RRDR_DIMENSION_FLAGS *od; // the options for the dimensions @@ -85,8 +84,8 @@ typedef struct rrdresult { RRDR_VALUE_FLAGS *o; // array n x d options for each value returned NETDATA_DOUBLE *ar; // array n x d of anomaly rates (0 - 100) - long group; // how many collected values were grouped for each row - int update_every; // what is the suggested update frequency in seconds + size_t group; // how many collected values were grouped for each row + time_t update_every; // what is the suggested update frequency in seconds NETDATA_DOUBLE min; NETDATA_DOUBLE max; @@ -94,53 +93,57 @@ typedef struct rrdresult { time_t before; time_t after; - bool st_locked_by_rrdr_create; // if st is read locked by us - // internal rrd2rrdr() members below this point struct { - int query_tier; // the selected tier - RRDR_OPTIONS query_options; // RRDR_OPTION_* (as run by the query) + ONEWAYALLOC *owa; // the allocator used + struct query_target *qt; // the QUERY_TARGET + + RRDR_OPTIONS query_options; // RRDR_OPTION_* (as run by the query) - long points_wanted; - long resampling_group; - NETDATA_DOUBLE resampling_divisor; + size_t points_wanted; // used by SES and DES + size_t resampling_group; // used by AVERAGE + NETDATA_DOUBLE resampling_divisor; // used by AVERAGE + // grouping function pointers void (*grouping_create)(struct rrdresult *r, const char *options); void (*grouping_reset)(struct rrdresult *r); void (*grouping_free)(struct rrdresult *r); void (*grouping_add)(struct rrdresult *r, NETDATA_DOUBLE value); NETDATA_DOUBLE (*grouping_flush)(struct rrdresult *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); - void *grouping_data; - TIER_QUERY_FETCH tier_query_fetch; - #ifdef NETDATA_INTERNAL_CHECKS + TIER_QUERY_FETCH tier_query_fetch; // which value to use from STORAGE_POINT + void *grouping_data; // the internal data of the grouping function + +#ifdef NETDATA_INTERNAL_CHECKS const char *log; - #endif +#endif + // statistics size_t db_points_read; size_t result_points_generated; size_t tier_points_read[RRD_STORAGE_TIERS]; - ONEWAYALLOC *owa; } internal; } RRDR; #define rrdr_rows(r) ((r)->rows) #include "database/rrd.h" -extern void rrdr_free(ONEWAYALLOC *owa, RRDR *r); -extern RRDR *rrdr_create(ONEWAYALLOC *owa, struct rrdset *st, long n, struct context_param *context_param_list); -extern RRDR *rrdr_create_for_x_dimensions(ONEWAYALLOC *owa, int dimensions, long points); +void rrdr_free(ONEWAYALLOC *owa, RRDR *r); +RRDR *rrdr_create(ONEWAYALLOC *owa, struct query_target *qt); #include "../web_api_v1.h" #include "web/api/queries/query.h" -extern RRDR *rrd2rrdr( - ONEWAYALLOC *owa, - RRDSET *st, long points_wanted, long long after_wanted, long long before_wanted, - RRDR_GROUPING group_method, long resampling_time_requested, RRDR_OPTIONS options, const char *dimensions, - struct context_param *context_param_list, const char *group_options, int timeout, int tier); +RRDR *rrd2rrdr_legacy( + ONEWAYALLOC *owa, + RRDSET *st, size_t points, time_t after, time_t before, + RRDR_GROUPING group_method, time_t resampling_time, RRDR_OPTIONS options, const char *dimensions, + const char *group_options, time_t timeout, size_t tier, QUERY_SOURCE query_source); + +RRDR *rrd2rrdr(ONEWAYALLOC *owa, struct query_target *qt); +bool query_target_calculate_window(struct query_target *qt); -extern int rrdr_relative_window_to_absolute(long long *after, long long *before); +bool rrdr_relative_window_to_absolute(time_t *after, time_t *before); #ifdef __cplusplus } diff --git a/web/api/queries/ses/ses.h b/web/api/queries/ses/ses.h index 094b8de3f..79b09fbdf 100644 --- a/web/api/queries/ses/ses.h +++ b/web/api/queries/ses/ses.h @@ -6,12 +6,12 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_init_ses(void); +void grouping_init_ses(void); -extern void grouping_create_ses(RRDR *r, const char *options __maybe_unused); -extern void grouping_reset_ses(RRDR *r); -extern void grouping_free_ses(RRDR *r); -extern void grouping_add_ses(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_ses(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_ses(RRDR *r, const char *options __maybe_unused); +void grouping_reset_ses(RRDR *r); +void grouping_free_ses(RRDR *r); +void grouping_add_ses(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_ses(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERIES_SES_H diff --git a/web/api/queries/stddev/stddev.h b/web/api/queries/stddev/stddev.h index c5c91f88d..4b8ffcd53 100644 --- a/web/api/queries/stddev/stddev.h +++ b/web/api/queries/stddev/stddev.h @@ -6,13 +6,13 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_stddev(RRDR *r, const char *options __maybe_unused); -extern void grouping_reset_stddev(RRDR *r); -extern void grouping_free_stddev(RRDR *r); -extern void grouping_add_stddev(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); -extern NETDATA_DOUBLE grouping_flush_coefficient_of_variation(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); -// extern NETDATA_DOUBLE grouping_flush_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); -// extern NETDATA_DOUBLE grouping_flush_variance(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_stddev(RRDR *r, const char *options __maybe_unused); +void grouping_reset_stddev(RRDR *r); +void grouping_free_stddev(RRDR *r); +void grouping_add_stddev(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +NETDATA_DOUBLE grouping_flush_coefficient_of_variation(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +// NETDATA_DOUBLE grouping_flush_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +// NETDATA_DOUBLE grouping_flush_variance(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERIES_STDDEV_H diff --git a/web/api/queries/sum/sum.h b/web/api/queries/sum/sum.h index 4e7e396e9..898782775 100644 --- a/web/api/queries/sum/sum.h +++ b/web/api/queries/sum/sum.h @@ -6,10 +6,10 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_sum(RRDR *r, const char *options __maybe_unused); -extern void grouping_reset_sum(RRDR *r); -extern void grouping_free_sum(RRDR *r); -extern void grouping_add_sum(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_sum(RRDR *r, const char *options __maybe_unused); +void grouping_reset_sum(RRDR *r); +void grouping_free_sum(RRDR *r); +void grouping_add_sum(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERY_SUM_H diff --git a/web/api/queries/trimmed_mean/trimmed_mean.h b/web/api/queries/trimmed_mean/trimmed_mean.h index 1a4f63e9c..e66d92541 100644 --- a/web/api/queries/trimmed_mean/trimmed_mean.h +++ b/web/api/queries/trimmed_mean/trimmed_mean.h @@ -6,17 +6,17 @@ #include "../query.h" #include "../rrdr.h" -extern void grouping_create_trimmed_mean1(RRDR *r, const char *options); -extern void grouping_create_trimmed_mean2(RRDR *r, const char *options); -extern void grouping_create_trimmed_mean3(RRDR *r, const char *options); -extern void grouping_create_trimmed_mean5(RRDR *r, const char *options); -extern void grouping_create_trimmed_mean10(RRDR *r, const char *options); -extern void grouping_create_trimmed_mean15(RRDR *r, const char *options); -extern void grouping_create_trimmed_mean20(RRDR *r, const char *options); -extern void grouping_create_trimmed_mean25(RRDR *r, const char *options); -extern void grouping_reset_trimmed_mean(RRDR *r); -extern void grouping_free_trimmed_mean(RRDR *r); -extern void grouping_add_trimmed_mean(RRDR *r, NETDATA_DOUBLE value); -extern NETDATA_DOUBLE grouping_flush_trimmed_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); +void grouping_create_trimmed_mean1(RRDR *r, const char *options); +void grouping_create_trimmed_mean2(RRDR *r, const char *options); +void grouping_create_trimmed_mean3(RRDR *r, const char *options); +void grouping_create_trimmed_mean5(RRDR *r, const char *options); +void grouping_create_trimmed_mean10(RRDR *r, const char *options); +void grouping_create_trimmed_mean15(RRDR *r, const char *options); +void grouping_create_trimmed_mean20(RRDR *r, const char *options); +void grouping_create_trimmed_mean25(RRDR *r, const char *options); +void grouping_reset_trimmed_mean(RRDR *r); +void grouping_free_trimmed_mean(RRDR *r); +void grouping_add_trimmed_mean(RRDR *r, NETDATA_DOUBLE value); +NETDATA_DOUBLE grouping_flush_trimmed_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr); #endif //NETDATA_API_QUERIES_TRIMMED_MEAN_H diff --git a/web/api/queries/weights.c b/web/api/queries/weights.c index 97a00f91c..a9555a66b 100644 --- a/web/api/queries/weights.c +++ b/web/api/queries/weights.c @@ -56,40 +56,14 @@ typedef enum { struct register_result { RESULT_FLAGS flags; - RRDSET *st; - const char *chart_id; - const char *context; - const char *dim_name; + RRDCONTEXT_ACQUIRED *rca; + RRDINSTANCE_ACQUIRED *ria; + RRDMETRIC_ACQUIRED *rma; NETDATA_DOUBLE value; - - struct register_result *next; // used to link contexts together }; -static void register_result_insert_callback(const char *name, void *value, void *data) { - (void)name; - (void)data; - - struct register_result *t = (struct register_result *)value; - - if(t->chart_id) t->chart_id = strdupz(t->chart_id); - if(t->context) t->context = strdupz(t->context); - if(t->dim_name) t->dim_name = strdupz(t->dim_name); -} - -static void register_result_delete_callback(const char *name, void *value, void *data) { - (void)name; - (void)data; - struct register_result *t = (struct register_result *)value; - - freez((void *)t->chart_id); - freez((void *)t->context); - freez((void *)t->dim_name); -} - static DICTIONARY *register_result_init() { - DICTIONARY *results = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); - dictionary_register_insert_callback(results, register_result_insert_callback, results); - dictionary_register_delete_callback(results, register_result_delete_callback, results); + DICTIONARY *results = dictionary_create(DICT_OPTION_SINGLE_THREADED); return results; } @@ -98,8 +72,9 @@ static void register_result_destroy(DICTIONARY *results) { } static void register_result(DICTIONARY *results, - RRDSET *st, - RRDDIM *d, + RRDCONTEXT_ACQUIRED *rca, + RRDINSTANCE_ACQUIRED *ria, + RRDMETRIC_ACQUIRED *rma, NETDATA_DOUBLE value, RESULT_FLAGS flags, WEIGHTS_STATS *stats, @@ -120,25 +95,25 @@ static void register_result(DICTIONARY *results, struct register_result t = { .flags = flags, - .st = st, - .chart_id = st->id, - .context = st->context, - .dim_name = d->name, + .rca = rca, + .ria = ria, + .rma = rma, .value = v }; - char buf[5000 + 1]; - snprintfz(buf, 5000, "%s:%s", st->id, d->name); - dictionary_set(results, buf, &t, sizeof(struct register_result)); + // we can use the pointer address or RMA as a unique key for each metric + char buf[20 + 1]; + ssize_t len = snprintfz(buf, 20, "%p", rma); + dictionary_set_advanced(results, buf, len + 1, &t, sizeof(struct register_result), NULL); } // ---------------------------------------------------------------------------- // Generation of JSON output for the results static void results_header_to_json(DICTIONARY *results __maybe_unused, BUFFER *wb, - long long after, long long before, - long long baseline_after, long long baseline_before, - long points, WEIGHTS_METHOD method, + time_t after, time_t before, + time_t baseline_after, time_t baseline_before, + size_t points, WEIGHTS_METHOD method, RRDR_GROUPING group, RRDR_OPTIONS options, uint32_t shifts, size_t examined_dimensions __maybe_unused, usec_t duration, WEIGHTS_STATS *stats) { @@ -147,10 +122,10 @@ static void results_header_to_json(DICTIONARY *results __maybe_unused, BUFFER *w "\t\"after\": %lld,\n" "\t\"before\": %lld,\n" "\t\"duration\": %lld,\n" - "\t\"points\": %ld,\n", - after, - before, - before - after, + "\t\"points\": %zu,\n", + (long long)after, + (long long)before, + (long long)(before - after), points ); @@ -159,10 +134,10 @@ static void results_header_to_json(DICTIONARY *results __maybe_unused, BUFFER *w "\t\"baseline_after\": %lld,\n" "\t\"baseline_before\": %lld,\n" "\t\"baseline_duration\": %lld,\n" - "\t\"baseline_points\": %ld,\n", - baseline_after, - baseline_before, - baseline_before - baseline_after, + "\t\"baseline_points\": %zu,\n", + (long long)baseline_after, + (long long)baseline_before, + (long long)(baseline_before - baseline_after), points << shifts ); @@ -181,7 +156,7 @@ static void results_header_to_json(DICTIONARY *results __maybe_unused, BUFFER *w stats->db_points ); - for(int tier = 0; tier < storage_tiers ;tier++) + for(size_t tier = 0; tier < storage_tiers ;tier++) buffer_sprintf(wb, "%s%zu", tier?", ":"", stats->db_points_per_tier[tier]); buffer_sprintf(wb, " ]\n" @@ -193,13 +168,13 @@ static void results_header_to_json(DICTIONARY *results __maybe_unused, BUFFER *w weights_method_to_string(method) ); - web_client_api_request_v1_data_options_to_string(wb, options); + web_client_api_request_v1_data_options_to_buffer(wb, options); } static size_t registered_results_to_json_charts(DICTIONARY *results, BUFFER *wb, - long long after, long long before, - long long baseline_after, long long baseline_before, - long points, WEIGHTS_METHOD method, + time_t after, time_t before, + time_t baseline_after, time_t baseline_before, + size_t points, WEIGHTS_METHOD method, RRDR_GROUPING group, RRDR_OPTIONS options, uint32_t shifts, size_t examined_dimensions, usec_t duration, WEIGHTS_STATS *stats) { @@ -211,23 +186,23 @@ static size_t registered_results_to_json_charts(DICTIONARY *results, BUFFER *wb, size_t charts = 0, chart_dims = 0, total_dimensions = 0; struct register_result *t; - RRDSET *last_st = NULL; // never access this - we use it only for comparison + RRDINSTANCE_ACQUIRED *last_ria = NULL; // never access this - we use it only for comparison dfe_start_read(results, t) { - if(!last_st || t->st != last_st) { - last_st = t->st; + if(t->ria != last_ria) { + last_ria = t->ria; if(charts) buffer_strcat(wb, "\n\t\t\t}\n\t\t},\n"); buffer_strcat(wb, "\t\t\""); - buffer_strcat(wb, t->chart_id); + buffer_strcat(wb, rrdinstance_acquired_id(t->ria)); buffer_strcat(wb, "\": {\n"); buffer_strcat(wb, "\t\t\t\"context\": \""); - buffer_strcat(wb, t->context); + buffer_strcat(wb, rrdcontext_acquired_id(t->rca)); buffer_strcat(wb, "\",\n\t\t\t\"dimensions\": {\n"); charts++; chart_dims = 0; } if (chart_dims) buffer_sprintf(wb, ",\n"); - buffer_sprintf(wb, "\t\t\t\t\"%s\": " NETDATA_DOUBLE_FORMAT, t->dim_name, t->value); + buffer_sprintf(wb, "\t\t\t\t\"%s\": " NETDATA_DOUBLE_FORMAT, rrdmetric_acquired_name(t->rma), t->value); chart_dims++; total_dimensions++; } @@ -250,9 +225,9 @@ static size_t registered_results_to_json_charts(DICTIONARY *results, BUFFER *wb, } static size_t registered_results_to_json_contexts(DICTIONARY *results, BUFFER *wb, - long long after, long long before, - long long baseline_after, long long baseline_before, - long points, WEIGHTS_METHOD method, + time_t after, time_t before, + time_t baseline_after, time_t baseline_before, + size_t points, WEIGHTS_METHOD method, RRDR_GROUPING group, RRDR_OPTIONS options, uint32_t shifts, size_t examined_dimensions, usec_t duration, WEIGHTS_STATS *stats) { @@ -260,78 +235,80 @@ static size_t registered_results_to_json_contexts(DICTIONARY *results, BUFFER *w results_header_to_json(results, wb, after, before, baseline_after, baseline_before, points, method, group, options, shifts, examined_dimensions, duration, stats); - DICTIONARY *context_results = dictionary_create( - DICTIONARY_FLAG_SINGLE_THREADED - |DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE - |DICTIONARY_FLAG_NAME_LINK_DONT_CLONE - |DICTIONARY_FLAG_DONT_OVERWRITE_VALUE - ); + buffer_strcat(wb, "\",\n\t\"contexts\": {\n"); + size_t contexts = 0, charts = 0, total_dimensions = 0, context_dims = 0, chart_dims = 0; + NETDATA_DOUBLE contexts_total_weight = 0.0, charts_total_weight = 0.0; struct register_result *t; + RRDCONTEXT_ACQUIRED *last_rca = NULL; + RRDINSTANCE_ACQUIRED *last_ria = NULL; dfe_start_read(results, t) { - struct register_result *tc = dictionary_set(context_results, t->context, t, sizeof(*t)); - if(tc == t) - t->next = NULL; - else { - t->next = tc->next; - tc->next = t; + + if(t->rca != last_rca) { + last_rca = t->rca; + + if(contexts) + buffer_sprintf(wb, "\n" + "\t\t\t\t\t},\n" + "\t\t\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n" + "\t\t\t\t}\n\t\t\t},\n" + "\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n\t\t},\n" + , charts_total_weight / (double)chart_dims + , contexts_total_weight / (double)context_dims); + + buffer_strcat(wb, "\t\t\""); + buffer_strcat(wb, rrdcontext_acquired_id(t->rca)); + buffer_strcat(wb, "\": {\n\t\t\t\"charts\":{\n"); + + contexts++; + charts = 0; + context_dims = 0; + contexts_total_weight = 0.0; + + last_ria = NULL; } - } - dfe_done(t); - buffer_strcat(wb, "\",\n\t\"contexts\": {\n"); + if(t->ria != last_ria) { + last_ria = t->ria; - size_t contexts = 0, total_dimensions = 0, charts = 0, context_dims = 0, chart_dims = 0; - NETDATA_DOUBLE contexts_total_weight = 0.0, charts_total_weight = 0.0; - RRDSET *last_st = NULL; // never access this - we use it only for comparison - dfe_start_read(context_results, t) { - - if(contexts) - buffer_sprintf(wb, "\n\t\t\t\t\t},\n\t\t\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n\t\t},\n", charts_total_weight / chart_dims, contexts_total_weight / context_dims); - - contexts++; - context_dims = 0; - contexts_total_weight = 0.0; - - buffer_strcat(wb, "\t\t\""); - buffer_strcat(wb, t->context); - buffer_strcat(wb, "\": {\n\t\t\t\"charts\":{\n"); - - charts = 0; - chart_dims = 0; - struct register_result *tt; - for(tt = t; tt ; tt = tt->next) { - if(!last_st || tt->st != last_st) { - last_st = tt->st; - - if(charts) - buffer_sprintf(wb, "\n\t\t\t\t\t},\n\t\t\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n\t\t\t\t},\n", charts_total_weight / chart_dims); - - buffer_strcat(wb, "\t\t\t\t\""); - buffer_strcat(wb, tt->chart_id); - buffer_strcat(wb, "\": {\n"); - buffer_strcat(wb, "\t\t\t\t\t\"dimensions\": {\n"); - charts++; - chart_dims = 0; - charts_total_weight = 0.0; - } - - if (chart_dims) buffer_sprintf(wb, ",\n"); - buffer_sprintf(wb, "\t\t\t\t\t\t\"%s\": " NETDATA_DOUBLE_FORMAT, tt->dim_name, tt->value); - charts_total_weight += tt->value; - contexts_total_weight += tt->value; - chart_dims++; - context_dims++; - total_dimensions++; + if(charts) + buffer_sprintf(wb, "\n" + "\t\t\t\t\t},\n" + "\t\t\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n" + "\t\t\t\t},\n" + , charts_total_weight / (double)chart_dims); + + buffer_strcat(wb, "\t\t\t\t\""); + buffer_strcat(wb, rrdinstance_acquired_id(t->ria)); + buffer_strcat(wb, "\": {\n"); + buffer_strcat(wb, "\t\t\t\t\t\"dimensions\": {\n"); + + charts++; + chart_dims = 0; + charts_total_weight = 0.0; } + + if (chart_dims) buffer_sprintf(wb, ",\n"); + buffer_sprintf(wb, "\t\t\t\t\t\t\"%s\": " NETDATA_DOUBLE_FORMAT, rrdmetric_acquired_name(t->rma), t->value); + charts_total_weight += t->value; + contexts_total_weight += t->value; + chart_dims++; + context_dims++; + total_dimensions++; } dfe_done(t); - dictionary_destroy(context_results); - // close dimensions and chart if (total_dimensions) - buffer_sprintf(wb, "\n\t\t\t\t\t},\n\t\t\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n\t\t}\n", charts_total_weight / chart_dims, contexts_total_weight / context_dims); + buffer_sprintf(wb, "\n" + "\t\t\t\t\t},\n" + "\t\t\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n" + "\t\t\t\t}\n" + "\t\t\t},\n" + "\t\t\t\"weight\":" NETDATA_DOUBLE_FORMAT "\n" + "\t\t}\n" + , charts_total_weight / (double)chart_dims + , contexts_total_weight / (double)context_dims); // close correlated_charts buffer_sprintf(wb, "\t},\n" @@ -391,7 +368,10 @@ static size_t calculate_pairs_diff(DIFFS_NUMBERS *diffs, NETDATA_DOUBLE *arr, si return added; } -static double ks_2samp(DIFFS_NUMBERS baseline_diffs[], int base_size, DIFFS_NUMBERS highlight_diffs[], int high_size, uint32_t base_shifts) { +static double ks_2samp( + DIFFS_NUMBERS baseline_diffs[], int base_size, + DIFFS_NUMBERS highlight_diffs[], int high_size, + uint32_t base_shifts) { qsort(baseline_diffs, base_size, sizeof(DIFFS_NUMBERS), compare_diffs); qsort(highlight_diffs, high_size, sizeof(DIFFS_NUMBERS), compare_diffs); @@ -414,7 +394,7 @@ static double ks_2samp(DIFFS_NUMBERS baseline_diffs[], int base_size, DIFFS_NUMB // This would require a lot of multiplications and divisions. // // To speed it up, we do the binary search to find the index of each number - // but then we divide the base index by the power of two number (shifts) it + // but, then we divide the base index by the power of two number (shifts) it // is bigger than high index. So the 2 indexes are now comparable. // We also keep track of the original indexes with min and max, to properly // calculate their percentages once the loops finish. @@ -495,7 +475,9 @@ static double ks_2samp(DIFFS_NUMBERS baseline_diffs[], int base_size, DIFFS_NUMB static double kstwo( NETDATA_DOUBLE baseline[], int baseline_points, - NETDATA_DOUBLE highlight[], int highlight_points, uint32_t base_shifts) { + NETDATA_DOUBLE highlight[], int highlight_points, + uint32_t base_shifts) { + // -1 in size, since the calculate_pairs_diffs() returns one less point DIFFS_NUMBERS baseline_diffs[baseline_points - 1]; DIFFS_NUMBERS highlight_diffs[highlight_points - 1]; @@ -514,308 +496,215 @@ static double kstwo( return ks_2samp(baseline_diffs, base_size, highlight_diffs, high_size, base_shifts); } +NETDATA_DOUBLE *rrd2rrdr_ks2( + ONEWAYALLOC *owa, RRDHOST *host, + RRDCONTEXT_ACQUIRED *rca, RRDINSTANCE_ACQUIRED *ria, RRDMETRIC_ACQUIRED *rma, + time_t after, time_t before, size_t points, RRDR_OPTIONS options, + RRDR_GROUPING group_method, const char *group_options, size_t tier, + WEIGHTS_STATS *stats, + size_t *entries + ) { + + NETDATA_DOUBLE *ret = NULL; + + QUERY_TARGET_REQUEST qtr = { + .host = host, + .rca = rca, + .ria = ria, + .rma = rma, + .after = after, + .before = before, + .points = points, + .options = options, + .group_method = group_method, + .group_options = group_options, + .tier = tier, + .query_source = QUERY_SOURCE_API_WEIGHTS, + }; -static int rrdset_metric_correlations_ks2(RRDSET *st, DICTIONARY *results, - long long baseline_after, long long baseline_before, - long long after, long long before, - long long points, RRDR_OPTIONS options, - RRDR_GROUPING group, const char *group_options, int tier, - uint32_t shifts, int timeout, - WEIGHTS_STATS *stats, bool register_zero) { - options |= RRDR_OPTION_NATURAL_POINTS; - - long group_time = 0; - struct context_param *context_param_list = NULL; - - int examined_dimensions = 0; - - RRDR *high_rrdr = NULL; - RRDR *base_rrdr = NULL; - - // get first the highlight to find the number of points available - stats->db_queries++; - usec_t started_usec = now_realtime_usec(); - ONEWAYALLOC *owa = onewayalloc_create(0); - high_rrdr = rrd2rrdr(owa, st, points, - after, before, group, - group_time, options, NULL, context_param_list, group_options, - timeout, tier); - if(!high_rrdr) { - info("Metric correlations: rrd2rrdr() failed for the highlighted window on chart '%s'.", st->name); + RRDR *r = rrd2rrdr(owa, query_target_create(&qtr)); + if(!r) goto cleanup; - } - for(int i = 0; i < storage_tiers ;i++) - stats->db_points_per_tier[i] += high_rrdr->internal.tier_points_read[i]; + stats->db_queries++; + stats->result_points += r->internal.result_points_generated; + stats->db_points += r->internal.db_points_read; + for(size_t tr = 0; tr < storage_tiers ; tr++) + stats->db_points_per_tier[tr] += r->internal.tier_points_read[tr]; - stats->db_points += high_rrdr->internal.db_points_read; - stats->result_points += high_rrdr->internal.result_points_generated; - if(!high_rrdr->d) { - info("Metric correlations: rrd2rrdr() did not return any dimensions on chart '%s'.", st->name); + if(r->d != 1) { + error("WEIGHTS: on query '%s' expected 1 dimension in RRDR but got %zu", r->internal.qt->id, r->d); goto cleanup; } - if(high_rrdr->result_options & RRDR_RESULT_OPTION_CANCEL) { - info("Metric correlations: rrd2rrdr() on highlighted window timed out '%s'.", st->name); + + if(unlikely(r->od[0] & RRDR_DIMENSION_HIDDEN)) goto cleanup; - } - int high_points = rrdr_rows(high_rrdr); - usec_t now_usec = now_realtime_usec(); - if(now_usec - started_usec > timeout * USEC_PER_MS) + if(unlikely(!(r->od[0] & RRDR_DIMENSION_NONZERO))) goto cleanup; - // get the baseline, requesting the same number of points as the highlight - stats->db_queries++; - base_rrdr = rrd2rrdr(owa, st,high_points << shifts, - baseline_after, baseline_before, group, - group_time, options, NULL, context_param_list, group_options, - (int)(timeout - ((now_usec - started_usec) / USEC_PER_MS)), tier); - if(!base_rrdr) { - info("Metric correlations: rrd2rrdr() failed for the baseline window on chart '%s'.", st->name); + if(rrdr_rows(r) < 2) goto cleanup; - } - for(int i = 0; i < storage_tiers ;i++) - stats->db_points_per_tier[i] += base_rrdr->internal.tier_points_read[i]; + *entries = rrdr_rows(r); + ret = onewayalloc_mallocz(owa, sizeof(NETDATA_DOUBLE) * rrdr_rows(r)); - stats->db_points += base_rrdr->internal.db_points_read; - stats->result_points += base_rrdr->internal.result_points_generated; - if(!base_rrdr->d) { - info("Metric correlations: rrd2rrdr() did not return any dimensions on chart '%s'.", st->name); - goto cleanup; - } - if (base_rrdr->d != high_rrdr->d) { - info("Cannot generate metric correlations for chart '%s' when the baseline and the highlight have different number of dimensions.", st->name); - goto cleanup; - } - if(base_rrdr->result_options & RRDR_RESULT_OPTION_CANCEL) { - info("Metric correlations: rrd2rrdr() on baseline window timed out '%s'.", st->name); - goto cleanup; - } - int base_points = rrdr_rows(base_rrdr); + // copy the points of the dimension to a contiguous array + // there is no need to check for empty values, since empty values are already zero + // https://github.com/netdata/netdata/blob/6e3144683a73a2024d51425b20ecfd569034c858/web/api/queries/average/average.c#L41-L43 + memcpy(ret, r->v, rrdr_rows(r) * sizeof(NETDATA_DOUBLE)); - now_usec = now_realtime_usec(); - if(now_usec - started_usec > timeout * USEC_PER_MS) - goto cleanup; +cleanup: + rrdr_free(owa, r); + return ret; +} + +static void rrdset_metric_correlations_ks2( + RRDHOST *host, + RRDCONTEXT_ACQUIRED *rca, RRDINSTANCE_ACQUIRED *ria, RRDMETRIC_ACQUIRED *rma, + DICTIONARY *results, + time_t baseline_after, time_t baseline_before, + time_t after, time_t before, + size_t points, RRDR_OPTIONS options, + RRDR_GROUPING group_method, const char *group_options, size_t tier, + uint32_t shifts, + WEIGHTS_STATS *stats, bool register_zero + ) { + + options |= RRDR_OPTION_NATURAL_POINTS; - // we need at least 2 points to do the job - if(base_points < 2 || high_points < 2) + ONEWAYALLOC *owa = onewayalloc_create(16 * 1024); + + size_t high_points = 0; + NETDATA_DOUBLE *highlight = rrd2rrdr_ks2( + owa, host, rca, ria, rma, after, before, points, + options, group_method, group_options, tier, stats, &high_points); + + if(!highlight) goto cleanup; - // for each dimension - RRDDIM *d; - int i; - for(i = 0, d = base_rrdr->st->dimensions ; d && i < base_rrdr->d; i++, d = d->next) { + size_t base_points = 0; + NETDATA_DOUBLE *baseline = rrd2rrdr_ks2( + owa, host, rca, ria, rma, baseline_after, baseline_before, high_points << shifts, + options, group_method, group_options, tier, stats, &base_points); + + if(!baseline) + goto cleanup; - // skip the not evaluated ones - if(unlikely(base_rrdr->od[i] & RRDR_DIMENSION_HIDDEN) || (high_rrdr->od[i] & RRDR_DIMENSION_HIDDEN)) - continue; + stats->binary_searches += 2 * (base_points - 1) + 2 * (high_points - 1); - examined_dimensions++; + double prob = kstwo(baseline, (int)base_points, highlight, (int)high_points, shifts); + if(!isnan(prob) && !isinf(prob)) { - // skip the dimensions that are just zero for both the baseline and the highlight - if(unlikely(!(base_rrdr->od[i] & RRDR_DIMENSION_NONZERO) && !(high_rrdr->od[i] & RRDR_DIMENSION_NONZERO))) - continue; - - // copy the baseline points of the dimension to a contiguous array - // there is no need to check for empty values, since empty are already zero - NETDATA_DOUBLE baseline[base_points]; - for(int c = 0; c < base_points; c++) - baseline[c] = base_rrdr->v[ c * base_rrdr->d + i ]; - - // copy the highlight points of the dimension to a contiguous array - // there is no need to check for empty values, since empty values are already zero - // https://github.com/netdata/netdata/blob/6e3144683a73a2024d51425b20ecfd569034c858/web/api/queries/average/average.c#L41-L43 - NETDATA_DOUBLE highlight[high_points]; - for(int c = 0; c < high_points; c++) - highlight[c] = high_rrdr->v[ c * high_rrdr->d + i ]; - - stats->binary_searches += 2 * (base_points - 1) + 2 * (high_points - 1); - - double prob = kstwo(baseline, base_points, highlight, high_points, shifts); - if(!isnan(prob) && !isinf(prob)) { - - // these conditions should never happen, but still let's check - if(unlikely(prob < 0.0)) { - error("Metric correlations: kstwo() returned a negative number: %f", prob); - prob = -prob; - } - if(unlikely(prob > 1.0)) { - error("Metric correlations: kstwo() returned a number above 1.0: %f", prob); - prob = 1.0; - } - - // to spread the results evenly, 0.0 needs to be the less correlated and 1.0 the most correlated - // so we flip the result of kstwo() - register_result(results, base_rrdr->st, d, 1.0 - prob, RESULT_IS_BASE_HIGH_RATIO, stats, register_zero); + // these conditions should never happen, but still let's check + if(unlikely(prob < 0.0)) { + error("Metric correlations: kstwo() returned a negative number: %f", prob); + prob = -prob; + } + if(unlikely(prob > 1.0)) { + error("Metric correlations: kstwo() returned a number above 1.0: %f", prob); + prob = 1.0; } + + // to spread the results evenly, 0.0 needs to be the less correlated and 1.0 the most correlated + // so, we flip the result of kstwo() + register_result(results, rca, ria, rma, 1.0 - prob, RESULT_IS_BASE_HIGH_RATIO, stats, register_zero); } cleanup: - rrdr_free(owa, high_rrdr); - rrdr_free(owa, base_rrdr); onewayalloc_destroy(owa); - return examined_dimensions; } // ---------------------------------------------------------------------------- // VOLUME algorithm functions -static int rrdset_metric_correlations_volume(RRDSET *st, DICTIONARY *results, - long long baseline_after, long long baseline_before, - long long after, long long before, - RRDR_OPTIONS options, RRDR_GROUPING group, const char *group_options, - int tier, int timeout, - WEIGHTS_STATS *stats, bool register_zero) { +static void merge_query_value_to_stats(QUERY_VALUE *qv, WEIGHTS_STATS *stats) { + stats->db_queries++; + stats->result_points += qv->result_points; + stats->db_points += qv->points_read; + for(size_t tier = 0; tier < storage_tiers ; tier++) + stats->db_points_per_tier[tier] += qv->storage_points_per_tier[tier]; +} + +static void rrdset_metric_correlations_volume( + RRDHOST *host, + RRDCONTEXT_ACQUIRED *rca, RRDINSTANCE_ACQUIRED *ria, RRDMETRIC_ACQUIRED *rma, + DICTIONARY *results, + time_t baseline_after, time_t baseline_before, + time_t after, time_t before, + RRDR_OPTIONS options, RRDR_GROUPING group_method, const char *group_options, + size_t tier, + WEIGHTS_STATS *stats, bool register_zero) { options |= RRDR_OPTION_MATCH_IDS | RRDR_OPTION_ABSOLUTE | RRDR_OPTION_NATURAL_POINTS; - long group_time = 0; - - int examined_dimensions = 0; - int ret, value_is_null; - usec_t started_usec = now_realtime_usec(); - RRDDIM *d; - for(d = st->dimensions; d ; d = d->next) { - usec_t now_usec = now_realtime_usec(); - if(now_usec - started_usec > timeout * USEC_PER_MS) - return examined_dimensions; + QUERY_VALUE baseline_average = rrdmetric2value(host, rca, ria, rma, baseline_after, baseline_before, options, group_method, group_options, tier, 0, QUERY_SOURCE_API_WEIGHTS); + merge_query_value_to_stats(&baseline_average, stats); - // we count how many metrics we evaluated - examined_dimensions++; + if(!netdata_double_isnumber(baseline_average.value)) { + // this means no data for the baseline window, but we may have data for the highlighted one - assume zero + baseline_average.value = 0.0; + } - // there is no point to pass a timeout to these queries - // since the query engine checks for a timeout between - // dimensions, and we query a single dimension at a time. - - stats->db_queries++; - NETDATA_DOUBLE baseline_average = NAN; - NETDATA_DOUBLE base_anomaly_rate = 0; - value_is_null = 1; - ret = rrdset2value_api_v1(st, NULL, &baseline_average, d->id, 1, - baseline_after, baseline_before, - group, group_options, group_time, options, - NULL, NULL, - &stats->db_points, stats->db_points_per_tier, - &stats->result_points, - &value_is_null, &base_anomaly_rate, 0, tier); - - if(ret != HTTP_RESP_OK || value_is_null || !netdata_double_isnumber(baseline_average)) { - // this means no data for the baseline window, but we may have data for the highlighted one - assume zero - baseline_average = 0.0; - } + QUERY_VALUE highlight_average = rrdmetric2value(host, rca, ria, rma, after, before, options, group_method, group_options, tier, 0, QUERY_SOURCE_API_WEIGHTS); + merge_query_value_to_stats(&highlight_average, stats); - stats->db_queries++; - NETDATA_DOUBLE highlight_average = NAN; - NETDATA_DOUBLE high_anomaly_rate = 0; - value_is_null = 1; - ret = rrdset2value_api_v1(st, NULL, &highlight_average, d->id, 1, - after, before, - group, group_options, group_time, options, - NULL, NULL, - &stats->db_points, stats->db_points_per_tier, - &stats->result_points, - &value_is_null, &high_anomaly_rate, 0, tier); - - if(ret != HTTP_RESP_OK || value_is_null || !netdata_double_isnumber(highlight_average)) { - // this means no data for the highlighted duration - so skip it - continue; - } + if(!netdata_double_isnumber(highlight_average.value)) + return; - if(baseline_average == highlight_average) { - // they are the same - let's move on - continue; - } + if(baseline_average.value == highlight_average.value) { + // they are the same - let's move on + return; + } - stats->db_queries++; - NETDATA_DOUBLE highlight_countif = NAN; - value_is_null = 1; - - char highlighted_countif_options[50 + 1]; - snprintfz(highlighted_countif_options, 50, "%s" NETDATA_DOUBLE_FORMAT, highlight_average < baseline_average ? "<":">", baseline_average); - - ret = rrdset2value_api_v1(st, NULL, &highlight_countif, d->id, 1, - after, before, - RRDR_GROUPING_COUNTIF,highlighted_countif_options, - group_time, options, - NULL, NULL, - &stats->db_points, stats->db_points_per_tier, - &stats->result_points, - &value_is_null, NULL, 0, tier); - - if(ret != HTTP_RESP_OK || value_is_null || !netdata_double_isnumber(highlight_countif)) { - info("MC: highlighted countif query failed, but highlighted average worked - strange..."); - continue; - } + char highlight_countif_options[50 + 1]; + snprintfz(highlight_countif_options, 50, "%s" NETDATA_DOUBLE_FORMAT, highlight_average.value < baseline_average.value ? "<" : ">", baseline_average.value); + QUERY_VALUE highlight_countif = rrdmetric2value(host, rca, ria, rma, after, before, options, RRDR_GROUPING_COUNTIF, highlight_countif_options, tier, 0, QUERY_SOURCE_API_WEIGHTS); + merge_query_value_to_stats(&highlight_countif, stats); - // this represents the percentage of time - // the highlighted window was above/below the baseline window - // (above or below depending on their averages) - highlight_countif = highlight_countif / 100.0; // countif returns 0 - 100.0 + if(!netdata_double_isnumber(highlight_countif.value)) { + info("WEIGHTS: highlighted countif query failed, but highlighted average worked - strange..."); + return; + } - RESULT_FLAGS flags; - NETDATA_DOUBLE pcent = NAN; - if(isgreater(baseline_average, 0.0) || isless(baseline_average, 0.0)) { - flags = RESULT_IS_BASE_HIGH_RATIO; - pcent = (highlight_average - baseline_average) / baseline_average * highlight_countif; - } - else { - flags = RESULT_IS_PERCENTAGE_OF_TIME; - pcent = highlight_countif; - } + // this represents the percentage of time + // the highlighted window was above/below the baseline window + // (above or below depending on their averages) + highlight_countif.value = highlight_countif.value / 100.0; // countif returns 0 - 100.0 - register_result(results, st, d, pcent, flags, stats, register_zero); + RESULT_FLAGS flags; + NETDATA_DOUBLE pcent = NAN; + if(isgreater(baseline_average.value, 0.0) || isless(baseline_average.value, 0.0)) { + flags = RESULT_IS_BASE_HIGH_RATIO; + pcent = (highlight_average.value - baseline_average.value) / baseline_average.value * highlight_countif.value; + } + else { + flags = RESULT_IS_PERCENTAGE_OF_TIME; + pcent = highlight_countif.value; } - return examined_dimensions; + register_result(results, rca, ria, rma, pcent, flags, stats, register_zero); } // ---------------------------------------------------------------------------- // ANOMALY RATE algorithm functions -static int rrdset_weights_anomaly_rate(RRDSET *st, DICTIONARY *results, - long long after, long long before, - RRDR_OPTIONS options, RRDR_GROUPING group, const char *group_options, - int tier, int timeout, - WEIGHTS_STATS *stats, bool register_zero) { +static void rrdset_weights_anomaly_rate( + RRDHOST *host, + RRDCONTEXT_ACQUIRED *rca, RRDINSTANCE_ACQUIRED *ria, RRDMETRIC_ACQUIRED *rma, + DICTIONARY *results, + time_t after, time_t before, + RRDR_OPTIONS options, RRDR_GROUPING group_method, const char *group_options, + size_t tier, + WEIGHTS_STATS *stats, bool register_zero) { options |= RRDR_OPTION_MATCH_IDS | RRDR_OPTION_ANOMALY_BIT | RRDR_OPTION_NATURAL_POINTS; - long group_time = 0; - int examined_dimensions = 0; - int ret, value_is_null; - usec_t started_usec = now_realtime_usec(); - - RRDDIM *d; - for(d = st->dimensions; d ; d = d->next) { - usec_t now_usec = now_realtime_usec(); - if(now_usec - started_usec > timeout * USEC_PER_MS) - return examined_dimensions; + QUERY_VALUE qv = rrdmetric2value(host, rca, ria, rma, after, before, options, group_method, group_options, tier, 0, QUERY_SOURCE_API_WEIGHTS); + merge_query_value_to_stats(&qv, stats); - // we count how many metrics we evaluated - examined_dimensions++; - - // there is no point to pass a timeout to these queries - // since the query engine checks for a timeout between - // dimensions, and we query a single dimension at a time. - - stats->db_queries++; - NETDATA_DOUBLE average = NAN; - NETDATA_DOUBLE anomaly_rate = 0; - value_is_null = 1; - ret = rrdset2value_api_v1(st, NULL, &average, d->id, 1, - after, before, - group, group_options, group_time, options, - NULL, NULL, - &stats->db_points, stats->db_points_per_tier, - &stats->result_points, - &value_is_null, &anomaly_rate, 0, tier); - - if(ret == HTTP_RESP_OK || !value_is_null || netdata_double_isnumber(average)) - register_result(results, st, d, average, 0, stats, register_zero); - } - - return examined_dimensions; + if(netdata_double_isnumber(qv.value)) + register_result(results, rca, ria, rma, qv.value, 0, stats, register_zero); } // ---------------------------------------------------------------------------- @@ -853,7 +742,7 @@ static size_t spread_results_evenly(DICTIONARY *results, WEIGHTS_STATS *stats) { struct register_result *t; // count the dimensions - size_t dimensions = dictionary_stats_entries(results); + size_t dimensions = dictionary_entries(results); if(!dimensions) return 0; if(stats->max_base_high_ratio == 0.0) @@ -903,15 +792,17 @@ static size_t spread_results_evenly(DICTIONARY *results, WEIGHTS_STATS *stats) { // ---------------------------------------------------------------------------- // The main function -int web_api_v1_weights(RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS_FORMAT format, - RRDR_GROUPING group, const char *group_options, - long long baseline_after, long long baseline_before, - long long after, long long before, - long long points, RRDR_OPTIONS options, SIMPLE_PATTERN *contexts, int tier, int timeout) { +int web_api_v1_weights( + RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS_FORMAT format, + RRDR_GROUPING group, const char *group_options, + time_t baseline_after, time_t baseline_before, + time_t after, time_t before, + size_t points, RRDR_OPTIONS options, SIMPLE_PATTERN *contexts, size_t tier, size_t timeout) { + WEIGHTS_STATS stats = {}; DICTIONARY *results = register_result_init(); - DICTIONARY *charts = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE);; + DICTIONARY *metrics = NULL; char *error = NULL; int resp = HTTP_RESP_OK; @@ -1000,20 +891,7 @@ int web_api_v1_weights(RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS baseline_after = baseline_before - (high_delta << shifts); } - // dont lock here and wait for results - // get the charts and run mc after - RRDSET *st; - rrdhost_rdlock(host); - rrdset_foreach_read(st, host) { - if (rrdset_is_available_for_viewers(st)) { - if(!contexts || simple_pattern_matches(contexts, st->context)) - dictionary_set(charts, st->name, NULL, 0); - } - } - rrdhost_unlock(host); - size_t examined_dimensions = 0; - void *ptr; bool register_zero = true; if(options & RRDR_OPTION_NONZERO) { @@ -1021,8 +899,11 @@ int web_api_v1_weights(RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS options &= ~RRDR_OPTION_NONZERO; } - // for every chart in the dictionary - dfe_start_read(charts, ptr) { + metrics = rrdcontext_all_metrics_to_dict(host, contexts); + struct metric_entry *me; + + // for every metric_entry in the dictionary + dfe_start_read(metrics, me) { usec_t now_usec = now_realtime_usec(); if(now_usec - started_usec > timeout_usec) { error = "timed out"; @@ -1030,46 +911,48 @@ int web_api_v1_weights(RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS goto cleanup; } - st = rrdset_find_byname(host, ptr_name); - if(!st) continue; - - rrdset_rdlock(st); + examined_dimensions++; switch(method) { case WEIGHTS_METHOD_ANOMALY_RATE: options |= RRDR_OPTION_ANOMALY_BIT; - points = 1; - examined_dimensions += rrdset_weights_anomaly_rate(st, results, - after, before, - options, group, group_options, tier, - (int)(timeout - ((now_usec - started_usec) / USEC_PER_MS)), - &stats, register_zero); + rrdset_weights_anomaly_rate( + host, + me->rca, me->ria, me->rma, + results, + after, before, + options, group, group_options, tier, + &stats, register_zero + ); break; case WEIGHTS_METHOD_MC_VOLUME: - points = 1; - examined_dimensions += rrdset_metric_correlations_volume(st, results, - baseline_after, baseline_before, - after, before, - options, group, group_options, tier, - (int)(timeout - ((now_usec - started_usec) / USEC_PER_MS)), - &stats, register_zero); + rrdset_metric_correlations_volume( + host, + me->rca, me->ria, me->rma, + results, + baseline_after, baseline_before, + after, before, + options, group, group_options, tier, + &stats, register_zero + ); break; default: case WEIGHTS_METHOD_MC_KS2: - examined_dimensions += rrdset_metric_correlations_ks2(st, results, - baseline_after, baseline_before, - after, before, - points, options, group, group_options, tier, shifts, - (int)(timeout - ((now_usec - started_usec) / USEC_PER_MS)), - &stats, register_zero); + rrdset_metric_correlations_ks2( + host, + me->rca, me->ria, me->rma, + results, + baseline_after, baseline_before, + after, before, points, + options, group, group_options, tier, shifts, + &stats, register_zero + ); break; } - - rrdset_unlock(st); } - dfe_done(ptr); + dfe_done(me); if(!register_zero) options |= RRDR_OPTION_NONZERO; @@ -1085,22 +968,26 @@ int web_api_v1_weights(RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS size_t added_dimensions = 0; switch(format) { case WEIGHTS_FORMAT_CHARTS: - added_dimensions = registered_results_to_json_charts(results, wb, - after, before, - baseline_after, baseline_before, - points, method, group, options, shifts, - examined_dimensions, - ended_usec - started_usec, &stats); + added_dimensions = + registered_results_to_json_charts( + results, wb, + after, before, + baseline_after, baseline_before, + points, method, group, options, shifts, + examined_dimensions, + ended_usec - started_usec, &stats); break; default: case WEIGHTS_FORMAT_CONTEXTS: - added_dimensions = registered_results_to_json_contexts(results, wb, - after, before, - baseline_after, baseline_before, - points, method, group, options, shifts, - examined_dimensions, - ended_usec - started_usec, &stats); + added_dimensions = + registered_results_to_json_contexts( + results, wb, + after, before, + baseline_after, baseline_before, + points, method, group, options, shifts, + examined_dimensions, + ended_usec - started_usec, &stats); break; } @@ -1110,7 +997,7 @@ int web_api_v1_weights(RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS } cleanup: - if(charts) dictionary_destroy(charts); + if(metrics) dictionary_destroy(metrics); if(results) register_result_destroy(results); if(error) { diff --git a/web/api/queries/weights.h b/web/api/queries/weights.h index f88a134f2..50d8634ef 100644 --- a/web/api/queries/weights.h +++ b/web/api/queries/weights.h @@ -20,14 +20,14 @@ extern int enable_metric_correlations; extern int metric_correlations_version; extern WEIGHTS_METHOD default_metric_correlations_method; -extern int web_api_v1_weights (RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS_FORMAT format, +int web_api_v1_weights (RRDHOST *host, BUFFER *wb, WEIGHTS_METHOD method, WEIGHTS_FORMAT format, RRDR_GROUPING group, const char *group_options, - long long baseline_after, long long baseline_before, - long long after, long long before, - long long points, RRDR_OPTIONS options, SIMPLE_PATTERN *contexts, int tier, int timeout); + time_t baseline_after, time_t baseline_before, + time_t after, time_t before, + size_t points, RRDR_OPTIONS options, SIMPLE_PATTERN *contexts, size_t tier, size_t timeout); -extern WEIGHTS_METHOD weights_string_to_method(const char *method); -extern const char *weights_method_to_string(WEIGHTS_METHOD method); -extern int mc_unittest(void); +WEIGHTS_METHOD weights_string_to_method(const char *method); +const char *weights_method_to_string(WEIGHTS_METHOD method); +int mc_unittest(void); #endif //NETDATA_API_WEIGHTS_H diff --git a/web/api/tests/valid_urls.c b/web/api/tests/valid_urls.c index 91cd19b09..8a2a87f10 100644 --- a/web/api/tests/valid_urls.c +++ b/web/api/tests/valid_urls.c @@ -19,19 +19,6 @@ void *__wrap_free_temporary_host(RRDHOST *host) return NULL; } - -RRDHOST *sql_create_host_by_uuid(char *hostname) -{ - (void) hostname; - return NULL; -} - -RRDHOST *__wrap_sql_create_host_by_uuid(char *hostname) -{ - (void) hostname; - return NULL; -} - void repr(char *result, int result_size, char const *buf, int size) { int n; diff --git a/web/api/tests/web_api.c b/web/api/tests/web_api.c index fd9a86ef6..93e6454ee 100644 --- a/web/api/tests/web_api.c +++ b/web/api/tests/web_api.c @@ -19,18 +19,6 @@ void *__wrap_free_temporary_host(RRDHOST *host) return NULL; } -RRDHOST *sql_create_host_by_uuid(char *hostname) -{ - (void) hostname; - return NULL; -} - -RRDHOST *__wrap_sql_create_host_by_uuid(char *hostname) -{ - (void) hostname; - return NULL; -} - void repr(char *result, int result_size, char const *buf, int size) { int n; diff --git a/web/api/web_api_v1.c b/web/api/web_api_v1.c index 8bfc617fd..93f501f9e 100644 --- a/web/api/web_api_v1.c +++ b/web/api/web_api_v1.c @@ -34,20 +34,20 @@ static struct { , {"match-ids" , 0 , RRDR_OPTION_MATCH_IDS} , {"match_names" , 0 , RRDR_OPTION_MATCH_NAMES} , {"match-names" , 0 , RRDR_OPTION_MATCH_NAMES} - , {"showcustomvars" , 0 , RRDR_OPTION_CUSTOM_VARS} , {"anomaly-bit" , 0 , RRDR_OPTION_ANOMALY_BIT} , {"selected-tier" , 0 , RRDR_OPTION_SELECTED_TIER} , {"raw" , 0 , RRDR_OPTION_RETURN_RAW} , {"jw-anomaly-rates" , 0 , RRDR_OPTION_RETURN_JWAR} , {"natural-points" , 0 , RRDR_OPTION_NATURAL_POINTS} , {"virtual-points" , 0 , RRDR_OPTION_VIRTUAL_POINTS} + , {"all-dimensions" , 0 , RRDR_OPTION_ALL_DIMENSIONS} , {NULL , 0 , 0} }; static struct { const char *name; uint32_t hash; - uint32_t value; + DATASOURCE_FORMAT value; } api_v1_data_formats[] = { { DATASOURCE_FORMAT_DATATABLE_JSON , 0 , DATASOURCE_DATATABLE_JSON} , {DATASOURCE_FORMAT_DATATABLE_JSONP, 0 , DATASOURCE_DATATABLE_JSONP} @@ -68,7 +68,7 @@ static struct { static struct { const char *name; uint32_t hash; - uint32_t value; + DATASOURCE_FORMAT value; } api_v1_data_google_formats[] = { // this is not error - when google requests json, it expects javascript // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source#responseformat @@ -185,20 +185,46 @@ inline RRDR_OPTIONS web_client_api_request_v1_data_options(char *o) { return ret; } -void web_client_api_request_v1_data_options_to_string(BUFFER *wb, RRDR_OPTIONS options) { +void web_client_api_request_v1_data_options_to_buffer(BUFFER *wb, RRDR_OPTIONS options) { RRDR_OPTIONS used = 0; // to prevent adding duplicates int added = 0; for(int i = 0; api_v1_data_options[i].name ; i++) { if (unlikely((api_v1_data_options[i].value & options) && !(api_v1_data_options[i].value & used))) { + const char *name = api_v1_data_options[i].name; + used |= api_v1_data_options[i].value; + if(added) buffer_strcat(wb, ","); - buffer_strcat(wb, api_v1_data_options[i].name); + buffer_strcat(wb, name); + + added++; + } + } +} + +void web_client_api_request_v1_data_options_to_string(char *buf, size_t size, RRDR_OPTIONS options) { + char *write = buf; + char *end = &buf[size - 1]; + + RRDR_OPTIONS used = 0; // to prevent adding duplicates + int added = 0; + for(int i = 0; api_v1_data_options[i].name ; i++) { + if (unlikely((api_v1_data_options[i].value & options) && !(api_v1_data_options[i].value & used))) { + const char *name = api_v1_data_options[i].name; used |= api_v1_data_options[i].value; + + if(added && write < end) + *write++ = ','; + + while(*name && write < end) + *write++ = *name++; + added++; } } + *write = *end = '\0'; } -inline uint32_t web_client_api_request_v1_data_format(char *name) { +inline DATASOURCE_FORMAT web_client_api_request_v1_data_format(char *name) { uint32_t hash = simple_hash(name); int i; @@ -584,16 +610,14 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c char *group_time_str = NULL; char *points_str = NULL; char *timeout_str = NULL; - char *max_anomaly_rates_str = NULL; char *context = NULL; char *chart_label_key = NULL; char *chart_labels_filter = NULL; char *group_options = NULL; - int tier = 0; - int group = RRDR_GROUPING_AVERAGE; - int show_dimensions = 0; - uint32_t format = DATASOURCE_JSON; - uint32_t options = 0x00000000; + size_t tier = 0; + RRDR_GROUPING group = RRDR_GROUPING_AVERAGE; + DATASOURCE_FORMAT format = DATASOURCE_JSON; + RRDR_OPTIONS options = 0; while(url) { char *value = mystrsep(&url, "&"); @@ -617,7 +641,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c buffer_strcat(dimensions, "|"); buffer_strcat(dimensions, value); } - else if(!strcmp(name, "show_dimensions")) show_dimensions = 1; + else if(!strcmp(name, "show_dimensions")) options |= RRDR_OPTION_ALL_DIMENSIONS; else if(!strcmp(name, "after")) after_str = value; else if(!strcmp(name, "before")) before_str = value; else if(!strcmp(name, "points")) points_str = value; @@ -670,13 +694,12 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c outFileName = tqx_value; } } - else if(!strcmp(name, "max_anomaly_rates")) { - max_anomaly_rates_str = value; - } else if(!strcmp(name, "tier")) { - tier = str2i(value); - if(tier >= 0 && tier < storage_tiers) + tier = str2ul(value); + if(tier < storage_tiers) options |= RRDR_OPTION_SELECTED_TIER; + else + tier = 0; } } @@ -690,81 +713,17 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c RRDSET *st = NULL; ONEWAYALLOC *owa = onewayalloc_create(0); + QUERY_TARGET *qt = NULL; - if((!chart || !*chart) && (!context)) { - buffer_sprintf(w->response.data, "No chart id is given at the request."); + if(!is_valid_sp(chart) && !is_valid_sp(context)) { + buffer_sprintf(w->response.data, "No chart or context is given."); goto cleanup; } - struct context_param *context_param_list = NULL; - - if (context && !chart) { - RRDSET *st1; - - uint32_t context_hash = simple_hash(context); - - SIMPLE_PATTERN *chart_label_key_pattern = NULL; - if(chart_label_key) - chart_label_key_pattern = simple_pattern_create(chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); - - SIMPLE_PATTERN *chart_labels_filter_pattern = NULL; - if(chart_labels_filter) - chart_labels_filter_pattern = simple_pattern_create(chart_labels_filter, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); - - rrdhost_rdlock(host); - rrdset_foreach_read(st1, host) { - if (st1->hash_context == context_hash && !strcmp(st1->context, context) && - (!chart_label_key_pattern || rrdlabels_match_simple_pattern_parsed(st1->state->chart_labels, chart_label_key_pattern, ':')) && - (!chart_labels_filter_pattern || rrdlabels_match_simple_pattern_parsed(st1->state->chart_labels, chart_labels_filter_pattern, ':'))) - build_context_param_list(owa, &context_param_list, st1); - } - rrdhost_unlock(host); - - if (likely(context_param_list && context_param_list->rd)) // Just set the first one - st = context_param_list->rd->rrdset; - else { - if (!chart_label_key && !chart_labels_filter) - sql_build_context_param_list(owa, &context_param_list, host, context, NULL); - } - } - else { + if(chart && !context) { + // check if this is a specific chart st = rrdset_find(host, chart); - if (!st) - st = rrdset_find_byname(host, chart); - if (likely(st)) - st->last_accessed_time = now_realtime_sec(); - else - sql_build_context_param_list(owa, &context_param_list, host, NULL, chart); - } - - if (!st) { - if (likely(context_param_list && context_param_list->rd && context_param_list->rd->rrdset)) - st = context_param_list->rd->rrdset; - else { - free_context_param_list(owa, &context_param_list); - context_param_list = NULL; - } - } - - if (!st && !context_param_list) { - if (context && !chart) { - if (!chart_label_key) { - buffer_strcat(w->response.data, "Context is not found: "); - buffer_strcat_htmlescape(w->response.data, context); - } else { - buffer_strcat(w->response.data, "Context: "); - buffer_strcat_htmlescape(w->response.data, context); - buffer_strcat(w->response.data, " or chart label key: "); - buffer_strcat_htmlescape(w->response.data, chart_label_key); - buffer_strcat(w->response.data, " not found"); - } - } - else { - buffer_strcat(w->response.data, "Chart is not found: "); - buffer_strcat_htmlescape(w->response.data, chart); - } - ret = HTTP_RESP_NOT_FOUND; - goto cleanup; + if (!st) st = rrdset_find_byname(host, chart); } long long before = (before_str && *before_str)?str2l(before_str):0; @@ -772,7 +731,35 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c int points = (points_str && *points_str)?str2i(points_str):0; int timeout = (timeout_str && *timeout_str)?str2i(timeout_str): 0; long group_time = (group_time_str && *group_time_str)?str2l(group_time_str):0; - int max_anomaly_rates = (max_anomaly_rates_str && *max_anomaly_rates_str) ? str2i(max_anomaly_rates_str) : 0; + + QUERY_TARGET_REQUEST qtr = { + .after = after, + .before = before, + .host = host, + .st = st, + .hosts = NULL, + .contexts = context, + .charts = chart, + .dimensions = (dimensions)?buffer_tostring(dimensions):NULL, + .timeout = timeout, + .points = points, + .format = format, + .options = options, + .group_method = group, + .group_options = group_options, + .resampling_time = group_time, + .tier = tier, + .chart_label_key = chart_label_key, + .charts_labels_filter = chart_labels_filter, + .query_source = QUERY_SOURCE_API_DATA, + }; + qt = query_target_create(&qtr); + + if(!qt || !qt->query.used) { + buffer_sprintf(w->response.data, "No metrics where matched to query."); + ret = HTTP_RESP_NOT_FOUND; + goto cleanup; + } if (timeout) { struct timeval now; @@ -782,21 +769,13 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c if (timeout <= 0) { buffer_flush(w->response.data); buffer_strcat(w->response.data, "Query timeout exceeded"); - return HTTP_RESP_BACKEND_FETCH_FAILED; + ret = HTTP_RESP_BACKEND_FETCH_FAILED; + goto cleanup; } } - debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'" - , w->id - , chart - , (dimensions)?buffer_tostring(dimensions):"" - , after - , before - , points - , group - , format - , options - ); + debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%u', format '%u', options '0x%08x'" + , w->id, chart, (dimensions)?buffer_tostring(dimensions):"", after, before , points, group, format, options); if(outFileName && *outFileName) { buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName); @@ -827,18 +806,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c buffer_strcat(w->response.data, "("); } - QUERY_PARAMS query_params = { - .context_param_list = context_param_list, - .timeout = timeout, - .max_anomaly_rates = max_anomaly_rates, - .show_dimensions = show_dimensions, - .chart_label_key = chart_label_key, - .wb = w->response.data}; - - ret = rrdset2anything_api_v1(owa, st, &query_params, dimensions, format, - points, after, before, group, group_options, group_time, options, &last_timestamp_in_data, tier); - - free_context_param_list(owa, &context_param_list); + ret = data_query_execute(owa, w->response.data, qt, &last_timestamp_in_data); if(format == DATASOURCE_DATATABLE_JSONP) { if(google_timestamp < last_timestamp_in_data) @@ -856,6 +824,10 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c buffer_strcat(w->response.data, ");"); cleanup: + if(qt && qt->used) { + internal_error(true, "QUERY_TARGET: left non-released on query '%s'", qt->id); + query_target_release(qt); + } onewayalloc_destroy(owa); buffer_free(dimensions); return ret; @@ -1054,8 +1026,7 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client * static inline void web_client_api_request_v1_info_summary_alarm_statuses(RRDHOST *host, BUFFER *wb) { int alarm_normal = 0, alarm_warn = 0, alarm_crit = 0; RRDCALC *rc; - rrdhost_rdlock(host); - for(rc = host->alarms; rc ; rc = rc->next) { + foreach_rrdcalc_in_rrdhost_read(host, rc) { if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) continue; @@ -1070,7 +1041,7 @@ static inline void web_client_api_request_v1_info_summary_alarm_statuses(RRDHOST alarm_normal++; } } - rrdhost_unlock(host); + foreach_rrdcalc_in_rrdhost_done(rc); buffer_sprintf(wb, "\t\t\"normal\": %d,\n", alarm_normal); buffer_sprintf(wb, "\t\t\"warning\": %d,\n", alarm_warn); buffer_sprintf(wb, "\t\t\"critical\": %d\n", alarm_crit); @@ -1086,7 +1057,7 @@ static inline void web_client_api_request_v1_info_mirrored_hosts(BUFFER *wb) { if (count > 0) buffer_strcat(wb, ",\n"); - buffer_sprintf(wb, "\t\t\"%s\"", host->hostname); + buffer_sprintf(wb, "\t\t\"%s\"", rrdhost_hostname(host)); count++; } @@ -1101,7 +1072,7 @@ static inline void web_client_api_request_v1_info_mirrored_hosts(BUFFER *wb) { buffer_sprintf( wb, "\t\t{ \"guid\": \"%s\", \"hostname\": \"%s\", \"reachable\": %s, \"hops\": %d" , host->machine_guid - , host->hostname + , rrdhost_hostname(host) , (host->receiver || host == localhost) ? "true" : "false" , host->system_info ? host->system_info->hops : (host == localhost) ? 0 : 1 ); @@ -1140,7 +1111,7 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) { indentation--; } - rrdlabels_to_buffer(host->host_labels, wb, tabs, ":", "\"", ",\n", NULL, NULL, NULL, NULL); + rrdlabels_to_buffer(host->rrdlabels, wb, tabs, ":", "\"", ",\n", NULL, NULL, NULL, NULL); buffer_strcat(wb, "\n"); } @@ -1148,7 +1119,7 @@ extern int aclk_connected; inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb) { buffer_strcat(wb, "{\n"); - buffer_sprintf(wb, "\t\"version\": \"%s\",\n", host->program_version); + buffer_sprintf(wb, "\t\"version\": \"%s\",\n", rrdhost_program_version(host)); buffer_sprintf(wb, "\t\"uid\": \"%s\",\n", host->machine_guid); web_client_api_request_v1_info_mirrored_hosts(wb); @@ -1202,6 +1173,10 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb) host_labels2json(host, wb, 2); buffer_strcat(wb, "\t},\n"); + buffer_strcat(wb, "\t\"functions\": {\n"); + host_functions2json(host, wb, 2, "\"", "\""); + buffer_strcat(wb, "\t},\n"); + buffer_strcat(wb, "\t\"collectors\": ["); chartcollectors2json(host, wb); buffer_strcat(wb, "\n\t],\n"); @@ -1215,14 +1190,8 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb) #ifdef ENABLE_ACLK buffer_strcat(wb, "\t\"cloud-available\": true,\n"); - buffer_strcat(wb, "\t\"aclk-ng-available\": true,\n"); - buffer_strcat(wb, "\t\"aclk-ng-new-cloud-protocol\": true,\n"); - buffer_strcat(wb, "\t\"aclk-legacy-available\": false,\n"); - buffer_strcat(wb, "\t\"aclk-implementation\": \"Next Generation\",\n"); #else buffer_strcat(wb, "\t\"cloud-available\": false,\n"); - buffer_strcat(wb, "\t\"aclk-ng-available\": false,\n"); - buffer_strcat(wb, "\t\"aclk-legacy-available\": false,\n"); #endif char *agent_id = get_agent_claimid(); if (agent_id == NULL) @@ -1234,11 +1203,10 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb) #ifdef ENABLE_ACLK if (aclk_connected) { buffer_strcat(wb, "\t\"aclk-available\": true,\n"); - buffer_strcat(wb, "\t\"aclk-available-protocol\": \"New\",\n"); } else #endif - buffer_strcat(wb, "\t\"aclk-available\": false,\n\t\"aclk-available-protocol\": null,\n"); // Intentionally valid with/without #ifdef above + buffer_strcat(wb, "\t\"aclk-available\": false,\n"); // Intentionally valid with/without #ifdef above buffer_strcat(wb, "\t\"memory-mode\": "); analytics_get_data(analytics_data.netdata_config_memory_mode, wb); @@ -1259,7 +1227,7 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb) #ifdef ENABLE_COMPRESSION if(host->sender){ buffer_strcat(wb, "\t\"stream-compression\": "); - buffer_strcat(wb, (host->sender->rrdpush_compression ? "true" : "false")); + buffer_strcat(wb, stream_has_capability(host->sender, STREAM_CAP_COMPRESSION) ? "true" : "false"); buffer_strcat(wb, ",\n"); }else{ buffer_strcat(wb, "\t\"stream-compression\": null,\n"); @@ -1330,7 +1298,7 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb) buffer_strcat(wb, "\t\"ml-info\": "); buffer_strcat(wb, ml_info); - free(ml_info); + freez(ml_info); #endif buffer_strcat(wb, "\n}"); @@ -1338,81 +1306,15 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb) } #if defined(ENABLE_ML) -int web_client_api_request_v1_anomaly_events(RRDHOST *host, struct web_client *w, char *url) { - if (!netdata_ready) - return HTTP_RESP_BACKEND_FETCH_FAILED; - - uint32_t after = 0, before = 0; - - while (url) { - char *value = mystrsep(&url, "&"); - if (!value || !*value) - continue; - - char *name = mystrsep(&value, "="); - if (!name || !*name) - continue; - if (!value || !*value) - continue; - - if (!strcmp(name, "after")) - after = (uint32_t) (strtoul(value, NULL, 0) / 1000); - else if (!strcmp(name, "before")) - before = (uint32_t) (strtoul(value, NULL, 0) / 1000); - } - - char *s; - if (!before || !after) - s = strdupz("{\"error\": \"missing after/before parameters\" }\n"); - else { - s = ml_get_anomaly_events(host, "AD1", 1, after, before); - if (!s) - s = strdupz("{\"error\": \"json string is empty\" }\n"); - } - - BUFFER *wb = w->response.data; - buffer_flush(wb); - - wb->contenttype = CT_APPLICATION_JSON; - buffer_strcat(wb, s); - buffer_no_cacheable(wb); - - freez(s); - - return HTTP_RESP_OK; -} +int web_client_api_request_v1_ml_info(RRDHOST *host, struct web_client *w, char *url) { + (void) url; -int web_client_api_request_v1_anomaly_event_info(RRDHOST *host, struct web_client *w, char *url) { if (!netdata_ready) return HTTP_RESP_BACKEND_FETCH_FAILED; - uint32_t after = 0, before = 0; - - while (url) { - char *value = mystrsep(&url, "&"); - if (!value || !*value) - continue; - - char *name = mystrsep(&value, "="); - if (!name || !*name) - continue; - if (!value || !*value) - continue; - - if (!strcmp(name, "after")) - after = (uint32_t) strtoul(value, NULL, 0); - else if (!strcmp(name, "before")) - before = (uint32_t) strtoul(value, NULL, 0); - } - - char *s; - if (!before || !after) - s = strdupz("{\"error\": \"missing after/before parameters\" }\n"); - else { - s = ml_get_anomaly_event_info(host, "AD1", 1, after, before); - if (!s) - s = strdupz("{\"error\": \"json string is empty\" }\n"); - } + char *s = ml_get_host_runtime_info(host); + if (!s) + s = strdupz("{\"error\": \"json string is empty\" }\n"); BUFFER *wb = w->response.data; buffer_flush(wb); @@ -1424,13 +1326,13 @@ int web_client_api_request_v1_anomaly_event_info(RRDHOST *host, struct web_clien return HTTP_RESP_OK; } -int web_client_api_request_v1_ml_info(RRDHOST *host, struct web_client *w, char *url) { +int web_client_api_request_v1_ml_models(RRDHOST *host, struct web_client *w, char *url) { (void) url; if (!netdata_ready) return HTTP_RESP_BACKEND_FETCH_FAILED; - char *s = ml_get_host_runtime_info(host); + char *s = ml_get_host_models(host); if (!s) s = strdupz("{\"error\": \"json string is empty\" }\n"); @@ -1443,8 +1345,7 @@ int web_client_api_request_v1_ml_info(RRDHOST *host, struct web_client *w, char freez(s); return HTTP_RESP_OK; } - -#endif // defined(ENABLE_ML) +#endif inline int web_client_api_request_v1_info(RRDHOST *host, struct web_client *w, char *url) { (void)url; @@ -1485,7 +1386,7 @@ static int web_client_api_request_v1_weights_internal(RRDHOST *host, struct web_ int options_count = 0; RRDR_GROUPING group = RRDR_GROUPING_AVERAGE; int timeout = 0; - int tier = 0; + size_t tier = 0; const char *group_options = NULL, *contexts_str = NULL; while (url) { @@ -1533,9 +1434,11 @@ static int web_client_api_request_v1_weights_internal(RRDHOST *host, struct web_ contexts_str = value; else if(!strcmp(name, "tier")) { - tier = str2i(value); - if(tier >= 0 && tier < storage_tiers) + tier = str2ul(value); + if(tier < storage_tiers) options |= RRDR_OPTION_SELECTED_TIER; + else + tier = 0; } } @@ -1559,12 +1462,59 @@ int web_client_api_request_v1_weights(RRDHOST *host, struct web_client *w, char return web_client_api_request_v1_weights_internal(host, w, url, WEIGHTS_METHOD_ANOMALY_RATE, WEIGHTS_FORMAT_CONTEXTS); } +int web_client_api_request_v1_function(RRDHOST *host, struct web_client *w, char *url) { + if (!netdata_ready) + return HTTP_RESP_BACKEND_FETCH_FAILED; + + int timeout = 0; + const char *function = NULL; + + while (url) { + char *value = mystrsep(&url, "&"); + if (!value || !*value) + continue; + + char *name = mystrsep(&value, "="); + if (!name || !*name) + continue; + + if (!strcmp(name, "function")) + function = value; + + else if (!strcmp(name, "timeout")) + timeout = (int) strtoul(value, NULL, 0); + } + + BUFFER *wb = w->response.data; + buffer_flush(wb); + wb->contenttype = CT_APPLICATION_JSON; + buffer_no_cacheable(wb); + + return rrd_call_function_and_wait(host, wb, timeout, function); +} + +int web_client_api_request_v1_functions(RRDHOST *host, struct web_client *w, char *url __maybe_unused) { + if (!netdata_ready) + return HTTP_RESP_BACKEND_FETCH_FAILED; + + BUFFER *wb = w->response.data; + buffer_flush(wb); + wb->contenttype = CT_APPLICATION_JSON; + buffer_no_cacheable(wb); + + buffer_strcat(wb, "{\n"); + host_functions2json(host, wb, 1, "\"", "\""); + buffer_strcat(wb, "}"); + + return HTTP_RESP_OK; +} + #ifndef ENABLE_DBENGINE int web_client_api_request_v1_dbengine_stats(RRDHOST *host, struct web_client *w, char *url) { return HTTP_RESP_NOT_FOUND; } #else -static void web_client_api_v1_dbengine_stats_for_tier(BUFFER *wb, int tier) { +static void web_client_api_v1_dbengine_stats_for_tier(BUFFER *wb, size_t tier) { RRDENG_SIZE_STATS stats = rrdeng_size_statistics(multidb_ctx[tier]); buffer_sprintf(wb, @@ -1588,11 +1538,11 @@ static void web_client_api_v1_dbengine_stats_for_tier(BUFFER *wb, int tier) { ",\n\t\t\"metrics_pages\":%zu" ",\n\t\t\"extents_compressed_bytes\":%zu" ",\n\t\t\"pages_uncompressed_bytes\":%zu" - ",\n\t\t\"pages_duration_secs\":%ld" + ",\n\t\t\"pages_duration_secs\":%lld" ",\n\t\t\"single_point_pages\":%zu" ",\n\t\t\"first_t\":%llu" ",\n\t\t\"last_t\":%llu" - ",\n\t\t\"database_retention_secs\":%ld" + ",\n\t\t\"database_retention_secs\":%lld" ",\n\t\t\"average_compression_savings\":%0.2f" ",\n\t\t\"average_point_duration_secs\":%0.2f" ",\n\t\t\"average_metric_retention_secs\":%0.2f" @@ -1623,11 +1573,11 @@ static void web_client_api_v1_dbengine_stats_for_tier(BUFFER *wb, int tier) { , stats.metrics_pages , stats.extents_compressed_bytes , stats.pages_uncompressed_bytes - , stats.pages_duration_secs + , (long long)stats.pages_duration_secs , stats.single_point_pages , stats.first_t , stats.last_t - , stats.database_retention_secs + , (long long)stats.database_retention_secs , stats.average_compression_savings , stats.average_point_duration_secs , stats.average_metric_retention_secs @@ -1646,12 +1596,17 @@ int web_client_api_request_v1_dbengine_stats(RRDHOST *host __maybe_unused, struc BUFFER *wb = w->response.data; buffer_flush(wb); + + if(!dbengine_enabled) { + buffer_strcat(wb, "dbengine is not enabled"); + return HTTP_RESP_NOT_FOUND; + } + wb->contenttype = CT_APPLICATION_JSON; buffer_no_cacheable(wb); - buffer_strcat(wb, "{"); - for(int tier = 0; tier < storage_tiers ;tier++) { - buffer_sprintf(wb, "%s\n\t\"tier%d\": {", tier?",":"", tier); + for(size_t tier = 0; tier < storage_tiers ;tier++) { + buffer_sprintf(wb, "%s\n\t\"tier%zu\": {", tier?",":"", tier); web_client_api_v1_dbengine_stats_for_tier(wb, tier); buffer_strcat(wb, "\n\t}"); } @@ -1661,48 +1616,56 @@ int web_client_api_request_v1_dbengine_stats(RRDHOST *host __maybe_unused, struc } #endif +#ifdef NETDATA_DEV_MODE +#define ACL_DEV_OPEN_ACCESS WEB_CLIENT_ACL_DASHBOARD +#else +#define ACL_DEV_OPEN_ACCESS 0 +#endif + static struct api_command { const char *command; uint32_t hash; WEB_CLIENT_ACL acl; int (*callback)(RRDHOST *host, struct web_client *w, char *url); } api_commands[] = { - { "info", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_info }, - { "data", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_data }, - { "chart", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_chart }, - { "charts", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_charts }, - { "context", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_context }, - { "contexts", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_contexts }, - { "archivedcharts", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_archivedcharts }, + { "info", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_info }, + { "data", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_data }, + { "chart", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_chart }, + { "charts", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_charts }, + { "context", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_context }, + { "contexts", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_contexts }, + { "archivedcharts", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_archivedcharts }, // registry checks the ACL by itself, so we allow everything - { "registry", 0, WEB_CLIENT_ACL_NOCHECK, web_client_api_request_v1_registry }, + { "registry", 0, WEB_CLIENT_ACL_NOCHECK, web_client_api_request_v1_registry }, // badges can be fetched with both dashboard and badge permissions - { "badge.svg", 0, WEB_CLIENT_ACL_DASHBOARD|WEB_CLIENT_ACL_BADGE, web_client_api_request_v1_badge }, + { "badge.svg", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_badge }, - { "alarms", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarms }, - { "alarms_values", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarms_values }, - { "alarm_log", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarm_log }, - { "alarm_variables", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarm_variables }, - { "alarm_count", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarm_count }, - { "allmetrics", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_allmetrics }, + { "alarms", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarms }, + { "alarms_values", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarms_values }, + { "alarm_log", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarm_log }, + { "alarm_variables", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarm_variables }, + { "alarm_count", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarm_count }, + { "allmetrics", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_allmetrics }, #if defined(ENABLE_ML) - { "anomaly_events", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_anomaly_events }, - { "anomaly_event_info", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_anomaly_event_info }, - { "ml_info", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_ml_info }, + { "ml_info", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_ml_info }, + { "ml_models", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_ml_models }, #endif - { "manage/health", 0, WEB_CLIENT_ACL_MGMT, web_client_api_request_v1_mgmt_health }, - { "aclk", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_aclk_state }, - { "metric_correlations", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_metric_correlations }, - { "weights", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_weights }, + { "manage/health", 0, WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_mgmt_health }, + { "aclk", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_aclk_state }, + { "metric_correlations", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_metric_correlations }, + { "weights", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_weights }, + + { "function", 0, WEB_CLIENT_ACL_ACLK | ACL_DEV_OPEN_ACCESS, web_client_api_request_v1_function }, + { "functions", 0, WEB_CLIENT_ACL_ACLK | ACL_DEV_OPEN_ACCESS, web_client_api_request_v1_functions }, - { "dbengine_stats", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_dbengine_stats }, + { "dbengine_stats", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_dbengine_stats }, // terminator - { NULL, 0, WEB_CLIENT_ACL_NONE, NULL }, + { NULL, 0, WEB_CLIENT_ACL_NONE, NULL }, }; inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) { diff --git a/web/api/web_api_v1.h b/web/api/web_api_v1.h index 544f1e574..e6682c99c 100644 --- a/web/api/web_api_v1.h +++ b/web/api/web_api_v1.h @@ -10,30 +10,31 @@ #include "web/api/queries/weights.h" #define MAX_CHART_LABELS_FILTER (32) -extern RRDR_OPTIONS web_client_api_request_v1_data_options(char *o); -extern void web_client_api_request_v1_data_options_to_string(BUFFER *wb, RRDR_OPTIONS options); - -extern uint32_t web_client_api_request_v1_data_format(char *name); -extern uint32_t web_client_api_request_v1_data_google_format(char *name); - -extern int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_alarms_values(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)); -extern int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_alarm_count(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_archivedcharts(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_info(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url); -extern int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb); -extern void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation); - -extern void web_client_api_v1_init(void); -extern void web_client_api_v1_management_init(void); +RRDR_OPTIONS web_client_api_request_v1_data_options(char *o); +void web_client_api_request_v1_data_options_to_buffer(BUFFER *wb, RRDR_OPTIONS options); +void web_client_api_request_v1_data_options_to_string(char *buf, size_t size, RRDR_OPTIONS options); + +uint32_t web_client_api_request_v1_data_format(char *name); +uint32_t web_client_api_request_v1_data_google_format(char *name); + +int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_alarms_values(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)); +int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_alarm_count(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_archivedcharts(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_info(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url); +int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb); +void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation); + +void web_client_api_v1_init(void); +void web_client_api_v1_management_init(void); extern char *api_secret; diff --git a/web/gui/Makefile.am b/web/gui/Makefile.am index 938d56836..f00744d64 100644 --- a/web/gui/Makefile.am +++ b/web/gui/Makefile.am @@ -13,66 +13,66 @@ SUBDIRS = \ $(NULL) DASHBOARD_JS_FILES = \ - src/dashboard.js/prologue.js.inc \ - src/dashboard.js/utils.js \ - src/dashboard.js/server-detection.js \ - src/dashboard.js/dependencies.js \ - src/dashboard.js/error-handling.js \ - src/dashboard.js/compatibility.js \ - src/dashboard.js/xss.js \ - src/dashboard.js/colors.js \ - src/dashboard.js/units-conversion.js \ - src/dashboard.js/options.js \ - src/dashboard.js/localstorage.js \ - src/dashboard.js/timeout.js \ - src/dashboard.js/themes.js \ - src/dashboard.js/charting/dygraph.js \ - src/dashboard.js/charting/sparkline.js \ - src/dashboard.js/charting/google-charts.js \ - src/dashboard.js/charting/gauge.js \ - src/dashboard.js/charting/easy-pie-chart.js \ - src/dashboard.js/charting/d3pie.js \ - src/dashboard.js/charting/d3.js \ - src/dashboard.js/charting/peity.js \ - src/dashboard.js/charting/textonly.js \ - src/dashboard.js/charting.js \ - src/dashboard.js/chart-registry.js \ - src/dashboard.js/common.js \ - src/dashboard.js/main.js \ - src/dashboard.js/alarms.js \ - src/dashboard.js/registry.js \ - src/dashboard.js/boot.js \ - src/dashboard.js/epilogue.js.inc \ + $(srcdir)/src/dashboard.js/prologue.js.inc \ + $(srcdir)/src/dashboard.js/utils.js \ + $(srcdir)/src/dashboard.js/server-detection.js \ + $(srcdir)/src/dashboard.js/dependencies.js \ + $(srcdir)/src/dashboard.js/error-handling.js \ + $(srcdir)/src/dashboard.js/compatibility.js \ + $(srcdir)/src/dashboard.js/xss.js \ + $(srcdir)/src/dashboard.js/colors.js \ + $(srcdir)/src/dashboard.js/units-conversion.js \ + $(srcdir)/src/dashboard.js/options.js \ + $(srcdir)/src/dashboard.js/localstorage.js \ + $(srcdir)/src/dashboard.js/timeout.js \ + $(srcdir)/src/dashboard.js/themes.js \ + $(srcdir)/src/dashboard.js/charting/dygraph.js \ + $(srcdir)/src/dashboard.js/charting/sparkline.js \ + $(srcdir)/src/dashboard.js/charting/google-charts.js \ + $(srcdir)/src/dashboard.js/charting/gauge.js \ + $(srcdir)/src/dashboard.js/charting/easy-pie-chart.js \ + $(srcdir)/src/dashboard.js/charting/d3pie.js \ + $(srcdir)/src/dashboard.js/charting/d3.js \ + $(srcdir)/src/dashboard.js/charting/peity.js \ + $(srcdir)/src/dashboard.js/charting/textonly.js \ + $(srcdir)/src/dashboard.js/charting.js \ + $(srcdir)/src/dashboard.js/chart-registry.js \ + $(srcdir)/src/dashboard.js/common.js \ + $(srcdir)/src/dashboard.js/main.js \ + $(srcdir)/src/dashboard.js/alarms.js \ + $(srcdir)/src/dashboard.js/registry.js \ + $(srcdir)/src/dashboard.js/boot.js \ + $(srcdir)/src/dashboard.js/epilogue.js.inc \ $(NULL) dist_noinst_DATA = \ - README.md \ + $(srcdir)/README.md \ $(DASHBOARD_JS_FILES) \ $(NULL) dist_web_DATA = \ dashboard.js \ - dashboard_info.js \ - dashboard_info_custom_example.js \ - main.css \ - main.js \ + $(srcdir)/dashboard_info.js \ + $(srcdir)/dashboard_info_custom_example.js \ + $(srcdir)/main.css \ + $(srcdir)/main.js \ version.txt \ $(NULL) webolddir=$(webdir)/old dist_webold_DATA = \ - old/index.html \ + $(srcdir)/old/index.html \ $(NULL) webstaticdir=$(webdir)/static/img dist_webstatic_DATA = \ - static/img/netdata-logomark.svg \ + $(srcdir)/static/img/netdata-logomark.svg \ $(NULL) webcssdir=$(webdir)/css dist_webcss_DATA = \ - css/morris-0.5.1.css \ - css/c3-0.4.18.min.css \ + $(srcdir)/css/morris-0.5.1.css \ + $(srcdir)/css/c3-0.4.18.min.css \ $(NULL) dashboard.js: $(DASHBOARD_JS_FILES) @@ -85,7 +85,7 @@ dist_webwellknown_DATA = \ webdntdir=$(webdir)/.well-known/dnt dist_webdnt_DATA = \ - .well-known/dnt/cookies \ + $(srcdir)/.well-known/dnt/cookies \ $(NULL) version.txt: diff --git a/web/gui/bundle_dashboard.py b/web/gui/bundle_dashboard.py index e1815b1e2..3b685be72 100755 --- a/web/gui/bundle_dashboard.py +++ b/web/gui/bundle_dashboard.py @@ -20,13 +20,13 @@ BASEPATH = Path('dashboard') URLTEMPLATE = 'https://github.com/netdata/dashboard/releases/download/{0}/dashboard.tar.gz' MAKEFILETEMPLATE = ''' -# Auto-generated by generate-dashboard-makefile.py +# Auto-generated by bundle_dashboard.py # Copyright: © 2021 Netdata Inc. # SPDX-License-Identifier: GPL-3.0-or-later MAINTAINERCLEANFILES = $(srcdir)/Makefile.in dist_noinst_DATA = \\ - README.md + $(srcdir)/README.md dist_web_DATA = \\ {0} \\ @@ -91,7 +91,7 @@ def genfilelist(path): files = [f for f in path.iterdir() if f.is_file() and f.name != 'README.md'] files = [Path(*f.parts[1:]) for f in files] files.sort() - return ' \\\n '.join([str(f) for f in files]) + return ' \\\n '.join([("$(srcdir)/" + str(f)) for f in files]) def write_makefile(): diff --git a/web/gui/dashboard/Makefile.am b/web/gui/dashboard/Makefile.am index 619bdaf91..f939a7ead 100644 --- a/web/gui/dashboard/Makefile.am +++ b/web/gui/dashboard/Makefile.am @@ -1,206 +1,206 @@ -# Auto-generated by generate-dashboard-makefile.py +# Auto-generated by bundle_dashboard.py # Copyright: © 2021 Netdata Inc. # SPDX-License-Identifier: GPL-3.0-or-later MAINTAINERCLEANFILES = $(srcdir)/Makefile.in dist_noinst_DATA = \ - README.md + $(srcdir)/README.md dist_web_DATA = \ - asset-manifest.json \ - console.html \ - dash-example.html \ - dashboard-react.js \ - dashboard.css \ - dashboard.html \ - dashboard.js \ - dashboard.slate.css \ - demo.html \ - demo2.html \ - demosites.html \ - demosites2.html \ - favicon.ico \ - goto-host-from-alarm.html \ - index-node-view.html \ - index.html \ - infographic.html \ - manifest.json \ - precache-manifest.e95d658eed560f9e0189217cc6919238.js \ - refresh-badges.js \ - robots.txt \ - service-worker.js \ - sitemap.xml \ - tv-react.html \ - tv.html \ + $(srcdir)/asset-manifest.json \ + $(srcdir)/console.html \ + $(srcdir)/dash-example.html \ + $(srcdir)/dashboard-react.js \ + $(srcdir)/dashboard.css \ + $(srcdir)/dashboard.html \ + $(srcdir)/dashboard.js \ + $(srcdir)/dashboard.slate.css \ + $(srcdir)/demo.html \ + $(srcdir)/demo2.html \ + $(srcdir)/demosites.html \ + $(srcdir)/demosites2.html \ + $(srcdir)/favicon.ico \ + $(srcdir)/goto-host-from-alarm.html \ + $(srcdir)/index-node-view.html \ + $(srcdir)/index.html \ + $(srcdir)/infographic.html \ + $(srcdir)/manifest.json \ + $(srcdir)/precache-manifest.5fec6109084644adf7bf854243e1a044.js \ + $(srcdir)/refresh-badges.js \ + $(srcdir)/robots.txt \ + $(srcdir)/service-worker.js \ + $(srcdir)/sitemap.xml \ + $(srcdir)/tv-react.html \ + $(srcdir)/tv.html \ $(NULL) webcssdir=$(webdir)/css dist_webcss_DATA = \ - css/bootstrap-3.3.7.css \ - css/bootstrap-slate-flat-3.3.7.css \ - css/bootstrap-slider-10.0.0.min.css \ - css/bootstrap-theme-3.3.7.min.css \ - css/bootstrap-toggle-2.2.2.min.css \ - css/dashboard.css \ - css/dashboard.slate.css \ + $(srcdir)/css/bootstrap-3.3.7.css \ + $(srcdir)/css/bootstrap-slate-flat-3.3.7.css \ + $(srcdir)/css/bootstrap-slider-10.0.0.min.css \ + $(srcdir)/css/bootstrap-theme-3.3.7.min.css \ + $(srcdir)/css/bootstrap-toggle-2.2.2.min.css \ + $(srcdir)/css/dashboard.css \ + $(srcdir)/css/dashboard.slate.css \ $(NULL) webfontsdir=$(webdir)/fonts dist_webfonts_DATA = \ - fonts/glyphicons-halflings-regular.eot \ - fonts/glyphicons-halflings-regular.svg \ - fonts/glyphicons-halflings-regular.ttf \ - fonts/glyphicons-halflings-regular.woff \ - fonts/glyphicons-halflings-regular.woff2 \ + $(srcdir)/fonts/glyphicons-halflings-regular.eot \ + $(srcdir)/fonts/glyphicons-halflings-regular.svg \ + $(srcdir)/fonts/glyphicons-halflings-regular.ttf \ + $(srcdir)/fonts/glyphicons-halflings-regular.woff \ + $(srcdir)/fonts/glyphicons-halflings-regular.woff2 \ $(NULL) webimagesdir=$(webdir)/images dist_webimages_DATA = \ - images/alert-128-orange.png \ - images/alert-128-red.png \ - images/alert-multi-size-orange.ico \ - images/alert-multi-size-red.ico \ - images/alerts.jpg \ - images/alerts.png \ - images/android-icon-144x144.png \ - images/android-icon-192x192.png \ - images/android-icon-36x36.png \ - images/android-icon-48x48.png \ - images/android-icon-72x72.png \ - images/android-icon-96x96.png \ - images/animated.gif \ - images/apple-icon-114x114.png \ - images/apple-icon-120x120.png \ - images/apple-icon-144x144.png \ - images/apple-icon-152x152.png \ - images/apple-icon-180x180.png \ - images/apple-icon-57x57.png \ - images/apple-icon-60x60.png \ - images/apple-icon-72x72.png \ - images/apple-icon-76x76.png \ - images/apple-icon-precomposed.png \ - images/apple-icon.png \ - images/banner-icon-144x144.png \ - images/check-mark-2-128-green.png \ - images/check-mark-2-multi-size-green.ico \ - images/dashboards.png \ - images/favicon-128.png \ - images/favicon-16x16.png \ - images/favicon-196x196.png \ - images/favicon-32x32.png \ - images/favicon-96x96.png \ - images/favicon.ico \ - images/home.png \ - images/ms-icon-144x144.png \ - images/ms-icon-150x150.png \ - images/ms-icon-310x150.png \ - images/ms-icon-310x310.png \ - images/ms-icon-36x36.png \ - images/ms-icon-70x70.png \ - images/netdata-logomark.svg \ - images/netdata.svg \ - images/nodeView.png \ - images/nodes.jpg \ - images/overview.png \ - images/packaging-beta-tag.svg \ - images/post.png \ - images/pricing.png \ - images/seo-performance-128.png \ + $(srcdir)/images/alert-128-orange.png \ + $(srcdir)/images/alert-128-red.png \ + $(srcdir)/images/alert-multi-size-orange.ico \ + $(srcdir)/images/alert-multi-size-red.ico \ + $(srcdir)/images/alerts.jpg \ + $(srcdir)/images/alerts.png \ + $(srcdir)/images/android-icon-144x144.png \ + $(srcdir)/images/android-icon-192x192.png \ + $(srcdir)/images/android-icon-36x36.png \ + $(srcdir)/images/android-icon-48x48.png \ + $(srcdir)/images/android-icon-72x72.png \ + $(srcdir)/images/android-icon-96x96.png \ + $(srcdir)/images/animated.gif \ + $(srcdir)/images/apple-icon-114x114.png \ + $(srcdir)/images/apple-icon-120x120.png \ + $(srcdir)/images/apple-icon-144x144.png \ + $(srcdir)/images/apple-icon-152x152.png \ + $(srcdir)/images/apple-icon-180x180.png \ + $(srcdir)/images/apple-icon-57x57.png \ + $(srcdir)/images/apple-icon-60x60.png \ + $(srcdir)/images/apple-icon-72x72.png \ + $(srcdir)/images/apple-icon-76x76.png \ + $(srcdir)/images/apple-icon-precomposed.png \ + $(srcdir)/images/apple-icon.png \ + $(srcdir)/images/banner-icon-144x144.png \ + $(srcdir)/images/check-mark-2-128-green.png \ + $(srcdir)/images/check-mark-2-multi-size-green.ico \ + $(srcdir)/images/dashboards.png \ + $(srcdir)/images/favicon-128.png \ + $(srcdir)/images/favicon-16x16.png \ + $(srcdir)/images/favicon-196x196.png \ + $(srcdir)/images/favicon-32x32.png \ + $(srcdir)/images/favicon-96x96.png \ + $(srcdir)/images/favicon.ico \ + $(srcdir)/images/home.png \ + $(srcdir)/images/ms-icon-144x144.png \ + $(srcdir)/images/ms-icon-150x150.png \ + $(srcdir)/images/ms-icon-310x150.png \ + $(srcdir)/images/ms-icon-310x310.png \ + $(srcdir)/images/ms-icon-36x36.png \ + $(srcdir)/images/ms-icon-70x70.png \ + $(srcdir)/images/netdata-logomark.svg \ + $(srcdir)/images/netdata.svg \ + $(srcdir)/images/nodeView.png \ + $(srcdir)/images/nodes.jpg \ + $(srcdir)/images/overview.png \ + $(srcdir)/images/packaging-beta-tag.svg \ + $(srcdir)/images/post.png \ + $(srcdir)/images/pricing.png \ + $(srcdir)/images/seo-performance-128.png \ $(NULL) weblibdir=$(webdir)/lib dist_weblib_DATA = \ - lib/bootstrap-3.3.7.min.js \ - lib/bootstrap-slider-10.0.0.min.js \ - lib/bootstrap-table-1.11.0.min.js \ - lib/bootstrap-table-export-1.11.0.min.js \ - lib/bootstrap-toggle-2.2.2.min.js \ - lib/clipboard-polyfill-be05dad.js \ - lib/d3-4.12.2.min.js \ - lib/d3pie-0.2.1-netdata-3.js \ - lib/dygraph-c91c859.min.js \ - lib/dygraph-smooth-plotter-c91c859.js \ - lib/fontawesome-all-5.0.1.min.js \ - lib/gauge-1.3.2.min.js \ - lib/jquery-3.6.0.min.js \ - lib/jquery.easypiechart-97b5824.min.js \ - lib/jquery.peity-3.2.0.min.js \ - lib/jquery.sparkline-2.1.2.min.js \ - lib/lz-string-1.4.4.min.js \ - lib/pako-1.0.6.min.js \ - lib/perfect-scrollbar-0.6.15.min.js \ - lib/tableExport-1.6.0.min.js \ + $(srcdir)/lib/bootstrap-3.3.7.min.js \ + $(srcdir)/lib/bootstrap-slider-10.0.0.min.js \ + $(srcdir)/lib/bootstrap-table-1.11.0.min.js \ + $(srcdir)/lib/bootstrap-table-export-1.11.0.min.js \ + $(srcdir)/lib/bootstrap-toggle-2.2.2.min.js \ + $(srcdir)/lib/clipboard-polyfill-be05dad.js \ + $(srcdir)/lib/d3-4.12.2.min.js \ + $(srcdir)/lib/d3pie-0.2.1-netdata-3.js \ + $(srcdir)/lib/dygraph-c91c859.min.js \ + $(srcdir)/lib/dygraph-smooth-plotter-c91c859.js \ + $(srcdir)/lib/fontawesome-all-5.0.1.min.js \ + $(srcdir)/lib/gauge-1.3.2.min.js \ + $(srcdir)/lib/jquery-3.6.0.min.js \ + $(srcdir)/lib/jquery.easypiechart-97b5824.min.js \ + $(srcdir)/lib/jquery.peity-3.2.0.min.js \ + $(srcdir)/lib/jquery.sparkline-2.1.2.min.js \ + $(srcdir)/lib/lz-string-1.4.4.min.js \ + $(srcdir)/lib/pako-1.0.6.min.js \ + $(srcdir)/lib/perfect-scrollbar-0.6.15.min.js \ + $(srcdir)/lib/tableExport-1.6.0.min.js \ $(NULL) webstaticcssdir=$(webdir)/static/css dist_webstaticcss_DATA = \ - static/css/2.20fd0a40.chunk.css \ - static/css/2.20fd0a40.chunk.css.map \ - static/css/4.a36e3b73.chunk.css \ - static/css/4.a36e3b73.chunk.css.map \ - static/css/main.53ba10f1.chunk.css \ - static/css/main.53ba10f1.chunk.css.map \ + $(srcdir)/static/css/2.c454aab8.chunk.css \ + $(srcdir)/static/css/2.c454aab8.chunk.css.map \ + $(srcdir)/static/css/4.a36e3b73.chunk.css \ + $(srcdir)/static/css/4.a36e3b73.chunk.css.map \ + $(srcdir)/static/css/main.53ba10f1.chunk.css \ + $(srcdir)/static/css/main.53ba10f1.chunk.css.map \ $(NULL) webstaticjsdir=$(webdir)/static/js dist_webstaticjs_DATA = \ - static/js/10.44d9d40b.chunk.js \ - static/js/10.44d9d40b.chunk.js.map \ - static/js/2.3123f37d.chunk.js \ - static/js/2.3123f37d.chunk.js.LICENSE \ - static/js/2.3123f37d.chunk.js.map \ - static/js/3.d49f0857.chunk.js \ - static/js/3.d49f0857.chunk.js.map \ - static/js/4.8b70c754.chunk.js \ - static/js/4.8b70c754.chunk.js.map \ - static/js/5.29dab1cd.chunk.js \ - static/js/5.29dab1cd.chunk.js.LICENSE \ - static/js/5.29dab1cd.chunk.js.map \ - static/js/6.7b15cdf3.chunk.js \ - static/js/6.7b15cdf3.chunk.js.map \ - static/js/7.cf6bc66f.chunk.js \ - static/js/7.cf6bc66f.chunk.js.map \ - static/js/8.b1a4b595.chunk.js \ - static/js/8.b1a4b595.chunk.js.map \ - static/js/9.50358509.chunk.js \ - static/js/9.50358509.chunk.js.map \ - static/js/main.cc0e57d1.chunk.js \ - static/js/main.cc0e57d1.chunk.js.LICENSE \ - static/js/main.cc0e57d1.chunk.js.map \ - static/js/runtime-main.b352aa47.js \ - static/js/runtime-main.b352aa47.js.map \ + $(srcdir)/static/js/10.a5cd7d0e.chunk.js \ + $(srcdir)/static/js/10.a5cd7d0e.chunk.js.map \ + $(srcdir)/static/js/2.92ca8446.chunk.js \ + $(srcdir)/static/js/2.92ca8446.chunk.js.LICENSE \ + $(srcdir)/static/js/2.92ca8446.chunk.js.map \ + $(srcdir)/static/js/3.f137faca.chunk.js \ + $(srcdir)/static/js/3.f137faca.chunk.js.map \ + $(srcdir)/static/js/4.2dbcd906.chunk.js \ + $(srcdir)/static/js/4.2dbcd906.chunk.js.map \ + $(srcdir)/static/js/5.2f783a54.chunk.js \ + $(srcdir)/static/js/5.2f783a54.chunk.js.LICENSE \ + $(srcdir)/static/js/5.2f783a54.chunk.js.map \ + $(srcdir)/static/js/6.e1951239.chunk.js \ + $(srcdir)/static/js/6.e1951239.chunk.js.map \ + $(srcdir)/static/js/7.c2417fb0.chunk.js \ + $(srcdir)/static/js/7.c2417fb0.chunk.js.map \ + $(srcdir)/static/js/8.b4161ea2.chunk.js \ + $(srcdir)/static/js/8.b4161ea2.chunk.js.map \ + $(srcdir)/static/js/9.a4363968.chunk.js \ + $(srcdir)/static/js/9.a4363968.chunk.js.map \ + $(srcdir)/static/js/main.7d1bdca1.chunk.js \ + $(srcdir)/static/js/main.7d1bdca1.chunk.js.LICENSE \ + $(srcdir)/static/js/main.7d1bdca1.chunk.js.map \ + $(srcdir)/static/js/runtime-main.08abed8f.js \ + $(srcdir)/static/js/runtime-main.08abed8f.js.map \ $(NULL) webstaticmediadir=$(webdir)/static/media dist_webstaticmedia_DATA = \ - static/media/ibm-plex-sans-latin-100.245539db.woff2 \ - static/media/ibm-plex-sans-latin-100.9a582f3a.woff \ - static/media/ibm-plex-sans-latin-100italic.1ea7c5d2.woff \ - static/media/ibm-plex-sans-latin-100italic.3c34cf08.woff2 \ - static/media/ibm-plex-sans-latin-200.67524c36.woff \ - static/media/ibm-plex-sans-latin-200.bf72c841.woff2 \ - static/media/ibm-plex-sans-latin-200italic.52df2560.woff \ - static/media/ibm-plex-sans-latin-200italic.bbc2d552.woff2 \ - static/media/ibm-plex-sans-latin-300.10bb6a0a.woff \ - static/media/ibm-plex-sans-latin-300.9e1c48af.woff2 \ - static/media/ibm-plex-sans-latin-300italic.c76f2ab5.woff2 \ - static/media/ibm-plex-sans-latin-300italic.d3566d5b.woff \ - static/media/ibm-plex-sans-latin-400.263d6267.woff2 \ - static/media/ibm-plex-sans-latin-400.a2c56f94.woff \ - static/media/ibm-plex-sans-latin-400italic.272f8611.woff \ - static/media/ibm-plex-sans-latin-400italic.89a93a1b.woff2 \ - static/media/ibm-plex-sans-latin-500.0866c244.woff2 \ - static/media/ibm-plex-sans-latin-500.f6d5c5d5.woff \ - static/media/ibm-plex-sans-latin-500italic.ccd41bd1.woff \ - static/media/ibm-plex-sans-latin-500italic.ffd12d59.woff2 \ - static/media/ibm-plex-sans-latin-600.337b1651.woff \ - static/media/ibm-plex-sans-latin-600.7852d4dc.woff2 \ - static/media/ibm-plex-sans-latin-600italic.17e5379f.woff2 \ - static/media/ibm-plex-sans-latin-600italic.6f4ba6aa.woff \ - static/media/ibm-plex-sans-latin-700.b8809d61.woff \ - static/media/ibm-plex-sans-latin-700.c9983d3d.woff2 \ - static/media/ibm-plex-sans-latin-700italic.02954bee.woff2 \ - static/media/ibm-plex-sans-latin-700italic.72e9af40.woff \ - static/media/material-icons.0509ab09.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-100.245539db.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-100.9a582f3a.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-100italic.1ea7c5d2.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-100italic.3c34cf08.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-200.67524c36.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-200.bf72c841.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-200italic.52df2560.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-200italic.bbc2d552.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-300.10bb6a0a.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-300.9e1c48af.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-300italic.c76f2ab5.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-300italic.d3566d5b.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-400.263d6267.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-400.a2c56f94.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-400italic.272f8611.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-400italic.89a93a1b.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-500.0866c244.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-500.f6d5c5d5.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-500italic.ccd41bd1.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-500italic.ffd12d59.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-600.337b1651.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-600.7852d4dc.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-600italic.17e5379f.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-600italic.6f4ba6aa.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-700.b8809d61.woff \ + $(srcdir)/static/media/ibm-plex-sans-latin-700.c9983d3d.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-700italic.02954bee.woff2 \ + $(srcdir)/static/media/ibm-plex-sans-latin-700italic.72e9af40.woff \ + $(srcdir)/static/media/material-icons.0509ab09.woff2 \ $(NULL) diff --git a/web/gui/dashboard/asset-manifest.json b/web/gui/dashboard/asset-manifest.json index 03653fcf8..584402417 100644 --- a/web/gui/dashboard/asset-manifest.json +++ b/web/gui/dashboard/asset-manifest.json @@ -1,47 +1,47 @@ { "files": { "main.css": "./static/css/main.53ba10f1.chunk.css", - "main.js": "./static/js/main.cc0e57d1.chunk.js", - "main.js.map": "./static/js/main.cc0e57d1.chunk.js.map", - "runtime-main.js": "./static/js/runtime-main.b352aa47.js", - "runtime-main.js.map": "./static/js/runtime-main.b352aa47.js.map", - "static/css/2.20fd0a40.chunk.css": "./static/css/2.20fd0a40.chunk.css", - "static/js/2.3123f37d.chunk.js": "./static/js/2.3123f37d.chunk.js", - "static/js/2.3123f37d.chunk.js.map": "./static/js/2.3123f37d.chunk.js.map", - "static/js/3.d49f0857.chunk.js": "./static/js/3.d49f0857.chunk.js", - "static/js/3.d49f0857.chunk.js.map": "./static/js/3.d49f0857.chunk.js.map", + "main.js": "./static/js/main.7d1bdca1.chunk.js", + "main.js.map": "./static/js/main.7d1bdca1.chunk.js.map", + "runtime-main.js": "./static/js/runtime-main.08abed8f.js", + "runtime-main.js.map": "./static/js/runtime-main.08abed8f.js.map", + "static/css/2.c454aab8.chunk.css": "./static/css/2.c454aab8.chunk.css", + "static/js/2.92ca8446.chunk.js": "./static/js/2.92ca8446.chunk.js", + "static/js/2.92ca8446.chunk.js.map": "./static/js/2.92ca8446.chunk.js.map", + "static/js/3.f137faca.chunk.js": "./static/js/3.f137faca.chunk.js", + "static/js/3.f137faca.chunk.js.map": "./static/js/3.f137faca.chunk.js.map", "static/css/4.a36e3b73.chunk.css": "./static/css/4.a36e3b73.chunk.css", - "static/js/4.8b70c754.chunk.js": "./static/js/4.8b70c754.chunk.js", - "static/js/4.8b70c754.chunk.js.map": "./static/js/4.8b70c754.chunk.js.map", - "static/js/5.29dab1cd.chunk.js": "./static/js/5.29dab1cd.chunk.js", - "static/js/5.29dab1cd.chunk.js.map": "./static/js/5.29dab1cd.chunk.js.map", - "static/js/6.7b15cdf3.chunk.js": "./static/js/6.7b15cdf3.chunk.js", - "static/js/6.7b15cdf3.chunk.js.map": "./static/js/6.7b15cdf3.chunk.js.map", - "static/js/7.cf6bc66f.chunk.js": "./static/js/7.cf6bc66f.chunk.js", - "static/js/7.cf6bc66f.chunk.js.map": "./static/js/7.cf6bc66f.chunk.js.map", - "static/js/8.b1a4b595.chunk.js": "./static/js/8.b1a4b595.chunk.js", - "static/js/8.b1a4b595.chunk.js.map": "./static/js/8.b1a4b595.chunk.js.map", - "static/js/9.50358509.chunk.js": "./static/js/9.50358509.chunk.js", - "static/js/9.50358509.chunk.js.map": "./static/js/9.50358509.chunk.js.map", - "static/js/10.44d9d40b.chunk.js": "./static/js/10.44d9d40b.chunk.js", - "static/js/10.44d9d40b.chunk.js.map": "./static/js/10.44d9d40b.chunk.js.map", + "static/js/4.2dbcd906.chunk.js": "./static/js/4.2dbcd906.chunk.js", + "static/js/4.2dbcd906.chunk.js.map": "./static/js/4.2dbcd906.chunk.js.map", + "static/js/5.2f783a54.chunk.js": "./static/js/5.2f783a54.chunk.js", + "static/js/5.2f783a54.chunk.js.map": "./static/js/5.2f783a54.chunk.js.map", + "static/js/6.e1951239.chunk.js": "./static/js/6.e1951239.chunk.js", + "static/js/6.e1951239.chunk.js.map": "./static/js/6.e1951239.chunk.js.map", + "static/js/7.c2417fb0.chunk.js": "./static/js/7.c2417fb0.chunk.js", + "static/js/7.c2417fb0.chunk.js.map": "./static/js/7.c2417fb0.chunk.js.map", + "static/js/8.b4161ea2.chunk.js": "./static/js/8.b4161ea2.chunk.js", + "static/js/8.b4161ea2.chunk.js.map": "./static/js/8.b4161ea2.chunk.js.map", + "static/js/9.a4363968.chunk.js": "./static/js/9.a4363968.chunk.js", + "static/js/9.a4363968.chunk.js.map": "./static/js/9.a4363968.chunk.js.map", + "static/js/10.a5cd7d0e.chunk.js": "./static/js/10.a5cd7d0e.chunk.js", + "static/js/10.a5cd7d0e.chunk.js.map": "./static/js/10.a5cd7d0e.chunk.js.map", "index.html": "./index.html", - "precache-manifest.e95d658eed560f9e0189217cc6919238.js": "./precache-manifest.e95d658eed560f9e0189217cc6919238.js", + "precache-manifest.5fec6109084644adf7bf854243e1a044.js": "./precache-manifest.5fec6109084644adf7bf854243e1a044.js", "service-worker.js": "./service-worker.js", - "static/css/2.20fd0a40.chunk.css.map": "./static/css/2.20fd0a40.chunk.css.map", + "static/css/2.c454aab8.chunk.css.map": "./static/css/2.c454aab8.chunk.css.map", "static/css/4.a36e3b73.chunk.css.map": "./static/css/4.a36e3b73.chunk.css.map", "static/css/main.53ba10f1.chunk.css.map": "./static/css/main.53ba10f1.chunk.css.map", - "static/js/2.3123f37d.chunk.js.LICENSE": "./static/js/2.3123f37d.chunk.js.LICENSE", - "static/js/5.29dab1cd.chunk.js.LICENSE": "./static/js/5.29dab1cd.chunk.js.LICENSE", - "static/js/main.cc0e57d1.chunk.js.LICENSE": "./static/js/main.cc0e57d1.chunk.js.LICENSE", + "static/js/2.92ca8446.chunk.js.LICENSE": "./static/js/2.92ca8446.chunk.js.LICENSE", + "static/js/5.2f783a54.chunk.js.LICENSE": "./static/js/5.2f783a54.chunk.js.LICENSE", + "static/js/main.7d1bdca1.chunk.js.LICENSE": "./static/js/main.7d1bdca1.chunk.js.LICENSE", "static/media/index.css": "./static/media/ibm-plex-sans-latin-700italic.72e9af40.woff", "static/media/fonts.css": "./static/media/material-icons.0509ab09.woff2" }, "entrypoints": [ - "static/js/runtime-main.b352aa47.js", - "static/css/2.20fd0a40.chunk.css", - "static/js/2.3123f37d.chunk.js", + "static/js/runtime-main.08abed8f.js", + "static/css/2.c454aab8.chunk.css", + "static/js/2.92ca8446.chunk.js", "static/css/main.53ba10f1.chunk.css", - "static/js/main.cc0e57d1.chunk.js" + "static/js/main.7d1bdca1.chunk.js" ] } \ No newline at end of file diff --git a/web/gui/dashboard/index.html b/web/gui/dashboard/index.html index d6f0ca410..db46d079f 100644 --- a/web/gui/dashboard/index.html +++ b/web/gui/dashboard/index.html @@ -1,4 +1,4 @@ -netdata dashboard
You must enable JavaScript in order to use Netdata!
You can do this in your browser settings.
\ No newline at end of file + overlayEl.style = theme == 'slate' ? "background-color: #272b30; color: #373b40;" : "background-color: #fff; color: #ddd;";
\ No newline at end of file diff --git a/web/gui/dashboard/precache-manifest.5fec6109084644adf7bf854243e1a044.js b/web/gui/dashboard/precache-manifest.5fec6109084644adf7bf854243e1a044.js new file mode 100644 index 000000000..2816bb2a0 --- /dev/null +++ b/web/gui/dashboard/precache-manifest.5fec6109084644adf7bf854243e1a044.js @@ -0,0 +1,190 @@ +self.__precacheManifest = (self.__precacheManifest || []).concat([ + { + "revision": "928c945fab999ac6b49d381d496fd4fa", + "url": "./index.html" + }, + { + "revision": "9ff07d20678a36d676fa", + "url": "./static/css/2.c454aab8.chunk.css" + }, + { + "revision": "3f3e6e773062ffa97127", + "url": "./static/css/4.a36e3b73.chunk.css" + }, + { + "revision": "1ede0c6b7366f2db76cd", + "url": "./static/css/main.53ba10f1.chunk.css" + }, + { + "revision": "2e1518ba242fafff36c2", + "url": "./static/js/10.a5cd7d0e.chunk.js" + }, + { + "revision": "9ff07d20678a36d676fa", + "url": "./static/js/2.92ca8446.chunk.js" + }, + { + "revision": "202231b589b831e707a2050ef8b04086", + "url": "./static/js/2.92ca8446.chunk.js.LICENSE" + }, + { + "revision": "9d949a4191279169d355", + "url": "./static/js/3.f137faca.chunk.js" + }, + { + "revision": "3f3e6e773062ffa97127", + "url": "./static/js/4.2dbcd906.chunk.js" + }, + { + "revision": "0a44fcc2aa7ee04633c0", + "url": "./static/js/5.2f783a54.chunk.js" + }, + { + "revision": "f05f27d89effd681fe0717b6a67b9a0d", + "url": "./static/js/5.2f783a54.chunk.js.LICENSE" + }, + { + "revision": "38dacb6fd8c4687e55a7", + "url": "./static/js/6.e1951239.chunk.js" + }, + { + "revision": "31df05c0a16aeb47134f", + "url": "./static/js/7.c2417fb0.chunk.js" + }, + { + "revision": "5a726e66eb5aa05866c5", + "url": "./static/js/8.b4161ea2.chunk.js" + }, + { + "revision": "79862bcda7c7424c10fc", + "url": "./static/js/9.a4363968.chunk.js" + }, + { + "revision": "1ede0c6b7366f2db76cd", + "url": "./static/js/main.7d1bdca1.chunk.js" + }, + { + "revision": "19356475904bddb45614eb6ff7f6cd44", + "url": "./static/js/main.7d1bdca1.chunk.js.LICENSE" + }, + { + "revision": "a99878aeea4445b20339", + "url": "./static/js/runtime-main.08abed8f.js" + }, + { + "revision": "245539db8ee56425757ef728eda8194e", + "url": "./static/media/ibm-plex-sans-latin-100.245539db.woff2" + }, + { + "revision": "9a582f3a304f421eca4027517706843c", + "url": "./static/media/ibm-plex-sans-latin-100.9a582f3a.woff" + }, + { + "revision": "1ea7c5d21b5956b602bdf9656cfb353f", + "url": "./static/media/ibm-plex-sans-latin-100italic.1ea7c5d2.woff" + }, + { + "revision": "3c34cf080b38f5fb1d4c59ffa45b3967", + "url": "./static/media/ibm-plex-sans-latin-100italic.3c34cf08.woff2" + }, + { + "revision": "67524c36348a323f78f2845e3aafc2d4", + "url": "./static/media/ibm-plex-sans-latin-200.67524c36.woff" + }, + { + "revision": "bf72c8412ab06c393f52efc5beb26ea7", + "url": "./static/media/ibm-plex-sans-latin-200.bf72c841.woff2" + }, + { + "revision": "52df25607ec284ca361ae50ba24b3580", + "url": "./static/media/ibm-plex-sans-latin-200italic.52df2560.woff" + }, + { + "revision": "bbc2d55223638ce450424a917e1104b2", + "url": "./static/media/ibm-plex-sans-latin-200italic.bbc2d552.woff2" + }, + { + "revision": "10bb6a0ae6dc8000d999ab622a45e281", + "url": "./static/media/ibm-plex-sans-latin-300.10bb6a0a.woff" + }, + { + "revision": "9e1c48af24191f6ea8aede14957c5d01", + "url": "./static/media/ibm-plex-sans-latin-300.9e1c48af.woff2" + }, + { + "revision": "c76f2ab53673e964b6e6734c1c455761", + "url": "./static/media/ibm-plex-sans-latin-300italic.c76f2ab5.woff2" + }, + { + "revision": "d3566d5bb4f31d86bfb9fda09563b416", + "url": "./static/media/ibm-plex-sans-latin-300italic.d3566d5b.woff" + }, + { + "revision": "263d6267533501f58c33b12b382e3abb", + "url": "./static/media/ibm-plex-sans-latin-400.263d6267.woff2" + }, + { + "revision": "a2c56f946488a9a267ba6ba21471a217", + "url": "./static/media/ibm-plex-sans-latin-400.a2c56f94.woff" + }, + { + "revision": "272f86114c980c52c131dfc3b4ae3276", + "url": "./static/media/ibm-plex-sans-latin-400italic.272f8611.woff" + }, + { + "revision": "89a93a1bdde48c7bb104150de88affce", + "url": "./static/media/ibm-plex-sans-latin-400italic.89a93a1b.woff2" + }, + { + "revision": "0866c24487514ad726738fb24f8e015b", + "url": "./static/media/ibm-plex-sans-latin-500.0866c244.woff2" + }, + { + "revision": "f6d5c5d5b849796d6a8f5a2953b60753", + "url": "./static/media/ibm-plex-sans-latin-500.f6d5c5d5.woff" + }, + { + "revision": "ccd41bd1a5bfa8bad2cd8d35fdaeb3d1", + "url": "./static/media/ibm-plex-sans-latin-500italic.ccd41bd1.woff" + }, + { + "revision": "ffd12d59339823b8cf53b9f99b47d87c", + "url": "./static/media/ibm-plex-sans-latin-500italic.ffd12d59.woff2" + }, + { + "revision": "337b16517a230dc830b84dc6e6167b68", + "url": "./static/media/ibm-plex-sans-latin-600.337b1651.woff" + }, + { + "revision": "7852d4dc26ef44df58e23dc0b9722d6f", + "url": "./static/media/ibm-plex-sans-latin-600.7852d4dc.woff2" + }, + { + "revision": "17e5379fd9a99b9bcb26ea983f391b6a", + "url": "./static/media/ibm-plex-sans-latin-600italic.17e5379f.woff2" + }, + { + "revision": "6f4ba6aa87fa99d5bc2b90a7b40a0ded", + "url": "./static/media/ibm-plex-sans-latin-600italic.6f4ba6aa.woff" + }, + { + "revision": "b8809d619a33eb825b0450281ff752e7", + "url": "./static/media/ibm-plex-sans-latin-700.b8809d61.woff" + }, + { + "revision": "c9983d3d04f3ed6c2eafee1db1d24e06", + "url": "./static/media/ibm-plex-sans-latin-700.c9983d3d.woff2" + }, + { + "revision": "02954beec9e742bb1f3ae27b7e7cb71f", + "url": "./static/media/ibm-plex-sans-latin-700italic.02954bee.woff2" + }, + { + "revision": "72e9af409ddafc63a5dd380e34758560", + "url": "./static/media/ibm-plex-sans-latin-700italic.72e9af40.woff" + }, + { + "revision": "0509ab09c1b0d2200a4135803c91d6ce", + "url": "./static/media/material-icons.0509ab09.woff2" + } +]); \ No newline at end of file diff --git a/web/gui/dashboard/precache-manifest.e95d658eed560f9e0189217cc6919238.js b/web/gui/dashboard/precache-manifest.e95d658eed560f9e0189217cc6919238.js deleted file mode 100644 index 24489d46d..000000000 --- a/web/gui/dashboard/precache-manifest.e95d658eed560f9e0189217cc6919238.js +++ /dev/null @@ -1,190 +0,0 @@ -self.__precacheManifest = (self.__precacheManifest || []).concat([ - { - "revision": "fbc883e451c1a5b68d9e3a6be82c34b5", - "url": "./index.html" - }, - { - "revision": "5c3544e36e0c9c6041af", - "url": "./static/css/2.20fd0a40.chunk.css" - }, - { - "revision": "212d45239fd5f6814421", - "url": "./static/css/4.a36e3b73.chunk.css" - }, - { - "revision": "56020ad27dc4cda734dc", - "url": "./static/css/main.53ba10f1.chunk.css" - }, - { - "revision": "c45bd47abb091dfda126", - "url": "./static/js/10.44d9d40b.chunk.js" - }, - { - "revision": "5c3544e36e0c9c6041af", - "url": "./static/js/2.3123f37d.chunk.js" - }, - { - "revision": "766a5a832af1f575ad69e0e14fd2915f", - "url": "./static/js/2.3123f37d.chunk.js.LICENSE" - }, - { - "revision": "b7798b669b1966ed23ec", - "url": "./static/js/3.d49f0857.chunk.js" - }, - { - "revision": "212d45239fd5f6814421", - "url": "./static/js/4.8b70c754.chunk.js" - }, - { - "revision": "2555e37b8dd72102b21e", - "url": "./static/js/5.29dab1cd.chunk.js" - }, - { - "revision": "f05f27d89effd681fe0717b6a67b9a0d", - "url": "./static/js/5.29dab1cd.chunk.js.LICENSE" - }, - { - "revision": "a20d9477721b5236a011", - "url": "./static/js/6.7b15cdf3.chunk.js" - }, - { - "revision": "58e2d9966bc46cb2f6e7", - "url": "./static/js/7.cf6bc66f.chunk.js" - }, - { - "revision": "f58cbc2f5fd752800e1c", - "url": "./static/js/8.b1a4b595.chunk.js" - }, - { - "revision": "74e97210cbd08c0b3ff1", - "url": "./static/js/9.50358509.chunk.js" - }, - { - "revision": "56020ad27dc4cda734dc", - "url": "./static/js/main.cc0e57d1.chunk.js" - }, - { - "revision": "19356475904bddb45614eb6ff7f6cd44", - "url": "./static/js/main.cc0e57d1.chunk.js.LICENSE" - }, - { - "revision": "904a84c9f2c6baa9dabe", - "url": "./static/js/runtime-main.b352aa47.js" - }, - { - "revision": "245539db8ee56425757ef728eda8194e", - "url": "./static/media/ibm-plex-sans-latin-100.245539db.woff2" - }, - { - "revision": "9a582f3a304f421eca4027517706843c", - "url": "./static/media/ibm-plex-sans-latin-100.9a582f3a.woff" - }, - { - "revision": "1ea7c5d21b5956b602bdf9656cfb353f", - "url": "./static/media/ibm-plex-sans-latin-100italic.1ea7c5d2.woff" - }, - { - "revision": "3c34cf080b38f5fb1d4c59ffa45b3967", - "url": "./static/media/ibm-plex-sans-latin-100italic.3c34cf08.woff2" - }, - { - "revision": "67524c36348a323f78f2845e3aafc2d4", - "url": "./static/media/ibm-plex-sans-latin-200.67524c36.woff" - }, - { - "revision": "bf72c8412ab06c393f52efc5beb26ea7", - "url": "./static/media/ibm-plex-sans-latin-200.bf72c841.woff2" - }, - { - "revision": "52df25607ec284ca361ae50ba24b3580", - "url": "./static/media/ibm-plex-sans-latin-200italic.52df2560.woff" - }, - { - "revision": "bbc2d55223638ce450424a917e1104b2", - "url": "./static/media/ibm-plex-sans-latin-200italic.bbc2d552.woff2" - }, - { - "revision": "10bb6a0ae6dc8000d999ab622a45e281", - "url": "./static/media/ibm-plex-sans-latin-300.10bb6a0a.woff" - }, - { - "revision": "9e1c48af24191f6ea8aede14957c5d01", - "url": "./static/media/ibm-plex-sans-latin-300.9e1c48af.woff2" - }, - { - "revision": "c76f2ab53673e964b6e6734c1c455761", - "url": "./static/media/ibm-plex-sans-latin-300italic.c76f2ab5.woff2" - }, - { - "revision": "d3566d5bb4f31d86bfb9fda09563b416", - "url": "./static/media/ibm-plex-sans-latin-300italic.d3566d5b.woff" - }, - { - "revision": "263d6267533501f58c33b12b382e3abb", - "url": "./static/media/ibm-plex-sans-latin-400.263d6267.woff2" - }, - { - "revision": "a2c56f946488a9a267ba6ba21471a217", - "url": "./static/media/ibm-plex-sans-latin-400.a2c56f94.woff" - }, - { - "revision": "272f86114c980c52c131dfc3b4ae3276", - "url": "./static/media/ibm-plex-sans-latin-400italic.272f8611.woff" - }, - { - "revision": "89a93a1bdde48c7bb104150de88affce", - "url": "./static/media/ibm-plex-sans-latin-400italic.89a93a1b.woff2" - }, - { - "revision": "0866c24487514ad726738fb24f8e015b", - "url": "./static/media/ibm-plex-sans-latin-500.0866c244.woff2" - }, - { - "revision": "f6d5c5d5b849796d6a8f5a2953b60753", - "url": "./static/media/ibm-plex-sans-latin-500.f6d5c5d5.woff" - }, - { - "revision": "ccd41bd1a5bfa8bad2cd8d35fdaeb3d1", - "url": "./static/media/ibm-plex-sans-latin-500italic.ccd41bd1.woff" - }, - { - "revision": "ffd12d59339823b8cf53b9f99b47d87c", - "url": "./static/media/ibm-plex-sans-latin-500italic.ffd12d59.woff2" - }, - { - "revision": "337b16517a230dc830b84dc6e6167b68", - "url": "./static/media/ibm-plex-sans-latin-600.337b1651.woff" - }, - { - "revision": "7852d4dc26ef44df58e23dc0b9722d6f", - "url": "./static/media/ibm-plex-sans-latin-600.7852d4dc.woff2" - }, - { - "revision": "17e5379fd9a99b9bcb26ea983f391b6a", - "url": "./static/media/ibm-plex-sans-latin-600italic.17e5379f.woff2" - }, - { - "revision": "6f4ba6aa87fa99d5bc2b90a7b40a0ded", - "url": "./static/media/ibm-plex-sans-latin-600italic.6f4ba6aa.woff" - }, - { - "revision": "b8809d619a33eb825b0450281ff752e7", - "url": "./static/media/ibm-plex-sans-latin-700.b8809d61.woff" - }, - { - "revision": "c9983d3d04f3ed6c2eafee1db1d24e06", - "url": "./static/media/ibm-plex-sans-latin-700.c9983d3d.woff2" - }, - { - "revision": "02954beec9e742bb1f3ae27b7e7cb71f", - "url": "./static/media/ibm-plex-sans-latin-700italic.02954bee.woff2" - }, - { - "revision": "72e9af409ddafc63a5dd380e34758560", - "url": "./static/media/ibm-plex-sans-latin-700italic.72e9af40.woff" - }, - { - "revision": "0509ab09c1b0d2200a4135803c91d6ce", - "url": "./static/media/material-icons.0509ab09.woff2" - } -]); \ No newline at end of file diff --git a/web/gui/dashboard/service-worker.js b/web/gui/dashboard/service-worker.js index 3017de3cb..b7eba5a42 100644 --- a/web/gui/dashboard/service-worker.js +++ b/web/gui/dashboard/service-worker.js @@ -14,7 +14,7 @@ importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); importScripts( - "./precache-manifest.e95d658eed560f9e0189217cc6919238.js" + "./precache-manifest.5fec6109084644adf7bf854243e1a044.js" ); self.addEventListener('message', (event) => { diff --git a/web/gui/dashboard/static/css/2.20fd0a40.chunk.css b/web/gui/dashboard/static/css/2.20fd0a40.chunk.css deleted file mode 100644 index ebbaf6ddf..000000000 --- a/web/gui/dashboard/static/css/2.20fd0a40.chunk.css +++ /dev/null @@ -1,15 +0,0 @@ -@charset "UTF-8";.CodeMirror{height:25px}.ReactCodeMirror{height:30px}.cm-category{color:#2196f3}.cm-operator{color:#9e9e9e}.cm-value{color:#e91e63}.react-filter-box{overflow-y:hidden;height:28px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;border-radius:4px;background-color:#fff;border:1px solid #ccc;box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border .2s linear,box-shadow .2s linear;transition:border .2s linear,box-shadow .2s linear}.react-filter-box.focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.react-filter-box.error{border-color:#a94442;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.react-filter-box.error.focus{box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.CodeMirror-hints{padding:5px;min-width:100px}.CodeMirror-hints .CodeMirror-hint{padding:5px}li.CodeMirror-hint-active{background:#2196f3}li.CodeMirror-hint-active .hint-value{color:#fff} - -/*! - Material Components for the Web - Copyright (c) 2019 Google Inc. - License: MIT -*/.mdc-menu-surface{display:none;position:absolute;box-sizing:border-box;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);margin:0;padding:0;-webkit-transform:scale(1);transform:scale(1);-webkit-transform-origin:top left;transform-origin:top left;opacity:0;overflow:auto;will-change:transform,opacity;z-index:8;transition:opacity .03s linear,-webkit-transform .12s cubic-bezier(0,0,.2,1);-webkit-transition:opacity .03s linear,-webkit-transform .12s cubic-bezier(0,0,.2,1);transition:opacity .03s linear,transform .12s cubic-bezier(0,0,.2,1);transition:opacity .03s linear,transform .12s cubic-bezier(0,0,.2,1),-webkit-transform .12s cubic-bezier(0,0,.2,1);box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12);background-color:#fff;background-color:var(--mdc-theme-surface,#fff);color:#000;color:var(--mdc-theme-on-surface,#000);border-radius:4px;transform-origin-left:top left;transform-origin-right:top right}.mdc-menu-surface:focus{outline:none}.mdc-menu-surface--open{display:inline-block;-webkit-transform:scale(1);transform:scale(1);opacity:1}.mdc-menu-surface--animating-open{display:inline-block;-webkit-transform:scale(.8);transform:scale(.8);opacity:0}.mdc-menu-surface--animating-closed{display:inline-block;opacity:0;-webkit-transition:opacity 75ms linear;transition:opacity 75ms linear}.mdc-menu-surface[dir=rtl],[dir=rtl] .mdc-menu-surface{transform-origin-left:top right;transform-origin-right:top left}.mdc-menu-surface--anchor{position:relative;overflow:visible}.mdc-menu-surface--fixed{position:fixed} -/*! ======================================================================== - * Bootstrap Toggle: bootstrap-toggle.css v2.2.0 - * http://www.bootstraptoggle.com - * ======================================================================== - * Copyright 2014 Min Hur, The New York Times Company - * Licensed under MIT - * ======================================================================== */.checkbox-inline .toggle,.checkbox label .toggle{margin-left:-20px;margin-right:5px}.toggle{position:relative;overflow:hidden}.toggle input[type=checkbox]{display:none}.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}.toggle.off .toggle-group{left:-100%}.toggle-on{left:0;right:50%}.toggle-off,.toggle-on{position:absolute;top:0;bottom:0;margin:0;border:0;border-radius:0}.toggle-off{left:50%;right:0}.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}.toggle.btn{min-width:59px;min-height:34px}.toggle-on.btn{padding-right:24px}.toggle-off.btn{padding-left:24px}.toggle.btn-lg{min-width:79px;min-height:45px}.toggle-on.btn-lg{padding-right:31px}.toggle-off.btn-lg{padding-left:31px}.toggle-handle.btn-lg{width:40px}.toggle.btn-sm{min-width:50px;min-height:30px}.toggle-on.btn-sm{padding-right:20px}.toggle-off.btn-sm{padding-left:20px}.toggle.btn-xs{min-width:35px;min-height:22px}.toggle-on.btn-xs{padding-right:12px}.toggle-off.btn-xs{padding-left:12px}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:100;src:local("IBM Plex Sans Thin "),local("IBM Plex Sans-Thin"),url(../../static/media/ibm-plex-sans-latin-100.245539db.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-100.9a582f3a.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:100;src:local("IBM Plex Sans Thin italic"),local("IBM Plex Sans-Thinitalic"),url(../../static/media/ibm-plex-sans-latin-100italic.3c34cf08.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-100italic.1ea7c5d2.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:200;src:local("IBM Plex Sans Extra Light "),local("IBM Plex Sans-Extra Light"),url(../../static/media/ibm-plex-sans-latin-200.bf72c841.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-200.67524c36.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:200;src:local("IBM Plex Sans Extra Light italic"),local("IBM Plex Sans-Extra Lightitalic"),url(../../static/media/ibm-plex-sans-latin-200italic.bbc2d552.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-200italic.52df2560.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:300;src:local("IBM Plex Sans Light "),local("IBM Plex Sans-Light"),url(../../static/media/ibm-plex-sans-latin-300.9e1c48af.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-300.10bb6a0a.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:300;src:local("IBM Plex Sans Light italic"),local("IBM Plex Sans-Lightitalic"),url(../../static/media/ibm-plex-sans-latin-300italic.c76f2ab5.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-300italic.d3566d5b.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:local("IBM Plex Sans Regular "),local("IBM Plex Sans-Regular"),url(../../static/media/ibm-plex-sans-latin-400.263d6267.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-400.a2c56f94.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:400;src:local("IBM Plex Sans Regular italic"),local("IBM Plex Sans-Regularitalic"),url(../../static/media/ibm-plex-sans-latin-400italic.89a93a1b.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-400italic.272f8611.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:local("IBM Plex Sans Medium "),local("IBM Plex Sans-Medium"),url(../../static/media/ibm-plex-sans-latin-500.0866c244.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-500.f6d5c5d5.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:500;src:local("IBM Plex Sans Medium italic"),local("IBM Plex Sans-Mediumitalic"),url(../../static/media/ibm-plex-sans-latin-500italic.ffd12d59.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-500italic.ccd41bd1.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:local("IBM Plex Sans SemiBold "),local("IBM Plex Sans-SemiBold"),url(../../static/media/ibm-plex-sans-latin-600.7852d4dc.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-600.337b1651.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:600;src:local("IBM Plex Sans SemiBold italic"),local("IBM Plex Sans-SemiBolditalic"),url(../../static/media/ibm-plex-sans-latin-600italic.17e5379f.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-600italic.6f4ba6aa.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:700;src:local("IBM Plex Sans Bold "),local("IBM Plex Sans-Bold"),url(../../static/media/ibm-plex-sans-latin-700.c9983d3d.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-700.b8809d61.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:700;src:local("IBM Plex Sans Bold italic"),local("IBM Plex Sans-Bolditalic"),url(../../static/media/ibm-plex-sans-latin-700italic.02954bee.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-700italic.72e9af40.woff) format("woff")}.Toastify__toast-container{z-index:9999;-webkit-transform:translateZ(9999px);position:fixed;padding:4px;width:320px;box-sizing:border-box;color:#fff}.Toastify__toast-container--top-left{top:1em;left:1em}.Toastify__toast-container--top-center{top:1em;left:50%;margin-left:-160px}.Toastify__toast-container--top-right{top:1em;right:1em}.Toastify__toast-container--bottom-left{bottom:1em;left:1em}.Toastify__toast-container--bottom-center{bottom:1em;left:50%;margin-left:-160px}.Toastify__toast-container--bottom-right{bottom:1em;right:1em}@media only screen and (max-width:480px){.Toastify__toast-container{width:100vw;padding:0;left:0;margin:0}.Toastify__toast-container--top-center,.Toastify__toast-container--top-left,.Toastify__toast-container--top-right{top:0}.Toastify__toast-container--bottom-center,.Toastify__toast-container--bottom-left,.Toastify__toast-container--bottom-right{bottom:0}.Toastify__toast-container--rtl{right:0;left:auto}}.Toastify__toast{position:relative;min-height:64px;box-sizing:border-box;margin-bottom:1rem;padding:8px;border-radius:1px;box-shadow:0 1px 10px 0 rgba(0,0,0,.1),0 2px 15px 0 rgba(0,0,0,.05);display:flex;justify-content:space-between;max-height:800px;overflow:hidden;font-family:sans-serif;cursor:pointer;direction:ltr}.Toastify__toast--rtl{direction:rtl}.Toastify__toast--default{background:#fff;color:#aaa}.Toastify__toast--info{background:#3498db}.Toastify__toast--success{background:#07bc0c}.Toastify__toast--warning{background:#f1c40f}.Toastify__toast--error{background:#e74c3c}.Toastify__toast-body{margin:auto 0;flex:1 1}@media only screen and (max-width:480px){.Toastify__toast{margin-bottom:0}}.Toastify__close-button{color:#fff;font-weight:700;font-size:14px;background:transparent;outline:none;border:none;padding:0;cursor:pointer;opacity:.7;-webkit-transition:.3s ease;transition:.3s ease;align-self:flex-start}.Toastify__close-button--default{color:#000;opacity:.3}.Toastify__close-button:focus,.Toastify__close-button:hover{opacity:1}@-webkit-keyframes Toastify__trackProgress{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}to{-webkit-transform:scaleX(0);transform:scaleX(0)}}@keyframes Toastify__trackProgress{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}to{-webkit-transform:scaleX(0);transform:scaleX(0)}}.Toastify__progress-bar{position:absolute;bottom:0;left:0;width:100%;height:5px;z-index:9999;opacity:.7;background-color:hsla(0,0%,100%,.7);-webkit-transform-origin:left;transform-origin:left}.Toastify__progress-bar--animated{-webkit-animation:Toastify__trackProgress linear 1 forwards;animation:Toastify__trackProgress linear 1 forwards}.Toastify__progress-bar--controlled{-webkit-transition:-webkit-transform .2s;transition:-webkit-transform .2s;transition:transform .2s;transition:transform .2s,-webkit-transform .2s}.Toastify__progress-bar--rtl{right:0;left:auto;-webkit-transform-origin:right;transform-origin:right}.Toastify__progress-bar--default{background:-webkit-gradient(linear,left top,right top,from(#4cd964),color-stop(#5ac8fa),color-stop(#007aff),color-stop(#34aadc),color-stop(#5856d6),to(#ff2d55));background:linear-gradient(90deg,#4cd964,#5ac8fa,#007aff,#34aadc,#5856d6,#ff2d55)}@-webkit-keyframes Toastify__bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes Toastify__bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}@-webkit-keyframes Toastify__bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes Toastify__bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@-webkit-keyframes Toastify__bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes Toastify__bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}@-webkit-keyframes Toastify__bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes Toastify__bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@-webkit-keyframes Toastify__bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes Toastify__bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@-webkit-keyframes Toastify__bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}@keyframes Toastify__bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}@-webkit-keyframes Toastify__bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes Toastify__bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.Toastify__bounce-enter--bottom-left,.Toastify__bounce-enter--top-left{-webkit-animation-name:Toastify__bounceInLeft;animation-name:Toastify__bounceInLeft}.Toastify__bounce-enter--bottom-right,.Toastify__bounce-enter--top-right{-webkit-animation-name:Toastify__bounceInRight;animation-name:Toastify__bounceInRight}.Toastify__bounce-enter--top-center{-webkit-animation-name:Toastify__bounceInDown;animation-name:Toastify__bounceInDown}.Toastify__bounce-enter--bottom-center{-webkit-animation-name:Toastify__bounceInUp;animation-name:Toastify__bounceInUp}.Toastify__bounce-exit--bottom-left,.Toastify__bounce-exit--top-left{-webkit-animation-name:Toastify__bounceOutLeft;animation-name:Toastify__bounceOutLeft}.Toastify__bounce-exit--bottom-right,.Toastify__bounce-exit--top-right{-webkit-animation-name:Toastify__bounceOutRight;animation-name:Toastify__bounceOutRight}.Toastify__bounce-exit--top-center{-webkit-animation-name:Toastify__bounceOutUp;animation-name:Toastify__bounceOutUp}.Toastify__bounce-exit--bottom-center{-webkit-animation-name:Toastify__bounceOutDown;animation-name:Toastify__bounceOutDown}@-webkit-keyframes Toastify__zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes Toastify__zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@-webkit-keyframes Toastify__zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@keyframes Toastify__zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}.Toastify__zoom-enter{-webkit-animation-name:Toastify__zoomIn;animation-name:Toastify__zoomIn}.Toastify__zoom-exit{-webkit-animation-name:Toastify__zoomOut;animation-name:Toastify__zoomOut}@-webkit-keyframes Toastify__flipIn{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes Toastify__flipIn{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@-webkit-keyframes Toastify__flipOut{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}@keyframes Toastify__flipOut{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}.Toastify__flip-enter{-webkit-animation-name:Toastify__flipIn;animation-name:Toastify__flipIn}.Toastify__flip-exit{-webkit-animation-name:Toastify__flipOut;animation-name:Toastify__flipOut}@-webkit-keyframes Toastify__slideInRight{0%{-webkit-transform:translate3d(110%,0,0);transform:translate3d(110%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__slideInRight{0%{-webkit-transform:translate3d(110%,0,0);transform:translate3d(110%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__slideInLeft{0%{-webkit-transform:translate3d(-110%,0,0);transform:translate3d(-110%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__slideInLeft{0%{-webkit-transform:translate3d(-110%,0,0);transform:translate3d(-110%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__slideInUp{0%{-webkit-transform:translate3d(0,110%,0);transform:translate3d(0,110%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__slideInUp{0%{-webkit-transform:translate3d(0,110%,0);transform:translate3d(0,110%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__slideInDown{0%{-webkit-transform:translate3d(0,-110%,0);transform:translate3d(0,-110%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__slideInDown{0%{-webkit-transform:translate3d(0,-110%,0);transform:translate3d(0,-110%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(110%,0,0);transform:translate3d(110%,0,0)}}@keyframes Toastify__slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(110%,0,0);transform:translate3d(110%,0,0)}}@-webkit-keyframes Toastify__slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-110%,0,0);transform:translate3d(-110%,0,0)}}@keyframes Toastify__slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-110%,0,0);transform:translate3d(-110%,0,0)}}@-webkit-keyframes Toastify__slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,500px,0);transform:translate3d(0,500px,0)}}@keyframes Toastify__slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,500px,0);transform:translate3d(0,500px,0)}}@-webkit-keyframes Toastify__slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-500px,0);transform:translate3d(0,-500px,0)}}@keyframes Toastify__slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-500px,0);transform:translate3d(0,-500px,0)}}.Toastify__slide-enter--bottom-left,.Toastify__slide-enter--top-left{-webkit-animation-name:Toastify__slideInLeft;animation-name:Toastify__slideInLeft}.Toastify__slide-enter--bottom-right,.Toastify__slide-enter--top-right{-webkit-animation-name:Toastify__slideInRight;animation-name:Toastify__slideInRight}.Toastify__slide-enter--top-center{-webkit-animation-name:Toastify__slideInDown;animation-name:Toastify__slideInDown}.Toastify__slide-enter--bottom-center{-webkit-animation-name:Toastify__slideInUp;animation-name:Toastify__slideInUp}.Toastify__slide-exit--bottom-left,.Toastify__slide-exit--top-left{-webkit-animation-name:Toastify__slideOutLeft;animation-name:Toastify__slideOutLeft}.Toastify__slide-exit--bottom-right,.Toastify__slide-exit--top-right{-webkit-animation-name:Toastify__slideOutRight;animation-name:Toastify__slideOutRight}.Toastify__slide-exit--top-center{-webkit-animation-name:Toastify__slideOutUp;animation-name:Toastify__slideOutUp}.Toastify__slide-exit--bottom-center{-webkit-animation-name:Toastify__slideOutDown;animation-name:Toastify__slideOutDown}.react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view--down-arrow,.react-datepicker__navigation-icon:before,.react-datepicker__year-read-view--down-arrow{border-color:#ccc;border-style:solid;border-width:3px 3px 0 0;content:"";display:block;height:9px;position:absolute;top:6px;width:9px}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle{margin-left:-4px;position:absolute;width:0}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{box-sizing:content-box;position:absolute;height:0;width:1px;content:"";z-index:-1;border:8px solid transparent;left:-8px}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{border-bottom-color:#aeaeae}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle{top:0;margin-top:-8px}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before{border-top:none;border-bottom-color:#f0f0f0}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after{top:0}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before{top:-1px;border-bottom-color:#aeaeae}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle{bottom:0;margin-bottom:-8px}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{border-bottom:none;border-top-color:#fff}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after{bottom:0}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{bottom:-1px;border-top-color:#aeaeae}.react-datepicker-wrapper{display:inline-block;padding:0;border:0;width:100%}.react-datepicker{font-family:Helvetica Neue,helvetica,arial,sans-serif;font-size:.8rem;background-color:#fff;color:#000;border:1px solid #aeaeae;border-radius:.3rem;display:inline-block;position:relative}.react-datepicker--time-only .react-datepicker__triangle{left:35px}.react-datepicker--time-only .react-datepicker__time-container{border-left:0}.react-datepicker--time-only .react-datepicker__time,.react-datepicker--time-only .react-datepicker__time-box{border-bottom-left-radius:.3rem;border-bottom-right-radius:.3rem}.react-datepicker__triangle{position:absolute;left:50px}.react-datepicker-popper{z-index:1}.react-datepicker-popper[data-placement^=bottom]{padding-top:10px}.react-datepicker-popper[data-placement=bottom-end] .react-datepicker__triangle,.react-datepicker-popper[data-placement=top-end] .react-datepicker__triangle{left:auto;right:50px}.react-datepicker-popper[data-placement^=top]{padding-bottom:10px}.react-datepicker-popper[data-placement^=right]{padding-left:8px}.react-datepicker-popper[data-placement^=right] .react-datepicker__triangle{left:auto;right:42px}.react-datepicker-popper[data-placement^=left]{padding-right:8px}.react-datepicker-popper[data-placement^=left] .react-datepicker__triangle{left:42px;right:auto}.react-datepicker__header{text-align:center;background-color:#f0f0f0;border-bottom:1px solid #aeaeae;border-top-left-radius:.3rem;padding:8px 0;position:relative}.react-datepicker__header--time{padding-bottom:8px;padding-left:5px;padding-right:5px}.react-datepicker__header--time:not(.react-datepicker__header--time--only){border-top-left-radius:0}.react-datepicker__header:not(.react-datepicker__header--has-time-select){border-top-right-radius:.3rem}.react-datepicker__month-dropdown-container--scroll,.react-datepicker__month-dropdown-container--select,.react-datepicker__month-year-dropdown-container--scroll,.react-datepicker__month-year-dropdown-container--select,.react-datepicker__year-dropdown-container--scroll,.react-datepicker__year-dropdown-container--select{display:inline-block;margin:0 2px}.react-datepicker-time__header,.react-datepicker-year-header,.react-datepicker__current-month{margin-top:0;color:#000;font-weight:700;font-size:.944rem}.react-datepicker-time__header{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.react-datepicker__navigation{align-items:center;background:none;display:flex;justify-content:center;text-align:center;cursor:pointer;position:absolute;top:2px;padding:0;border:none;z-index:1;height:32px;width:32px;text-indent:-999em;overflow:hidden}.react-datepicker__navigation--previous{left:2px}.react-datepicker__navigation--next{right:2px}.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button){right:85px}.react-datepicker__navigation--years{position:relative;top:0;display:block;margin-left:auto;margin-right:auto}.react-datepicker__navigation--years-previous{top:4px}.react-datepicker__navigation--years-upcoming{top:-4px}.react-datepicker__navigation:hover :before{border-color:#a6a6a6}.react-datepicker__navigation-icon{position:relative;top:-1px;font-size:20px;width:0}.react-datepicker__navigation-icon--next{left:-2px}.react-datepicker__navigation-icon--next:before{-webkit-transform:rotate(45deg);transform:rotate(45deg);left:-7px}.react-datepicker__navigation-icon--previous{right:-2px}.react-datepicker__navigation-icon--previous:before{-webkit-transform:rotate(225deg);transform:rotate(225deg);right:-7px}.react-datepicker__month-container{float:left}.react-datepicker__year{margin:.4rem;text-align:center}.react-datepicker__year-wrapper{display:flex;flex-wrap:wrap;max-width:180px}.react-datepicker__year .react-datepicker__year-text{display:inline-block;width:4rem;margin:2px}.react-datepicker__month{margin:.4rem;text-align:center}.react-datepicker__month .react-datepicker__month-text,.react-datepicker__month .react-datepicker__quarter-text{display:inline-block;width:4rem;margin:2px}.react-datepicker__input-time-container{clear:both;width:100%;float:left;margin:5px 0 10px 15px;text-align:left}.react-datepicker__input-time-container .react-datepicker-time__caption,.react-datepicker__input-time-container .react-datepicker-time__input-container{display:inline-block}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input{display:inline-block;margin-left:10px}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input{width:auto}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-inner-spin-button,.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]{-moz-appearance:textfield}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__delimiter{margin-left:5px;display:inline-block}.react-datepicker__time-container{float:right;border-left:1px solid #aeaeae;width:85px}.react-datepicker__time-container--with-today-button{display:inline;border:1px solid #aeaeae;border-radius:.3rem;position:absolute;right:-72px;top:0}.react-datepicker__time-container .react-datepicker__time{position:relative;background:#fff;border-bottom-right-radius:.3rem}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box{width:85px;overflow-x:hidden;margin:0 auto;text-align:center;border-bottom-right-radius:.3rem}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list{list-style:none;margin:0;height:calc(195px + .85rem);overflow-y:scroll;padding-right:0;padding-left:0;width:100%;box-sizing:content-box}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item{height:30px;padding:5px 10px;white-space:nowrap}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item:hover{cursor:pointer;background-color:#f0f0f0}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected{background-color:#216ba5;color:#fff;font-weight:700}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected:hover{background-color:#216ba5}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled{color:#ccc}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled:hover{cursor:default;background-color:transparent}.react-datepicker__week-number{color:#ccc;display:inline-block;width:1.7rem;line-height:1.7rem;text-align:center;margin:.166rem}.react-datepicker__week-number.react-datepicker__week-number--clickable{cursor:pointer}.react-datepicker__week-number.react-datepicker__week-number--clickable:hover{border-radius:.3rem;background-color:#f0f0f0}.react-datepicker__day-names,.react-datepicker__week{white-space:nowrap}.react-datepicker__day-names{margin-bottom:-8px}.react-datepicker__day,.react-datepicker__day-name,.react-datepicker__time-name{color:#000;display:inline-block;width:1.7rem;line-height:1.7rem;text-align:center;margin:.166rem}.react-datepicker__month--in-range,.react-datepicker__month--in-selecting-range,.react-datepicker__month--selected,.react-datepicker__quarter--in-range,.react-datepicker__quarter--in-selecting-range,.react-datepicker__quarter--selected{border-radius:.3rem;background-color:#216ba5;color:#fff}.react-datepicker__month--in-range:hover,.react-datepicker__month--in-selecting-range:hover,.react-datepicker__month--selected:hover,.react-datepicker__quarter--in-range:hover,.react-datepicker__quarter--in-selecting-range:hover,.react-datepicker__quarter--selected:hover{background-color:#1d5d90}.react-datepicker__month--disabled,.react-datepicker__quarter--disabled{color:#ccc;pointer-events:none}.react-datepicker__month--disabled:hover,.react-datepicker__quarter--disabled:hover{cursor:default;background-color:transparent}.react-datepicker__day,.react-datepicker__month-text,.react-datepicker__quarter-text,.react-datepicker__year-text{cursor:pointer}.react-datepicker__day:hover,.react-datepicker__month-text:hover,.react-datepicker__quarter-text:hover,.react-datepicker__year-text:hover{border-radius:.3rem;background-color:#f0f0f0}.react-datepicker__day--today,.react-datepicker__month-text--today,.react-datepicker__quarter-text--today,.react-datepicker__year-text--today{font-weight:700}.react-datepicker__day--highlighted,.react-datepicker__month-text--highlighted,.react-datepicker__quarter-text--highlighted,.react-datepicker__year-text--highlighted{border-radius:.3rem;background-color:#3dcc4a;color:#fff}.react-datepicker__day--highlighted:hover,.react-datepicker__month-text--highlighted:hover,.react-datepicker__quarter-text--highlighted:hover,.react-datepicker__year-text--highlighted:hover{background-color:#32be3f}.react-datepicker__day--highlighted-custom-1,.react-datepicker__month-text--highlighted-custom-1,.react-datepicker__quarter-text--highlighted-custom-1,.react-datepicker__year-text--highlighted-custom-1{color:#f0f}.react-datepicker__day--highlighted-custom-2,.react-datepicker__month-text--highlighted-custom-2,.react-datepicker__quarter-text--highlighted-custom-2,.react-datepicker__year-text--highlighted-custom-2{color:green}.react-datepicker__day--in-range,.react-datepicker__day--in-selecting-range,.react-datepicker__day--selected,.react-datepicker__month-text--in-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__month-text--selected,.react-datepicker__quarter-text--in-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__quarter-text--selected,.react-datepicker__year-text--in-range,.react-datepicker__year-text--in-selecting-range,.react-datepicker__year-text--selected{border-radius:.3rem;background-color:#216ba5;color:#fff}.react-datepicker__day--in-range:hover,.react-datepicker__day--in-selecting-range:hover,.react-datepicker__day--selected:hover,.react-datepicker__month-text--in-range:hover,.react-datepicker__month-text--in-selecting-range:hover,.react-datepicker__month-text--selected:hover,.react-datepicker__quarter-text--in-range:hover,.react-datepicker__quarter-text--in-selecting-range:hover,.react-datepicker__quarter-text--selected:hover,.react-datepicker__year-text--in-range:hover,.react-datepicker__year-text--in-selecting-range:hover,.react-datepicker__year-text--selected:hover{background-color:#1d5d90}.react-datepicker__day--keyboard-selected,.react-datepicker__month-text--keyboard-selected,.react-datepicker__quarter-text--keyboard-selected,.react-datepicker__year-text--keyboard-selected{border-radius:.3rem;background-color:#2a87d0;color:#fff}.react-datepicker__day--keyboard-selected:hover,.react-datepicker__month-text--keyboard-selected:hover,.react-datepicker__quarter-text--keyboard-selected:hover,.react-datepicker__year-text--keyboard-selected:hover{background-color:#1d5d90}.react-datepicker__day--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range),.react-datepicker__month-text--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range),.react-datepicker__quarter-text--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range),.react-datepicker__year-text--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range){background-color:rgba(33,107,165,.5)}.react-datepicker__month--selecting-range .react-datepicker__day--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range),.react-datepicker__month--selecting-range .react-datepicker__month-text--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range),.react-datepicker__month--selecting-range .react-datepicker__quarter-text--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range),.react-datepicker__month--selecting-range .react-datepicker__year-text--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range){background-color:#f0f0f0;color:#000}.react-datepicker__day--disabled,.react-datepicker__month-text--disabled,.react-datepicker__quarter-text--disabled,.react-datepicker__year-text--disabled{cursor:default;color:#ccc}.react-datepicker__day--disabled:hover,.react-datepicker__month-text--disabled:hover,.react-datepicker__quarter-text--disabled:hover,.react-datepicker__year-text--disabled:hover{background-color:transparent}.react-datepicker__month-text.react-datepicker__month--in-range:hover,.react-datepicker__month-text.react-datepicker__month--selected:hover,.react-datepicker__month-text.react-datepicker__quarter--in-range:hover,.react-datepicker__month-text.react-datepicker__quarter--selected:hover,.react-datepicker__quarter-text.react-datepicker__month--in-range:hover,.react-datepicker__quarter-text.react-datepicker__month--selected:hover,.react-datepicker__quarter-text.react-datepicker__quarter--in-range:hover,.react-datepicker__quarter-text.react-datepicker__quarter--selected:hover{background-color:#216ba5}.react-datepicker__month-text:hover,.react-datepicker__quarter-text:hover{background-color:#f0f0f0}.react-datepicker__input-container{position:relative;display:inline-block;width:100%}.react-datepicker__month-read-view,.react-datepicker__month-year-read-view,.react-datepicker__year-read-view{border:1px solid transparent;border-radius:.3rem;position:relative}.react-datepicker__month-read-view:hover,.react-datepicker__month-year-read-view:hover,.react-datepicker__year-read-view:hover{cursor:pointer}.react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__month-read-view:hover .react-datepicker__year-read-view--down-arrow,.react-datepicker__month-year-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view:hover .react-datepicker__year-read-view--down-arrow,.react-datepicker__year-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow{border-top-color:#b3b3b3}.react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view--down-arrow,.react-datepicker__year-read-view--down-arrow{-webkit-transform:rotate(135deg);transform:rotate(135deg);right:-16px;top:0}.react-datepicker__month-dropdown,.react-datepicker__month-year-dropdown,.react-datepicker__year-dropdown{background-color:#f0f0f0;position:absolute;width:50%;left:25%;top:30px;z-index:1;text-align:center;border-radius:.3rem;border:1px solid #aeaeae}.react-datepicker__month-dropdown:hover,.react-datepicker__month-year-dropdown:hover,.react-datepicker__year-dropdown:hover{cursor:pointer}.react-datepicker__month-dropdown--scrollable,.react-datepicker__month-year-dropdown--scrollable,.react-datepicker__year-dropdown--scrollable{height:150px;overflow-y:scroll}.react-datepicker__month-option,.react-datepicker__month-year-option,.react-datepicker__year-option{line-height:20px;width:100%;display:block;margin-left:auto;margin-right:auto}.react-datepicker__month-option:first-of-type,.react-datepicker__month-year-option:first-of-type,.react-datepicker__year-option:first-of-type{border-top-left-radius:.3rem;border-top-right-radius:.3rem}.react-datepicker__month-option:last-of-type,.react-datepicker__month-year-option:last-of-type,.react-datepicker__year-option:last-of-type{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-bottom-left-radius:.3rem;border-bottom-right-radius:.3rem}.react-datepicker__month-option:hover,.react-datepicker__month-year-option:hover,.react-datepicker__year-option:hover{background-color:#ccc}.react-datepicker__month-option:hover .react-datepicker__navigation--years-upcoming,.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-upcoming,.react-datepicker__year-option:hover .react-datepicker__navigation--years-upcoming{border-bottom-color:#b3b3b3}.react-datepicker__month-option:hover .react-datepicker__navigation--years-previous,.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-previous,.react-datepicker__year-option:hover .react-datepicker__navigation--years-previous{border-top-color:#b3b3b3}.react-datepicker__month-option--selected,.react-datepicker__month-year-option--selected,.react-datepicker__year-option--selected{position:absolute;left:15px}.react-datepicker__close-icon{cursor:pointer;background-color:transparent;border:0;outline:0;padding:0 6px 0 0;position:absolute;top:0;right:0;height:100%;display:table-cell;vertical-align:middle}.react-datepicker__close-icon:after{cursor:pointer;background-color:#216ba5;color:#fff;border-radius:50%;height:16px;width:16px;padding:2px;font-size:12px;line-height:1;text-align:center;display:table-cell;vertical-align:middle;content:"×"}.react-datepicker__today-button{background:#f0f0f0;border-top:1px solid #aeaeae;cursor:pointer;text-align:center;font-weight:700;padding:5px 0;clear:left}.react-datepicker__portal{position:fixed;width:100vw;height:100vh;background-color:rgba(0,0,0,.8);left:0;top:0;justify-content:center;align-items:center;display:flex;z-index:2147483647}.react-datepicker__portal .react-datepicker__day,.react-datepicker__portal .react-datepicker__day-name,.react-datepicker__portal .react-datepicker__time-name{width:3rem;line-height:3rem}@media (max-height:550px),(max-width:400px){.react-datepicker__portal .react-datepicker__day,.react-datepicker__portal .react-datepicker__day-name,.react-datepicker__portal .react-datepicker__time-name{width:2rem;line-height:2rem}}.react-datepicker__portal .react-datepicker-time__header,.react-datepicker__portal .react-datepicker__current-month{font-size:1.44rem} -/*# sourceMappingURL=2.20fd0a40.chunk.css.map */ \ No newline at end of file diff --git a/web/gui/dashboard/static/css/2.20fd0a40.chunk.css.map b/web/gui/dashboard/static/css/2.20fd0a40.chunk.css.map deleted file mode 100644 index 8a9e5c1bd..000000000 --- a/web/gui/dashboard/static/css/2.20fd0a40.chunk.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["react-datepicker.css","react-filter-box.css","mdc.menu-surface.css","webpack:///packages/mdc-menu-surface/mdc-menu-surface.scss","webpack:///packages/material-components-web/node_modules/@material/elevation/_mixins.scss","webpack:///packages/material-components-web/node_modules/@material/theme/_mixins.scss","webpack:///packages/material-components-web/node_modules/@material/shape/_mixins.scss","webpack:///packages/material-components-web/node_modules/@material/rtl/_mixins.scss","bootstrap-toggle.min.css","index.css","ReactToastify.min.css"],"names":[],"mappings":"AAirBA,gBAcA,CC/rBA,YACE,WACF,CACA,iBACE,WACF,CACA,aACE,aACF,CACA,aACE,aACF,CACA,UACE,aACF,CACA,kBACE,iBAAkB,CAClB,WAAY,CACZ,eAAgB,CAChB,kBAAmB,CACnB,cAAe,CACf,gBAAiB,CACjB,UAAc,CACd,qBAAsB,CAGtB,iBAAkB,CAClB,qBAAyB,CACzB,qBAAyB,CAGzB,2CAAgD,CAChD,0DAA8D,CAG9D,kDACF,CACA,wBACE,oBAAqB,CACrB,SAAU,CACV,wEACF,CACA,wBACE,oBAAqB,CACrB,SAAU,CACV,2CACF,CACA,8BACE,2DACF,CACA,kBACE,WAAY,CACZ,eACF,CACA,mCACE,WACF,CACA,0BACE,kBACF,CACA,sCACE,UACF;;AC9DA;;;;CAIC,CCmCC,kBAwDE,YAAA,CACA,iBAAA,CACA,qBAAA,CACA,4BAAA,CACA,6BAAA,CACA,QAAA,CACA,SAAA,CACA,0BAAA,CAAA,kBAAA,CACA,iCAAA,CAAA,yBAAA,CACA,SAAA,CACA,aAAA,CACA,6BAAA,CACA,SAAA,CAIA,4EAAA,CAAA,oFAAA,CAAA,oEAAA,CAAA,kHAAA,CCrCA,sGAAA,CC+DI,qBAAA,CAAA,8CAAA,CAAA,UAAA,CAAA,sCAAA,CCnGJ,iBAAA,CC6NF,8BAAA,CAEA,gCLjOF,CCgFE,wBAEI,YD/EN,CCoFE,wBAEI,oBAAA,CACA,0BAAA,CAAA,kBAAA,CACA,SDlFN,CCsFE,kCAEI,oBAAA,CACA,2BAAA,CAAA,mBAAA,CACA,SDpFN,CCwFE,oCAEI,oBAAA,CACA,SAAA,CAIA,sCAAA,CAAA,8BD1FN,CKqBI,uDA4LA,+BAAA,CAEA,+BL7MJ,CCXE,0BAEI,iBAAA,CACA,gBDaN,CCTE,yBAEI,cDWN;AMvEA;;;;;;6EAM6E,CAC7E,iDAAiD,iBAAiB,CAAC,gBAAgB,CACnF,QAAQ,iBAAiB,CAAC,eAAe,CACzC,6BAA6B,YAAY,CACzC,cAAc,iBAAiB,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,oBAAoB,CAAC,4BAA4B,CAAC,qBAAqB,CAAC,wBAAwB,CACjK,0BAA0B,UAAU,CACpC,WAA4C,MAAM,CAAC,SAA2C,CAC9F,uBADW,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAkB,QAAQ,CAAC,QAAQ,CAAC,eACgB,CAA/F,YAA6C,QAAQ,CAAC,OAAyC,CAC/F,eAAe,iBAAiB,CAAC,aAAa,CAAC,aAAa,CAAC,gBAAgB,CAAC,WAAW,CAAC,OAAO,CAAC,kBAAkB,CACpH,YAAY,cAAc,CAAC,eAAe,CAC1C,eAAe,kBAAkB,CACjC,gBAAgB,iBAAiB,CACjC,eAAe,cAAc,CAAC,eAAe,CAC7C,kBAAkB,kBAAkB,CACpC,mBAAmB,iBAAiB,CACpC,sBAAsB,UAAU,CAChC,eAAe,cAAc,CAAC,eAAe,CAC7C,kBAAkB,kBAAkB,CACpC,mBAAmB,iBAAiB,CACpC,eAAe,cAAc,CAAC,eAAe,CAC7C,kBAAkB,kBAAkB,CACpC,mBAAmB,iBAAiB,CC1BpC,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,wNAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,gPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,sOAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,8PAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,0NAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,kPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,8NAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,sPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,4NAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,oPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,gOAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,wPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,wNAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,gPAKF,CCpLA,2BAA2B,YAAY,CAAC,oCAAoC,CAAC,cAAc,CAAC,WAAW,CAAC,WAAW,CAAC,qBAAqB,CAAC,UAAU,CAAC,qCAAqC,OAAO,CAAC,QAAQ,CAAC,uCAAuC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,sCAAsC,OAAO,CAAC,SAAS,CAAC,wCAAwC,UAAU,CAAC,QAAQ,CAAC,0CAA0C,UAAU,CAAC,QAAQ,CAAC,kBAAkB,CAAC,yCAAyC,UAAU,CAAC,SAAS,CAAC,yCAAyC,2BAA2B,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,kHAAkH,KAAK,CAAC,2HAA2H,QAAQ,CAAC,gCAAgC,OAAO,CAAC,SAAS,CAAC,CAAC,iBAAiB,iBAAiB,CAAC,eAAe,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,WAAW,CAAC,iBAAiB,CAAC,mEAAmE,CAAqB,YAAY,CAAuB,6BAA6B,CAAC,gBAAgB,CAAC,eAAe,CAAC,sBAAsB,CAAC,cAAc,CAAC,aAAa,CAAC,sBAAsB,aAAa,CAAC,0BAA0B,eAAe,CAAC,UAAU,CAAC,uBAAuB,kBAAkB,CAAC,0BAA0B,kBAAkB,CAAC,0BAA0B,kBAAkB,CAAC,wBAAwB,kBAAkB,CAAC,sBAAsB,aAAa,CAAY,QAAM,CAAC,yCAAyC,iBAAiB,eAAe,CAAC,CAAC,wBAAwB,UAAU,CAAC,eAAe,CAAC,cAAc,CAAC,sBAAsB,CAAC,YAAY,CAAC,WAAW,CAAC,SAAS,CAAC,cAAc,CAAC,UAAU,CAAC,2BAAmB,CAAnB,mBAAmB,CAA2B,qBAAqB,CAAC,iCAAiC,UAAU,CAAC,UAAU,CAAC,4DAA4D,SAAS,CAAC,2CAAmC,GAAG,2BAAkB,CAAlB,mBAAmB,CAAC,GAAG,2BAAkB,CAAlB,mBAAmB,CAAC,CAAjF,mCAAmC,GAAG,2BAAkB,CAAlB,mBAAmB,CAAC,GAAG,2BAAkB,CAAlB,mBAAmB,CAAC,CAAC,wBAAwB,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,mCAAmC,CAAC,6BAAoB,CAApB,qBAAqB,CAAC,kCAAkC,2DAAkD,CAAlD,mDAAmD,CAAC,oCAAoC,wCAAuB,CAAvB,gCAAuB,CAAvB,wBAAuB,CAAvB,8CAAwB,CAAC,6BAA6B,OAAO,CAAC,SAAS,CAAC,8BAAqB,CAArB,sBAAsB,CAAC,iCAAiC,gKAAgF,CAAhF,iFAAiF,CAAC,2CAAmC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAArS,mCAAmC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAAC,4CAAoC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAlI,oCAAoC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAC,0CAAkC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,sCAA6B,CAA7B,8BAA8B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAApS,kCAAkC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,sCAA6B,CAA7B,8BAA8B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAAC,2CAAmC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,CAAjI,mCAAmC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,CAAC,wCAAgC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAA3S,gCAAgC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,yCAAiC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,QAAQ,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,CAAxK,iCAAiC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,QAAQ,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,CAAC,0CAAkC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,sCAA6B,CAA7B,8BAA8B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAApS,kCAAkC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,sCAA6B,CAA7B,8BAA8B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAAC,2CAAmC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,QAAQ,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAzK,mCAAmC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,QAAQ,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAC,uEAAuE,6CAAoC,CAApC,qCAAqC,CAAC,yEAAyE,8CAAqC,CAArC,sCAAsC,CAAC,oCAAoC,6CAAoC,CAApC,qCAAqC,CAAC,uCAAuC,2CAAkC,CAAlC,mCAAmC,CAAC,qEAAqE,8CAAqC,CAArC,sCAAsC,CAAC,uEAAuE,+CAAsC,CAAtC,uCAAuC,CAAC,mCAAmC,4CAAmC,CAAnC,oCAAoC,CAAC,sCAAsC,8CAAqC,CAArC,sCAAsC,CAAC,oCAA4B,GAAG,SAAS,CAAC,mCAA0B,CAA1B,2BAA2B,CAAC,IAAI,SAAS,CAAC,CAAnF,4BAA4B,GAAG,SAAS,CAAC,mCAA0B,CAA1B,2BAA2B,CAAC,IAAI,SAAS,CAAC,CAAC,qCAA6B,GAAG,SAAS,CAAC,IAAI,SAAS,CAAC,mCAA0B,CAA1B,2BAA2B,CAAC,GAAG,SAAS,CAAC,CAAjG,6BAA6B,GAAG,SAAS,CAAC,IAAI,SAAS,CAAC,mCAA0B,CAA1B,2BAA2B,CAAC,GAAG,SAAS,CAAC,CAAC,sBAAsB,uCAA8B,CAA9B,+BAA+B,CAAC,qBAAqB,wCAA+B,CAA/B,gCAAgC,CAAC,oCAA4B,GAAG,mDAA2C,CAA3C,2CAA2C,CAAC,yCAAiC,CAAjC,iCAAiC,CAAC,SAAS,CAAC,IAAI,oDAA4C,CAA5C,4CAA4C,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,mDAA2C,CAA3C,2CAA2C,CAAC,SAAS,CAAC,IAAI,mDAA0C,CAA1C,2CAA2C,CAAC,GAAG,oCAA2B,CAA3B,4BAA4B,CAAC,CAApV,4BAA4B,GAAG,mDAA2C,CAA3C,2CAA2C,CAAC,yCAAiC,CAAjC,iCAAiC,CAAC,SAAS,CAAC,IAAI,oDAA4C,CAA5C,4CAA4C,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,mDAA2C,CAA3C,2CAA2C,CAAC,SAAS,CAAC,IAAI,mDAA0C,CAA1C,2CAA2C,CAAC,GAAG,oCAA2B,CAA3B,4BAA4B,CAAC,CAAC,qCAA6B,GAAG,oCAA2B,CAA3B,4BAA4B,CAAC,IAAI,oDAA4C,CAA5C,4CAA4C,CAAC,SAAS,CAAC,GAAG,mDAA2C,CAA3C,2CAA2C,CAAC,SAAS,CAAC,CAAjL,6BAA6B,GAAG,oCAA2B,CAA3B,4BAA4B,CAAC,IAAI,oDAA4C,CAA5C,4CAA4C,CAAC,SAAS,CAAC,GAAG,mDAA2C,CAA3C,2CAA2C,CAAC,SAAS,CAAC,CAAC,sBAAsB,uCAA8B,CAA9B,+BAA+B,CAAC,qBAAqB,wCAA+B,CAA/B,gCAAgC,CAAC,0CAAkC,GAAG,uCAA+B,CAA/B,+BAA+B,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAnH,kCAAkC,GAAG,uCAA+B,CAA/B,+BAA+B,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,yCAAiC,GAAG,wCAAgC,CAAhC,gCAAgC,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAnH,iCAAiC,GAAG,wCAAgC,CAAhC,gCAAgC,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,uCAA+B,GAAG,uCAA+B,CAA/B,+BAA+B,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAhH,+BAA+B,GAAG,uCAA+B,CAA/B,+BAA+B,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,yCAAiC,GAAG,wCAAgC,CAAhC,gCAAgC,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAnH,iCAAiC,GAAG,wCAAgC,CAAhC,gCAAgC,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,2CAAmC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,CAAnH,mCAAmC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,CAAC,0CAAkC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,CAAnH,kCAAkC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,CAAC,0CAAkC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,CAAnH,kCAAkC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,CAAC,wCAAgC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAlH,gCAAgC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAC,qEAAqE,4CAAmC,CAAnC,oCAAoC,CAAC,uEAAuE,6CAAoC,CAApC,qCAAqC,CAAC,mCAAmC,4CAAmC,CAAnC,oCAAoC,CAAC,sCAAsC,0CAAiC,CAAjC,kCAAkC,CAAC,mEAAmE,6CAAoC,CAApC,qCAAqC,CAAC,qEAAqE,8CAAqC,CAArC,sCAAsC,CAAC,kCAAkC,2CAAkC,CAAlC,mCAAmC,CAAC,qCAAqC,6CAAoC,CAApC,qCAAqC,CVC9gP,2LAKE,iBAAyB,CAAzB,kBAAyB,CAAzB,wBAAyB,CACzB,UAAW,CACX,aAAc,CACd,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,SACF,CACA,uJACE,gBAAiB,CACjB,iBAAkB,CAClB,OACF,CACA,wUACE,sBAAuB,CACvB,iBAAkB,CAElB,QAAS,CACT,SAAU,CACV,UAAW,CACX,UAAW,CACX,4BAAiB,CACjB,SACF,CACA,qKACE,2BACF,CAEA,6EACE,KAAM,CACN,eACF,CACA,uKACE,eAAgB,CAChB,2BACF,CACA,mFACE,KACF,CACA,oFACE,QAAS,CACT,2BACF,CAEA,0EACE,QAAS,CACT,kBACF,CACA,iKACE,kBAAmB,CACnB,qBACF,CACA,gFACE,QACF,CACA,iFACE,WAAY,CACZ,wBACF,CAEA,0BACE,oBAAqB,CACrB,SAAU,CACV,QAAS,CACT,UACF,CAEA,kBACE,qDAA2D,CAC3D,eAAiB,CACjB,qBAAsB,CACtB,UAAW,CACX,wBAAyB,CACzB,mBAAqB,CACrB,oBAAqB,CACrB,iBACF,CAEA,yDACE,SACF,CACA,+DACE,aACF,CACA,8GAEE,+BAAiC,CACjC,gCACF,CAEA,4BACE,iBAAkB,CAClB,SACF,CAEA,yBACE,SACF,CACA,iDACE,gBACF,CACA,6JACE,SAAU,CACV,UACF,CACA,8CACE,mBACF,CACA,gDACE,gBACF,CACA,4EACE,SAAU,CACV,UACF,CACA,+CACE,iBACF,CACA,2EACE,SAAU,CACV,UACF,CAEA,0BACE,iBAAkB,CAClB,wBAAyB,CACzB,+BAAgC,CAChC,4BAA8B,CAC9B,aAAc,CACd,iBACF,CACA,gCACE,kBAAmB,CACnB,gBAAiB,CACjB,iBACF,CACA,2EACE,wBACF,CACA,0EACE,6BACF,CAEA,gUAME,oBAAqB,CACrB,YACF,CAEA,8FAGE,YAAa,CACb,UAAW,CACX,eAAiB,CACjB,iBACF,CAEA,+BACE,sBAAuB,CACvB,kBAAmB,CACnB,eACF,CAEA,8BACE,kBAAmB,CACnB,eAAgB,CAChB,YAAa,CACb,sBAAuB,CACvB,iBAAkB,CAClB,cAAe,CACf,iBAAkB,CAClB,OAAQ,CACR,SAAU,CACV,WAAY,CACZ,SAAU,CACV,WAAY,CACZ,UAAW,CACX,kBAAmB,CACnB,eACF,CACA,wCACE,QACF,CACA,oCACE,SACF,CACA,2GACE,UACF,CACA,qCACE,iBAAkB,CAClB,KAAM,CACN,aAAc,CACd,gBAAiB,CACjB,iBACF,CACA,8CACE,OACF,CACA,8CACE,QACF,CACA,4CACE,oBACF,CAEA,mCACE,iBAAkB,CAClB,QAAS,CACT,cAAe,CACf,OACF,CACA,yCACE,SACF,CACA,gDACE,+BAAwB,CAAxB,uBAAwB,CACxB,SACF,CACA,6CACE,UACF,CACA,oDACE,gCAAyB,CAAzB,wBAAyB,CACzB,UACF,CAEA,mCACE,UACF,CAEA,wBACE,YAAc,CACd,iBACF,CACA,gCACE,YAAa,CACb,cAAe,CACf,eACF,CACA,qDACE,oBAAqB,CACrB,UAAW,CACX,UACF,CAEA,yBACE,YAAc,CACd,iBACF,CACA,gHAEE,oBAAqB,CACrB,UAAW,CACX,UACF,CAEA,wCACE,UAAW,CACX,UAAW,CACX,UAAW,CACX,sBAAuB,CACvB,eACF,CAIA,wJACE,oBACF,CACA,8GACE,oBAAqB,CACrB,gBACF,CACA,oHACE,UACF,CACA,oTAEE,uBAAwB,CACxB,QACF,CACA,+HACE,yBACF,CACA,kHACE,eAAgB,CAChB,oBACF,CAEA,kCACE,WAAY,CACZ,6BAA8B,CAC9B,UACF,CACA,qDACE,cAAe,CACf,wBAAyB,CACzB,mBAAqB,CACrB,iBAAkB,CAClB,WAAY,CACZ,KACF,CACA,0DACE,iBAAkB,CAClB,eAAiB,CACjB,gCACF,CACA,sFACE,UAAW,CACX,iBAAkB,CAClB,aAAc,CACd,iBAAkB,CAClB,gCACF,CACA,qHACE,eAAgB,CAChB,QAAS,CACT,2BAAkC,CAClC,iBAAkB,CAClB,eAAgB,CAChB,cAAe,CACf,UAAW,CACX,sBACF,CACA,yJACE,WAAY,CACZ,gBAAiB,CACjB,kBACF,CACA,+JACE,cAAe,CACf,wBACF,CACA,mKACE,wBAAyB,CACzB,UAAY,CACZ,eACF,CACA,yKACE,wBACF,CACA,mKACE,UACF,CACA,yKACE,cAAe,CACf,4BACF,CAEA,+BACE,UAAW,CACX,oBAAqB,CACrB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,cACF,CACA,wEACE,cACF,CACA,8EACE,mBAAqB,CACrB,wBACF,CAEA,qDAEE,kBACF,CAEA,6BACE,kBACF,CAEA,gFAGE,UAAW,CACX,oBAAqB,CACrB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,cACF,CAEA,4OAIE,mBAAqB,CACrB,wBAAyB,CACzB,UACF,CACA,gRAIE,wBACF,CACA,wEAEE,UAAW,CACX,mBACF,CACA,oFAEE,cAAe,CACf,4BACF,CAEA,kHAIE,cACF,CACA,0IAIE,mBAAqB,CACrB,wBACF,CACA,8IAIE,eACF,CACA,sKAIE,mBAAqB,CACrB,wBAAyB,CACzB,UACF,CACA,8LAIE,wBACF,CACA,0MAIE,UACF,CACA,0MAIE,WACF,CACA,sfAUE,mBAAqB,CACrB,wBAAyB,CACzB,UACF,CACA,8jBAUE,wBACF,CACA,8LAIE,mBAAqB,CACrB,wBAAyB,CACzB,UACF,CACA,sNAIE,wBACF,CACA,8zBAgBE,oCACF,CACA,8lCAgBE,wBAAyB,CACzB,UACF,CACA,0JAIE,cAAe,CACf,UACF,CACA,kLAIE,4BACF,CAEA,gkBAKE,wBACF,CACA,0EAEE,wBACF,CAEA,mCACE,iBAAkB,CAClB,oBAAqB,CACrB,UACF,CAEA,6GAGE,4BAA6B,CAC7B,mBAAqB,CACrB,iBACF,CACA,+HAGE,cACF,CACA,qhBAME,wBACF,CACA,iJAGE,gCAAyB,CAAzB,wBAAyB,CACzB,WAAY,CACZ,KACF,CAEA,0GAGE,wBAAyB,CACzB,iBAAkB,CAClB,SAAU,CACV,QAAS,CACT,QAAS,CACT,SAAU,CACV,iBAAkB,CAClB,mBAAqB,CACrB,wBACF,CACA,4HAGE,cACF,CACA,8IAGE,YAAa,CACb,iBACF,CAEA,oGAGE,gBAAiB,CACjB,UAAW,CACX,aAAc,CACd,gBAAiB,CACjB,iBACF,CACA,8IAGE,4BAA8B,CAC9B,6BACF,CACA,2IAGE,wBAAyB,CACzB,qBAAsB,CACtB,oBAAqB,CACrB,gBAAiB,CACjB,+BAAiC,CACjC,gCACF,CACA,sHAGE,qBACF,CACA,gQAGE,2BACF,CACA,gQAGE,wBACF,CACA,kIAGE,iBAAkB,CAClB,SACF,CAEA,8BACE,cAAe,CACf,4BAA6B,CAC7B,QAAS,CACT,SAAU,CACV,iBAAkB,CAClB,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,WAAY,CACZ,kBAAmB,CACnB,qBACF,CACA,oCACE,cAAe,CACf,wBAAyB,CACzB,UAAW,CACX,iBAAkB,CAClB,WAAY,CACZ,UAAW,CACX,WAAY,CACZ,cAAe,CACf,aAAc,CACd,iBAAkB,CAClB,kBAAmB,CACnB,qBAAsB,CACtB,WACF,CAEA,gCACE,kBAAmB,CACnB,4BAA6B,CAC7B,cAAe,CACf,iBAAkB,CAClB,eAAiB,CACjB,aAAc,CACd,UACF,CAEA,0BACE,cAAe,CACf,WAAY,CACZ,YAAa,CACb,+BAAoC,CACpC,MAAO,CACP,KAAM,CACN,sBAAuB,CACvB,kBAAmB,CACnB,YAAa,CACb,kBACF,CACA,8JAGE,UAAW,CACX,gBACF,CACA,4CACE,8JAGE,UAAW,CACX,gBACF,CACF,CACA,oHAEE,iBACF","file":"2.20fd0a40.chunk.css","sourcesContent":["@charset \"UTF-8\";\n.react-datepicker__year-read-view--down-arrow,\n.react-datepicker__month-read-view--down-arrow,\n.react-datepicker__month-year-read-view--down-arrow, .react-datepicker__navigation-icon::before {\n border-color: #ccc;\n border-style: solid;\n border-width: 3px 3px 0 0;\n content: \"\";\n display: block;\n height: 9px;\n position: absolute;\n top: 6px;\n width: 9px;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle {\n margin-left: -4px;\n position: absolute;\n width: 0;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::after, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::after {\n box-sizing: content-box;\n position: absolute;\n border: 8px solid transparent;\n height: 0;\n width: 1px;\n content: \"\";\n z-index: -1;\n border-width: 8px;\n left: -8px;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before {\n border-bottom-color: #aeaeae;\n}\n\n.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle {\n top: 0;\n margin-top: -8px;\n}\n.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::after {\n border-top: none;\n border-bottom-color: #f0f0f0;\n}\n.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::after {\n top: 0;\n}\n.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before {\n top: -1px;\n border-bottom-color: #aeaeae;\n}\n\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle {\n bottom: 0;\n margin-bottom: -8px;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::after {\n border-bottom: none;\n border-top-color: #fff;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::after {\n bottom: 0;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::before {\n bottom: -1px;\n border-top-color: #aeaeae;\n}\n\n.react-datepicker-wrapper {\n display: inline-block;\n padding: 0;\n border: 0;\n width: 100%;\n}\n\n.react-datepicker {\n font-family: \"Helvetica Neue\", helvetica, arial, sans-serif;\n font-size: 0.8rem;\n background-color: #fff;\n color: #000;\n border: 1px solid #aeaeae;\n border-radius: 0.3rem;\n display: inline-block;\n position: relative;\n}\n\n.react-datepicker--time-only .react-datepicker__triangle {\n left: 35px;\n}\n.react-datepicker--time-only .react-datepicker__time-container {\n border-left: 0;\n}\n.react-datepicker--time-only .react-datepicker__time,\n.react-datepicker--time-only .react-datepicker__time-box {\n border-bottom-left-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.react-datepicker__triangle {\n position: absolute;\n left: 50px;\n}\n\n.react-datepicker-popper {\n z-index: 1;\n}\n.react-datepicker-popper[data-placement^=bottom] {\n padding-top: 10px;\n}\n.react-datepicker-popper[data-placement=bottom-end] .react-datepicker__triangle, .react-datepicker-popper[data-placement=top-end] .react-datepicker__triangle {\n left: auto;\n right: 50px;\n}\n.react-datepicker-popper[data-placement^=top] {\n padding-bottom: 10px;\n}\n.react-datepicker-popper[data-placement^=right] {\n padding-left: 8px;\n}\n.react-datepicker-popper[data-placement^=right] .react-datepicker__triangle {\n left: auto;\n right: 42px;\n}\n.react-datepicker-popper[data-placement^=left] {\n padding-right: 8px;\n}\n.react-datepicker-popper[data-placement^=left] .react-datepicker__triangle {\n left: 42px;\n right: auto;\n}\n\n.react-datepicker__header {\n text-align: center;\n background-color: #f0f0f0;\n border-bottom: 1px solid #aeaeae;\n border-top-left-radius: 0.3rem;\n padding: 8px 0;\n position: relative;\n}\n.react-datepicker__header--time {\n padding-bottom: 8px;\n padding-left: 5px;\n padding-right: 5px;\n}\n.react-datepicker__header--time:not(.react-datepicker__header--time--only) {\n border-top-left-radius: 0;\n}\n.react-datepicker__header:not(.react-datepicker__header--has-time-select) {\n border-top-right-radius: 0.3rem;\n}\n\n.react-datepicker__year-dropdown-container--select,\n.react-datepicker__month-dropdown-container--select,\n.react-datepicker__month-year-dropdown-container--select,\n.react-datepicker__year-dropdown-container--scroll,\n.react-datepicker__month-dropdown-container--scroll,\n.react-datepicker__month-year-dropdown-container--scroll {\n display: inline-block;\n margin: 0 2px;\n}\n\n.react-datepicker__current-month,\n.react-datepicker-time__header,\n.react-datepicker-year-header {\n margin-top: 0;\n color: #000;\n font-weight: bold;\n font-size: 0.944rem;\n}\n\n.react-datepicker-time__header {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.react-datepicker__navigation {\n align-items: center;\n background: none;\n display: flex;\n justify-content: center;\n text-align: center;\n cursor: pointer;\n position: absolute;\n top: 2px;\n padding: 0;\n border: none;\n z-index: 1;\n height: 32px;\n width: 32px;\n text-indent: -999em;\n overflow: hidden;\n}\n.react-datepicker__navigation--previous {\n left: 2px;\n}\n.react-datepicker__navigation--next {\n right: 2px;\n}\n.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button) {\n right: 85px;\n}\n.react-datepicker__navigation--years {\n position: relative;\n top: 0;\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.react-datepicker__navigation--years-previous {\n top: 4px;\n}\n.react-datepicker__navigation--years-upcoming {\n top: -4px;\n}\n.react-datepicker__navigation:hover *::before {\n border-color: #a6a6a6;\n}\n\n.react-datepicker__navigation-icon {\n position: relative;\n top: -1px;\n font-size: 20px;\n width: 0;\n}\n.react-datepicker__navigation-icon--next {\n left: -2px;\n}\n.react-datepicker__navigation-icon--next::before {\n transform: rotate(45deg);\n left: -7px;\n}\n.react-datepicker__navigation-icon--previous {\n right: -2px;\n}\n.react-datepicker__navigation-icon--previous::before {\n transform: rotate(225deg);\n right: -7px;\n}\n\n.react-datepicker__month-container {\n float: left;\n}\n\n.react-datepicker__year {\n margin: 0.4rem;\n text-align: center;\n}\n.react-datepicker__year-wrapper {\n display: flex;\n flex-wrap: wrap;\n max-width: 180px;\n}\n.react-datepicker__year .react-datepicker__year-text {\n display: inline-block;\n width: 4rem;\n margin: 2px;\n}\n\n.react-datepicker__month {\n margin: 0.4rem;\n text-align: center;\n}\n.react-datepicker__month .react-datepicker__month-text,\n.react-datepicker__month .react-datepicker__quarter-text {\n display: inline-block;\n width: 4rem;\n margin: 2px;\n}\n\n.react-datepicker__input-time-container {\n clear: both;\n width: 100%;\n float: left;\n margin: 5px 0 10px 15px;\n text-align: left;\n}\n.react-datepicker__input-time-container .react-datepicker-time__caption {\n display: inline-block;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container {\n display: inline-block;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input {\n display: inline-block;\n margin-left: 10px;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input {\n width: auto;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-inner-spin-button,\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-outer-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time] {\n -moz-appearance: textfield;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__delimiter {\n margin-left: 5px;\n display: inline-block;\n}\n\n.react-datepicker__time-container {\n float: right;\n border-left: 1px solid #aeaeae;\n width: 85px;\n}\n.react-datepicker__time-container--with-today-button {\n display: inline;\n border: 1px solid #aeaeae;\n border-radius: 0.3rem;\n position: absolute;\n right: -72px;\n top: 0;\n}\n.react-datepicker__time-container .react-datepicker__time {\n position: relative;\n background: white;\n border-bottom-right-radius: 0.3rem;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box {\n width: 85px;\n overflow-x: hidden;\n margin: 0 auto;\n text-align: center;\n border-bottom-right-radius: 0.3rem;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list {\n list-style: none;\n margin: 0;\n height: calc(195px + (1.7rem / 2));\n overflow-y: scroll;\n padding-right: 0;\n padding-left: 0;\n width: 100%;\n box-sizing: content-box;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item {\n height: 30px;\n padding: 5px 10px;\n white-space: nowrap;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item:hover {\n cursor: pointer;\n background-color: #f0f0f0;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected {\n background-color: #216ba5;\n color: white;\n font-weight: bold;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected:hover {\n background-color: #216ba5;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled {\n color: #ccc;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled:hover {\n cursor: default;\n background-color: transparent;\n}\n\n.react-datepicker__week-number {\n color: #ccc;\n display: inline-block;\n width: 1.7rem;\n line-height: 1.7rem;\n text-align: center;\n margin: 0.166rem;\n}\n.react-datepicker__week-number.react-datepicker__week-number--clickable {\n cursor: pointer;\n}\n.react-datepicker__week-number.react-datepicker__week-number--clickable:hover {\n border-radius: 0.3rem;\n background-color: #f0f0f0;\n}\n\n.react-datepicker__day-names,\n.react-datepicker__week {\n white-space: nowrap;\n}\n\n.react-datepicker__day-names {\n margin-bottom: -8px;\n}\n\n.react-datepicker__day-name,\n.react-datepicker__day,\n.react-datepicker__time-name {\n color: #000;\n display: inline-block;\n width: 1.7rem;\n line-height: 1.7rem;\n text-align: center;\n margin: 0.166rem;\n}\n\n.react-datepicker__month--selected, .react-datepicker__month--in-selecting-range, .react-datepicker__month--in-range,\n.react-datepicker__quarter--selected,\n.react-datepicker__quarter--in-selecting-range,\n.react-datepicker__quarter--in-range {\n border-radius: 0.3rem;\n background-color: #216ba5;\n color: #fff;\n}\n.react-datepicker__month--selected:hover, .react-datepicker__month--in-selecting-range:hover, .react-datepicker__month--in-range:hover,\n.react-datepicker__quarter--selected:hover,\n.react-datepicker__quarter--in-selecting-range:hover,\n.react-datepicker__quarter--in-range:hover {\n background-color: #1d5d90;\n}\n.react-datepicker__month--disabled,\n.react-datepicker__quarter--disabled {\n color: #ccc;\n pointer-events: none;\n}\n.react-datepicker__month--disabled:hover,\n.react-datepicker__quarter--disabled:hover {\n cursor: default;\n background-color: transparent;\n}\n\n.react-datepicker__day,\n.react-datepicker__month-text,\n.react-datepicker__quarter-text,\n.react-datepicker__year-text {\n cursor: pointer;\n}\n.react-datepicker__day:hover,\n.react-datepicker__month-text:hover,\n.react-datepicker__quarter-text:hover,\n.react-datepicker__year-text:hover {\n border-radius: 0.3rem;\n background-color: #f0f0f0;\n}\n.react-datepicker__day--today,\n.react-datepicker__month-text--today,\n.react-datepicker__quarter-text--today,\n.react-datepicker__year-text--today {\n font-weight: bold;\n}\n.react-datepicker__day--highlighted,\n.react-datepicker__month-text--highlighted,\n.react-datepicker__quarter-text--highlighted,\n.react-datepicker__year-text--highlighted {\n border-radius: 0.3rem;\n background-color: #3dcc4a;\n color: #fff;\n}\n.react-datepicker__day--highlighted:hover,\n.react-datepicker__month-text--highlighted:hover,\n.react-datepicker__quarter-text--highlighted:hover,\n.react-datepicker__year-text--highlighted:hover {\n background-color: #32be3f;\n}\n.react-datepicker__day--highlighted-custom-1,\n.react-datepicker__month-text--highlighted-custom-1,\n.react-datepicker__quarter-text--highlighted-custom-1,\n.react-datepicker__year-text--highlighted-custom-1 {\n color: magenta;\n}\n.react-datepicker__day--highlighted-custom-2,\n.react-datepicker__month-text--highlighted-custom-2,\n.react-datepicker__quarter-text--highlighted-custom-2,\n.react-datepicker__year-text--highlighted-custom-2 {\n color: green;\n}\n.react-datepicker__day--selected, .react-datepicker__day--in-selecting-range, .react-datepicker__day--in-range,\n.react-datepicker__month-text--selected,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--selected,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--selected,\n.react-datepicker__year-text--in-selecting-range,\n.react-datepicker__year-text--in-range {\n border-radius: 0.3rem;\n background-color: #216ba5;\n color: #fff;\n}\n.react-datepicker__day--selected:hover, .react-datepicker__day--in-selecting-range:hover, .react-datepicker__day--in-range:hover,\n.react-datepicker__month-text--selected:hover,\n.react-datepicker__month-text--in-selecting-range:hover,\n.react-datepicker__month-text--in-range:hover,\n.react-datepicker__quarter-text--selected:hover,\n.react-datepicker__quarter-text--in-selecting-range:hover,\n.react-datepicker__quarter-text--in-range:hover,\n.react-datepicker__year-text--selected:hover,\n.react-datepicker__year-text--in-selecting-range:hover,\n.react-datepicker__year-text--in-range:hover {\n background-color: #1d5d90;\n}\n.react-datepicker__day--keyboard-selected,\n.react-datepicker__month-text--keyboard-selected,\n.react-datepicker__quarter-text--keyboard-selected,\n.react-datepicker__year-text--keyboard-selected {\n border-radius: 0.3rem;\n background-color: #2a87d0;\n color: #fff;\n}\n.react-datepicker__day--keyboard-selected:hover,\n.react-datepicker__month-text--keyboard-selected:hover,\n.react-datepicker__quarter-text--keyboard-selected:hover,\n.react-datepicker__year-text--keyboard-selected:hover {\n background-color: #1d5d90;\n}\n.react-datepicker__day--in-selecting-range:not(.react-datepicker__day--in-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--in-range),\n.react-datepicker__month-text--in-selecting-range:not(.react-datepicker__day--in-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--in-range),\n.react-datepicker__quarter-text--in-selecting-range:not(.react-datepicker__day--in-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--in-range),\n.react-datepicker__year-text--in-selecting-range:not(.react-datepicker__day--in-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--in-range) {\n background-color: rgba(33, 107, 165, 0.5);\n}\n.react-datepicker__month--selecting-range .react-datepicker__day--in-range:not(.react-datepicker__day--in-selecting-range,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__year-text--in-selecting-range),\n.react-datepicker__month--selecting-range .react-datepicker__month-text--in-range:not(.react-datepicker__day--in-selecting-range,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__year-text--in-selecting-range),\n.react-datepicker__month--selecting-range .react-datepicker__quarter-text--in-range:not(.react-datepicker__day--in-selecting-range,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__year-text--in-selecting-range),\n.react-datepicker__month--selecting-range .react-datepicker__year-text--in-range:not(.react-datepicker__day--in-selecting-range,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__year-text--in-selecting-range) {\n background-color: #f0f0f0;\n color: #000;\n}\n.react-datepicker__day--disabled,\n.react-datepicker__month-text--disabled,\n.react-datepicker__quarter-text--disabled,\n.react-datepicker__year-text--disabled {\n cursor: default;\n color: #ccc;\n}\n.react-datepicker__day--disabled:hover,\n.react-datepicker__month-text--disabled:hover,\n.react-datepicker__quarter-text--disabled:hover,\n.react-datepicker__year-text--disabled:hover {\n background-color: transparent;\n}\n\n.react-datepicker__month-text.react-datepicker__month--selected:hover, .react-datepicker__month-text.react-datepicker__month--in-range:hover, .react-datepicker__month-text.react-datepicker__quarter--selected:hover, .react-datepicker__month-text.react-datepicker__quarter--in-range:hover,\n.react-datepicker__quarter-text.react-datepicker__month--selected:hover,\n.react-datepicker__quarter-text.react-datepicker__month--in-range:hover,\n.react-datepicker__quarter-text.react-datepicker__quarter--selected:hover,\n.react-datepicker__quarter-text.react-datepicker__quarter--in-range:hover {\n background-color: #216ba5;\n}\n.react-datepicker__month-text:hover,\n.react-datepicker__quarter-text:hover {\n background-color: #f0f0f0;\n}\n\n.react-datepicker__input-container {\n position: relative;\n display: inline-block;\n width: 100%;\n}\n\n.react-datepicker__year-read-view,\n.react-datepicker__month-read-view,\n.react-datepicker__month-year-read-view {\n border: 1px solid transparent;\n border-radius: 0.3rem;\n position: relative;\n}\n.react-datepicker__year-read-view:hover,\n.react-datepicker__month-read-view:hover,\n.react-datepicker__month-year-read-view:hover {\n cursor: pointer;\n}\n.react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow,\n.react-datepicker__year-read-view:hover .react-datepicker__month-read-view--down-arrow,\n.react-datepicker__month-read-view:hover .react-datepicker__year-read-view--down-arrow,\n.react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow,\n.react-datepicker__month-year-read-view:hover .react-datepicker__year-read-view--down-arrow,\n.react-datepicker__month-year-read-view:hover .react-datepicker__month-read-view--down-arrow {\n border-top-color: #b3b3b3;\n}\n.react-datepicker__year-read-view--down-arrow,\n.react-datepicker__month-read-view--down-arrow,\n.react-datepicker__month-year-read-view--down-arrow {\n transform: rotate(135deg);\n right: -16px;\n top: 0;\n}\n\n.react-datepicker__year-dropdown,\n.react-datepicker__month-dropdown,\n.react-datepicker__month-year-dropdown {\n background-color: #f0f0f0;\n position: absolute;\n width: 50%;\n left: 25%;\n top: 30px;\n z-index: 1;\n text-align: center;\n border-radius: 0.3rem;\n border: 1px solid #aeaeae;\n}\n.react-datepicker__year-dropdown:hover,\n.react-datepicker__month-dropdown:hover,\n.react-datepicker__month-year-dropdown:hover {\n cursor: pointer;\n}\n.react-datepicker__year-dropdown--scrollable,\n.react-datepicker__month-dropdown--scrollable,\n.react-datepicker__month-year-dropdown--scrollable {\n height: 150px;\n overflow-y: scroll;\n}\n\n.react-datepicker__year-option,\n.react-datepicker__month-option,\n.react-datepicker__month-year-option {\n line-height: 20px;\n width: 100%;\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.react-datepicker__year-option:first-of-type,\n.react-datepicker__month-option:first-of-type,\n.react-datepicker__month-year-option:first-of-type {\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n.react-datepicker__year-option:last-of-type,\n.react-datepicker__month-option:last-of-type,\n.react-datepicker__month-year-option:last-of-type {\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n border-bottom-left-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n.react-datepicker__year-option:hover,\n.react-datepicker__month-option:hover,\n.react-datepicker__month-year-option:hover {\n background-color: #ccc;\n}\n.react-datepicker__year-option:hover .react-datepicker__navigation--years-upcoming,\n.react-datepicker__month-option:hover .react-datepicker__navigation--years-upcoming,\n.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-upcoming {\n border-bottom-color: #b3b3b3;\n}\n.react-datepicker__year-option:hover .react-datepicker__navigation--years-previous,\n.react-datepicker__month-option:hover .react-datepicker__navigation--years-previous,\n.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-previous {\n border-top-color: #b3b3b3;\n}\n.react-datepicker__year-option--selected,\n.react-datepicker__month-option--selected,\n.react-datepicker__month-year-option--selected {\n position: absolute;\n left: 15px;\n}\n\n.react-datepicker__close-icon {\n cursor: pointer;\n background-color: transparent;\n border: 0;\n outline: 0;\n padding: 0 6px 0 0;\n position: absolute;\n top: 0;\n right: 0;\n height: 100%;\n display: table-cell;\n vertical-align: middle;\n}\n.react-datepicker__close-icon::after {\n cursor: pointer;\n background-color: #216ba5;\n color: #fff;\n border-radius: 50%;\n height: 16px;\n width: 16px;\n padding: 2px;\n font-size: 12px;\n line-height: 1;\n text-align: center;\n display: table-cell;\n vertical-align: middle;\n content: \"×\";\n}\n\n.react-datepicker__today-button {\n background: #f0f0f0;\n border-top: 1px solid #aeaeae;\n cursor: pointer;\n text-align: center;\n font-weight: bold;\n padding: 5px 0;\n clear: left;\n}\n\n.react-datepicker__portal {\n position: fixed;\n width: 100vw;\n height: 100vh;\n background-color: rgba(0, 0, 0, 0.8);\n left: 0;\n top: 0;\n justify-content: center;\n align-items: center;\n display: flex;\n z-index: 2147483647;\n}\n.react-datepicker__portal .react-datepicker__day-name,\n.react-datepicker__portal .react-datepicker__day,\n.react-datepicker__portal .react-datepicker__time-name {\n width: 3rem;\n line-height: 3rem;\n}\n@media (max-width: 400px), (max-height: 550px) {\n .react-datepicker__portal .react-datepicker__day-name,\n.react-datepicker__portal .react-datepicker__day,\n.react-datepicker__portal .react-datepicker__time-name {\n width: 2rem;\n line-height: 2rem;\n }\n}\n.react-datepicker__portal .react-datepicker__current-month,\n.react-datepicker__portal .react-datepicker-time__header {\n font-size: 1.44rem;\n}\n",".CodeMirror {\n height: 25px;\n}\n.ReactCodeMirror {\n height: 30px;\n}\n.cm-category {\n color: #2196F3;\n}\n.cm-operator {\n color: #9E9E9E;\n}\n.cm-value {\n color: #E91E63;\n}\n.react-filter-box {\n overflow-y: hidden;\n height: 28px;\n padding: 4px 6px;\n margin-bottom: 10px;\n font-size: 14px;\n line-height: 20px;\n color: #555555;\n vertical-align: middle;\n -webkit-border-radius: 4px;\n -moz-border-radius: 4px;\n border-radius: 4px;\n background-color: #ffffff;\n border: 1px solid #cccccc;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;\n -moz-transition: border linear 0.2s, box-shadow linear 0.2s;\n -o-transition: border linear 0.2s, box-shadow linear 0.2s;\n transition: border linear 0.2s, box-shadow linear 0.2s;\n}\n.react-filter-box.focus {\n border-color: #66afe9;\n outline: 0;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.react-filter-box.error {\n border-color: #a94442;\n outline: 0;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.react-filter-box.error.focus {\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.CodeMirror-hints {\n padding: 5px;\n min-width: 100px;\n}\n.CodeMirror-hints .CodeMirror-hint {\n padding: 5px 5px;\n}\nli.CodeMirror-hint-active {\n background: #2196F3;\n}\nli.CodeMirror-hint-active .hint-value {\n color: white;\n}\n","/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/\n.mdc-menu-surface {\n display: none;\n position: absolute;\n box-sizing: border-box;\n max-width: calc(100vw - 32px);\n max-height: calc(100vh - 32px);\n margin: 0;\n padding: 0;\n -webkit-transform: scale(1);\n transform: scale(1);\n -webkit-transform-origin: top left;\n transform-origin: top left;\n opacity: 0;\n overflow: auto;\n will-change: transform, opacity;\n z-index: 8;\n transition: opacity 0.03s linear, -webkit-transform 0.12s cubic-bezier(0, 0, 0.2, 1);\n transition: opacity 0.03s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1);\n transition: opacity 0.03s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1), -webkit-transform 0.12s cubic-bezier(0, 0, 0.2, 1);\n box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12);\n background-color: #fff;\n /* @alternate */\n background-color: var(--mdc-theme-surface, #fff);\n color: #000;\n /* @alternate */\n color: var(--mdc-theme-on-surface, #000);\n border-radius: 4px;\n /* @noflip */\n transform-origin-left: top left;\n /* @noflip */\n transform-origin-right: top right;\n}\n.mdc-menu-surface:focus {\n outline: none;\n}\n.mdc-menu-surface--open {\n display: inline-block;\n -webkit-transform: scale(1);\n transform: scale(1);\n opacity: 1;\n}\n.mdc-menu-surface--animating-open {\n display: inline-block;\n -webkit-transform: scale(0.8);\n transform: scale(0.8);\n opacity: 0;\n}\n.mdc-menu-surface--animating-closed {\n display: inline-block;\n opacity: 0;\n transition: opacity 0.075s linear;\n}\n[dir=rtl] .mdc-menu-surface, .mdc-menu-surface[dir=rtl] {\n /* @noflip */\n transform-origin-left: top right;\n /* @noflip */\n transform-origin-right: top left;\n}\n\n.mdc-menu-surface--anchor {\n position: relative;\n overflow: visible;\n}\n\n.mdc-menu-surface--fixed {\n position: fixed;\n}\n/*# sourceMappingURL=mdc.menu-surface.css.map*/","//\n// Copyright 2018 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"@material/theme/mixins\";\n@import \"@material/shape/mixins\";\n@import \"@material/animation/variables\";\n@import \"@material/elevation/mixins\";\n@import \"@material/rtl/mixins\";\n@import \"./variables\";\n\n//\n// Public\n//\n\n@mixin mdc-menu-surface-core-styles($query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n\n // postcss-bem-linter: define menu-surface\n .mdc-menu-surface {\n @include mdc-menu-surface-base_($query);\n @include mdc-elevation($z-value: 8, $query: $query);\n @include mdc-menu-surface-fill-color(surface, $query);\n @include mdc-menu-surface-ink-color(on-surface, $query);\n @include mdc-menu-surface-shape-radius(medium, false, $query);\n\n @include mdc-feature-targets($feat-structure) {\n @include mdc-rtl-reflexive-property(transform-origin, top left, top right);\n }\n }\n\n .mdc-menu-surface--anchor {\n @include mdc-feature-targets($feat-structure) {\n position: relative;\n overflow: visible;\n }\n }\n\n .mdc-menu-surface--fixed {\n @include mdc-feature-targets($feat-structure) {\n position: fixed;\n }\n }\n // postcss-bem-linter: end\n}\n\n@mixin mdc-menu-surface-ink-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $color);\n }\n}\n\n@mixin mdc-menu-surface-fill-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(background-color, $color);\n }\n}\n\n@mixin mdc-menu-surface-shape-radius($radius, $rtl-reflexive: false, $query: mdc-feature-all()) {\n @include mdc-shape-radius($radius, $rtl-reflexive, $query: $query);\n}\n\n//\n// Private\n//\n\n@mixin mdc-menu-surface-base_($query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n $feat-animation: mdc-feature-create-target($query, animation);\n\n @include mdc-feature-targets($feat-structure) {\n display: none;\n position: absolute;\n box-sizing: border-box;\n max-width: calc(100vw - #{$mdc-menu-surface-min-distance-from-edge});\n max-height: calc(100vh - #{$mdc-menu-surface-min-distance-from-edge});\n margin: 0;\n padding: 0;\n transform: scale(1);\n transform-origin: top left;\n opacity: 0;\n overflow: auto;\n will-change: transform, opacity;\n z-index: $mdc-menu-surface-z-index;\n }\n\n @include mdc-feature-targets($feat-animation) {\n transition:\n opacity $mdc-menu-surface-fade-in-duration linear,\n transform $mdc-menu-surface-scale-duration $mdc-animation-deceleration-curve-timing-function;\n }\n\n &:focus {\n @include mdc-feature-targets($feat-structure) {\n outline: none;\n }\n }\n\n // stylelint-disable-next-line selector-max-type\n &--open {\n @include mdc-feature-targets($feat-structure) {\n display: inline-block;\n transform: scale(1);\n opacity: 1;\n }\n }\n\n &--animating-open {\n @include mdc-feature-targets($feat-structure) {\n display: inline-block;\n transform: scale(.8);\n opacity: 0;\n }\n }\n\n &--animating-closed {\n @include mdc-feature-targets($feat-structure) {\n display: inline-block;\n opacity: 0;\n }\n\n @include mdc-feature-targets($feat-animation) {\n transition: opacity $mdc-menu-surface-fade-out-duration linear;\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./packages/mdc-menu-surface/mdc-menu-surface.scss","//\n// Copyright 2017 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"@material/theme/variables\";\n@import \"./variables\";\n\n@mixin mdc-elevation-core-styles($query: mdc-feature-all()) {\n $feat-animation: mdc-feature-create-target($query, animation);\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @for $z-value from 0 through 24 {\n .mdc-elevation--z#{$z-value} {\n @include mdc-elevation($z-value, $query: $query);\n }\n }\n\n .mdc-elevation-transition {\n @include mdc-feature-targets($feat-animation) {\n transition: mdc-elevation-transition-value();\n }\n\n @include mdc-feature-targets($feat-structure) {\n will-change: $mdc-elevation-property;\n }\n }\n}\n\n// Applies the correct CSS rules to an element to give it the elevation specified by $z-value.\n// The $z-value must be between 0 and 24.\n// If $color has an alpha channel, it will be ignored and overridden. To increase the opacity of the shadow, use\n// $opacity-boost.\n@mixin mdc-elevation($z-value, $color: $mdc-elevation-baseline-color, $opacity-boost: 0, $query: mdc-feature-all()) {\n @if type-of($z-value) != number or not unitless($z-value) {\n @error \"$z-value must be a unitless number, but received '#{$z-value}'\";\n }\n\n @if $z-value < 0 or $z-value > 24 {\n @error \"$z-value must be between 0 and 24, but received '#{$z-value}'\";\n }\n\n $feat-color: mdc-feature-create-target($query, color);\n\n $color: mdc-theme-prop-value($color);\n\n $umbra-z-value: map-get($mdc-elevation-umbra-map, $z-value);\n $penumbra-z-value: map-get($mdc-elevation-penumbra-map, $z-value);\n $ambient-z-value: map-get($mdc-elevation-ambient-map, $z-value);\n\n $umbra-color: rgba($color, $mdc-elevation-umbra-opacity + $opacity-boost);\n $penumbra-color: rgba($color, $mdc-elevation-penumbra-opacity + $opacity-boost);\n $ambient-color: rgba($color, $mdc-elevation-ambient-opacity + $opacity-boost);\n\n @include mdc-feature-targets($feat-color) {\n box-shadow:\n #{\"#{$umbra-z-value} #{$umbra-color}\"},\n #{\"#{$penumbra-z-value} #{$penumbra-color}\"},\n #{$ambient-z-value} $ambient-color;\n }\n}\n\n// Returns a string that can be used as the value for a `transition` property for elevation.\n// Calling this function directly is useful in situations where a component needs to transition\n// more than one property.\n//\n// ```scss\n// .foo {\n// transition: mdc-elevation-transition-value(), opacity 100ms ease;\n// will-change: $mdc-elevation-property, opacity;\n// }\n// ```\n@function mdc-elevation-transition-value(\n $duration: $mdc-elevation-transition-duration,\n $easing: $mdc-elevation-transition-timing-function\n) {\n @return #{$mdc-elevation-property} #{$duration} #{$easing};\n}\n\n\n\n// WEBPACK FOOTER //\n// ./packages/material-components-web/node_modules/@material/elevation/_mixins.scss","//\n// Copyright 2017 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"./variables\";\n@import \"./functions\";\n\n@mixin mdc-theme-core-styles($query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n :root {\n @include mdc-feature-targets($feat-color) {\n @each $style in map-keys($mdc-theme-property-values) {\n --mdc-theme-#{$style}: #{map-get($mdc-theme-property-values, $style)};\n }\n }\n }\n\n @each $style in map-keys($mdc-theme-property-values) {\n @if $style != \"background\" and $style != \"surface\" {\n .mdc-theme--#{$style} {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $style, true);\n }\n }\n } @else {\n .mdc-theme--#{$style} {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(background-color, $style);\n }\n }\n }\n }\n\n // CSS rules for using primary and secondary (plus light/dark variants) as background colors.\n @each $style in (\"primary\", \"secondary\") {\n .mdc-theme--#{$style}-bg {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(background-color, $style, true);\n }\n }\n }\n}\n\n// Applies the correct theme color style to the specified property.\n// $property is typically color or background-color, but can be any CSS property that accepts color values.\n// $style should be one of the map keys in $mdc-theme-property-values (_variables.scss), or a color value.\n// $edgeOptOut controls whether to feature-detect around Edge to avoid emitting CSS variables for it,\n// intended for use in cases where interactions with pseudo-element styles cause problems due to Edge bugs.\n@mixin mdc-theme-prop($property, $style, $important: false, $edgeOptOut: false) {\n @if mdc-theme-is-var-with-fallback_($style) {\n @if $important {\n #{$property}: mdc-theme-get-var-fallback_($style) !important;\n /* @alternate */\n #{$property}: mdc-theme-var_($style) !important;\n } @else {\n #{$property}: mdc-theme-get-var-fallback_($style);\n /* @alternate */\n #{$property}: mdc-theme-var_($style);\n }\n } @else if mdc-theme-is-valid-theme-prop-value_($style) {\n @if $important {\n #{$property}: $style !important;\n } @else {\n #{$property}: $style;\n }\n } @else {\n @if not map-has-key($mdc-theme-property-values, $style) {\n @error \"Invalid style: '#{$style}'. Choose one of: #{map-keys($mdc-theme-property-values)}\";\n }\n\n $value: map-get($mdc-theme-property-values, $style);\n\n @if $important {\n #{$property}: $value !important;\n\n @if $edgeOptOut {\n // stylelint-disable max-nesting-depth\n @at-root {\n // IE 11 doesn't understand this syntax and ignores the entire block.\n // Edge understands this syntax and skips the entire block to avoid a nasty :before/:after pseudo-element bug.\n // All other browsers apply the styles within the block.\n @supports not (-ms-ime-align: auto) {\n // stylelint-disable scss/selector-no-redundant-nesting-selector\n & {\n /* @alternate */\n #{$property}: var(--mdc-theme-#{$style}, $value) !important;\n }\n // stylelint-enable scss/selector-no-redundant-nesting-selector\n }\n }\n // stylelint-enable max-nesting-depth\n } @else {\n /* @alternate */\n #{$property}: var(--mdc-theme-#{$style}, $value) !important;\n }\n } @else {\n #{$property}: $value;\n\n @if $edgeOptOut {\n // stylelint-disable max-nesting-depth\n @at-root {\n // IE 11 doesn't understand this syntax and ignores the entire block.\n // Edge understands this syntax and skips the entire block to avoid a nasty :before/:after pseudo-element bug.\n // All other browsers apply the styles within the block.\n @supports not (-ms-ime-align: auto) {\n // stylelint-disable scss/selector-no-redundant-nesting-selector\n & {\n /* @alternate */\n #{$property}: var(--mdc-theme-#{$style}, $value);\n }\n // stylelint-enable scss/selector-no-redundant-nesting-selector\n }\n }\n // stylelint-enable max-nesting-depth\n } @else {\n /* @alternate */\n #{$property}: var(--mdc-theme-#{$style}, $value);\n }\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./packages/material-components-web/node_modules/@material/theme/_mixins.scss","//\n// Copyright 2018 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"./variables\";\n@import \"./functions\";\n\n@mixin mdc-shape-radius($radius, $rtl-reflexive: false, $query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @include mdc-feature-targets($feat-structure) {\n // Even if $rtl-reflexive is true, only emit RTL styles if we can't easily tell that the given radius is symmetrical\n $needs-flip: $rtl-reflexive and length($radius) > 1;\n\n @if ($needs-flip) {\n /* @noflip */\n }\n\n border-radius: mdc-shape-prop-value($radius);\n\n @if ($needs-flip) {\n @include mdc-rtl {\n /* @noflip */\n border-radius: mdc-shape-flip-radius(mdc-shape-prop-value($radius));\n }\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./packages/material-components-web/node_modules/@material/shape/_mixins.scss","//\n// Copyright 2017 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n// Creates a rule that will be applied when an MDC Web component is within the context of an RTL layout.\n//\n// Usage Example:\n//\n// ```scss\n// .mdc-foo {\n// position: absolute;\n// left: 0;\n//\n// @include mdc-rtl {\n// left: auto;\n// right: 0;\n// }\n//\n// &__bar {\n// margin-left: 4px;\n// @include mdc-rtl(\".mdc-foo\") {\n// margin-left: auto;\n// margin-right: 4px;\n// }\n// }\n// }\n//\n// .mdc-foo--mod {\n// padding-left: 4px;\n//\n// @include mdc-rtl {\n// padding-left: auto;\n// padding-right: 4px;\n// }\n// }\n// ```\n//\n// Note that this mixin works by checking for an ancestor element with `[dir=\"rtl\"]`.\n// As a result, nested `dir` values are not supported:\n//\n// ```html\n// \n// \n//
\n//
Styled incorrectly as RTL!
\n//
\n// \n// ```\n//\n// In the future, selectors such as the `:dir` pseudo-class (http://mdn.io/css/:dir) will help us mitigate this.\n@mixin mdc-rtl($root-selector: null) {\n @if ($root-selector) {\n @at-root {\n #{$root-selector}[dir=\"rtl\"] &,\n [dir=\"rtl\"] #{$root-selector} & {\n @content;\n }\n }\n } @else {\n [dir=\"rtl\"] &,\n &[dir=\"rtl\"] {\n @content;\n }\n }\n}\n\n// Takes a base box-model property name (`margin`, `border`, `padding`, etc.) along with a\n// default direction (`left` or `right`) and value, and emits rules which apply the given value to the\n// specified direction by default and the opposite direction in RTL.\n//\n// For example:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive-box(margin, left, 8px);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// margin-left: 8px;\n// margin-right: 0;\n//\n// @include mdc-rtl {\n// margin-left: 0;\n// margin-right: 8px;\n// }\n// }\n// ```\n//\n// whereas:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive-box(margin, right, 8px);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// margin-left: 0;\n// margin-right: 8px;\n//\n// @include mdc-rtl {\n// margin-left: 8px;\n// margin-right: 0;\n// }\n// }\n// ```\n//\n// You can also pass an optional 4th `$root-selector` argument which will be forwarded to `mdc-rtl`,\n// e.g. `@include mdc-rtl-reflexive-box(margin, left, 8px, \".mdc-component\")`.\n//\n// Note that this function will always zero out the original value in an RTL context.\n// If you're trying to flip the values, use `mdc-rtl-reflexive-property()` instead.\n@mixin mdc-rtl-reflexive-box($base-property, $default-direction, $value, $root-selector: null) {\n @if (index((right, left), $default-direction) == null) {\n @error \"Invalid default direction: '#{$default-direction}'. Please specifiy either 'right' or 'left'.\";\n }\n\n $left-value: $value;\n $right-value: 0;\n\n @if ($default-direction == right) {\n $left-value: 0;\n $right-value: $value;\n }\n\n @include mdc-rtl-reflexive-property($base-property, $left-value, $right-value, $root-selector);\n}\n\n// Takes a base property and emits rules that assign -left to and\n// -right to in a LTR context, and vice versa in a RTL context.\n// For example:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive-property(margin, auto, 12px);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// margin-left: auto;\n// margin-right: 12px;\n//\n// @include mdc-rtl {\n// margin-left: 12px;\n// margin-right: auto;\n// }\n// }\n// ```\n//\n// An optional 4th `$root-selector` argument can be given, which will be passed to `mdc-rtl`.\n@mixin mdc-rtl-reflexive-property($base-property, $left-value, $right-value, $root-selector: null) {\n $prop-left: #{$base-property}-left;\n $prop-right: #{$base-property}-right;\n\n @include mdc-rtl-reflexive($prop-left, $left-value, $prop-right, $right-value, $root-selector);\n}\n\n// Takes an argument specifying a horizontal position property (either \"left\" or \"right\") as well\n// as a value, and applies that value to the specified position in a LTR context, and flips it in a\n// RTL context. For example:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive-position(left, 0);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// left: 0;\n// right: initial;\n//\n// @include mdc-rtl {\n// left: initial;\n// right: 0;\n// }\n// }\n// ```\n//\n// An optional third $root-selector argument may also be given, which is passed to `mdc-rtl`.\n@mixin mdc-rtl-reflexive-position($position-property, $value, $root-selector: null) {\n @if (index((right, left), $position-property) == null) {\n @error \"Invalid position #{position-property}. Please specifiy either right or left\";\n }\n\n // TODO: \"initial\" is not supported in IE 11. https://caniuse.com/#feat=css-initial-value\n $left-value: $value;\n $right-value: initial;\n\n @if ($position-property == right) {\n $right-value: $value;\n $left-value: initial;\n }\n\n @include mdc-rtl-reflexive(left, $left-value, right, $right-value, $root-selector);\n}\n\n// Takes pair of properties with values as arguments and flips it in RTL context.\n// For example:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive(left, 2px, right, 5px);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// left: 2px;\n// right: 5px;\n//\n// @include mdc-rtl {\n// right: 2px;\n// left: 5px;\n// }\n// }\n// ```\n//\n// An optional fifth `$root-selector` argument may also be given, which is passed to `mdc-rtl`.\n@mixin mdc-rtl-reflexive(\n $left-property,\n $left-value,\n $right-property,\n $right-value,\n $root-selector: null\n) {\n /* @noflip */\n #{$left-property}: $left-value;\n /* @noflip */\n #{$right-property}: $right-value;\n\n @include mdc-rtl($root-selector) {\n /* @noflip */\n #{$left-property}: $right-value;\n /* @noflip */\n #{$right-property}: $left-value;\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./packages/material-components-web/node_modules/@material/rtl/_mixins.scss","/*! ========================================================================\n * Bootstrap Toggle: bootstrap-toggle.css v2.2.0\n * http://www.bootstraptoggle.com\n * ========================================================================\n * Copyright 2014 Min Hur, The New York Times Company\n * Licensed under MIT\n * ======================================================================== */\n.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-20px;margin-right:5px}\n.toggle{position:relative;overflow:hidden}\n.toggle input[type=checkbox]{display:none}\n.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}\n.toggle.off .toggle-group{left:-100%}\n.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}\n.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}\n.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}\n.toggle.btn{min-width:59px;min-height:34px}\n.toggle-on.btn{padding-right:24px}\n.toggle-off.btn{padding-left:24px}\n.toggle.btn-lg{min-width:79px;min-height:45px}\n.toggle-on.btn-lg{padding-right:31px}\n.toggle-off.btn-lg{padding-left:31px}\n.toggle-handle.btn-lg{width:40px}\n.toggle.btn-sm{min-width:50px;min-height:30px}\n.toggle-on.btn-sm{padding-right:20px}\n.toggle-off.btn-sm{padding-left:20px}\n.toggle.btn-xs{min-width:35px;min-height:22px}\n.toggle-on.btn-xs{padding-right:12px}\n.toggle-off.btn-xs{padding-left:12px}","/* ibm-plex-sans-100normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 100;\n src:\n local('IBM Plex Sans Thin '),\n local('IBM Plex Sans-Thin'),\n url('./files/ibm-plex-sans-latin-100.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-100.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-100italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 100;\n src:\n local('IBM Plex Sans Thin italic'),\n local('IBM Plex Sans-Thinitalic'),\n url('./files/ibm-plex-sans-latin-100italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-100italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-200normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 200;\n src:\n local('IBM Plex Sans Extra Light '),\n local('IBM Plex Sans-Extra Light'),\n url('./files/ibm-plex-sans-latin-200.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-200.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-200italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 200;\n src:\n local('IBM Plex Sans Extra Light italic'),\n local('IBM Plex Sans-Extra Lightitalic'),\n url('./files/ibm-plex-sans-latin-200italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-200italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-300normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 300;\n src:\n local('IBM Plex Sans Light '),\n local('IBM Plex Sans-Light'),\n url('./files/ibm-plex-sans-latin-300.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-300.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-300italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 300;\n src:\n local('IBM Plex Sans Light italic'),\n local('IBM Plex Sans-Lightitalic'),\n url('./files/ibm-plex-sans-latin-300italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-300italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-400normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 400;\n src:\n local('IBM Plex Sans Regular '),\n local('IBM Plex Sans-Regular'),\n url('./files/ibm-plex-sans-latin-400.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-400.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-400italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 400;\n src:\n local('IBM Plex Sans Regular italic'),\n local('IBM Plex Sans-Regularitalic'),\n url('./files/ibm-plex-sans-latin-400italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-400italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-500normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 500;\n src:\n local('IBM Plex Sans Medium '),\n local('IBM Plex Sans-Medium'),\n url('./files/ibm-plex-sans-latin-500.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-500.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-500italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 500;\n src:\n local('IBM Plex Sans Medium italic'),\n local('IBM Plex Sans-Mediumitalic'),\n url('./files/ibm-plex-sans-latin-500italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-500italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-600normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 600;\n src:\n local('IBM Plex Sans SemiBold '),\n local('IBM Plex Sans-SemiBold'),\n url('./files/ibm-plex-sans-latin-600.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-600.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-600italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 600;\n src:\n local('IBM Plex Sans SemiBold italic'),\n local('IBM Plex Sans-SemiBolditalic'),\n url('./files/ibm-plex-sans-latin-600italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-600italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-700normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 700;\n src:\n local('IBM Plex Sans Bold '),\n local('IBM Plex Sans-Bold'),\n url('./files/ibm-plex-sans-latin-700.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-700.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-700italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 700;\n src:\n local('IBM Plex Sans Bold italic'),\n local('IBM Plex Sans-Bolditalic'),\n url('./files/ibm-plex-sans-latin-700italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-700italic.woff') format('woff'); /* Modern Browsers */\n}\n\n",".Toastify__toast-container{z-index:9999;-webkit-transform:translateZ(9999px);position:fixed;padding:4px;width:320px;box-sizing:border-box;color:#fff}.Toastify__toast-container--top-left{top:1em;left:1em}.Toastify__toast-container--top-center{top:1em;left:50%;margin-left:-160px}.Toastify__toast-container--top-right{top:1em;right:1em}.Toastify__toast-container--bottom-left{bottom:1em;left:1em}.Toastify__toast-container--bottom-center{bottom:1em;left:50%;margin-left:-160px}.Toastify__toast-container--bottom-right{bottom:1em;right:1em}@media only screen and (max-width:480px){.Toastify__toast-container{width:100vw;padding:0;left:0;margin:0}.Toastify__toast-container--top-center,.Toastify__toast-container--top-left,.Toastify__toast-container--top-right{top:0}.Toastify__toast-container--bottom-center,.Toastify__toast-container--bottom-left,.Toastify__toast-container--bottom-right{bottom:0}.Toastify__toast-container--rtl{right:0;left:auto}}.Toastify__toast{position:relative;min-height:64px;box-sizing:border-box;margin-bottom:1rem;padding:8px;border-radius:1px;box-shadow:0 1px 10px 0 rgba(0,0,0,.1),0 2px 15px 0 rgba(0,0,0,.05);display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;max-height:800px;overflow:hidden;font-family:sans-serif;cursor:pointer;direction:ltr}.Toastify__toast--rtl{direction:rtl}.Toastify__toast--default{background:#fff;color:#aaa}.Toastify__toast--info{background:#3498db}.Toastify__toast--success{background:#07bc0c}.Toastify__toast--warning{background:#f1c40f}.Toastify__toast--error{background:#e74c3c}.Toastify__toast-body{margin:auto 0;-ms-flex:1;flex:1}@media only screen and (max-width:480px){.Toastify__toast{margin-bottom:0}}.Toastify__close-button{color:#fff;font-weight:700;font-size:14px;background:transparent;outline:none;border:none;padding:0;cursor:pointer;opacity:.7;transition:.3s ease;-ms-flex-item-align:start;align-self:flex-start}.Toastify__close-button--default{color:#000;opacity:.3}.Toastify__close-button:focus,.Toastify__close-button:hover{opacity:1}@keyframes Toastify__trackProgress{0%{transform:scaleX(1)}to{transform:scaleX(0)}}.Toastify__progress-bar{position:absolute;bottom:0;left:0;width:100%;height:5px;z-index:9999;opacity:.7;background-color:hsla(0,0%,100%,.7);transform-origin:left}.Toastify__progress-bar--animated{animation:Toastify__trackProgress linear 1 forwards}.Toastify__progress-bar--controlled{transition:transform .2s}.Toastify__progress-bar--rtl{right:0;left:auto;transform-origin:right}.Toastify__progress-bar--default{background:linear-gradient(90deg,#4cd964,#5ac8fa,#007aff,#34aadc,#5856d6,#ff2d55)}@keyframes Toastify__bounceInRight{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(3000px,0,0)}60%{opacity:1;transform:translate3d(-25px,0,0)}75%{transform:translate3d(10px,0,0)}90%{transform:translate3d(-5px,0,0)}to{transform:none}}@keyframes Toastify__bounceOutRight{20%{opacity:1;transform:translate3d(-20px,0,0)}to{opacity:0;transform:translate3d(2000px,0,0)}}@keyframes Toastify__bounceInLeft{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(-3000px,0,0)}60%{opacity:1;transform:translate3d(25px,0,0)}75%{transform:translate3d(-10px,0,0)}90%{transform:translate3d(5px,0,0)}to{transform:none}}@keyframes Toastify__bounceOutLeft{20%{opacity:1;transform:translate3d(20px,0,0)}to{opacity:0;transform:translate3d(-2000px,0,0)}}@keyframes Toastify__bounceInUp{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(0,3000px,0)}60%{opacity:1;transform:translate3d(0,-20px,0)}75%{transform:translate3d(0,10px,0)}90%{transform:translate3d(0,-5px,0)}to{transform:translateZ(0)}}@keyframes Toastify__bounceOutUp{20%{transform:translate3d(0,-10px,0)}40%,45%{opacity:1;transform:translate3d(0,20px,0)}to{opacity:0;transform:translate3d(0,-2000px,0)}}@keyframes Toastify__bounceInDown{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(0,-3000px,0)}60%{opacity:1;transform:translate3d(0,25px,0)}75%{transform:translate3d(0,-10px,0)}90%{transform:translate3d(0,5px,0)}to{transform:none}}@keyframes Toastify__bounceOutDown{20%{transform:translate3d(0,10px,0)}40%,45%{opacity:1;transform:translate3d(0,-20px,0)}to{opacity:0;transform:translate3d(0,2000px,0)}}.Toastify__bounce-enter--bottom-left,.Toastify__bounce-enter--top-left{animation-name:Toastify__bounceInLeft}.Toastify__bounce-enter--bottom-right,.Toastify__bounce-enter--top-right{animation-name:Toastify__bounceInRight}.Toastify__bounce-enter--top-center{animation-name:Toastify__bounceInDown}.Toastify__bounce-enter--bottom-center{animation-name:Toastify__bounceInUp}.Toastify__bounce-exit--bottom-left,.Toastify__bounce-exit--top-left{animation-name:Toastify__bounceOutLeft}.Toastify__bounce-exit--bottom-right,.Toastify__bounce-exit--top-right{animation-name:Toastify__bounceOutRight}.Toastify__bounce-exit--top-center{animation-name:Toastify__bounceOutUp}.Toastify__bounce-exit--bottom-center{animation-name:Toastify__bounceOutDown}@keyframes Toastify__zoomIn{0%{opacity:0;transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes Toastify__zoomOut{0%{opacity:1}50%{opacity:0;transform:scale3d(.3,.3,.3)}to{opacity:0}}.Toastify__zoom-enter{animation-name:Toastify__zoomIn}.Toastify__zoom-exit{animation-name:Toastify__zoomOut}@keyframes Toastify__flipIn{0%{transform:perspective(400px) rotateX(90deg);animation-timing-function:ease-in;opacity:0}40%{transform:perspective(400px) rotateX(-20deg);animation-timing-function:ease-in}60%{transform:perspective(400px) rotateX(10deg);opacity:1}80%{transform:perspective(400px) rotateX(-5deg)}to{transform:perspective(400px)}}@keyframes Toastify__flipOut{0%{transform:perspective(400px)}30%{transform:perspective(400px) rotateX(-20deg);opacity:1}to{transform:perspective(400px) rotateX(90deg);opacity:0}}.Toastify__flip-enter{animation-name:Toastify__flipIn}.Toastify__flip-exit{animation-name:Toastify__flipOut}@keyframes Toastify__slideInRight{0%{transform:translate3d(110%,0,0);visibility:visible}to{transform:translateZ(0)}}@keyframes Toastify__slideInLeft{0%{transform:translate3d(-110%,0,0);visibility:visible}to{transform:translateZ(0)}}@keyframes Toastify__slideInUp{0%{transform:translate3d(0,110%,0);visibility:visible}to{transform:translateZ(0)}}@keyframes Toastify__slideInDown{0%{transform:translate3d(0,-110%,0);visibility:visible}to{transform:translateZ(0)}}@keyframes Toastify__slideOutRight{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(110%,0,0)}}@keyframes Toastify__slideOutLeft{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(-110%,0,0)}}@keyframes Toastify__slideOutDown{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(0,500px,0)}}@keyframes Toastify__slideOutUp{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(0,-500px,0)}}.Toastify__slide-enter--bottom-left,.Toastify__slide-enter--top-left{animation-name:Toastify__slideInLeft}.Toastify__slide-enter--bottom-right,.Toastify__slide-enter--top-right{animation-name:Toastify__slideInRight}.Toastify__slide-enter--top-center{animation-name:Toastify__slideInDown}.Toastify__slide-enter--bottom-center{animation-name:Toastify__slideInUp}.Toastify__slide-exit--bottom-left,.Toastify__slide-exit--top-left{animation-name:Toastify__slideOutLeft}.Toastify__slide-exit--bottom-right,.Toastify__slide-exit--top-right{animation-name:Toastify__slideOutRight}.Toastify__slide-exit--top-center{animation-name:Toastify__slideOutUp}.Toastify__slide-exit--bottom-center{animation-name:Toastify__slideOutDown}"]} \ No newline at end of file diff --git a/web/gui/dashboard/static/css/2.c454aab8.chunk.css b/web/gui/dashboard/static/css/2.c454aab8.chunk.css new file mode 100644 index 000000000..3daf7f083 --- /dev/null +++ b/web/gui/dashboard/static/css/2.c454aab8.chunk.css @@ -0,0 +1,15 @@ +@charset "UTF-8";.CodeMirror{height:25px}.ReactCodeMirror{height:30px}.cm-category{color:#2196f3}.cm-operator{color:#9e9e9e}.cm-value{color:#e91e63}.react-filter-box{overflow-y:hidden;height:28px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;border-radius:4px;background-color:#fff;border:1px solid #ccc;box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border .2s linear,box-shadow .2s linear;transition:border .2s linear,box-shadow .2s linear}.react-filter-box.focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.react-filter-box.error{border-color:#a94442;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.react-filter-box.error.focus{box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.CodeMirror-hints{padding:5px;min-width:100px}.CodeMirror-hints .CodeMirror-hint{padding:5px}li.CodeMirror-hint-active{background:#2196f3}li.CodeMirror-hint-active .hint-value{color:#fff} + +/*! + Material Components for the Web + Copyright (c) 2019 Google Inc. + License: MIT +*/.mdc-menu-surface{display:none;position:absolute;box-sizing:border-box;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);margin:0;padding:0;-webkit-transform:scale(1);transform:scale(1);-webkit-transform-origin:top left;transform-origin:top left;opacity:0;overflow:auto;will-change:transform,opacity;z-index:8;transition:opacity .03s linear,-webkit-transform .12s cubic-bezier(0,0,.2,1);-webkit-transition:opacity .03s linear,-webkit-transform .12s cubic-bezier(0,0,.2,1);transition:opacity .03s linear,transform .12s cubic-bezier(0,0,.2,1);transition:opacity .03s linear,transform .12s cubic-bezier(0,0,.2,1),-webkit-transform .12s cubic-bezier(0,0,.2,1);box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12);background-color:#fff;background-color:var(--mdc-theme-surface,#fff);color:#000;color:var(--mdc-theme-on-surface,#000);border-radius:4px;transform-origin-left:top left;transform-origin-right:top right}.mdc-menu-surface:focus{outline:none}.mdc-menu-surface--open{display:inline-block;-webkit-transform:scale(1);transform:scale(1);opacity:1}.mdc-menu-surface--animating-open{display:inline-block;-webkit-transform:scale(.8);transform:scale(.8);opacity:0}.mdc-menu-surface--animating-closed{display:inline-block;opacity:0;-webkit-transition:opacity 75ms linear;transition:opacity 75ms linear}.mdc-menu-surface[dir=rtl],[dir=rtl] .mdc-menu-surface{transform-origin-left:top right;transform-origin-right:top left}.mdc-menu-surface--anchor{position:relative;overflow:visible}.mdc-menu-surface--fixed{position:fixed} +/*! ======================================================================== + * Bootstrap Toggle: bootstrap-toggle.css v2.2.0 + * http://www.bootstraptoggle.com + * ======================================================================== + * Copyright 2014 Min Hur, The New York Times Company + * Licensed under MIT + * ======================================================================== */.checkbox-inline .toggle,.checkbox label .toggle{margin-left:-20px;margin-right:5px}.toggle{position:relative;overflow:hidden}.toggle input[type=checkbox]{display:none}.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}.toggle.off .toggle-group{left:-100%}.toggle-on{left:0;right:50%}.toggle-off,.toggle-on{position:absolute;top:0;bottom:0;margin:0;border:0;border-radius:0}.toggle-off{left:50%;right:0}.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}.toggle.btn{min-width:59px;min-height:34px}.toggle-on.btn{padding-right:24px}.toggle-off.btn{padding-left:24px}.toggle.btn-lg{min-width:79px;min-height:45px}.toggle-on.btn-lg{padding-right:31px}.toggle-off.btn-lg{padding-left:31px}.toggle-handle.btn-lg{width:40px}.toggle.btn-sm{min-width:50px;min-height:30px}.toggle-on.btn-sm{padding-right:20px}.toggle-off.btn-sm{padding-left:20px}.toggle.btn-xs{min-width:35px;min-height:22px}.toggle-on.btn-xs{padding-right:12px}.toggle-off.btn-xs{padding-left:12px}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:100;src:local("IBM Plex Sans Thin "),local("IBM Plex Sans-Thin"),url(../../static/media/ibm-plex-sans-latin-100.245539db.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-100.9a582f3a.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:100;src:local("IBM Plex Sans Thin italic"),local("IBM Plex Sans-Thinitalic"),url(../../static/media/ibm-plex-sans-latin-100italic.3c34cf08.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-100italic.1ea7c5d2.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:200;src:local("IBM Plex Sans Extra Light "),local("IBM Plex Sans-Extra Light"),url(../../static/media/ibm-plex-sans-latin-200.bf72c841.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-200.67524c36.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:200;src:local("IBM Plex Sans Extra Light italic"),local("IBM Plex Sans-Extra Lightitalic"),url(../../static/media/ibm-plex-sans-latin-200italic.bbc2d552.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-200italic.52df2560.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:300;src:local("IBM Plex Sans Light "),local("IBM Plex Sans-Light"),url(../../static/media/ibm-plex-sans-latin-300.9e1c48af.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-300.10bb6a0a.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:300;src:local("IBM Plex Sans Light italic"),local("IBM Plex Sans-Lightitalic"),url(../../static/media/ibm-plex-sans-latin-300italic.c76f2ab5.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-300italic.d3566d5b.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:400;src:local("IBM Plex Sans Regular "),local("IBM Plex Sans-Regular"),url(../../static/media/ibm-plex-sans-latin-400.263d6267.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-400.a2c56f94.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:400;src:local("IBM Plex Sans Regular italic"),local("IBM Plex Sans-Regularitalic"),url(../../static/media/ibm-plex-sans-latin-400italic.89a93a1b.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-400italic.272f8611.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:500;src:local("IBM Plex Sans Medium "),local("IBM Plex Sans-Medium"),url(../../static/media/ibm-plex-sans-latin-500.0866c244.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-500.f6d5c5d5.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:500;src:local("IBM Plex Sans Medium italic"),local("IBM Plex Sans-Mediumitalic"),url(../../static/media/ibm-plex-sans-latin-500italic.ffd12d59.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-500italic.ccd41bd1.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:600;src:local("IBM Plex Sans SemiBold "),local("IBM Plex Sans-SemiBold"),url(../../static/media/ibm-plex-sans-latin-600.7852d4dc.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-600.337b1651.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:600;src:local("IBM Plex Sans SemiBold italic"),local("IBM Plex Sans-SemiBolditalic"),url(../../static/media/ibm-plex-sans-latin-600italic.17e5379f.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-600italic.6f4ba6aa.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-display:swap;font-weight:700;src:local("IBM Plex Sans Bold "),local("IBM Plex Sans-Bold"),url(../../static/media/ibm-plex-sans-latin-700.c9983d3d.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-700.b8809d61.woff) format("woff")}@font-face{font-family:IBM Plex Sans;font-style:italic;font-display:swap;font-weight:700;src:local("IBM Plex Sans Bold italic"),local("IBM Plex Sans-Bolditalic"),url(../../static/media/ibm-plex-sans-latin-700italic.02954bee.woff2) format("woff2"),url(../../static/media/ibm-plex-sans-latin-700italic.72e9af40.woff) format("woff")}.Toastify__toast-container{z-index:9999;-webkit-transform:translateZ(9999px);position:fixed;padding:4px;width:320px;box-sizing:border-box;color:#fff}.Toastify__toast-container--top-left{top:1em;left:1em}.Toastify__toast-container--top-center{top:1em;left:50%;margin-left:-160px}.Toastify__toast-container--top-right{top:1em;right:1em}.Toastify__toast-container--bottom-left{bottom:1em;left:1em}.Toastify__toast-container--bottom-center{bottom:1em;left:50%;margin-left:-160px}.Toastify__toast-container--bottom-right{bottom:1em;right:1em}@media only screen and (max-width:480px){.Toastify__toast-container{width:100vw;padding:0;left:0;margin:0}.Toastify__toast-container--top-center,.Toastify__toast-container--top-left,.Toastify__toast-container--top-right{top:0}.Toastify__toast-container--bottom-center,.Toastify__toast-container--bottom-left,.Toastify__toast-container--bottom-right{bottom:0}.Toastify__toast-container--rtl{right:0;left:auto}}.Toastify__toast{position:relative;min-height:64px;box-sizing:border-box;margin-bottom:1rem;padding:8px;border-radius:1px;box-shadow:0 1px 10px 0 rgba(0,0,0,.1),0 2px 15px 0 rgba(0,0,0,.05);display:flex;justify-content:space-between;max-height:800px;overflow:hidden;font-family:sans-serif;cursor:pointer;direction:ltr}.Toastify__toast--rtl{direction:rtl}.Toastify__toast--default{background:#fff;color:#aaa}.Toastify__toast--info{background:#3498db}.Toastify__toast--success{background:#07bc0c}.Toastify__toast--warning{background:#f1c40f}.Toastify__toast--error{background:#e74c3c}.Toastify__toast-body{margin:auto 0;flex:1 1}@media only screen and (max-width:480px){.Toastify__toast{margin-bottom:0}}.Toastify__close-button{color:#fff;font-weight:700;font-size:14px;background:transparent;outline:none;border:none;padding:0;cursor:pointer;opacity:.7;-webkit-transition:.3s ease;transition:.3s ease;align-self:flex-start}.Toastify__close-button--default{color:#000;opacity:.3}.Toastify__close-button:focus,.Toastify__close-button:hover{opacity:1}@-webkit-keyframes Toastify__trackProgress{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}to{-webkit-transform:scaleX(0);transform:scaleX(0)}}@keyframes Toastify__trackProgress{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}to{-webkit-transform:scaleX(0);transform:scaleX(0)}}.Toastify__progress-bar{position:absolute;bottom:0;left:0;width:100%;height:5px;z-index:9999;opacity:.7;background-color:hsla(0,0%,100%,.7);-webkit-transform-origin:left;transform-origin:left}.Toastify__progress-bar--animated{-webkit-animation:Toastify__trackProgress linear 1 forwards;animation:Toastify__trackProgress linear 1 forwards}.Toastify__progress-bar--controlled{-webkit-transition:-webkit-transform .2s;transition:-webkit-transform .2s;transition:transform .2s;transition:transform .2s,-webkit-transform .2s}.Toastify__progress-bar--rtl{right:0;left:auto;-webkit-transform-origin:right;transform-origin:right}.Toastify__progress-bar--default{background:-webkit-gradient(linear,left top,right top,from(#4cd964),color-stop(#5ac8fa),color-stop(#007aff),color-stop(#34aadc),color-stop(#5856d6),to(#ff2d55));background:linear-gradient(90deg,#4cd964,#5ac8fa,#007aff,#34aadc,#5856d6,#ff2d55)}@-webkit-keyframes Toastify__bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes Toastify__bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}@-webkit-keyframes Toastify__bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes Toastify__bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@-webkit-keyframes Toastify__bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes Toastify__bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}@-webkit-keyframes Toastify__bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes Toastify__bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@-webkit-keyframes Toastify__bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes Toastify__bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@-webkit-keyframes Toastify__bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}@keyframes Toastify__bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}@-webkit-keyframes Toastify__bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes Toastify__bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.Toastify__bounce-enter--bottom-left,.Toastify__bounce-enter--top-left{-webkit-animation-name:Toastify__bounceInLeft;animation-name:Toastify__bounceInLeft}.Toastify__bounce-enter--bottom-right,.Toastify__bounce-enter--top-right{-webkit-animation-name:Toastify__bounceInRight;animation-name:Toastify__bounceInRight}.Toastify__bounce-enter--top-center{-webkit-animation-name:Toastify__bounceInDown;animation-name:Toastify__bounceInDown}.Toastify__bounce-enter--bottom-center{-webkit-animation-name:Toastify__bounceInUp;animation-name:Toastify__bounceInUp}.Toastify__bounce-exit--bottom-left,.Toastify__bounce-exit--top-left{-webkit-animation-name:Toastify__bounceOutLeft;animation-name:Toastify__bounceOutLeft}.Toastify__bounce-exit--bottom-right,.Toastify__bounce-exit--top-right{-webkit-animation-name:Toastify__bounceOutRight;animation-name:Toastify__bounceOutRight}.Toastify__bounce-exit--top-center{-webkit-animation-name:Toastify__bounceOutUp;animation-name:Toastify__bounceOutUp}.Toastify__bounce-exit--bottom-center{-webkit-animation-name:Toastify__bounceOutDown;animation-name:Toastify__bounceOutDown}@-webkit-keyframes Toastify__zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes Toastify__zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@-webkit-keyframes Toastify__zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@keyframes Toastify__zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}.Toastify__zoom-enter{-webkit-animation-name:Toastify__zoomIn;animation-name:Toastify__zoomIn}.Toastify__zoom-exit{-webkit-animation-name:Toastify__zoomOut;animation-name:Toastify__zoomOut}@-webkit-keyframes Toastify__flipIn{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes Toastify__flipIn{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@-webkit-keyframes Toastify__flipOut{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}@keyframes Toastify__flipOut{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}.Toastify__flip-enter{-webkit-animation-name:Toastify__flipIn;animation-name:Toastify__flipIn}.Toastify__flip-exit{-webkit-animation-name:Toastify__flipOut;animation-name:Toastify__flipOut}@-webkit-keyframes Toastify__slideInRight{0%{-webkit-transform:translate3d(110%,0,0);transform:translate3d(110%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__slideInRight{0%{-webkit-transform:translate3d(110%,0,0);transform:translate3d(110%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__slideInLeft{0%{-webkit-transform:translate3d(-110%,0,0);transform:translate3d(-110%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__slideInLeft{0%{-webkit-transform:translate3d(-110%,0,0);transform:translate3d(-110%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__slideInUp{0%{-webkit-transform:translate3d(0,110%,0);transform:translate3d(0,110%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__slideInUp{0%{-webkit-transform:translate3d(0,110%,0);transform:translate3d(0,110%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__slideInDown{0%{-webkit-transform:translate3d(0,-110%,0);transform:translate3d(0,-110%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes Toastify__slideInDown{0%{-webkit-transform:translate3d(0,-110%,0);transform:translate3d(0,-110%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes Toastify__slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(110%,0,0);transform:translate3d(110%,0,0)}}@keyframes Toastify__slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(110%,0,0);transform:translate3d(110%,0,0)}}@-webkit-keyframes Toastify__slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-110%,0,0);transform:translate3d(-110%,0,0)}}@keyframes Toastify__slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-110%,0,0);transform:translate3d(-110%,0,0)}}@-webkit-keyframes Toastify__slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,500px,0);transform:translate3d(0,500px,0)}}@keyframes Toastify__slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,500px,0);transform:translate3d(0,500px,0)}}@-webkit-keyframes Toastify__slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-500px,0);transform:translate3d(0,-500px,0)}}@keyframes Toastify__slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-500px,0);transform:translate3d(0,-500px,0)}}.Toastify__slide-enter--bottom-left,.Toastify__slide-enter--top-left{-webkit-animation-name:Toastify__slideInLeft;animation-name:Toastify__slideInLeft}.Toastify__slide-enter--bottom-right,.Toastify__slide-enter--top-right{-webkit-animation-name:Toastify__slideInRight;animation-name:Toastify__slideInRight}.Toastify__slide-enter--top-center{-webkit-animation-name:Toastify__slideInDown;animation-name:Toastify__slideInDown}.Toastify__slide-enter--bottom-center{-webkit-animation-name:Toastify__slideInUp;animation-name:Toastify__slideInUp}.Toastify__slide-exit--bottom-left,.Toastify__slide-exit--top-left{-webkit-animation-name:Toastify__slideOutLeft;animation-name:Toastify__slideOutLeft}.Toastify__slide-exit--bottom-right,.Toastify__slide-exit--top-right{-webkit-animation-name:Toastify__slideOutRight;animation-name:Toastify__slideOutRight}.Toastify__slide-exit--top-center{-webkit-animation-name:Toastify__slideOutUp;animation-name:Toastify__slideOutUp}.Toastify__slide-exit--bottom-center{-webkit-animation-name:Toastify__slideOutDown;animation-name:Toastify__slideOutDown}.react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view--down-arrow,.react-datepicker__navigation-icon:before,.react-datepicker__year-read-view--down-arrow{border-color:#ccc;border-style:solid;border-width:3px 3px 0 0;content:"";display:block;height:9px;position:absolute;top:6px;width:9px}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle{margin-left:-4px;position:absolute;width:0}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{box-sizing:content-box;position:absolute;height:0;width:1px;content:"";z-index:-1;border:8px solid transparent;left:-8px}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{border-bottom-color:#aeaeae}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle{top:0;margin-top:-8px}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before{border-top:none;border-bottom-color:#f0f0f0}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after{top:0}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before{top:-1px;border-bottom-color:#aeaeae}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle{bottom:0;margin-bottom:-8px}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{border-bottom:none;border-top-color:#fff}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after{bottom:0}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{bottom:-1px;border-top-color:#aeaeae}.react-datepicker-wrapper{display:inline-block;padding:0;border:0;width:100%}.react-datepicker{font-family:Helvetica Neue,helvetica,arial,sans-serif;font-size:.8rem;background-color:#fff;color:#000;border:1px solid #aeaeae;border-radius:.3rem;display:inline-block;position:relative}.react-datepicker--time-only .react-datepicker__triangle{left:35px}.react-datepicker--time-only .react-datepicker__time-container{border-left:0}.react-datepicker--time-only .react-datepicker__time,.react-datepicker--time-only .react-datepicker__time-box{border-bottom-left-radius:.3rem;border-bottom-right-radius:.3rem}.react-datepicker__triangle{position:absolute;left:50px}.react-datepicker-popper{z-index:1}.react-datepicker-popper[data-placement^=bottom]{padding-top:10px}.react-datepicker-popper[data-placement=bottom-end] .react-datepicker__triangle,.react-datepicker-popper[data-placement=top-end] .react-datepicker__triangle{left:auto;right:50px}.react-datepicker-popper[data-placement^=top]{padding-bottom:10px}.react-datepicker-popper[data-placement^=right]{padding-left:8px}.react-datepicker-popper[data-placement^=right] .react-datepicker__triangle{left:auto;right:42px}.react-datepicker-popper[data-placement^=left]{padding-right:8px}.react-datepicker-popper[data-placement^=left] .react-datepicker__triangle{left:42px;right:auto}.react-datepicker__header{text-align:center;background-color:#f0f0f0;border-bottom:1px solid #aeaeae;border-top-left-radius:.3rem;padding:8px 0;position:relative}.react-datepicker__header--time{padding-bottom:8px;padding-left:5px;padding-right:5px}.react-datepicker__header--time:not(.react-datepicker__header--time--only){border-top-left-radius:0}.react-datepicker__header:not(.react-datepicker__header--has-time-select){border-top-right-radius:.3rem}.react-datepicker__month-dropdown-container--scroll,.react-datepicker__month-dropdown-container--select,.react-datepicker__month-year-dropdown-container--scroll,.react-datepicker__month-year-dropdown-container--select,.react-datepicker__year-dropdown-container--scroll,.react-datepicker__year-dropdown-container--select{display:inline-block;margin:0 2px}.react-datepicker-time__header,.react-datepicker-year-header,.react-datepicker__current-month{margin-top:0;color:#000;font-weight:700;font-size:.944rem}.react-datepicker-time__header{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.react-datepicker__navigation{align-items:center;background:none;display:flex;justify-content:center;text-align:center;cursor:pointer;position:absolute;top:2px;padding:0;border:none;z-index:1;height:32px;width:32px;text-indent:-999em;overflow:hidden}.react-datepicker__navigation--previous{left:2px}.react-datepicker__navigation--next{right:2px}.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button){right:85px}.react-datepicker__navigation--years{position:relative;top:0;display:block;margin-left:auto;margin-right:auto}.react-datepicker__navigation--years-previous{top:4px}.react-datepicker__navigation--years-upcoming{top:-4px}.react-datepicker__navigation:hover :before{border-color:#a6a6a6}.react-datepicker__navigation-icon{position:relative;top:-1px;font-size:20px;width:0}.react-datepicker__navigation-icon--next{left:-2px}.react-datepicker__navigation-icon--next:before{-webkit-transform:rotate(45deg);transform:rotate(45deg);left:-7px}.react-datepicker__navigation-icon--previous{right:-2px}.react-datepicker__navigation-icon--previous:before{-webkit-transform:rotate(225deg);transform:rotate(225deg);right:-7px}.react-datepicker__month-container{float:left}.react-datepicker__year{margin:.4rem;text-align:center}.react-datepicker__year-wrapper{display:flex;flex-wrap:wrap;max-width:180px}.react-datepicker__year .react-datepicker__year-text{display:inline-block;width:4rem;margin:2px}.react-datepicker__month{margin:.4rem;text-align:center}.react-datepicker__month .react-datepicker__month-text,.react-datepicker__month .react-datepicker__quarter-text{display:inline-block;width:4rem;margin:2px}.react-datepicker__input-time-container{clear:both;width:100%;float:left;margin:5px 0 10px 15px;text-align:left}.react-datepicker__input-time-container .react-datepicker-time__caption,.react-datepicker__input-time-container .react-datepicker-time__input-container{display:inline-block}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input{display:inline-block;margin-left:10px}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input{width:auto}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-inner-spin-button,.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]{-moz-appearance:textfield}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__delimiter{margin-left:5px;display:inline-block}.react-datepicker__time-container{float:right;border-left:1px solid #aeaeae;width:85px}.react-datepicker__time-container--with-today-button{display:inline;border:1px solid #aeaeae;border-radius:.3rem;position:absolute;right:-72px;top:0}.react-datepicker__time-container .react-datepicker__time{position:relative;background:#fff;border-bottom-right-radius:.3rem}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box{width:85px;overflow-x:hidden;margin:0 auto;text-align:center;border-bottom-right-radius:.3rem}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list{list-style:none;margin:0;height:calc(195px + .85rem);overflow-y:scroll;padding-right:0;padding-left:0;width:100%;box-sizing:content-box}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item{height:30px;padding:5px 10px;white-space:nowrap}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item:hover{cursor:pointer;background-color:#f0f0f0}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected{background-color:#216ba5;color:#fff;font-weight:700}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected:hover{background-color:#216ba5}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled{color:#ccc}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled:hover{cursor:default;background-color:transparent}.react-datepicker__week-number{color:#ccc;display:inline-block;width:1.7rem;line-height:1.7rem;text-align:center;margin:.166rem}.react-datepicker__week-number.react-datepicker__week-number--clickable{cursor:pointer}.react-datepicker__week-number.react-datepicker__week-number--clickable:hover{border-radius:.3rem;background-color:#f0f0f0}.react-datepicker__day-names,.react-datepicker__week{white-space:nowrap}.react-datepicker__day-names{margin-bottom:-8px}.react-datepicker__day,.react-datepicker__day-name,.react-datepicker__time-name{color:#000;display:inline-block;width:1.7rem;line-height:1.7rem;text-align:center;margin:.166rem}.react-datepicker__month--in-range,.react-datepicker__month--in-selecting-range,.react-datepicker__month--selected,.react-datepicker__quarter--in-range,.react-datepicker__quarter--in-selecting-range,.react-datepicker__quarter--selected{border-radius:.3rem;background-color:#216ba5;color:#fff}.react-datepicker__month--in-range:hover,.react-datepicker__month--in-selecting-range:hover,.react-datepicker__month--selected:hover,.react-datepicker__quarter--in-range:hover,.react-datepicker__quarter--in-selecting-range:hover,.react-datepicker__quarter--selected:hover{background-color:#1d5d90}.react-datepicker__month--disabled,.react-datepicker__quarter--disabled{color:#ccc;pointer-events:none}.react-datepicker__month--disabled:hover,.react-datepicker__quarter--disabled:hover{cursor:default;background-color:transparent}.react-datepicker__day,.react-datepicker__month-text,.react-datepicker__quarter-text,.react-datepicker__year-text{cursor:pointer}.react-datepicker__day:hover,.react-datepicker__month-text:hover,.react-datepicker__quarter-text:hover,.react-datepicker__year-text:hover{border-radius:.3rem;background-color:#f0f0f0}.react-datepicker__day--today,.react-datepicker__month-text--today,.react-datepicker__quarter-text--today,.react-datepicker__year-text--today{font-weight:700}.react-datepicker__day--highlighted,.react-datepicker__month-text--highlighted,.react-datepicker__quarter-text--highlighted,.react-datepicker__year-text--highlighted{border-radius:.3rem;background-color:#3dcc4a;color:#fff}.react-datepicker__day--highlighted:hover,.react-datepicker__month-text--highlighted:hover,.react-datepicker__quarter-text--highlighted:hover,.react-datepicker__year-text--highlighted:hover{background-color:#32be3f}.react-datepicker__day--highlighted-custom-1,.react-datepicker__month-text--highlighted-custom-1,.react-datepicker__quarter-text--highlighted-custom-1,.react-datepicker__year-text--highlighted-custom-1{color:#f0f}.react-datepicker__day--highlighted-custom-2,.react-datepicker__month-text--highlighted-custom-2,.react-datepicker__quarter-text--highlighted-custom-2,.react-datepicker__year-text--highlighted-custom-2{color:green}.react-datepicker__day--in-range,.react-datepicker__day--in-selecting-range,.react-datepicker__day--selected,.react-datepicker__month-text--in-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__month-text--selected,.react-datepicker__quarter-text--in-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__quarter-text--selected,.react-datepicker__year-text--in-range,.react-datepicker__year-text--in-selecting-range,.react-datepicker__year-text--selected{border-radius:.3rem;background-color:#216ba5;color:#fff}.react-datepicker__day--in-range:hover,.react-datepicker__day--in-selecting-range:hover,.react-datepicker__day--selected:hover,.react-datepicker__month-text--in-range:hover,.react-datepicker__month-text--in-selecting-range:hover,.react-datepicker__month-text--selected:hover,.react-datepicker__quarter-text--in-range:hover,.react-datepicker__quarter-text--in-selecting-range:hover,.react-datepicker__quarter-text--selected:hover,.react-datepicker__year-text--in-range:hover,.react-datepicker__year-text--in-selecting-range:hover,.react-datepicker__year-text--selected:hover{background-color:#1d5d90}.react-datepicker__day--keyboard-selected,.react-datepicker__month-text--keyboard-selected,.react-datepicker__quarter-text--keyboard-selected,.react-datepicker__year-text--keyboard-selected{border-radius:.3rem;background-color:#2a87d0;color:#fff}.react-datepicker__day--keyboard-selected:hover,.react-datepicker__month-text--keyboard-selected:hover,.react-datepicker__quarter-text--keyboard-selected:hover,.react-datepicker__year-text--keyboard-selected:hover{background-color:#1d5d90}.react-datepicker__day--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range),.react-datepicker__month-text--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range),.react-datepicker__quarter-text--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range),.react-datepicker__year-text--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range){background-color:rgba(33,107,165,.5)}.react-datepicker__month--selecting-range .react-datepicker__day--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range),.react-datepicker__month--selecting-range .react-datepicker__month-text--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range),.react-datepicker__month--selecting-range .react-datepicker__quarter-text--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range),.react-datepicker__month--selecting-range .react-datepicker__year-text--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range){background-color:#f0f0f0;color:#000}.react-datepicker__day--disabled,.react-datepicker__month-text--disabled,.react-datepicker__quarter-text--disabled,.react-datepicker__year-text--disabled{cursor:default;color:#ccc}.react-datepicker__day--disabled:hover,.react-datepicker__month-text--disabled:hover,.react-datepicker__quarter-text--disabled:hover,.react-datepicker__year-text--disabled:hover{background-color:transparent}.react-datepicker__month-text.react-datepicker__month--in-range:hover,.react-datepicker__month-text.react-datepicker__month--selected:hover,.react-datepicker__month-text.react-datepicker__quarter--in-range:hover,.react-datepicker__month-text.react-datepicker__quarter--selected:hover,.react-datepicker__quarter-text.react-datepicker__month--in-range:hover,.react-datepicker__quarter-text.react-datepicker__month--selected:hover,.react-datepicker__quarter-text.react-datepicker__quarter--in-range:hover,.react-datepicker__quarter-text.react-datepicker__quarter--selected:hover{background-color:#216ba5}.react-datepicker__month-text:hover,.react-datepicker__quarter-text:hover{background-color:#f0f0f0}.react-datepicker__input-container{position:relative;display:inline-block;width:100%}.react-datepicker__month-read-view,.react-datepicker__month-year-read-view,.react-datepicker__year-read-view{border:1px solid transparent;border-radius:.3rem;position:relative}.react-datepicker__month-read-view:hover,.react-datepicker__month-year-read-view:hover,.react-datepicker__year-read-view:hover{cursor:pointer}.react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__month-read-view:hover .react-datepicker__year-read-view--down-arrow,.react-datepicker__month-year-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view:hover .react-datepicker__year-read-view--down-arrow,.react-datepicker__year-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow{border-top-color:#b3b3b3}.react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view--down-arrow,.react-datepicker__year-read-view--down-arrow{-webkit-transform:rotate(135deg);transform:rotate(135deg);right:-16px;top:0}.react-datepicker__month-dropdown,.react-datepicker__month-year-dropdown,.react-datepicker__year-dropdown{background-color:#f0f0f0;position:absolute;width:50%;left:25%;top:30px;z-index:1;text-align:center;border-radius:.3rem;border:1px solid #aeaeae}.react-datepicker__month-dropdown:hover,.react-datepicker__month-year-dropdown:hover,.react-datepicker__year-dropdown:hover{cursor:pointer}.react-datepicker__month-dropdown--scrollable,.react-datepicker__month-year-dropdown--scrollable,.react-datepicker__year-dropdown--scrollable{height:150px;overflow-y:scroll}.react-datepicker__month-option,.react-datepicker__month-year-option,.react-datepicker__year-option{line-height:20px;width:100%;display:block;margin-left:auto;margin-right:auto}.react-datepicker__month-option:first-of-type,.react-datepicker__month-year-option:first-of-type,.react-datepicker__year-option:first-of-type{border-top-left-radius:.3rem;border-top-right-radius:.3rem}.react-datepicker__month-option:last-of-type,.react-datepicker__month-year-option:last-of-type,.react-datepicker__year-option:last-of-type{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-bottom-left-radius:.3rem;border-bottom-right-radius:.3rem}.react-datepicker__month-option:hover,.react-datepicker__month-year-option:hover,.react-datepicker__year-option:hover{background-color:#ccc}.react-datepicker__month-option:hover .react-datepicker__navigation--years-upcoming,.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-upcoming,.react-datepicker__year-option:hover .react-datepicker__navigation--years-upcoming{border-bottom-color:#b3b3b3}.react-datepicker__month-option:hover .react-datepicker__navigation--years-previous,.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-previous,.react-datepicker__year-option:hover .react-datepicker__navigation--years-previous{border-top-color:#b3b3b3}.react-datepicker__month-option--selected,.react-datepicker__month-year-option--selected,.react-datepicker__year-option--selected{position:absolute;left:15px}.react-datepicker__close-icon{cursor:pointer;background-color:transparent;border:0;outline:0;padding:0 6px 0 0;position:absolute;top:0;right:0;height:100%;display:table-cell;vertical-align:middle}.react-datepicker__close-icon:after{cursor:pointer;background-color:#216ba5;color:#fff;border-radius:50%;height:16px;width:16px;padding:2px;font-size:12px;line-height:1;text-align:center;display:table-cell;vertical-align:middle;content:"×"}.react-datepicker__today-button{background:#f0f0f0;border-top:1px solid #aeaeae;cursor:pointer;text-align:center;font-weight:700;padding:5px 0;clear:left}.react-datepicker__portal{position:fixed;width:100vw;height:100vh;background-color:rgba(0,0,0,.8);left:0;top:0;justify-content:center;align-items:center;display:flex;z-index:2147483647}.react-datepicker__portal .react-datepicker__day,.react-datepicker__portal .react-datepicker__day-name,.react-datepicker__portal .react-datepicker__time-name{width:3rem;line-height:3rem}@media (max-height:550px),(max-width:400px){.react-datepicker__portal .react-datepicker__day,.react-datepicker__portal .react-datepicker__day-name,.react-datepicker__portal .react-datepicker__time-name{width:2rem;line-height:2rem}}.react-datepicker__portal .react-datepicker-time__header,.react-datepicker__portal .react-datepicker__current-month{font-size:1.44rem}.mdc-list{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:1rem;line-height:1.75rem;font-weight:400;letter-spacing:.009375em;text-decoration:inherit;text-transform:inherit;line-height:1.5rem;margin:0;padding:8px 0;list-style-type:none;color:rgba(0,0,0,.87);color:var(--mdc-theme-text-primary-on-background,rgba(0,0,0,.87))}.mdc-list:focus{outline:none}.mdc-list-item{height:48px}.mdc-list-item__secondary-text{color:rgba(0,0,0,.54);color:var(--mdc-theme-text-secondary-on-background,rgba(0,0,0,.54))}.mdc-list-item__graphic{background-color:transparent;color:rgba(0,0,0,.38);color:var(--mdc-theme-text-icon-on-background,rgba(0,0,0,.38))}.mdc-list-item__meta{color:rgba(0,0,0,.38);color:var(--mdc-theme-text-hint-on-background,rgba(0,0,0,.38))}.mdc-list-group__subheader{color:rgba(0,0,0,.87);color:var(--mdc-theme-text-primary-on-background,rgba(0,0,0,.87))}.mdc-list-item--disabled .mdc-list-item__text{opacity:.38;color:#000;color:var(--mdc-theme-on-surface,#000)}.mdc-list--dense{padding-top:4px;padding-bottom:4px;font-size:.812rem}.mdc-list-item{display:flex;position:relative;align-items:center;justify-content:flex-start;padding:0 16px;overflow:hidden}.mdc-list-item:focus{outline:none}.mdc-list-item--activated,.mdc-list-item--activated .mdc-list-item__graphic,.mdc-list-item--selected,.mdc-list-item--selected .mdc-list-item__graphic{color:#6200ee;color:var(--mdc-theme-primary,#6200ee)}.mdc-list-item__graphic{margin-left:0;margin-right:32px;width:24px;height:24px;flex-shrink:0;align-items:center;justify-content:center;fill:currentColor}.mdc-list-item[dir=rtl] .mdc-list-item__graphic,[dir=rtl] .mdc-list-item .mdc-list-item__graphic{margin-left:32px;margin-right:0}.mdc-list .mdc-list-item__graphic{display:inline-flex}.mdc-list-item__meta{margin-left:auto;margin-right:0}.mdc-list-item__meta:not(.material-icons){font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.75rem;line-height:1.25rem;font-weight:400;letter-spacing:.0333333333em;text-decoration:inherit;text-transform:inherit}.mdc-list-item[dir=rtl] .mdc-list-item__meta,[dir=rtl] .mdc-list-item .mdc-list-item__meta{margin-left:0;margin-right:auto}.mdc-list-item__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mdc-list-item__text[for]{pointer-events:none}.mdc-list-item__primary-text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;margin-top:0;line-height:normal;margin-bottom:-20px;display:block}.mdc-list-item__primary-text:before{display:inline-block;width:0;height:32px;content:"";vertical-align:0}.mdc-list-item__primary-text:after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-list--dense .mdc-list-item__primary-text{display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-list--dense .mdc-list-item__primary-text:before{display:inline-block;width:0;height:24px;content:"";vertical-align:0}.mdc-list--dense .mdc-list-item__primary-text:after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-list-item__secondary-text{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:.875rem;line-height:1.25rem;font-weight:400;letter-spacing:.0178571429em;text-decoration:inherit;text-transform:inherit;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;margin-top:0;line-height:normal;display:block}.mdc-list-item__secondary-text:before{display:inline-block;width:0;height:20px;content:"";vertical-align:0}.mdc-list--dense .mdc-list-item__secondary-text{display:block;margin-top:0;line-height:normal;font-size:inherit}.mdc-list--dense .mdc-list-item__secondary-text:before{display:inline-block;width:0;height:20px;content:"";vertical-align:0}.mdc-list--dense .mdc-list-item{height:40px}.mdc-list--dense .mdc-list-item__graphic{margin-left:0;margin-right:36px;width:20px;height:20px}.mdc-list-item[dir=rtl] .mdc-list--dense .mdc-list-item__graphic,[dir=rtl] .mdc-list-item .mdc-list--dense .mdc-list-item__graphic{margin-left:36px;margin-right:0}.mdc-list--avatar-list .mdc-list-item{height:56px}.mdc-list--avatar-list .mdc-list-item__graphic{margin-left:0;margin-right:16px;width:40px;height:40px;border-radius:50%}.mdc-list-item[dir=rtl] .mdc-list--avatar-list .mdc-list-item__graphic,[dir=rtl] .mdc-list-item .mdc-list--avatar-list .mdc-list-item__graphic{margin-left:16px;margin-right:0}.mdc-list--two-line .mdc-list-item__text{align-self:flex-start}.mdc-list--two-line .mdc-list-item{height:72px}.mdc-list--avatar-list.mdc-list--dense .mdc-list-item,.mdc-list--two-line.mdc-list--dense .mdc-list-item{height:60px}.mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic{margin-left:0;margin-right:20px;width:36px;height:36px}.mdc-list-item[dir=rtl] .mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic,[dir=rtl] .mdc-list-item .mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic{margin-left:20px;margin-right:0}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item{cursor:pointer}a.mdc-list-item{color:inherit;text-decoration:none}.mdc-list-divider{height:0;margin:0;border:none;border-bottom:1px solid;border-bottom-color:rgba(0,0,0,.12)}.mdc-list-divider--padded{margin:0 16px}.mdc-list-divider--inset{margin-left:72px;margin-right:0;width:calc(100% - 72px)}.mdc-list-group[dir=rtl] .mdc-list-divider--inset,[dir=rtl] .mdc-list-group .mdc-list-divider--inset{margin-left:0;margin-right:72px}.mdc-list-divider--inset.mdc-list-divider--padded{width:calc(100% - 88px)}.mdc-list-group .mdc-list{padding:0}.mdc-list-group__subheader{font-family:Roboto,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-size:1rem;line-height:1.75rem;font-weight:400;letter-spacing:.009375em;text-decoration:inherit;text-transform:inherit;margin:.75rem 16px}@-webkit-keyframes mdc-ripple-fg-radius-in{0%{-webkit-animation-timing-function:cubic-bezier(.4,0,.2,1);animation-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-start,0)) scale(1);transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-start,0)) scale(1)}to{-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}}@keyframes mdc-ripple-fg-radius-in{0%{-webkit-animation-timing-function:cubic-bezier(.4,0,.2,1);animation-timing-function:cubic-bezier(.4,0,.2,1);-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-start,0)) scale(1);transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-start,0)) scale(1)}to{-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}}@-webkit-keyframes mdc-ripple-fg-opacity-in{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0}to{opacity:0;opacity:var(--mdc-ripple-fg-opacity,0)}}@keyframes mdc-ripple-fg-opacity-in{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0}to{opacity:0;opacity:var(--mdc-ripple-fg-opacity,0)}}@-webkit-keyframes mdc-ripple-fg-opacity-out{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0;opacity:var(--mdc-ripple-fg-opacity,0)}to{opacity:0}}@keyframes mdc-ripple-fg-opacity-out{0%{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0;opacity:var(--mdc-ripple-fg-opacity,0)}to{opacity:0}}.mdc-ripple-surface--test-edge-var-bug{--mdc-ripple-surface-test-edge-var:1px solid #000;visibility:hidden}.mdc-ripple-surface--test-edge-var-bug:before{border:var(--mdc-ripple-surface-test-edge-var)}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item{--mdc-ripple-fg-size:0;--mdc-ripple-left:0;--mdc-ripple-top:0;--mdc-ripple-fg-scale:1;--mdc-ripple-fg-translate-end:0;--mdc-ripple-fg-translate-start:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:before{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:before{-webkit-transition:opacity 15ms linear,background-color 15ms linear;transition:opacity 15ms linear,background-color 15ms linear;z-index:1}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded:before{-webkit-transform:scale(1);-webkit-transform:scale(var(--mdc-ripple-fg-scale,1));transform:scale(1);transform:scale(var(--mdc-ripple-fg-scale,1))}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded:after{top:0;left:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:center center;transform-origin:center center}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--unbounded:after{top:0;top:var(--mdc-ripple-top,0);left:0;left:var(--mdc-ripple-left,0)}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--foreground-activation:after{-webkit-animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards;animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--foreground-deactivation:after{-webkit-animation:mdc-ripple-fg-opacity-out .15s;animation:mdc-ripple-fg-opacity-out .15s;-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:before{top:-50%;left:-50%;width:200%;height:200%}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded:after{width:100%;width:var(--mdc-ripple-fg-size,100%);height:100%;height:var(--mdc-ripple-fg-size,100%)}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:before{background-color:#000}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:hover:before{opacity:.04}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--background-focused:before,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:before{opacity:.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:before{background-color:#6200ee}@supports not (-ms-ime-align:auto){:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:before{background-color:#6200ee;background-color:var(--mdc-theme-primary,#6200ee)}}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:hover:before{opacity:.16}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated.mdc-ripple-upgraded--background-focused:before,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.24}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.24}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--activated.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.24}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:before{opacity:.08}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:before{background-color:#6200ee}@supports not (-ms-ime-align:auto){:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:after,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:before{background-color:#6200ee;background-color:var(--mdc-theme-primary,#6200ee)}}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:hover:before{opacity:.12}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected.mdc-ripple-upgraded--background-focused:before,:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.2}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:not(.mdc-ripple-upgraded):after{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected:not(.mdc-ripple-upgraded):active:after{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.2}:not(.mdc-list--non-interactive)>:not(.mdc-list-item--disabled).mdc-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:0.2}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled{--mdc-ripple-fg-size:0;--mdc-ripple-left:0;--mdc-ripple-top:0;--mdc-ripple-fg-scale:1;--mdc-ripple-fg-translate-end:0;--mdc-ripple-fg-translate-start:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled:after,:not(.mdc-list--non-interactive)>.mdc-list-item--disabled:before{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled:before{-webkit-transition:opacity 15ms linear,background-color 15ms linear;transition:opacity 15ms linear,background-color 15ms linear;z-index:1}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled.mdc-ripple-upgraded:before{-webkit-transform:scale(1);-webkit-transform:scale(var(--mdc-ripple-fg-scale,1));transform:scale(1);transform:scale(var(--mdc-ripple-fg-scale,1))}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled.mdc-ripple-upgraded:after{top:0;left:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:center center;transform-origin:center center}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled.mdc-ripple-upgraded--unbounded:after{top:0;top:var(--mdc-ripple-top,0);left:0;left:var(--mdc-ripple-left,0)}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled.mdc-ripple-upgraded--foreground-activation:after{-webkit-animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards;animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled.mdc-ripple-upgraded--foreground-deactivation:after{-webkit-animation:mdc-ripple-fg-opacity-out .15s;animation:mdc-ripple-fg-opacity-out .15s;-webkit-transform:translate(0) scale(1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1));transform:translate(0) scale(1);transform:translate(var(--mdc-ripple-fg-translate-end,0)) scale(var(--mdc-ripple-fg-scale,1))}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled:after,:not(.mdc-list--non-interactive)>.mdc-list-item--disabled:before{top:-50%;left:-50%;width:200%;height:200%}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled.mdc-ripple-upgraded:after{width:100%;width:var(--mdc-ripple-fg-size,100%);height:100%;height:var(--mdc-ripple-fg-size,100%)}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled:after,:not(.mdc-list--non-interactive)>.mdc-list-item--disabled:before{background-color:#000}:not(.mdc-list--non-interactive)>.mdc-list-item--disabled.mdc-ripple-upgraded--background-focused:before,:not(.mdc-list--non-interactive)>.mdc-list-item--disabled:not(.mdc-ripple-upgraded):focus:before{-webkit-transition-duration:75ms;transition-duration:75ms;opacity:.12}.rmwc-collapsible-list{width:100%}.rmwc-collapsible-list__children{overflow:hidden;max-height:0;-webkit-transition:max-height .3s,opacity .3s;transition:max-height .3s,opacity .3s;opacity:0}.rmwc-collapsible-list--open>.rmwc-collapsible-list__children{opacity:1}.rmwc-collapsible-list__handle .mdc-list-item__meta{-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.rmwc-collapsible-list--open>.rmwc-collapsible-list__handle .mdc-list-item__meta{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rmwc-collapsible-list__children .mdc-list-item{padding-left:2rem}.rmwc-collapsible-list__children-inner{overflow:auto}.rmwc-icon--image{min-width:1em;min-height:1em;background-repeat:no-repeat;font-size:1.5rem;background-size:1em;background-position:50%}.rmwc-icon--size-xsmall{font-size:1.125rem;width:1em;height:1em}.rmwc-icon--size-small{font-size:1.25rem;width:1em;height:1em}.rmwc-icon--size-medium{font-size:1.5rem;width:1em;height:1em}.rmwc-icon--size-large{font-size:2.25rem;width:1em;height:1em}.rmwc-icon--size-xlarge{font-size:3rem;width:1em;height:1em} +/*# sourceMappingURL=2.c454aab8.chunk.css.map */ \ No newline at end of file diff --git a/web/gui/dashboard/static/css/2.c454aab8.chunk.css.map b/web/gui/dashboard/static/css/2.c454aab8.chunk.css.map new file mode 100644 index 000000000..7a9ad2ba8 --- /dev/null +++ b/web/gui/dashboard/static/css/2.c454aab8.chunk.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["react-datepicker.css","react-filter-box.css","mdc.menu-surface.css","webpack:///packages/mdc-menu-surface/mdc-menu-surface.scss","webpack:///packages/material-components-web/node_modules/@material/elevation/_mixins.scss","webpack:///packages/material-components-web/node_modules/@material/theme/_mixins.scss","webpack:///packages/material-components-web/node_modules/@material/shape/_mixins.scss","webpack:///packages/material-components-web/node_modules/@material/rtl/_mixins.scss","bootstrap-toggle.min.css","index.css","ReactToastify.min.css","webpack:///packages/mdc-list/mdc-list.scss","webpack:///packages/material-components-web/node_modules/@material/typography/_mixins.scss","mdc.list.css","webpack:///packages/material-components-web/node_modules/@material/ripple/_keyframes.scss","webpack:///packages/material-components-web/node_modules/@material/ripple/_mixins.scss","collapsible-list.css","icon.css"],"names":[],"mappings":"AAirBA,gBAcA,CC/rBA,YACE,WACF,CACA,iBACE,WACF,CACA,aACE,aACF,CACA,aACE,aACF,CACA,UACE,aACF,CACA,kBACE,iBAAkB,CAClB,WAAY,CACZ,eAAgB,CAChB,kBAAmB,CACnB,cAAe,CACf,gBAAiB,CACjB,UAAc,CACd,qBAAsB,CAGtB,iBAAkB,CAClB,qBAAyB,CACzB,qBAAyB,CAGzB,2CAAgD,CAChD,0DAA8D,CAG9D,kDACF,CACA,wBACE,oBAAqB,CACrB,SAAU,CACV,wEACF,CACA,wBACE,oBAAqB,CACrB,SAAU,CACV,2CACF,CACA,8BACE,2DACF,CACA,kBACE,WAAY,CACZ,eACF,CACA,mCACE,WACF,CACA,0BACE,kBACF,CACA,sCACE,UACF;;AC9DA;;;;CAIC,CCmCC,kBAwDE,YAAA,CACA,iBAAA,CACA,qBAAA,CACA,4BAAA,CACA,6BAAA,CACA,QAAA,CACA,SAAA,CACA,0BAAA,CAAA,kBAAA,CACA,iCAAA,CAAA,yBAAA,CACA,SAAA,CACA,aAAA,CACA,6BAAA,CACA,SAAA,CAIA,4EAAA,CAAA,oFAAA,CAAA,oEAAA,CAAA,kHAAA,CCrCA,sGAAA,CC+DI,qBAAA,CAAA,8CAAA,CAAA,UAAA,CAAA,sCAAA,CCnGJ,iBAAA,CC6NF,8BAAA,CAEA,gCLjOF,CCgFE,wBAEI,YD/EN,CCoFE,wBAEI,oBAAA,CACA,0BAAA,CAAA,kBAAA,CACA,SDlFN,CCsFE,kCAEI,oBAAA,CACA,2BAAA,CAAA,mBAAA,CACA,SDpFN,CCwFE,oCAEI,oBAAA,CACA,SAAA,CAIA,sCAAA,CAAA,8BD1FN,CKqBI,uDA4LA,+BAAA,CAEA,+BL7MJ,CCXE,0BAEI,iBAAA,CACA,gBDaN,CCTE,yBAEI,cDWN;AMvEA;;;;;;6EAM6E,CAC7E,iDAAiD,iBAAiB,CAAC,gBAAgB,CACnF,QAAQ,iBAAiB,CAAC,eAAe,CACzC,6BAA6B,YAAY,CACzC,cAAc,iBAAiB,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,oBAAoB,CAAC,4BAA4B,CAAC,qBAAqB,CAAC,wBAAwB,CACjK,0BAA0B,UAAU,CACpC,WAA4C,MAAM,CAAC,SAA2C,CAC9F,uBADW,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAkB,QAAQ,CAAC,QAAQ,CAAC,eACgB,CAA/F,YAA6C,QAAQ,CAAC,OAAyC,CAC/F,eAAe,iBAAiB,CAAC,aAAa,CAAC,aAAa,CAAC,gBAAgB,CAAC,WAAW,CAAC,OAAO,CAAC,kBAAkB,CACpH,YAAY,cAAc,CAAC,eAAe,CAC1C,eAAe,kBAAkB,CACjC,gBAAgB,iBAAiB,CACjC,eAAe,cAAc,CAAC,eAAe,CAC7C,kBAAkB,kBAAkB,CACpC,mBAAmB,iBAAiB,CACpC,sBAAsB,UAAU,CAChC,eAAe,cAAc,CAAC,eAAe,CAC7C,kBAAkB,kBAAkB,CACpC,mBAAmB,iBAAiB,CACpC,eAAe,cAAc,CAAC,eAAe,CAC7C,kBAAkB,kBAAkB,CACpC,mBAAmB,iBAAiB,CC1BpC,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,wNAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,gPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,sOAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,8PAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,0NAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,kPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,8NAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,sPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,4NAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,oPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,gOAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,wPAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,wNAKF,CAGA,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,gPAKF,CCpLA,2BAA2B,YAAY,CAAC,oCAAoC,CAAC,cAAc,CAAC,WAAW,CAAC,WAAW,CAAC,qBAAqB,CAAC,UAAU,CAAC,qCAAqC,OAAO,CAAC,QAAQ,CAAC,uCAAuC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,sCAAsC,OAAO,CAAC,SAAS,CAAC,wCAAwC,UAAU,CAAC,QAAQ,CAAC,0CAA0C,UAAU,CAAC,QAAQ,CAAC,kBAAkB,CAAC,yCAAyC,UAAU,CAAC,SAAS,CAAC,yCAAyC,2BAA2B,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,kHAAkH,KAAK,CAAC,2HAA2H,QAAQ,CAAC,gCAAgC,OAAO,CAAC,SAAS,CAAC,CAAC,iBAAiB,iBAAiB,CAAC,eAAe,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,WAAW,CAAC,iBAAiB,CAAC,mEAAmE,CAAqB,YAAY,CAAuB,6BAA6B,CAAC,gBAAgB,CAAC,eAAe,CAAC,sBAAsB,CAAC,cAAc,CAAC,aAAa,CAAC,sBAAsB,aAAa,CAAC,0BAA0B,eAAe,CAAC,UAAU,CAAC,uBAAuB,kBAAkB,CAAC,0BAA0B,kBAAkB,CAAC,0BAA0B,kBAAkB,CAAC,wBAAwB,kBAAkB,CAAC,sBAAsB,aAAa,CAAY,QAAM,CAAC,yCAAyC,iBAAiB,eAAe,CAAC,CAAC,wBAAwB,UAAU,CAAC,eAAe,CAAC,cAAc,CAAC,sBAAsB,CAAC,YAAY,CAAC,WAAW,CAAC,SAAS,CAAC,cAAc,CAAC,UAAU,CAAC,2BAAmB,CAAnB,mBAAmB,CAA2B,qBAAqB,CAAC,iCAAiC,UAAU,CAAC,UAAU,CAAC,4DAA4D,SAAS,CAAC,2CAAmC,GAAG,2BAAkB,CAAlB,mBAAmB,CAAC,GAAG,2BAAkB,CAAlB,mBAAmB,CAAC,CAAjF,mCAAmC,GAAG,2BAAkB,CAAlB,mBAAmB,CAAC,GAAG,2BAAkB,CAAlB,mBAAmB,CAAC,CAAC,wBAAwB,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,mCAAmC,CAAC,6BAAoB,CAApB,qBAAqB,CAAC,kCAAkC,2DAAkD,CAAlD,mDAAmD,CAAC,oCAAoC,wCAAuB,CAAvB,gCAAuB,CAAvB,wBAAuB,CAAvB,8CAAwB,CAAC,6BAA6B,OAAO,CAAC,SAAS,CAAC,8BAAqB,CAArB,sBAAsB,CAAC,iCAAiC,gKAAgF,CAAhF,iFAAiF,CAAC,2CAAmC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAArS,mCAAmC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAAC,4CAAoC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAlI,oCAAoC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAC,0CAAkC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,sCAA6B,CAA7B,8BAA8B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAApS,kCAAkC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,sCAA6B,CAA7B,8BAA8B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAAC,2CAAmC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,CAAjI,mCAAmC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,CAAC,wCAAgC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAA3S,gCAAgC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,yCAAiC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,QAAQ,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,CAAxK,iCAAiC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,QAAQ,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,CAAC,0CAAkC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,sCAA6B,CAA7B,8BAA8B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAApS,kCAAkC,kBAAkB,+DAAsD,CAAtD,uDAAuD,CAAC,GAAG,SAAS,CAAC,0CAAiC,CAAjC,kCAAkC,CAAC,IAAI,SAAS,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,IAAI,wCAA+B,CAA/B,gCAAgC,CAAC,IAAI,sCAA6B,CAA7B,8BAA8B,CAAC,GAAG,sBAAa,CAAb,cAAc,CAAC,CAAC,2CAAmC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,QAAQ,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAzK,mCAAmC,IAAI,uCAA8B,CAA9B,+BAA+B,CAAC,QAAQ,SAAS,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,GAAG,SAAS,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAC,uEAAuE,6CAAoC,CAApC,qCAAqC,CAAC,yEAAyE,8CAAqC,CAArC,sCAAsC,CAAC,oCAAoC,6CAAoC,CAApC,qCAAqC,CAAC,uCAAuC,2CAAkC,CAAlC,mCAAmC,CAAC,qEAAqE,8CAAqC,CAArC,sCAAsC,CAAC,uEAAuE,+CAAsC,CAAtC,uCAAuC,CAAC,mCAAmC,4CAAmC,CAAnC,oCAAoC,CAAC,sCAAsC,8CAAqC,CAArC,sCAAsC,CAAC,oCAA4B,GAAG,SAAS,CAAC,mCAA0B,CAA1B,2BAA2B,CAAC,IAAI,SAAS,CAAC,CAAnF,4BAA4B,GAAG,SAAS,CAAC,mCAA0B,CAA1B,2BAA2B,CAAC,IAAI,SAAS,CAAC,CAAC,qCAA6B,GAAG,SAAS,CAAC,IAAI,SAAS,CAAC,mCAA0B,CAA1B,2BAA2B,CAAC,GAAG,SAAS,CAAC,CAAjG,6BAA6B,GAAG,SAAS,CAAC,IAAI,SAAS,CAAC,mCAA0B,CAA1B,2BAA2B,CAAC,GAAG,SAAS,CAAC,CAAC,sBAAsB,uCAA8B,CAA9B,+BAA+B,CAAC,qBAAqB,wCAA+B,CAA/B,gCAAgC,CAAC,oCAA4B,GAAG,mDAA2C,CAA3C,2CAA2C,CAAC,yCAAiC,CAAjC,iCAAiC,CAAC,SAAS,CAAC,IAAI,oDAA4C,CAA5C,4CAA4C,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,mDAA2C,CAA3C,2CAA2C,CAAC,SAAS,CAAC,IAAI,mDAA0C,CAA1C,2CAA2C,CAAC,GAAG,oCAA2B,CAA3B,4BAA4B,CAAC,CAApV,4BAA4B,GAAG,mDAA2C,CAA3C,2CAA2C,CAAC,yCAAiC,CAAjC,iCAAiC,CAAC,SAAS,CAAC,IAAI,oDAA4C,CAA5C,4CAA4C,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,IAAI,mDAA2C,CAA3C,2CAA2C,CAAC,SAAS,CAAC,IAAI,mDAA0C,CAA1C,2CAA2C,CAAC,GAAG,oCAA2B,CAA3B,4BAA4B,CAAC,CAAC,qCAA6B,GAAG,oCAA2B,CAA3B,4BAA4B,CAAC,IAAI,oDAA4C,CAA5C,4CAA4C,CAAC,SAAS,CAAC,GAAG,mDAA2C,CAA3C,2CAA2C,CAAC,SAAS,CAAC,CAAjL,6BAA6B,GAAG,oCAA2B,CAA3B,4BAA4B,CAAC,IAAI,oDAA4C,CAA5C,4CAA4C,CAAC,SAAS,CAAC,GAAG,mDAA2C,CAA3C,2CAA2C,CAAC,SAAS,CAAC,CAAC,sBAAsB,uCAA8B,CAA9B,+BAA+B,CAAC,qBAAqB,wCAA+B,CAA/B,gCAAgC,CAAC,0CAAkC,GAAG,uCAA+B,CAA/B,+BAA+B,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAnH,kCAAkC,GAAG,uCAA+B,CAA/B,+BAA+B,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,yCAAiC,GAAG,wCAAgC,CAAhC,gCAAgC,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAnH,iCAAiC,GAAG,wCAAgC,CAAhC,gCAAgC,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,uCAA+B,GAAG,uCAA+B,CAA/B,+BAA+B,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAhH,+BAA+B,GAAG,uCAA+B,CAA/B,+BAA+B,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,yCAAiC,GAAG,wCAAgC,CAAhC,gCAAgC,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAnH,iCAAiC,GAAG,wCAAgC,CAAhC,gCAAgC,CAAC,kBAAkB,CAAC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,CAAC,2CAAmC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,CAAnH,mCAAmC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,uCAA8B,CAA9B,+BAA+B,CAAC,CAAC,0CAAkC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,CAAnH,kCAAkC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,CAAC,0CAAkC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,CAAnH,kCAAkC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,wCAA+B,CAA/B,gCAAgC,CAAC,CAAC,wCAAgC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAlH,gCAAgC,GAAG,+BAAsB,CAAtB,uBAAuB,CAAC,GAAG,iBAAiB,CAAC,yCAAgC,CAAhC,iCAAiC,CAAC,CAAC,qEAAqE,4CAAmC,CAAnC,oCAAoC,CAAC,uEAAuE,6CAAoC,CAApC,qCAAqC,CAAC,mCAAmC,4CAAmC,CAAnC,oCAAoC,CAAC,sCAAsC,0CAAiC,CAAjC,kCAAkC,CAAC,mEAAmE,6CAAoC,CAApC,qCAAqC,CAAC,qEAAqE,8CAAqC,CAArC,sCAAsC,CAAC,kCAAkC,2CAAkC,CAAlC,mCAAmC,CAAC,qCAAqC,6CAAoC,CAApC,qCAAqC,CVC9gP,2LAKE,iBAAyB,CAAzB,kBAAyB,CAAzB,wBAAyB,CACzB,UAAW,CACX,aAAc,CACd,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,SACF,CACA,uJACE,gBAAiB,CACjB,iBAAkB,CAClB,OACF,CACA,wUACE,sBAAuB,CACvB,iBAAkB,CAElB,QAAS,CACT,SAAU,CACV,UAAW,CACX,UAAW,CACX,4BAAiB,CACjB,SACF,CACA,qKACE,2BACF,CAEA,6EACE,KAAM,CACN,eACF,CACA,uKACE,eAAgB,CAChB,2BACF,CACA,mFACE,KACF,CACA,oFACE,QAAS,CACT,2BACF,CAEA,0EACE,QAAS,CACT,kBACF,CACA,iKACE,kBAAmB,CACnB,qBACF,CACA,gFACE,QACF,CACA,iFACE,WAAY,CACZ,wBACF,CAEA,0BACE,oBAAqB,CACrB,SAAU,CACV,QAAS,CACT,UACF,CAEA,kBACE,qDAA2D,CAC3D,eAAiB,CACjB,qBAAsB,CACtB,UAAW,CACX,wBAAyB,CACzB,mBAAqB,CACrB,oBAAqB,CACrB,iBACF,CAEA,yDACE,SACF,CACA,+DACE,aACF,CACA,8GAEE,+BAAiC,CACjC,gCACF,CAEA,4BACE,iBAAkB,CAClB,SACF,CAEA,yBACE,SACF,CACA,iDACE,gBACF,CACA,6JACE,SAAU,CACV,UACF,CACA,8CACE,mBACF,CACA,gDACE,gBACF,CACA,4EACE,SAAU,CACV,UACF,CACA,+CACE,iBACF,CACA,2EACE,SAAU,CACV,UACF,CAEA,0BACE,iBAAkB,CAClB,wBAAyB,CACzB,+BAAgC,CAChC,4BAA8B,CAC9B,aAAc,CACd,iBACF,CACA,gCACE,kBAAmB,CACnB,gBAAiB,CACjB,iBACF,CACA,2EACE,wBACF,CACA,0EACE,6BACF,CAEA,gUAME,oBAAqB,CACrB,YACF,CAEA,8FAGE,YAAa,CACb,UAAW,CACX,eAAiB,CACjB,iBACF,CAEA,+BACE,sBAAuB,CACvB,kBAAmB,CACnB,eACF,CAEA,8BACE,kBAAmB,CACnB,eAAgB,CAChB,YAAa,CACb,sBAAuB,CACvB,iBAAkB,CAClB,cAAe,CACf,iBAAkB,CAClB,OAAQ,CACR,SAAU,CACV,WAAY,CACZ,SAAU,CACV,WAAY,CACZ,UAAW,CACX,kBAAmB,CACnB,eACF,CACA,wCACE,QACF,CACA,oCACE,SACF,CACA,2GACE,UACF,CACA,qCACE,iBAAkB,CAClB,KAAM,CACN,aAAc,CACd,gBAAiB,CACjB,iBACF,CACA,8CACE,OACF,CACA,8CACE,QACF,CACA,4CACE,oBACF,CAEA,mCACE,iBAAkB,CAClB,QAAS,CACT,cAAe,CACf,OACF,CACA,yCACE,SACF,CACA,gDACE,+BAAwB,CAAxB,uBAAwB,CACxB,SACF,CACA,6CACE,UACF,CACA,oDACE,gCAAyB,CAAzB,wBAAyB,CACzB,UACF,CAEA,mCACE,UACF,CAEA,wBACE,YAAc,CACd,iBACF,CACA,gCACE,YAAa,CACb,cAAe,CACf,eACF,CACA,qDACE,oBAAqB,CACrB,UAAW,CACX,UACF,CAEA,yBACE,YAAc,CACd,iBACF,CACA,gHAEE,oBAAqB,CACrB,UAAW,CACX,UACF,CAEA,wCACE,UAAW,CACX,UAAW,CACX,UAAW,CACX,sBAAuB,CACvB,eACF,CAIA,wJACE,oBACF,CACA,8GACE,oBAAqB,CACrB,gBACF,CACA,oHACE,UACF,CACA,oTAEE,uBAAwB,CACxB,QACF,CACA,+HACE,yBACF,CACA,kHACE,eAAgB,CAChB,oBACF,CAEA,kCACE,WAAY,CACZ,6BAA8B,CAC9B,UACF,CACA,qDACE,cAAe,CACf,wBAAyB,CACzB,mBAAqB,CACrB,iBAAkB,CAClB,WAAY,CACZ,KACF,CACA,0DACE,iBAAkB,CAClB,eAAiB,CACjB,gCACF,CACA,sFACE,UAAW,CACX,iBAAkB,CAClB,aAAc,CACd,iBAAkB,CAClB,gCACF,CACA,qHACE,eAAgB,CAChB,QAAS,CACT,2BAAkC,CAClC,iBAAkB,CAClB,eAAgB,CAChB,cAAe,CACf,UAAW,CACX,sBACF,CACA,yJACE,WAAY,CACZ,gBAAiB,CACjB,kBACF,CACA,+JACE,cAAe,CACf,wBACF,CACA,mKACE,wBAAyB,CACzB,UAAY,CACZ,eACF,CACA,yKACE,wBACF,CACA,mKACE,UACF,CACA,yKACE,cAAe,CACf,4BACF,CAEA,+BACE,UAAW,CACX,oBAAqB,CACrB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,cACF,CACA,wEACE,cACF,CACA,8EACE,mBAAqB,CACrB,wBACF,CAEA,qDAEE,kBACF,CAEA,6BACE,kBACF,CAEA,gFAGE,UAAW,CACX,oBAAqB,CACrB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,cACF,CAEA,4OAIE,mBAAqB,CACrB,wBAAyB,CACzB,UACF,CACA,gRAIE,wBACF,CACA,wEAEE,UAAW,CACX,mBACF,CACA,oFAEE,cAAe,CACf,4BACF,CAEA,kHAIE,cACF,CACA,0IAIE,mBAAqB,CACrB,wBACF,CACA,8IAIE,eACF,CACA,sKAIE,mBAAqB,CACrB,wBAAyB,CACzB,UACF,CACA,8LAIE,wBACF,CACA,0MAIE,UACF,CACA,0MAIE,WACF,CACA,sfAUE,mBAAqB,CACrB,wBAAyB,CACzB,UACF,CACA,8jBAUE,wBACF,CACA,8LAIE,mBAAqB,CACrB,wBAAyB,CACzB,UACF,CACA,sNAIE,wBACF,CACA,8zBAgBE,oCACF,CACA,8lCAgBE,wBAAyB,CACzB,UACF,CACA,0JAIE,cAAe,CACf,UACF,CACA,kLAIE,4BACF,CAEA,gkBAKE,wBACF,CACA,0EAEE,wBACF,CAEA,mCACE,iBAAkB,CAClB,oBAAqB,CACrB,UACF,CAEA,6GAGE,4BAA6B,CAC7B,mBAAqB,CACrB,iBACF,CACA,+HAGE,cACF,CACA,qhBAME,wBACF,CACA,iJAGE,gCAAyB,CAAzB,wBAAyB,CACzB,WAAY,CACZ,KACF,CAEA,0GAGE,wBAAyB,CACzB,iBAAkB,CAClB,SAAU,CACV,QAAS,CACT,QAAS,CACT,SAAU,CACV,iBAAkB,CAClB,mBAAqB,CACrB,wBACF,CACA,4HAGE,cACF,CACA,8IAGE,YAAa,CACb,iBACF,CAEA,oGAGE,gBAAiB,CACjB,UAAW,CACX,aAAc,CACd,gBAAiB,CACjB,iBACF,CACA,8IAGE,4BAA8B,CAC9B,6BACF,CACA,2IAGE,wBAAyB,CACzB,qBAAsB,CACtB,oBAAqB,CACrB,gBAAiB,CACjB,+BAAiC,CACjC,gCACF,CACA,sHAGE,qBACF,CACA,gQAGE,2BACF,CACA,gQAGE,wBACF,CACA,kIAGE,iBAAkB,CAClB,SACF,CAEA,8BACE,cAAe,CACf,4BAA6B,CAC7B,QAAS,CACT,SAAU,CACV,iBAAkB,CAClB,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,WAAY,CACZ,kBAAmB,CACnB,qBACF,CACA,oCACE,cAAe,CACf,wBAAyB,CACzB,UAAW,CACX,iBAAkB,CAClB,WAAY,CACZ,UAAW,CACX,WAAY,CACZ,cAAe,CACf,aAAc,CACd,iBAAkB,CAClB,kBAAmB,CACnB,qBAAsB,CACtB,WACF,CAEA,gCACE,kBAAmB,CACnB,4BAA6B,CAC7B,cAAe,CACf,iBAAkB,CAClB,eAAiB,CACjB,aAAc,CACd,UACF,CAEA,0BACE,cAAe,CACf,WAAY,CACZ,YAAa,CACb,+BAAoC,CACpC,MAAO,CACP,KAAM,CACN,sBAAuB,CACvB,kBAAmB,CACnB,YAAa,CACb,kBACF,CACA,8JAGE,UAAW,CACX,gBACF,CACA,4CACE,8JAGE,UAAW,CACX,gBACF,CACF,CACA,oHAEE,iBACF,CW9qBE,UCCM,6BAAA,CAAA,iCAAA,CAAA,kCAAA,CAAA,cAAA,CAAA,mBAAA,CAAA,eAAA,CAAA,wBAAA,CAAA,uBAAA,CAAA,sBAAA,CD8aJ,kBAAA,CAIA,QAAA,CACA,aAAA,CACA,oBAAA,CNtWI,qBAAA,CAAA,iEQhHR,CFwdI,gBACE,YEtdN,CF0aE,eAgBE,WEtbJ,CF6SE,+BNpMM,qBAAA,CAAA,mEQnGR,CFiTE,wBNnQI,4BAAA,CAqDE,qBAAA,CAAA,8DQ/FR,CFiUE,qBNlOM,qBAAA,CAAA,8DQnFR,CFuWE,2BNpRM,qBAAA,CAAA,iEQ7ER,CF2WE,8CAEI,WAAA,CNhSE,UAAA,CAAA,sCQzER,CFOE,iBAEI,eAAA,CACA,kBAAA,CACA,iBECN,CFGE,eA2aA,YAAA,CACA,iBAAA,CACA,kBAAA,CACA,0BAAA,CACA,cAAA,CACA,eE1aF,CF4aE,qBACE,YE1aJ,CFyQE,sJNxNM,aAAA,CAAA,sCQpCR,CFRE,wBJ8LA,aAAA,CAAA,iBAAA,CI8PA,UAAA,CACA,WAAA,CAzbI,aAAA,CACA,kBAAA,CACA,sBAAA,CACA,iBEcN,CNxCQ,iGAiNN,gBAAA,CAAA,cMnKF,CFbE,kCAEI,mBEeN,CFVE,qBJyKA,gBAAA,CAAA,cMxJF,CFfI,0CCzDI,6BAAA,CAAA,iCAAA,CAAA,kCAAA,CAAA,gBAAA,CAAA,mBAAA,CAAA,eAAA,CAAA,4BAAA,CAAA,uBAAA,CAAA,sBCmFR,CNpEQ,2FAiNN,aAAA,CAAA,iBMvIF,CFtBE,qBCxDE,sBAAA,CACA,kBAAA,CACA,eCkFJ,CFtBE,0BAEI,mBEwBN,CFpBE,6BCpEE,sBAAA,CACA,kBAAA,CACA,eAAA,CASA,YAAA,CAEA,kBAAA,CAgBA,mBAAA,CD6CE,aE0BN,CDpFE,oCA0BA,oBAAA,CACA,OAAA,CACA,WAAA,CACA,UAAA,CAzBI,gBCuFN,CD3EE,mCAUA,oBAAA,CACA,OAAA,CACA,WAAA,CACA,UAAA,CATI,oBC8EN,CFpCI,8CCpEA,aAAA,CACA,YAAA,CAEA,kBAAA,CAgBA,mBC4FJ,CDzGE,qDA0BA,oBAAA,CACA,OAAA,CACA,WAAA,CACA,UAAA,CAzBI,gBC4GN,CDhGE,oDAUA,oBAAA,CACA,OAAA,CACA,WAAA,CACA,UAAA,CATI,oBCmGN,CFlDE,+BChGM,6BAAA,CAAA,iCAAA,CAAA,kCAAA,CAAA,iBAAA,CAAA,mBAAA,CAAA,eAAA,CAAA,4BAAA,CAAA,uBAAA,CAAA,sBAAA,CAWJ,sBAAA,CACA,kBAAA,CACA,eAAA,CASA,YAAA,CAEA,kBAAA,CD8EE,aEgEN,CD3IE,sCA0BA,oBAAA,CACA,OAAA,CACA,WAAA,CACA,UAAA,CAzBI,gBC8IN,CFnEI,gDCrFA,aAAA,CACA,YAAA,CAEA,kBAAA,CDsFI,iBEsER,CDzJE,uDA0BA,oBAAA,CACA,OAAA,CACA,WAAA,CACA,UAAA,CAzBI,gBC4JN,CFtEE,gCAEI,WEwEN,CFpEE,yCJqGA,aAAA,CAAA,iBAAA,CI8PA,UAAA,CACA,WEvRF,CNzLQ,mIAiNN,gBAAA,CAAA,cMlBF,CF7EE,sCAEI,WE+EN,CF3EE,+CJyFA,aAAA,CAAA,iBAAA,CI8PA,UAAA,CACA,WAAA,CApVI,iBEiFN,CN7MQ,+IAiNN,gBAAA,CAAA,cMEF,CFnFE,yCAEI,qBEqFN,CFjFE,mCAEI,WEmFN,CF/EE,yGAGI,WEiFN,CF7EE,+DJ8DA,aAAA,CAAA,iBAAA,CI8PA,UAAA,CACA,WEvOF,CNzOQ,+KAiNN,gBAAA,CAAA,cM8BF,CFrFE,8EAEI,cEuFN,CFjFE,gBAEI,aAAA,CACA,oBEmFN,CF9EE,kBAEI,QAAA,CACA,QAAA,CAGA,WAAA,CAAA,uBAAA,CNtKA,mCQsPN,CFxEE,0BAGI,aE6EN,CFzEE,yBJkBA,gBAAA,CAAA,cAAA,CIdI,uBE6EN,CNhRQ,qGAiNN,aAAA,CAAA,iBMqEF,CF/EE,kDAEI,uBEiFN,CF1EM,0BACE,SE6ER,CFxEE,2BCrOM,6BAAA,CAAA,iCAAA,CAAA,kCAAA,CAAA,cAAA,CAAA,mBAAA,CAAA,eAAA,CAAA,wBAAA,CAAA,uBAAA,CAAA,sBAAA,CD6OF,kBE6EN,CCzVE,2CACE,GACE,yDAAA,CAAA,iDAAA,CAKA,uCAAA,CAAA,4EAAA,CAAA,+BAAA,CAAA,oED0VJ,CCvVE,GACE,uCAAA,CAAA,qGAAA,CAAA,+BAAA,CAAA,6FD0VJ,CACF,CCtWE,mCACE,GACE,yDAAA,CAAA,iDAAA,CAKA,uCAAA,CAAA,4EAAA,CAAA,+BAAA,CAAA,oEDuWJ,CCpWE,GACE,uCAAA,CAAA,qGAAA,CAAA,+BAAA,CAAA,6FDuWJ,CACF,CCpWE,4CACE,GACE,wCAAA,CAAA,gCAAA,CACA,SDuWJ,CCpWE,GACE,SAAA,CAAA,sCDsWJ,CACF,CC9WE,oCACE,GACE,wCAAA,CAAA,gCAAA,CACA,SDiXJ,CC9WE,GACE,SAAA,CAAA,sCDgXJ,CACF,CC7WE,6CACE,GACE,wCAAA,CAAA,gCAAA,CACA,SAAA,CAAA,sCDgXJ,CC7WE,GACE,SD+WJ,CACF,CCvXE,qCACE,GACE,wCAAA,CAAA,gCAAA,CACA,SAAA,CAAA,sCD0XJ,CCvXE,GACE,SDyXJ,CACF,CEhWM,uCACE,iDAAA,CAEA,iBFiWR,CE/VQ,8CACE,8CFiWV,CFzIE,8EI5ME,sBAAA,CACA,mBAAA,CACA,kBAAA,CACA,uBAAA,CACA,+BAAA,CACA,iCAAA,CAEA,yCFwVJ,CEpVE,yKAGI,iBAAA,CACA,iBAAA,CACA,SAAA,CACA,mBAAA,CACA,UFoVN,CEhVE,qFAGI,mEAAA,CAAA,2DAAA,CAMA,SF2UN,CEpUI,yGAEI,0BAAA,CAAA,qDAAA,CAAA,kBAAA,CAAA,6CFsUR,CElUI,wGAEI,KAAA,CAEA,MAAA,CACA,0BAAA,CAAA,kBAAA,CACA,sCAAA,CAAA,8BFqUR,CE/TI,mHAEI,KAAA,CAAA,2BAAA,CAEA,MAAA,CAAA,6BFgUR,CE1TI,+HAEI,+FAAA,CAAA,uFF4TR,CEpTI,iIAEI,gDAAA,CAAA,wCAAA,CAKA,uCAAA,CAAA,qGAAA,CAAA,+BAAA,CAAA,6FFmTR,CEzFE,yKAGI,QAAA,CAEA,SAAA,CACA,UAAA,CACA,WFyFN,CEpFI,wGAEI,UAAA,CAAA,oCAAA,CACA,WAAA,CAAA,qCFqFR,CEpTE,yKVlHI,qBQyaN,CE/QI,2FAGI,WF+QR,CE7OM,kPAeF,gCAAA,CAAA,wBAAA,CAIA,WF8NJ,CElNI,8GAEI,sCAAA,CAAA,8BFmNR,CE9MM,qHAEI,gCAAA,CAAA,wBAAA,CAKA,WF2MV,CErME,kGAEI,4BFsMN,CExKI,gGAGI,WFwKR,CE3UE,+LVjFI,wBQ+ZN,CRvZU,mCAEE,+LAEE,wBAAA,CAAA,iDQwZZ,CACF,CE5SI,sGAGI,WF4SR,CE1QM,wQAeF,gCAAA,CAAA,wBAAA,CAIA,WF2PJ,CE/OI,yHAEI,sCAAA,CAAA,8BFgPR,CE3OM,gIAEI,gCAAA,CAAA,wBAAA,CAKA,WFwOV,CElOE,6GAEI,4BFmON,CEzKI,+FAGI,WFyKR,CExWE,6LVjFI,wBQ4bN,CRpbU,mCAEE,6LAEE,wBAAA,CAAA,iDQqbZ,CACF,CEzUI,qGAGI,WFyUR,CEvSM,sQAeF,gCAAA,CAAA,wBAAA,CAIA,UFwRJ,CE5QI,wHAEI,sCAAA,CAAA,8BF6QR,CExQM,+HAEI,gCAAA,CAAA,wBAAA,CAKA,UFqQV,CE/PE,4GAEI,2BFgQN,CF9QE,0DIjNE,sBAAA,CACA,mBAAA,CACA,kBAAA,CACA,uBAAA,CACA,+BAAA,CACA,iCAAA,CAEA,yCFkeJ,CE9dE,iIAGI,iBAAA,CACA,iBAAA,CACA,SAAA,CACA,mBAAA,CACA,UF8dN,CE1dE,iEAGI,mEAAA,CAAA,2DAAA,CAMA,SFqdN,CE9cI,qFAEI,0BAAA,CAAA,qDAAA,CAAA,kBAAA,CAAA,6CFgdR,CE5cI,oFAEI,KAAA,CAEA,MAAA,CACA,0BAAA,CAAA,kBAAA,CACA,sCAAA,CAAA,8BF+cR,CEzcI,+FAEI,KAAA,CAAA,2BAAA,CAEA,MAAA,CAAA,6BF0cR,CEpcI,2GAEI,+FAAA,CAAA,uFFscR,CE9bI,6GAEI,gDAAA,CAAA,wCAAA,CAKA,uCAAA,CAAA,qGAAA,CAAA,+BAAA,CAAA,6FF6bR,CEnOE,iIAGI,QAAA,CAEA,SAAA,CACA,UAAA,CACA,WFmON,CE9NI,oFAEI,UAAA,CAAA,oCAAA,CACA,WAAA,CAAA,qCF+NR,CE9bE,iIVlHI,qBQmjBN,CEpXM,0MAeF,gCAAA,CAAA,wBAAA,CAIA,WFqWJ,CG3oBA,uBACE,UACF,CAEA,iCACE,eAAgB,CAChB,YAAa,CACb,6CAAyC,CAAzC,qCAAyC,CACzC,SACF,CAEA,8DACE,SACF,CAEA,oDACE,wCAA0B,CAA1B,gCAA0B,CAA1B,wBAA0B,CAA1B,8CAA0B,CAC1B,wBAAiB,CAAjB,qBAAiB,CAAjB,oBAAiB,CAAjB,gBACF,CAEA,iFAGE,+BAAwB,CAAxB,uBACF,CAEA,gDACE,iBACF,CAEA,uCACE,aACF,CC5BA,kBACE,aAAc,CACd,cAAe,CACf,2BAA4B,CAC5B,gBAAiB,CACjB,mBAAoB,CACpB,uBACF,CAEA,wBACE,kBAAmB,CACnB,SAAU,CACV,UACF,CAEA,uBACE,iBAAkB,CAClB,SAAU,CACV,UACF,CAEA,wBACE,gBAAiB,CACjB,SAAU,CACV,UACF,CAEA,uBACE,iBAAkB,CAClB,SAAU,CACV,UACF,CAEA,wBACE,cAAe,CACf,SAAU,CACV,UACF","file":"2.c454aab8.chunk.css","sourcesContent":["@charset \"UTF-8\";\n.react-datepicker__year-read-view--down-arrow,\n.react-datepicker__month-read-view--down-arrow,\n.react-datepicker__month-year-read-view--down-arrow, .react-datepicker__navigation-icon::before {\n border-color: #ccc;\n border-style: solid;\n border-width: 3px 3px 0 0;\n content: \"\";\n display: block;\n height: 9px;\n position: absolute;\n top: 6px;\n width: 9px;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle {\n margin-left: -4px;\n position: absolute;\n width: 0;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::after, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::after {\n box-sizing: content-box;\n position: absolute;\n border: 8px solid transparent;\n height: 0;\n width: 1px;\n content: \"\";\n z-index: -1;\n border-width: 8px;\n left: -8px;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before {\n border-bottom-color: #aeaeae;\n}\n\n.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle {\n top: 0;\n margin-top: -8px;\n}\n.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::after {\n border-top: none;\n border-bottom-color: #f0f0f0;\n}\n.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::after {\n top: 0;\n}\n.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before {\n top: -1px;\n border-bottom-color: #aeaeae;\n}\n\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle {\n bottom: 0;\n margin-bottom: -8px;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::after {\n border-bottom: none;\n border-top-color: #fff;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::after {\n bottom: 0;\n}\n.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle::before {\n bottom: -1px;\n border-top-color: #aeaeae;\n}\n\n.react-datepicker-wrapper {\n display: inline-block;\n padding: 0;\n border: 0;\n width: 100%;\n}\n\n.react-datepicker {\n font-family: \"Helvetica Neue\", helvetica, arial, sans-serif;\n font-size: 0.8rem;\n background-color: #fff;\n color: #000;\n border: 1px solid #aeaeae;\n border-radius: 0.3rem;\n display: inline-block;\n position: relative;\n}\n\n.react-datepicker--time-only .react-datepicker__triangle {\n left: 35px;\n}\n.react-datepicker--time-only .react-datepicker__time-container {\n border-left: 0;\n}\n.react-datepicker--time-only .react-datepicker__time,\n.react-datepicker--time-only .react-datepicker__time-box {\n border-bottom-left-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.react-datepicker__triangle {\n position: absolute;\n left: 50px;\n}\n\n.react-datepicker-popper {\n z-index: 1;\n}\n.react-datepicker-popper[data-placement^=bottom] {\n padding-top: 10px;\n}\n.react-datepicker-popper[data-placement=bottom-end] .react-datepicker__triangle, .react-datepicker-popper[data-placement=top-end] .react-datepicker__triangle {\n left: auto;\n right: 50px;\n}\n.react-datepicker-popper[data-placement^=top] {\n padding-bottom: 10px;\n}\n.react-datepicker-popper[data-placement^=right] {\n padding-left: 8px;\n}\n.react-datepicker-popper[data-placement^=right] .react-datepicker__triangle {\n left: auto;\n right: 42px;\n}\n.react-datepicker-popper[data-placement^=left] {\n padding-right: 8px;\n}\n.react-datepicker-popper[data-placement^=left] .react-datepicker__triangle {\n left: 42px;\n right: auto;\n}\n\n.react-datepicker__header {\n text-align: center;\n background-color: #f0f0f0;\n border-bottom: 1px solid #aeaeae;\n border-top-left-radius: 0.3rem;\n padding: 8px 0;\n position: relative;\n}\n.react-datepicker__header--time {\n padding-bottom: 8px;\n padding-left: 5px;\n padding-right: 5px;\n}\n.react-datepicker__header--time:not(.react-datepicker__header--time--only) {\n border-top-left-radius: 0;\n}\n.react-datepicker__header:not(.react-datepicker__header--has-time-select) {\n border-top-right-radius: 0.3rem;\n}\n\n.react-datepicker__year-dropdown-container--select,\n.react-datepicker__month-dropdown-container--select,\n.react-datepicker__month-year-dropdown-container--select,\n.react-datepicker__year-dropdown-container--scroll,\n.react-datepicker__month-dropdown-container--scroll,\n.react-datepicker__month-year-dropdown-container--scroll {\n display: inline-block;\n margin: 0 2px;\n}\n\n.react-datepicker__current-month,\n.react-datepicker-time__header,\n.react-datepicker-year-header {\n margin-top: 0;\n color: #000;\n font-weight: bold;\n font-size: 0.944rem;\n}\n\n.react-datepicker-time__header {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.react-datepicker__navigation {\n align-items: center;\n background: none;\n display: flex;\n justify-content: center;\n text-align: center;\n cursor: pointer;\n position: absolute;\n top: 2px;\n padding: 0;\n border: none;\n z-index: 1;\n height: 32px;\n width: 32px;\n text-indent: -999em;\n overflow: hidden;\n}\n.react-datepicker__navigation--previous {\n left: 2px;\n}\n.react-datepicker__navigation--next {\n right: 2px;\n}\n.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button) {\n right: 85px;\n}\n.react-datepicker__navigation--years {\n position: relative;\n top: 0;\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.react-datepicker__navigation--years-previous {\n top: 4px;\n}\n.react-datepicker__navigation--years-upcoming {\n top: -4px;\n}\n.react-datepicker__navigation:hover *::before {\n border-color: #a6a6a6;\n}\n\n.react-datepicker__navigation-icon {\n position: relative;\n top: -1px;\n font-size: 20px;\n width: 0;\n}\n.react-datepicker__navigation-icon--next {\n left: -2px;\n}\n.react-datepicker__navigation-icon--next::before {\n transform: rotate(45deg);\n left: -7px;\n}\n.react-datepicker__navigation-icon--previous {\n right: -2px;\n}\n.react-datepicker__navigation-icon--previous::before {\n transform: rotate(225deg);\n right: -7px;\n}\n\n.react-datepicker__month-container {\n float: left;\n}\n\n.react-datepicker__year {\n margin: 0.4rem;\n text-align: center;\n}\n.react-datepicker__year-wrapper {\n display: flex;\n flex-wrap: wrap;\n max-width: 180px;\n}\n.react-datepicker__year .react-datepicker__year-text {\n display: inline-block;\n width: 4rem;\n margin: 2px;\n}\n\n.react-datepicker__month {\n margin: 0.4rem;\n text-align: center;\n}\n.react-datepicker__month .react-datepicker__month-text,\n.react-datepicker__month .react-datepicker__quarter-text {\n display: inline-block;\n width: 4rem;\n margin: 2px;\n}\n\n.react-datepicker__input-time-container {\n clear: both;\n width: 100%;\n float: left;\n margin: 5px 0 10px 15px;\n text-align: left;\n}\n.react-datepicker__input-time-container .react-datepicker-time__caption {\n display: inline-block;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container {\n display: inline-block;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input {\n display: inline-block;\n margin-left: 10px;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input {\n width: auto;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-inner-spin-button,\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-outer-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time] {\n -moz-appearance: textfield;\n}\n.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__delimiter {\n margin-left: 5px;\n display: inline-block;\n}\n\n.react-datepicker__time-container {\n float: right;\n border-left: 1px solid #aeaeae;\n width: 85px;\n}\n.react-datepicker__time-container--with-today-button {\n display: inline;\n border: 1px solid #aeaeae;\n border-radius: 0.3rem;\n position: absolute;\n right: -72px;\n top: 0;\n}\n.react-datepicker__time-container .react-datepicker__time {\n position: relative;\n background: white;\n border-bottom-right-radius: 0.3rem;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box {\n width: 85px;\n overflow-x: hidden;\n margin: 0 auto;\n text-align: center;\n border-bottom-right-radius: 0.3rem;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list {\n list-style: none;\n margin: 0;\n height: calc(195px + (1.7rem / 2));\n overflow-y: scroll;\n padding-right: 0;\n padding-left: 0;\n width: 100%;\n box-sizing: content-box;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item {\n height: 30px;\n padding: 5px 10px;\n white-space: nowrap;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item:hover {\n cursor: pointer;\n background-color: #f0f0f0;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected {\n background-color: #216ba5;\n color: white;\n font-weight: bold;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected:hover {\n background-color: #216ba5;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled {\n color: #ccc;\n}\n.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled:hover {\n cursor: default;\n background-color: transparent;\n}\n\n.react-datepicker__week-number {\n color: #ccc;\n display: inline-block;\n width: 1.7rem;\n line-height: 1.7rem;\n text-align: center;\n margin: 0.166rem;\n}\n.react-datepicker__week-number.react-datepicker__week-number--clickable {\n cursor: pointer;\n}\n.react-datepicker__week-number.react-datepicker__week-number--clickable:hover {\n border-radius: 0.3rem;\n background-color: #f0f0f0;\n}\n\n.react-datepicker__day-names,\n.react-datepicker__week {\n white-space: nowrap;\n}\n\n.react-datepicker__day-names {\n margin-bottom: -8px;\n}\n\n.react-datepicker__day-name,\n.react-datepicker__day,\n.react-datepicker__time-name {\n color: #000;\n display: inline-block;\n width: 1.7rem;\n line-height: 1.7rem;\n text-align: center;\n margin: 0.166rem;\n}\n\n.react-datepicker__month--selected, .react-datepicker__month--in-selecting-range, .react-datepicker__month--in-range,\n.react-datepicker__quarter--selected,\n.react-datepicker__quarter--in-selecting-range,\n.react-datepicker__quarter--in-range {\n border-radius: 0.3rem;\n background-color: #216ba5;\n color: #fff;\n}\n.react-datepicker__month--selected:hover, .react-datepicker__month--in-selecting-range:hover, .react-datepicker__month--in-range:hover,\n.react-datepicker__quarter--selected:hover,\n.react-datepicker__quarter--in-selecting-range:hover,\n.react-datepicker__quarter--in-range:hover {\n background-color: #1d5d90;\n}\n.react-datepicker__month--disabled,\n.react-datepicker__quarter--disabled {\n color: #ccc;\n pointer-events: none;\n}\n.react-datepicker__month--disabled:hover,\n.react-datepicker__quarter--disabled:hover {\n cursor: default;\n background-color: transparent;\n}\n\n.react-datepicker__day,\n.react-datepicker__month-text,\n.react-datepicker__quarter-text,\n.react-datepicker__year-text {\n cursor: pointer;\n}\n.react-datepicker__day:hover,\n.react-datepicker__month-text:hover,\n.react-datepicker__quarter-text:hover,\n.react-datepicker__year-text:hover {\n border-radius: 0.3rem;\n background-color: #f0f0f0;\n}\n.react-datepicker__day--today,\n.react-datepicker__month-text--today,\n.react-datepicker__quarter-text--today,\n.react-datepicker__year-text--today {\n font-weight: bold;\n}\n.react-datepicker__day--highlighted,\n.react-datepicker__month-text--highlighted,\n.react-datepicker__quarter-text--highlighted,\n.react-datepicker__year-text--highlighted {\n border-radius: 0.3rem;\n background-color: #3dcc4a;\n color: #fff;\n}\n.react-datepicker__day--highlighted:hover,\n.react-datepicker__month-text--highlighted:hover,\n.react-datepicker__quarter-text--highlighted:hover,\n.react-datepicker__year-text--highlighted:hover {\n background-color: #32be3f;\n}\n.react-datepicker__day--highlighted-custom-1,\n.react-datepicker__month-text--highlighted-custom-1,\n.react-datepicker__quarter-text--highlighted-custom-1,\n.react-datepicker__year-text--highlighted-custom-1 {\n color: magenta;\n}\n.react-datepicker__day--highlighted-custom-2,\n.react-datepicker__month-text--highlighted-custom-2,\n.react-datepicker__quarter-text--highlighted-custom-2,\n.react-datepicker__year-text--highlighted-custom-2 {\n color: green;\n}\n.react-datepicker__day--selected, .react-datepicker__day--in-selecting-range, .react-datepicker__day--in-range,\n.react-datepicker__month-text--selected,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--selected,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--selected,\n.react-datepicker__year-text--in-selecting-range,\n.react-datepicker__year-text--in-range {\n border-radius: 0.3rem;\n background-color: #216ba5;\n color: #fff;\n}\n.react-datepicker__day--selected:hover, .react-datepicker__day--in-selecting-range:hover, .react-datepicker__day--in-range:hover,\n.react-datepicker__month-text--selected:hover,\n.react-datepicker__month-text--in-selecting-range:hover,\n.react-datepicker__month-text--in-range:hover,\n.react-datepicker__quarter-text--selected:hover,\n.react-datepicker__quarter-text--in-selecting-range:hover,\n.react-datepicker__quarter-text--in-range:hover,\n.react-datepicker__year-text--selected:hover,\n.react-datepicker__year-text--in-selecting-range:hover,\n.react-datepicker__year-text--in-range:hover {\n background-color: #1d5d90;\n}\n.react-datepicker__day--keyboard-selected,\n.react-datepicker__month-text--keyboard-selected,\n.react-datepicker__quarter-text--keyboard-selected,\n.react-datepicker__year-text--keyboard-selected {\n border-radius: 0.3rem;\n background-color: #2a87d0;\n color: #fff;\n}\n.react-datepicker__day--keyboard-selected:hover,\n.react-datepicker__month-text--keyboard-selected:hover,\n.react-datepicker__quarter-text--keyboard-selected:hover,\n.react-datepicker__year-text--keyboard-selected:hover {\n background-color: #1d5d90;\n}\n.react-datepicker__day--in-selecting-range:not(.react-datepicker__day--in-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--in-range),\n.react-datepicker__month-text--in-selecting-range:not(.react-datepicker__day--in-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--in-range),\n.react-datepicker__quarter-text--in-selecting-range:not(.react-datepicker__day--in-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--in-range),\n.react-datepicker__year-text--in-selecting-range:not(.react-datepicker__day--in-range,\n.react-datepicker__month-text--in-range,\n.react-datepicker__quarter-text--in-range,\n.react-datepicker__year-text--in-range) {\n background-color: rgba(33, 107, 165, 0.5);\n}\n.react-datepicker__month--selecting-range .react-datepicker__day--in-range:not(.react-datepicker__day--in-selecting-range,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__year-text--in-selecting-range),\n.react-datepicker__month--selecting-range .react-datepicker__month-text--in-range:not(.react-datepicker__day--in-selecting-range,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__year-text--in-selecting-range),\n.react-datepicker__month--selecting-range .react-datepicker__quarter-text--in-range:not(.react-datepicker__day--in-selecting-range,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__year-text--in-selecting-range),\n.react-datepicker__month--selecting-range .react-datepicker__year-text--in-range:not(.react-datepicker__day--in-selecting-range,\n.react-datepicker__month-text--in-selecting-range,\n.react-datepicker__quarter-text--in-selecting-range,\n.react-datepicker__year-text--in-selecting-range) {\n background-color: #f0f0f0;\n color: #000;\n}\n.react-datepicker__day--disabled,\n.react-datepicker__month-text--disabled,\n.react-datepicker__quarter-text--disabled,\n.react-datepicker__year-text--disabled {\n cursor: default;\n color: #ccc;\n}\n.react-datepicker__day--disabled:hover,\n.react-datepicker__month-text--disabled:hover,\n.react-datepicker__quarter-text--disabled:hover,\n.react-datepicker__year-text--disabled:hover {\n background-color: transparent;\n}\n\n.react-datepicker__month-text.react-datepicker__month--selected:hover, .react-datepicker__month-text.react-datepicker__month--in-range:hover, .react-datepicker__month-text.react-datepicker__quarter--selected:hover, .react-datepicker__month-text.react-datepicker__quarter--in-range:hover,\n.react-datepicker__quarter-text.react-datepicker__month--selected:hover,\n.react-datepicker__quarter-text.react-datepicker__month--in-range:hover,\n.react-datepicker__quarter-text.react-datepicker__quarter--selected:hover,\n.react-datepicker__quarter-text.react-datepicker__quarter--in-range:hover {\n background-color: #216ba5;\n}\n.react-datepicker__month-text:hover,\n.react-datepicker__quarter-text:hover {\n background-color: #f0f0f0;\n}\n\n.react-datepicker__input-container {\n position: relative;\n display: inline-block;\n width: 100%;\n}\n\n.react-datepicker__year-read-view,\n.react-datepicker__month-read-view,\n.react-datepicker__month-year-read-view {\n border: 1px solid transparent;\n border-radius: 0.3rem;\n position: relative;\n}\n.react-datepicker__year-read-view:hover,\n.react-datepicker__month-read-view:hover,\n.react-datepicker__month-year-read-view:hover {\n cursor: pointer;\n}\n.react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow,\n.react-datepicker__year-read-view:hover .react-datepicker__month-read-view--down-arrow,\n.react-datepicker__month-read-view:hover .react-datepicker__year-read-view--down-arrow,\n.react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow,\n.react-datepicker__month-year-read-view:hover .react-datepicker__year-read-view--down-arrow,\n.react-datepicker__month-year-read-view:hover .react-datepicker__month-read-view--down-arrow {\n border-top-color: #b3b3b3;\n}\n.react-datepicker__year-read-view--down-arrow,\n.react-datepicker__month-read-view--down-arrow,\n.react-datepicker__month-year-read-view--down-arrow {\n transform: rotate(135deg);\n right: -16px;\n top: 0;\n}\n\n.react-datepicker__year-dropdown,\n.react-datepicker__month-dropdown,\n.react-datepicker__month-year-dropdown {\n background-color: #f0f0f0;\n position: absolute;\n width: 50%;\n left: 25%;\n top: 30px;\n z-index: 1;\n text-align: center;\n border-radius: 0.3rem;\n border: 1px solid #aeaeae;\n}\n.react-datepicker__year-dropdown:hover,\n.react-datepicker__month-dropdown:hover,\n.react-datepicker__month-year-dropdown:hover {\n cursor: pointer;\n}\n.react-datepicker__year-dropdown--scrollable,\n.react-datepicker__month-dropdown--scrollable,\n.react-datepicker__month-year-dropdown--scrollable {\n height: 150px;\n overflow-y: scroll;\n}\n\n.react-datepicker__year-option,\n.react-datepicker__month-option,\n.react-datepicker__month-year-option {\n line-height: 20px;\n width: 100%;\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.react-datepicker__year-option:first-of-type,\n.react-datepicker__month-option:first-of-type,\n.react-datepicker__month-year-option:first-of-type {\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n.react-datepicker__year-option:last-of-type,\n.react-datepicker__month-option:last-of-type,\n.react-datepicker__month-year-option:last-of-type {\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n border-bottom-left-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n.react-datepicker__year-option:hover,\n.react-datepicker__month-option:hover,\n.react-datepicker__month-year-option:hover {\n background-color: #ccc;\n}\n.react-datepicker__year-option:hover .react-datepicker__navigation--years-upcoming,\n.react-datepicker__month-option:hover .react-datepicker__navigation--years-upcoming,\n.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-upcoming {\n border-bottom-color: #b3b3b3;\n}\n.react-datepicker__year-option:hover .react-datepicker__navigation--years-previous,\n.react-datepicker__month-option:hover .react-datepicker__navigation--years-previous,\n.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-previous {\n border-top-color: #b3b3b3;\n}\n.react-datepicker__year-option--selected,\n.react-datepicker__month-option--selected,\n.react-datepicker__month-year-option--selected {\n position: absolute;\n left: 15px;\n}\n\n.react-datepicker__close-icon {\n cursor: pointer;\n background-color: transparent;\n border: 0;\n outline: 0;\n padding: 0 6px 0 0;\n position: absolute;\n top: 0;\n right: 0;\n height: 100%;\n display: table-cell;\n vertical-align: middle;\n}\n.react-datepicker__close-icon::after {\n cursor: pointer;\n background-color: #216ba5;\n color: #fff;\n border-radius: 50%;\n height: 16px;\n width: 16px;\n padding: 2px;\n font-size: 12px;\n line-height: 1;\n text-align: center;\n display: table-cell;\n vertical-align: middle;\n content: \"×\";\n}\n\n.react-datepicker__today-button {\n background: #f0f0f0;\n border-top: 1px solid #aeaeae;\n cursor: pointer;\n text-align: center;\n font-weight: bold;\n padding: 5px 0;\n clear: left;\n}\n\n.react-datepicker__portal {\n position: fixed;\n width: 100vw;\n height: 100vh;\n background-color: rgba(0, 0, 0, 0.8);\n left: 0;\n top: 0;\n justify-content: center;\n align-items: center;\n display: flex;\n z-index: 2147483647;\n}\n.react-datepicker__portal .react-datepicker__day-name,\n.react-datepicker__portal .react-datepicker__day,\n.react-datepicker__portal .react-datepicker__time-name {\n width: 3rem;\n line-height: 3rem;\n}\n@media (max-width: 400px), (max-height: 550px) {\n .react-datepicker__portal .react-datepicker__day-name,\n.react-datepicker__portal .react-datepicker__day,\n.react-datepicker__portal .react-datepicker__time-name {\n width: 2rem;\n line-height: 2rem;\n }\n}\n.react-datepicker__portal .react-datepicker__current-month,\n.react-datepicker__portal .react-datepicker-time__header {\n font-size: 1.44rem;\n}\n",".CodeMirror {\n height: 25px;\n}\n.ReactCodeMirror {\n height: 30px;\n}\n.cm-category {\n color: #2196F3;\n}\n.cm-operator {\n color: #9E9E9E;\n}\n.cm-value {\n color: #E91E63;\n}\n.react-filter-box {\n overflow-y: hidden;\n height: 28px;\n padding: 4px 6px;\n margin-bottom: 10px;\n font-size: 14px;\n line-height: 20px;\n color: #555555;\n vertical-align: middle;\n -webkit-border-radius: 4px;\n -moz-border-radius: 4px;\n border-radius: 4px;\n background-color: #ffffff;\n border: 1px solid #cccccc;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;\n -moz-transition: border linear 0.2s, box-shadow linear 0.2s;\n -o-transition: border linear 0.2s, box-shadow linear 0.2s;\n transition: border linear 0.2s, box-shadow linear 0.2s;\n}\n.react-filter-box.focus {\n border-color: #66afe9;\n outline: 0;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.react-filter-box.error {\n border-color: #a94442;\n outline: 0;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.react-filter-box.error.focus {\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.CodeMirror-hints {\n padding: 5px;\n min-width: 100px;\n}\n.CodeMirror-hints .CodeMirror-hint {\n padding: 5px 5px;\n}\nli.CodeMirror-hint-active {\n background: #2196F3;\n}\nli.CodeMirror-hint-active .hint-value {\n color: white;\n}\n","/*!\n Material Components for the Web\n Copyright (c) 2019 Google Inc.\n License: MIT\n*/\n.mdc-menu-surface {\n display: none;\n position: absolute;\n box-sizing: border-box;\n max-width: calc(100vw - 32px);\n max-height: calc(100vh - 32px);\n margin: 0;\n padding: 0;\n -webkit-transform: scale(1);\n transform: scale(1);\n -webkit-transform-origin: top left;\n transform-origin: top left;\n opacity: 0;\n overflow: auto;\n will-change: transform, opacity;\n z-index: 8;\n transition: opacity 0.03s linear, -webkit-transform 0.12s cubic-bezier(0, 0, 0.2, 1);\n transition: opacity 0.03s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1);\n transition: opacity 0.03s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1), -webkit-transform 0.12s cubic-bezier(0, 0, 0.2, 1);\n box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12);\n background-color: #fff;\n /* @alternate */\n background-color: var(--mdc-theme-surface, #fff);\n color: #000;\n /* @alternate */\n color: var(--mdc-theme-on-surface, #000);\n border-radius: 4px;\n /* @noflip */\n transform-origin-left: top left;\n /* @noflip */\n transform-origin-right: top right;\n}\n.mdc-menu-surface:focus {\n outline: none;\n}\n.mdc-menu-surface--open {\n display: inline-block;\n -webkit-transform: scale(1);\n transform: scale(1);\n opacity: 1;\n}\n.mdc-menu-surface--animating-open {\n display: inline-block;\n -webkit-transform: scale(0.8);\n transform: scale(0.8);\n opacity: 0;\n}\n.mdc-menu-surface--animating-closed {\n display: inline-block;\n opacity: 0;\n transition: opacity 0.075s linear;\n}\n[dir=rtl] .mdc-menu-surface, .mdc-menu-surface[dir=rtl] {\n /* @noflip */\n transform-origin-left: top right;\n /* @noflip */\n transform-origin-right: top left;\n}\n\n.mdc-menu-surface--anchor {\n position: relative;\n overflow: visible;\n}\n\n.mdc-menu-surface--fixed {\n position: fixed;\n}\n/*# sourceMappingURL=mdc.menu-surface.css.map*/","//\n// Copyright 2018 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"@material/theme/mixins\";\n@import \"@material/shape/mixins\";\n@import \"@material/animation/variables\";\n@import \"@material/elevation/mixins\";\n@import \"@material/rtl/mixins\";\n@import \"./variables\";\n\n//\n// Public\n//\n\n@mixin mdc-menu-surface-core-styles($query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n\n // postcss-bem-linter: define menu-surface\n .mdc-menu-surface {\n @include mdc-menu-surface-base_($query);\n @include mdc-elevation($z-value: 8, $query: $query);\n @include mdc-menu-surface-fill-color(surface, $query);\n @include mdc-menu-surface-ink-color(on-surface, $query);\n @include mdc-menu-surface-shape-radius(medium, false, $query);\n\n @include mdc-feature-targets($feat-structure) {\n @include mdc-rtl-reflexive-property(transform-origin, top left, top right);\n }\n }\n\n .mdc-menu-surface--anchor {\n @include mdc-feature-targets($feat-structure) {\n position: relative;\n overflow: visible;\n }\n }\n\n .mdc-menu-surface--fixed {\n @include mdc-feature-targets($feat-structure) {\n position: fixed;\n }\n }\n // postcss-bem-linter: end\n}\n\n@mixin mdc-menu-surface-ink-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $color);\n }\n}\n\n@mixin mdc-menu-surface-fill-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(background-color, $color);\n }\n}\n\n@mixin mdc-menu-surface-shape-radius($radius, $rtl-reflexive: false, $query: mdc-feature-all()) {\n @include mdc-shape-radius($radius, $rtl-reflexive, $query: $query);\n}\n\n//\n// Private\n//\n\n@mixin mdc-menu-surface-base_($query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n $feat-animation: mdc-feature-create-target($query, animation);\n\n @include mdc-feature-targets($feat-structure) {\n display: none;\n position: absolute;\n box-sizing: border-box;\n max-width: calc(100vw - #{$mdc-menu-surface-min-distance-from-edge});\n max-height: calc(100vh - #{$mdc-menu-surface-min-distance-from-edge});\n margin: 0;\n padding: 0;\n transform: scale(1);\n transform-origin: top left;\n opacity: 0;\n overflow: auto;\n will-change: transform, opacity;\n z-index: $mdc-menu-surface-z-index;\n }\n\n @include mdc-feature-targets($feat-animation) {\n transition:\n opacity $mdc-menu-surface-fade-in-duration linear,\n transform $mdc-menu-surface-scale-duration $mdc-animation-deceleration-curve-timing-function;\n }\n\n &:focus {\n @include mdc-feature-targets($feat-structure) {\n outline: none;\n }\n }\n\n // stylelint-disable-next-line selector-max-type\n &--open {\n @include mdc-feature-targets($feat-structure) {\n display: inline-block;\n transform: scale(1);\n opacity: 1;\n }\n }\n\n &--animating-open {\n @include mdc-feature-targets($feat-structure) {\n display: inline-block;\n transform: scale(.8);\n opacity: 0;\n }\n }\n\n &--animating-closed {\n @include mdc-feature-targets($feat-structure) {\n display: inline-block;\n opacity: 0;\n }\n\n @include mdc-feature-targets($feat-animation) {\n transition: opacity $mdc-menu-surface-fade-out-duration linear;\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./packages/mdc-menu-surface/mdc-menu-surface.scss","//\n// Copyright 2017 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"@material/theme/variables\";\n@import \"./variables\";\n\n@mixin mdc-elevation-core-styles($query: mdc-feature-all()) {\n $feat-animation: mdc-feature-create-target($query, animation);\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @for $z-value from 0 through 24 {\n .mdc-elevation--z#{$z-value} {\n @include mdc-elevation($z-value, $query: $query);\n }\n }\n\n .mdc-elevation-transition {\n @include mdc-feature-targets($feat-animation) {\n transition: mdc-elevation-transition-value();\n }\n\n @include mdc-feature-targets($feat-structure) {\n will-change: $mdc-elevation-property;\n }\n }\n}\n\n// Applies the correct CSS rules to an element to give it the elevation specified by $z-value.\n// The $z-value must be between 0 and 24.\n// If $color has an alpha channel, it will be ignored and overridden. To increase the opacity of the shadow, use\n// $opacity-boost.\n@mixin mdc-elevation($z-value, $color: $mdc-elevation-baseline-color, $opacity-boost: 0, $query: mdc-feature-all()) {\n @if type-of($z-value) != number or not unitless($z-value) {\n @error \"$z-value must be a unitless number, but received '#{$z-value}'\";\n }\n\n @if $z-value < 0 or $z-value > 24 {\n @error \"$z-value must be between 0 and 24, but received '#{$z-value}'\";\n }\n\n $feat-color: mdc-feature-create-target($query, color);\n\n $color: mdc-theme-prop-value($color);\n\n $umbra-z-value: map-get($mdc-elevation-umbra-map, $z-value);\n $penumbra-z-value: map-get($mdc-elevation-penumbra-map, $z-value);\n $ambient-z-value: map-get($mdc-elevation-ambient-map, $z-value);\n\n $umbra-color: rgba($color, $mdc-elevation-umbra-opacity + $opacity-boost);\n $penumbra-color: rgba($color, $mdc-elevation-penumbra-opacity + $opacity-boost);\n $ambient-color: rgba($color, $mdc-elevation-ambient-opacity + $opacity-boost);\n\n @include mdc-feature-targets($feat-color) {\n box-shadow:\n #{\"#{$umbra-z-value} #{$umbra-color}\"},\n #{\"#{$penumbra-z-value} #{$penumbra-color}\"},\n #{$ambient-z-value} $ambient-color;\n }\n}\n\n// Returns a string that can be used as the value for a `transition` property for elevation.\n// Calling this function directly is useful in situations where a component needs to transition\n// more than one property.\n//\n// ```scss\n// .foo {\n// transition: mdc-elevation-transition-value(), opacity 100ms ease;\n// will-change: $mdc-elevation-property, opacity;\n// }\n// ```\n@function mdc-elevation-transition-value(\n $duration: $mdc-elevation-transition-duration,\n $easing: $mdc-elevation-transition-timing-function\n) {\n @return #{$mdc-elevation-property} #{$duration} #{$easing};\n}\n\n\n\n// WEBPACK FOOTER //\n// ./packages/material-components-web/node_modules/@material/elevation/_mixins.scss","//\n// Copyright 2017 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"./variables\";\n@import \"./functions\";\n\n@mixin mdc-theme-core-styles($query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n :root {\n @include mdc-feature-targets($feat-color) {\n @each $style in map-keys($mdc-theme-property-values) {\n --mdc-theme-#{$style}: #{map-get($mdc-theme-property-values, $style)};\n }\n }\n }\n\n @each $style in map-keys($mdc-theme-property-values) {\n @if $style != \"background\" and $style != \"surface\" {\n .mdc-theme--#{$style} {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $style, true);\n }\n }\n } @else {\n .mdc-theme--#{$style} {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(background-color, $style);\n }\n }\n }\n }\n\n // CSS rules for using primary and secondary (plus light/dark variants) as background colors.\n @each $style in (\"primary\", \"secondary\") {\n .mdc-theme--#{$style}-bg {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(background-color, $style, true);\n }\n }\n }\n}\n\n// Applies the correct theme color style to the specified property.\n// $property is typically color or background-color, but can be any CSS property that accepts color values.\n// $style should be one of the map keys in $mdc-theme-property-values (_variables.scss), or a color value.\n// $edgeOptOut controls whether to feature-detect around Edge to avoid emitting CSS variables for it,\n// intended for use in cases where interactions with pseudo-element styles cause problems due to Edge bugs.\n@mixin mdc-theme-prop($property, $style, $important: false, $edgeOptOut: false) {\n @if mdc-theme-is-var-with-fallback_($style) {\n @if $important {\n #{$property}: mdc-theme-get-var-fallback_($style) !important;\n /* @alternate */\n #{$property}: mdc-theme-var_($style) !important;\n } @else {\n #{$property}: mdc-theme-get-var-fallback_($style);\n /* @alternate */\n #{$property}: mdc-theme-var_($style);\n }\n } @else if mdc-theme-is-valid-theme-prop-value_($style) {\n @if $important {\n #{$property}: $style !important;\n } @else {\n #{$property}: $style;\n }\n } @else {\n @if not map-has-key($mdc-theme-property-values, $style) {\n @error \"Invalid style: '#{$style}'. Choose one of: #{map-keys($mdc-theme-property-values)}\";\n }\n\n $value: map-get($mdc-theme-property-values, $style);\n\n @if $important {\n #{$property}: $value !important;\n\n @if $edgeOptOut {\n // stylelint-disable max-nesting-depth\n @at-root {\n // IE 11 doesn't understand this syntax and ignores the entire block.\n // Edge understands this syntax and skips the entire block to avoid a nasty :before/:after pseudo-element bug.\n // All other browsers apply the styles within the block.\n @supports not (-ms-ime-align: auto) {\n // stylelint-disable scss/selector-no-redundant-nesting-selector\n & {\n /* @alternate */\n #{$property}: var(--mdc-theme-#{$style}, $value) !important;\n }\n // stylelint-enable scss/selector-no-redundant-nesting-selector\n }\n }\n // stylelint-enable max-nesting-depth\n } @else {\n /* @alternate */\n #{$property}: var(--mdc-theme-#{$style}, $value) !important;\n }\n } @else {\n #{$property}: $value;\n\n @if $edgeOptOut {\n // stylelint-disable max-nesting-depth\n @at-root {\n // IE 11 doesn't understand this syntax and ignores the entire block.\n // Edge understands this syntax and skips the entire block to avoid a nasty :before/:after pseudo-element bug.\n // All other browsers apply the styles within the block.\n @supports not (-ms-ime-align: auto) {\n // stylelint-disable scss/selector-no-redundant-nesting-selector\n & {\n /* @alternate */\n #{$property}: var(--mdc-theme-#{$style}, $value);\n }\n // stylelint-enable scss/selector-no-redundant-nesting-selector\n }\n }\n // stylelint-enable max-nesting-depth\n } @else {\n /* @alternate */\n #{$property}: var(--mdc-theme-#{$style}, $value);\n }\n }\n }\n}\n","//\n// Copyright 2018 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"./variables\";\n@import \"./functions\";\n\n@mixin mdc-shape-radius($radius, $rtl-reflexive: false, $query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @include mdc-feature-targets($feat-structure) {\n // Even if $rtl-reflexive is true, only emit RTL styles if we can't easily tell that the given radius is symmetrical\n $needs-flip: $rtl-reflexive and length($radius) > 1;\n\n @if ($needs-flip) {\n /* @noflip */\n }\n\n border-radius: mdc-shape-prop-value($radius);\n\n @if ($needs-flip) {\n @include mdc-rtl {\n /* @noflip */\n border-radius: mdc-shape-flip-radius(mdc-shape-prop-value($radius));\n }\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./packages/material-components-web/node_modules/@material/shape/_mixins.scss","//\n// Copyright 2017 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"./variables\";\n\n// Creates a rule that will be applied when an MDC Web component is within the context of an RTL layout.\n//\n// Usage Example:\n//\n// ```scss\n// .mdc-foo {\n// position: absolute;\n// left: 0;\n//\n// @include mdc-rtl {\n// left: auto;\n// right: 0;\n// }\n//\n// &__bar {\n// margin-left: 4px;\n// @include mdc-rtl(\".mdc-foo\") {\n// margin-left: auto;\n// margin-right: 4px;\n// }\n// }\n// }\n//\n// .mdc-foo--mod {\n// padding-left: 4px;\n//\n// @include mdc-rtl {\n// padding-left: auto;\n// padding-right: 4px;\n// }\n// }\n// ```\n//\n// Note that this mixin works by checking for an ancestor element with `[dir=\"rtl\"]`.\n// As a result, nested `dir` values are not supported:\n//\n// ```html\n// \n// \n//
\n//
Styled incorrectly as RTL!
\n//
\n// \n// ```\n//\n// In the future, selectors such as the `:dir` pseudo-class (http://mdn.io/css/:dir) will help us mitigate this.\n@mixin mdc-rtl($root-selector: null) {\n @if ($mdc-rtl-include) {\n @if ($root-selector) {\n @at-root {\n #{$root-selector}[dir=\"rtl\"] &,\n [dir=\"rtl\"] #{$root-selector} & {\n @content;\n }\n }\n } @else {\n [dir=\"rtl\"] &,\n &[dir=\"rtl\"] {\n @content;\n }\n }\n }\n}\n\n// Takes a base box-model property name (`margin`, `border`, `padding`, etc.) along with a\n// default direction (`left` or `right`) and value, and emits rules which apply the given value to the\n// specified direction by default and the opposite direction in RTL.\n//\n// For example:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive-box(margin, left, 8px);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// margin-left: 8px;\n// margin-right: 0;\n//\n// @include mdc-rtl {\n// margin-left: 0;\n// margin-right: 8px;\n// }\n// }\n// ```\n//\n// whereas:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive-box(margin, right, 8px);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// margin-left: 0;\n// margin-right: 8px;\n//\n// @include mdc-rtl {\n// margin-left: 8px;\n// margin-right: 0;\n// }\n// }\n// ```\n//\n// You can also pass an optional 4th `$root-selector` argument which will be forwarded to `mdc-rtl`,\n// e.g. `@include mdc-rtl-reflexive-box(margin, left, 8px, \".mdc-component\")`.\n//\n// Note that this function will always zero out the original value in an RTL context.\n// If you're trying to flip the values, use `mdc-rtl-reflexive-property()` instead.\n@mixin mdc-rtl-reflexive-box($base-property, $default-direction, $value, $root-selector: null) {\n @if (index((right, left), $default-direction) == null) {\n @error \"Invalid default direction: '#{$default-direction}'. Please specifiy either 'right' or 'left'.\";\n }\n\n $left-value: $value;\n $right-value: 0;\n\n @if ($default-direction == right) {\n $left-value: 0;\n $right-value: $value;\n }\n\n @include mdc-rtl-reflexive-property($base-property, $left-value, $right-value, $root-selector);\n}\n\n// Takes a base property and emits rules that assign -left to and\n// -right to in a LTR context, and vice versa in a RTL context.\n// For example:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive-property(margin, auto, 12px);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// margin-left: auto;\n// margin-right: 12px;\n//\n// @include mdc-rtl {\n// margin-left: 12px;\n// margin-right: auto;\n// }\n// }\n// ```\n//\n// An optional 4th `$root-selector` argument can be given, which will be passed to `mdc-rtl`.\n@mixin mdc-rtl-reflexive-property($base-property, $left-value, $right-value, $root-selector: null) {\n $prop-left: #{$base-property}-left;\n $prop-right: #{$base-property}-right;\n\n @include mdc-rtl-reflexive($prop-left, $left-value, $prop-right, $right-value, $root-selector);\n}\n\n// Takes an argument specifying a horizontal position property (either \"left\" or \"right\") as well\n// as a value, and applies that value to the specified position in a LTR context, and flips it in a\n// RTL context. For example:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive-position(left, 0);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// left: 0;\n// right: initial;\n//\n// @include mdc-rtl {\n// left: initial;\n// right: 0;\n// }\n// }\n// ```\n//\n// An optional third $root-selector argument may also be given, which is passed to `mdc-rtl`.\n@mixin mdc-rtl-reflexive-position($position-property, $value, $root-selector: null) {\n @if (index((right, left), $position-property) == null) {\n @error \"Invalid position #{position-property}. Please specifiy either right or left\";\n }\n\n // TODO: \"initial\" is not supported in IE 11. https://caniuse.com/#feat=css-initial-value\n $left-value: $value;\n $right-value: initial;\n\n @if ($position-property == right) {\n $right-value: $value;\n $left-value: initial;\n }\n\n @include mdc-rtl-reflexive(left, $left-value, right, $right-value, $root-selector);\n}\n\n// Takes pair of properties with values as arguments and flips it in RTL context.\n// For example:\n//\n// ```scss\n// .mdc-foo {\n// @include mdc-rtl-reflexive(left, 2px, right, 5px);\n// }\n// ```\n//\n// is equivalent to:\n//\n// ```scss\n// .mdc-foo {\n// left: 2px;\n// right: 5px;\n//\n// @include mdc-rtl {\n// right: 2px;\n// left: 5px;\n// }\n// }\n// ```\n//\n// An optional fifth `$root-selector` argument may also be given, which is passed to `mdc-rtl`.\n@mixin mdc-rtl-reflexive(\n $left-property,\n $left-value,\n $right-property,\n $right-value,\n $root-selector: null\n) {\n @include mdc-rtl-property_($left-property, $left-value);\n @include mdc-rtl-property_($right-property, $right-value);\n\n @include mdc-rtl($root-selector) {\n @include mdc-rtl-property_($left-property, $right-value);\n @include mdc-rtl-property_($right-property, $left-value);\n }\n}\n\n///\n/// Adds `@noflip` annotation when `$mdc-rtl-include` is true.\n///\n/// @param {String} $property\n/// @param {String} $value\n/// @access private\n///\n@mixin mdc-rtl-property_($property, $value) {\n @if $mdc-rtl-include {\n /* @noflip */\n }\n\n #{$property}: #{$value};\n}\n","/*! ========================================================================\n * Bootstrap Toggle: bootstrap-toggle.css v2.2.0\n * http://www.bootstraptoggle.com\n * ========================================================================\n * Copyright 2014 Min Hur, The New York Times Company\n * Licensed under MIT\n * ======================================================================== */\n.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-20px;margin-right:5px}\n.toggle{position:relative;overflow:hidden}\n.toggle input[type=checkbox]{display:none}\n.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}\n.toggle.off .toggle-group{left:-100%}\n.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}\n.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}\n.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}\n.toggle.btn{min-width:59px;min-height:34px}\n.toggle-on.btn{padding-right:24px}\n.toggle-off.btn{padding-left:24px}\n.toggle.btn-lg{min-width:79px;min-height:45px}\n.toggle-on.btn-lg{padding-right:31px}\n.toggle-off.btn-lg{padding-left:31px}\n.toggle-handle.btn-lg{width:40px}\n.toggle.btn-sm{min-width:50px;min-height:30px}\n.toggle-on.btn-sm{padding-right:20px}\n.toggle-off.btn-sm{padding-left:20px}\n.toggle.btn-xs{min-width:35px;min-height:22px}\n.toggle-on.btn-xs{padding-right:12px}\n.toggle-off.btn-xs{padding-left:12px}","/* ibm-plex-sans-100normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 100;\n src:\n local('IBM Plex Sans Thin '),\n local('IBM Plex Sans-Thin'),\n url('./files/ibm-plex-sans-latin-100.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-100.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-100italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 100;\n src:\n local('IBM Plex Sans Thin italic'),\n local('IBM Plex Sans-Thinitalic'),\n url('./files/ibm-plex-sans-latin-100italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-100italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-200normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 200;\n src:\n local('IBM Plex Sans Extra Light '),\n local('IBM Plex Sans-Extra Light'),\n url('./files/ibm-plex-sans-latin-200.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-200.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-200italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 200;\n src:\n local('IBM Plex Sans Extra Light italic'),\n local('IBM Plex Sans-Extra Lightitalic'),\n url('./files/ibm-plex-sans-latin-200italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-200italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-300normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 300;\n src:\n local('IBM Plex Sans Light '),\n local('IBM Plex Sans-Light'),\n url('./files/ibm-plex-sans-latin-300.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-300.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-300italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 300;\n src:\n local('IBM Plex Sans Light italic'),\n local('IBM Plex Sans-Lightitalic'),\n url('./files/ibm-plex-sans-latin-300italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-300italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-400normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 400;\n src:\n local('IBM Plex Sans Regular '),\n local('IBM Plex Sans-Regular'),\n url('./files/ibm-plex-sans-latin-400.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-400.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-400italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 400;\n src:\n local('IBM Plex Sans Regular italic'),\n local('IBM Plex Sans-Regularitalic'),\n url('./files/ibm-plex-sans-latin-400italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-400italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-500normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 500;\n src:\n local('IBM Plex Sans Medium '),\n local('IBM Plex Sans-Medium'),\n url('./files/ibm-plex-sans-latin-500.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-500.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-500italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 500;\n src:\n local('IBM Plex Sans Medium italic'),\n local('IBM Plex Sans-Mediumitalic'),\n url('./files/ibm-plex-sans-latin-500italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-500italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-600normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 600;\n src:\n local('IBM Plex Sans SemiBold '),\n local('IBM Plex Sans-SemiBold'),\n url('./files/ibm-plex-sans-latin-600.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-600.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-600italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 600;\n src:\n local('IBM Plex Sans SemiBold italic'),\n local('IBM Plex Sans-SemiBolditalic'),\n url('./files/ibm-plex-sans-latin-600italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-600italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-700normal - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: normal;\n font-display: swap;\n font-weight: 700;\n src:\n local('IBM Plex Sans Bold '),\n local('IBM Plex Sans-Bold'),\n url('./files/ibm-plex-sans-latin-700.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-700.woff') format('woff'); /* Modern Browsers */\n}\n\n/* ibm-plex-sans-700italic - latin */\n@font-face {\n font-family: 'IBM Plex Sans';\n font-style: italic;\n font-display: swap;\n font-weight: 700;\n src:\n local('IBM Plex Sans Bold italic'),\n local('IBM Plex Sans-Bolditalic'),\n url('./files/ibm-plex-sans-latin-700italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/ibm-plex-sans-latin-700italic.woff') format('woff'); /* Modern Browsers */\n}\n\n",".Toastify__toast-container{z-index:9999;-webkit-transform:translateZ(9999px);position:fixed;padding:4px;width:320px;box-sizing:border-box;color:#fff}.Toastify__toast-container--top-left{top:1em;left:1em}.Toastify__toast-container--top-center{top:1em;left:50%;margin-left:-160px}.Toastify__toast-container--top-right{top:1em;right:1em}.Toastify__toast-container--bottom-left{bottom:1em;left:1em}.Toastify__toast-container--bottom-center{bottom:1em;left:50%;margin-left:-160px}.Toastify__toast-container--bottom-right{bottom:1em;right:1em}@media only screen and (max-width:480px){.Toastify__toast-container{width:100vw;padding:0;left:0;margin:0}.Toastify__toast-container--top-center,.Toastify__toast-container--top-left,.Toastify__toast-container--top-right{top:0}.Toastify__toast-container--bottom-center,.Toastify__toast-container--bottom-left,.Toastify__toast-container--bottom-right{bottom:0}.Toastify__toast-container--rtl{right:0;left:auto}}.Toastify__toast{position:relative;min-height:64px;box-sizing:border-box;margin-bottom:1rem;padding:8px;border-radius:1px;box-shadow:0 1px 10px 0 rgba(0,0,0,.1),0 2px 15px 0 rgba(0,0,0,.05);display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;max-height:800px;overflow:hidden;font-family:sans-serif;cursor:pointer;direction:ltr}.Toastify__toast--rtl{direction:rtl}.Toastify__toast--default{background:#fff;color:#aaa}.Toastify__toast--info{background:#3498db}.Toastify__toast--success{background:#07bc0c}.Toastify__toast--warning{background:#f1c40f}.Toastify__toast--error{background:#e74c3c}.Toastify__toast-body{margin:auto 0;-ms-flex:1;flex:1}@media only screen and (max-width:480px){.Toastify__toast{margin-bottom:0}}.Toastify__close-button{color:#fff;font-weight:700;font-size:14px;background:transparent;outline:none;border:none;padding:0;cursor:pointer;opacity:.7;transition:.3s ease;-ms-flex-item-align:start;align-self:flex-start}.Toastify__close-button--default{color:#000;opacity:.3}.Toastify__close-button:focus,.Toastify__close-button:hover{opacity:1}@keyframes Toastify__trackProgress{0%{transform:scaleX(1)}to{transform:scaleX(0)}}.Toastify__progress-bar{position:absolute;bottom:0;left:0;width:100%;height:5px;z-index:9999;opacity:.7;background-color:hsla(0,0%,100%,.7);transform-origin:left}.Toastify__progress-bar--animated{animation:Toastify__trackProgress linear 1 forwards}.Toastify__progress-bar--controlled{transition:transform .2s}.Toastify__progress-bar--rtl{right:0;left:auto;transform-origin:right}.Toastify__progress-bar--default{background:linear-gradient(90deg,#4cd964,#5ac8fa,#007aff,#34aadc,#5856d6,#ff2d55)}@keyframes Toastify__bounceInRight{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(3000px,0,0)}60%{opacity:1;transform:translate3d(-25px,0,0)}75%{transform:translate3d(10px,0,0)}90%{transform:translate3d(-5px,0,0)}to{transform:none}}@keyframes Toastify__bounceOutRight{20%{opacity:1;transform:translate3d(-20px,0,0)}to{opacity:0;transform:translate3d(2000px,0,0)}}@keyframes Toastify__bounceInLeft{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(-3000px,0,0)}60%{opacity:1;transform:translate3d(25px,0,0)}75%{transform:translate3d(-10px,0,0)}90%{transform:translate3d(5px,0,0)}to{transform:none}}@keyframes Toastify__bounceOutLeft{20%{opacity:1;transform:translate3d(20px,0,0)}to{opacity:0;transform:translate3d(-2000px,0,0)}}@keyframes Toastify__bounceInUp{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(0,3000px,0)}60%{opacity:1;transform:translate3d(0,-20px,0)}75%{transform:translate3d(0,10px,0)}90%{transform:translate3d(0,-5px,0)}to{transform:translateZ(0)}}@keyframes Toastify__bounceOutUp{20%{transform:translate3d(0,-10px,0)}40%,45%{opacity:1;transform:translate3d(0,20px,0)}to{opacity:0;transform:translate3d(0,-2000px,0)}}@keyframes Toastify__bounceInDown{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;transform:translate3d(0,-3000px,0)}60%{opacity:1;transform:translate3d(0,25px,0)}75%{transform:translate3d(0,-10px,0)}90%{transform:translate3d(0,5px,0)}to{transform:none}}@keyframes Toastify__bounceOutDown{20%{transform:translate3d(0,10px,0)}40%,45%{opacity:1;transform:translate3d(0,-20px,0)}to{opacity:0;transform:translate3d(0,2000px,0)}}.Toastify__bounce-enter--bottom-left,.Toastify__bounce-enter--top-left{animation-name:Toastify__bounceInLeft}.Toastify__bounce-enter--bottom-right,.Toastify__bounce-enter--top-right{animation-name:Toastify__bounceInRight}.Toastify__bounce-enter--top-center{animation-name:Toastify__bounceInDown}.Toastify__bounce-enter--bottom-center{animation-name:Toastify__bounceInUp}.Toastify__bounce-exit--bottom-left,.Toastify__bounce-exit--top-left{animation-name:Toastify__bounceOutLeft}.Toastify__bounce-exit--bottom-right,.Toastify__bounce-exit--top-right{animation-name:Toastify__bounceOutRight}.Toastify__bounce-exit--top-center{animation-name:Toastify__bounceOutUp}.Toastify__bounce-exit--bottom-center{animation-name:Toastify__bounceOutDown}@keyframes Toastify__zoomIn{0%{opacity:0;transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes Toastify__zoomOut{0%{opacity:1}50%{opacity:0;transform:scale3d(.3,.3,.3)}to{opacity:0}}.Toastify__zoom-enter{animation-name:Toastify__zoomIn}.Toastify__zoom-exit{animation-name:Toastify__zoomOut}@keyframes Toastify__flipIn{0%{transform:perspective(400px) rotateX(90deg);animation-timing-function:ease-in;opacity:0}40%{transform:perspective(400px) rotateX(-20deg);animation-timing-function:ease-in}60%{transform:perspective(400px) rotateX(10deg);opacity:1}80%{transform:perspective(400px) rotateX(-5deg)}to{transform:perspective(400px)}}@keyframes Toastify__flipOut{0%{transform:perspective(400px)}30%{transform:perspective(400px) rotateX(-20deg);opacity:1}to{transform:perspective(400px) rotateX(90deg);opacity:0}}.Toastify__flip-enter{animation-name:Toastify__flipIn}.Toastify__flip-exit{animation-name:Toastify__flipOut}@keyframes Toastify__slideInRight{0%{transform:translate3d(110%,0,0);visibility:visible}to{transform:translateZ(0)}}@keyframes Toastify__slideInLeft{0%{transform:translate3d(-110%,0,0);visibility:visible}to{transform:translateZ(0)}}@keyframes Toastify__slideInUp{0%{transform:translate3d(0,110%,0);visibility:visible}to{transform:translateZ(0)}}@keyframes Toastify__slideInDown{0%{transform:translate3d(0,-110%,0);visibility:visible}to{transform:translateZ(0)}}@keyframes Toastify__slideOutRight{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(110%,0,0)}}@keyframes Toastify__slideOutLeft{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(-110%,0,0)}}@keyframes Toastify__slideOutDown{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(0,500px,0)}}@keyframes Toastify__slideOutUp{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(0,-500px,0)}}.Toastify__slide-enter--bottom-left,.Toastify__slide-enter--top-left{animation-name:Toastify__slideInLeft}.Toastify__slide-enter--bottom-right,.Toastify__slide-enter--top-right{animation-name:Toastify__slideInRight}.Toastify__slide-enter--top-center{animation-name:Toastify__slideInDown}.Toastify__slide-enter--bottom-center{animation-name:Toastify__slideInUp}.Toastify__slide-exit--bottom-left,.Toastify__slide-exit--top-left{animation-name:Toastify__slideOutLeft}.Toastify__slide-exit--bottom-right,.Toastify__slide-exit--top-right{animation-name:Toastify__slideOutRight}.Toastify__slide-exit--top-center{animation-name:Toastify__slideOutUp}.Toastify__slide-exit--bottom-center{animation-name:Toastify__slideOutDown}","// Copyright 2016 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n@import \"@material/density/functions\";\n@import \"@material/rtl/mixins\";\n@import \"@material/theme/mixins\";\n@import \"@material/shape/functions\";\n@import \"@material/shape/mixins\";\n@import \"@material/ripple/mixins\";\n@import \"@material/theme/functions\";\n@import \"@material/typography/mixins\";\n@import \"@material/typography/variables\";\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"./variables\";\n\n//\n// Public\n//\n\n@mixin mdc-list-core-styles($query: mdc-feature-all()) {\n @include mdc-list-without-ripple($query);\n @include mdc-list-ripple($query);\n}\n\n// This API is intended for use by frameworks that may want to separate the ripple-related styles from the other\n// list styles. It is recommended that most users use `mdc-list-core-styles` instead.\n@mixin mdc-list-without-ripple($query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n $feat-structure: mdc-feature-create-target($query, structure);\n $feat-typography: mdc-feature-create-target($query, typography);\n\n $item-primary-text-baseline-height: 32px;\n $item-secondary-text-baseline-height: 20px;\n $dense-item-primary-text-baseline-height: 24px;\n $divider-color: if(\n mdc-theme-tone($mdc-theme-background) == \"dark\",\n $mdc-list-divider-color-on-dark-bg,\n $mdc-list-divider-color-on-light-bg\n );\n\n .mdc-list {\n @include mdc-list-base_($query);\n }\n\n @include mdc-list-single-line-density($mdc-list-single-line-density-scale, $query: $query);\n @include mdc-list-item-secondary-text-ink-color(text-secondary-on-background, $query);\n @include mdc-list-item-graphic-fill-color(transparent, $query);\n @include mdc-list-item-graphic-ink-color(text-icon-on-background, $query);\n @include mdc-list-item-meta-ink-color(text-hint-on-background, $query);\n @include mdc-list-group-subheader-ink-color(text-primary-on-background, $query);\n @include mdc-list-item-disabled-text-opacity($mdc-list-text-disabled-opacity, $query);\n @include mdc-list-item-disabled-text-color($mdc-list-text-disabled-color, $query);\n\n .mdc-list--dense {\n @include mdc-feature-targets($feat-structure) {\n padding-top: 4px;\n padding-bottom: 4px;\n font-size: .812rem;\n }\n }\n\n .mdc-list-item {\n @include mdc-feature-targets($feat-structure) {\n @include mdc-list-item-base_;\n }\n }\n\n // \"Selected\" is ephemeral and likely to change soon. E.g., selecting one or more photos to share in Google Photos.\n // \"Activated\" is more permanent. E.g., the currently highlighted navigation destination in a drawer.\n .mdc-list-item--selected,\n .mdc-list-item--activated {\n @include mdc-list-item-primary-text-ink-color(primary, $query);\n @include mdc-list-item-graphic-ink-color(primary, $query);\n }\n\n .mdc-list-item__graphic {\n @include mdc-feature-targets($feat-structure) {\n @include mdc-list-graphic-size_(24px);\n\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n fill: currentColor;\n }\n }\n\n // Extra specificity is to override .material-icons display style if used in\n // conjunction with mdc-list-item__graphic\n // stylelint-disable plugin/selector-bem-pattern\n .mdc-list .mdc-list-item__graphic {\n @include mdc-feature-targets($feat-structure) {\n display: inline-flex;\n }\n }\n // stylelint-enable plugin/selector-bem-pattern\n\n .mdc-list-item__meta {\n // stylelint-disable selector-class-pattern\n &:not(.material-icons) {\n @include mdc-typography(caption, $query);\n }\n // stylelint-enable selector-class-pattern\n\n @include mdc-feature-targets($feat-structure) {\n @include mdc-rtl-reflexive-property(margin, auto, 0, \".mdc-list-item\");\n }\n }\n\n .mdc-list-item__text {\n @include mdc-typography-overflow-ellipsis($query);\n }\n\n // Disable interaction on label elements that may automatically\n // toggle corresponding checkbox / radio input.\n .mdc-list-item__text[for] {\n @include mdc-feature-targets($feat-structure) {\n pointer-events: none;\n }\n }\n\n .mdc-list-item__primary-text {\n @include mdc-typography-overflow-ellipsis($query);\n @include mdc-typography-baseline-top($item-primary-text-baseline-height, $query);\n @include mdc-typography-baseline-bottom($item-secondary-text-baseline-height, $query);\n\n @include mdc-feature-targets($feat-structure) {\n display: block;\n }\n\n // stylelint-disable plugin/selector-bem-pattern\n .mdc-list--dense & {\n @include mdc-typography-baseline-top($dense-item-primary-text-baseline-height, $query);\n @include mdc-typography-baseline-bottom($item-secondary-text-baseline-height, $query);\n }\n // stylelint-enable plugin/selector-bem-pattern\n }\n\n .mdc-list-item__secondary-text {\n @include mdc-typography(body2, $query);\n @include mdc-typography-overflow-ellipsis($query);\n @include mdc-typography-baseline-top($item-secondary-text-baseline-height, $query);\n\n @include mdc-feature-targets($feat-structure) {\n display: block;\n }\n\n // stylelint-disable plugin/selector-bem-pattern\n .mdc-list--dense & {\n @include mdc-typography-baseline-top($item-secondary-text-baseline-height, $query);\n\n @include mdc-feature-targets($feat-structure) {\n font-size: inherit;\n }\n }\n // stylelint-enable plugin/selector-bem-pattern\n }\n\n // stylelint-disable plugin/selector-bem-pattern\n .mdc-list--dense .mdc-list-item {\n @include mdc-feature-targets($feat-structure) {\n height: 40px;\n }\n }\n\n .mdc-list--dense .mdc-list-item__graphic {\n @include mdc-feature-targets($feat-structure) {\n @include mdc-list-graphic-size_(20px);\n }\n }\n\n .mdc-list--avatar-list .mdc-list-item {\n @include mdc-feature-targets($feat-structure) {\n height: 56px;\n }\n }\n\n .mdc-list--avatar-list .mdc-list-item__graphic {\n @include mdc-feature-targets($feat-structure) {\n @include mdc-list-graphic-size_(40px);\n\n border-radius: 50%;\n }\n }\n\n .mdc-list--two-line .mdc-list-item__text {\n @include mdc-feature-targets($feat-structure) {\n align-self: flex-start;\n }\n }\n\n .mdc-list--two-line .mdc-list-item {\n @include mdc-feature-targets($feat-structure) {\n height: 72px;\n }\n }\n\n .mdc-list--two-line.mdc-list--dense .mdc-list-item,\n .mdc-list--avatar-list.mdc-list--dense .mdc-list-item {\n @include mdc-feature-targets($feat-structure) {\n height: 60px;\n }\n }\n\n .mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic {\n @include mdc-feature-targets($feat-structure) {\n @include mdc-list-graphic-size_(36px);\n }\n }\n\n // Only change mouse cursor for interactive list items which are not disabled.\n :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item {\n @include mdc-feature-targets($feat-structure) {\n cursor: pointer;\n }\n }\n\n // Override anchor tag styles for the use-case of a list being used for navigation\n // stylelint-disable selector-max-type,selector-no-qualifying-type\n a.mdc-list-item {\n @include mdc-feature-targets($feat-structure) {\n color: inherit;\n text-decoration: none;\n }\n }\n // stylelint-enable selector-max-type,selector-no-qualifying-type\n\n .mdc-list-divider {\n @include mdc-feature-targets($feat-structure) {\n height: 0;\n margin: 0;\n border: none;\n border-bottom-width: 1px;\n border-bottom-style: solid;\n }\n }\n\n // Note: ideally we'd be able to hoist this to the top-level `$feat-color`, but doing so\n // will cause the `border` declaration on `.mdc-list-divider` above to override it.\n @include mdc-list-divider-color($divider-color, $query);\n\n .mdc-list-divider--padded {\n @include mdc-feature-targets($feat-structure) {\n // Leave gaps on each side to match the padding on list items\n margin: 0 $mdc-list-side-padding;\n }\n }\n\n .mdc-list-divider--inset {\n @include mdc-feature-targets($feat-structure) {\n @include mdc-rtl-reflexive-box(margin, left, $mdc-list-text-offset, \".mdc-list-group\");\n\n width: calc(100% - #{$mdc-list-text-offset});\n }\n }\n\n .mdc-list-divider--inset.mdc-list-divider--padded {\n @include mdc-feature-targets($feat-structure) {\n width: calc(100% - #{$mdc-list-text-offset} - #{$mdc-list-side-padding});\n }\n }\n\n .mdc-list-group {\n @include mdc-feature-targets($feat-structure) {\n // Cancel top/bottom padding on individual lists within group\n .mdc-list {\n padding: 0;\n }\n }\n }\n\n .mdc-list-group__subheader {\n $mdc-list-subheader-virtual-height: 3rem;\n $mdc-list-subheader-leading: map-get(map-get($mdc-typography-styles, body1), line-height);\n $mdc-list-subheader-margin: ($mdc-list-subheader-virtual-height - $mdc-list-subheader-leading) / 2;\n\n @include mdc-typography(subtitle1, $query);\n\n @include mdc-feature-targets($feat-structure) {\n margin: $mdc-list-subheader-margin $mdc-list-side-padding;\n }\n }\n}\n\n// This API is intended for use by frameworks that may want to separate the ripple-related styles from the other\n// list styles. It is recommended that most users use `mdc-list-core-styles` instead.\n@mixin mdc-list-ripple($query: mdc-feature-all()) {\n @include mdc-ripple-common($query);\n\n // List items should support states by default, but it should be possible to opt out.\n // Direct child combinator is necessary for non-interactive modifier on parent to not\n // match this selector.\n :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item {\n @include mdc-list-item-interactive-ripple_($query);\n }\n\n // Set styles only for focus state on disabled list item.\n :not(.mdc-list--non-interactive) > .mdc-list-item--disabled {\n @include mdc-ripple-surface($query: $query);\n @include mdc-ripple-radius-bounded($query: $query);\n @include mdc-states-base-color(mdc-theme-prop-value(on-surface), $query: $query);\n @include mdc-states-focus-opacity(mdc-states-opacity(primary, focus), $query: $query);\n }\n}\n\n@mixin mdc-list-item-primary-text-ink-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $color);\n }\n}\n\n@mixin mdc-list-item-secondary-text-ink-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n .mdc-list-item__secondary-text {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $color);\n }\n }\n}\n\n@mixin mdc-list-item-graphic-fill-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n .mdc-list-item__graphic {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(background-color, $color);\n }\n }\n}\n\n@mixin mdc-list-item-graphic-ink-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n .mdc-list-item__graphic {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $color);\n }\n }\n}\n\n@mixin mdc-list-item-meta-ink-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n .mdc-list-item__meta {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $color);\n }\n }\n}\n\n///\n/// Sets shape radius (rounded) to single line list variant.\n///\n/// @param {Number | List} $radius Radius size in `px` or percentage. It can be 4 value corner or single radius.\n/// Set to `50%` for rounded shape.\n/// @param {Boolean} $rtl-reflexive Set to true to flip border radius in RTL context. Defaults to `false`.\n/// @param {Number} $density-scale Density scale of single line list. Set this only when custom density is applied.\n/// Defaults to `$mdc-list-single-line-density-scale`.\n///\n/// @access public\n///\n@mixin mdc-list-single-line-shape-radius(\n $radius,\n $rtl-reflexive: false,\n $density-scale: $mdc-list-single-line-density-scale,\n $query: mdc-feature-all()) {\n\n $height: mdc-density-prop-value(\n $density-config: $mdc-list-single-line-density-config,\n $density-scale: $density-scale,\n $property-name: height,\n );\n\n $resolved-radius: mdc-shape-resolve-percentage-radius($height, $radius);\n\n .mdc-list-item {\n @include mdc-shape-radius($resolved-radius, $rtl-reflexive, $query: $query);\n }\n}\n\n@mixin mdc-list-divider-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n .mdc-list-divider {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(border-bottom-color, $color);\n }\n }\n}\n\n@mixin mdc-list-group-subheader-ink-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n .mdc-list-group__subheader {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $color);\n }\n }\n}\n\n@mixin mdc-list-item-disabled-text-opacity($opacity, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n .mdc-list-item--disabled .mdc-list-item__text {\n @include mdc-feature-targets($feat-color) {\n opacity: $opacity;\n }\n }\n}\n\n@mixin mdc-list-item-disabled-text-color($color, $query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n\n .mdc-list-item--disabled .mdc-list-item__text {\n @include mdc-feature-targets($feat-color) {\n @include mdc-theme-prop(color, $color);\n }\n }\n}\n\n///\n/// Sets density scale to single line list variant.\n///\n/// @param {Number} $density-scale Density scale for list. Supported density scales are `-4`, `-3`, `-2`, `-1` and `0`.\n///\n/// @access public\n///\n@mixin mdc-list-single-line-density($density-scale, $query: mdc-feature-all()) {\n $height: mdc-density-prop-value(\n $density-config: $mdc-list-single-line-density-config,\n $density-scale: $density-scale,\n $property-name: height,\n );\n\n .mdc-list-item {\n @include mdc-list-single-line-height($height, $query: $query);\n }\n}\n\n///\n/// Sets height to single line list variant.\n///\n/// @param {Number} $height Height value in `px`.\n///\n/// @access public\n///\n@mixin mdc-list-single-line-height($height, $query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @include mdc-feature-targets($feat-structure) {\n height: $height;\n }\n}\n\n//\n// Private\n//\n\n@mixin mdc-list-base_($query: mdc-feature-all()) {\n $feat-color: mdc-feature-create-target($query, color);\n $feat-structure: mdc-feature-create-target($query, structure);\n $feat-typography: mdc-feature-create-target($query, typography);\n\n @include mdc-typography(subtitle1, $query);\n\n @include mdc-feature-targets($feat-typography) {\n // According to the mocks and stickersheet, the line-height is\n // adjusted to 24px for text content, same as for body1.\n /* @alternate */\n line-height: map-get(map-get($mdc-typography-styles, body1), line-height);\n }\n\n @include mdc-feature-targets($feat-structure) {\n margin: 0;\n padding: 8px 0;\n list-style-type: none;\n\n &:focus {\n outline: none;\n }\n }\n\n @include mdc-list-item-primary-text-ink-color(text-primary-on-background, $query);\n}\n\n@mixin mdc-list-item-base_ {\n display: flex;\n position: relative;\n align-items: center;\n justify-content: flex-start;\n padding: 0 $mdc-list-side-padding;\n overflow: hidden;\n\n &:focus {\n outline: none;\n }\n}\n\n// Ripple styles for an interactive list item (one that is enabled and inside an interactive list).\n@mixin mdc-list-item-interactive-ripple_($query: mdc-feature-all()) {\n @include mdc-ripple-surface($query);\n @include mdc-ripple-radius-bounded($query: $query);\n @include mdc-states(mdc-theme-prop-value(on-surface), false, $query);\n @include mdc-states-activated(primary, false, $query);\n @include mdc-states-selected(primary, false, $query);\n}\n\n// Sets the width and height of the graphic element, as well as calculates the margins for\n// the graphic element such that the text is always offset by 72px, which is defined within\n// the spec.\n@mixin mdc-list-graphic-size_($size) {\n $text-offset: 72px;\n $side-padding: $mdc-list-side-padding;\n $margin-value: $text-offset - $side-padding - $size;\n\n @include mdc-rtl-reflexive-box(margin, right, $margin-value, \".mdc-list-item\");\n\n width: $size;\n height: $size;\n}\n","//\n// Copyright 2017 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"./variables\";\n\n@mixin mdc-typography-core-styles($query: mdc-feature-all()) {\n .mdc-typography {\n @include mdc-typography-base($query: $query);\n }\n\n @each $style in map-keys($mdc-typography-styles) {\n .mdc-typography--#{$style} {\n @include mdc-typography($style, $query: $query);\n }\n }\n}\n\n@mixin mdc-typography-base($query: mdc-feature-all()) {\n $feat-typography: mdc-feature-create-target($query, typography);\n\n @include mdc-feature-targets($feat-typography) {\n @each $key, $value in $mdc-typography-base {\n #{$key}: $value;\n }\n }\n}\n\n@mixin mdc-typography($style, $query: mdc-feature-all(), $exclude-props: ()) {\n $feat-typography: mdc-feature-create-target($query, typography);\n $style-props: map-get($mdc-typography-styles, $style);\n\n @if not map-has-key($mdc-typography-styles, $style) {\n @error \"Invalid style specified! #{$style} doesn't exist. Choose one of #{map-keys($mdc-typography-styles)}\";\n }\n\n @include mdc-feature-targets($feat-typography) {\n @each $key, $value in $style-props {\n @if index($exclude-props, $key) == null {\n #{$key}: $value;\n }\n }\n }\n}\n\n// Element must be `display: block` or `display: inline-block` for this to work.\n@mixin mdc-typography-overflow-ellipsis($query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @include mdc-feature-targets($feat-structure) {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n }\n}\n\n@mixin mdc-typography-baseline-top($distance, $query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @include mdc-feature-targets($feat-structure) {\n display: block;\n margin-top: 0;\n /* @alternate */\n line-height: normal;\n }\n\n &::before {\n @include mdc-feature-targets($feat-structure) {\n @include mdc-typography-baseline-strut_($distance);\n\n vertical-align: 0;\n }\n }\n}\n\n@mixin mdc-typography-baseline-bottom($distance, $query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @include mdc-feature-targets($feat-structure) {\n margin-bottom: -1 * $distance;\n }\n\n &::after {\n @include mdc-feature-targets($feat-structure) {\n @include mdc-typography-baseline-strut_($distance);\n\n vertical-align: -1 * $distance;\n }\n }\n}\n\n@mixin mdc-typography-baseline-strut_($distance) {\n display: inline-block;\n width: 0;\n height: $distance;\n content: \"\";\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/material-components/material-components-web/blob/master/LICENSE\n */\n.mdc-list {\n font-family: Roboto, sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-size: 1rem;\n line-height: 1.75rem;\n font-weight: 400;\n letter-spacing: 0.009375em;\n text-decoration: inherit;\n text-transform: inherit;\n /* @alternate */\n line-height: 1.5rem;\n margin: 0;\n padding: 8px 0;\n list-style-type: none;\n color: rgba(0, 0, 0, 0.87);\n /* @alternate */\n color: var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87));\n}\n.mdc-list:focus {\n outline: none;\n}\n\n.mdc-list-item {\n height: 48px;\n}\n\n.mdc-list-item__secondary-text {\n color: rgba(0, 0, 0, 0.54);\n /* @alternate */\n color: var(--mdc-theme-text-secondary-on-background, rgba(0, 0, 0, 0.54));\n}\n\n.mdc-list-item__graphic {\n background-color: transparent;\n}\n\n.mdc-list-item__graphic {\n color: rgba(0, 0, 0, 0.38);\n /* @alternate */\n color: var(--mdc-theme-text-icon-on-background, rgba(0, 0, 0, 0.38));\n}\n\n.mdc-list-item__meta {\n color: rgba(0, 0, 0, 0.38);\n /* @alternate */\n color: var(--mdc-theme-text-hint-on-background, rgba(0, 0, 0, 0.38));\n}\n\n.mdc-list-group__subheader {\n color: rgba(0, 0, 0, 0.87);\n /* @alternate */\n color: var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87));\n}\n\n.mdc-list-item--disabled .mdc-list-item__text {\n opacity: 0.38;\n}\n\n.mdc-list-item--disabled .mdc-list-item__text {\n color: #000;\n /* @alternate */\n color: var(--mdc-theme-on-surface, #000);\n}\n\n.mdc-list--dense {\n padding-top: 4px;\n padding-bottom: 4px;\n font-size: 0.812rem;\n}\n\n.mdc-list-item {\n display: flex;\n position: relative;\n align-items: center;\n justify-content: flex-start;\n padding: 0 16px;\n overflow: hidden;\n}\n.mdc-list-item:focus {\n outline: none;\n}\n\n.mdc-list-item--selected,\n.mdc-list-item--activated {\n color: #6200ee;\n /* @alternate */\n color: var(--mdc-theme-primary, #6200ee);\n}\n.mdc-list-item--selected .mdc-list-item__graphic,\n.mdc-list-item--activated .mdc-list-item__graphic {\n color: #6200ee;\n /* @alternate */\n color: var(--mdc-theme-primary, #6200ee);\n}\n\n.mdc-list-item__graphic {\n /* @noflip */\n margin-left: 0;\n /* @noflip */\n margin-right: 32px;\n width: 24px;\n height: 24px;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n fill: currentColor;\n}\n.mdc-list-item[dir=rtl] .mdc-list-item__graphic, [dir=rtl] .mdc-list-item .mdc-list-item__graphic {\n /* @noflip */\n margin-left: 32px;\n /* @noflip */\n margin-right: 0;\n}\n\n.mdc-list .mdc-list-item__graphic {\n display: inline-flex;\n}\n\n.mdc-list-item__meta {\n /* @noflip */\n margin-left: auto;\n /* @noflip */\n margin-right: 0;\n}\n.mdc-list-item__meta:not(.material-icons) {\n font-family: Roboto, sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-size: 0.75rem;\n line-height: 1.25rem;\n font-weight: 400;\n letter-spacing: 0.0333333333em;\n text-decoration: inherit;\n text-transform: inherit;\n}\n.mdc-list-item[dir=rtl] .mdc-list-item__meta, [dir=rtl] .mdc-list-item .mdc-list-item__meta {\n /* @noflip */\n margin-left: 0;\n /* @noflip */\n margin-right: auto;\n}\n\n.mdc-list-item__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.mdc-list-item__text[for] {\n pointer-events: none;\n}\n\n.mdc-list-item__primary-text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n display: block;\n margin-top: 0;\n /* @alternate */\n line-height: normal;\n margin-bottom: -20px;\n display: block;\n}\n.mdc-list-item__primary-text::before {\n display: inline-block;\n width: 0;\n height: 32px;\n content: \"\";\n vertical-align: 0;\n}\n.mdc-list-item__primary-text::after {\n display: inline-block;\n width: 0;\n height: 20px;\n content: \"\";\n vertical-align: -20px;\n}\n.mdc-list--dense .mdc-list-item__primary-text {\n display: block;\n margin-top: 0;\n /* @alternate */\n line-height: normal;\n margin-bottom: -20px;\n}\n.mdc-list--dense .mdc-list-item__primary-text::before {\n display: inline-block;\n width: 0;\n height: 24px;\n content: \"\";\n vertical-align: 0;\n}\n.mdc-list--dense .mdc-list-item__primary-text::after {\n display: inline-block;\n width: 0;\n height: 20px;\n content: \"\";\n vertical-align: -20px;\n}\n\n.mdc-list-item__secondary-text {\n font-family: Roboto, sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-size: 0.875rem;\n line-height: 1.25rem;\n font-weight: 400;\n letter-spacing: 0.0178571429em;\n text-decoration: inherit;\n text-transform: inherit;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n display: block;\n margin-top: 0;\n /* @alternate */\n line-height: normal;\n display: block;\n}\n.mdc-list-item__secondary-text::before {\n display: inline-block;\n width: 0;\n height: 20px;\n content: \"\";\n vertical-align: 0;\n}\n.mdc-list--dense .mdc-list-item__secondary-text {\n display: block;\n margin-top: 0;\n /* @alternate */\n line-height: normal;\n font-size: inherit;\n}\n.mdc-list--dense .mdc-list-item__secondary-text::before {\n display: inline-block;\n width: 0;\n height: 20px;\n content: \"\";\n vertical-align: 0;\n}\n\n.mdc-list--dense .mdc-list-item {\n height: 40px;\n}\n\n.mdc-list--dense .mdc-list-item__graphic {\n /* @noflip */\n margin-left: 0;\n /* @noflip */\n margin-right: 36px;\n width: 20px;\n height: 20px;\n}\n.mdc-list-item[dir=rtl] .mdc-list--dense .mdc-list-item__graphic, [dir=rtl] .mdc-list-item .mdc-list--dense .mdc-list-item__graphic {\n /* @noflip */\n margin-left: 36px;\n /* @noflip */\n margin-right: 0;\n}\n\n.mdc-list--avatar-list .mdc-list-item {\n height: 56px;\n}\n\n.mdc-list--avatar-list .mdc-list-item__graphic {\n /* @noflip */\n margin-left: 0;\n /* @noflip */\n margin-right: 16px;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n}\n.mdc-list-item[dir=rtl] .mdc-list--avatar-list .mdc-list-item__graphic, [dir=rtl] .mdc-list-item .mdc-list--avatar-list .mdc-list-item__graphic {\n /* @noflip */\n margin-left: 16px;\n /* @noflip */\n margin-right: 0;\n}\n\n.mdc-list--two-line .mdc-list-item__text {\n align-self: flex-start;\n}\n\n.mdc-list--two-line .mdc-list-item {\n height: 72px;\n}\n\n.mdc-list--two-line.mdc-list--dense .mdc-list-item,\n.mdc-list--avatar-list.mdc-list--dense .mdc-list-item {\n height: 60px;\n}\n\n.mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic {\n /* @noflip */\n margin-left: 0;\n /* @noflip */\n margin-right: 20px;\n width: 36px;\n height: 36px;\n}\n.mdc-list-item[dir=rtl] .mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic, [dir=rtl] .mdc-list-item .mdc-list--avatar-list.mdc-list--dense .mdc-list-item__graphic {\n /* @noflip */\n margin-left: 20px;\n /* @noflip */\n margin-right: 0;\n}\n\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item {\n cursor: pointer;\n}\n\na.mdc-list-item {\n color: inherit;\n text-decoration: none;\n}\n\n.mdc-list-divider {\n height: 0;\n margin: 0;\n border: none;\n border-bottom-width: 1px;\n border-bottom-style: solid;\n}\n\n.mdc-list-divider {\n border-bottom-color: rgba(0, 0, 0, 0.12);\n}\n\n.mdc-list-divider--padded {\n margin: 0 16px;\n}\n\n.mdc-list-divider--inset {\n /* @noflip */\n margin-left: 72px;\n /* @noflip */\n margin-right: 0;\n width: calc(100% - 72px);\n}\n.mdc-list-group[dir=rtl] .mdc-list-divider--inset, [dir=rtl] .mdc-list-group .mdc-list-divider--inset {\n /* @noflip */\n margin-left: 0;\n /* @noflip */\n margin-right: 72px;\n}\n\n.mdc-list-divider--inset.mdc-list-divider--padded {\n width: calc(100% - 72px - 16px);\n}\n\n.mdc-list-group .mdc-list {\n padding: 0;\n}\n\n.mdc-list-group__subheader {\n font-family: Roboto, sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-size: 1rem;\n line-height: 1.75rem;\n font-weight: 400;\n letter-spacing: 0.009375em;\n text-decoration: inherit;\n text-transform: inherit;\n margin: 0.75rem 16px;\n}\n\n@-webkit-keyframes mdc-ripple-fg-radius-in {\n from {\n -webkit-animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transform: translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1);\n transform: translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1);\n }\n to {\n -webkit-transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n }\n}\n\n@keyframes mdc-ripple-fg-radius-in {\n from {\n -webkit-animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n -webkit-transform: translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1);\n transform: translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1);\n }\n to {\n -webkit-transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n }\n}\n@-webkit-keyframes mdc-ripple-fg-opacity-in {\n from {\n -webkit-animation-timing-function: linear;\n animation-timing-function: linear;\n opacity: 0;\n }\n to {\n opacity: var(--mdc-ripple-fg-opacity, 0);\n }\n}\n@keyframes mdc-ripple-fg-opacity-in {\n from {\n -webkit-animation-timing-function: linear;\n animation-timing-function: linear;\n opacity: 0;\n }\n to {\n opacity: var(--mdc-ripple-fg-opacity, 0);\n }\n}\n@-webkit-keyframes mdc-ripple-fg-opacity-out {\n from {\n -webkit-animation-timing-function: linear;\n animation-timing-function: linear;\n opacity: var(--mdc-ripple-fg-opacity, 0);\n }\n to {\n opacity: 0;\n }\n}\n@keyframes mdc-ripple-fg-opacity-out {\n from {\n -webkit-animation-timing-function: linear;\n animation-timing-function: linear;\n opacity: var(--mdc-ripple-fg-opacity, 0);\n }\n to {\n opacity: 0;\n }\n}\n.mdc-ripple-surface--test-edge-var-bug {\n --mdc-ripple-surface-test-edge-var: 1px solid #000;\n visibility: hidden;\n}\n.mdc-ripple-surface--test-edge-var-bug::before {\n border: var(--mdc-ripple-surface-test-edge-var);\n}\n\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item {\n --mdc-ripple-fg-size: 0;\n --mdc-ripple-left: 0;\n --mdc-ripple-top: 0;\n --mdc-ripple-fg-scale: 1;\n --mdc-ripple-fg-translate-end: 0;\n --mdc-ripple-fg-translate-start: 0;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item::after {\n position: absolute;\n border-radius: 50%;\n opacity: 0;\n pointer-events: none;\n content: \"\";\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item::before {\n transition: opacity 15ms linear, background-color 15ms linear;\n z-index: 1;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded::before {\n -webkit-transform: scale(var(--mdc-ripple-fg-scale, 1));\n transform: scale(var(--mdc-ripple-fg-scale, 1));\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded::after {\n top: 0;\n /* @noflip */\n left: 0;\n -webkit-transform: scale(0);\n transform: scale(0);\n -webkit-transform-origin: center center;\n transform-origin: center center;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--unbounded::after {\n top: var(--mdc-ripple-top, 0);\n /* @noflip */\n left: var(--mdc-ripple-left, 0);\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--foreground-activation::after {\n -webkit-animation: mdc-ripple-fg-radius-in 225ms forwards, mdc-ripple-fg-opacity-in 75ms forwards;\n animation: mdc-ripple-fg-radius-in 225ms forwards, mdc-ripple-fg-opacity-in 75ms forwards;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--foreground-deactivation::after {\n -webkit-animation: mdc-ripple-fg-opacity-out 150ms;\n animation: mdc-ripple-fg-opacity-out 150ms;\n -webkit-transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item::after {\n top: calc(50% - 100%);\n /* @noflip */\n left: calc(50% - 100%);\n width: 200%;\n height: 200%;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded::after {\n width: var(--mdc-ripple-fg-size, 100%);\n height: var(--mdc-ripple-fg-size, 100%);\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item::after {\n background-color: #000;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item:hover::before {\n opacity: 0.04;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded--background-focused::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item:not(.mdc-ripple-upgraded):focus::before {\n transition-duration: 75ms;\n opacity: 0.12;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item:not(.mdc-ripple-upgraded)::after {\n transition: opacity 150ms linear;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item:not(.mdc-ripple-upgraded):active::after {\n transition-duration: 75ms;\n opacity: 0.12;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item.mdc-ripple-upgraded {\n --mdc-ripple-fg-opacity: 0.12;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated::before {\n opacity: 0.12;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated::after {\n background-color: #6200ee;\n}\n@supports not (-ms-ime-align: auto) {\n :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated::after {\n /* @alternate */\n background-color: var(--mdc-theme-primary, #6200ee);\n }\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated:hover::before {\n opacity: 0.16;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated.mdc-ripple-upgraded--background-focused::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated:not(.mdc-ripple-upgraded):focus::before {\n transition-duration: 75ms;\n opacity: 0.24;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated:not(.mdc-ripple-upgraded)::after {\n transition: opacity 150ms linear;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated:not(.mdc-ripple-upgraded):active::after {\n transition-duration: 75ms;\n opacity: 0.24;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--activated.mdc-ripple-upgraded {\n --mdc-ripple-fg-opacity: 0.24;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected::before {\n opacity: 0.08;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected::after {\n background-color: #6200ee;\n}\n@supports not (-ms-ime-align: auto) {\n :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected::after {\n /* @alternate */\n background-color: var(--mdc-theme-primary, #6200ee);\n }\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected:hover::before {\n opacity: 0.12;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected.mdc-ripple-upgraded--background-focused::before, :not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected:not(.mdc-ripple-upgraded):focus::before {\n transition-duration: 75ms;\n opacity: 0.2;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected:not(.mdc-ripple-upgraded)::after {\n transition: opacity 150ms linear;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected:not(.mdc-ripple-upgraded):active::after {\n transition-duration: 75ms;\n opacity: 0.2;\n}\n:not(.mdc-list--non-interactive) > :not(.mdc-list-item--disabled).mdc-list-item--selected.mdc-ripple-upgraded {\n --mdc-ripple-fg-opacity: 0.2;\n}\n\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled {\n --mdc-ripple-fg-size: 0;\n --mdc-ripple-left: 0;\n --mdc-ripple-top: 0;\n --mdc-ripple-fg-scale: 1;\n --mdc-ripple-fg-translate-end: 0;\n --mdc-ripple-fg-translate-start: 0;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled::before, :not(.mdc-list--non-interactive) > .mdc-list-item--disabled::after {\n position: absolute;\n border-radius: 50%;\n opacity: 0;\n pointer-events: none;\n content: \"\";\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled::before {\n transition: opacity 15ms linear, background-color 15ms linear;\n z-index: 1;\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled.mdc-ripple-upgraded::before {\n -webkit-transform: scale(var(--mdc-ripple-fg-scale, 1));\n transform: scale(var(--mdc-ripple-fg-scale, 1));\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled.mdc-ripple-upgraded::after {\n top: 0;\n /* @noflip */\n left: 0;\n -webkit-transform: scale(0);\n transform: scale(0);\n -webkit-transform-origin: center center;\n transform-origin: center center;\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled.mdc-ripple-upgraded--unbounded::after {\n top: var(--mdc-ripple-top, 0);\n /* @noflip */\n left: var(--mdc-ripple-left, 0);\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled.mdc-ripple-upgraded--foreground-activation::after {\n -webkit-animation: mdc-ripple-fg-radius-in 225ms forwards, mdc-ripple-fg-opacity-in 75ms forwards;\n animation: mdc-ripple-fg-radius-in 225ms forwards, mdc-ripple-fg-opacity-in 75ms forwards;\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled.mdc-ripple-upgraded--foreground-deactivation::after {\n -webkit-animation: mdc-ripple-fg-opacity-out 150ms;\n animation: mdc-ripple-fg-opacity-out 150ms;\n -webkit-transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled::before, :not(.mdc-list--non-interactive) > .mdc-list-item--disabled::after {\n top: calc(50% - 100%);\n /* @noflip */\n left: calc(50% - 100%);\n width: 200%;\n height: 200%;\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled.mdc-ripple-upgraded::after {\n width: var(--mdc-ripple-fg-size, 100%);\n height: var(--mdc-ripple-fg-size, 100%);\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled::before, :not(.mdc-list--non-interactive) > .mdc-list-item--disabled::after {\n background-color: #000;\n}\n:not(.mdc-list--non-interactive) > .mdc-list-item--disabled.mdc-ripple-upgraded--background-focused::before, :not(.mdc-list--non-interactive) > .mdc-list-item--disabled:not(.mdc-ripple-upgraded):focus::before {\n transition-duration: 75ms;\n opacity: 0.12;\n}\n\n/*# sourceMappingURL=mdc.list.css.map*/","//\n// Copyright 2017 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n// MDC Ripple keyframes are split into their own file so that _mixins.scss can rely on them.\n\n@import \"@material/animation/variables\";\n@import \"./variables\";\n\n@mixin mdc-ripple-keyframes_ {\n @keyframes mdc-ripple-fg-radius-in {\n from {\n animation-timing-function: $mdc-animation-standard-curve-timing-function;\n // NOTE: For these keyframes, we do not need custom property fallbacks because they are only\n // used in conjunction with `.mdc-ripple-upgraded`. Since MDCRippleFoundation checks to ensure\n // that custom properties are supported within the browser before adding this class, we can\n // safely use them without a fallback.\n transform: translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1);\n }\n\n to {\n transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n }\n }\n\n @keyframes mdc-ripple-fg-opacity-in {\n from {\n animation-timing-function: linear;\n opacity: 0;\n }\n\n to {\n opacity: var(--mdc-ripple-fg-opacity, 0);\n }\n }\n\n @keyframes mdc-ripple-fg-opacity-out {\n from {\n animation-timing-function: linear;\n opacity: var(--mdc-ripple-fg-opacity, 0);\n }\n\n to {\n opacity: 0;\n }\n }\n}\n","//\n// Copyright 2016 Google Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n@import \"@material/animation/functions\";\n@import \"@material/animation/variables\";\n@import \"@material/base/mixins\";\n@import \"@material/feature-targeting/functions\";\n@import \"@material/feature-targeting/mixins\";\n@import \"@material/theme/mixins\";\n@import \"./functions\";\n@import \"./keyframes\";\n@import \"./variables\";\n\n@mixin mdc-ripple-core-styles($query: mdc-feature-all()) {\n // postcss-bem-linter: define ripple-surface\n\n $feat-structure: mdc-feature-create-target($query, structure);\n\n .mdc-ripple-surface {\n @include mdc-ripple-surface($query: $query);\n @include mdc-states($query: $query);\n @include mdc-ripple-radius-bounded($query: $query);\n\n @include mdc-feature-targets($feat-structure) {\n position: relative;\n outline: none;\n overflow: hidden;\n }\n\n &[data-mdc-ripple-is-unbounded] {\n @include mdc-ripple-radius-unbounded($query: $query);\n\n @include mdc-feature-targets($feat-structure) {\n overflow: visible;\n }\n }\n\n &--primary {\n @include mdc-states(primary, $query: $query);\n }\n\n &--accent {\n @include mdc-states(secondary, $query: $query);\n }\n }\n\n // postcss-bem-linter: end\n}\n\n@mixin mdc-ripple-common($query: mdc-feature-all()) {\n $feat-animation: mdc-feature-create-target($query, animation);\n $feat-structure: mdc-feature-create-target($query, structure);\n\n // Ensure that styles needed by any component using MDC Ripple are emitted, but only once.\n // (Every component using MDC Ripple imports these mixins, but doesn't necessarily import\n // mdc-ripple.scss.)\n @include mdc-feature-targets($feat-animation) {\n @include mdc-base-emit-once(\"mdc-ripple/common/animation\") {\n @include mdc-ripple-keyframes_;\n }\n }\n\n @include mdc-feature-targets($feat-structure) {\n @include mdc-base-emit-once(\"mdc-ripple/common/structure\") {\n // Styles used to detect buggy behavior of CSS custom properties in Edge.\n // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11495448/\n // This is included in _mixins.scss rather than mdc-ripple.scss so that it will be\n // present for other components which rely on ripple as well as mdc-ripple itself.\n .mdc-ripple-surface--test-edge-var-bug {\n --mdc-ripple-surface-test-edge-var: 1px solid #000;\n\n visibility: hidden;\n\n &::before {\n border: var(--mdc-ripple-surface-test-edge-var);\n }\n }\n }\n }\n}\n\n@mixin mdc-ripple-surface($query: mdc-feature-all(), $ripple-target: \"&\") {\n $feat-animation: mdc-feature-create-target($query, animation);\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @include mdc-feature-targets($feat-structure) {\n --mdc-ripple-fg-size: 0;\n --mdc-ripple-left: 0;\n --mdc-ripple-top: 0;\n --mdc-ripple-fg-scale: 1;\n --mdc-ripple-fg-translate-end: 0;\n --mdc-ripple-fg-translate-start: 0;\n\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n // !!DO NOT REMOVE!! mdc-ripple-will-change-replacer\n }\n\n #{$ripple-target}::before,\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-structure) {\n position: absolute;\n border-radius: 50%;\n opacity: 0;\n pointer-events: none;\n content: \"\";\n }\n }\n\n #{$ripple-target}::before {\n @include mdc-feature-targets($feat-animation) {\n // Also transition background-color to avoid unnatural color flashes when toggling activated/selected state\n transition:\n opacity $mdc-states-wash-duration linear,\n background-color $mdc-states-wash-duration linear;\n }\n\n @include mdc-feature-targets($feat-structure) {\n z-index: 1; // Ensure that the ripple wash for hover/focus states is displayed on top of positioned child elements\n }\n }\n\n // Common styles for upgraded surfaces (some of these depend on custom properties set via JS or other mixins)\n\n &.mdc-ripple-upgraded {\n #{$ripple-target}::before {\n @include mdc-feature-targets($feat-structure) {\n transform: scale(var(--mdc-ripple-fg-scale, 1));\n }\n }\n\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-structure) {\n top: 0;\n /* @noflip */\n left: 0;\n transform: scale(0);\n transform-origin: center center;\n }\n }\n }\n\n &.mdc-ripple-upgraded--unbounded {\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-structure) {\n top: var(--mdc-ripple-top, 0);\n /* @noflip */\n left: var(--mdc-ripple-left, 0);\n }\n }\n }\n\n &.mdc-ripple-upgraded--foreground-activation {\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-animation) {\n animation:\n mdc-ripple-fg-radius-in $mdc-ripple-translate-duration forwards,\n mdc-ripple-fg-opacity-in $mdc-ripple-fade-in-duration forwards;\n }\n }\n }\n\n &.mdc-ripple-upgraded--foreground-deactivation {\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-animation) {\n animation: mdc-ripple-fg-opacity-out $mdc-ripple-fade-out-duration;\n }\n\n @include mdc-feature-targets($feat-structure) {\n // Retain transform from mdc-ripple-fg-radius-in activation\n transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));\n }\n }\n }\n}\n\n@mixin mdc-states-base-color(\n $color, $query: mdc-feature-all(), $ripple-target: \"&\") {\n $feat-color: mdc-feature-create-target($query, color);\n\n #{$ripple-target}::before,\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-color) {\n @if alpha(mdc-theme-prop-value($color)) > 0 {\n @include mdc-theme-prop(background-color, $color, $edgeOptOut: true);\n } @else {\n // If a color with 0 alpha is specified, don't render the ripple pseudo-elements at all.\n // This avoids unnecessary transitions and overflow.\n content: none;\n }\n }\n }\n}\n\n///\n/// Customizes ripple opacities in `hover`, `focus`, or `press` states\n/// @param {map} $opacity-map - map specifying custom opacity of zero or more states\n/// @param {bool} $has-nested-focusable-element - whether the component contains a focusable element in the root\n///\n@mixin mdc-states-opacities($opacity-map: (), $has-nested-focusable-element: false, $query: mdc-feature-all()) {\n // Ensure sufficient specificity to override base state opacities\n @if map-has-key($opacity-map, hover) {\n @include mdc-states-hover-opacity(map-get($opacity-map, hover), $query: $query);\n }\n\n @if map-has-key($opacity-map, focus) {\n @include mdc-states-focus-opacity(map-get($opacity-map, focus), $has-nested-focusable-element, $query: $query);\n }\n\n @if map-has-key($opacity-map, press) {\n @include mdc-states-press-opacity(map-get($opacity-map, press), $query: $query);\n }\n}\n\n@mixin mdc-states-hover-opacity(\n $opacity, $query: mdc-feature-all(), $ripple-target: \"&\") {\n $feat-color: mdc-feature-create-target($query, color);\n\n // Background wash styles, for both CSS-only and upgraded stateful surfaces\n &:hover {\n #{$ripple-target}::before {\n // Opacity falls under color because the chosen opacity is color-dependent in typical usage\n @include mdc-feature-targets($feat-color) {\n opacity: $opacity;\n }\n }\n }\n}\n\n@mixin mdc-states-focus-opacity(\n $opacity,\n $has-nested-focusable-element: false,\n $query: mdc-feature-all(),\n $ripple-target: \"&\") {\n\n // Focus overrides hover by reusing the ::before pseudo-element.\n // :focus-within generally works on non-MS browsers and matches when a *child* of the element has focus.\n // It is useful for cases where a component has a focusable element within the root node, e.g. text field,\n // but undesirable in general in case of nested stateful components.\n // We use a modifier class for JS-enabled surfaces to support all use cases in all browsers.\n @if $has-nested-focusable-element {\n // JS-enabled selectors.\n &.mdc-ripple-upgraded--background-focused,\n &.mdc-ripple-upgraded:focus-within,\n // CSS-only selectors.\n &:not(.mdc-ripple-upgraded):focus,\n &:not(.mdc-ripple-upgraded):focus-within {\n #{$ripple-target}::before {\n @include mdc-states-focus-opacity-properties_(\n $opacity: $opacity, $query: $query);\n }\n }\n } @else {\n // JS-enabled selectors.\n &.mdc-ripple-upgraded--background-focused,\n // CSS-only selectors.\n &:not(.mdc-ripple-upgraded):focus {\n #{$ripple-target}::before {\n @include mdc-states-focus-opacity-properties_(\n $opacity: $opacity, $query: $query);\n }\n }\n }\n}\n\n@mixin mdc-states-focus-opacity-properties_($opacity, $query) {\n $feat-animation: mdc-feature-create-target($query, animation);\n // Opacity falls under color because the chosen opacity is color-dependent in typical usage\n $feat-color: mdc-feature-create-target($query, color);\n\n // Note that this duration is only effective on focus, not blur\n @include mdc-feature-targets($feat-animation) {\n transition-duration: 75ms;\n }\n\n @include mdc-feature-targets($feat-color) {\n opacity: $opacity;\n }\n}\n\n@mixin mdc-states-press-opacity($opacity, $query: mdc-feature-all(), $ripple-target: \"&\") {\n $feat-animation: mdc-feature-create-target($query, animation);\n $feat-color: mdc-feature-create-target($query, color);\n\n // Styles for non-upgraded (CSS-only) stateful surfaces\n\n &:not(.mdc-ripple-upgraded) {\n // Apply press additively by using the ::after pseudo-element\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-animation) {\n transition: opacity $mdc-ripple-fade-out-duration linear;\n }\n }\n\n &:active {\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-animation) {\n transition-duration: $mdc-ripple-fade-in-duration;\n }\n\n // Opacity falls under color because the chosen opacity is color-dependent in typical usage\n @include mdc-feature-targets($feat-color) {\n opacity: $opacity;\n }\n }\n }\n }\n\n &.mdc-ripple-upgraded {\n @include mdc-feature-targets($feat-color) {\n --mdc-ripple-fg-opacity: #{$opacity};\n }\n }\n}\n\n// Simple mixin for base states which automatically selects opacity values based on whether the ink color is\n// light or dark.\n@mixin mdc-states(\n $color: mdc-theme-prop-value(on-surface),\n $has-nested-focusable-element: false,\n $query: mdc-feature-all(),\n $ripple-target: \"&\",\n) {\n @include mdc-states-interactions_(\n $color: $color,\n $has-nested-focusable-element: $has-nested-focusable-element,\n $query: $query,\n $ripple-target: $ripple-target);\n}\n\n// Simple mixin for activated states which automatically selects opacity values based on whether the ink color is\n// light or dark.\n@mixin mdc-states-activated(\n $color, $has-nested-focusable-element: false, $query: mdc-feature-all(), $ripple-target: \"&\") {\n $feat-color: mdc-feature-create-target($query, color);\n $activated-opacity: mdc-states-opacity($color, activated);\n\n &--activated {\n // Stylelint seems to think that '&' qualifies as a type selector here?\n // stylelint-disable-next-line selector-max-type\n #{$ripple-target}::before {\n // Opacity falls under color because the chosen opacity is color-dependent.\n @include mdc-feature-targets($feat-color) {\n opacity: $activated-opacity;\n }\n }\n\n @include mdc-states-interactions_(\n $color: $color,\n $has-nested-focusable-element: $has-nested-focusable-element,\n $opacity-modifier: $activated-opacity,\n $query: $query,\n $ripple-target: $ripple-target);\n }\n}\n\n// Simple mixin for selected states which automatically selects opacity values based on whether the ink color is\n// light or dark.\n@mixin mdc-states-selected(\n $color,\n $has-nested-focusable-element: false,\n $query: mdc-feature-all(),\n $ripple-target: \"&\") {\n $feat-color: mdc-feature-create-target($query, color);\n $selected-opacity: mdc-states-opacity($color, selected);\n\n &--selected {\n // stylelint-disable-next-line selector-max-type\n #{$ripple-target}::before {\n // Opacity falls under color because the chosen opacity is color-dependent.\n @include mdc-feature-targets($feat-color) {\n opacity: $selected-opacity;\n }\n }\n\n @include mdc-states-interactions_(\n $color: $color,\n $has-nested-focusable-element: $has-nested-focusable-element,\n $opacity-modifier: $selected-opacity,\n $query: $query,\n $ripple-target: $ripple-target);\n }\n}\n\n@mixin mdc-ripple-radius-bounded(\n $radius: 100%, $query: mdc-feature-all(), $ripple-target: \"&\") {\n $feat-struture: mdc-feature-create-target($query, structure);\n\n #{$ripple-target}::before,\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-struture) {\n top: calc(50% - #{$radius});\n /* @noflip */\n left: calc(50% - #{$radius});\n width: $radius * 2;\n height: $radius * 2;\n }\n }\n\n &.mdc-ripple-upgraded {\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-struture) {\n width: var(--mdc-ripple-fg-size, $radius);\n height: var(--mdc-ripple-fg-size, $radius);\n }\n }\n }\n}\n\n@mixin mdc-ripple-radius-unbounded(\n $radius: 100%, $query: mdc-feature-all(), $ripple-target: \"&\") {\n $feat-struture: mdc-feature-create-target($query, structure);\n\n #{$ripple-target}::before,\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-struture) {\n top: calc(50% - #{$radius / 2});\n /* @noflip */\n left: calc(50% - #{$radius / 2});\n width: $radius;\n height: $radius;\n }\n }\n\n &.mdc-ripple-upgraded {\n #{$ripple-target}::before,\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-struture) {\n top: var(--mdc-ripple-top, calc(50% - #{$radius / 2}));\n /* @noflip */\n left: var(--mdc-ripple-left, calc(50% - #{$radius / 2}));\n width: var(--mdc-ripple-fg-size, $radius);\n height: var(--mdc-ripple-fg-size, $radius);\n }\n }\n\n #{$ripple-target}::after {\n @include mdc-feature-targets($feat-struture) {\n width: var(--mdc-ripple-fg-size, $radius);\n height: var(--mdc-ripple-fg-size, $radius);\n }\n }\n }\n}\n\n@mixin mdc-states-interactions_(\n $color,\n $has-nested-focusable-element,\n $opacity-modifier: 0,\n $query: mdc-feature-all(),\n $ripple-target: \"&\",\n) {\n @include mdc-ripple-target-selector($ripple-target) {\n @include mdc-states-base-color($color, $query);\n }\n\n @include mdc-states-hover-opacity(\n $opacity: mdc-states-opacity($color, hover) + $opacity-modifier,\n $query: $query,\n $ripple-target: $ripple-target);\n @include mdc-states-focus-opacity(\n $opacity: mdc-states-opacity($color, focus) + $opacity-modifier,\n $has-nested-focusable-element: $has-nested-focusable-element,\n $query: $query,\n $ripple-target: $ripple-target,\n );\n @include mdc-states-press-opacity(\n $opacity: mdc-states-opacity($color, press) + $opacity-modifier,\n $query: $query,\n $ripple-target: $ripple-target);\n}\n\n// Wraps content in the `ripple-target` selector if it exists.\n@mixin mdc-ripple-target-selector($ripple-target: \"&\") {\n @if $ripple-target == \"&\" {\n @content;\n } @else {\n #{$ripple-target} {\n @content;\n }\n }\n}\n\n// Common styles for a ripple target element.\n// Used for components which have an inner ripple target element.\n@mixin mdc-ripple-target-common($query: mdc-feature-all()) {\n $feat-structure: mdc-feature-create-target($query, structure);\n\n @include mdc-feature-targets($feat-structure) {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n // Necessary for clicks on other inner elements (e.g. close icon in chip)\n // to go through.\n pointer-events: none;\n }\n}\n",".rmwc-collapsible-list {\n width: 100%;\n}\n\n.rmwc-collapsible-list__children {\n overflow: hidden;\n max-height: 0;\n transition: max-height 0.3s, opacity 0.3s;\n opacity: 0;\n}\n\n.rmwc-collapsible-list--open > .rmwc-collapsible-list__children {\n opacity: 1;\n}\n\n.rmwc-collapsible-list__handle .mdc-list-item__meta {\n transition: transform 0.3s;\n user-select: none;\n}\n\n.rmwc-collapsible-list--open\n > .rmwc-collapsible-list__handle\n .mdc-list-item__meta {\n transform: rotate(90deg);\n}\n\n.rmwc-collapsible-list__children .mdc-list-item {\n padding-left: 2rem;\n}\n\n.rmwc-collapsible-list__children-inner {\n overflow: auto;\n}\n",".rmwc-icon {\n \n}\n\n.rmwc-icon--image {\n min-width: 1em;\n min-height: 1em;\n background-repeat: no-repeat;\n font-size: 1.5rem;\n background-size: 1em;\n background-position: center center;\n}\n\n.rmwc-icon--size-xsmall {\n font-size: 1.125rem;\n width: 1em;\n height: 1em;\n}\n\n.rmwc-icon--size-small {\n font-size: 1.25rem;\n width: 1em;\n height: 1em;\n}\n\n.rmwc-icon--size-medium {\n font-size: 1.5rem;\n width: 1em;\n height: 1em;\n}\n\n.rmwc-icon--size-large {\n font-size: 2.25rem;\n width: 1em;\n height: 1em;\n}\n\n.rmwc-icon--size-xlarge {\n font-size: 3rem;\n width: 1em;\n height: 1em;\n}\n"]} \ No newline at end of file diff --git a/web/gui/dashboard/static/js/10.44d9d40b.chunk.js b/web/gui/dashboard/static/js/10.44d9d40b.chunk.js deleted file mode 100644 index 8d30dd9f7..000000000 --- a/web/gui/dashboard/static/js/10.44d9d40b.chunk.js +++ /dev/null @@ -1,2 +0,0 @@ -(this["webpackJsonp@netdata/dashboard"]=this["webpackJsonp@netdata/dashboard"]||[]).push([[10],{507:function(t,e){var n;(n=jQuery).fn.extend({tableExport:function(t){function e(t){var e=[];return n(t).find("thead").first().find("th").each((function(t,o){void 0!==n(o).attr("data-field")?e[t]=n(o).attr("data-field"):e[t]=t.toString()})),e}function o(t,e,o,i,a){if(-1==n.inArray(o,b.ignoreRow)&&-1==n.inArray(o-i,b.ignoreRow)){var l=n(t).filter((function(){return"none"!=n(this).data("tableexport-display")&&(n(this).is(":visible")||"always"==n(this).data("tableexport-display")||"always"==n(this).closest("table").data("tableexport-display"))})).find(e),r=0;if(l.each((function(t){if("always"==n(this).data("tableexport-display")||"none"!=n(this).css("display")&&"hidden"!=n(this).css("visibility")&&"none"!=n(this).data("tableexport-display")){var e=t,i=!1;if(0e&&"undefined"!=typeof k[e]&&-1!=n.inArray(k[e],b.ignoreColumn)&&(i=!0):"number"!=typeof b.ignoreColumn[0]||-1==n.inArray(e,b.ignoreColumn)&&-1==n.inArray(e-l.length,b.ignoreColumn)||(i=!0)),!1===i&&"function"===typeof a){i=0;var s,d=0;if("undefined"!=typeof A[o]&&0a&&(d=Math.min(t.width,this.width),y=this.height*d/this.width),y/gi,"\u2060");if(t=n("
").html(s).contents(),s="",n.each(t.text().split("\u2028"),(function(t,e){0t?"-":"")+(b.numbers.output.thousandsSeparator?(h?d[0].substr(0,h)+b.numbers.output.thousandsSeparator:"")+d[0].substr(h).replace(/(\d{3})(?=\d)/g,"$1"+b.numbers.output.thousandsSeparator):d[0])+(d[1].length?b.numbers.output.decimalMark+d[1]:"")}}!0===b.escape&&(i=escape(i)),"function"===typeof b.onCellData&&(i=b.onCellData(l,e,o,i))}return i}function d(t,e,n){return e+"-"+n.toLowerCase()}function h(t,e){var n=/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(t),o=e;return n&&(o=[parseInt(n[1]),parseInt(n[2]),parseInt(n[3])]),o}function f(t){var e=c(t,"text-align"),o=c(t,"font-weight"),i=c(t,"font-style"),a="";return"start"==e&&(e="rtl"==c(t,"direction")?"right":"left"),700<=o&&(a="bold"),"italic"==i&&(a+=i),""===a&&(a="normal"),e={style:{align:e,bcolor:h(c(t,"background-color"),[255,255,255]),color:h(c(t,"color"),[0,0,0]),fstyle:a},colspan:parseInt(n(t).attr("colspan"))||0,rowspan:parseInt(n(t).attr("rowspan"))||0},null!==t&&(t=t.getBoundingClientRect(),e.rect={width:t.width,height:t.height}),e}function c(t,e){try{return window.getComputedStyle?(e=e.replace(/([a-z])([A-Z])/,d),window.getComputedStyle(t,null).getPropertyValue(e)):t.currentStyle?t.currentStyle[e]:t.style[e]}catch(n){}return""}function u(t,e,n){if(null!==(e=c(t,e).match(/\d+/))){e=e[0],t=t.parentElement;var o=document.createElement("div");return o.style.overflow="hidden",o.style.visibility="hidden",t.appendChild(o),o.style.width=100+n,n=100/o.offsetWidth,t.removeChild(o),e*n}return 0}function p(t){var e,n,o=0;if(0===t.length)return o;for(e=0,n=t.length;e(i=t.charCodeAt(o))?n+=String.fromCharCode(i):(127i?n+=String.fromCharCode(i>>6|192):(n+=String.fromCharCode(i>>12|224),n+=String.fromCharCode(i>>6&63|128)),n+=String.fromCharCode(63&i|128));for(t=n;s>2,e=(3&e)<<4|(n=t.charCodeAt(s++))>>4,a=(15&n)<<2|(o=t.charCodeAt(s++))>>6,l=63&o,isNaN(n)?a=l=64:isNaN(o)&&(l=64),r=r+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(i)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(e)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(a)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(l);return r}var m,b={consoleLog:!1,csvEnclosure:'"',csvSeparator:",",csvUseBOM:!0,displayTableName:!1,escape:!1,excelstyles:[],fileName:"tableExport",htmlContent:!1,ignoreColumn:[],ignoreRow:[],jsonScope:"all",jspdf:{orientation:"p",unit:"pt",format:"a4",margins:{left:20,right:10,top:10,bottom:10},autotable:{styles:{cellPadding:2,rowHeight:12,fontSize:8,fillColor:255,textColor:50,fontStyle:"normal",overflow:"ellipsize",halign:"left",valign:"middle"},headerStyles:{fillColor:[52,73,94],textColor:255,fontStyle:"bold",halign:"center"},alternateRowStyles:{fillColor:245},tableExport:{onAfterAutotable:null,onBeforeAutotable:null,onTable:null,outputImages:!0}}},numbers:{html:{decimalMark:".",thousandsSeparator:","},output:{decimalMark:".",thousandsSeparator:","}},onCellData:null,onCellHtmlData:null,outputMode:"file",pdfmake:{enabled:!1},tbodySelector:"tr",tfootSelector:"tr",theadSelector:"tr",tableName:"myTableName",type:"csv",worksheetName:"xlsWorksheetName"},w=this,v=null,x=[],S=[],C=0,A=[],N="",k=[];if(n.extend(!0,b,t),k=e(w),"csv"==b.type||"tsv"==b.type||"txt"==b.type){var E="",j=(C=0,function(t,e,i){return t.each((function(){N="",o(this,e,C,i+t.length,(function(t,e,n){var o=N,i="";null!==t&&(e=null===(t=s(t,e,n))||""===t?"":t.toString(),"tsv"==b.type?(t instanceof Date&&t.toLocaleString(),i=r(e,"\t"," ")):t instanceof Date?i=b.csvEnclosure+t.toLocaleString()+b.csvEnclosure:(0<=(i=r(e,b.csvEnclosure,b.csvEnclosure+b.csvEnclosure)).indexOf(b.csvSeparator)||/[\r\n ]/g.test(i))&&(i=b.csvEnclosure+i+b.csvEnclosure)),N=o+(i+("tsv"==b.type?"\t":b.csvSeparator))})),0<(N=n.trim(N).substring(0,N.length-1)).length&&(0')+"";(x=n(w).find("thead").first().find(b.theadSelector)).each((function(){o(this,"th,td",C,x.length,(function(t,e,n){T+=""+s(t,e,n)+""})),C++})),T+="";var B=1;if(n(w).find("tbody").each((function(){S.push.apply(S,n(this).find(b.tbodySelector))})),b.tfootSelector.length&&S.push.apply(S,n(w).find("tfoot").find(b.tfootSelector)),n(S).each((function(){var t=1;N="",o(this,"td,th",C,x.length+S.length,(function(e,n,o){N+=""+s(e,n,o)+"",t++})),0"!=N&&(T+=''+N+"",B++),C++})),T+="",!0===b.consoleLog&&console.log(T),"string"===b.outputMode)return T;if("base64"===b.outputMode)return g(T);try{m=new Blob([T],{type:"application/xml;charset=utf-8"}),saveAs(m,b.fileName+".xml")}catch(V){y(b.fileName+".xml","data:application/xml;charset=utf-8;base64,",T)}}else if("excel"==b.type||"xls"==b.type||"word"==b.type||"doc"==b.type){var P="excel"==(t="excel"==b.type||"xls"==b.type?"excel":"word")?"xls":"doc",R='xmlns:x="urn:schemas-microsoft-com:office:'+t+'"',I="";if(n(w).filter((function(){return"none"!=n(this).data("tableexport-display")&&(n(this).is(":visible")||"always"==n(this).data("tableexport-display"))})).each((function(){var t=n(this);C=0,k=e(this),I+="",(x=t.find("thead").first().find(b.theadSelector)).each((function(){N="",o(this,"th,td",C,x.length,(function(t,e,o){if(null!==t){var i="";for(var a in N+=""}})),0"+N+""),C++})),I+="",t.find("tbody").each((function(){S.push.apply(S,n(this).find(b.tbodySelector))})),b.tfootSelector.length&&S.push.apply(S,t.find("tfoot").find(b.tfootSelector)),n(S).each((function(){var t=n(this);N="",o(this,"td,th",C,x.length+S.length,(function(e,o,i){if(null!==e){var a="",l=n(e).data("tableexport-msonumberformat");for(var r in"undefined"==typeof l&&"function"===typeof b.onMsoNumberFormat&&(l=b.onMsoNumberFormat(e,o,i)),"undefined"!=typeof l&&""!==l&&(a="style=\"mso-number-format:'"+l+"'"),b.excelstyles)b.excelstyles.hasOwnProperty(r)&&(""===(l=n(e).css(b.excelstyles[r]))&&(l=t.css(b.excelstyles[r])),""!==l&&"0px none rgb(0, 0, 0)"!=l&&"rgba(0, 0, 0, 0)"!=l&&(a+=""===a?'style="':";",a+=b.excelstyles[r]+":"+l));N+="")+""}})),0"+N+""),C++})),b.displayTableName&&(I+=""),I+="
"+s(n("

"+b.tableName+"

"))+"
",!0===b.consoleLog&&console.log(I)})),R='',"excel"===t&&(R+="\x3c!--[if gte mso 9]>",R+="",R+="",R+="",R+="",R+="",R+=b.worksheetName,R+="",R+="",R+="",R+="",R+="",R+="",R+="",R+="",R+="br {mso-data-placement:same-cell;}",R+="",R+="",R+=I,R+="",R+="",!0===b.consoleLog&&console.log(R),"string"===b.outputMode)return R;if("base64"===b.outputMode)return g(R);try{m=new Blob([R],{type:"application/vnd.ms-"+b.type}),saveAs(m,b.fileName+"."+P)}catch(V){y(b.fileName+"."+P,"data:application/vnd.ms-"+t+";base64,",R)}}else if("xlsx"==b.type){var U=[],H=[];C=0,S=n(w).find("thead").first().find(b.theadSelector),n(w).find("tbody").each((function(){S.push.apply(S,n(this).find(b.tbodySelector))})),b.tfootSelector.length&&S.push.apply(S,n(w).find("tfoot").find(b.tfootSelector)),n(S).each((function(){var t=[];o(this,"th,td",C,S.length,(function(e,n,o){if("undefined"!==typeof e&&null!==e){var i=parseInt(e.getAttribute("colspan")),a=parseInt(e.getAttribute("rowspan"));if(""!==(e=s(e,n,o))&&e==+e&&(e=+e),H.forEach((function(e){if(C>=e.s.r&&C<=e.e.r&&t.length>=e.s.c&&t.length<=e.e.c)for(var n=0;n<=e.e.c-e.s.c;++n)t.push(null)})),(a||i)&&(i=i||1,H.push({s:{r:C,c:t.length},e:{r:C+(a||1)-1,c:t.length+i-1}})),t.push(""!==e?e:null),i)for(a=0;ao&&(n.s.r=o),n.s.c>i&&(n.s.c=i),n.e.rn.s.c&&(e["!ref"]=XLSX.utils.encode_range(n)),e}(U))["!merges"]=H,t.SheetNames.push(b.worksheetName),t.Sheets[b.worksheetName]=P,t=XLSX.write(t,{bookType:b.type,bookSST:!1,type:"binary"});try{m=new Blob([function(t){for(var e=new ArrayBuffer(t.length),n=new Uint8Array(e),o=0;o!=t.length;++o)n[o]=255&t.charCodeAt(o);return e}(t)],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8"}),saveAs(m,b.fileName+"."+b.type)}catch(V){y(b.fileName+"."+b.type,"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8",U)}}else if("png"==b.type)html2canvas(n(w)[0]).then((function(t){t=t.toDataURL();for(var e=atob(t.substring(22)),n=new ArrayBuffer(e.length),o=new Uint8Array(n),i=0;iQ){for(var e in t>z.a0[0]&&(q="a0",J="l"),z)z.hasOwnProperty(e)&&z[e][1]>t&&(q=e,J="l",z[e][0]>t&&(J="p"));Q=t}}})),b.jspdf.format=""===q?"a4":q,b.jspdf.orientation=""===J?"w":J}X.doc=new jsPDF(b.jspdf.orientation,b.jspdf.unit,b.jspdf.format),!0===X.outputImages&&(X.images={}),"undefined"!=typeof X.images&&(n(w).filter((function(){return"none"!=n(this).data("tableexport-display")&&(n(this).is(":visible")||"always"==n(this).data("tableexport-display"))})).each((function(){var t=0;x=n(this).find("thead").find(b.theadSelector),n(this).find("tbody").each((function(){S.push.apply(S,n(this).find(b.tbodySelector))})),b.tfootSelector.length&&S.push.apply(S,n(this).find("tfoot").find(b.tfootSelector)),n(S).each((function(){o(this,"td,th",x.length+t,x.length+S.length,(function(t,e,o){"undefined"!==typeof t&&null!==t&&"undefined"!=typeof(e=n(t).children())&&0t.styles.rowHeight&&(t.styles.rowHeight=o)),"undefined"!=typeof i.style&&!0!==i.style.hidden&&(t.styles.halign=i.style.align,"inherit"===r.styles.fillColor&&(t.styles.fillColor=i.style.bcolor),"inherit"===r.styles.textColor&&(t.styles.textColor=i.style.color),"inherit"===r.styles.fontStyle&&(t.styles.fontStyle=i.style.fstyle))}}),"function"!==typeof r.createdCell&&(r.createdCell=function(t,e){var n=X.rowoptions[e.row.index+":"+e.column.dataKey];"undefined"!=typeof n&&"undefined"!=typeof n.style&&!0!==n.style.hidden&&(t.styles.halign=n.style.align,"inherit"===r.styles.fillColor&&(t.styles.fillColor=n.style.bcolor),"inherit"===r.styles.textColor&&(t.styles.textColor=n.style.color),"inherit"===r.styles.fontStyle&&(t.styles.fontStyle=n.style.fstyle))}),"function"!==typeof r.drawHeaderCell&&(r.drawHeaderCell=function(t,e){var n=X.columns[e.column.dataKey];return(!0!==n.style.hasOwnProperty("hidden")||!0!==n.style.hidden)&&0<=n.rowIndex&&a(t,e,n)}),"function"!==typeof r.drawCell&&(r.drawCell=function(t,e){var n=X.rowoptions[e.row.index+":"+e.column.dataKey];if(a(t,e,n)){if(X.doc.rect(t.x,t.y,t.width,t.height,t.styles.fillStyle),"undefined"!=typeof n&&"undefined"!=typeof n.kids&&0X.dh||"undefined"==typeof X.dh)&&(X.dh=o),X.dw=t.width/n.rect.width,l(t,n.kids,X)}X.doc.autoTableText(t.text,t.textPos.x,t.textPos.y,{halign:t.styles.halign,valign:t.styles.valign})}return!1}),X.headerrows=[],(x=n(this).find("thead").find(b.theadSelector)).each((function(){t=0,X.headerrows[i]=[],o(this,"th,td",i,x.length,(function(e,n,o){var a=f(e);a.title=s(e,n,o),a.key=t++,a.rowIndex=i,X.headerrows[i].push(a)})),i++})),0d&&\"undefined\"!=typeof H[d]&&-1!=c.inArray(H[d],a.ignoreColumn)&&(k=!0):\"number\"!=typeof a.ignoreColumn[0]||-1==c.inArray(d,a.ignoreColumn)&&-1==c.inArray(d-g.length,a.ignoreColumn)||(k=!0));if(!1===k&&\"function\"===typeof w){var k=\r\n0,m,f=0;if(\"undefined\"!=typeof A[e]&&0g&&(f=Math.min(b.width,this.width),l=this.height*f/this.width),l/gi,\"\\u2060\");b=c(\"
\").html(t).contents();t=\"\";c.each(b.text().split(\"\\u2028\"),function(b,a){0b?\"-\":\"\")+(a.numbers.output.thousandsSeparator?(f?m[0].substr(0,f)+a.numbers.output.thousandsSeparator:\"\")+m[0].substr(f).replace(/(\\d{3})(?=\\d)/g,\"$1\"+a.numbers.output.thousandsSeparator):m[0])+(m[1].length?\r\na.numbers.output.decimalMark+m[1]:\"\")}}!0===a.escape&&(d=escape(d));\"function\"===typeof a.onCellData&&(d=a.onCellData(w,k,e,d))}return d}function la(b,a,e){return a+\"-\"+e.toLowerCase()}function N(b,a){var k=/^rgb\\((\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})\\)$/.exec(b),d=a;k&&(d=[parseInt(k[1]),parseInt(k[2]),parseInt(k[3])]);return d}function fa(b){var a=E(b,\"text-align\"),e=E(b,\"font-weight\"),d=E(b,\"font-style\"),w=\"\";\"start\"==a&&(a=\"rtl\"==E(b,\"direction\")?\"right\":\"left\");700<=e&&(w=\"bold\");\"italic\"==d&&\r\n(w+=d);\"\"===w&&(w=\"normal\");a={style:{align:a,bcolor:N(E(b,\"background-color\"),[255,255,255]),color:N(E(b,\"color\"),[0,0,0]),fstyle:w},colspan:parseInt(c(b).attr(\"colspan\"))||0,rowspan:parseInt(c(b).attr(\"rowspan\"))||0};null!==b&&(b=b.getBoundingClientRect(),a.rect={width:b.width,height:b.height});return a}function E(b,a){try{return window.getComputedStyle?(a=a.replace(/([a-z])([A-Z])/,la),window.getComputedStyle(b,null).getPropertyValue(a)):b.currentStyle?b.currentStyle[a]:b.style[a]}catch(e){}return\"\"}\r\nfunction O(b,a,e){a=E(b,a).match(/\\d+/);if(null!==a){a=a[0];b=b.parentElement;var d=document.createElement(\"div\");d.style.overflow=\"hidden\";d.style.visibility=\"hidden\";b.appendChild(d);d.style.width=100+e;e=100/d.offsetWidth;b.removeChild(d);return a*e}return 0}function T(){if(!(this instanceof T))return new T;this.SheetNames=[];this.Sheets={}}function ma(a){for(var b=new ArrayBuffer(a.length),e=new Uint8Array(b),d=0;d!=a.length;++d)e[d]=a.charCodeAt(d)&255;return b}function na(a){for(var b={},e=\r\n{s:{c:1E7,r:1E7},e:{c:0,r:0}},d=0;d!=a.length;++d)for(var c=0;c!=a[d].length;++c){e.s.r>d&&(e.s.r=d);e.s.c>c&&(e.s.c=c);e.e.re.s.c&&(b[\"!ref\"]=XLSX.utils.encode_range(e));\r\nreturn b}function da(a){var b=0,c,d,f;if(0===a.length)return b;c=0;for(f=a.length;cg?d+=String.fromCharCode(g):(127g?d+=String.fromCharCode(g>>6|192):(d+=String.fromCharCode(g>>12|224),d+=String.fromCharCode(g>>6&63|128)),d+=String.fromCharCode(g&63|128));for(a=d;h>2,c=(c&3)<<4|d>>4,t=(d&15)<<2|f>>6,m=f&63,isNaN(d)?t=m=64:\r\nisNaN(f)&&(m=64),b=b+\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".charAt(g)+\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".charAt(c)+\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".charAt(t)+\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".charAt(m);return b}var a={consoleLog:!1,csvEnclosure:'\"',csvSeparator:\",\",csvUseBOM:!0,displayTableName:!1,escape:!1,excelstyles:[],fileName:\"tableExport\",htmlContent:!1,ignoreColumn:[],\r\nignoreRow:[],jsonScope:\"all\",jspdf:{orientation:\"p\",unit:\"pt\",format:\"a4\",margins:{left:20,right:10,top:10,bottom:10},autotable:{styles:{cellPadding:2,rowHeight:12,fontSize:8,fillColor:255,textColor:50,fontStyle:\"normal\",overflow:\"ellipsize\",halign:\"left\",valign:\"middle\"},headerStyles:{fillColor:[52,73,94],textColor:255,fontStyle:\"bold\",halign:\"center\"},alternateRowStyles:{fillColor:245},tableExport:{onAfterAutotable:null,onBeforeAutotable:null,onTable:null,outputImages:!0}}},numbers:{html:{decimalMark:\".\",\r\nthousandsSeparator:\",\"},output:{decimalMark:\".\",thousandsSeparator:\",\"}},onCellData:null,onCellHtmlData:null,outputMode:\"file\",pdfmake:{enabled:!1},tbodySelector:\"tr\",tfootSelector:\"tr\",theadSelector:\"tr\",tableName:\"myTableName\",type:\"csv\",worksheetName:\"xlsWorksheetName\"},r=this,Q=null,p=[],h=[],l=0,A=[],n=\"\",H=[],z;c.extend(!0,a,u);H=S(r);if(\"csv\"==a.type||\"tsv\"==a.type||\"txt\"==a.type){var D=\"\",I=0,l=0,U=function(b,k,e){b.each(function(){n=\"\";y(this,k,l,e+b.length,function(b,c,e){var d=n,g=\"\";if(null!==\r\nb)if(b=x(b,c,e),c=null===b||\"\"===b?\"\":b.toString(),\"tsv\"==a.type)b instanceof Date&&b.toLocaleString(),g=P(c,\"\\t\",\" \");else if(b instanceof Date)g=a.csvEnclosure+b.toLocaleString()+a.csvEnclosure;else if(g=P(c,a.csvEnclosure,a.csvEnclosure+a.csvEnclosure),0<=g.indexOf(a.csvSeparator)||/[\\r\\n ]/g.test(g))g=a.csvEnclosure+g+a.csvEnclosure;n=d+(g+(\"tsv\"==a.type?\"\\t\":a.csvSeparator))});n=c.trim(n).substring(0,n.length-1);0',B=B+\"\",p=c(r).find(\"thead\").first().find(a.theadSelector);p.each(function(){y(this,\"th,td\",l,p.length,function(a,c,e){B+=\"\"+x(a,c,e)+\"\"});l++});var B=B+\"\",ga=1;c(r).find(\"tbody\").each(function(){h.push.apply(h,c(this).find(a.tbodySelector))});a.tfootSelector.length&&h.push.apply(h,c(r).find(\"tfoot\").find(a.tfootSelector));c(h).each(function(){var a=1;n=\"\";y(this,\"td,th\",\r\nl,p.length+h.length,function(b,c,d){n+=\"\"+x(b,c,d)+\"\";a++});0\"!=n&&(B+=''+n+\"\",ga++);l++});B+=\"\";!0===a.consoleLog&&console.log(B);if(\"string\"===a.outputMode)return B;if(\"base64\"===a.outputMode)return F(B);try{z=new Blob([B],{type:\"application/xml;charset=utf-8\"}),saveAs(z,a.fileName+\".xml\")}catch(b){C(a.fileName+\".xml\",\"data:application/xml;charset=utf-8;base64,\",B)}}else if(\"excel\"==a.type||\"xls\"==\r\na.type||\"word\"==a.type||\"doc\"==a.type){u=\"excel\"==a.type||\"xls\"==a.type?\"excel\":\"word\";var K=\"excel\"==u?\"xls\":\"doc\",q='xmlns:x=\"urn:schemas-microsoft-com:office:'+u+'\"',G=\"\";c(r).filter(function(){return\"none\"!=c(this).data(\"tableexport-display\")&&(c(this).is(\":visible\")||\"always\"==c(this).data(\"tableexport-display\"))}).each(function(){var b=c(this);l=0;H=S(this);G+=\"\";p=b.find(\"thead\").first().find(a.theadSelector);p.each(function(){n=\"\";y(this,\"th,td\",l,p.length,function(b,e,d){if(null!==\r\nb){var k=\"\";n+=\"\"+x(b,e,d)+\"\"}});0\"+n+\"\");l++});G+=\"\";b.find(\"tbody\").each(function(){h.push.apply(h,\r\nc(this).find(a.tbodySelector))});a.tfootSelector.length&&h.push.apply(h,b.find(\"tfoot\").find(a.tfootSelector));c(h).each(function(){var b=c(this);n=\"\";y(this,\"td,th\",l,p.length+h.length,function(e,d,k){if(null!==e){var g=\"\",f=c(e).data(\"tableexport-msonumberformat\");\"undefined\"==typeof f&&\"function\"===typeof a.onMsoNumberFormat&&(f=a.onMsoNumberFormat(e,d,k));\"undefined\"!=typeof f&&\"\"!==f&&(g=\"style=\\\"mso-number-format:'\"+f+\"'\");for(var m in a.excelstyles)a.excelstyles.hasOwnProperty(m)&&(f=c(e).css(a.excelstyles[m]),\r\n\"\"===f&&(f=b.css(a.excelstyles[m])),\"\"!==f&&\"0px none rgb(0, 0, 0)\"!=f&&\"rgba(0, 0, 0, 0)\"!=f&&(g+=\"\"===g?'style=\"':\";\",g+=a.excelstyles[m]+\":\"+f));n+=\"\"+x(e,d,k).replace(/\\n/g,\"
\")+\"\"}});0\"+n+\"\");l++});a.displayTableName&&(G+=\"
\");G+=\"
\"+x(c(\"

\"+a.tableName+\"

\"))+\r\n\"
\";!0===a.consoleLog&&console.log(G)});q=''+('')+\"\";\"excel\"===u&&(q+=\"\\x3c!--[if gte mso 9]>\",q+=\"\",q+=\"\",q+=\"\",q+=\"\",q+=\"\",q+=a.worksheetName,q+=\"\",q+=\"\",q+=\"\",\r\nq+=\"\",q+=\"\",q+=\"\",q+=\"\",q+=\"\",q+=\"br {mso-data-placement:same-cell;}\";q+=\"\";q+=\"\";q+=G;q+=\"\";q+=\"\";!0===a.consoleLog&&console.log(q);if(\"string\"===a.outputMode)return q;if(\"base64\"===a.outputMode)return F(q);try{z=new Blob([q],{type:\"application/vnd.ms-\"+a.type}),saveAs(z,a.fileName+\".\"+K)}catch(b){C(a.fileName+\".\"+K,\"data:application/vnd.ms-\"+u+\";base64,\",\r\nq)}}else if(\"xlsx\"==a.type){var W=[],X=[],l=0,h=c(r).find(\"thead\").first().find(a.theadSelector);c(r).find(\"tbody\").each(function(){h.push.apply(h,c(this).find(a.tbodySelector))});a.tfootSelector.length&&h.push.apply(h,c(r).find(\"tfoot\").find(a.tfootSelector));c(h).each(function(){var a=[];y(this,\"th,td\",l,h.length,function(b,c,d){if(\"undefined\"!==typeof b&&null!==b){var e=parseInt(b.getAttribute(\"colspan\")),g=parseInt(b.getAttribute(\"rowspan\"));b=x(b,c,d);\"\"!==b&&b==+b&&(b=+b);X.forEach(function(b){if(l>=\r\nb.s.r&&l<=b.e.r&&a.length>=b.s.c&&a.length<=b.e.c)for(var c=0;c<=b.e.c-b.s.c;++c)a.push(null)});if(g||e)e=e||1,X.push({s:{r:l,c:a.length},e:{r:l+(g||1)-1,c:a.length+e-1}});a.push(\"\"!==b?b:null);if(e)for(g=0;gia){a>L.a0[0]&&(R=\"a0\",M=\"l\");for(var f in L)L.hasOwnProperty(f)&&L[f][1]>a&&(R=f,M=\"l\",L[f][0]>a&&(M=\"p\"));ia=a}}});a.jspdf.format=\"\"===R?\"a4\":R;a.jspdf.orientation=\"\"===M?\"w\":M}f.doc=new jsPDF(a.jspdf.orientation,a.jspdf.unit,a.jspdf.format);!0===f.outputImages&&(f.images={});\"undefined\"!=typeof f.images&&(c(r).filter(function(){return\"none\"!=c(this).data(\"tableexport-display\")&&(c(this).is(\":visible\")||\"always\"==c(this).data(\"tableexport-display\"))}).each(function(){var b=\r\n0;p=c(this).find(\"thead\").find(a.theadSelector);c(this).find(\"tbody\").each(function(){h.push.apply(h,c(this).find(a.tbodySelector))});a.tfootSelector.length&&h.push.apply(h,c(this).find(\"tfoot\").find(a.tfootSelector));c(h).each(function(){y(this,\"td,th\",p.length+b,p.length+h.length,function(a,b,d){\"undefined\"!==typeof a&&null!==a&&(b=c(a).children(),\"undefined\"!=typeof b&&0a.styles.rowHeight&&(a.styles.rowHeight=g)}\"undefined\"!=typeof e.style&&!0!==e.style.hidden&&(a.styles.halign=e.style.align,\"inherit\"===d.styles.fillColor&&(a.styles.fillColor=e.style.bcolor),\"inherit\"===d.styles.textColor&&(a.styles.textColor=e.style.color),\"inherit\"===d.styles.fontStyle&&(a.styles.fontStyle=e.style.fstyle))}});\"function\"!==typeof d.createdCell&&(d.createdCell=\r\nfunction(a,b){var c=f.rowoptions[b.row.index+\":\"+b.column.dataKey];\"undefined\"!=typeof c&&\"undefined\"!=typeof c.style&&!0!==c.style.hidden&&(a.styles.halign=c.style.align,\"inherit\"===d.styles.fillColor&&(a.styles.fillColor=c.style.bcolor),\"inherit\"===d.styles.textColor&&(a.styles.textColor=c.style.color),\"inherit\"===d.styles.fontStyle&&(a.styles.fontStyle=c.style.fstyle))});\"function\"!==typeof d.drawHeaderCell&&(d.drawHeaderCell=function(a,b){var c=f.columns[b.column.dataKey];return(!0!==c.style.hasOwnProperty(\"hidden\")||\r\n!0!==c.style.hidden)&&0<=c.rowIndex?ba(a,b,c):!1});\"function\"!==typeof d.drawCell&&(d.drawCell=function(a,b){var c=f.rowoptions[b.row.index+\":\"+b.column.dataKey];if(ba(a,b,c)){f.doc.rect(a.x,a.y,a.width,a.height,a.styles.fillStyle);if(\"undefined\"!=typeof c&&\"undefined\"!=typeof c.kids&&0f.dh||\"undefined\"==typeof f.dh)f.dh=d;f.dw=a.width/c.rect.width;ea(a,c.kids,f)}f.doc.autoTableText(a.text,a.textPos.x,a.textPos.y,{halign:a.styles.halign,valign:a.styles.valign})}return!1});\r\nf.headerrows=[];p=c(this).find(\"thead\").find(a.theadSelector);p.each(function(){b=0;f.headerrows[e]=[];y(this,\"th,td\",e,p.length,function(a,c,d){var g=fa(a);g.title=x(a,c,d);g.key=b++;g.rowIndex=e;f.headerrows[e].push(g)});e++});0e&&"undefined"!=typeof k[e]&&-1!=n.inArray(k[e],b.ignoreColumn)&&(i=!0):"number"!=typeof b.ignoreColumn[0]||-1==n.inArray(e,b.ignoreColumn)&&-1==n.inArray(e-l.length,b.ignoreColumn)||(i=!0)),!1===i&&"function"===typeof a){i=0;var s,d=0;if("undefined"!=typeof A[o]&&0a&&(d=Math.min(t.width,this.width),y=this.height*d/this.width),y/gi,"\u2060");if(t=n("
").html(s).contents(),s="",n.each(t.text().split("\u2028"),(function(t,e){0t?"-":"")+(b.numbers.output.thousandsSeparator?(h?d[0].substr(0,h)+b.numbers.output.thousandsSeparator:"")+d[0].substr(h).replace(/(\d{3})(?=\d)/g,"$1"+b.numbers.output.thousandsSeparator):d[0])+(d[1].length?b.numbers.output.decimalMark+d[1]:"")}}!0===b.escape&&(i=escape(i)),"function"===typeof b.onCellData&&(i=b.onCellData(l,e,o,i))}return i}function d(t,e,n){return e+"-"+n.toLowerCase()}function h(t,e){var n=/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(t),o=e;return n&&(o=[parseInt(n[1]),parseInt(n[2]),parseInt(n[3])]),o}function f(t){var e=c(t,"text-align"),o=c(t,"font-weight"),i=c(t,"font-style"),a="";return"start"==e&&(e="rtl"==c(t,"direction")?"right":"left"),700<=o&&(a="bold"),"italic"==i&&(a+=i),""===a&&(a="normal"),e={style:{align:e,bcolor:h(c(t,"background-color"),[255,255,255]),color:h(c(t,"color"),[0,0,0]),fstyle:a},colspan:parseInt(n(t).attr("colspan"))||0,rowspan:parseInt(n(t).attr("rowspan"))||0},null!==t&&(t=t.getBoundingClientRect(),e.rect={width:t.width,height:t.height}),e}function c(t,e){try{return window.getComputedStyle?(e=e.replace(/([a-z])([A-Z])/,d),window.getComputedStyle(t,null).getPropertyValue(e)):t.currentStyle?t.currentStyle[e]:t.style[e]}catch(n){}return""}function u(t,e,n){if(null!==(e=c(t,e).match(/\d+/))){e=e[0],t=t.parentElement;var o=document.createElement("div");return o.style.overflow="hidden",o.style.visibility="hidden",t.appendChild(o),o.style.width=100+n,n=100/o.offsetWidth,t.removeChild(o),e*n}return 0}function p(t){var e,n,o=0;if(0===t.length)return o;for(e=0,n=t.length;e(i=t.charCodeAt(o))?n+=String.fromCharCode(i):(127i?n+=String.fromCharCode(i>>6|192):(n+=String.fromCharCode(i>>12|224),n+=String.fromCharCode(i>>6&63|128)),n+=String.fromCharCode(63&i|128));for(t=n;s>2,e=(3&e)<<4|(n=t.charCodeAt(s++))>>4,a=(15&n)<<2|(o=t.charCodeAt(s++))>>6,l=63&o,isNaN(n)?a=l=64:isNaN(o)&&(l=64),r=r+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(i)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(e)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(a)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(l);return r}var m,b={consoleLog:!1,csvEnclosure:'"',csvSeparator:",",csvUseBOM:!0,displayTableName:!1,escape:!1,excelstyles:[],fileName:"tableExport",htmlContent:!1,ignoreColumn:[],ignoreRow:[],jsonScope:"all",jspdf:{orientation:"p",unit:"pt",format:"a4",margins:{left:20,right:10,top:10,bottom:10},autotable:{styles:{cellPadding:2,rowHeight:12,fontSize:8,fillColor:255,textColor:50,fontStyle:"normal",overflow:"ellipsize",halign:"left",valign:"middle"},headerStyles:{fillColor:[52,73,94],textColor:255,fontStyle:"bold",halign:"center"},alternateRowStyles:{fillColor:245},tableExport:{onAfterAutotable:null,onBeforeAutotable:null,onTable:null,outputImages:!0}}},numbers:{html:{decimalMark:".",thousandsSeparator:","},output:{decimalMark:".",thousandsSeparator:","}},onCellData:null,onCellHtmlData:null,outputMode:"file",pdfmake:{enabled:!1},tbodySelector:"tr",tfootSelector:"tr",theadSelector:"tr",tableName:"myTableName",type:"csv",worksheetName:"xlsWorksheetName"},w=this,v=null,x=[],S=[],C=0,A=[],N="",k=[];if(n.extend(!0,b,t),k=e(w),"csv"==b.type||"tsv"==b.type||"txt"==b.type){var E="",j=(C=0,function(t,e,i){return t.each((function(){N="",o(this,e,C,i+t.length,(function(t,e,n){var o=N,i="";null!==t&&(e=null===(t=s(t,e,n))||""===t?"":t.toString(),"tsv"==b.type?(t instanceof Date&&t.toLocaleString(),i=r(e,"\t"," ")):t instanceof Date?i=b.csvEnclosure+t.toLocaleString()+b.csvEnclosure:(0<=(i=r(e,b.csvEnclosure,b.csvEnclosure+b.csvEnclosure)).indexOf(b.csvSeparator)||/[\r\n ]/g.test(i))&&(i=b.csvEnclosure+i+b.csvEnclosure)),N=o+(i+("tsv"==b.type?"\t":b.csvSeparator))})),0<(N=n.trim(N).substring(0,N.length-1)).length&&(0')+"";(x=n(w).find("thead").first().find(b.theadSelector)).each((function(){o(this,"th,td",C,x.length,(function(t,e,n){T+=""+s(t,e,n)+""})),C++})),T+="";var B=1;if(n(w).find("tbody").each((function(){S.push.apply(S,n(this).find(b.tbodySelector))})),b.tfootSelector.length&&S.push.apply(S,n(w).find("tfoot").find(b.tfootSelector)),n(S).each((function(){var t=1;N="",o(this,"td,th",C,x.length+S.length,(function(e,n,o){N+=""+s(e,n,o)+"",t++})),0"!=N&&(T+=''+N+"",B++),C++})),T+="",!0===b.consoleLog&&console.log(T),"string"===b.outputMode)return T;if("base64"===b.outputMode)return g(T);try{m=new Blob([T],{type:"application/xml;charset=utf-8"}),saveAs(m,b.fileName+".xml")}catch(V){y(b.fileName+".xml","data:application/xml;charset=utf-8;base64,",T)}}else if("excel"==b.type||"xls"==b.type||"word"==b.type||"doc"==b.type){var P="excel"==(t="excel"==b.type||"xls"==b.type?"excel":"word")?"xls":"doc",R='xmlns:x="urn:schemas-microsoft-com:office:'+t+'"',I="";if(n(w).filter((function(){return"none"!=n(this).data("tableexport-display")&&(n(this).is(":visible")||"always"==n(this).data("tableexport-display"))})).each((function(){var t=n(this);C=0,k=e(this),I+="",(x=t.find("thead").first().find(b.theadSelector)).each((function(){N="",o(this,"th,td",C,x.length,(function(t,e,o){if(null!==t){var i="";for(var a in N+=""}})),0"+N+""),C++})),I+="",t.find("tbody").each((function(){S.push.apply(S,n(this).find(b.tbodySelector))})),b.tfootSelector.length&&S.push.apply(S,t.find("tfoot").find(b.tfootSelector)),n(S).each((function(){var t=n(this);N="",o(this,"td,th",C,x.length+S.length,(function(e,o,i){if(null!==e){var a="",l=n(e).data("tableexport-msonumberformat");for(var r in"undefined"==typeof l&&"function"===typeof b.onMsoNumberFormat&&(l=b.onMsoNumberFormat(e,o,i)),"undefined"!=typeof l&&""!==l&&(a="style=\"mso-number-format:'"+l+"'"),b.excelstyles)b.excelstyles.hasOwnProperty(r)&&(""===(l=n(e).css(b.excelstyles[r]))&&(l=t.css(b.excelstyles[r])),""!==l&&"0px none rgb(0, 0, 0)"!=l&&"rgba(0, 0, 0, 0)"!=l&&(a+=""===a?'style="':";",a+=b.excelstyles[r]+":"+l));N+="")+""}})),0"+N+""),C++})),b.displayTableName&&(I+=""),I+="
"+s(n("

"+b.tableName+"

"))+"
",!0===b.consoleLog&&console.log(I)})),R='',"excel"===t&&(R+="\x3c!--[if gte mso 9]>",R+="",R+="",R+="",R+="",R+="",R+=b.worksheetName,R+="",R+="",R+="",R+="",R+="",R+="",R+="",R+="",R+="br {mso-data-placement:same-cell;}",R+="",R+="",R+=I,R+="",R+="",!0===b.consoleLog&&console.log(R),"string"===b.outputMode)return R;if("base64"===b.outputMode)return g(R);try{m=new Blob([R],{type:"application/vnd.ms-"+b.type}),saveAs(m,b.fileName+"."+P)}catch(V){y(b.fileName+"."+P,"data:application/vnd.ms-"+t+";base64,",R)}}else if("xlsx"==b.type){var U=[],H=[];C=0,S=n(w).find("thead").first().find(b.theadSelector),n(w).find("tbody").each((function(){S.push.apply(S,n(this).find(b.tbodySelector))})),b.tfootSelector.length&&S.push.apply(S,n(w).find("tfoot").find(b.tfootSelector)),n(S).each((function(){var t=[];o(this,"th,td",C,S.length,(function(e,n,o){if("undefined"!==typeof e&&null!==e){var i=parseInt(e.getAttribute("colspan")),a=parseInt(e.getAttribute("rowspan"));if(""!==(e=s(e,n,o))&&e==+e&&(e=+e),H.forEach((function(e){if(C>=e.s.r&&C<=e.e.r&&t.length>=e.s.c&&t.length<=e.e.c)for(var n=0;n<=e.e.c-e.s.c;++n)t.push(null)})),(a||i)&&(i=i||1,H.push({s:{r:C,c:t.length},e:{r:C+(a||1)-1,c:t.length+i-1}})),t.push(""!==e?e:null),i)for(a=0;ao&&(n.s.r=o),n.s.c>i&&(n.s.c=i),n.e.rn.s.c&&(e["!ref"]=XLSX.utils.encode_range(n)),e}(U))["!merges"]=H,t.SheetNames.push(b.worksheetName),t.Sheets[b.worksheetName]=P,t=XLSX.write(t,{bookType:b.type,bookSST:!1,type:"binary"});try{m=new Blob([function(t){for(var e=new ArrayBuffer(t.length),n=new Uint8Array(e),o=0;o!=t.length;++o)n[o]=255&t.charCodeAt(o);return e}(t)],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8"}),saveAs(m,b.fileName+"."+b.type)}catch(V){y(b.fileName+"."+b.type,"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8",U)}}else if("png"==b.type)html2canvas(n(w)[0]).then((function(t){t=t.toDataURL();for(var e=atob(t.substring(22)),n=new ArrayBuffer(e.length),o=new Uint8Array(n),i=0;iQ){for(var e in t>z.a0[0]&&(q="a0",J="l"),z)z.hasOwnProperty(e)&&z[e][1]>t&&(q=e,J="l",z[e][0]>t&&(J="p"));Q=t}}})),b.jspdf.format=""===q?"a4":q,b.jspdf.orientation=""===J?"w":J}X.doc=new jsPDF(b.jspdf.orientation,b.jspdf.unit,b.jspdf.format),!0===X.outputImages&&(X.images={}),"undefined"!=typeof X.images&&(n(w).filter((function(){return"none"!=n(this).data("tableexport-display")&&(n(this).is(":visible")||"always"==n(this).data("tableexport-display"))})).each((function(){var t=0;x=n(this).find("thead").find(b.theadSelector),n(this).find("tbody").each((function(){S.push.apply(S,n(this).find(b.tbodySelector))})),b.tfootSelector.length&&S.push.apply(S,n(this).find("tfoot").find(b.tfootSelector)),n(S).each((function(){o(this,"td,th",x.length+t,x.length+S.length,(function(t,e,o){"undefined"!==typeof t&&null!==t&&"undefined"!=typeof(e=n(t).children())&&0t.styles.rowHeight&&(t.styles.rowHeight=o)),"undefined"!=typeof i.style&&!0!==i.style.hidden&&(t.styles.halign=i.style.align,"inherit"===r.styles.fillColor&&(t.styles.fillColor=i.style.bcolor),"inherit"===r.styles.textColor&&(t.styles.textColor=i.style.color),"inherit"===r.styles.fontStyle&&(t.styles.fontStyle=i.style.fstyle))}}),"function"!==typeof r.createdCell&&(r.createdCell=function(t,e){var n=X.rowoptions[e.row.index+":"+e.column.dataKey];"undefined"!=typeof n&&"undefined"!=typeof n.style&&!0!==n.style.hidden&&(t.styles.halign=n.style.align,"inherit"===r.styles.fillColor&&(t.styles.fillColor=n.style.bcolor),"inherit"===r.styles.textColor&&(t.styles.textColor=n.style.color),"inherit"===r.styles.fontStyle&&(t.styles.fontStyle=n.style.fstyle))}),"function"!==typeof r.drawHeaderCell&&(r.drawHeaderCell=function(t,e){var n=X.columns[e.column.dataKey];return(!0!==n.style.hasOwnProperty("hidden")||!0!==n.style.hidden)&&0<=n.rowIndex&&a(t,e,n)}),"function"!==typeof r.drawCell&&(r.drawCell=function(t,e){var n=X.rowoptions[e.row.index+":"+e.column.dataKey];if(a(t,e,n)){if(X.doc.rect(t.x,t.y,t.width,t.height,t.styles.fillStyle),"undefined"!=typeof n&&"undefined"!=typeof n.kids&&0X.dh||"undefined"==typeof X.dh)&&(X.dh=o),X.dw=t.width/n.rect.width,l(t,n.kids,X)}X.doc.autoTableText(t.text,t.textPos.x,t.textPos.y,{halign:t.styles.halign,valign:t.styles.valign})}return!1}),X.headerrows=[],(x=n(this).find("thead").find(b.theadSelector)).each((function(){t=0,X.headerrows[i]=[],o(this,"th,td",i,x.length,(function(e,n,o){var a=f(e);a.title=s(e,n,o),a.key=t++,a.rowIndex=i,X.headerrows[i].push(a)})),i++})),0d&&\"undefined\"!=typeof H[d]&&-1!=c.inArray(H[d],a.ignoreColumn)&&(k=!0):\"number\"!=typeof a.ignoreColumn[0]||-1==c.inArray(d,a.ignoreColumn)&&-1==c.inArray(d-g.length,a.ignoreColumn)||(k=!0));if(!1===k&&\"function\"===typeof w){var k=\r\n0,m,f=0;if(\"undefined\"!=typeof A[e]&&0g&&(f=Math.min(b.width,this.width),l=this.height*f/this.width),l/gi,\"\\u2060\");b=c(\"
\").html(t).contents();t=\"\";c.each(b.text().split(\"\\u2028\"),function(b,a){0b?\"-\":\"\")+(a.numbers.output.thousandsSeparator?(f?m[0].substr(0,f)+a.numbers.output.thousandsSeparator:\"\")+m[0].substr(f).replace(/(\\d{3})(?=\\d)/g,\"$1\"+a.numbers.output.thousandsSeparator):m[0])+(m[1].length?\r\na.numbers.output.decimalMark+m[1]:\"\")}}!0===a.escape&&(d=escape(d));\"function\"===typeof a.onCellData&&(d=a.onCellData(w,k,e,d))}return d}function la(b,a,e){return a+\"-\"+e.toLowerCase()}function N(b,a){var k=/^rgb\\((\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})\\)$/.exec(b),d=a;k&&(d=[parseInt(k[1]),parseInt(k[2]),parseInt(k[3])]);return d}function fa(b){var a=E(b,\"text-align\"),e=E(b,\"font-weight\"),d=E(b,\"font-style\"),w=\"\";\"start\"==a&&(a=\"rtl\"==E(b,\"direction\")?\"right\":\"left\");700<=e&&(w=\"bold\");\"italic\"==d&&\r\n(w+=d);\"\"===w&&(w=\"normal\");a={style:{align:a,bcolor:N(E(b,\"background-color\"),[255,255,255]),color:N(E(b,\"color\"),[0,0,0]),fstyle:w},colspan:parseInt(c(b).attr(\"colspan\"))||0,rowspan:parseInt(c(b).attr(\"rowspan\"))||0};null!==b&&(b=b.getBoundingClientRect(),a.rect={width:b.width,height:b.height});return a}function E(b,a){try{return window.getComputedStyle?(a=a.replace(/([a-z])([A-Z])/,la),window.getComputedStyle(b,null).getPropertyValue(a)):b.currentStyle?b.currentStyle[a]:b.style[a]}catch(e){}return\"\"}\r\nfunction O(b,a,e){a=E(b,a).match(/\\d+/);if(null!==a){a=a[0];b=b.parentElement;var d=document.createElement(\"div\");d.style.overflow=\"hidden\";d.style.visibility=\"hidden\";b.appendChild(d);d.style.width=100+e;e=100/d.offsetWidth;b.removeChild(d);return a*e}return 0}function T(){if(!(this instanceof T))return new T;this.SheetNames=[];this.Sheets={}}function ma(a){for(var b=new ArrayBuffer(a.length),e=new Uint8Array(b),d=0;d!=a.length;++d)e[d]=a.charCodeAt(d)&255;return b}function na(a){for(var b={},e=\r\n{s:{c:1E7,r:1E7},e:{c:0,r:0}},d=0;d!=a.length;++d)for(var c=0;c!=a[d].length;++c){e.s.r>d&&(e.s.r=d);e.s.c>c&&(e.s.c=c);e.e.re.s.c&&(b[\"!ref\"]=XLSX.utils.encode_range(e));\r\nreturn b}function da(a){var b=0,c,d,f;if(0===a.length)return b;c=0;for(f=a.length;cg?d+=String.fromCharCode(g):(127g?d+=String.fromCharCode(g>>6|192):(d+=String.fromCharCode(g>>12|224),d+=String.fromCharCode(g>>6&63|128)),d+=String.fromCharCode(g&63|128));for(a=d;h>2,c=(c&3)<<4|d>>4,t=(d&15)<<2|f>>6,m=f&63,isNaN(d)?t=m=64:\r\nisNaN(f)&&(m=64),b=b+\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".charAt(g)+\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".charAt(c)+\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".charAt(t)+\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\".charAt(m);return b}var a={consoleLog:!1,csvEnclosure:'\"',csvSeparator:\",\",csvUseBOM:!0,displayTableName:!1,escape:!1,excelstyles:[],fileName:\"tableExport\",htmlContent:!1,ignoreColumn:[],\r\nignoreRow:[],jsonScope:\"all\",jspdf:{orientation:\"p\",unit:\"pt\",format:\"a4\",margins:{left:20,right:10,top:10,bottom:10},autotable:{styles:{cellPadding:2,rowHeight:12,fontSize:8,fillColor:255,textColor:50,fontStyle:\"normal\",overflow:\"ellipsize\",halign:\"left\",valign:\"middle\"},headerStyles:{fillColor:[52,73,94],textColor:255,fontStyle:\"bold\",halign:\"center\"},alternateRowStyles:{fillColor:245},tableExport:{onAfterAutotable:null,onBeforeAutotable:null,onTable:null,outputImages:!0}}},numbers:{html:{decimalMark:\".\",\r\nthousandsSeparator:\",\"},output:{decimalMark:\".\",thousandsSeparator:\",\"}},onCellData:null,onCellHtmlData:null,outputMode:\"file\",pdfmake:{enabled:!1},tbodySelector:\"tr\",tfootSelector:\"tr\",theadSelector:\"tr\",tableName:\"myTableName\",type:\"csv\",worksheetName:\"xlsWorksheetName\"},r=this,Q=null,p=[],h=[],l=0,A=[],n=\"\",H=[],z;c.extend(!0,a,u);H=S(r);if(\"csv\"==a.type||\"tsv\"==a.type||\"txt\"==a.type){var D=\"\",I=0,l=0,U=function(b,k,e){b.each(function(){n=\"\";y(this,k,l,e+b.length,function(b,c,e){var d=n,g=\"\";if(null!==\r\nb)if(b=x(b,c,e),c=null===b||\"\"===b?\"\":b.toString(),\"tsv\"==a.type)b instanceof Date&&b.toLocaleString(),g=P(c,\"\\t\",\" \");else if(b instanceof Date)g=a.csvEnclosure+b.toLocaleString()+a.csvEnclosure;else if(g=P(c,a.csvEnclosure,a.csvEnclosure+a.csvEnclosure),0<=g.indexOf(a.csvSeparator)||/[\\r\\n ]/g.test(g))g=a.csvEnclosure+g+a.csvEnclosure;n=d+(g+(\"tsv\"==a.type?\"\\t\":a.csvSeparator))});n=c.trim(n).substring(0,n.length-1);0',B=B+\"\",p=c(r).find(\"thead\").first().find(a.theadSelector);p.each(function(){y(this,\"th,td\",l,p.length,function(a,c,e){B+=\"\"+x(a,c,e)+\"\"});l++});var B=B+\"\",ga=1;c(r).find(\"tbody\").each(function(){h.push.apply(h,c(this).find(a.tbodySelector))});a.tfootSelector.length&&h.push.apply(h,c(r).find(\"tfoot\").find(a.tfootSelector));c(h).each(function(){var a=1;n=\"\";y(this,\"td,th\",\r\nl,p.length+h.length,function(b,c,d){n+=\"\"+x(b,c,d)+\"\";a++});0\"!=n&&(B+=''+n+\"\",ga++);l++});B+=\"\";!0===a.consoleLog&&console.log(B);if(\"string\"===a.outputMode)return B;if(\"base64\"===a.outputMode)return F(B);try{z=new Blob([B],{type:\"application/xml;charset=utf-8\"}),saveAs(z,a.fileName+\".xml\")}catch(b){C(a.fileName+\".xml\",\"data:application/xml;charset=utf-8;base64,\",B)}}else if(\"excel\"==a.type||\"xls\"==\r\na.type||\"word\"==a.type||\"doc\"==a.type){u=\"excel\"==a.type||\"xls\"==a.type?\"excel\":\"word\";var K=\"excel\"==u?\"xls\":\"doc\",q='xmlns:x=\"urn:schemas-microsoft-com:office:'+u+'\"',G=\"\";c(r).filter(function(){return\"none\"!=c(this).data(\"tableexport-display\")&&(c(this).is(\":visible\")||\"always\"==c(this).data(\"tableexport-display\"))}).each(function(){var b=c(this);l=0;H=S(this);G+=\"\";p=b.find(\"thead\").first().find(a.theadSelector);p.each(function(){n=\"\";y(this,\"th,td\",l,p.length,function(b,e,d){if(null!==\r\nb){var k=\"\";n+=\"\"+x(b,e,d)+\"\"}});0\"+n+\"\");l++});G+=\"\";b.find(\"tbody\").each(function(){h.push.apply(h,\r\nc(this).find(a.tbodySelector))});a.tfootSelector.length&&h.push.apply(h,b.find(\"tfoot\").find(a.tfootSelector));c(h).each(function(){var b=c(this);n=\"\";y(this,\"td,th\",l,p.length+h.length,function(e,d,k){if(null!==e){var g=\"\",f=c(e).data(\"tableexport-msonumberformat\");\"undefined\"==typeof f&&\"function\"===typeof a.onMsoNumberFormat&&(f=a.onMsoNumberFormat(e,d,k));\"undefined\"!=typeof f&&\"\"!==f&&(g=\"style=\\\"mso-number-format:'\"+f+\"'\");for(var m in a.excelstyles)a.excelstyles.hasOwnProperty(m)&&(f=c(e).css(a.excelstyles[m]),\r\n\"\"===f&&(f=b.css(a.excelstyles[m])),\"\"!==f&&\"0px none rgb(0, 0, 0)\"!=f&&\"rgba(0, 0, 0, 0)\"!=f&&(g+=\"\"===g?'style=\"':\";\",g+=a.excelstyles[m]+\":\"+f));n+=\"\"+x(e,d,k).replace(/\\n/g,\"
\")+\"\"}});0\"+n+\"\");l++});a.displayTableName&&(G+=\"
\");G+=\"
\"+x(c(\"

\"+a.tableName+\"

\"))+\r\n\"
\";!0===a.consoleLog&&console.log(G)});q=''+('')+\"\";\"excel\"===u&&(q+=\"\\x3c!--[if gte mso 9]>\",q+=\"\",q+=\"\",q+=\"\",q+=\"\",q+=\"\",q+=a.worksheetName,q+=\"\",q+=\"\",q+=\"\",\r\nq+=\"\",q+=\"\",q+=\"\",q+=\"\",q+=\"\",q+=\"br {mso-data-placement:same-cell;}\";q+=\"\";q+=\"\";q+=G;q+=\"\";q+=\"\";!0===a.consoleLog&&console.log(q);if(\"string\"===a.outputMode)return q;if(\"base64\"===a.outputMode)return F(q);try{z=new Blob([q],{type:\"application/vnd.ms-\"+a.type}),saveAs(z,a.fileName+\".\"+K)}catch(b){C(a.fileName+\".\"+K,\"data:application/vnd.ms-\"+u+\";base64,\",\r\nq)}}else if(\"xlsx\"==a.type){var W=[],X=[],l=0,h=c(r).find(\"thead\").first().find(a.theadSelector);c(r).find(\"tbody\").each(function(){h.push.apply(h,c(this).find(a.tbodySelector))});a.tfootSelector.length&&h.push.apply(h,c(r).find(\"tfoot\").find(a.tfootSelector));c(h).each(function(){var a=[];y(this,\"th,td\",l,h.length,function(b,c,d){if(\"undefined\"!==typeof b&&null!==b){var e=parseInt(b.getAttribute(\"colspan\")),g=parseInt(b.getAttribute(\"rowspan\"));b=x(b,c,d);\"\"!==b&&b==+b&&(b=+b);X.forEach(function(b){if(l>=\r\nb.s.r&&l<=b.e.r&&a.length>=b.s.c&&a.length<=b.e.c)for(var c=0;c<=b.e.c-b.s.c;++c)a.push(null)});if(g||e)e=e||1,X.push({s:{r:l,c:a.length},e:{r:l+(g||1)-1,c:a.length+e-1}});a.push(\"\"!==b?b:null);if(e)for(g=0;gia){a>L.a0[0]&&(R=\"a0\",M=\"l\");for(var f in L)L.hasOwnProperty(f)&&L[f][1]>a&&(R=f,M=\"l\",L[f][0]>a&&(M=\"p\"));ia=a}}});a.jspdf.format=\"\"===R?\"a4\":R;a.jspdf.orientation=\"\"===M?\"w\":M}f.doc=new jsPDF(a.jspdf.orientation,a.jspdf.unit,a.jspdf.format);!0===f.outputImages&&(f.images={});\"undefined\"!=typeof f.images&&(c(r).filter(function(){return\"none\"!=c(this).data(\"tableexport-display\")&&(c(this).is(\":visible\")||\"always\"==c(this).data(\"tableexport-display\"))}).each(function(){var b=\r\n0;p=c(this).find(\"thead\").find(a.theadSelector);c(this).find(\"tbody\").each(function(){h.push.apply(h,c(this).find(a.tbodySelector))});a.tfootSelector.length&&h.push.apply(h,c(this).find(\"tfoot\").find(a.tfootSelector));c(h).each(function(){y(this,\"td,th\",p.length+b,p.length+h.length,function(a,b,d){\"undefined\"!==typeof a&&null!==a&&(b=c(a).children(),\"undefined\"!=typeof b&&0a.styles.rowHeight&&(a.styles.rowHeight=g)}\"undefined\"!=typeof e.style&&!0!==e.style.hidden&&(a.styles.halign=e.style.align,\"inherit\"===d.styles.fillColor&&(a.styles.fillColor=e.style.bcolor),\"inherit\"===d.styles.textColor&&(a.styles.textColor=e.style.color),\"inherit\"===d.styles.fontStyle&&(a.styles.fontStyle=e.style.fstyle))}});\"function\"!==typeof d.createdCell&&(d.createdCell=\r\nfunction(a,b){var c=f.rowoptions[b.row.index+\":\"+b.column.dataKey];\"undefined\"!=typeof c&&\"undefined\"!=typeof c.style&&!0!==c.style.hidden&&(a.styles.halign=c.style.align,\"inherit\"===d.styles.fillColor&&(a.styles.fillColor=c.style.bcolor),\"inherit\"===d.styles.textColor&&(a.styles.textColor=c.style.color),\"inherit\"===d.styles.fontStyle&&(a.styles.fontStyle=c.style.fstyle))});\"function\"!==typeof d.drawHeaderCell&&(d.drawHeaderCell=function(a,b){var c=f.columns[b.column.dataKey];return(!0!==c.style.hasOwnProperty(\"hidden\")||\r\n!0!==c.style.hidden)&&0<=c.rowIndex?ba(a,b,c):!1});\"function\"!==typeof d.drawCell&&(d.drawCell=function(a,b){var c=f.rowoptions[b.row.index+\":\"+b.column.dataKey];if(ba(a,b,c)){f.doc.rect(a.x,a.y,a.width,a.height,a.styles.fillStyle);if(\"undefined\"!=typeof c&&\"undefined\"!=typeof c.kids&&0f.dh||\"undefined\"==typeof f.dh)f.dh=d;f.dw=a.width/c.rect.width;ea(a,c.kids,f)}f.doc.autoTableText(a.text,a.textPos.x,a.textPos.y,{halign:a.styles.halign,valign:a.styles.valign})}return!1});\r\nf.headerrows=[];p=c(this).find(\"thead\").find(a.theadSelector);p.each(function(){b=0;f.headerrows[e]=[];y(this,\"th,td\",e,p.length,function(a,c,d){var g=fa(a);g.title=x(a,c,d);g.key=b++;g.rowIndex=e;f.headerrows[e].push(g)});e++});0"+e+""};return function(e){function t(){e.apply(this,arguments)}e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t;var n={isMounted:{}};return n.isMounted.get=function(){return!!this.node},t.createFromExistingNode=function(e){return new t({id:e.getAttribute("id"),viewBox:e.getAttribute("viewBox"),content:e.outerHTML})},t.prototype.destroy=function(){this.isMounted&&this.unmount(),e.prototype.destroy.call(this)},t.prototype.mount=function(e){if(this.isMounted)return this.node;var t="string"===typeof e?document.querySelector(e):e,n=this.render();return this.node=n,t.appendChild(n),n},t.prototype.render=function(){var e=this.stringify();return function(e){var t=!!document.importNode,n=(new DOMParser).parseFromString(e,"image/svg+xml").documentElement;return t?document.importNode(n,!0):n}(s(e)).childNodes[0]},t.prototype.unmount=function(){this.node.parentNode.removeChild(this.node)},Object.defineProperties(t.prototype,n),t}(e)},e.exports=n()}).call(this,n(91))},function(e,t,n){(function(t){var n;n=function(){"use strict";function e(e,t){return e(t={exports:{}},t.exports),t.exports}"undefined"!==typeof window?window:"undefined"!==typeof t||"undefined"!==typeof self&&self;var n=e((function(e,t){e.exports=function(){function e(e){return e&&"object"===typeof e&&"[object RegExp]"!==Object.prototype.toString.call(e)&&"[object Date]"!==Object.prototype.toString.call(e)}function t(t,n){var i;return n&&!0===n.clone&&e(t)?r((i=t,Array.isArray(i)?[]:{}),t,n):t}function n(n,i,o){var a=n.slice();return i.forEach((function(i,c){"undefined"===typeof a[c]?a[c]=t(i,o):e(i)?a[c]=r(n[c],i,o):-1===n.indexOf(i)&&a.push(t(i,o))})),a}function r(i,o,a){var c=Array.isArray(o),s=(a||{arrayMerge:n}).arrayMerge||n;return c?Array.isArray(i)?s(i,o,a):t(o,a):function(n,i,o){var a={};return e(n)&&Object.keys(n).forEach((function(e){a[e]=t(n[e],o)})),Object.keys(i).forEach((function(c){e(i[c])&&n[c]?a[c]=r(n[c],i[c],o):a[c]=t(i[c],o)})),a}(i,o,a)}return r.all=function(e,t){if(!Array.isArray(e)||e.length<2)throw new Error("first argument should be an array with at least two elements");return e.reduce((function(e,n){return r(e,n,t)}))},r}()})),r=e((function(e,t){t.default={svg:{name:"xmlns",uri:"http://www.w3.org/2000/svg"},xlink:{name:"xmlns:xlink",uri:"http://www.w3.org/1999/xlink"}},e.exports=t.default})),i=r.svg,o=r.xlink,a={};a[i.name]=i.uri,a[o.name]=o.uri;var c,s=function(e,t){return void 0===e&&(e=""),""+e+""},l=r.svg,u=r.xlink,f={attrs:(c={style:["position: absolute","width: 0","height: 0"].join("; ")},c[l.name]=l.uri,c[u.name]=u.uri,c)},h=function(e){this.config=n(f,e||{}),this.symbols=[]};h.prototype.add=function(e){var t=this.symbols,n=this.find(e.id);return n?(t[t.indexOf(n)]=e,!1):(t.push(e),!0)},h.prototype.remove=function(e){var t=this.symbols,n=this.find(e);return!!n&&(t.splice(t.indexOf(n),1),n.destroy(),!0)},h.prototype.find=function(e){return this.symbols.filter((function(t){return t.id===e}))[0]||null},h.prototype.has=function(e){return null!==this.find(e)},h.prototype.stringify=function(){var e=this.config.attrs,t=this.symbols.map((function(e){return e.stringify()})).join("");return s(t,e)},h.prototype.toString=function(){return this.stringify()},h.prototype.destroy=function(){this.symbols.forEach((function(e){return e.destroy()}))};var d=function(e){var t=e.id,n=e.viewBox,r=e.content;this.id=t,this.viewBox=n,this.content=r};d.prototype.stringify=function(){return this.content},d.prototype.toString=function(){return this.stringify()},d.prototype.destroy=function(){var e=this;["id","viewBox","content"].forEach((function(t){return delete e[t]}))};var p=function(e){var t=!!document.importNode,n=(new DOMParser).parseFromString(e,"image/svg+xml").documentElement;return t?document.importNode(n,!0):n},z=function(e){function t(){e.apply(this,arguments)}e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t;var n={isMounted:{}};return n.isMounted.get=function(){return!!this.node},t.createFromExistingNode=function(e){return new t({id:e.getAttribute("id"),viewBox:e.getAttribute("viewBox"),content:e.outerHTML})},t.prototype.destroy=function(){this.isMounted&&this.unmount(),e.prototype.destroy.call(this)},t.prototype.mount=function(e){if(this.isMounted)return this.node;var t="string"===typeof e?document.querySelector(e):e,n=this.render();return this.node=n,t.appendChild(n),n},t.prototype.render=function(){var e=this.stringify();return p(s(e)).childNodes[0]},t.prototype.unmount=function(){this.node.parentNode.removeChild(this.node)},Object.defineProperties(t.prototype,n),t}(d),v={autoConfigure:!0,mountTo:"body",syncUrlsWithBaseTag:!1,listenLocationChangeEvent:!0,locationChangeEvent:"locationChange",locationChangeAngularEmitter:!1,usagesToUpdate:"use[*|href]",moveGradientsOutsideSymbol:!1},g=function(e){return Array.prototype.slice.call(e,0)},m={isChrome:function(){return/chrome/i.test(navigator.userAgent)},isFirefox:function(){return/firefox/i.test(navigator.userAgent)},isIE:function(){return/msie/i.test(navigator.userAgent)||/trident/i.test(navigator.userAgent)},isEdge:function(){return/edge/i.test(navigator.userAgent)}},y=function(e){var t=[];return g(e.querySelectorAll("style")).forEach((function(e){e.textContent+="",t.push(e)})),t},b=function(e){return(e||window.location.href).split("#")[0]},w=function(e){angular.module("ng").run(["$rootScope",function(t){t.$on("$locationChangeSuccess",(function(t,n,r){!function(e,t){var n=document.createEvent("CustomEvent");n.initCustomEvent(e,!1,!1,t),window.dispatchEvent(n)}(e,{oldUrl:r,newUrl:n})}))}])},k=function(e,t){return void 0===t&&(t="linearGradient, radialGradient, pattern"),g(e.querySelectorAll("symbol")).forEach((function(e){g(e.querySelectorAll(t)).forEach((function(t){e.parentNode.insertBefore(t,e)}))})),e},x=r.xlink.uri,j="xlink:href",M=/[{}|\\\^\[\]`"<>]/g;function _(e){return e.replace(M,(function(e){return"%"+e[0].charCodeAt(0).toString(16).toUpperCase()}))}var C,q=["clipPath","colorProfile","src","cursor","fill","filter","marker","markerStart","markerMid","markerEnd","mask","stroke","style"],S=q.map((function(e){return"["+e+"]"})).join(","),O=function(e,t,n,r){var i=_(n),o=_(r);(function(e,t){return g(e).reduce((function(e,n){if(!n.attributes)return e;var r=g(n.attributes),i=t?r.filter(t):r;return e.concat(i)}),[])})(e.querySelectorAll(S),(function(e){var t=e.localName,n=e.value;return-1!==q.indexOf(t)&&-1!==n.indexOf("url("+i)})).forEach((function(e){return e.value=e.value.replace(new RegExp(i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"g"),o)})),function(e,t,n){g(e).forEach((function(e){var r=e.getAttribute(j);if(r&&0===r.indexOf(t)){var i=r.replace(t,n);e.setAttributeNS(x,j,i)}}))}(t,i,o)},T={MOUNT:"mount",SYMBOL_MOUNT:"symbol_mount"},E=function(e){function t(t){var r=this;void 0===t&&(t={}),e.call(this,n(v,t));var i,o=(i=i||Object.create(null),{on:function(e,t){(i[e]||(i[e]=[])).push(t)},off:function(e,t){i[e]&&i[e].splice(i[e].indexOf(t)>>>0,1)},emit:function(e,t){(i[e]||[]).map((function(e){e(t)})),(i["*"]||[]).map((function(n){n(e,t)}))}});this._emitter=o,this.node=null;var a=this.config;if(a.autoConfigure&&this._autoConfigure(t),a.syncUrlsWithBaseTag){var c=document.getElementsByTagName("base")[0].getAttribute("href");o.on(T.MOUNT,(function(){return r.updateUrls("#",c)}))}var s=this._handleLocationChange.bind(this);this._handleLocationChange=s,a.listenLocationChangeEvent&&window.addEventListener(a.locationChangeEvent,s),a.locationChangeAngularEmitter&&w(a.locationChangeEvent),o.on(T.MOUNT,(function(e){a.moveGradientsOutsideSymbol&&k(e)})),o.on(T.SYMBOL_MOUNT,(function(e){a.moveGradientsOutsideSymbol&&k(e.parentNode),(m.isIE()||m.isEdge())&&y(e)}))}e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t;var r={isMounted:{}};return r.isMounted.get=function(){return!!this.node},t.prototype._autoConfigure=function(e){var t=this.config;"undefined"===typeof e.syncUrlsWithBaseTag&&(t.syncUrlsWithBaseTag="undefined"!==typeof document.getElementsByTagName("base")[0]),"undefined"===typeof e.locationChangeAngularEmitter&&(t.locationChangeAngularEmitter="angular"in window),"undefined"===typeof e.moveGradientsOutsideSymbol&&(t.moveGradientsOutsideSymbol=m.isFirefox())},t.prototype._handleLocationChange=function(e){var t=e.detail,n=t.oldUrl,r=t.newUrl;this.updateUrls(n,r)},t.prototype.add=function(t){var n=e.prototype.add.call(this,t);return this.isMounted&&n&&(t.mount(this.node),this._emitter.emit(T.SYMBOL_MOUNT,t.node)),n},t.prototype.attach=function(e){var t=this,n=this;if(n.isMounted)return n.node;var r="string"===typeof e?document.querySelector(e):e;return n.node=r,this.symbols.forEach((function(e){e.mount(n.node),t._emitter.emit(T.SYMBOL_MOUNT,e.node)})),g(r.querySelectorAll("symbol")).forEach((function(e){var t=z.createFromExistingNode(e);t.node=e,n.add(t)})),this._emitter.emit(T.MOUNT,r),r},t.prototype.destroy=function(){var e=this.config,t=this.symbols,n=this._emitter;t.forEach((function(e){return e.destroy()})),n.off("*"),window.removeEventListener(e.locationChangeEvent,this._handleLocationChange),this.isMounted&&this.unmount()},t.prototype.mount=function(e,t){if(void 0===e&&(e=this.config.mountTo),void 0===t&&(t=!1),this.isMounted)return this.node;var n="string"===typeof e?document.querySelector(e):e,r=this.render();return this.node=r,t&&n.childNodes[0]?n.insertBefore(r,n.childNodes[0]):n.appendChild(r),this._emitter.emit(T.MOUNT,r),r},t.prototype.render=function(){return p(this.stringify())},t.prototype.unmount=function(){this.node.parentNode.removeChild(this.node)},t.prototype.updateUrls=function(e,t){if(!this.isMounted)return!1;var n=document.querySelectorAll(this.config.usagesToUpdate);return O(this.node,n,b(e)+"#",b(t)+"#"),!0},Object.defineProperties(t.prototype,r),t}(h),A=e((function(e){e.exports=function(){var e,t=[],n=document,r=n.documentElement.doScroll,i=(r?/^loaded|^c/:/^loaded|^i|^c/).test(n.readyState);return i||n.addEventListener("DOMContentLoaded",e=function(){for(n.removeEventListener("DOMContentLoaded",e),i=1;e=t.shift();)e()}),function(e){i?setTimeout(e,0):t.push(e)}}()}));window.__SVG_SPRITE__?C=window.__SVG_SPRITE__:(C=new E({attrs:{id:"__SVG_SPRITE_NODE__"}}),window.__SVG_SPRITE__=C);var H=function(){var e=document.getElementById("__SVG_SPRITE_NODE__");e?C.attach(e):C.mount(document.body,!0)};return document.body?H():A(H),C},e.exports=n()}).call(this,n(91))},function(e,t,n){"use strict";var r={};n.r(r),n.d(r,"SIZE_SUB_UNIT",(function(){return d})),n.d(r,"SIZE_UNIT",(function(){return p})),n.d(r,"GUTTER_HEIGHT",(function(){return z}));var i={};n.r(i),n.d(i,"findFilterValues",(function(){return _y})),n.d(i,"removeSingleFilterValue",(function(){return Cy})),n.d(i,"markSelectedFacetValuesFromFilters",(function(){return qy})),n.d(i,"doFilterValuesMatch",(function(){return Sy})),n.d(i,"mergeFilters",(function(){return Oy})),n.d(i,"isFilterValueRange",(function(){return Ty}));var o={};n.r(o),n.d(o,"addFilter",(function(){return Cb})),n.d(o,"trackAutocompleteClickThrough",(function(){return qb})),n.d(o,"clearFilters",(function(){return Sb})),n.d(o,"removeFilter",(function(){return Ob})),n.d(o,"reset",(function(){return Tb})),n.d(o,"setCurrent",(function(){return Eb})),n.d(o,"setFilter",(function(){return Ab})),n.d(o,"setResultsPerPage",(function(){return Hb})),n.d(o,"setSearchTerm",(function(){return Lb})),n.d(o,"setSort",(function(){return Db})),n.d(o,"trackClickThrough",(function(){return Pb})),n.d(o,"a11yNotify",(function(){return Fb}));var a=n(0),c=n.n(a),s=n(4),l={transparent:{full:"rgba(255, 255, 255, 0.0)",semi:"rgba(255, 255, 255, 0.5)",popover:"rgba(18, 36, 50, 0.9)"},green:{chateau:"#42B861",netdata:"#00AB44",deyork:"#68C47D",vista:"#96D4A2",fringyFlower:"#BFE5C6",frostee:"#E5F5E8",limeGreen:"#48E499"},red:{pomegranate:"#FF4136",carnation:"#F95251",apricot:"#ED7374",wewak:"#F59B9B",pastelpink:"#FFCED3",lavender:"#FFEBEF"},yellow:{amber:"#FFC300",sunglow:"#FFCC26",seaBuckthorn:"#F9A825",mustard:"#FFD74F",salomie:"#FFE182",buttermilk:"#FFEDB3",ginfizz:"#FFF8E1"},neutral:{white:"#FFFFFF",black:"#000000",limedSpruce:"#35414A",regentgrey:"#8F9EAA",blackhaze:"#F7F8F8",iron:"#CFD5DA",porcelain:"#ECEEEF",bluebayoux:"#536775",shark:"#1C1E22",tuna:"#383B40",outerSpace:"#2B3136"},purple:{mauve:"#DB94F4",daisy:"#563D7C",lilac:"#B596F8"},blue:{aquamarine:"#19C89E",indigo:"#5790FF",cyan:"#00BAE2"}},u=function(){return(u=Object.assign||function(e){for(var t,n=1,r=arguments.length;n>16&255)+", "+(o>>8&255)+", "+(255&o)+", "+t+")"}},Z=function(e){return void 0===e&&(e=1),function(t){return(W(t)||0)*e+"px"}},$=function(e,t){return void 0===e&&(e="border"),void 0===t&&(t="disabled"),function(n){var r=n.theme,i=n.success,o=n.error,a=n.disabled;return i?G(["success"])({theme:r}):o?G(["error"])({theme:r}):a?G([t])({theme:r}):G([e])({theme:r})}},X=function(e,t){return"number"===typeof t?0===(n=e.constants.SIZE_SUB_UNIT*t)?"0":n+"px":"auto";var n},K=function(e,t){return t.map((function(t){return X(e,t)})).join(" ")},Q=function(e){var t=e.theme,n=e.margin;return n?Array.isArray(n)&&n.length>=1&&n.length<=4?"margin: "+K(t,n)+";":(console.error("Please provide an array (max 4 elements) for `margin` style helper."),""):""},J=function(e){var t=e.theme,n=e.padding;return n?Array.isArray(n)&&n.length>=1&&n.length<=4?"padding: "+K(t,n)+";":(console.error("Please provide an array (max 4 elements) for `padding` style helper."),""):""},ee={end:"flex-end",start:"flex-start",center:"center",stretch:"stretch"},te=function(e){var t=e.alignSelf;return t in ee&&"align-self: "+ee[t]+";"},ne={none:"none",capitalize:"capitalize",uppercase:"uppercase",lowercase:"lowercase",firstLetter:"firstLetter",fullWidth:"full-width"},re=function(e){var t=(void 0===e?{}:e).textTransform,n=void 0===t?"none":t;return n===ne.firstLetter?"text-transform: lowercase;\n &::first-letter {\n text-transform: uppercase;\n }\n":n in ne?"text-transform: "+ne[n]+";":"text-transform: "+ne.none+";"},ie=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},oe=function(){return(oe=Object.assign||function(e){for(var t,n=1,r=arguments.length;n span {\n ",";\n margin-left: ",";\n }\n\n &:hover {\n border-color: ",";\n background-color: ",";\n color: ",";\n text-decoration: none;\n\n .button-icon {\n fill: ",";\n }\n }\n\n &:active {\n ","\n }\n ","\n\n &:focus {\n outline: none;\n }\n\n .button-icon {\n height: ",";\n width: ",";\n fill: ",";\n }\n\n .ntd-spinner {\n fill: none;\n stroke-width: 17px;\n stroke-dasharray: 100;\n stroke-dashoffset: 100;\n animation: ntd-draw 1s linear infinite;\n stroke: ",";\n width: 24px;\n }\n\n .path {\n stroke: ",";\n }\n\n @keyframes ntd-draw {\n to {\n stroke-dashoffset: 0;\n }\n }\n }\n"],["\n && {\n display: flex;\n justify-content: center;\n align-items: center;\n position: relative;\n ",";\n\n width: ",";\n height: ",";\n\n font-weight: 500;\n font-size: ",";\n line-height: ",";\n white-space: nowrap;\n word-break: keep-all;\n\n cursor: pointer;\n opacity: ",";\n pointer-events: ",";\n\n ","\n ","\n transition: all 150ms;\n\n background-color: ",";\n color: ",";\n\n border-width: 1px;\n border-style: solid;\n border-color: ",";\n border-radius: 4px;\n box-sizing: border-box;\n\n text-decoration: none;\n & > span {\n ",";\n margin-left: ",";\n }\n\n &:hover {\n border-color: ",";\n background-color: ",";\n color: ",";\n text-decoration: none;\n\n .button-icon {\n fill: ",";\n }\n }\n\n &:active {\n ","\n }\n ","\n\n &:focus {\n outline: none;\n }\n\n .button-icon {\n height: ",";\n width: ",";\n fill: ",";\n }\n\n .ntd-spinner {\n fill: none;\n stroke-width: 17px;\n stroke-dasharray: 100;\n stroke-dashoffset: 100;\n animation: ntd-draw 1s linear infinite;\n stroke: ",";\n width: 24px;\n }\n\n .path {\n stroke: ",";\n }\n\n @keyframes ntd-draw {\n to {\n stroke-dashoffset: 0;\n }\n }\n }\n"])),te,(function(e){return e.width?e.width:e.hasLabel?Z(16):Z(e.tiny?2.75:e.small?3:4)}),(function(e){return e.hasLabel?Z(e.small?4:5):Z(e.tiny?2.75:e.small?3:4)}),(function(e){var t=e.small;return e.tiny?"10px":t?"12px":"14px"}),Z(2),(function(e){var t=e.disabled;return e.neutral?1:t?.4:1}),(function(e){return e.disabled?"none":"auto"}),Q,J,(function(e){return e.colors.bg(e)}),(function(e){return e.colors.color(e)}),(function(e){return e.colors.border(e)}),re,(function(e){return e.hasIcon?Z(1.5):"0px"}),(function(e){return e.colors.borderHover(e)}),(function(e){return e.colors.bgHover(e)}),(function(e){return e.colors.colorHover(e)}),(function(e){return e.colors.colorHover(e)}),ce,(function(e){return e.active&&"\n "+ce+"\n "}),Z(2),Z(2),(function(e){return e.colors.color(e)}),(function(e){return e.colors.color(e)}),(function(e){return e.colors.color(e)})),ge=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},me=s.d.svg.withConfig({displayName:"loader__StyledSvg",componentId:"sc-1mq98qd-0"})(A||(A=ge(["\n fill: none;\n stroke-width: 17px;\n stroke-dasharray: 100;\n stroke-dashoffset: 100;\n animation: ntd-draw 1s linear infinite;\n stroke: ",";\n width: 24px;\n .path {\n stroke: ",";\n }\n\n @keyframes ntd-draw {\n to {\n stroke-dashoffset: 0;\n }\n }\n"],["\n fill: none;\n stroke-width: 17px;\n stroke-dasharray: 100;\n stroke-dashoffset: 100;\n animation: ntd-draw 1s linear infinite;\n stroke: ",";\n width: 24px;\n .path {\n stroke: ",";\n }\n\n @keyframes ntd-draw {\n to {\n stroke-dashoffset: 0;\n }\n }\n"])),G("bright"),G("bright")),ye=function(e){var t=e.className;return c.a.createElement(me,{className:t,viewBox:"0 0 21 17",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},c.a.createElement("g",{className:"path",stroke:"none",strokeWidth:"1",fill:"none",fillRule:"evenodd"},c.a.createElement("path",{d:"M2,1 C8.25086152,1 11.9367136,1 13.0575562,1 C14.73882,1 19.6834591,2 19.9614325,7.72050108 C20.239406,13.4410022 15.7459591,15.1224845 13.6463763,15.1224845 C12.2466545,15.1224845 10.0279195,15.1224845 6.9901715,15.1224845 L2,1 Z",id:"Path-2",strokeWidth:"2"})))},be=n(1),we=n.n(be),ke=n(2),xe=n.n(ke),je=new we.a({id:"add_node",use:"add_node-usage",viewBox:"0 0 18 18",content:''}),Me=(xe.a.add(je),je),_e=new we.a({id:"add_user",use:"add_user-usage",viewBox:"0 0 15 16",content:''}),Ce=(xe.a.add(_e),_e),qe=new we.a({id:"aggregation_avg",use:"aggregation_avg-usage",viewBox:"0 0 16 12",content:''}),Se=(xe.a.add(qe),qe),Oe=new we.a({id:"aggregation_max",use:"aggregation_max-usage",viewBox:"0 0 15 16",content:''}),Te=(xe.a.add(Oe),Oe),Ee=new we.a({id:"aggregation_med",use:"aggregation_med-usage",viewBox:"0 0 14 14",content:''}),Ae=(xe.a.add(Ee),Ee),He=new we.a({id:"aggregation_min",use:"aggregation_min-usage",viewBox:"0 0 15 16",content:''}),Le=(xe.a.add(He),He),De=new we.a({id:"aggregation_sum",use:"aggregation_sum-usage",viewBox:"0 0 12 14",content:''}),Pe=(xe.a.add(De),De),Ve=new we.a({id:"aggregation_sum_abs",use:"aggregation_sum_abs-usage",viewBox:"0 0 14 14",content:''}),Ne=(xe.a.add(Ve),Ve),Ie=new we.a({id:"alarm",use:"alarm-usage",viewBox:"0 0 18 21",content:''}),Re=(xe.a.add(Ie),Ie),Be=new we.a({id:"alarm_c",use:"alarm_c-usage",viewBox:"0 0 24 24",content:''}),Fe=(xe.a.add(Be),Be),Ue=new we.a({id:"alarm_cw",use:"alarm_cw-usage",viewBox:"0 0 24 24",content:''}),We=(xe.a.add(Ue),Ue),Ge=new we.a({id:"alarm_w",use:"alarm_w-usage",viewBox:"0 0 24 24",content:''}),Ye=(xe.a.add(Ge),Ge),Ze=new we.a({id:"alarm_bell",use:"alarm_bell-usage",viewBox:"0 0 12 14",content:''}),$e=(xe.a.add(Ze),Ze),Xe=new we.a({id:"alarms_new",use:"alarms_new-usage",viewBox:"0 0 22 20",content:''}),Ke=(xe.a.add(Xe),Xe),Qe=new we.a({id:"anomalies_brain",use:"anomalies_brain-usage",viewBox:"0 0 18 18",content:''}),Je=(xe.a.add(Qe),Qe),et=new we.a({id:"anomalies_lens",use:"anomalies_lens-usage",viewBox:"0 0 18 18",content:''}),tt=(xe.a.add(et),et),nt=new we.a({id:"applications_hollow",use:"applications_hollow-usage",viewBox:"0 0 18 18",content:''}),rt=(xe.a.add(nt),nt),it=new we.a({id:"around_clock",use:"around_clock-usage",viewBox:"0 0 16 16",content:''}),ot=(xe.a.add(it),it),at=new we.a({id:"arrow_down",use:"arrow_down-usage",viewBox:"0 0 16 16",content:''}),ct=(xe.a.add(at),at),st=new we.a({id:"arrow_w_line_left",use:"arrow_w_line_left-usage",viewBox:"0 0 26 24",content:''}),lt=(xe.a.add(st),st),ut=new we.a({id:"arrow_w_line_right",use:"arrow_w_line_right-usage",viewBox:"0 0 24 13",content:''}),ft=(xe.a.add(ut),ut),ht=new we.a({id:"arrow_left",use:"arrow_left-usage",viewBox:"0 0 24 24",content:''}),dt=(xe.a.add(ht),ht),pt=new we.a({id:"arrow-s_down",use:"arrow-s_down-usage",viewBox:"0 0 8 9",content:''}),zt=(xe.a.add(pt),pt),vt=new we.a({id:"arrow-s_left",use:"arrow-s_left-usage",viewBox:"0 0 8 9",content:''}),gt=(xe.a.add(vt),vt),mt=new we.a({id:"arrows_vertical",use:"arrows_vertical-usage",viewBox:"0 0 6 10",content:''}),yt=(xe.a.add(mt),mt),bt=new we.a({id:"bookmark",use:"bookmark-usage",viewBox:"0 0 12 14",content:''}),wt=(xe.a.add(bt),bt),kt=new we.a({id:"bullet_one",use:"bullet_one-usage",viewBox:"0 0 10 10",content:''}),xt=(xe.a.add(kt),kt),jt=new we.a({id:"bullet_three",use:"bullet_three-usage",viewBox:"0 0 10 10",content:''}),Mt=(xe.a.add(jt),jt),_t=new we.a({id:"bullet_two",use:"bullet_two-usage",viewBox:"0 0 10 10",content:''}),Ct=(xe.a.add(_t),_t),qt=new we.a({id:"calendar_full",use:"calendar_full-usage",viewBox:"0 0 18 18",content:''}),St=(xe.a.add(qt),qt),Ot=new we.a({id:"calendar_full_press",use:"calendar_full_press-usage",viewBox:"0 0 18 18",content:''}),Tt=(xe.a.add(Ot),Ot),Et=new we.a({id:"chart_added",use:"chart_added-usage",viewBox:"0 0 17 17",content:''}),At=(xe.a.add(Et),Et),Ht=new we.a({id:"charts",use:"charts-usage",viewBox:"0 0 20 20",content:''}),Lt=(xe.a.add(Ht),Ht),Dt=new we.a({id:"check",use:"check-usage",viewBox:"0 0 24 24",content:''}),Pt=(xe.a.add(Dt),Dt),Vt=new we.a({id:"checkmark_partial_s",use:"checkmark_partial_s-usage",viewBox:"0 0 16 16",content:''}),Nt=(xe.a.add(Vt),Vt),It=new we.a({id:"checkmark_s",use:"checkmark_s-usage",viewBox:"0 0 16 16",content:''}),Rt=(xe.a.add(It),It),Bt=new we.a({id:"checkmark",use:"checkmark-usage",viewBox:"0 0 168 168",content:''}),Ft=(xe.a.add(Bt),Bt),Ut=new we.a({id:"chevron_double",use:"chevron_double-usage",viewBox:"0 0 6 10",content:''}),Wt=(xe.a.add(Ut),Ut),Gt=new we.a({id:"chevron_down",use:"chevron_down-usage",viewBox:"0 0 12 12",content:''}),Yt=(xe.a.add(Gt),Gt),Zt=new we.a({id:"chevron_left",use:"chevron_left-usage",viewBox:"0 0 24 24",content:''}),$t=(xe.a.add(Zt),Zt),Xt=new we.a({id:"chevron_right_s",use:"chevron_right_s-usage",viewBox:"0 0 5 6",content:''}),Kt=(xe.a.add(Xt),Xt),Qt=new we.a({id:"class_error",use:"class_error-usage",viewBox:"0 0 21 22",content:''}),Jt=(xe.a.add(Qt),Qt),en=new we.a({id:"class_latency",use:"class_latency-usage",viewBox:"0 0 21 20",content:''}),tn=(xe.a.add(en),en),nn=new we.a({id:"class_utilization",use:"class_utilization-usage",viewBox:"0 0 25 19",content:''}),rn=(xe.a.add(nn),nn),on=new we.a({id:"class_workload",use:"class_workload-usage",viewBox:"0 0 22 21",content:''}),an=(xe.a.add(on),on),cn=new we.a({id:"clock_hollow",use:"clock_hollow-usage",viewBox:"0 0 24 24",content:''}),sn=(xe.a.add(cn),cn),ln=new we.a({id:"clock_5_min",use:"clock_5_min-usage",viewBox:"0 0 18 18",content:''}),un=(xe.a.add(ln),ln),fn=new we.a({id:"clock_5_min_press",use:"clock_5_min_press-usage",viewBox:"0 0 18 18",content:''}),hn=(xe.a.add(fn),fn),dn=new we.a({id:"close_circle",use:"close_circle-usage",viewBox:"0 0 10 10",content:''}),pn=(xe.a.add(dn),dn),zn=new we.a({id:"cluster",use:"cluster-usage",viewBox:"0 0 22 22",content:''}),vn=(xe.a.add(zn),zn),gn=new we.a({id:"cluster_spaces",use:"cluster_spaces-usage",viewBox:"0 0 22 22",content:''}),mn=(xe.a.add(gn),gn),yn=new we.a({id:"code",use:"code-usage",viewBox:"0 0 16 16",content:''}),bn=(xe.a.add(yn),yn),wn=new we.a({id:"collapse",use:"collapse-usage",viewBox:"0 0 16 2",content:''}),kn=(xe.a.add(wn),wn),xn=new we.a({id:"community",use:"community-usage",viewBox:"0 0 18 18",content:''}),jn=(xe.a.add(xn),xn),Mn=new we.a({id:"connectivity_status_live",use:"connectivity_status_live-usage",viewBox:"0 0 18 18",content:''}),_n=(xe.a.add(Mn),Mn),Cn=new we.a({id:"connectivity_status_offline",use:"connectivity_status_offline-usage",viewBox:"0 0 18 18",content:''}),qn=(xe.a.add(Cn),Cn),Sn=new we.a({id:"connectivity_status_stale",use:"connectivity_status_stale-usage",viewBox:"0 0 18 18",content:''}),On=(xe.a.add(Sn),Sn),Tn=new we.a({id:"container",use:"container-usage",viewBox:"0 0 22 22",content:''}),En=(xe.a.add(Tn),Tn),An=new we.a({id:"controller_kind",use:"controller_kind-usage",viewBox:"0 0 22 22",content:''}),Hn=(xe.a.add(An),An),Ln=new we.a({id:"controller_name",use:"controller_name-usage",viewBox:"0 0 22 22",content:''}),Dn=(xe.a.add(Ln),Ln),Pn=new we.a({id:"copy",use:"copy-usage",viewBox:"0 0 14 14",content:''}),Vn=(xe.a.add(Pn),Pn),Nn=new we.a({id:"correlation",use:"correlation-usage",viewBox:"0 0 28 28",content:''}),In=(xe.a.add(Nn),Nn),Rn=new we.a({id:"correlation_inv",use:"correlation_inv-usage",viewBox:"0 0 24 24",content:''}),Bn=(xe.a.add(Rn),Rn),Fn=new we.a({id:"cpu",use:"cpu-usage",viewBox:"0 0 18 18",content:''}),Un=(xe.a.add(Fn),Fn),Wn=new we.a({id:"cross_s",use:"cross_s-usage",viewBox:"0 0 16 16",content:''}),Gn=(xe.a.add(Wn),Wn),Yn=new we.a({id:"data_retention",use:"data_retention-usage",viewBox:"0 0 18 18",content:''}),Zn=(xe.a.add(Yn),Yn),$n=new we.a({id:"database",use:"database-usage",viewBox:"0 0 24 24",content:''}),Xn=(xe.a.add($n),$n),Kn=new we.a({id:"dashboard",use:"dashboard-usage",viewBox:"0 0 22 18",content:''}),Qn=(xe.a.add(Kn),Kn),Jn=new we.a({id:"dashboard_add",use:"dashboard_add-usage",viewBox:"0 0 16 16",content:''}),er=(xe.a.add(Jn),Jn),tr=new we.a({id:"dashboards",use:"dashboards-usage",viewBox:"0 0 16 10",content:''}),nr=(xe.a.add(tr),tr),rr=new we.a({id:"disk",use:"disk-usage",viewBox:"0 0 18 18",content:''}),ir=(xe.a.add(rr),rr),or=new we.a({id:"documentation",use:"documentation-usage",viewBox:"0 0 24 24",content:''}),ar=(xe.a.add(or),or),cr=new we.a({id:"dot",use:"dot-usage",viewBox:"0 0 10 10",content:''}),sr=(xe.a.add(cr),cr),lr=new we.a({id:"dots_2x3",use:"dots_2x3-usage",viewBox:"0 0 6 10",content:''}),ur=(xe.a.add(lr),lr),fr=new we.a({id:"download",use:"download-usage",viewBox:"0 0 20 20",content:''}),hr=(xe.a.add(fr),fr),dr=new we.a({id:"error",use:"error-usage",viewBox:"0 0 24 24",content:''}),pr=(xe.a.add(dr),dr),zr=new we.a({id:"exclamation",use:"exclamation-usage",viewBox:"0 0 24 24",content:''}),vr=(xe.a.add(zr),zr),gr=new we.a({id:"expand",use:"expand-usage",viewBox:"0 0 24 24",content:''}),mr=(xe.a.add(gr),gr),yr=new we.a({id:"filterList",use:"filterList-usage",viewBox:"0 0 18 18",content:''}),br=(xe.a.add(yr),yr),wr=new we.a({id:"force_play",use:"force_play-usage",viewBox:"0 0 18 18",content:''}),kr=(xe.a.add(wr),wr),xr=new we.a({id:"force_play_outline",use:"force_play_outline-usage",viewBox:"0 0 18 18",content:''}),jr=(xe.a.add(xr),xr),Mr=new we.a({id:"gear",use:"gear-usage",viewBox:"0 0 20 20",content:''}),_r=(xe.a.add(Mr),Mr),Cr=new we.a({id:"github",use:"github-usage",viewBox:"0 0 24 24",content:''}),qr=(xe.a.add(Cr),Cr),Sr=new we.a({id:"go_to_node",use:"go_to_node-usage",viewBox:"0 0 18 18",content:''}),Or=(xe.a.add(Sr),Sr),Tr=new we.a({id:"google",use:"google-usage",viewBox:"0 0 24 24",content:''}),Er=(xe.a.add(Tr),Tr),Ar=new we.a({id:"group_by",use:"group_by-usage",viewBox:"0 0 18 18",content:''}),Hr=(xe.a.add(Ar),Ar),Lr=new we.a({id:"hamburger",use:"hamburger-usage",viewBox:"0 0 24 24",content:''}),Dr=(xe.a.add(Lr),Lr),Pr=new we.a({id:"help",use:"help-usage",viewBox:"0 0 20 21",content:''}),Vr=(xe.a.add(Pr),Pr),Nr=new we.a({id:"hide",use:"hide-usage",viewBox:"0 0 18 18",content:''}),Ir=(xe.a.add(Nr),Nr),Rr=new we.a({id:"highlight_area",use:"highlight_area-usage",viewBox:"0 0 16 16",content:''}),Br=(xe.a.add(Rr),Rr),Fr=new we.a({id:"holder",use:"holder-usage",viewBox:"0 0 24 24",content:''}),Ur=(xe.a.add(Fr),Fr),Wr=new we.a({id:"incident_manager",use:"incident_manager-usage",viewBox:"0 0 18 18",content:''}),Gr=(xe.a.add(Wr),Wr),Yr=new we.a({id:"information",use:"information-usage",viewBox:"0 0 18 18",content:''}),Zr=(xe.a.add(Yr),Yr),$r=new we.a({id:"information_press",use:"information_press-usage",viewBox:"0 0 18 18",content:''}),Xr=(xe.a.add($r),$r),Kr=new we.a({id:"insights",use:"insights-usage",viewBox:"0 0 18 18",content:''}),Qr=(xe.a.add(Kr),Kr),Jr=new we.a({id:"integrations",use:"integrations-usage",viewBox:"0 0 16 16",content:''}),ei=(xe.a.add(Jr),Jr),ti=new we.a({id:"ipNetworking",use:"ipNetworking-usage",viewBox:"0 0 16 16",content:''}),ni=(xe.a.add(ti),ti),ri=new we.a({id:"ipNetworkingPress",use:"ipNetworkingPress-usage",viewBox:"0 0 16 16",content:''}),ii=(xe.a.add(ri),ri),oi=new we.a({id:"last_week",use:"last_week-usage",viewBox:"0 0 18 18",content:''}),ai=(xe.a.add(oi),oi),ci=new we.a({id:"line_chart",use:"line_chart-usage",viewBox:"0 0 15 15",content:''}),si=(xe.a.add(ci),ci),li=new we.a({id:"logo_s",use:"logo_s-usage",viewBox:"0 0 14 13",content:''}),ui=(xe.a.add(li),li),fi=new we.a({id:"loading",use:"loading-usage",viewBox:"0 0 24 24",content:''}),hi=(xe.a.add(fi),fi),di=new we.a({id:"magnify",use:"magnify-usage",viewBox:"0 0 24 24",content:''}),pi=(xe.a.add(di),di),zi=new we.a({id:"metrics",use:"metrics-usage",viewBox:"0 0 24 24",content:''}),vi=(xe.a.add(zi),zi),gi=new we.a({id:"metrics_explorer",use:"metrics_explorer-usage",viewBox:"0 0 18 18",content:''}),mi=(xe.a.add(gi),gi),yi=new we.a({id:"monitoring",use:"monitoring-usage",viewBox:"0 0 20 20",content:''}),bi=(xe.a.add(yi),yi),wi=new we.a({id:"more",use:"more-usage",viewBox:"0 0 18 4",content:''}),ki=(xe.a.add(wi),wi),xi=new we.a({id:"nav_left",use:"nav_left-usage",viewBox:"0 0 8 10",content:''}),ji=(xe.a.add(xi),xi),Mi=new we.a({id:"nav_right",use:"nav_right-usage",viewBox:"0 0 8 10",content:''}),_i=(xe.a.add(Mi),Mi),Ci=new we.a({id:"nav_arrow_goto",use:"nav_arrow_goto-usage",viewBox:"0 0 10 10",content:''}),qi=(xe.a.add(Ci),Ci),Si=new we.a({id:"nav_dots",use:"nav_dots-usage",viewBox:"0 0 24 24",content:''}),Oi=(xe.a.add(Si),Si),Ti=new we.a({id:"netdata",use:"netdata-usage",viewBox:"0 0 24 24",content:''}),Ei=(xe.a.add(Ti),Ti),Ai=new we.a({id:"netdata-press",use:"netdata-press-usage",viewBox:"0 0 18 18",content:''}),Hi=(xe.a.add(Ai),Ai),Li=new we.a({id:"node",use:"node-usage",viewBox:"0 0 24 24",content:''}),Di=(xe.a.add(Li),Li),Pi=new we.a({id:"node_child",use:"node_child-usage",viewBox:"0 0 18 18",content:''}),Vi=(xe.a.add(Pi),Pi),Ni=new we.a({id:"node_default_l",use:"node_default_l-usage",viewBox:"0 0 40 40",content:''}),Ii=(xe.a.add(Ni),Ni),Ri=new we.a({id:"node_hollow",use:"node_hollow-usage",viewBox:"0 0 22 12",content:''}),Bi=(xe.a.add(Ri),Ri),Fi=new we.a({id:"node_import_export",use:"node_import_export-usage",viewBox:"0 0 24 24",content:''}),Ui=(xe.a.add(Fi),Fi),Wi=new we.a({id:"node_notification_l",use:"node_notification_l-usage",viewBox:"0 0 40 40",content:''}),Gi=(xe.a.add(Wi),Wi),Yi=new we.a({id:"node_parent",use:"node_parent-usage",viewBox:"0 0 18 18",content:''}),Zi=(xe.a.add(Yi),Yi),$i=new we.a({id:"node_selected_l",use:"node_selected_l-usage",viewBox:"0 0 40 40",content:''}),Xi=(xe.a.add($i),$i),Ki=new we.a({id:"nodes",use:"nodes-usage",viewBox:"0 0 16 16",content:''}),Qi=(xe.a.add(Ki),Ki),Ji=new we.a({id:"nodes_hollow",use:"nodes_hollow-usage",viewBox:"0 0 18 18",content:''}),eo=(xe.a.add(Ji),Ji),to=new we.a({id:"none_selected",use:"none_selected-usage",viewBox:"0 0 16 16",content:''}),no=(xe.a.add(to),to),ro=new we.a({id:"os",use:"os-usage",viewBox:"0 0 18 18",content:''}),io=(xe.a.add(ro),ro),oo=new we.a({id:"alpine_linux",use:"alpine_linux-usage",viewBox:"0 0 18 18",content:''}),ao=(xe.a.add(oo),oo),co=new we.a({id:"amazon_linux",use:"amazon_linux-usage",viewBox:"0 0 18 18",content:''}),so=(xe.a.add(co),co),lo=new we.a({id:"arch_linux",use:"arch_linux-usage",viewBox:"0 0 18 18",content:''}),uo=(xe.a.add(lo),lo),fo=new we.a({id:"celarOS",use:"celarOS-usage",viewBox:"0 0 18 18",content:''}),ho=(xe.a.add(fo),fo),po=new we.a({id:"centos",use:"centos-usage",viewBox:"0 0 18 18",content:''}),zo=(xe.a.add(po),po),vo=new we.a({id:"centos_color",use:"centos_color-usage",viewBox:"0 0 18 18",content:''}),go=(xe.a.add(vo),vo),mo=new we.a({id:"coreOS",use:"coreOS-usage",viewBox:"0 0 18 18",content:''}),yo=(xe.a.add(mo),mo),bo=new we.a({id:"debian",use:"debian-usage",viewBox:"0 0 18 18",content:''}),wo=(xe.a.add(bo),bo),ko=new we.a({id:"debian_color",use:"debian_color-usage",viewBox:"0 0 18 18",content:''}),xo=(xe.a.add(ko),ko),jo=new we.a({id:"fedora",use:"fedora-usage",viewBox:"0 0 18 18",content:''}),Mo=(xe.a.add(jo),jo),_o=new we.a({id:"freeBSD",use:"freeBSD-usage",viewBox:"0 0 18 18",content:''}),Co=(xe.a.add(_o),_o),qo=new we.a({id:"gentoo",use:"gentoo-usage",viewBox:"0 0 18 18",content:''}),So=(xe.a.add(qo),qo),Oo=new we.a({id:"linux",use:"linux-usage",viewBox:"0 0 18 18",content:''}),To=(xe.a.add(Oo),Oo),Eo=new we.a({id:"linux_color",use:"linux_color-usage",viewBox:"0 0 18 18",content:''}),Ao=(xe.a.add(Eo),Eo),Ho=new we.a({id:"macOSX",use:"macOSX-usage",viewBox:"0 0 18 18",content:''}),Lo=(xe.a.add(Ho),Ho),Do=new we.a({id:"oracle",use:"oracle-usage",viewBox:"0 0 18 18",content:''}),Po=(xe.a.add(Do),Do),Vo=new we.a({id:"oracle_color",use:"oracle_color-usage",viewBox:"0 0 18 18",content:''}),No=(xe.a.add(Vo),Vo),Io=new we.a({id:"os_press",use:"os_press-usage",viewBox:"0 0 18 18",content:''}),Ro=(xe.a.add(Io),Io),Bo=new we.a({id:"raspbian",use:"raspbian-usage",viewBox:"0 0 18 18",content:''}),Fo=(xe.a.add(Bo),Bo),Uo=new we.a({id:"red_hat",use:"red_hat-usage",viewBox:"0 0 18 18",content:''}),Wo=(xe.a.add(Uo),Uo),Go=new we.a({id:"suse_linux",use:"suse_linux-usage",viewBox:"0 0 18 18",content:''}),Yo=(xe.a.add(Go),Go),Zo=new we.a({id:"ubuntu",use:"ubuntu-usage",viewBox:"0 0 18 18",content:''}),$o=(xe.a.add(Zo),Zo),Xo=new we.a({id:"ubuntu_color",use:"ubuntu_color-usage",viewBox:"0 0 18 18",content:''}),Ko=(xe.a.add(Xo),Xo),Qo=new we.a({id:"notification",use:"notification-usage",viewBox:"0 0 40 24",content:''}),Jo=(xe.a.add(Qo),Qo),ea=new we.a({id:"padlock",use:"padlock-usage",viewBox:"0 0 18 18",content:''}),ta=(xe.a.add(ea),ea),na=new we.a({id:"pause_outline",use:"pause_outline-usage",viewBox:"0 0 18 18",content:''}),ra=(xe.a.add(na),na),ia=new we.a({id:"pause_solid",use:"pause_solid-usage",viewBox:"0 0 24 24",content:''}),oa=(xe.a.add(ia),ia),aa=new we.a({id:"pencil_outline",use:"pencil_outline-usage",viewBox:"0 0 14 14",content:''}),ca=(xe.a.add(aa),aa),sa=new we.a({id:"pencil_solid",use:"pencil_solid-usage",viewBox:"0 0 19 19",content:''}),la=(xe.a.add(sa),sa),ua=new we.a({id:"pie_chart_skeleton",use:"pie_chart_skeleton-usage",viewBox:"0 0 100 100",content:''}),fa=(xe.a.add(ua),ua),ha=new we.a({id:"pin_element",use:"pin_element-usage",viewBox:"0 0 14 14",content:''}),da=(xe.a.add(ha),ha),pa=new we.a({id:"play_outline",use:"play_outline-usage",viewBox:"0 0 18 18",content:''}),za=(xe.a.add(pa),pa),va=new we.a({id:"play_solid",use:"play_solid-usage",viewBox:"0 0 24 24",content:''}),ga=(xe.a.add(va),va),ma=new we.a({id:"plus",use:"plus-usage",viewBox:"0 0 24 24",content:''}),ya=(xe.a.add(ma),ma),ba=new we.a({id:"plus_mini_s",use:"plus_mini_s-usage",viewBox:"0 0 24 24",content:''}),wa=(xe.a.add(ba),ba),ka=new we.a({id:"pod",use:"pod-usage",viewBox:"0 0 22 22",content:''}),xa=(xe.a.add(ka),ka),ja=new we.a({id:"pricing",use:"pricing-usage",viewBox:"0 0 16 16",content:''}),Ma=(xe.a.add(ja),ja),_a=new we.a({id:"print",use:"print-usage",viewBox:"0 0 21 20",content:''}),Ca=(xe.a.add(_a),_a),qa=new we.a({id:"privacy",use:"privacy-usage",viewBox:"0 0 16 16",content:''}),Sa=(xe.a.add(qa),qa),Oa=new we.a({id:"question",use:"question-usage",viewBox:"0 0 20 20",content:''}),Ta=(xe.a.add(Oa),Oa),Ea=new we.a({id:"questionFilled",use:"questionFilled-usage",viewBox:"0 0 24 24",content:''}),Aa=(xe.a.add(Ea),Ea),Ha=new we.a({id:"ram",use:"ram-usage",viewBox:"0 0 18 18",content:''}),La=(xe.a.add(Ha),Ha),Da=new we.a({id:"refresh",use:"refresh-usage",viewBox:"0 0 18 19",content:''}),Pa=(xe.a.add(Da),Da),Va=new we.a({id:"reload",use:"reload-usage",viewBox:"0 0 24 24",content:''}),Na=(xe.a.add(Va),Va),Ia=new we.a({id:"remove_node",use:"remove_node-usage",viewBox:"0 0 18 18",content:''}),Ra=(xe.a.add(Ia),Ia),Ba=new we.a({id:"resize_handler",use:"resize_handler-usage",viewBox:"0 0 16 16",content:''}),Fa=(xe.a.add(Ba),Ba),Ua=new we.a({id:"room",use:"room-usage",viewBox:"0 0 24 24",content:''}),Wa=(xe.a.add(Ua),Ua),Ga=new we.a({id:"room_home",use:"room_home-usage",viewBox:"0 0 14 12",content:''}),Ya=(xe.a.add(Ga),Ga),Za=new we.a({id:"room_new",use:"room_new-usage",viewBox:"0 0 20 20",content:''}),$a=(xe.a.add(Za),Za),Xa=new we.a({id:"room_overview",use:"room_overview-usage",viewBox:"0 0 24 25",content:''}),Ka=(xe.a.add(Xa),Xa),Qa=new we.a({id:"sad",use:"sad-usage",viewBox:"0 0 24 24",content:''}),Ja=(xe.a.add(Qa),Qa),ec=new we.a({id:"save",use:"save-usage",viewBox:"0 0 14 14",content:''}),tc=(xe.a.add(ec),ec),nc=new we.a({id:"search",use:"search-usage",viewBox:"0 0 18 18",content:''}),rc=(xe.a.add(nc),nc),ic=new we.a({id:"search_s",use:"search_s-usage",viewBox:"0 0 14 14",content:''}),oc=(xe.a.add(ic),ic),ac=new we.a({id:"search_press",use:"search_press-usage",viewBox:"0 0 18 18",content:''}),cc=(xe.a.add(ac),ac),sc=new we.a({id:"apache",use:"apache-usage",viewBox:"0 0 18 18",content:''}),lc=(xe.a.add(sc),sc),uc=new we.a({id:"apache_tomcat",use:"apache_tomcat-usage",viewBox:"0 0 18 18",content:''}),fc=(xe.a.add(uc),uc),hc=new we.a({id:"beanstalk",use:"beanstalk-usage",viewBox:"0 0 18 18",content:''}),dc=(xe.a.add(hc),hc),pc=new we.a({id:"couchDB",use:"couchDB-usage",viewBox:"0 0 18 18",content:''}),zc=(xe.a.add(pc),pc),vc=new we.a({id:"database",use:"database-usage",viewBox:"0 0 18 18",content:''}),gc=(xe.a.add(vc),vc),mc=new we.a({id:"docker_hub",use:"docker_hub-usage",viewBox:"0 0 18 18",content:''}),yc=(xe.a.add(mc),mc),bc=new we.a({id:"docker_hub_press",use:"docker_hub_press-usage",viewBox:"0 0 18 18",content:''}),wc=(xe.a.add(bc),bc),kc=new we.a({id:"eBPF",use:"eBPF-usage",viewBox:"0 0 18 18",content:''}),xc=(xe.a.add(kc),kc),jc=new we.a({id:"elasticSearch",use:"elasticSearch-usage",viewBox:"0 0 18 18",content:''}),Mc=(xe.a.add(jc),jc),_c=new we.a({id:"freeNAS",use:"freeNAS-usage",viewBox:"0 0 18 18",content:''}),Cc=(xe.a.add(_c),_c),qc=new we.a({id:"haProxy",use:"haProxy-usage",viewBox:"0 0 18 18",content:''}),Sc=(xe.a.add(qc),qc),Oc=new we.a({id:"httpCheck",use:"httpCheck-usage",viewBox:"0 0 18 18",content:''}),Tc=(xe.a.add(Oc),Oc),Ec=new we.a({id:"iceCast",use:"iceCast-usage",viewBox:"0 0 18 18",content:''}),Ac=(xe.a.add(Ec),Ec),Hc=new we.a({id:"influxDB",use:"influxDB-usage",viewBox:"0 0 18 18",content:''}),Lc=(xe.a.add(Hc),Hc),Dc=new we.a({id:"ipfs",use:"ipfs-usage",viewBox:"0 0 18 18",content:''}),Pc=(xe.a.add(Dc),Dc),Vc=new we.a({id:"ipvs",use:"ipvs-usage",viewBox:"0 0 18 18",content:''}),Nc=(xe.a.add(Vc),Vc),Ic=new we.a({id:"kubermetes",use:"kubermetes-usage",viewBox:"0 0 18 18",content:''}),Rc=(xe.a.add(Ic),Ic),Bc=new we.a({id:"lighthttpd",use:"lighthttpd-usage",viewBox:"0 0 18 18",content:''}),Fc=(xe.a.add(Bc),Bc),Uc=new we.a({id:"lighthttpd2",use:"lighthttpd2-usage",viewBox:"0 0 18 18",content:''}),Wc=(xe.a.add(Uc),Uc),Gc=new we.a({id:"liteSpeed",use:"liteSpeed-usage",viewBox:"0 0 18 18",content:''}),Yc=(xe.a.add(Gc),Gc),Zc=new we.a({id:"lxc",use:"lxc-usage",viewBox:"0 0 18 18",content:''}),$c=(xe.a.add(Zc),Zc),Xc=new we.a({id:"mariaDB",use:"mariaDB-usage",viewBox:"0 0 18 18",content:''}),Kc=(xe.a.add(Xc),Xc),Qc=new we.a({id:"memCached",use:"memCached-usage",viewBox:"0 0 18 18",content:''}),Jc=(xe.a.add(Qc),Qc),es=new we.a({id:"mongoDB",use:"mongoDB-usage",viewBox:"0 0 18 18",content:''}),ts=(xe.a.add(es),es),ns=new we.a({id:"mySQL",use:"mySQL-usage",viewBox:"0 0 18 18",content:''}),rs=(xe.a.add(ns),ns),is=new we.a({id:"mySQL_press",use:"mySQL_press-usage",viewBox:"0 0 18 18",content:''}),os=(xe.a.add(is),is),as=new we.a({id:"nginx",use:"nginx-usage",viewBox:"0 0 18 18",content:''}),cs=(xe.a.add(as),as),ss=new we.a({id:"nginx_local",use:"nginx_local-usage",viewBox:"0 0 18 18",content:''}),ls=(xe.a.add(ss),ss),us=new we.a({id:"nginx_plus",use:"nginx_plus-usage",viewBox:"0 0 18 18",content:''}),fs=(xe.a.add(us),us),hs=new we.a({id:"ntpd",use:"ntpd-usage",viewBox:"0 0 18 18",content:''}),ds=(xe.a.add(hs),hs),ps=new we.a({id:"ntpd_press",use:"ntpd_press-usage",viewBox:"0 0 18 18",content:''}),zs=(xe.a.add(ps),ps),vs=new we.a({id:"openStack",use:"openStack-usage",viewBox:"0 0 18 18",content:''}),gs=(xe.a.add(vs),vs),ms=new we.a({id:"openWrt",use:"openWrt-usage",viewBox:"0 0 18 18",content:''}),ys=(xe.a.add(ms),ms),bs=new we.a({id:"pan",use:"pan-usage",viewBox:"0 0 18 18",content:''}),ws=(xe.a.add(bs),bs),ks=new we.a({id:"percona",use:"percona-usage",viewBox:"0 0 18 18",content:''}),xs=(xe.a.add(ks),ks),js=new we.a({id:"pfSense",use:"pfSense-usage",viewBox:"0 0 18 18",content:''}),Ms=(xe.a.add(js),js),_s=new we.a({id:"php_fpm",use:"php_fpm-usage",viewBox:"0 0 18 18",content:''}),Cs=(xe.a.add(_s),_s),qs=new we.a({id:"postgreSQL",use:"postgreSQL-usage",viewBox:"0 0 18 18",content:''}),Ss=(xe.a.add(qs),qs),Os=new we.a({id:"rabbitMQ",use:"rabbitMQ-usage",viewBox:"0 0 18 18",content:''}),Ts=(xe.a.add(Os),Os),Es=new we.a({id:"redis",use:"redis-usage",viewBox:"0 0 18 18",content:''}),As=(xe.a.add(Es),Es),Hs=new we.a({id:"rethinkDB",use:"rethinkDB-usage",viewBox:"0 0 18 18",content:''}),Ls=(xe.a.add(Hs),Hs),Ds=new we.a({id:"retroShare",use:"retroShare-usage",viewBox:"0 0 18 18",content:''}),Ps=(xe.a.add(Ds),Ds),Vs=new we.a({id:"services",use:"services-usage",viewBox:"0 0 18 18",content:''}),Ns=(xe.a.add(Vs),Vs),Is=new we.a({id:"selected_area",use:"selected_area-usage",viewBox:"0 0 18 18",content:''}),Rs=(xe.a.add(Is),Is),Bs=new we.a({id:"solr",use:"solr-usage",viewBox:"0 0 18 18",content:''}),Fs=(xe.a.add(Bs),Bs),Us=new we.a({id:"squid",use:"squid-usage",viewBox:"0 0 18 18",content:''}),Ws=(xe.a.add(Us),Us),Gs=new we.a({id:"summary_statistic",use:"summary_statistic-usage",viewBox:"0 0 18 18",content:''}),Ys=(xe.a.add(Gs),Gs),Zs=new we.a({id:"traefik",use:"traefik-usage",viewBox:"0 0 18 18",content:''}),$s=(xe.a.add(Zs),Zs),Xs=new we.a({id:"varnish",use:"varnish-usage",viewBox:"0 0 18 18",content:''}),Ks=(xe.a.add(Xs),Xs),Qs=new we.a({id:"webLog",use:"webLog-usage",viewBox:"0 0 18 18",content:''}),Js=(xe.a.add(Qs),Qs),el=new we.a({id:"webLog_nginx",use:"webLog_nginx-usage",viewBox:"0 0 18 18",content:''}),tl=(xe.a.add(el),el),nl=new we.a({id:"x509_check",use:"x509_check-usage",viewBox:"0 0 18 18",content:''}),rl=(xe.a.add(nl),nl),il=new we.a({id:"xen",use:"xen-usage",viewBox:"0 0 18 18",content:''}),ol=(xe.a.add(il),il),al=new we.a({id:"settings",use:"settings-usage",viewBox:"0 0 17 15",content:''}),cl=(xe.a.add(al),al),sl=new we.a({id:"settings_h",use:"settings_h-usage",viewBox:"0 0 14 14",content:''}),ll=(xe.a.add(sl),sl),ul=new we.a({id:"sorting_vertical",use:"sorting_vertical-usage",viewBox:"0 0 19 18",content:''}),fl=(xe.a.add(ul),ul),hl=new we.a({id:"space",use:"space-usage",viewBox:"0 0 24 24",content:''}),dl=(xe.a.add(hl),hl),pl=new we.a({id:"space_new",use:"space_new-usage",viewBox:"0 0 20 20",content:''}),zl=(xe.a.add(pl),pl),vl=new we.a({id:"switch_off",use:"switch_off-usage",viewBox:"0 0 14 15",content:''}),gl=(xe.a.add(vl),vl),ml=new we.a({id:"system_overview",use:"system_overview-usage",viewBox:"0 0 32 32",content:''}),yl=(xe.a.add(ml),ml),bl=new we.a({id:"text_add",use:"text_add-usage",viewBox:"0 0 16 16",content:''}),wl=(xe.a.add(bl),bl),kl=new we.a({id:"thumb_down",use:"thumb_down-usage",viewBox:"0 0 24 24",content:''}),xl=(xe.a.add(kl),kl),jl=new we.a({id:"thumb_up",use:"thumb_up-usage",viewBox:"0 0 24 24",content:''}),Ml=(xe.a.add(jl),jl),_l=new we.a({id:"tiny_buttons",use:"tiny_buttons-usage",viewBox:"0 0 22 22",content:''}),Cl=(xe.a.add(_l),_l),ql=new we.a({id:"training",use:"training-usage",viewBox:"0 0 16 16",content:''}),Sl=(xe.a.add(ql),ql),Ol=new we.a({id:"trashcan",use:"trashcan-usage",viewBox:"0 0 14 15",content:''}),Tl=(xe.a.add(Ol),Ol),El=new we.a({id:"triangle",use:"triangle-usage",viewBox:"0 0 24 24",content:''}),Al=(xe.a.add(El),El),Hl=new we.a({id:"triangle_down",use:"triangle_down-usage",viewBox:"0 0 10 5",content:''}),Ll=(xe.a.add(Hl),Hl),Dl=new we.a({id:"unknownError",use:"unknownError-usage",viewBox:"0 0 16 16",content:''}),Pl=(xe.a.add(Dl),Dl),Vl=new we.a({id:"universe",use:"universe-usage",viewBox:"0 0 18 18",content:''}),Nl=(xe.a.add(Vl),Vl),Il=new we.a({id:"unreachable",use:"unreachable-usage",viewBox:"0 0 12 14",content:''}),Rl=(xe.a.add(Il),Il),Bl=new we.a({id:"unreachableNode",use:"unreachableNode-usage",viewBox:"0 0 231 230",content:''}),Fl=(xe.a.add(Bl),Bl),Ul=new we.a({id:"update",use:"update-usage",viewBox:"0 0 20 20",content:''}),Wl=(xe.a.add(Ul),Ul),Gl=new we.a({id:"update_pending",use:"update_pending-usage",viewBox:"0 0 20 20",content:''}),Yl=(xe.a.add(Gl),Gl),Zl=new we.a({id:"upload",use:"upload-usage",viewBox:"0 0 20 21",content:''}),$l=(xe.a.add(Zl),Zl),Xl=new we.a({id:"user",use:"user-usage",viewBox:"0 0 16 18",content:''}),Kl=(xe.a.add(Xl),Xl),Ql=new we.a({id:"users",use:"users-usage",viewBox:"0 0 14 14",content:''}),Jl=(xe.a.add(Ql),Ql),eu=new we.a({id:"view_list",use:"view_list-usage",viewBox:"0 0 24 24",content:''}),tu=(xe.a.add(eu),eu),nu=new we.a({id:"single_node_view",use:"single_node_view-usage",viewBox:"0 0 18 18",content:''}),ru=(xe.a.add(nu),nu),iu=new we.a({id:"single_node_view_press",use:"single_node_view_press-usage",viewBox:"0 0 18 18",content:''}),ou=(xe.a.add(iu),iu),au=new we.a({id:"virtualization",use:"virtualization-usage",viewBox:"0 0 16 16",content:''}),cu=(xe.a.add(au),au),su=new we.a({id:"warning",use:"warning-usage",viewBox:"0 0 24 24",content:''}),lu=(xe.a.add(su),su),uu=new we.a({id:"warning_triangle",use:"warning_triangle-usage",viewBox:"0 0 12 10",content:''}),fu=(xe.a.add(uu),uu),hu=new we.a({id:"warning_triangle_hollow",use:"warning_triangle_hollow-usage",viewBox:"0 0 24 24",content:''}),du=(xe.a.add(hu),hu),pu=new we.a({id:"x",use:"x-usage",viewBox:"0 0 24 24",content:''}),zu=(xe.a.add(pu),pu),vu=new we.a({id:"firewall_solid",use:"firewall_solid-usage",viewBox:"0 0 24 24",content:''}),gu=(xe.a.add(vu),vu),mu=new we.a({id:"qualityOfService_solid",use:"qualityOfService_solid-usage",viewBox:"0 0 24 24",content:''}),yu=(xe.a.add(mu),mu),bu=new we.a({id:"applications_solid",use:"applications_solid-usage",viewBox:"0 0 24 24",content:''}),wu=(xe.a.add(bu),bu),ku=new we.a({id:"networking_stack",use:"networking_stack-usage",viewBox:"0 0 18 18",content:''}),xu=(xe.a.add(ku),ku),ju=new we.a({id:"charts_view",use:"charts_view-usage",viewBox:"0 0 16 15",content:''}),Mu=(xe.a.add(ju),ju),_u=new we.a({id:"nodes_update",use:"nodes_update-usage",viewBox:"0 0 40 40",content:''}),Cu=(xe.a.add(_u),{add_node:Me,add_user:Ce,aggregation_avg:Se,aggregation_max:Te,aggregation_med:Ae,aggregation_min:Le,aggregation_sum:Pe,aggregation_sum_abs:Ne,alarm:Re,alarmCritical:Fe,alarmCriticalWarning:We,alarmWarning:Ye,alarm_bell:$e,alarms_new:Ke,anomaliesBrain:Je,anomaliesLens:tt,applications_hollow:rt,applicationsSolid:wu,around_clock:ot,arrow_down:ct,arrow_w_line_left:lt,arrow_w_line_right:ft,arrow_left:dt,arrow_s_down:zt,arrow_s_left:gt,arrows_vertical:yt,bookmark:wt,bullet_one:xt,bullet_three:Mt,bullet_two:Ct,calendarFull:St,calendarFullPress:Tt,chart_added:At,charts:Lt,charts_view:Mu,check:Pt,checkmark_partial_s:Nt,checkmark_s:Rt,checkmark:Ft,chevron_double:Wt,chevron_down:Yt,chevron_left:$t,chevron_right_s:Kt,classError:Jt,classLatency:tn,classUtilization:rn,classWorkload:an,clock_hollow:sn,clock5Min:un,clock5MinPress:hn,close_circle:pn,cluster:vn,cluster_spaces:mn,code:bn,collapse:kn,community:jn,connectivityStatusLive:_n,connectivityStatusOffline:qn,connectivityStatusStale:On,container:En,controller_kind:Hn,controller_name:Dn,copy:Vn,correlation:In,correlation_inv:Bn,cpu:Un,cross_s:Gn,data_retention:Zn,database:Xn,dashboard:Qn,dashboard_add:er,dashboards:nr,disk:ir,documentation:ar,dot:sr,dots_2x3:ur,download:hr,error:pr,exclamation:vr,expand:mr,filterList:br,firewallSolid:gu,forcePlay:kr,forcePlayOutline:jr,gear:_r,github:qr,google:Er,goToNode:Or,group_by:Hr,hamburger:Dr,help:Vr,hide:Ir,highlightArea:Br,holder:Ur,incident_manager:Gr,information:Zr,informationPress:Xr,insights:Qr,integrations:ei,ipNetworking:ni,ipNetworkingPress:ii,last_week:ai,line_chart:si,logo_s:ui,loading:hi,magnify:pi,metrics:vi,metrics_explorer:mi,monitoring:bi,more:ki,navLeft:ji,navRight:_i,nav_arrow_goto:qi,nav_dots:Oi,networkingStack:xu,netdata:Ei,netdataPress:Hi,node:Di,node_child:Vi,node_default_l:Ii,node_hollow:Bi,node_import_export:Ui,node_notification_l:Gi,node_parent:Zi,node_selected_l:Xi,nodes:Qi,nodes_hollow:eo,none_selected:no,nodes_update:_u,notification:Jo,os:io,osAlpineLinux:ao,osAmazonLinux:so,osArchLinux:uo,osCelarOS:ho,osCentos:zo,osCentosColor:go,osCoreOS:yo,osDebian:wo,osDebianColor:xo,osFedora:Mo,osFreeBSD:Co,osGentoo:So,osLinux:To,osLinuxColor:Ao,osMacOSX:Lo,osOracle:Po,osOracleColor:No,osPress:Ro,osRaspbian:Fo,osRedHat:Wo,osSuseLinux:Yo,osUbuntu:$o,osUbuntuColor:Ko,padlock:ta,pauseOutline:ra,pauseSolid:oa,pencilSolid:la,pencilOutline:ca,pie_chart_skeleton:fa,pin_element:da,playOutline:za,playSolid:ga,plus:ya,plus_mini_s:wa,pod:xa,pricing:Ma,print:Ca,privacy:Sa,ram:La,qualityOfServiceSolid:yu,question:Ta,questionFilled:Aa,refresh:Pa,reload:Na,removeNode:Ra,resize_handler:Fa,room:Wa,room_home:Ya,room_new:$a,room_overview:Ka,sad:Ja,save:tc,search:rc,search_s:oc,searchPress:cc,serviceApache:lc,serviceApacheTomcat:fc,serviceBeanstalk:dc,serviceCouchDB:zc,serviceDatabase:gc,serviceDockerHub:yc,serviceDockerHubPress:wc,serviceEBPF:xc,serviceElasticSearch:Mc,serviceFreeNAS:Cc,serviceHAProxy:Sc,serviceHTTPCheck:Tc,serviceIceCast:Ac,serviceInfluxDB:Lc,serviceIPFS:Pc,serviceIPVS:Nc,serviceKubernetes:Rc,serviceLighthttpd:Fc,serviceLighthttpd2:Wc,serviceLiteSpeed:Yc,serviceLxc:$c,serviceMariaDB:Kc,serviceMemCached:Jc,serviceMongoDB:ts,serviceMySQL:rs,serviceMySQLPress:os,serviceNginx:cs,serviceNginxLocal:ls,serviceNginxPlus:fs,serviceNtpd:ds,serviceNtpdPress:zs,serviceOpenStack:gs,serviceOpenWrt:ys,servicePan:ws,servicePercona:xs,servicePfSense:Ms,servicePhpFpm:Cs,servicePostgreSQL:Ss,serviceProxySQL:Ss,serviceRabbitMQ:Ts,serviceRedis:As,serviceRethinkDB:Ls,serviceRetroShare:Ps,services:Ns,serviceSelectedArea:Rs,serviceSolr:Fs,serviceSquid:Ws,serviceSummaryStatistic:Ys,serviceTraefik:$s,serviceVarnish:Ks,serviceWebLog:Js,serviceWebLogNginx:tl,serviceX509Check:rl,serviceXen:ol,settings:cl,settings_h:ll,sorting_vertical:fl,space:dl,space_new:zl,switch_off:gl,system_overview:yl,text_add:wl,thumb_down:xl,thumb_up:Ml,tiny_buttons:Cl,training:Sl,trashcan:Tl,triangle:Al,triangle_down:Ll,unknownError:Pl,universe:Nl,unreachable:Rl,unreachableNode:Fl,update:Wl,update_pending:Yl,upload:$l,user:Kl,users:Jl,view_list:tu,viewSingleNode:ru,viewSingleNodePress:ou,virtualization:cu,warning:lu,warning_triangle:fu,warning_triangle_hollow:du,x:zu}),qu=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},Su={small:"16px",medium:"24px",large:"40px"},Ou=s.d.svg.withConfig({displayName:"styled__StyledIcon",componentId:"sc-1pjn63w-0"})(H||(H=qu(["\n height: ",";\n width: ",";\n opacity: ",";\n pointer-events: ",";\n ","\n ","\n ","\n ","\n ","\n"],["\n height: ",";\n width: ",";\n opacity: ",";\n pointer-events: ",";\n ","\n ","\n ","\n ","\n ","\n"])),(function(e){var t=e.size;return e.height||Su[t]}),(function(e){var t=e.size;return e.width||Su[t]}),(function(e){return e.disabled?.3:1}),(function(e){return e.disabled?"none":"unset"}),(function(e){var t=e.rotate;return!isNaN(t)&&"transform: rotate("+90*t+"deg);"}),(function(e){var t=e.theme,n=e.color;return n&&"fill: "+G(n)({theme:t})+";"}),(function(e){var t=e.theme,n=e.hoverColor;return n&&"&:hover { fill: "+G(n)({theme:t})+"; }"}),Q,te),Tu=function(){return(Tu=Object.assign||function(e){for(var t,n=1,r=arguments.length;n *:not(:last-child) {\n margin-"+(r||i?"bottom":"right")+": "+t*n+"px;\n }\n "},sf=function(e){return"flex-direction: "+function(e,t,n){return e?"column":t?"column-reverse":n?"row-reverse":"row"}(e.column,e.columnReverse,e.rowReverse)+";"},lf=function(){return(lf=Object.assign||function(e){for(var t,n=1,r=arguments.length;n &",_invalid:"&[aria-invalid=true]",_pressed:"&[aria-pressed=true]",_readOnly:"&[aria-readonly=true], &[readonly]",_first:"&:first-of-type",_last:"&:last-of-type",_expanded:"&[aria-expanded=true]",_grabbed:"&[aria-grabbed=true]",_notFirst:"&:not(:first-of-type)",_notLast:"&:not(:last-of-type)",_groupHover:"[role=group]:hover &",_autofill:"&:-webkit-autofill",_placeholder:"&::placeholder"},mf=function(e){var t=e.theme,n=zf(e,["theme"]),r="";for(var i in n)if(i in gf){var o=i,a=n[o],c=vf(df,hf,Ku)(pf({theme:t},a));r=r+"\n "+gf[o]+"{ \n "+c+" \n }"}return r.replace(/^(?=\n)$|^\s*|\s*$|\n\n+/gm,"")};function yf(){return(yf=Object.assign||function(e){for(var t=1;t=0)return wf(e,t,t);var n=Math.abs(t),r=wf(e,n,n);return"string"===typeof r?"-"+r:-1*r},qf=["margin","marginTop","marginRight","marginBottom","marginLeft","marginX","marginY","top","bottom","left","right"].reduce((function(e,t){var n;return yf({},e,((n={})[t]=Cf,n))}),{}),Sf=function e(t){return function(n){void 0===n&&(n={});var r=yf({},xf,{},n.theme||n),i={},o=function(e){return function(t){var n={},r=wf(t,"breakpoints",kf),i=[null].concat(r.map((function(e){return"@media screen and (min-width: "+e+")"})));for(var o in e){var a="function"===typeof e[o]?e[o](t):e[o];if(null!=a)if(Array.isArray(a))for(var c=0;c=0&&"[object Array]"===Object.prototype.toString.call(e)},ep=j((function(e){return!!Jd(e)||!!e&&("object"===typeof e&&(!O(e)&&(1===e.nodeType?!!e.length:0===e.length||e.length>0&&(e.hasOwnProperty(0)&&e.hasOwnProperty(e.length-1)))))})),tp=function(){function e(e){this.f=e}return e.prototype["@@transducer/init"]=function(){throw new Error("init not implemented on XWrap")},e.prototype["@@transducer/result"]=function(e){return e},e.prototype["@@transducer/step"]=function(e,t){return this.f(e,t)},e}();var np=M((function(e,t){return Kd(e.length,(function(){return e.apply(t,arguments)}))}));function rp(e,t,n){for(var r=n.next();!r.done;){if((t=e["@@transducer/step"](t,r.value))&&t["@@transducer/reduced"]){t=t["@@transducer/value"];break}r=n.next()}return e["@@transducer/result"](t)}function ip(e,t,n,r){return e["@@transducer/result"](n[r](np(e["@@transducer/step"],e),t))}var op="undefined"!==typeof Symbol?Symbol.iterator:"@@iterator";function ap(e,t,n){if("function"===typeof e&&(e=function(e){return new tp(e)}(e)),ep(n))return function(e,t,n){for(var r=0,i=n.length;r=arguments.length)?s=n[c]:(s=arguments[o],o+=1),i[c]=s,x(s)||(a-=1),c+=1}return a<=0?r.apply(this,i):Kd(a,e(t,i,r))}}(e,[],t))})),yp=Object.prototype.toString,bp=function(){return"[object Arguments]"===yp.call(arguments)?function(e){return"[object Arguments]"===yp.call(e)}:function(e){return C("callee",e)}}(),wp=!{toString:null}.propertyIsEnumerable("toString"),kp=["constructor","valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"],xp=function(){return arguments.propertyIsEnumerable("length")}(),jp=function(e,t){for(var n=0;n=0;)C(t=kp[n],e)&&!jp(r,t)&&(r[r.length]=t),n-=1;return r})):j((function(e){return Object(e)!==e?[]:Object.keys(e)})),_p=M(hp(["fantasy-land/map","map"],gp,(function(e,t){switch(Object.prototype.toString.call(t)){case"[object Function]":return mp(t.length,(function(){return e.call(this,t.apply(this,arguments))}));case"[object Object]":return ap((function(n,r){return n[r]=e(t[r]),n}),{},Mp(t));default:return dp(e,t)}})));function Cp(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}function qp(e,t,n){for(var r=0,i=n.length;r=0;){if(n[o]===e)return r[o]===t;o-=1}switch(i){case"Map":return e.size===t.size&&Tp(e.entries(),t.entries(),n.concat([e]),r.concat([t]));case"Set":return e.size===t.size&&Tp(e.values(),t.values(),n.concat([e]),r.concat([t]));case"Arguments":case"Array":case"Object":case"Boolean":case"Number":case"String":case"Date":case"Error":case"RegExp":case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"ArrayBuffer":break;default:return!1}var a=Mp(e);if(a.length!==Mp(t).length)return!1;var c=n.concat([e]),s=r.concat([t]);for(o=a.length-1;o>=0;){var l=a[o];if(!C(l,t)||!Ep(t[l],e[l],c,s))return!1;o-=1}return!0}var Ap=M((function(e,t){return Ep(e,t,[],[])}));function Hp(e,t){return function(e,t,n){var r,i;if("function"===typeof e.indexOf)switch(typeof t){case"number":if(0===t){for(r=1/t;n=0}var Lp=function(e){return(e<10?"0":"")+e};Date.prototype.toISOString;var Dp,Pp,Vp,Np,Ip,Rp,Bp=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},Fp=(s.d.tr.withConfig({displayName:"styled__StyledRow",componentId:"ssimnk-0"})(Dp||(Dp=Bp(["\n cursor: ",";\n position: ",";\n"],["\n cursor: ",";\n position: ",";\n"])),(function(e){return e.onClick?"pointer":"auto"}),(function(e){return e.hasStickyHeader?"static":"relative"})),s.d.div.withConfig({displayName:"styled__StyledBlockRow",componentId:"ssimnk-1"})(Pp||(Pp=Bp(["\n position: relative;\n"],["\n position: relative;\n"]))),function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e}),Up=s.d.thead.withConfig({displayName:"styled__StyledThead",componentId:"sc-14orp2c-0"})(Vp||(Vp=Fp(["\n & > tr th {\n border-spacing: 0;\n border-bottom: 1px solid #aeb3b7;\n padding-bottom: 5px;\n }\n"],["\n & > tr th {\n border-spacing: 0;\n border-bottom: 1px solid #aeb3b7;\n padding-bottom: 5px;\n }\n"]))),Wp=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},Gp=function(){return(Gp=Object.assign||function(e){for(var t,n=1,r=arguments.length;n=t?e.call(null):r.id=requestAnimationFrame(i)}))};return r}var dz=null;function pz(e){if(void 0===e&&(e=!1),null===dz||e){var t=document.createElement("div"),n=t.style;n.width="50px",n.height="50px",n.overflow="scroll",n.direction="rtl";var r=document.createElement("div"),i=r.style;return i.width="100px",i.height="100px",t.appendChild(r),document.body.appendChild(t),t.scrollLeft>0?dz="positive-descending":(t.scrollLeft=1,dz=0===t.scrollLeft?"negative":"positive-ascending"),document.body.removeChild(t),dz}return dz}var zz=150,vz=function(e,t){return e};function gz(e){var t,n=e.getItemOffset,r=e.getEstimatedTotalSize,i=e.getItemSize,o=e.getOffsetForIndexAndAlignment,c=e.getStartIndexForOffset,s=e.getStopIndexForStartIndex,l=e.initInstanceProps,u=e.shouldResetStyleCacheOnItemSizeChange,f=e.validateProps;return(t=function(e){function t(t){var r;return(r=e.call(this,t)||this)._instanceProps=l(r.props,Object(oz.a)(r)),r._outerRef=void 0,r._resetIsScrollingTimeoutId=null,r.state={instance:Object(oz.a)(r),isScrolling:!1,scrollDirection:"forward",scrollOffset:"number"===typeof r.props.initialScrollOffset?r.props.initialScrollOffset:0,scrollUpdateWasRequested:!1},r._callOnItemsRendered=void 0,r._callOnItemsRendered=lz((function(e,t,n,i){return r.props.onItemsRendered({overscanStartIndex:e,overscanStopIndex:t,visibleStartIndex:n,visibleStopIndex:i})})),r._callOnScroll=void 0,r._callOnScroll=lz((function(e,t,n){return r.props.onScroll({scrollDirection:e,scrollOffset:t,scrollUpdateWasRequested:n})})),r._getItemStyle=void 0,r._getItemStyle=function(e){var t,o=r.props,a=o.direction,c=o.itemSize,s=o.layout,l=r._getItemStyleCache(u&&c,u&&s,u&&a);if(l.hasOwnProperty(e))t=l[e];else{var f=n(r.props,e,r._instanceProps),h=i(r.props,e,r._instanceProps),d="horizontal"===a||"horizontal"===s,p="rtl"===a,z=d?f:0;l[e]=t={position:"absolute",left:p?void 0:z,right:p?z:void 0,top:d?0:f,height:d?"100%":h,width:d?h:"100%"}}return t},r._getItemStyleCache=void 0,r._getItemStyleCache=lz((function(e,t,n){return{}})),r._onScrollHorizontal=function(e){var t=e.currentTarget,n=t.clientWidth,i=t.scrollLeft,o=t.scrollWidth;r.setState((function(e){if(e.scrollOffset===i)return null;var t=r.props.direction,a=i;if("rtl"===t)switch(pz()){case"negative":a=-i;break;case"positive-descending":a=o-n-i}return a=Math.max(0,Math.min(a,o-n)),{isScrolling:!0,scrollDirection:e.scrollOffset0)for(var C=j;C<=M;C++)_.push(Object(a.createElement)(t,{data:f,key:d(C,f),index:C,isScrolling:m?b:void 0,style:this._getItemStyle(C)}));var q=r(this.props,this._instanceProps);return Object(a.createElement)(z||v||"div",{className:n,onScroll:k,ref:this._outerRefSetter,style:Object(iz.a)({position:"relative",height:o,width:y,overflow:"auto",WebkitOverflowScrolling:"touch",willChange:"transform",direction:i},g)},Object(a.createElement)(s||l||"div",{children:_,ref:c,style:{height:w?"100%":q,pointerEvents:b?"none":void 0,width:w?q:"100%"}}))},h._callPropsCallbacks=function(){if("function"===typeof this.props.onItemsRendered&&this.props.itemCount>0){var e=this._getRangeToRender(),t=e[0],n=e[1],r=e[2],i=e[3];this._callOnItemsRendered(t,n,r,i)}if("function"===typeof this.props.onScroll){var o=this.state,a=o.scrollDirection,c=o.scrollOffset,s=o.scrollUpdateWasRequested;this._callOnScroll(a,c,s)}},h._getRangeToRender=function(){var e=this.props,t=e.itemCount,n=e.overscanCount,r=this.state,i=r.isScrolling,o=r.scrollDirection,a=r.scrollOffset;if(0===t)return[0,0,0,0];var l=c(this.props,a,this._instanceProps),u=s(this.props,l,a,this._instanceProps),f=i&&"backward"!==o?1:Math.max(1,n),h=i&&"forward"!==o?1:Math.max(1,n);return[Math.max(0,l-f),Math.max(0,Math.min(t-1,u+h)),l,u]},t}(a.PureComponent)).defaultProps={direction:"ltr",itemData:void 0,layout:"vertical",overscanCount:2,useIsScrolling:!1},t}var mz=function(e,t){e.children,e.direction,e.height,e.layout,e.innerTagName,e.outerTagName,e.width,t.instance},yz=function(e,t,n){var r=e.itemSize,i=n.itemMetadataMap,o=n.lastMeasuredIndex;if(t>o){var a=0;if(o>=0){var c=i[o];a=c.offset+c.size}for(var s=o+1;s<=t;s++){var l=r(s);i[s]={offset:a,size:l},a+=l}n.lastMeasuredIndex=t}return i[t]},bz=function(e,t,n,r,i){for(;r<=n;){var o=r+Math.floor((n-r)/2),a=yz(e,o,t).offset;if(a===i)return o;ai&&(n=o-1)}return r>0?r-1:0},wz=function(e,t,n,r){for(var i=e.itemCount,o=1;n=n&&(o=n-1),o>=0){var c=r[o];a=c.offset+c.size}return a+(n-o-1)*i},xz=gz({getItemOffset:function(e,t,n){return yz(e,t,n).offset},getItemSize:function(e,t,n){return n.itemMetadataMap[t].size},getEstimatedTotalSize:kz,getOffsetForIndexAndAlignment:function(e,t,n,r,i){var o=e.direction,a=e.height,c=e.layout,s=e.width,l="horizontal"===o||"horizontal"===c?s:a,u=yz(e,t,i),f=kz(e,i),h=Math.max(0,Math.min(f-l,u.offset)),d=Math.max(0,u.offset-l+u.size);switch("smart"===n&&(n=r>=d-l&&r<=h+l?"auto":"center"),n){case"start":return h;case"end":return d;case"center":return Math.round(d+(h-d)/2);case"auto":default:return r>=d&&r<=h?r:r0?r[i].offset:0)>=n?bz(e,t,i,0,n):wz(e,t,Math.max(0,i),n)}(e,n,t)},getStopIndexForStartIndex:function(e,t,n,r){for(var i=e.direction,o=e.height,a=e.itemCount,c=e.layout,s=e.width,l="horizontal"===i||"horizontal"===c?s:o,u=yz(e,t,r),f=n+l,h=u.offset+u.size,d=t;d=d-u&&r<=h+u?"auto":"center"),n){case"start":return h;case"end":return d;case"center":var p=Math.round(d+(h-d)/2);return pf+Math.floor(u/2)?f:p;case"auto":default:return r>=d&&r<=h?r:r span {\n font-weight: ",";\n }\n"],["\n border-bottom: "," solid\n ",";\n box-sizing: border-box;\n\n min-width: ",";\n max-width: ",";\n height: ",";\n color: ",";\n font-weight: ",";\n\n cursor: pointer;\n opacity: ",";\n pointer-events: ",";\n\n &:hover {\n border-bottom: "," solid ",";\n }\n\n & > span {\n font-weight: ",";\n }\n"])),(function(e){return e.small?"2px":"4px"}),(function(e){var t=e.active;return G(t?"accent":["transparent","full"])}),(function(e){var t=e.minWidth;return null!==t&&void 0!==t?t:Z(10)}),(function(e){var t=e.maxWidth;return null!==t&&void 0!==t?t:Z(26)}),(function(e){var t=e.small;return Z(t?4:6)}),G("text"),(function(e){return e.active?"bold":"normal"}),(function(e){return e.disabled?.4:1}),(function(e){return e.disabled?"none":"auto"}),(function(e){return e.small?"2px":"4px"}),G("primary"),(function(e){return e.active?"bold":"normal"})),tv=function(){return(tv=Object.assign||function(e){for(var t,n=1,r=arguments.length;ne.clientHeight}));return n.forEach((function(e){return e.addEventListener("scroll",t,{capture:!1,passive:!0})})),function(){return n.forEach((function(e){return e.removeEventListener("scroll",t)}))}};n=r();var i=function(){n(),n=r(),t()};return window.addEventListener("resize",i),function(){n(),window.removeEventListener("resize",i)}}),[e,t])}(i,b),Rv(m,f,i),Vv(h);var w=Pv();return Uf.a.createPortal(r?c.a.createElement(c.a.Fragment,null,c.a.createElement($v,Qv({ref:y,width:{max:"100%"},column:!0,"data-testid":"drop"},v),d),c.a.createElement(eg,null)):c.a.createElement($v,Qv({ref:y,width:{max:"100%"},column:!0,"data-testid":"drop"},v),d),w)})),rg=function(){return(rg=Object.assign||function(e){for(var t,n=1,r=arguments.length;n1&&i.forEach((function(n){var i;r[n]=e(((i={})[n]=t[n],i))})),r},Gg=function(e,t,n,r,i){var o={};return r.slice(0,e.length).forEach((function(r,a){var c,s=e[a],l=t(r,n,i);s?Ng()(o,((c={})[s]=Ng()({},o[s],l),c)):Ng()(o,l)})),o},Yg=function(e,t,n,r,i){var o={};for(var a in r){var c=e[a],s=t(r[a],n,i);if(c){var l,u=Bg(c);Ng()(o,((l={})[u]=Ng()({},o[u],s),l))}else Ng()(o,s)}return o},Zg=function(e){var t=e.properties,n=e.property,r=e.scale,i=e.transform,o=void 0===i?Fg:i,a=e.defaultScale;t=t||[n];var c=function(e,n,r){var i={},a=o(e,n,r);if(null!==a)return t.forEach((function(e){i[e]=a})),i};return c.scale=r,c.defaults=a,c},$g=function(e){void 0===e&&(e={});var t={};return Object.keys(e).forEach((function(n){var r=e[n];t[n]=!0!==r?"function"!==typeof r?Zg(r):r:Zg({property:n,scale:n})})),Wg(t)},Xg=$g({width:{property:"width",scale:"sizes",transform:function(e,t){return Ug(t,e,!function(e){return"number"===typeof e&&!isNaN(e)}(e)||e>1?e:100*e+"%")}},height:{property:"height",scale:"sizes"},minWidth:{property:"minWidth",scale:"sizes"},minHeight:{property:"minHeight",scale:"sizes"},maxWidth:{property:"maxWidth",scale:"sizes"},maxHeight:{property:"maxHeight",scale:"sizes"},size:{properties:["width","height"],scale:"sizes"},overflow:!0,overflowX:!0,overflowY:!0,display:!0,verticalAlign:!0}),Kg={color:{property:"color",scale:"colors"},backgroundColor:{property:"backgroundColor",scale:"colors"},opacity:!0};Kg.bg=Kg.backgroundColor;var Qg=$g(Kg),Jg=$g({fontFamily:{property:"fontFamily",scale:"fonts"},fontSize:{property:"fontSize",scale:"fontSizes",defaultScale:[12,14,16,20,24,32,48,64,72]},fontWeight:{property:"fontWeight",scale:"fontWeights"},lineHeight:{property:"lineHeight",scale:"lineHeights"},letterSpacing:{property:"letterSpacing",scale:"letterSpacings"},textAlign:!0,fontStyle:!0}),em=$g({alignItems:!0,alignContent:!0,justifyItems:!0,justifyContent:!0,flexWrap:!0,flexDirection:!0,flex:!0,flexGrow:!0,flexShrink:!0,flexBasis:!0,justifySelf:!0,alignSelf:!0,order:!0}),tm={space:[0,4,8,16,32,64,128,256,512]},nm=$g({gridGap:{property:"gridGap",scale:"space",defaultScale:tm.space},gridColumnGap:{property:"gridColumnGap",scale:"space",defaultScale:tm.space},gridRowGap:{property:"gridRowGap",scale:"space",defaultScale:tm.space},gridColumn:!0,gridRow:!0,gridAutoFlow:!0,gridAutoColumns:!0,gridAutoRows:!0,gridTemplateColumns:!0,gridTemplateRows:!0,gridTemplateAreas:!0,gridArea:!0}),rm={border:{property:"border",scale:"borders"},borderWidth:{property:"borderWidth",scale:"borderWidths"},borderStyle:{property:"borderStyle",scale:"borderStyles"},borderColor:{property:"borderColor",scale:"colors"},borderRadius:{property:"borderRadius",scale:"radii"},borderTop:{property:"borderTop",scale:"borders"},borderTopLeftRadius:{property:"borderTopLeftRadius",scale:"radii"},borderTopRightRadius:{property:"borderTopRightRadius",scale:"radii"},borderRight:{property:"borderRight",scale:"borders"},borderBottom:{property:"borderBottom",scale:"borders"},borderBottomLeftRadius:{property:"borderBottomLeftRadius",scale:"radii"},borderBottomRightRadius:{property:"borderBottomRightRadius",scale:"radii"},borderLeft:{property:"borderLeft",scale:"borders"},borderX:{properties:["borderLeft","borderRight"],scale:"borders"},borderY:{properties:["borderTop","borderBottom"],scale:"borders"},borderTopWidth:{property:"borderTopWidth",scale:"borderWidths"},borderTopColor:{property:"borderTopColor",scale:"colors"},borderTopStyle:{property:"borderTopStyle",scale:"borderStyles"}};rm.borderTopLeftRadius={property:"borderTopLeftRadius",scale:"radii"},rm.borderTopRightRadius={property:"borderTopRightRadius",scale:"radii"},rm.borderBottomWidth={property:"borderBottomWidth",scale:"borderWidths"},rm.borderBottomColor={property:"borderBottomColor",scale:"colors"},rm.borderBottomStyle={property:"borderBottomStyle",scale:"borderStyles"},rm.borderBottomLeftRadius={property:"borderBottomLeftRadius",scale:"radii"},rm.borderBottomRightRadius={property:"borderBottomRightRadius",scale:"radii"},rm.borderLeftWidth={property:"borderLeftWidth",scale:"borderWidths"},rm.borderLeftColor={property:"borderLeftColor",scale:"colors"},rm.borderLeftStyle={property:"borderLeftStyle",scale:"borderStyles"},rm.borderRightWidth={property:"borderRightWidth",scale:"borderWidths"},rm.borderRightColor={property:"borderRightColor",scale:"colors"},rm.borderRightStyle={property:"borderRightStyle",scale:"borderStyles"};var im=$g(rm),om={background:!0,backgroundImage:!0,backgroundSize:!0,backgroundPosition:!0,backgroundRepeat:!0};om.bgImage=om.backgroundImage,om.bgSize=om.backgroundSize,om.bgPosition=om.backgroundPosition,om.bgRepeat=om.backgroundRepeat;var am=$g(om),cm={space:[0,4,8,16,32,64,128,256,512]},sm=$g({position:!0,zIndex:{property:"zIndex",scale:"zIndices"},top:{property:"top",scale:"space",defaultScale:cm.space},right:{property:"right",scale:"space",defaultScale:cm.space},bottom:{property:"bottom",scale:"space",defaultScale:cm.space},left:{property:"left",scale:"space",defaultScale:cm.space}}),lm=sm,um={space:[0,4,8,16,32,64,128,256,512]},fm=function(e){return"number"===typeof e&&!isNaN(e)},hm=function(e,t){if(!fm(e))return Ug(t,e,e);var n=e<0,r=Math.abs(e),i=Ug(t,r,r);return fm(i)?i*(n?-1:1):n?"-"+i:i},dm={};dm.margin={margin:{property:"margin",scale:"space",transform:hm,defaultScale:um.space},marginTop:{property:"marginTop",scale:"space",transform:hm,defaultScale:um.space},marginRight:{property:"marginRight",scale:"space",transform:hm,defaultScale:um.space},marginBottom:{property:"marginBottom",scale:"space",transform:hm,defaultScale:um.space},marginLeft:{property:"marginLeft",scale:"space",transform:hm,defaultScale:um.space},marginX:{properties:["marginLeft","marginRight"],scale:"space",transform:hm,defaultScale:um.space},marginY:{properties:["marginTop","marginBottom"],scale:"space",transform:hm,defaultScale:um.space}},dm.margin.m=dm.margin.margin,dm.margin.mt=dm.margin.marginTop,dm.margin.mr=dm.margin.marginRight,dm.margin.mb=dm.margin.marginBottom,dm.margin.ml=dm.margin.marginLeft,dm.margin.mx=dm.margin.marginX,dm.margin.my=dm.margin.marginY,dm.padding={padding:{property:"padding",scale:"space",defaultScale:um.space},paddingTop:{property:"paddingTop",scale:"space",defaultScale:um.space},paddingRight:{property:"paddingRight",scale:"space",defaultScale:um.space},paddingBottom:{property:"paddingBottom",scale:"space",defaultScale:um.space},paddingLeft:{property:"paddingLeft",scale:"space",defaultScale:um.space},paddingX:{properties:["paddingLeft","paddingRight"],scale:"space",defaultScale:um.space},paddingY:{properties:["paddingTop","paddingBottom"],scale:"space",defaultScale:um.space}},dm.padding.p=dm.padding.padding,dm.padding.pt=dm.padding.paddingTop,dm.padding.pr=dm.padding.paddingRight,dm.padding.pb=dm.padding.paddingBottom,dm.padding.pl=dm.padding.paddingLeft,dm.padding.px=dm.padding.paddingX,dm.padding.py=dm.padding.paddingY;(function(){for(var e={},t=arguments.length,n=new Array(t),r=0;r4)return Mm;var n=t.map((function(t){return X(e,t)}));return 1===n.length?{top:n[0],right:n[0],bottom:n[0],left:n[0]}:2===n.length?{top:n[0],right:n[1],bottom:n[0],left:n[1]}:3===n.length?{top:n[0],right:n[1],bottom:n[2],left:n[1]}:{top:n[0],right:n[1],bottom:n[2],left:n[3]}},Cm=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},qm=function(e,t){return"0"!==e&&"0"!==t?"calc((100% - "+e+") - "+t+")":"0"===e&&"0"===t?"100%":"calc(100% - "+("0"===e?t:e)+")"},Sm=new Set(["top","center","bottom"]),Om=new Set(["bottom-left","left","top-left"]),Tm=new Set(["right","center","left"]),Em=new Set(["top-left","top","top-right"]),Am=new Set(["top-right","right","bottom-right"]),Hm=new Set(["bottom-right","bottom","bottom-left"]),Lm=s.d.div.attrs((function(e){var t=e.theme,n=e.margin;return{marginDimensions:_m(t,n)}})).withConfig({displayName:"container__Container",componentId:"sc-7g83tw-0"})(zm||(zm=Cm(["\n position: ",";\n display: flex;\n z-index: 35;\n outline: none;\n pointer-events: all;\n\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n\n ","\n"],["\n position: ",";\n display: flex;\n z-index: 35;\n outline: none;\n pointer-events: all;\n\n ","\n ","\n ","\n ","\n ","\n ","\n ","\n\n ","\n"])),(function(e){return e.isAbsolute?"absolute":"fixed"}),(function(e){var t=e.marginDimensions,n=t.top,r=t.bottom;return"max-height: "+qm(n,r)+";"}),(function(e){var t=e.marginDimensions,n=t.right,r=t.left;return"max-width: "+qm(r,n)+";"}),(function(e){var t=e.position,n=e.full,r=e.marginDimensions;return"vertical"===n||!0===n||Em.has(t)?"top: "+r.top+";":Tm.has(t)?"top: 50%;":""}),(function(e){var t=e.position,n=e.full,r=e.marginDimensions;return"horizontal"===n||!0===n||Am.has(t)?"right: "+r.right+";":""}),(function(e){var t=e.position,n=e.full,r=e.marginDimensions;return"vertical"===n||!0===n||Hm.has(t)?"bottom: "+r.bottom+";":""}),(function(e){var t=e.position,n=e.full,r=e.marginDimensions;return"horizontal"===n||!0===n||Om.has(t)?"left: "+r.left+";":Sm.has(t)?"left: 50%;":""}),(function(e){var t=e.full,n=e.position,r=function(){var e=!0!==t&&"horizontal"!==t&&Sm.has(n),r=!0!==t&&"vertical"!==t&&Tm.has(n);return e||r?e&&!r?"translateX(-50%)":!e&&r?"translateY(-50%)":"translate(-50%, -50%)":""}();return r&&"transform: "+r+";"}),(function(e){return e.borderShadow&&"box-shadow: 0px 2px 68px rgba(0, 0, 0, 0.288);"})),Dm=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},Pm=function(){return(Pm=Object.assign||function(e){for(var t,n=1,r=arguments.length;n * {\n min-width: initial;\n max-width: initial;\n }\n"],["\n width: 100%;\n\n .tabs > * {\n min-width: initial;\n max-width: initial;\n }\n"]))),py=function(){return c.a.createElement(Af,{overflow:{vertical:"auto"},"data-testid":"dashboard"},c.a.createElement(dy,null,c.a.createElement(rv,{label:"Using a Mouse"},c.a.createElement(uy,null)),c.a.createElement(rv,{label:"Using Touch"},c.a.createElement(fy,null))))},zy=n(21),vy=n.n(zy);function gy(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function my(e,t){for(var n=0;n0?e.concat(Object.assign({field:o,values:l,type:c},s)):e}return e.concat(i)}),[])}function qy(e,t,n,r){var i=e.data,o=_y(t,n,r)||[];return Object.assign(Object.assign({},e),{data:i.map((function(e){return Object.assign(Object.assign({},e),{selected:o.some((function(t){return Sy(t,e.value)}))})}))})}function Sy(e,t){return!!(e&&e.name&&t&&t.name&&e.name===t.name)||jy()(e,t,{strict:!0})}function Oy(e,t){return t?t.reduce((function(e,t){return e.find((function(e){return e.type===t.type&&e.field===t.field}))?e:[].concat(Object(ky.a)(e),[t])}),e):e}function Ty(e){return void 0!==e.name}var Ey=Object.assign({},i),Ay=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"===typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i1)return console.warn("search-ui-site-search-connector: Cannot apply more than 1 none-value filters to a single field"),e;var i=r[0];if(Ey.isFilterValueRange(i)){i.name;var o=Ay(i,["name"]);return e[n]=Object.assign({type:"range"},o),e}return e}return e[n]=Object.assign(Object.assign({},"any"===t.type?{}:{type:"and"}),{values:r}),e}),{})}(void 0!==t.filters?t.filters:e.filters),l=void 0!==t.current?t.current:e.current,u=void 0!==t.resultsPerPage?t.resultsPerPage:e.resultsPerPage,f=void 0!==t.sortDirection?t.sortDirection:e.sortDirection,h=void 0!==t.sortField?t.sortField:e.sortField,d=void 0!==t.sortList?t.sortList:e.sortList,p=(o=t.result_fields)?[Object.keys(o),Object.entries(o).reduce((function(e,t){var n=Object(by.a)(t,2),r=n[0],i=n[1];return i.snippet?Object.assign(Object.assign({},e),Object(wy.a)({},r,i.snippet)):e}),{})]:[],z=Object(by.a)(p,2),v=z[0],g=z[1],m=(a=t.search_fields)?Object.keys(a):[],y=e.searchTerm;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},u&&{per_page:u}),l&&{page:l}),f&&{sort_direction:Object(wy.a)({},n,f)}),h&&{sort_field:Object(wy.a)({},n,h)}),d&&{sort_list:Object(wy.a)({},n,d)}),s&&{filters:Object(wy.a)({},n,s)}),c&&{facets:Object(wy.a)({},n,c)}),v&&{fetch_fields:Object(wy.a)({},n,v)}),g&&{highlight_fields:Object(wy.a)({},n,g)}),m&&!!m.length&&{search_fields:Object(wy.a)({},n,m)}),{q:y})}var Ly=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"===typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i0&&{facets:a})}var Vy=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{s(r.next(e))}catch(t){o(t)}}function c(e){try{s(r.throw(e))}catch(t){o(t)}}function s(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,c)}s((r=r.apply(e,t||[])).next())}))};function Ny(e,t,n,r){return Vy(this,void 0,void 0,vy.a.mark((function i(){var o,a,c,s;return vy.a.wrap((function(i){for(;;)switch(i.prev=i.next){case 0:return o=new Headers({"Content-Type":"application/json"}),i.next=3,fetch("https://search-api.swiftype.com/api/v1/public/".concat(n),{method:t,headers:o,body:JSON.stringify(Object.assign({engine_key:e},r)),credentials:"include"});case 3:return a=i.sent,i.prev=4,i.next=7,a.json();case 7:c=i.sent,i.next=12;break;case 10:i.prev=10,i.t0=i.catch(4);case 12:if(!(a.status>=200&&a.status<300)){i.next=16;break}return i.abrupt("return",c);case 16:throw s=c&&c.errors&&Object.entries(c.errors).length>0?JSON.stringify(c.errors):a.status,new Error("".concat(s));case 18:case"end":return i.stop()}}),i,null,[[4,10]])})))}var Iy=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{s(r.next(e))}catch(t){o(t)}}function c(e){try{s(r.throw(e))}catch(t){o(t)}}function s(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,c)}s((r=r.apply(e,t||[])).next())}))};function Ry(e,t,n){var r=Object.entries(Object.assign({engine_key:e},n)).map((function(e){var t=Object(by.a)(e,2),n=t[0],r=t[1];return"".concat(n,"=").concat(encodeURIComponent(r))})).join("&");return fetch("https://search-api.swiftype.com/api/v1/public/".concat(t,"?").concat(r),{method:"GET",credentials:"include"})}var By=function(){function e(t){var n=t.documentType,r=t.engineKey,i=t.beforeSearchCall,o=void 0===i?function(e,t){return t(e)}:i,a=t.beforeAutocompleteResultsCall,c=void 0===a?function(e,t){return t(e)}:a;gy(this,e),this.documentType=n,this.engineKey=r,this.beforeSearchCall=o,this.beforeAutocompleteResultsCall=c,this.request=Ny.bind(this,r),this._get=Ry.bind(this,r)}return yy(e,[{key:"onResultClick",value:function(e){var t=e.query,n=e.documentId,r=e.tags;r&&r.length>0&&console.warn("search-ui-site-search-connector: Site Search does not support tags on click"),this._get("analytics/pc",{t:(new Date).getTime(),q:t,doc_id:n})}},{key:"onAutocompleteResultClick",value:function(e){var t=e.query,n=e.documentId;e.tags&&console.warn("search-ui-site-search-connector: Site Search does not support tags on autocompleteClick"),this._get("analytics/pas",{t:(new Date).getTime(),q:t,doc_id:n})}},{key:"onSearch",value:function(e,t){var n=this,r=Hy(e,t,this.documentType);return this.beforeSearchCall(r,(function(e){return n.request("POST","engines/search.json",e).then((function(e){return Py(e,n.documentType)}))}))}},{key:"onAutocomplete",value:function(e,t){var n=e.searchTerm;return Iy(this,void 0,void 0,vy.a.mark((function e(){var r,i=this;return vy.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!t.results){e.next=3;break}return r=Hy({searchTerm:n},t.results,this.documentType),e.abrupt("return",this.beforeAutocompleteResultsCall(r,(function(e){return i.request("POST","engines/suggest.json",e).then((function(e){return{autocompletedResults:Py(e,i.documentType).results}}))})));case 3:t.suggestions&&console.warn("search-ui-site-search-connector: Site Search does support query suggestions on autocomplete");case 4:case"end":return e.stop()}}),e,this)})))}}]),e}();function Fy(e){return"/"===e.charAt(0)}function Uy(e,t){for(var n=t,r=n+1,i=e.length;r=0;u--){var f=i[u];"."===f?Uy(i,u):".."===f?(Uy(i,u),l++):l&&(Uy(i,u),l--)}if(!c)for(;l--;l)i.unshift("..");!c||""===i[0]||i[0]&&Fy(i[0])||i.unshift("");var h=i.join("/");return n&&"/"!==h.substr(-1)&&(h+="/"),h};var Gy=!0,Yy="Invariant failed";function Zy(e,t){if(!e){if(Gy)throw new Error(Yy);var n="function"===typeof t?t():t;throw new Error(n?Yy+": "+n:Yy)}}function $y(e){return"/"===e.charAt(0)?e:"/"+e}function Xy(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function Ky(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function Qy(e){var t=e.pathname,n=e.search,r=e.hash,i=t||"/";return n&&"?"!==n&&(i+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(i+="#"===r.charAt(0)?r:"#"+r),i}function Jy(e,t,n,r){var i;"string"===typeof e?(i=function(e){var t=e||"/",n="",r="",i=t.indexOf("#");-1!==i&&(r=t.substr(i),t=t.substr(0,i));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e)).state=t:(void 0===(i=Object(iz.a)({},e)).pathname&&(i.pathname=""),i.search?"?"!==i.search.charAt(0)&&(i.search="?"+i.search):i.search="",i.hash?"#"!==i.hash.charAt(0)&&(i.hash="#"+i.hash):i.hash="",void 0!==t&&void 0===i.state&&(i.state=t));try{i.pathname=decodeURI(i.pathname)}catch(o){throw o instanceof URIError?new URIError('Pathname "'+i.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):o}return n&&(i.key=n),r?i.pathname?"/"!==i.pathname.charAt(0)&&(i.pathname=Wy(i.pathname,r.pathname)):i.pathname=r.pathname:i.pathname||(i.pathname="/"),i}function eb(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,i){if(null!=e){var o="function"===typeof e?e(t,n):e;"string"===typeof o?"function"===typeof r?r(o,i):i(!0):i(!1!==o)}else i(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;r1&&(s.current=n),t&&(s.q=t),i&&(s.size=i),r&&r.length>0&&(s.filters=r),c&&c.length>0?s.sort=c:a&&(s["sort-field"]=a,s["sort-direction"]=o),s}(e))}var kb=function(){function e(){gy(this,e),this.history="undefined"!==typeof window?ab():function(e){void 0===e&&(e={});var t=e,n=t.getUserConfirmation,r=t.initialEntries,i=void 0===r?["/"]:r,o=t.initialIndex,a=void 0===o?0:o,c=t.keyLength,s=void 0===c?6:c,l=eb();function u(e){Object(iz.a)(v,e),v.length=v.entries.length,l.notifyListeners(v.location,v.action)}function f(){return Math.random().toString(36).substr(2,s)}var h=cb(a,0,i.length-1),d=i.map((function(e){return Jy(e,void 0,"string"===typeof e?f():e.key||f())})),p=Qy;function z(e){var t=cb(v.index+e,0,v.entries.length-1),r=v.entries[t];l.confirmTransitionTo(r,"POP",n,(function(e){e?u({action:"POP",location:r,index:t}):u()}))}var v={length:d.length,action:"POP",location:d[h],index:h,entries:d,createHref:p,push:function(e,t){var r=Jy(e,t,f(),v.location);l.confirmTransitionTo(r,"PUSH",n,(function(e){if(e){var t=v.index+1,n=v.entries.slice(0);n.length>t?n.splice(t,n.length-t,r):n.push(r),u({action:"PUSH",location:r,index:t,entries:n})}}))},replace:function(e,t){var r=Jy(e,t,f(),v.location);l.confirmTransitionTo(r,"REPLACE",n,(function(e){e&&(v.entries[v.index]=r,u({action:"REPLACE",location:r}))}))},go:z,goBack:function(){z(-1)},goForward:function(){z(1)},canGo:function(e){var t=v.index+e;return t>=0&&t1&&void 0!==arguments[1]?arguments[1]:{},n=t.replaceUrl,r=void 0!==n&&n,i=wb(e);this.lastPushSearchString=i;var o=r?this.history.replace:this.history.push;o({search:"?".concat(i)})}},{key:"onURLStateChange",value:function(e){var t=this;this.unlisten=this.history.listen((function(n){"?".concat(t.lastPushSearchString)!==n.search&&(t.lastPushSearchString="",e(bb(fb.parse(n.search))))}))}},{key:"tearDown",value:function(){this.unlisten()}}]),e}(),xb=function(){function e(){gy(this,e),this.requestSequence=0,this.lastCompleted=0}return yy(e,[{key:"next",value:function(){return++this.requestSequence}},{key:"isOldRequest",value:function(e){return e3?r-3:0),o=3;o2&&void 0!==arguments[2]?arguments[2]:"all";this.debug&&(n=console).log.apply(n,["Search UI: Action","addFilter"].concat(Array.prototype.slice.call(arguments)));var i=this.state.filters,o=i.find((function(t){return t.field===e&&t.type===r}))||{},a=i.filter((function(t){return t.field!==e||t.type!==r}))||[],c=o.values||[],s=c.find((function(e){return Sy(e,t)}))?c:c.concat(t);this._updateSearchResults({current:1,filters:[].concat(Object(ky.a)(a),[{field:e,values:s,type:r}])})}function qb(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];this.debug&&(t=console).log.apply(t,["Search UI: Action","trackAutocompleteClickThrough"].concat(Array.prototype.slice.call(arguments)));var r=this.state,i=r.autocompletedResultsRequestId,o=r.searchTerm,a=r.autocompletedResults,c=a.findIndex((function(t){return t._meta.id===e})),s=a[c];this.events.autocompleteResultClick({query:o,documentId:e,requestId:i,tags:n,result:s,resultIndex:c})}function Sb(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.debug&&(e=console).log.apply(e,["Search UI: Action","clearFilters"].concat(Array.prototype.slice.call(arguments)));var n=this.state.filters.filter((function(e){var n=e.field;return t.includes(n)}));this._updateSearchResults({current:1,filters:n})}function Ob(e,t,n){var r;this.debug&&(r=console).log.apply(r,["Search UI: Action","removeFilter"].concat(Array.prototype.slice.call(arguments)));var i=this.state.filters,o=i;o=!t&&n?i.filter((function(t){return!(t.field===e&&t.type===n)})):t?Cy(i,e,t,n):i.filter((function(t){return t.field!==e})),this._updateSearchResults({current:1,filters:o})}function Tb(){var e;this.debug&&(e=console).log.apply(e,["Search UI: Action","reset"].concat(Array.prototype.slice.call(arguments))),this._setState(this.startingState),this.trackUrlState&&this.URLManager.pushStateToURL(this.state)}function Eb(e){var t;this.debug&&(t=console).log.apply(t,["Search UI: Action","setCurrent"].concat(Array.prototype.slice.call(arguments))),this._updateSearchResults({current:e})}function Ab(e,t){var n,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"all";this.debug&&(n=console).log.apply(n,["Search UI: Action","setFilter"].concat(Array.prototype.slice.call(arguments)));var i=this.state.filters;i=i.filter((function(t){return t.field!==e||t.type!==r})),this._updateSearchResults({current:1,filters:[].concat(Object(ky.a)(i),[{field:e,values:[t],type:r}])})}function Hb(e){var t;this.debug&&(t=console).log.apply(t,["Search UI: Action","setResultsPerPage"].concat(Array.prototype.slice.call(arguments))),this._updateSearchResults({current:1,resultsPerPage:e})}function Lb(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=n.autocompleteMinimumCharacters,i=void 0===r?0:r,o=n.autocompleteResults,a=void 0!==o&&o,c=n.autocompleteSuggestions,s=void 0!==c&&c,l=n.shouldClearFilters,u=void 0===l||l,f=n.refresh,h=void 0===f||f,d=n.debounce,p=void 0===d?0:d;this.debug&&(t=console).log.apply(t,["Search UI: Action","setSearchTerm"].concat(Array.prototype.slice.call(arguments))),this._setState({searchTerm:e}),h&&this.debounceManager.runWithDebounce(p,"_updateSearchResults",this._updateSearchResults,Object.assign({current:1},u&&{filters:[]})),(a||s)&&e.length>=i&&this.debounceManager.runWithDebounce(p,"_updateAutocomplete",this._updateAutocomplete,e,{autocompleteResults:a,autocompleteSuggestions:s})}function Db(e,t){var n;this.debug&&(n=console).log.apply(n,["Search UI: Action","setSort"].concat(Array.prototype.slice.call(arguments)));var r={current:1,sortList:null,sortField:null,sortDirection:null};Array.isArray(e)?r.sortList=e:(r.sortField=e,r.sortDirection=t),this._updateSearchResults(r)}function Pb(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];this.debug&&(t=console).log.apply(t,["Search UI: Action","trackClickThrough"].concat(Array.prototype.slice.call(arguments)));var r=this.state,i=r.requestId,o=r.searchTerm,a=r.results,c=r.current,s=r.resultsPerPage,l=a.findIndex((function(t){return t._meta.id===e})),u=a[l];this.events.resultClick({query:o,documentId:e,requestId:i,tags:n,result:u,page:c,resultsPerPage:s,resultIndexOnPage:l})}var Vb="search-ui-screen-reader-notifications",Nb="undefined"!==typeof document,Ib=function(){if(!Nb)return null;var e=document.getElementById(Vb);return e||((e=document.createElement("div")).id=Vb,e.setAttribute("role","status"),e.setAttribute("aria-live","polite"),e.style.position="absolute",e.style.width="1px",e.style.height="1px",e.style.margin="-1px",e.style.padding="0",e.style.border="0",e.style.overflow="hidden",e.style.clip="rect(0 0 0 0)",document.body.appendChild(e),e)},Rb=function(e){var t=Ib();t&&(t.textContent=e)},Bb={searchResults:function(e){var t=e.start,n=e.end,r=e.totalResults,i=e.searchTerm,o="Showing ".concat(t," to ").concat(n," results out of ").concat(r);return i&&(o+=', searching for "'.concat(i,'".')),o}};function Fb(e,t){if(this.hasA11yNotifications){var n=this.a11yNotificationMessages[e];if(n){var r=n(t);Rb(r),this.debug&&console.log("Search UI: Action","a11yNotify",{messageFunc:e,messageArgs:t,message:r})}else{var i='Could not find corresponding message function in a11yNotificationMessages: "'.concat(e,'"');console.warn("Action","a11yNotify",i)}}}function Ub(e,t,n){if(n){if(t){var r=t[e].bind(t);return function(){for(var e=arguments.length,t=new Array(e),i=0;i0&&void 0!==arguments[0]?arguments[0]:{},n=t.apiConnector,r=t.onSearch,i=t.onAutocomplete,o=t.onResultClick,a=t.onAutocompleteResultClick;gy(this,e),this.search=Ub("onSearch",n,r),this.autocomplete=Ub("onAutocomplete",n,i),this.resultClick=Ub("onResultClick",n,o),this.autocompleteResultClick=Ub("onAutocompleteResultClick",n,a)},Gb="Invalid credentials",Yb=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"===typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];return Object.entries(e).reduce((function(e,r){var i=Object(by.a)(r,2),o=i[0],a=i[1];return t[o]&&"function"===typeof t[o]&&!t[o]({filters:n})?e:(e[o]=a,e)}),{})}var Kb=function(){function e(t){var n,r=this,i=t.apiConnector,a=t.autocompleteQuery,c=void 0===a?{}:a,s=t.debug,l=t.initialState,u=t.onSearch,f=t.onAutocomplete,h=t.onResultClick,d=t.onAutocompleteResultClick,p=t.searchQuery,z=void 0===p?{}:p,v=t.trackUrlState,g=void 0===v||v,m=t.urlPushDebounceLength,y=void 0===m?500:m,b=t.hasA11yNotifications,w=void 0!==b&&b,k=t.a11yNotificationMessages,x=void 0===k?{}:k,j=t.alwaysSearchOnInitialLoad,M=void 0!==j&&j;gy(this,e),this.state=$b,this._updateAutocomplete=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.autocompleteResults,i=t.autocompleteSuggestions,o=r.autocompleteRequestSequencer.next(),a=Object.assign(Object.assign({},n&&{results:r.autocompleteQuery.results||{}}),i&&{suggestions:r.autocompleteQuery.suggestions||{}});return r.events.autocomplete({searchTerm:e},a).then((function(e){r.autocompleteRequestSequencer.isOldRequest(o)||(r.autocompleteRequestSequencer.completed(o),r._setState(e))}))},this._updateSearchResults=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.skipPushToUrl,i=void 0!==n&&n,o=t.replaceUrl,a=void 0!==o&&o,c=Object.assign(Object.assign({},r.state),e),s=c.current,l=c.filters,u=c.resultsPerPage,f=c.searchTerm,h=c.sortDirection,d=c.sortField,p=c.sortList;r.debounceManager.cancelByName("_updateSearchResults"),r._setState({current:s,error:"",filters:l,resultsPerPage:u,searchTerm:f,sortDirection:h,sortField:d,sortList:p}),r._makeSearchRequest({skipPushToUrl:i,replaceUrl:a})},this._makeSearchRequest=_b.debounce(0,(function(e){var t=e.skipPushToUrl,n=e.replaceUrl,i=r.state,o=i.current,a=i.filters,c=i.resultsPerPage,s=i.searchTerm,l=i.sortDirection,u=i.sortField,f=i.sortList;r._setState({isLoading:!0});var h=r.searchRequestSequencer.next(),d=r.searchQuery,p=(d.filters,d.conditionalFacets),z=Yb(d,["filters","conditionalFacets"]),v=Object.assign(Object.assign({},z),{facets:Xb(r.searchQuery.facets,p,a)}),g=Object.assign(Object.assign({},Zb(r.state)),{filters:Oy(a,r.searchQuery.filters)});return r.events.search(g,v).then((function(e){if(!r.searchRequestSequencer.isOldRequest(h)){r.searchRequestSequencer.completed(h);var i=e.totalResults,d=0===i?0:(o-1)*c+1,p=i0||this.alwaysSearchOnInitialLoad)&&this._updateSearchResults(_,{replaceUrl:!0})}return yy(e,[{key:"_setState",value:function(e){var t=Object.assign(Object.assign({},this.state),e);this.debug&&console.log("Search UI: State Update",e,t),this.state=t,this.subscriptions.forEach((function(e){return e(t)}))}},{key:"setSearchQuery",value:function(e){this.searchQuery=e,this._updateSearchResults({})}},{key:"setAutocompleteQuery",value:function(e){this.autocompleteQuery=e}},{key:"subscribeToStateChanges",value:function(e){this.subscriptions.push(e)}},{key:"unsubscribeToStateChanges",value:function(e){this.subscriptions=this.subscriptions.filter((function(t){return t!==e}))}},{key:"tearDown",value:function(){this.subscriptions=[],this.URLManager&&this.URLManager.tearDown()}},{key:"getActions",value:function(){return this.actions}},{key:"getState",value:function(){return Object.assign({},this.state)}}]),e}(),Qb=c.a.createContext(null),Jb={moreFilters:function(e){var t=e.visibleOptionsCount,n=e.showingAll?"All ":"";return n+="".concat(t," options shown.")}},ew=function(e){var t=e.children,n=e.config,r=e.driver,i=Object(a.useState)(null),o=Object(by.a)(i,2),s=o[0],l=o[1];if(Object(a.useEffect)((function(){var e=r||new Kb(Object.assign(Object.assign({},n),{a11yNotificationMessages:Object.assign(Object.assign({},Jb),n.a11yNotificationMessages)}));return l(e),function(){e.tearDown()}}),[]),Object(a.useEffect)((function(){s&&s.setSearchQuery(n.searchQuery)}),[n.searchQuery]),Object(a.useEffect)((function(){s&&s.setAutocompleteQuery(n.autocompleteQuery)}),[n.autocompleteQuery]),!s)return null;var u={driver:s};return c.a.createElement(Qb.Provider,{value:u},t)};function tw(e){return(tw="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function nw(e){return(nw="function"===typeof Symbol&&"symbol"===tw(Symbol.iterator)?function(e){return tw(e)}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":tw(e)})(e)}function rw(e,t){return!t||"object"!==nw(t)&&"function"!==typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function iw(e){return(iw=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function ow(e,t){return(ow=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}var aw=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"===typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);i0&&i[i.length-1])&&(6===o[0]||2===o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]1&&e}));return e[n]=e[n]||[],e[n].push(t),e}),{})},qw=function(){return(qw=Object.assign||function(e){for(var t,n=1,r=arguments.length;n * {\n min-width: 160px;\n max-width: 100%;\n }\n"],["\n width: 100%;\n\n .tabs > * {\n min-width: 160px;\n max-width: 100%;\n }\n"]))),Nw=["learn","community"],Iw={learn:"learn.netdata",community:"discourse","github-cloud":"netdata-cloud","github-agent":"netdata"},Rw={learn:"Documentation",community:"Community","github-cloud":"Github / Cloud","github-agent":"Github / Agent"},Bw=function(e){var t=e.results;return c.a.createElement(Af,{overflow:{vertical:"auto"},"data-testid":"searchResults",flex:!0,width:"1000px",height:"60vh"},c.a.createElement(Vw,null,Nw.map((function(e){var n=t[Iw[e]],r=null===n||void 0===n?void 0:n.length;return c.a.createElement(rv,{key:e,label:Rw[e]+(r?" ("+r+")":"")},c.a.createElement(Pw,null,r?n.map((function(e){var t=e.id,n=e.url,r=e.title,i=e.description;return c.a.createElement(Dw,{key:t.raw,url:n.raw,title:r,description:i})})):c.a.createElement(Af,{padding:[4]},c.a.createElement(wh,{strong:!0},"No results"))))}))))},Fw=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},Uw=Object(s.d)(Af).attrs({padding:[6],background:"dropdown",gap:6,column:!0,round:!0,overflow:{vertical:"auto"}}).withConfig({displayName:"documentation__Container",componentId:"sc-3qq6g2-0"})(hw||(hw=Fw(["\n box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n"],["\n box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n"]))),Ww=function(e){var t=e.children,n=e.onClose;return c.a.createElement(Af,{width:"100%",alignItems:"center",justifyContent:"between",padding:[0,0,4],border:{side:"bottom",color:"disabled"}},c.a.createElement(Af,{gap:2,alignItems:"center"},t),c.a.createElement(Df,{icon:"x",neutral:!0,small:!0,onClick:n,flavour:"borderless","data-testid":"documentation-help-close"}))},Gw="general",Yw="dashboard",Zw="search",$w={general:"Need help?",dashboard:"Need help?"},Xw=function(e){var t=e.app,n=void 0===t?"cloud":t,r=e.onCloseClick,i=e.onVisitDocumentClick,o=e.onOpenIssueClick,s=e.onOpenBugClick,l=e.onContributeClick,u=e.onSupportClick,f=e.children,h=Qm(),d=h[0],p=h[1],z=Object(a.useState)(Gw),v=z[0],g=z[1],m=v===Gw,y=Object(a.useCallback)((function(){return g(Yw)}),[]),b=Object(a.useCallback)((function(){return g(Gw)}),[]),w=Object(a.useCallback)((function(){return g(Zw)}),[]),k=Object(a.useCallback)((function(){p(),r&&r()}),[]);return c.a.createElement(a.Fragment,null,f(p,d),d&&c.a.createElement(Bm,{position:"bottom-left",backdrop:!0,margin:[5,17],onClickOutside:p,onEsc:p},c.a.createElement(Tw,null,(function(e){var t=e.searchTerm,r=e.setSearchTerm,f=e.results,h=e.reset;return c.a.createElement(a.Fragment,null,c.a.createElement(Uw,{width:{max:m?"325px":v===Yw?"600px":"100%"},"data-testid":"documentation-layer"},c.a.createElement(Ww,{onClose:k},m&&c.a.createElement(Au,{color:"text",name:"questionFilled",width:"18px",height:"18px"}),!m&&c.a.createElement(Df,{icon:"arrow_left",neutral:!0,small:!0,onClick:function(){b(),h()},flavour:"borderless","data-testid":"dashboard-back"}),c.a.createElement(vh,{margin:[0]},$w[v]||$w.general)),v!==Yw&&c.a.createElement(Ew,{defaultValue:t,setSearchTerm:r,setSearchView:w}),m&&c.a.createElement(Af,{gap:6,overflow:{vertical:"auto"},column:!0,padding:[1]},c.a.createElement(iy,{app:n,onDashboardClick:y,onVisitDocumentClick:i,onOpenIssueClick:o,onOpenBugClick:s,onContributeClick:l,onSupportClick:u})),v===Yw&&c.a.createElement(py,null),v===Zw&&c.a.createElement(Bw,{results:f})))}))))},Kw=n(268),Qw="object"===typeof window,Jw=(n.n(Kw).a,function(){}),ek=function(e,t,n){if(!Qw)return[t,Jw,Jw];if(!e)throw new Error("useLocalStorage key may not be falsy");var r=n?n.raw?function(e){return e}:n.deserializer:JSON.parse,i=Object(a.useState)((function(){try{var o=n?n.raw?String:n.serializer:JSON.stringify,a=localStorage.getItem(e);return null!==a?r(a):(t&&localStorage.setItem(e,o(t)),t)}catch(i){return t}})),o=i[0],c=i[1],s=Object(a.useCallback)((function(t){try{var a="function"===typeof t?t(o):t;if("undefined"===typeof a)return;var s=void 0;s=n?n.raw?"string"===typeof a?a:JSON.stringify(a):n.serializer?n.serializer(a):JSON.stringify(a):JSON.stringify(a),localStorage.setItem(e,s),c(r(s))}catch(i){}}),[e,c]),l=Object(a.useCallback)((function(){try{localStorage.removeItem(e),c(void 0)}catch(i){}}),[e,c]);return[o,s,l]},tk=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},nk=Object(s.d)(Af).attrs({overflow:{vertical:"auto"},padding:[0,4,0,0]}).withConfig({displayName:"container__Container",componentId:"sc-1v3y9uu-0"})(dw||(dw=tk(["\n ","\n"],["\n ","\n"])),vd),rk=function(e){var t=e.onClose;return c.a.createElement(Af,{border:{side:"bottom",color:"selected"},justifyContent:"between",alignItems:"center",padding:[0,0,4,0]},c.a.createElement(Af,{gap:2},c.a.createElement(Au,{color:"text",name:"insights"}),c.a.createElement(kh,{strong:!0},"Netdata News")),c.a.createElement(Df,{flavour:"borderless",neutral:!0,icon:"x",title:"close news",onClick:t}))},ik=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},ok=Object(s.d)(Af).attrs({as:"img"}).withConfig({displayName:"image__Image",componentId:"sc-1l0yjz3-0"})(pw||(pw=ik(["\n object-fit: cover;\n"],["\n object-fit: cover;\n"]))),ak=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},ck=Object(s.d)(Af).attrs({as:"a"}).withConfig({displayName:"anchor__Anchor",componentId:"sc-5t4sos-0"})(zw||(zw=ak(["\n text-decoration: none;\n & :hover {\n text-decoration: none;\n }\n"],["\n text-decoration: none;\n & :hover {\n text-decoration: none;\n }\n"]))),sk=function(e){var t=e.item,n=t.last_publication_date,r=t.data,i=r.title,o=r.description,a=r.url,s=r.image,l=r.label,u=s&&s.url,f=new Date(n);return c.a.createElement(Af,{column:!0,gap:2},c.a.createElement(Af,{gap:4},u&&c.a.createElement(ok,{src:u,width:"160px"}),c.a.createElement(Af,{column:!0,gap:2},c.a.createElement(wh,{strong:!0},i),c.a.createElement(wh,null,o))),c.a.createElement(Af,{justifyContent:"between",alignItems:"center"},c.a.createElement(bh,null,f.toLocaleDateString()),c.a.createElement(ck,{href:a,target:"_blank",rel:"noopener noreferrer",gap:1,alignItems:"center"},c.a.createElement(wh,{color:"success",strong:!0},l),c.a.createElement(Au,{color:"success",rotate:2,name:"arrow_left"}))))},lk=n(267),uk=function(e,t){return(uk=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function fk(e,t){function n(){this.constructor=e}uk(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var hk=function(){return(hk=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0?this.running[0]:null},e.prototype.refFromCookie=function(e){if(!e||""===e.trim())return null;var t=e.trim().split(" ");if(t.length<2)return null;var n=t[0],r=parseInt(t[1],10),i=this.running.filter((function(e){return e.googleId()===n&&e.variations.length>r}))[0];return i?i.variations[r].ref():null},e}(),vk=function(){function e(e,t){for(var n in this.httpClient=t,this.form=e,this.data={},e.fields)e.fields[n].default&&(this.data[n]=[e.fields[n].default])}return e.prototype.set=function(e,t){var n=this.form.fields[e];if(!n)throw new Error("Unknown field "+e);var r=""===t||void 0===t?null:t,i=this.data[e]||[];i=n.multiple?r?i.concat([r]):i:r?[r]:i,this.data[e]=i},e.prototype.url=function(){var e=this.form.action;if(this.data){var t=e.indexOf("?")>-1?"&":"?";for(var n in this.data)if(Object.prototype.hasOwnProperty.call(this.data,n)){var r=this.data[n];if(r)for(var i=0;i0&&(this.url+=function(e){return e.indexOf("?")>-1?"&":"?"}(e)+n.join("&")),this.apiDataTTL=this.options.apiDataTTL||5,this.httpClient=new ox(this.options.requestHandler,this.options.apiCache,this.options.proxyAgent,this.options.timeoutInMs)}return e.prototype.get=function(e){var t=this;return this.httpClient.cachedRequest(this.url,{ttl:this.apiDataTTL}).then((function(n){var r=new tx(n,t.httpClient,t.options);return e&&e(null,r),r})).catch((function(t){throw e&&e(t),t}))},e}(),cx=function(){function e(e,t){this.id=e,this.api=t,this.fields={}}return e.prototype.set=function(e,t){return this.fields[e]=t,this},e.prototype.ref=function(e){return this.set("ref",e)},e.prototype.query=function(e){return this.set("q",e)},e.prototype.pageSize=function(e){return this.set("pageSize",e)},e.prototype.graphQuery=function(e){return this.set("graphQuery",e)},e.prototype.lang=function(e){return this.set("lang",e)},e.prototype.page=function(e){return this.set("page",e)},e.prototype.after=function(e){return this.set("after",e)},e.prototype.orderings=function(e){return this.set("orderings",e)},e.prototype.url=function(){var t=this;return this.api.get().then((function(n){return e.toSearchForm(t,n).url()}))},e.prototype.submit=function(t){var n=this;return this.api.get().then((function(r){return e.toSearchForm(n,r).submit(t)}))},e.toSearchForm=function(e,t){var n=t.searchForm(e.id);if(n)return Object.keys(e.fields).reduce((function(t,n){var r=e.fields[n];return"q"===n?t.query(r):"pageSize"===n?t.pageSize(r):"graphQuery"===n?t.graphQuery(r):"lang"===n?t.lang(r):"page"===n?t.page(r):"after"===n?t.after(r):"orderings"===n?t.orderings(r):t.set(n,r)}),n);throw new Error("Unable to access to form "+e.id)},e}(),sx=function(){function e(e,t){this.api=new ax(e,t)}return e.prototype.getApi=function(){return this.api.get()},e.prototype.everything=function(){return this.form("everything")},e.prototype.form=function(e){return new cx(e,this.api)},e.prototype.query=function(e,t,n){return this.getApi().then((function(r){return r.query(e,t,n)}))},e.prototype.queryFirst=function(e,t,n){return this.getApi().then((function(r){return r.queryFirst(e,t,n)}))},e.prototype.getByID=function(e,t,n){return this.getApi().then((function(r){return r.getByID(e,t,n)}))},e.prototype.getByIDs=function(e,t,n){return this.getApi().then((function(r){return r.getByIDs(e,t,n)}))},e.prototype.getByUID=function(e,t,n,r){return this.getApi().then((function(i){return i.getByUID(e,t,n,r)}))},e.prototype.getSingle=function(e,t,n){return this.getApi().then((function(r){return r.getSingle(e,t,n)}))},e.prototype.getBookmark=function(e,t,n){return this.getApi().then((function(r){return r.getBookmark(e,t,n)}))},e.prototype.getTags=function(){return this.getApi().then((function(e){return e.getTags()}))},e.prototype.getPreviewResolver=function(e,t){var n=this;return ex(e,t,(function(e,t){return n.getApi().then((function(n){return n.getByID(e,t)}))}))},e.getApi=function(e,t){return new ax(e,t).get()},e}();function lx(e,t){return sx.getApi(e,t)}var ux={experimentCookie:"io.prismic.experiment",previewCookie:"io.prismic.preview",Predicates:Kk,predicates:Kk,Experiments:zk,Api:ax,client:function(e,t){return new sx(e,t)},getApi:lx,api:function(e,t){return lx(e,t)}},fx=ux.client("https://netdata-news.cdn.prismic.io/api/v2"),hx=[],dx=function(e){var t=e.app,n=void 0===t?"cloud":t,r=e.onCloseClick,i=e.children,o=ek("news_last_seen"),s=o[0],l=o[1],u=Object(a.useState)(hx),f=u[0],h=u[1],d=Object(a.useState)(),p=d[0],z=d[1],v=Qm(),g=v[0],m=v[1];Object(a.useEffect)((function(){!function(e,t,n){fx.query(ux.Predicates.at("document.tags",[e]),{pageSize:100,orderings:"[document.last_publication_date desc]"}).then(t).catch(n)}(n,(function(e){var t=e.results;return h(t)}),(function(){return z(!0)}))}),[]);var y=Object(a.useMemo)((function(){if(!f.length)return!0;var e=f[0].last_publication_date;return new Date(s)>=new Date(e)}),[s,f]),b=Object(a.useCallback)((function(){m(),l(new Date),r&&r()}),[r]);return c.a.createElement(a.Fragment,null,i({toggle:m,isOpen:g,upToDate:y}),g&&c.a.createElement(Bm,{backdrop:!0,onClickOutside:b,onEsc:b},c.a.createElement(Af,{background:"dropdown",round:!0,padding:[6],width:"640px",height:{max:"640px"},gap:4,column:!0},c.a.createElement(rk,{onClose:b}),c.a.createElement(nk,{column:!0,gap:6},p&&c.a.createElement(bh,{textAlign:"center"},"Something went wrong \ud83d\ude14"),!p&&!f.length&&c.a.createElement(bh,{textAlign:"center"},"There are no latest news"),!p&&f.length>0&&f.map((function(e){return c.a.createElement(sk,{key:e.id,item:e})}))))))},px=function(){return(px=Object.assign||function(e){for(var t,n=1,r=arguments.length;n=0||(i[n]=e[n]);return i}var Dx=n(214),Px=n.n(Dx),Vx=n(269),Nx=["getDisplayName","methodName","renderCountProp","shouldHandleStateChanges","storeKey","withRef","forwardRef","context"],Ix=["reactReduxForwardedRef"],Rx=[],Bx=[null,null];function Fx(e,t){var n=e[1];return[t.payload,n+1]}function Ux(e,t,n){Ax((function(){return e.apply(void 0,t)}),n)}function Wx(e,t,n,r,i,o,a){e.current=r,t.current=i,n.current=!1,o.current&&(o.current=null,a())}function Gx(e,t,n,r,i,o,a,c,s,l){if(e){var u=!1,f=null,h=function(){if(!u){var e,n,h=t.getState();try{e=r(h,i.current)}catch(d){n=d,f=d}n||(f=null),e===o.current?a.current||s():(o.current=e,c.current=e,a.current=!0,l({type:"STORE_UPDATED",payload:{error:n}}))}};n.onStateChange=h,n.trySubscribe(),h();return function(){if(u=!0,n.tryUnsubscribe(),n.onStateChange=null,f)throw f}}}var Yx=function(){return[null,0]};function Zx(e,t){void 0===t&&(t={});var n=t,r=n.getDisplayName,i=void 0===r?function(e){return"ConnectAdvanced("+e+")"}:r,o=n.methodName,s=void 0===o?"connectAdvanced":o,l=n.renderCountProp,u=void 0===l?void 0:l,f=n.shouldHandleStateChanges,h=void 0===f||f,d=n.storeKey,p=void 0===d?"store":d,z=(n.withRef,n.forwardRef),v=void 0!==z&&z,g=n.context,m=void 0===g?qx:g,y=Lx(n,Nx),b=m;return function(t){var n=t.displayName||t.name||"Component",r=i(n),o=_x({},y,{getDisplayName:i,methodName:s,renderCountProp:u,shouldHandleStateChanges:h,storeKey:p,displayName:r,wrappedComponentName:n,WrappedComponent:t}),l=y.pure;var f=l?a.useMemo:function(e){return e()};function d(n){var r=Object(a.useMemo)((function(){var e=n.reactReduxForwardedRef,t=Lx(n,Ix);return[n.context,e,t]}),[n]),i=r[0],s=r[1],l=r[2],u=Object(a.useMemo)((function(){return i&&i.Consumer&&Object(Vx.isContextConsumer)(c.a.createElement(i.Consumer,null))?i:b}),[i,b]),d=Object(a.useContext)(u),p=Boolean(n.store)&&Boolean(n.store.getState)&&Boolean(n.store.dispatch);Boolean(d)&&Boolean(d.store);var z=p?n.store:d.store,v=Object(a.useMemo)((function(){return function(t){return e(t.dispatch,o)}(z)}),[z]),g=Object(a.useMemo)((function(){if(!h)return Bx;var e=Ex(z,p?null:d.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]}),[z,p,d]),m=g[0],y=g[1],w=Object(a.useMemo)((function(){return p?d:_x({},d,{subscription:m})}),[p,d,m]),k=Object(a.useReducer)(Fx,Rx,Yx),x=k[0][0],j=k[1];if(x&&x.error)throw x.error;var M=Object(a.useRef)(),_=Object(a.useRef)(l),C=Object(a.useRef)(),q=Object(a.useRef)(!1),S=f((function(){return C.current&&l===_.current?C.current:v(z.getState(),l)}),[z,x,l]);Ux(Wx,[_,M,q,l,S,C,y]),Ux(Gx,[h,z,m,v,_,M,q,C,y,j],[z,m,v]);var O=Object(a.useMemo)((function(){return c.a.createElement(t,_x({},S,{ref:s}))}),[s,t,S]);return Object(a.useMemo)((function(){return h?c.a.createElement(u.Provider,{value:w},O):O}),[u,O,w])}var z=l?c.a.memo(d):d;if(z.WrappedComponent=t,z.displayName=d.displayName=r,v){var g=c.a.forwardRef((function(e,t){return c.a.createElement(z,_x({},e,{reactReduxForwardedRef:t}))}));return g.displayName=r,g.WrappedComponent=t,Px()(g,t)}return Px()(z,t)}}function $x(e,t){return e===t?0!==e||0!==t||1/e===1/t:e!==e&&t!==t}function Xx(e,t){if($x(e,t))return!0;if("object"!==typeof e||null===e||"object"!==typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(var i=0;i=0;r--){var i=t[r](e);if(i)return i}return function(t,r){throw new Error("Invalid value of type "+typeof e+" for "+n+" argument when connecting component "+r.wrappedComponentName+".")}}function uj(e,t){return e===t}function fj(e){var t=void 0===e?{}:e,n=t.connectHOC,r=void 0===n?Zx:n,i=t.mapStateToPropsFactories,o=void 0===i?tj:i,a=t.mapDispatchToPropsFactories,c=void 0===a?ej:a,s=t.mergePropsFactories,l=void 0===s?rj:s,u=t.selectorFactory,f=void 0===u?cj:u;return function(e,t,n,i){void 0===i&&(i={});var a=i,s=a.pure,u=void 0===s||s,h=a.areStatesEqual,d=void 0===h?uj:h,p=a.areOwnPropsEqual,z=void 0===p?Xx:p,v=a.areStatePropsEqual,g=void 0===v?Xx:v,m=a.areMergedPropsEqual,y=void 0===m?Xx:m,b=Lx(a,sj),w=lj(e,o,"mapStateToProps"),k=lj(t,c,"mapDispatchToProps"),x=lj(n,l,"mergeProps");return r(f,_x({methodName:"connect",getDisplayName:function(e){return"Connect("+e+")"},shouldHandleStateChanges:Boolean(e),initMapStateToProps:w,initMapDispatchToProps:k,initMergeProps:x,pure:u,areStatesEqual:d,areOwnPropsEqual:z,areStatePropsEqual:g,areMergedPropsEqual:y},b))}}var hj=fj();var dj;function pj(e,t){var n=Object(a.useState)((function(){return{inputs:t,result:e()}}))[0],r=Object(a.useRef)(!0),i=Object(a.useRef)(n),o=r.current||Boolean(t&&i.current.inputs&&function(e,t){if(e.length!==t.length)return!1;for(var n=0;n");return t.callbacks},t.setCallbacks=function(e){t.callbacks=e},t}Mx(t,e);var n=t.prototype;return n.componentDidMount=function(){this.unbind=Pj(window,[{eventName:"error",fn:this.onWindowError}])},n.componentDidCatch=function(e){if(!(e instanceof Ij))throw e;this.setState({})},n.componentWillUnmount=function(){this.unbind()},n.render=function(){return this.props.children(this.setCallbacks)},t}(c.a.Component),Fj=function(e){return e+1},Uj=function(e,t){var n=e.droppableId===t.droppableId,r=Fj(e.index),i=Fj(t.index);return n?"\n You have moved the item from position "+r+"\n to position "+i+"\n ":"\n You have moved the item from position "+r+"\n in list "+e.droppableId+"\n to list "+t.droppableId+"\n in position "+i+"\n "},Wj=function(e,t,n){return t.droppableId===n.droppableId?"\n The item "+e+"\n has been combined with "+n.draggableId:"\n The item "+e+"\n in list "+t.droppableId+"\n has been combined with "+n.draggableId+"\n in list "+n.droppableId+"\n "},Gj=function(e){return"\n The item has returned to its starting position\n of "+Fj(e.index)+"\n"},Yj={dragHandleUsageInstructions:"\n Press space bar to start a drag.\n When dragging you can use the arrow keys to move the item around and escape to cancel.\n Some screen readers may require you to be in focus mode or to use your pass through key\n",onDragStart:function(e){return"\n You have lifted an item in position "+Fj(e.source.index)+"\n"},onDragUpdate:function(e){var t=e.destination;if(t)return Uj(e.source,t);var n=e.combine;return n?Wj(e.draggableId,e.source,n):"You are over an area that cannot be dropped on"},onDragEnd:function(e){if("CANCEL"===e.reason)return"\n Movement cancelled.\n "+Gj(e.source)+"\n ";var t=e.destination,n=e.combine;return t?"\n You have dropped the item.\n "+Uj(e.source,t)+"\n ":n?"\n You have dropped the item.\n "+Wj(e.draggableId,e.source,n)+"\n ":"\n The item has been dropped while not over a drop area.\n "+Gj(e.source)+"\n "}},Zj={x:0,y:0},$j=function(e,t){return{x:e.x+t.x,y:e.y+t.y}},Xj=function(e,t){return{x:e.x-t.x,y:e.y-t.y}},Kj=function(e,t){return e.x===t.x&&e.y===t.y},Qj=function(e){return{x:0!==e.x?-e.x:0,y:0!==e.y?-e.y:0}},Jj=function(e,t,n){var r;return void 0===n&&(n=0),(r={})[e]=t,r["x"===e?"y":"x"]=n,r},eM=function(e,t){return Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))},tM=function(e,t){return Math.min.apply(Math,t.map((function(t){return eM(e,t)})))},nM=function(e){return function(t){return{x:e(t.x),y:e(t.y)}}},rM=function(e,t){return{top:e.top+t.y,left:e.left+t.x,bottom:e.bottom+t.y,right:e.right+t.x}},iM=function(e){return[{x:e.left,y:e.top},{x:e.right,y:e.top},{x:e.left,y:e.bottom},{x:e.right,y:e.bottom}]},oM=function(e,t){return t&&t.shouldClipSubject?function(e,t){var n=gj({top:Math.max(t.top,e.top),right:Math.min(t.right,e.right),bottom:Math.min(t.bottom,e.bottom),left:Math.max(t.left,e.left)});return n.width<=0||n.height<=0?null:n}(t.pageMarginBox,e):gj(e)},aM=function(e){var t=e.page,n=e.withPlaceholder,r=e.axis,i=e.frame,o=function(e,t,n){var r;return n&&n.increasedBy?_x({},e,((r={})[t.end]=e[t.end]+n.increasedBy[t.line],r)):e}(function(e,t){return t?rM(e,t.scroll.diff.displacement):e}(t.marginBox,i),r,n);return{page:t,withPlaceholder:n,active:oM(o,i)}},cM=function(e,t){e.frame||Rj(!1);var n=e.frame,r=Xj(t,n.scroll.initial),i=Qj(r),o=_x({},n,{scroll:{initial:n.scroll.initial,current:t,diff:{value:r,displacement:i},max:n.scroll.max}});return _x({},e,{frame:o,subject:aM({page:e.subject.page,withPlaceholder:e.subject.withPlaceholder,axis:e.axis,frame:o})})};function sM(e){return Object.values?Object.values(e):Object.keys(e).map((function(t){return e[t]}))}function lM(e,t){if(e.findIndex)return e.findIndex(t);for(var n=0;ne.bottom,c=r.lefte.right;return!(!a||!c)||(a&&o||c&&i)}},_M=function(e){var t=jM(e.top,e.bottom),n=jM(e.left,e.right);return function(e){return t(e.top)&&t(e.bottom)&&n(e.left)&&n(e.right)}},CM={direction:"vertical",line:"y",crossAxisLine:"x",start:"top",end:"bottom",size:"height",crossAxisStart:"left",crossAxisEnd:"right",crossAxisSize:"width"},qM={direction:"horizontal",line:"x",crossAxisLine:"y",start:"left",end:"right",size:"width",crossAxisStart:"top",crossAxisEnd:"bottom",crossAxisSize:"height"},SM=function(e){var t=e.target,n=e.destination,r=e.viewport,i=e.withDroppableDisplacement,o=e.isVisibleThroughFrameFn,a=i?function(e,t){var n=t.frame?t.frame.scroll.diff.displacement:Zj;return rM(e,n)}(t,n):t;return function(e,t,n){return!!t.subject.active&&n(t.subject.active)(e)}(a,n,o)&&function(e,t,n){return n(t)(e)}(a,r,o)},OM=function(e){return SM(_x({},e,{isVisibleThroughFrameFn:MM}))},TM=function(e){return SM(_x({},e,{isVisibleThroughFrameFn:_M}))},EM=function(e,t,n){if("boolean"===typeof n)return n;if(!t)return!0;var r=t.invisible,i=t.visible;if(r[e])return!1;var o=i[e];return!o||o.shouldAnimate};function AM(e){var t=e.afterDragging,n=e.destination,r=e.displacedBy,i=e.viewport,o=e.forceShouldAnimate,a=e.last;return t.reduce((function(e,t){var c=function(e,t){var n=e.page.marginBox,r={top:t.point.y,right:0,bottom:0,left:t.point.x};return gj(mj(n,r))}(t,r),s=t.descriptor.id;if(e.all.push(s),!OM({target:c,destination:n,viewport:i,withDroppableDisplacement:!0}))return e.invisible[t.descriptor.id]=!0,e;var l={draggableId:s,shouldAnimate:EM(s,a,o)};return e.visible[s]=l,e}),{all:[],visible:{},invisible:{}})}function HM(e){var t=e.insideDestination,n=e.inHomeList,r=e.displacedBy,i=e.destination,o=function(e,t){if(!e.length)return 0;var n=e[e.length-1].descriptor.index;return t.inHomeList?n:n+1}(t,{inHomeList:n});return{displaced:kM,displacedBy:r,at:{type:"REORDER",destination:{droppableId:i.descriptor.id,index:o}}}}function LM(e){var t=e.draggable,n=e.insideDestination,r=e.destination,i=e.viewport,o=e.displacedBy,a=e.last,c=e.index,s=e.forceShouldAnimate,l=bM(t,r);if(null==c)return HM({insideDestination:n,inHomeList:l,displacedBy:o,destination:r});var u=uM(n,(function(e){return e.descriptor.index===c}));if(!u)return HM({insideDestination:n,inHomeList:l,displacedBy:o,destination:r});var f=yM(t,n),h=n.indexOf(u);return{displaced:AM({afterDragging:f.slice(h),destination:r,displacedBy:o,last:a,viewport:i.frame,forceShouldAnimate:s}),displacedBy:o,at:{type:"REORDER",destination:{droppableId:r.descriptor.id,index:c}}}}function DM(e,t){return Boolean(t.effected[e])}var PM=function(e){var t=e.isMovingForward,n=e.isInHomeList,r=e.draggable,i=e.draggables,o=e.destination,a=e.insideDestination,c=e.previousImpact,s=e.viewport,l=e.afterCritical,u=c.at;if(u||Rj(!1),"REORDER"===u.type){var f=function(e){var t=e.isMovingForward,n=e.isInHomeList,r=e.insideDestination,i=e.location;if(!r.length)return null;var o=i.index,a=t?o+1:o-1,c=r[0].descriptor.index,s=r[r.length-1].descriptor.index;return a(n?s:s+1)?null:a}({isMovingForward:t,isInHomeList:n,location:u.destination,insideDestination:a});return null==f?null:LM({draggable:r,insideDestination:a,destination:o,viewport:s,last:c.displaced,displacedBy:c.displacedBy,index:f})}var h=function(e){var t=e.isMovingForward,n=e.destination,r=e.draggables,i=e.combine,o=e.afterCritical;if(!n.isCombineEnabled)return null;var a=i.draggableId,c=r[a].descriptor.index;return DM(a,o)?t?c:c-1:t?c+1:c}({isMovingForward:t,destination:o,displaced:c.displaced,draggables:i,combine:u.combine,afterCritical:l});return null==h?null:LM({draggable:r,insideDestination:a,destination:o,viewport:s,last:c.displaced,displacedBy:c.displacedBy,index:h})},VM=function(e){var t=e.afterCritical,n=e.impact,r=e.draggables,i=mM(n);i||Rj(!1);var o=i.draggableId,a=r[o].page.borderBox.center,c=function(e){var t=e.displaced,n=e.afterCritical,r=e.combineWith,i=e.displacedBy,o=Boolean(t.visible[r]||t.invisible[r]);return DM(r,n)?o?Zj:Qj(i.point):o?i.point:Zj}({displaced:n.displaced,afterCritical:t,combineWith:o,displacedBy:n.displacedBy});return $j(a,c)},NM=function(e,t){return t.margin[e.start]+t.borderBox[e.size]/2},IM=function(e,t,n){return t[e.crossAxisStart]+n.margin[e.crossAxisStart]+n.borderBox[e.crossAxisSize]/2},RM=function(e){var t=e.axis,n=e.moveRelativeTo,r=e.isMoving;return Jj(t.line,n.marginBox[t.end]+NM(t,r),IM(t,n.marginBox,r))},BM=function(e){var t=e.axis,n=e.moveRelativeTo,r=e.isMoving;return Jj(t.line,n.marginBox[t.start]-function(e,t){return t.margin[e.end]+t.borderBox[e.size]/2}(t,r),IM(t,n.marginBox,r))},FM=function(e){var t=e.impact,n=e.draggable,r=e.draggables,i=e.droppable,o=e.afterCritical,a=vM(i.descriptor.id,r),c=n.page,s=i.axis;if(!a.length)return function(e){var t=e.axis,n=e.moveInto,r=e.isMoving;return Jj(t.line,n.contentBox[t.start]+NM(t,r),IM(t,n.contentBox,r))}({axis:s,moveInto:i.page,isMoving:c});var l=t.displaced,u=t.displacedBy,f=l.all[0];if(f){var h=r[f];if(DM(f,o))return BM({axis:s,moveRelativeTo:h.page,isMoving:c});var d=xj(h.page,u.point);return BM({axis:s,moveRelativeTo:d,isMoving:c})}var p=a[a.length-1];if(p.descriptor.id===n.descriptor.id)return c.borderBox.center;if(DM(p.descriptor.id,o)){var z=xj(p.page,Qj(o.displacedBy.point));return RM({axis:s,moveRelativeTo:z,isMoving:c})}return RM({axis:s,moveRelativeTo:p.page,isMoving:c})},UM=function(e,t){var n=e.frame;return n?$j(t,n.scroll.diff.displacement):t},WM=function(e){var t=function(e){var t=e.impact,n=e.draggable,r=e.droppable,i=e.draggables,o=e.afterCritical,a=n.page.borderBox.center,c=t.at;return r&&c?"REORDER"===c.type?FM({impact:t,draggable:n,draggables:i,droppable:r,afterCritical:o}):VM({impact:t,draggables:i,afterCritical:o}):a}(e),n=e.droppable;return n?UM(n,t):t},GM=function(e,t){var n=Xj(t,e.scroll.initial),r=Qj(n);return{frame:gj({top:t.y,bottom:t.y+e.frame.height,left:t.x,right:t.x+e.frame.width}),scroll:{initial:e.scroll.initial,max:e.scroll.max,current:t,diff:{value:n,displacement:r}}}};function YM(e,t){return e.map((function(e){return t[e]}))}var ZM=function(e){var t=e.pageBorderBoxCenter,n=e.draggable,r=function(e,t){return $j(e.scroll.diff.displacement,t)}(e.viewport,t),i=Xj(r,n.page.borderBox.center);return $j(n.client.borderBox.center,i)},$M=function(e){var t=e.draggable,n=e.destination,r=e.newPageBorderBoxCenter,i=e.viewport,o=e.withDroppableDisplacement,a=e.onlyOnMainAxis,c=void 0!==a&&a,s=Xj(r,t.page.borderBox.center),l={target:rM(t.page.borderBox,s),destination:n,withDroppableDisplacement:o,viewport:i};return c?function(e){return SM(_x({},e,{isVisibleThroughFrameFn:(t=e.destination.axis,function(e){var n=jM(e.top,e.bottom),r=jM(e.left,e.right);return function(e){return t===CM?n(e.top)&&n(e.bottom):r(e.left)&&r(e.right)}})}));var t}(l):TM(l)},XM=function(e){var t=e.isMovingForward,n=e.draggable,r=e.destination,i=e.draggables,o=e.previousImpact,a=e.viewport,c=e.previousPageBorderBoxCenter,s=e.previousClientSelection,l=e.afterCritical;if(!r.isEnabled)return null;var u=vM(r.descriptor.id,i),f=bM(n,r),h=function(e){var t=e.isMovingForward,n=e.draggable,r=e.destination,i=e.insideDestination,o=e.previousImpact;if(!r.isCombineEnabled)return null;if(!gM(o))return null;function a(e){var t={type:"COMBINE",combine:{draggableId:e,droppableId:r.descriptor.id}};return _x({},o,{at:t})}var c=o.displaced.all,s=c.length?c[0]:null;if(t)return s?a(s):null;var l=yM(n,i);if(!s)return l.length?a(l[l.length-1].descriptor.id):null;var u=lM(l,(function(e){return e.descriptor.id===s}));-1===u&&Rj(!1);var f=u-1;return f<0?null:a(l[f].descriptor.id)}({isMovingForward:t,draggable:n,destination:r,insideDestination:u,previousImpact:o})||PM({isMovingForward:t,isInHomeList:f,draggable:n,draggables:i,destination:r,insideDestination:u,previousImpact:o,viewport:a,afterCritical:l});if(!h)return null;var d=WM({impact:h,draggable:n,droppable:r,draggables:i,afterCritical:l});if($M({draggable:n,destination:r,newPageBorderBoxCenter:d,viewport:a.frame,withDroppableDisplacement:!1,onlyOnMainAxis:!0}))return{clientSelection:ZM({pageBorderBoxCenter:d,draggable:n,viewport:a}),impact:h,scrollJumpRequest:null};var p=Xj(d,c);return{clientSelection:s,impact:function(e){var t=e.impact,n=e.viewport,r=e.destination,i=e.draggables,o=e.maxScrollChange,a=GM(n,$j(n.scroll.current,o)),c=r.frame?cM(r,$j(r.frame.scroll.current,o)):r,s=t.displaced,l=AM({afterDragging:YM(s.all,i),destination:r,displacedBy:t.displacedBy,viewport:a.frame,last:s,forceShouldAnimate:!1}),u=AM({afterDragging:YM(s.all,i),destination:c,displacedBy:t.displacedBy,viewport:n.frame,last:s,forceShouldAnimate:!1}),f={},h={},d=[s,l,u];return s.all.forEach((function(e){var t=function(e,t){for(var n=0;n1?u.sort((function(e,t){return KM(e)[c.start]-KM(t)[c.start]}))[0]:l.sort((function(e,t){var r=tM(n,iM(KM(e))),i=tM(n,iM(KM(t)));return r!==i?r-i:KM(e)[c.start]-KM(t)[c.start]}))[0]}({isMovingForward:t,pageBorderBoxCenter:n,source:i,droppables:a,viewport:c});if(!l)return null;var u=vM(l.descriptor.id,o),f=function(e){var t=e.previousPageBorderBoxCenter,n=e.moveRelativeTo,r=e.insideDestination,i=e.draggable,o=e.draggables,a=e.destination,c=e.viewport,s=e.afterCritical;if(!n){if(r.length)return null;var l={displaced:kM,displacedBy:wM,at:{type:"REORDER",destination:{droppableId:a.descriptor.id,index:0}}},u=WM({impact:l,draggable:i,droppable:a,draggables:o,afterCritical:s}),f=bM(i,a)?a:n_(a,i,o);return $M({draggable:i,destination:f,newPageBorderBoxCenter:u,viewport:c.frame,withDroppableDisplacement:!1,onlyOnMainAxis:!0})?l:null}var h=Boolean(t[a.axis.line]<=n.page.borderBox.center[a.axis.line]),d=function(){var e=n.descriptor.index;return n.descriptor.id===i.descriptor.id?e:h?e:e+1}(),p=e_(a.axis,i.displaceBy);return LM({draggable:i,insideDestination:r,destination:a,viewport:c,displacedBy:p,last:kM,index:d})}({previousPageBorderBoxCenter:n,destination:l,draggable:r,draggables:o,moveRelativeTo:function(e){var t=e.pageBorderBoxCenter,n=e.viewport,r=e.destination,i=e.insideDestination,o=e.afterCritical;return i.filter((function(e){return TM({target:JM(e,o),destination:r,viewport:n.frame,withDroppableDisplacement:!0})})).sort((function(e,n){var i=eM(t,UM(r,QM(e,o))),a=eM(t,UM(r,QM(n,o)));return ir.left&&n.topr.top))return!1;if(c_(i)(t.center))return!0;var o=e.axis,a=i.center[o.crossAxisLine],c=t[o.crossAxisStart],s=t[o.crossAxisEnd],l=jM(i[o.crossAxisStart],i[o.crossAxisEnd]),u=l(c),f=l(s);return!u&&!f||(u?ca)}));return i.length?1===i.length?i[0].descriptor.id:function(e){var t=e.pageBorderBox,n=e.draggable,r=e.candidates,i=n.page.borderBox.center,o=r.map((function(e){var n=e.axis,r=Jj(e.axis.line,t.center[n.line],e.page.borderBox.center[n.crossAxisLine]);return{id:e.descriptor.id,distance:eM(i,r)}})).sort((function(e,t){return t.distance-e.distance}));return o[0]?o[0].id:null}({pageBorderBox:t,draggable:n,candidates:i}):null}var l_=function(e,t){return gj(rM(e,t))};function u_(e){var t=e.displaced,n=e.id;return Boolean(t.visible[n]||t.invisible[n])}var f_=function(e){var t=e.pageOffset,n=e.draggable,r=e.draggables,i=e.droppables,o=e.previousImpact,a=e.viewport,c=e.afterCritical,s=l_(n.page.borderBox,t),l=s_({pageBorderBox:s,draggable:n,droppables:i});if(!l)return xM;var u=i[l],f=vM(u.descriptor.id,r),h=function(e,t){var n=e.frame;return n?l_(t,n.scroll.diff.value):t}(u,s);return function(e){var t=e.draggable,n=e.pageBorderBoxWithDroppableScroll,r=e.previousImpact,i=e.destination,o=e.insideDestination,a=e.afterCritical;if(!i.isCombineEnabled)return null;var c=i.axis,s=e_(i.axis,t.displaceBy),l=s.value,u=n[c.start],f=n[c.end],h=uM(yM(t,o),(function(e){var t=e.descriptor.id,n=e.page.borderBox,i=n[c.size]/4,o=DM(t,a),s=u_({displaced:r.displaced,id:t});return o?s?f>n[c.start]+i&&fn[c.start]-l+i&&un[c.start]+l+i&&fn[c.start]+i&&ut.descriptor.index?n.descriptor.index-1:n.descriptor.index:null}({draggable:n,closest:uM(yM(n,i),(function(e){var t=e.descriptor.id,n=e.page.borderBox.center[s.line],r=DM(t,c),i=u_({displaced:o,id:t});return r?i?h<=n:f=1500)return K_;var o=X_+Q_*(i/1500);return Number(("CANCEL"===r?.6*o:o).toFixed(2))}({current:i.current.client.offset,destination:v,reason:o});n(function(e){return{type:"DROP_ANIMATE",payload:e}}({newHomeClientOffset:v,dropDuration:m,completed:g}))}else n(N_({completed:g}))}}else n(function(e){return{type:"DROP_PENDING",payload:e}}({reason:o}))}else e(r)}}},eC=function(){return{x:window.pageXOffset,y:window.pageYOffset}};function tC(e){var t=e.onWindowScroll;var n,r=Cj((function(){t(eC())})),i=(n=r,{eventName:"scroll",options:{passive:!0,capture:!1},fn:function(e){e.target!==window&&e.target!==window.document||n()}}),o=Dj;function a(){return o!==Dj}return{start:function(){a()&&Rj(!1),o=Pj(window,[i])},stop:function(){a()||Rj(!1),r.cancel(),o(),o=Dj},isActive:a}}var nC=function(e){var t=tC({onWindowScroll:function(t){e.dispatch({type:"MOVE_BY_WINDOW_SCROLL",payload:{newScroll:t}})}});return function(e){return function(n){t.isActive()||"INITIAL_PUBLISH"!==n.type||t.start(),t.isActive()&&function(e){return"DROP_COMPLETE"===e.type||"DROP_ANIMATE"===e.type||"FLUSH"===e.type}(n)&&t.stop(),e(n)}}},rC=function(){var e=[];return{add:function(t){var n=setTimeout((function(){return function(t){var n=lM(e,(function(e){return e.timerId===t}));-1===n&&Rj(!1),e.splice(n,1)[0].callback()}(n)})),r={timerId:n,callback:t};e.push(r)},flush:function(){if(e.length){var t=[].concat(e);e.length=0,t.forEach((function(e){clearTimeout(e.timerId),e.callback()}))}}}},iC=function(e,t){y_(),t(),b_()},oC=function(e,t){return{draggableId:e.draggable.id,type:e.droppable.type,source:{droppableId:e.droppable.id,index:e.draggable.index},mode:t}},aC=function(e,t,n,r){if(e){var i=function(e){var t=!1,n=!1,r=setTimeout((function(){n=!0})),i=function(i){t||n||(t=!0,e(i),clearTimeout(r))};return i.wasCalled=function(){return t},i}(n);e(t,{announce:i}),i.wasCalled()||n(r(t))}else n(r(t))},cC=function(e,t){var n=function(e,t){var n=rC(),r=null,i=function(n){r||Rj(!1),r=null,iC(0,(function(){return aC(e().onDragEnd,n,t,Yj.onDragEnd)}))};return{beforeCapture:function(t,n){r&&Rj(!1),iC(0,(function(){var r=e().onBeforeCapture;r&&r({draggableId:t,mode:n})}))},beforeStart:function(t,n){r&&Rj(!1),iC(0,(function(){var r=e().onBeforeDragStart;r&&r(oC(t,n))}))},start:function(i,o){r&&Rj(!1);var a=oC(i,o);r={mode:o,lastCritical:i,lastLocation:a.source,lastCombine:null},n.add((function(){iC(0,(function(){return aC(e().onDragStart,a,t,Yj.onDragStart)}))}))},update:function(i,o){var a=gM(o),c=mM(o);r||Rj(!1);var s=!function(e,t){if(e===t)return!0;var n=e.draggable.id===t.draggable.id&&e.draggable.droppableId===t.draggable.droppableId&&e.draggable.type===t.draggable.type&&e.draggable.index===t.draggable.index,r=e.droppable.id===t.droppable.id&&e.droppable.type===t.droppable.type;return n&&r}(i,r.lastCritical);s&&(r.lastCritical=i);var l,u,f=(l=r.lastLocation,u=a,!(null==l&&null==u||null!=l&&null!=u&&l.droppableId===u.droppableId&&l.index===u.index));f&&(r.lastLocation=a);var h=!function(e,t){return null==e&&null==t||null!=e&&null!=t&&(e.draggableId===t.draggableId&&e.droppableId===t.droppableId)}(r.lastCombine,c);if(h&&(r.lastCombine=c),s||f||h){var d=_x({},oC(i,r.mode),{combine:c,destination:a});n.add((function(){iC(0,(function(){return aC(e().onDragUpdate,d,t,Yj.onDragUpdate)}))}))}},flush:function(){r||Rj(!1),n.flush()},drop:i,abort:function(){if(r){var e=_x({},oC(r.lastCritical,r.mode),{combine:null,destination:null,reason:"CANCEL"});i(e)}}}}(e,t);return function(e){return function(t){return function(r){if("BEFORE_INITIAL_CAPTURE"!==r.type){if("INITIAL_PUBLISH"===r.type){var i=r.payload.critical;return n.beforeStart(i,r.payload.movementMode),t(r),void n.start(i,r.payload.movementMode)}if("DROP_COMPLETE"===r.type){var o=r.payload.completed.result;return n.flush(),t(r),void n.drop(o)}if(t(r),"FLUSH"!==r.type){var a=e.getState();"DRAGGING"===a.phase&&n.update(a.critical,a.impact)}else n.abort()}else n.beforeCapture(r.payload.draggableId,r.payload.movementMode)}}}},sC=function(e){return function(t){return function(n){if("DROP_ANIMATION_FINISHED"===n.type){var r=e.getState();"DROP_ANIMATING"!==r.phase&&Rj(!1),e.dispatch(N_({completed:r.completed}))}else t(n)}}},lC=function(e){var t=null,n=null;return function(r){return function(i){if("FLUSH"!==i.type&&"DROP_COMPLETE"!==i.type&&"DROP_ANIMATION_FINISHED"!==i.type||(n&&(cancelAnimationFrame(n),n=null),t&&(t(),t=null)),r(i),"DROP_ANIMATE"===i.type){var o={eventName:"scroll",options:{capture:!0,passive:!1,once:!0},fn:function(){"DROP_ANIMATING"===e.getState().phase&&e.dispatch({type:"DROP_ANIMATION_FINISHED",payload:null})}};n=requestAnimationFrame((function(){n=null,t=Pj(window,[o])}))}}}},uC=function(e){return function(t){return function(n){if(t(n),"PUBLISH_WHILE_DRAGGING"===n.type){var r=e.getState();"DROP_PENDING"===r.phase&&(r.isWaiting||e.dispatch(I_({reason:r.reason})))}}}},fC=Cx.d,hC=function(e){var t,n=e.dimensionMarshal,r=e.focusMarshal,i=e.styleMarshal,o=e.getResponders,a=e.announce,c=e.autoScroller;return Object(Cx.e)(__,fC(Object(Cx.a)((t=i,function(){return function(e){return function(n){"INITIAL_PUBLISH"===n.type&&t.dragging(),"DROP_ANIMATE"===n.type&&t.dropping(n.payload.completed.result.reason),"FLUSH"!==n.type&&"DROP_COMPLETE"!==n.type||t.resting(),e(n)}}}),function(e){return function(){return function(t){return function(n){"DROP_COMPLETE"!==n.type&&"FLUSH"!==n.type&&"DROP_ANIMATE"!==n.type||e.stopPublishing(),t(n)}}}}(n),function(e){return function(t){var n=t.getState,r=t.dispatch;return function(t){return function(i){if("LIFT"===i.type){var o=i.payload,a=o.id,c=o.clientSelection,s=o.movementMode,l=n();"DROP_ANIMATING"===l.phase&&r(N_({completed:l.completed})),"IDLE"!==n().phase&&Rj(!1),r(V_()),r({type:"BEFORE_INITIAL_CAPTURE",payload:{draggableId:a,movementMode:s}});var u={draggableId:a,scrollOptions:{shouldPublishImmediately:"SNAP"===s}},f=e.startPublishing(u),h=f.critical,d=f.dimensions,p=f.viewport;r(function(e){return{type:"INITIAL_PUBLISH",payload:e}}({critical:h,dimensions:d,clientSelection:c,movementMode:s,viewport:p}))}else t(i)}}}}(n),J_,sC,lC,uC,function(e){return function(t){return function(n){return function(r){if(function(e){return"DROP_COMPLETE"===e.type||"DROP_ANIMATE"===e.type||"FLUSH"===e.type}(r))return e.stop(),void n(r);if("INITIAL_PUBLISH"===r.type){n(r);var i=t.getState();return"DRAGGING"!==i.phase&&Rj(!1),void e.start(i)}n(r),e.scroll(t.getState())}}}}(c),nC,function(e){var t=!1;return function(){return function(n){return function(r){if("INITIAL_PUBLISH"===r.type)return t=!0,e.tryRecordFocus(r.payload.critical.draggable.id),n(r),void e.tryRestoreFocusRecorded();if(n(r),t){if("FLUSH"===r.type)return t=!1,void e.tryRestoreFocusRecorded();if("DROP_COMPLETE"===r.type){t=!1;var i=r.payload.completed.result;i.combine&&e.tryShiftRecord(i.draggableId,i.combine.draggableId),e.tryRestoreFocusRecorded()}}}}}}(r),cC(o,a))))},dC=function(){return{additions:{},removals:{},modified:{}}};var pC=function(e){var t=e.scrollHeight,n=e.scrollWidth,r=e.height,i=e.width,o=Xj({x:n,y:t},{x:i,y:r});return{x:Math.max(0,o.x),y:Math.max(0,o.y)}},zC=function(){var e=document.documentElement;return e||Rj(!1),e},vC=function(){var e=zC();return pC({scrollHeight:e.scrollHeight,scrollWidth:e.scrollWidth,width:e.clientWidth,height:e.clientHeight})},gC=function(e){var t=e.critical,n=e.scrollOptions,r=e.registry;y_();var i=function(){var e=eC(),t=vC(),n=e.y,r=e.x,i=zC(),o=i.clientWidth,a=i.clientHeight;return{frame:gj({top:n,left:r,right:r+o,bottom:n+a}),scroll:{initial:e,current:e,max:t,diff:{value:Zj,displacement:Zj}}}}(),o=i.scroll.current,a=t.droppable,c=r.droppable.getAllByType(a.type).map((function(e){return e.callbacks.getDimensionAndWatchScroll(o,n)})),s=r.draggable.getAllByType(t.draggable.type).map((function(e){return e.getDimension(o)})),l={draggables:dM(s),droppables:hM(c)};return b_(),{dimensions:l,critical:t,viewport:i}};function mC(e,t,n){return n.descriptor.id!==t.id&&(n.descriptor.type===t.type&&"virtual"===e.droppable.getById(n.descriptor.droppableId).descriptor.mode)}var yC=function(e,t){var n=null,r=function(e){var t=e.registry,n=e.callbacks,r=dC(),i=null,o=function(){i||(n.collectionStarting(),i=requestAnimationFrame((function(){i=null,y_();var e=r,o=e.additions,a=e.removals,c=e.modified,s=Object.keys(o).map((function(e){return t.draggable.getById(e).getDimension(Zj)})).sort((function(e,t){return e.descriptor.index-t.descriptor.index})),l=Object.keys(c).map((function(e){return{droppableId:e,scroll:t.droppable.getById(e).callbacks.getScrollWhileDragging()}})),u={additions:s,removals:Object.keys(a),modified:l};r=dC(),b_(),n.publish(u)})))};return{add:function(e){var t=e.descriptor.id;r.additions[t]=e,r.modified[e.descriptor.droppableId]=!0,r.removals[t]&&delete r.removals[t],o()},remove:function(e){var t=e.descriptor;r.removals[t.id]=!0,r.modified[t.droppableId]=!0,r.additions[t.id]&&delete r.additions[t.id],o()},stop:function(){i&&(cancelAnimationFrame(i),i=null,r=dC())}}}({callbacks:{publish:t.publishWhileDragging,collectionStarting:t.collectionStarting},registry:e}),i=function(t){n||Rj(!1);var i=n.critical.draggable;"ADDITION"===t.type&&mC(e,i,t.value)&&r.add(t.value),"REMOVAL"===t.type&&mC(e,i,t.value)&&r.remove(t.value)};return{updateDroppableIsEnabled:function(r,i){e.droppable.exists(r)||Rj(!1),n&&t.updateDroppableIsEnabled({id:r,isEnabled:i})},updateDroppableIsCombineEnabled:function(r,i){n&&(e.droppable.exists(r)||Rj(!1),t.updateDroppableIsCombineEnabled({id:r,isCombineEnabled:i}))},scrollDroppable:function(t,r){n&&e.droppable.getById(t).callbacks.scroll(r)},updateDroppableScroll:function(r,i){n&&(e.droppable.exists(r)||Rj(!1),t.updateDroppableScroll({id:r,newScroll:i}))},startPublishing:function(t){n&&Rj(!1);var r=e.draggable.getById(t.draggableId),o=e.droppable.getById(r.descriptor.droppableId),a={draggable:r.descriptor,droppable:o.descriptor},c=e.subscribe(i);return n={critical:a,unsubscribe:c},gC({critical:a,registry:e,scrollOptions:t.scrollOptions})},stopPublishing:function(){if(n){r.stop();var t=n.critical.droppable;e.droppable.getAllByType(t.type).forEach((function(e){return e.callbacks.dragStopped()})),n.unsubscribe(),n=null}}}},bC=function(e,t){return"IDLE"===e.phase||"DROP_ANIMATING"===e.phase&&(e.completed.result.draggableId!==t&&"DROP"===e.completed.result.reason)},wC=function(e){window.scrollBy(e.x,e.y)},kC=lz((function(e){return pM(e).filter((function(e){return!!e.isEnabled&&!!e.frame}))})),xC=function(e){var t=e.center,n=e.destination,r=e.droppables;if(n){var i=r[n];return i.frame?i:null}return function(e,t){return uM(kC(t),(function(t){return t.frame||Rj(!1),c_(t.frame.pageMarginBox)(e)}))}(t,r)},jC=.25,MC=.05,_C=28,CC=function(e){return Math.pow(e,2)},qC={stopDampeningAt:1200,accelerateAt:360},SC=function(e){var t=e.startOfRange,n=e.endOfRange,r=e.current,i=n-t;return 0===i?0:(r-t)/i},OC=qC.accelerateAt,TC=qC.stopDampeningAt,EC=function(e){var t=e.distanceToEdge,n=e.thresholds,r=e.dragStartTime,i=e.shouldUseTimeDampening,o=function(e,t){if(e>t.startScrollingFrom)return 0;if(e<=t.maxScrollValueAt)return _C;if(e===t.startScrollingFrom)return 1;var n=SC({startOfRange:t.maxScrollValueAt,endOfRange:t.startScrollingFrom,current:e}),r=_C*CC(1-n);return Math.ceil(r)}(t,n);return 0===o?0:i?Math.max(function(e,t){var n=t,r=TC,i=Date.now()-n;if(i>=TC)return e;if(it.height,o=n.width>t.width;return o||i?o&&i?null:{x:o?0:r.x,y:i?0:r.y}:r}({container:n,subject:r,proposedScroll:l});return u?Kj(u,Zj)?null:u:null},DC=nM((function(e){return 0===e?0:e>0?1:-1})),PC=function(){var e=function(e,t){return e<0?e:e>t?e-t:0};return function(t){var n=t.current,r=t.max,i=t.change,o=$j(n,i),a={x:e(o.x,r.x),y:e(o.y,r.y)};return Kj(a,Zj)?null:a}}(),VC=function(e){var t=e.max,n=e.current,r=e.change,i={x:Math.max(n.x,t.x),y:Math.max(n.y,t.y)},o=DC(r),a=PC({max:i,current:n,change:o});return!a||(0!==o.x&&0===a.x||0!==o.y&&0===a.y)},NC=function(e,t){return VC({current:e.scroll.current,max:e.scroll.max,change:t})},IC=function(e,t){var n=e.frame;return!!n&&VC({current:n.scroll.current,max:n.scroll.max,change:t})},RC=function(e){var t=e.state,n=e.dragStartTime,r=e.shouldUseTimeDampening,i=e.scrollWindow,o=e.scrollDroppable,a=t.current.page.borderBoxCenter,c=t.dimensions.draggables[t.critical.draggable.id].page.marginBox;if(t.isWindowScrollAllowed){var s=function(e){var t=e.viewport,n=e.subject,r=e.center,i=e.dragStartTime,o=e.shouldUseTimeDampening,a=LC({dragStartTime:i,container:t.frame,subject:n,center:r,shouldUseTimeDampening:o});return a&&NC(t,a)?a:null}({dragStartTime:n,viewport:t.viewport,subject:c,center:a,shouldUseTimeDampening:r});if(s)return void i(s)}var l=xC({center:a,destination:i_(t.impact),droppables:t.dimensions.droppables});if(l){var u=function(e){var t=e.droppable,n=e.subject,r=e.center,i=e.dragStartTime,o=e.shouldUseTimeDampening,a=t.frame;if(!a)return null;var c=LC({dragStartTime:i,container:a.pageMarginBox,subject:n,center:r,shouldUseTimeDampening:o});return c&&IC(t,c)?c:null}({dragStartTime:n,droppable:l,subject:c,center:a,shouldUseTimeDampening:r});u&&o(l.descriptor.id,u)}},BC=function(e){var t=e.move,n=e.scrollDroppable,r=e.scrollWindow,i=function(e,t){if(!IC(e,t))return t;var r=function(e,t){var n=e.frame;return n&&IC(e,t)?PC({current:n.scroll.current,max:n.scroll.max,change:t}):null}(e,t);if(!r)return n(e.descriptor.id,t),null;var i=Xj(t,r);return n(e.descriptor.id,i),Xj(t,i)},o=function(e,t,n){if(!e)return n;if(!NC(t,n))return n;var i=function(e,t){if(!NC(e,t))return null;var n=e.scroll.max,r=e.scroll.current;return PC({current:r,max:n,change:t})}(t,n);if(!i)return r(n),null;var o=Xj(n,i);return r(o),Xj(n,o)};return function(e){var n=e.scrollJumpRequest;if(n){var r=i_(e.impact);r||Rj(!1);var a=i(e.dimensions.droppables[r],n);if(a){var c=e.viewport,s=o(e.isWindowScrollAllowed,c,a);s&&function(e,n){var r=$j(e.current.client.selection,n);t({client:r})}(e,s)}}}},FC=function(e){var t=e.scrollDroppable,n=e.scrollWindow,r=e.move,i=function(e){var t=e.scrollWindow,n=e.scrollDroppable,r=Cj(t),i=Cj(n),o=null,a=function(e){o||Rj(!1);var t=o,n=t.shouldUseTimeDampening,a=t.dragStartTime;RC({state:e,scrollWindow:r,scrollDroppable:i,dragStartTime:a,shouldUseTimeDampening:n})};return{start:function(e){y_(),o&&Rj(!1);var t=Date.now(),n=!1,r=function(){n=!0};RC({state:e,dragStartTime:0,shouldUseTimeDampening:!1,scrollWindow:r,scrollDroppable:r}),o={dragStartTime:t,shouldUseTimeDampening:n},b_(),n&&a(e)},stop:function(){o&&(r.cancel(),i.cancel(),o=null)},scroll:a}}({scrollWindow:n,scrollDroppable:t}),o=BC({move:r,scrollWindow:n,scrollDroppable:t});return{scroll:function(e){"DRAGGING"===e.phase&&("FLUID"!==e.movementMode?e.scrollJumpRequest&&o(e):i.scroll(e))},start:i.start,stop:i.stop}},UC="data-rbd",WC=function(){var e=UC+"-drag-handle";return{base:e,draggableId:e+"-draggable-id",contextId:e+"-context-id"}}(),GC=function(){var e=UC+"-draggable";return{base:e,contextId:e+"-context-id",id:e+"-id"}}(),YC=function(){var e=UC+"-droppable";return{base:e,contextId:e+"-context-id",id:e+"-id"}}(),ZC={contextId:UC+"-scroll-container-context-id"},$C=function(e,t){return e.map((function(e){var n=e.styles[t];return n?e.selector+" { "+n+" }":""})).join(" ")},XC=function(e){var t,n=(t=e,function(e){return"["+e+'="'+t+'"]'}),r=function(){var e="\n cursor: -webkit-grab;\n cursor: grab;\n ";return{selector:n(WC.contextId),styles:{always:"\n -webkit-touch-callout: none;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n touch-action: manipulation;\n ",resting:e,dragging:"pointer-events: none;",dropAnimating:e}}}(),i=[function(){var e="\n transition: "+Y_.outOfTheWay+";\n ";return{selector:n(GC.contextId),styles:{dragging:e,dropAnimating:e,userCancel:e}}}(),r,{selector:n(YC.contextId),styles:{always:"overflow-anchor: none;"}},{selector:"body",styles:{dragging:"\n cursor: grabbing;\n cursor: -webkit-grabbing;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n overflow-anchor: none;\n "}}];return{always:$C(i,"always"),resting:$C(i,"resting"),dragging:$C(i,"dragging"),dropAnimating:$C(i,"dropAnimating"),userCancel:$C(i,"userCancel")}},KC="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?a.useLayoutEffect:a.useEffect,QC=function(){var e=document.querySelector("head");return e||Rj(!1),e},JC=function(e){var t=document.createElement("style");return e&&t.setAttribute("nonce",e),t.type="text/css",t};var eq=function(e){return e&&e.ownerDocument?e.ownerDocument.defaultView:window};function tq(e){return e instanceof eq(e).HTMLElement}function nq(e,t){var n="["+WC.contextId+'="'+e+'"]',r=fM(document.querySelectorAll(n));if(!r.length)return null;var i=uM(r,(function(e){return e.getAttribute(WC.draggableId)===t}));return i&&tq(i)?i:null}function rq(){var e={draggables:{},droppables:{}},t=[];function n(e){t.length&&t.forEach((function(t){return t(e)}))}function r(t){return e.draggables[t]||null}function i(t){return e.droppables[t]||null}return{draggable:{register:function(t){e.draggables[t.descriptor.id]=t,n({type:"ADDITION",value:t})},update:function(t,n){var r=e.draggables[n.descriptor.id];r&&r.uniqueId===t.uniqueId&&(delete e.draggables[n.descriptor.id],e.draggables[t.descriptor.id]=t)},unregister:function(t){var i=t.descriptor.id,o=r(i);o&&t.uniqueId===o.uniqueId&&(delete e.draggables[i],n({type:"REMOVAL",value:t}))},getById:function(e){var t=r(e);return t||Rj(!1),t},findById:r,exists:function(e){return Boolean(r(e))},getAllByType:function(t){return sM(e.draggables).filter((function(e){return e.descriptor.type===t}))}},droppable:{register:function(t){e.droppables[t.descriptor.id]=t},unregister:function(t){var n=i(t.descriptor.id);n&&t.uniqueId===n.uniqueId&&delete e.droppables[t.descriptor.id]},getById:function(e){var t=i(e);return t||Rj(!1),t},findById:i,exists:function(e){return Boolean(i(e))},getAllByType:function(t){return sM(e.droppables).filter((function(e){return e.descriptor.type===t}))}},subscribe:function(e){return t.push(e),function(){var n=t.indexOf(e);-1!==n&&t.splice(n,1)}},clean:function(){e.draggables={},e.droppables={},t.length=0}}}var iq=c.a.createContext(null),oq=function(){var e=document.body;return e||Rj(!1),e},aq={position:"absolute",width:"1px",height:"1px",margin:"-1px",border:"0",padding:"0",overflow:"hidden",clip:"rect(0 0 0 0)","clip-path":"inset(100%)"},cq=function(e){return"rbd-announcement-"+e};var sq=0,lq={separator:"::"};function uq(e,t){return void 0===t&&(t=lq),zj((function(){return""+e+t.separator+sq++}),[t.separator,e])}var fq=c.a.createContext(null);function hq(e){0}function dq(e,t){hq()}function pq(e){var t=Object(a.useRef)(e);return Object(a.useEffect)((function(){t.current=e})),t}var zq,vq=27,gq=32,mq=37,yq=38,bq=39,wq=40,kq=((zq={})[13]=!0,zq[9]=!0,zq),xq=function(e){kq[e.keyCode]&&e.preventDefault()},jq=function(){var e="visibilitychange";return"undefined"===typeof document?e:uM([e,"ms"+e,"webkit"+e,"moz"+e,"o"+e],(function(e){return"on"+e in document}))||e}(),Mq=0,_q=5;var Cq,qq={type:"IDLE"};function Sq(e){var t=e.cancel,n=e.completed,r=e.getPhase,i=e.setPhase;return[{eventName:"mousemove",fn:function(e){var t=e.button,n=e.clientX,o=e.clientY;if(t===Mq){var a={x:n,y:o},c=r();if("DRAGGING"===c.type)return e.preventDefault(),void c.actions.move(a);"PENDING"!==c.type&&Rj(!1);var s=c.point;if(l=s,u=a,Math.abs(u.x-l.x)>=_q||Math.abs(u.y-l.y)>=_q){var l,u;e.preventDefault();var f=c.actions.fluidLift(a);i({type:"DRAGGING",actions:f})}}}},{eventName:"mouseup",fn:function(e){var i=r();"DRAGGING"===i.type?(e.preventDefault(),i.actions.drop({shouldBlockNextClick:!0}),n()):t()}},{eventName:"mousedown",fn:function(e){"DRAGGING"===r().type&&e.preventDefault(),t()}},{eventName:"keydown",fn:function(e){if("PENDING"!==r().type)return e.keyCode===vq?(e.preventDefault(),void t()):void xq(e);t()}},{eventName:"resize",fn:t},{eventName:"scroll",options:{passive:!0,capture:!1},fn:function(){"PENDING"===r().type&&t()}},{eventName:"webkitmouseforcedown",fn:function(e){var n=r();"IDLE"===n.type&&Rj(!1),n.actions.shouldRespectForcePress()?t():e.preventDefault()}},{eventName:jq,fn:t}]}function Oq(){}var Tq=((Cq={})[34]=!0,Cq[33]=!0,Cq[36]=!0,Cq[35]=!0,Cq);function Eq(e,t){function n(){t(),e.cancel()}return[{eventName:"keydown",fn:function(r){return r.keyCode===vq?(r.preventDefault(),void n()):r.keyCode===gq?(r.preventDefault(),t(),void e.drop()):r.keyCode===wq?(r.preventDefault(),void e.moveDown()):r.keyCode===yq?(r.preventDefault(),void e.moveUp()):r.keyCode===bq?(r.preventDefault(),void e.moveRight()):r.keyCode===mq?(r.preventDefault(),void e.moveLeft()):void(Tq[r.keyCode]?r.preventDefault():xq(r))}},{eventName:"mousedown",fn:n},{eventName:"mouseup",fn:n},{eventName:"click",fn:n},{eventName:"touchstart",fn:n},{eventName:"resize",fn:n},{eventName:"wheel",fn:n,options:{passive:!0}},{eventName:jq,fn:n}]}var Aq={type:"IDLE"},Hq=120,Lq=.15;var Dq={input:!0,button:!0,textarea:!0,select:!0,option:!0,optgroup:!0,video:!0,audio:!0};function Pq(e,t){var n=t.target;return!!tq(n)&&function e(t,n){if(null==n)return!1;if(Boolean(Dq[n.tagName.toLowerCase()]))return!0;var r=n.getAttribute("contenteditable");return"true"===r||""===r||n!==t&&e(t,n.parentElement)}(e,n)}var Vq=function(e){return gj(e.getBoundingClientRect()).center};var Nq="undefined"===typeof document?"matches":uM(["matches","msMatchesSelector","webkitMatchesSelector"],(function(e){return e in Element.prototype}))||"matches";function Iq(e,t){return e.closest?e.closest(t):function e(t,n){return null==t?null:t[Nq](n)?t:e(t.parentElement,n)}(e,t)}function Rq(e,t){var n,r=t.target;if(!((n=r)instanceof eq(n).Element))return null;var i=Iq(r,function(e){return"["+WC.contextId+'="'+e+'"]'}(e));return i&&tq(i)?i:null}function Bq(e){e.preventDefault()}function Fq(e){var t=e.expected,n=e.phase,r=e.isLockActive;e.shouldWarn;return!!r()&&t===n}function Uq(e){var t=e.lockAPI,n=e.store,r=e.registry,i=e.draggableId;if(t.isClaimed())return!1;var o=r.draggable.findById(i);return!!o&&(!!o.options.isEnabled&&!!bC(n.getState(),i))}function Wq(e){var t=e.lockAPI,n=e.contextId,r=e.store,i=e.registry,o=e.draggableId,a=e.forceSensorStop,c=e.sourceEvent;if(!Uq({lockAPI:t,store:r,registry:i,draggableId:o}))return null;var s=i.draggable.getById(o),l=function(e,t){var n="["+GC.contextId+'="'+e+'"]',r=uM(fM(document.querySelectorAll(n)),(function(e){return e.getAttribute(GC.id)===t}));return r&&tq(r)?r:null}(n,s.descriptor.id);if(!l)return null;if(c&&!s.options.canDragInteractiveElements&&Pq(l,c))return null;var u=t.claim(a||Dj),f="PRE_DRAG";function h(){return s.options.shouldRespectForcePress}function d(){return t.isActive(u)}var p=function(e,t){Fq({expected:e,phase:f,isLockActive:d,shouldWarn:!0})&&r.dispatch(t())}.bind(null,"DRAGGING");function z(e){function n(){t.release(),f="COMPLETED"}function i(t,i){if(void 0===i&&(i={shouldBlockNextClick:!1}),e.cleanup(),i.shouldBlockNextClick){var o=Pj(window,[{eventName:"click",fn:Bq,options:{once:!0,passive:!1,capture:!0}}]);setTimeout(o)}n(),r.dispatch(I_({reason:t}))}return"PRE_DRAG"!==f&&(n(),"PRE_DRAG"!==f&&Rj(!1)),r.dispatch(C_(e.liftActionArgs)),f="DRAGGING",_x({isActive:function(){return Fq({expected:"DRAGGING",phase:f,isLockActive:d,shouldWarn:!1})},shouldRespectForcePress:h,drop:function(e){return i("DROP",e)},cancel:function(e){return i("CANCEL",e)}},e.actions)}return{isActive:function(){return Fq({expected:"PRE_DRAG",phase:f,isLockActive:d,shouldWarn:!1})},shouldRespectForcePress:h,fluidLift:function(e){var t=Cj((function(e){p((function(){return A_({client:e})}))}));return _x({},z({liftActionArgs:{id:o,clientSelection:e,movementMode:"FLUID"},cleanup:function(){return t.cancel()},actions:{move:t}}),{move:t})},snapLift:function(){var e={moveUp:function(){return p(H_)},moveRight:function(){return p(D_)},moveDown:function(){return p(L_)},moveLeft:function(){return p(P_)}};return z({liftActionArgs:{id:o,clientSelection:Vq(l),movementMode:"SNAP"},cleanup:Dj,actions:e})},abort:function(){Fq({expected:"PRE_DRAG",phase:f,isLockActive:d,shouldWarn:!0})&&t.release()}}}var Gq=[function(e){var t=Object(a.useRef)(qq),n=Object(a.useRef)(Dj),r=zj((function(){return{eventName:"mousedown",fn:function(t){if(!t.defaultPrevented&&t.button===Mq&&!(t.ctrlKey||t.metaKey||t.shiftKey||t.altKey)){var r=e.findClosestDraggableId(t);if(r){var i=e.tryGetLock(r,c,{sourceEvent:t});if(i){t.preventDefault();var o={x:t.clientX,y:t.clientY};n.current(),u(i,o)}}}}}}),[e]),i=zj((function(){return{eventName:"webkitmouseforcewillbegin",fn:function(t){if(!t.defaultPrevented){var n=e.findClosestDraggableId(t);if(n){var r=e.findOptionsForDraggable(n);r&&(r.shouldRespectForcePress||e.canGetLock(n)&&t.preventDefault())}}}}}),[e]),o=vj((function(){n.current=Pj(window,[i,r],{passive:!1,capture:!0})}),[i,r]),c=vj((function(){"IDLE"!==t.current.type&&(t.current=qq,n.current(),o())}),[o]),s=vj((function(){var e=t.current;c(),"DRAGGING"===e.type&&e.actions.cancel({shouldBlockNextClick:!0}),"PENDING"===e.type&&e.actions.abort()}),[c]),l=vj((function(){var e=Sq({cancel:s,completed:c,getPhase:function(){return t.current},setPhase:function(e){t.current=e}});n.current=Pj(window,e,{capture:!0,passive:!1})}),[s,c]),u=vj((function(e,n){"IDLE"!==t.current.type&&Rj(!1),t.current={type:"PENDING",point:n,actions:e},l()}),[l]);KC((function(){return o(),function(){n.current()}}),[o])},function(e){var t=Object(a.useRef)(Oq),n=zj((function(){return{eventName:"keydown",fn:function(n){if(!n.defaultPrevented&&n.keyCode===gq){var i=e.findClosestDraggableId(n);if(i){var o=e.tryGetLock(i,s,{sourceEvent:n});if(o){n.preventDefault();var a=!0,c=o.snapLift();t.current(),t.current=Pj(window,Eq(c,s),{capture:!0,passive:!1})}}}function s(){a||Rj(!1),a=!1,t.current(),r()}}}}),[e]),r=vj((function(){t.current=Pj(window,[n],{passive:!1,capture:!0})}),[n]);KC((function(){return r(),function(){t.current()}}),[r])},function(e){var t=Object(a.useRef)(Aq),n=Object(a.useRef)(Dj),r=vj((function(){return t.current}),[]),i=vj((function(e){t.current=e}),[]),o=zj((function(){return{eventName:"touchstart",fn:function(t){if(!t.defaultPrevented){var r=e.findClosestDraggableId(t);if(r){var i=e.tryGetLock(r,s,{sourceEvent:t});if(i){var o=t.touches[0],a={x:o.clientX,y:o.clientY};n.current(),h(i,a)}}}}}}),[e]),c=vj((function(){n.current=Pj(window,[o],{capture:!0,passive:!1})}),[o]),s=vj((function(){var e=t.current;"IDLE"!==e.type&&("PENDING"===e.type&&clearTimeout(e.longPressTimerId),i(Aq),n.current(),c())}),[c,i]),l=vj((function(){var e=t.current;s(),"DRAGGING"===e.type&&e.actions.cancel({shouldBlockNextClick:!0}),"PENDING"===e.type&&e.actions.abort()}),[s]),u=vj((function(){var e={capture:!0,passive:!1},t={cancel:l,completed:s,getPhase:r},i=Pj(window,function(e){var t=e.cancel,n=e.completed,r=e.getPhase;return[{eventName:"touchmove",options:{capture:!1},fn:function(e){var n=r();if("DRAGGING"===n.type){n.hasMoved=!0;var i=e.touches[0],o={x:i.clientX,y:i.clientY};e.preventDefault(),n.actions.move(o)}else t()}},{eventName:"touchend",fn:function(e){var i=r();"DRAGGING"===i.type?(e.preventDefault(),i.actions.drop({shouldBlockNextClick:!0}),n()):t()}},{eventName:"touchcancel",fn:function(e){"DRAGGING"===r().type?(e.preventDefault(),t()):t()}},{eventName:"touchforcechange",fn:function(e){var n=r();"IDLE"===n.type&&Rj(!1);var i=e.touches[0];if(i&&i.force>=Lq){var o=n.actions.shouldRespectForcePress();if("PENDING"!==n.type)return o?n.hasMoved?void e.preventDefault():void t():void e.preventDefault();o&&t()}}},{eventName:jq,fn:t}]}(t),e),o=Pj(window,function(e){var t=e.cancel,n=e.getPhase;return[{eventName:"orientationchange",fn:t},{eventName:"resize",fn:t},{eventName:"contextmenu",fn:function(e){e.preventDefault()}},{eventName:"keydown",fn:function(e){"DRAGGING"===n().type?(e.keyCode===vq&&e.preventDefault(),t()):t()}},{eventName:jq,fn:t}]}(t),e);n.current=function(){i(),o()}}),[l,r,s]),f=vj((function(){var e=r();"PENDING"!==e.type&&Rj(!1);var t=e.actions.fluidLift(e.point);i({type:"DRAGGING",actions:t,hasMoved:!1})}),[r,i]),h=vj((function(e,t){"IDLE"!==r().type&&Rj(!1);var n=setTimeout(f,Hq);i({type:"PENDING",point:t,actions:e,longPressTimerId:n}),u()}),[u,r,i,f]);KC((function(){return c(),function(){n.current();var e=r();"PENDING"===e.type&&(clearTimeout(e.longPressTimerId),i(Aq))}}),[r,c,i]),KC((function(){return Pj(window,[{eventName:"touchmove",fn:function(){},options:{capture:!1,passive:!1}}])}),[])}];function Yq(e){var t=e.contextId,n=e.store,r=e.registry,i=e.customSensors,o=e.enableDefaultSensors,c=[].concat(o?Gq:[],i||[]),s=Object(a.useState)((function(){return function(){var e=null;function t(){e||Rj(!1),e=null}return{isClaimed:function(){return Boolean(e)},isActive:function(t){return t===e},claim:function(t){e&&Rj(!1);var n={abandon:t};return e=n,n},release:t,tryAbandon:function(){e&&(e.abandon(),t())}}}()}))[0],l=vj((function(e,t){e.isDragging&&!t.isDragging&&s.tryAbandon()}),[s]);KC((function(){var e=n.getState();return n.subscribe((function(){var t=n.getState();l(e,t),e=t}))}),[s,n,l]),KC((function(){return s.tryAbandon}),[s.tryAbandon]);var u=vj((function(e){return Uq({lockAPI:s,registry:r,store:n,draggableId:e})}),[s,r,n]),f=vj((function(e,i,o){return Wq({lockAPI:s,registry:r,contextId:t,store:n,draggableId:e,forceSensorStop:i,sourceEvent:o&&o.sourceEvent?o.sourceEvent:null})}),[t,s,r,n]),h=vj((function(e){return function(e,t){var n=Rq(e,t);return n?n.getAttribute(WC.draggableId):null}(t,e)}),[t]),d=vj((function(e){var t=r.draggable.findById(e);return t?t.options:null}),[r.draggable]),p=vj((function(){s.isClaimed()&&(s.tryAbandon(),"IDLE"!==n.getState().phase&&n.dispatch(V_()))}),[s,n]),z=vj(s.isClaimed,[s]),v=zj((function(){return{canGetLock:u,tryGetLock:f,findClosestDraggableId:h,findOptionsForDraggable:d,tryReleaseLock:p,isLockClaimed:z}}),[u,f,h,d,p,z]);hq();for(var g=0;gi))return o.current=FS(FS({},o.current),{width:i,tabRight:f,containerRight:s}),f>=s&&!o.current.collapse?(o.current.collapse=!0,r(!0)):f+hs&&u(!0),l<=s&&u(!1),a>0&&c(!0),0===a&&c(!1)}}}),[r,n])]}(l,u,n,s),d=h[0],p=h[1],z=h[2];Object(a.useEffect)((function(){if(l.current){var e=l.current,t=Object(BS.a)(300,(function(){f(),z()}));return t(),e.addEventListener("scroll",z),window.addEventListener("resize",t),function(){e.removeEventListener("scroll",z),window.removeEventListener("resize",t)}}}),[n,s]);var v=Object(a.useCallback)((function(e){if(e){var t=u.current;n.length>=t.length&&(u.current=QS(t,[e])),n.length0||(i.disconnect(),oO.delete(e),delete rO[r],delete iO[r])}}((function(e){i&&i(e),u(e)}),e,s)}}),[t,n,r,i]);return Object(a.useEffect)((function(){return function(){var e;null===(e=c.current)||void 0===e||e.call(c),c.current=null}}),[]),[f,o,l]},lO=function(){return(lO=Object.assign||function(e){for(var t,n=1,r=arguments.length;n1?t-1:0),r=1;r0?" Args: "+n.join(", "):""))}var M=function(){function e(e){this.groupSizes=new Uint32Array(512),this.length=512,this.tag=e}var t=e.prototype;return t.indexOfGroup=function(e){for(var t=0,n=0;n=this.groupSizes.length){for(var n=this.groupSizes,r=n.length,i=r;e>=i;)(i<<=1)<0&&j(16,""+e);this.groupSizes=new Uint32Array(i),this.groupSizes.set(n),this.length=i;for(var o=r;o=this.length||0===this.groupSizes[e])return t;for(var n=this.groupSizes[e],r=this.indexOfGroup(e),i=r+n,o=r;o=q&&(q=t+1),_.set(e,t),C.set(t,e)},E="style["+w+'][data-styled-version="5.3.5"]',A=new RegExp("^"+w+'\\.g(\\d+)\\[id="([\\w\\d-]+)"\\].*?"([^"]*)'),H=function(e,t,n){for(var r,i=n.split(","),o=0,a=i.length;o=0;n--){var r=t[n];if(r&&1===r.nodeType&&r.hasAttribute(w))return r}}(n),o=void 0!==i?i.nextSibling:null;r.setAttribute(w,"active"),r.setAttribute("data-styled-version","5.3.5");var a=D();return a&&r.setAttribute("nonce",a),n.insertBefore(r,o),r},V=function(){function e(e){var t=this.element=P(e);t.appendChild(document.createTextNode("")),this.sheet=function(e){if(e.sheet)return e.sheet;for(var t=document.styleSheets,n=0,r=t.length;n=0){var n=document.createTextNode(t),r=this.nodes[e];return this.element.insertBefore(n,r||null),this.length++,!0}return!1},t.deleteRule=function(e){this.element.removeChild(this.nodes[e]),this.length--},t.getRule=function(e){return e0&&(l+=e+",")})),r+=""+c+s+'{content:"'+l+'"}/*!sc*/\n'}}}return r}(this)},e}(),U=/(a)(d)/gi,W=function(e){return String.fromCharCode(e+(e>25?39:97))};function G(e){var t,n="";for(t=Math.abs(e);t>52;t=t/52|0)n=W(t%52)+n;return(W(t%52)+n).replace(U,"$1-$2")}var Y=function(e,t){for(var n=t.length;n;)e=33*e^t.charCodeAt(--n);return e},Z=function(e){return Y(5381,e)};function $(e){for(var t=0;t>>0);if(!t.hasNameForId(r,a)){var c=n(o,"."+a,void 0,r);t.insertRules(r,a,c)}i.push(a),this.staticRulesId=a}else{for(var s=this.rules.length,l=Y(this.baseHash,n.hash),u="",f=0;f>>0);if(!t.hasNameForId(r,z)){var v=n(u,"."+z,void 0,r);t.insertRules(r,z,v)}i.push(z)}}return i.join(" ")},e}(),Q=/^\s*\/\/.*$/gm,J=[":","[",".","#"];function ee(e){var t,n,r,i,o=void 0===e?g:e,a=o.options,c=void 0===a?g:a,l=o.plugins,u=void 0===l?v:l,f=new s.a(c),h=[],d=function(e){function t(t){if(t)try{e(t+"}")}catch(e){}}return function(n,r,i,o,a,c,s,l,u,f){switch(n){case 1:if(0===u&&64===r.charCodeAt(0))return e(r+";"),"";break;case 2:if(0===l)return r+"/*|*/";break;case 3:switch(l){case 102:case 112:return e(i[0]+r),"";default:return r+(0===f?"/*|*/":"")}case-2:r.split("/*|*/}").forEach(t)}}}((function(e){h.push(e)})),p=function(e,r,o){return 0===r&&-1!==J.indexOf(o[n.length])||o.match(i)?e:"."+t};function z(e,o,a,c){void 0===c&&(c="&");var s=e.replace(Q,""),l=o&&a?a+" "+o+" { "+s+" }":s;return t=c,n=o,r=new RegExp("\\"+n+"\\b","g"),i=new RegExp("(\\"+n+"\\b){2,}"),f(a||!o?"":o,l)}return f.use([].concat(u,[function(e,t,i){2===e&&i.length&&i[0].lastIndexOf(n)>0&&(i[0]=i[0].replace(r,p))},d,function(e){if(-2===e){var t=h;return h=[],t}}])),z.hash=u.length?u.reduce((function(e,t){return t.name||j(15),Y(e,t.name)}),5381).toString():"",z}var te=o.a.createContext(),ne=(te.Consumer,o.a.createContext()),re=(ne.Consumer,new F),ie=ee();function oe(){return Object(i.useContext)(te)||re}function ae(){return Object(i.useContext)(ne)||ie}function ce(e){var t=Object(i.useState)(e.stylisPlugins),n=t[0],r=t[1],a=oe(),s=Object(i.useMemo)((function(){var t=a;return e.sheet?t=e.sheet:e.target&&(t=t.reconstructWithOptions({target:e.target},!1)),e.disableCSSOMInjection&&(t=t.reconstructWithOptions({useCSSOMInjection:!1})),t}),[e.disableCSSOMInjection,e.sheet,e.target]),l=Object(i.useMemo)((function(){return ee({options:{prefix:!e.disableVendorPrefixes},plugins:n})}),[e.disableVendorPrefixes,n]);return Object(i.useEffect)((function(){c()(n,e.stylisPlugins)||r(e.stylisPlugins)}),[e.stylisPlugins]),o.a.createElement(te.Provider,{value:s},o.a.createElement(ne.Provider,{value:l},e.children))}var se=function(){function e(e,t){var n=this;this.inject=function(e,t){void 0===t&&(t=ie);var r=n.name+t.hash;e.hasNameForId(n.id,r)||e.insertRules(n.id,r,t(n.rules,r,"@keyframes"))},this.toString=function(){return j(12,String(n.name))},this.name=e,this.id="sc-keyframes-"+e,this.rules=t}return e.prototype.getName=function(e){return void 0===e&&(e=ie),this.name+e.hash},e}(),le=/([A-Z])/,ue=/([A-Z])/g,fe=/^ms-/,he=function(e){return"-"+e.toLowerCase()};function de(e){return le.test(e)?e.replace(ue,he).replace(fe,"-ms-"):e}var pe=function(e){return null==e||!1===e||""===e};function ze(e,t,n,r){if(Array.isArray(e)){for(var i,o=[],a=0,c=e.length;a1?t-1:0),r=1;r?@[\\\]^`{|}~-]+/g,be=/(^-|-$)/g;function we(e){return e.replace(ye,"-").replace(be,"")}var ke=function(e){return G(Z(e)>>>0)};function xe(e){return"string"==typeof e&&!0}var je=function(e){return"function"==typeof e||"object"==typeof e&&null!==e&&!Array.isArray(e)},Me=function(e){return"__proto__"!==e&&"constructor"!==e&&"prototype"!==e};function _e(e,t,n){var r=e[n];je(t)&&je(r)?Ce(r,t):e[n]=t}function Ce(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r=0||(i[n]=e[n]);return i}(t,["componentId"]),o=r&&r+"-"+(xe(e)?e:we(y(e)));return Te(e,d({},i,{attrs:k,componentId:o}),n)},Object.defineProperty(j,"defaultProps",{get:function(){return this._foldedDefaultProps},set:function(t){this._foldedDefaultProps=r?Ce({},e.defaultProps,t):t}}),j.toString=function(){return"."+j.styledComponentId},a&&h()(j,e,{attrs:!0,componentStyle:!0,displayName:!0,foldedComponentIds:!0,shouldForwardProp:!0,styledComponentId:!0,target:!0,withComponent:!0}),j}var Ee=function(e){return function e(t,n,i){if(void 0===i&&(i=g),!Object(r.isValidElementType)(n))return j(1,String(n));var o=function(){return t(n,i,ge.apply(void 0,arguments))};return o.withConfig=function(r){return e(t,n,d({},i,{},r))},o.attrs=function(r){return e(t,n,d({},i,{attrs:Array.prototype.concat(i.attrs,r).filter(Boolean)}))},o}(Te,e)};["a","abbr","address","area","article","aside","audio","b","base","bdi","bdo","big","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","kbd","keygen","label","legend","li","link","main","map","mark","marquee","menu","menuitem","meta","meter","nav","noscript","object","ol","optgroup","option","output","p","param","picture","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","title","tr","track","u","ul","var","video","wbr","circle","clipPath","defs","ellipse","foreignObject","g","image","line","linearGradient","marker","mask","path","pattern","polygon","polyline","radialGradient","rect","stop","svg","text","textPath","tspan"].forEach((function(e){Ee[e]=Ee(e)}));!function(){function e(e,t){this.rules=e,this.componentId=t,this.isStatic=$(e),F.registerId(this.componentId+1)}var t=e.prototype;t.createStyles=function(e,t,n,r){var i=r(ze(this.rules,t,n,r).join(""),""),o=this.componentId+e;n.insertRules(o,o,i)},t.removeStyles=function(e,t){t.clearRules(this.componentId+e)},t.renderStyles=function(e,t,n,r){e>2&&F.registerId(this.componentId+e),this.removeStyles(e,n),this.createStyles(e,t,n,r)}}();function Ae(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r"+t+""},this.getStyleTags=function(){return e.sealed?j(2):e._emitSheetCSS()},this.getStyleElement=function(){var t;if(e.sealed)return j(2);var n=((t={})[w]="",t["data-styled-version"]="5.3.5",t.dangerouslySetInnerHTML={__html:e.instance.toString()},t),r=D();return r&&(n.nonce=r),[o.a.createElement("style",d({},n,{key:"sc-0-0"}))]},this.seal=function(){e.sealed=!0},this.instance=new F({isServer:!0}),this.sealed=!1}var t=e.prototype;t.collectStyles=function(e){return this.sealed?j(2):o.a.createElement(ce,{sheet:this.instance},e)},t.interleaveWithNodeStream=function(e){return j(3)}}();t.d=Ee}).call(this,n(103))},function(e,t,n){"use strict";var r=n(158);var i=n(159);function o(e,t){return Object(r.a)(e)||function(e,t){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e)){var n=[],r=!0,i=!1,o=void 0;try{for(var a,c=e[Symbol.iterator]();!(r=(a=c.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(s){i=!0,o=s}finally{try{r||null==c.return||c.return()}finally{if(i)throw o}}return n}}(e,t)||Object(i.a)()}n.d(t,"a",(function(){return o}))},function(e,t,n){"use strict";function r(e,t){if(t.length1?"s":"")+" required, but only "+t.length+" present")}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(14);function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0&&e.splice(n,1)}function p(e){var t=!1;return function(){t||(t=!0,e())}}var z=function(e){throw e},v=function(e){return{value:e,done:!0}};function g(e,t,n){void 0===t&&(t=z),void 0===n&&(n="iterator");var r={meta:{name:n},next:e,throw:t,return:v,isSagaIterator:!0};return"undefined"!==typeof Symbol&&(r[Symbol.iterator]=function(){return r}),r}function m(e,t){var n=t.sagaStack;console.error(e),console.error(n)}var y=function(e){return new Error("\n redux-saga: Error checking hooks detected an inconsistent state. This is likely a bug\n in redux-saga code and not yours. Thanks for reporting this in the project's github repo.\n Error: "+e+"\n")},b=function(e){return Array.apply(null,new Array(e))},w=function(e){return function(t){return e(Object.defineProperty(t,r.f,{value:!0}))}},k=function(e){return e===r.k},x=function(e){return e===r.j},j=function(e){return k(e)||x(e)};function M(e,t){var n=Object.keys(e),r=n.length;var i,a=0,c=Object(o.a)(e)?b(r):{},l={};return n.forEach((function(e){var n=function(n,o){i||(o||j(n)?(t.cancel(),t(n,o)):(c[e]=n,++a===r&&(i=!0,t(c))))};n.cancel=s,l[e]=n})),t.cancel=function(){i||(i=!0,n.forEach((function(e){return l[e].cancel()})))},l}function _(e){return{name:e.name||"anonymous",location:C(e)}}function C(e){return e[r.g]}var q="Channel's Buffer overflow!",S=1,O=3,T=4,E={isEmpty:c,put:s,take:s};function A(e,t){void 0===e&&(e=10);var n=new Array(e),r=0,i=0,o=0,a=function(t){n[i]=t,i=(i+1)%e,r++},c=function(){if(0!=r){var t=n[o];return n[o]=null,r--,o=(o+1)%e,t}},s=function(){for(var e=[];r;)e.push(c());return e};return{isEmpty:function(){return 0==r},put:function(c){var l;if(r1?t-1:0),r=1;r1?t-1:0),r=1;r1?t-1:0),r=1;r1?t-1:0),r=1;r2?n-2:0),o=2;o2?n-2:0),o=2;o1?t-1:0),r=1;r1&&void 0!==arguments[1]?arguments[1]:r,n=null,o=null;return function(){return i(t,n,arguments)||(o=e.apply(null,arguments)),n=arguments,o}}))},,function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(30),i=n(38);function o(e){return function t(n,o){switch(arguments.length){case 0:return t;case 1:return Object(i.a)(n)?t:Object(r.a)((function(t){return e(n,t)}));default:return Object(i.a)(n)&&Object(i.a)(o)?t:Object(i.a)(n)?Object(r.a)((function(t){return e(t,o)})):Object(i.a)(o)?Object(r.a)((function(t){return e(n,t)})):e(n,o)}}}},function(e,t,n){"use strict";function r(e,t){if(t.length1?"s":"")+" required, but only "+t.length+" present")}n.d(t,"a",(function(){return r}))},function(e,t,n){e.exports=n(317)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"createAction",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(t,"createReducer",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"assignAll",{enumerable:!0,get:function(){return a.default}}),Object.defineProperty(t,"bindAll",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(t,"batch",{enumerable:!0,get:function(){return s.default}}),Object.defineProperty(t,"disbatch",{enumerable:!0,get:function(){return l.default}}),Object.defineProperty(t,"loggers",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(t,"asError",{enumerable:!0,get:function(){return f.default}}),t.types=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(222)),i=h(n(223)),o=h(n(309)),a=h(n(310)),c=h(n(311)),s=h(n(148)),l=h(n(312)),u=h(n(313)),f=h(n(315));function h(e){return e&&e.__esModule?e:{default:e}}var d=r;t.types=d},function(e,t,n){"use strict";n.d(t,"a",(function(){return s})),n.d(t,"b",(function(){return d})),n.d(t,"c",(function(){return g})),n.d(t,"d",(function(){return a})),n.d(t,"e",(function(){return f})),n.d(t,"f",(function(){return v})),n.d(t,"g",(function(){return o})),n.d(t,"h",(function(){return l})),n.d(t,"i",(function(){return h})),n.d(t,"j",(function(){return u})),n.d(t,"k",(function(){return c})),n.d(t,"l",(function(){return p})),n.d(t,"m",(function(){return z})),n.d(t,"n",(function(){return i}));var r=n(33),i=function(e){return null===e||void 0===e},o=function(e){return null!==e&&void 0!==e},a=function(e){return"function"===typeof e},c=function(e){return"string"===typeof e},s=Array.isArray,l=function(e){return e&&!s(e)&&"object"===typeof e},u=function(e){return e&&a(e.then)},f=function(e){return e&&a(e.next)&&a(e.throw)},h=function e(t){return t&&(c(t)||z(t)||a(t)||s(t)&&t.every(e))},d=function(e){return e&&a(e.take)&&a(e.close)},p=function(e){return a(e)&&e.hasOwnProperty("toString")},z=function(e){return Boolean(e)&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype},v=function(e){return d(e)&&e[r.e]},g=function(e){return e&&e[r.c]}},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=n(20);function i(e){Object(r.a)(1,arguments);var t=Object.prototype.toString.call(e);return e instanceof Date||"object"===typeof e&&"[object Date]"===t?new Date(e.getTime()):"number"===typeof e||"[object Number]"===t?new Date(e):("string"!==typeof e&&"[object String]"!==t||"undefined"===typeof console||(console.warn("Starting with v2.0.0-beta.1 date-fns doesn't accept strings as date arguments. Please use `parseISO` to parse strings. See: https://git.io/fjule"),console.warn((new Error).stack)),new Date(NaN))}},function(e,t,n){"use strict";function r(e){if(null===e||!0===e||!1===e)return NaN;var t=Number(e);return isNaN(t)?t:t<0?Math.ceil(t):Math.floor(t)}n.d(t,"a",(function(){return r}))},,,function(e,t,n){"use strict";var r=n(0),i=n.n(r),o=n(13),a=n.n(o),c=i.a.createContext(null);var s=function(e){e()},l=function(){return s},u=null,f={notify:function(){}};var h=function(){function e(e,t){this.store=e,this.parentSub=t,this.unsubscribe=null,this.listeners=f,this.handleChangeWrapper=this.handleChangeWrapper.bind(this)}var t=e.prototype;return t.addNestedSub=function(e){return this.trySubscribe(),this.listeners.subscribe(e)},t.notifyNestedSubs=function(){this.listeners.notify()},t.handleChangeWrapper=function(){this.onStateChange&&this.onStateChange()},t.isSubscribed=function(){return Boolean(this.unsubscribe)},t.trySubscribe=function(){this.unsubscribe||(this.unsubscribe=this.parentSub?this.parentSub.addNestedSub(this.handleChangeWrapper):this.store.subscribe(this.handleChangeWrapper),this.listeners=function(){var e=l(),t=[],n=[];return{clear:function(){n=u,t=u},notify:function(){var r=t=n;e((function(){for(var e=0;e. You may also pass a {context : MyContext} option to connect");var H=E;return function(t){var n=t.displayName||t.name||"Component",o=a(n),c=Object(z.a)({},A,{getDisplayName:a,methodName:l,renderCountProp:f,shouldHandleStateChanges:p,storeKey:y,displayName:o,wrappedComponentName:n,WrappedComponent:t}),s=A.pure;var u=s?r.useMemo:function(e){return e()};function d(n){var a=Object(r.useMemo)((function(){var e=n.forwardedRef,t=Object(v.a)(n,["forwardedRef"]);return[n.context,e,t]}),[n]),s=a[0],l=a[1],f=a[2],d=Object(r.useMemo)((function(){return s&&s.Consumer&&Object(w.isContextConsumer)(i.a.createElement(s.Consumer,null))?s:H}),[s,H]),g=Object(r.useContext)(d),m=Boolean(n.store)&&Boolean(n.store.getState)&&Boolean(n.store.dispatch),y=Boolean(g)&&Boolean(g.store);b()(m||y,'Could not find "store" in the context of "'+o+'". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to '+o+" in connect options.");var C=m?n.store:g.store,q=Object(r.useMemo)((function(){return function(t){return e(t.dispatch,c)}(C)}),[C]),S=Object(r.useMemo)((function(){if(!p)return j;var e=new h(C,m?null:g.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]}),[C,m,g]),O=S[0],T=S[1],E=Object(r.useMemo)((function(){return m?g:Object(z.a)({},g,{subscription:O})}),[m,g,O]),A=Object(r.useReducer)(M,x,_),L=A[0][0],D=A[1];if(L&&L.error)throw L.error;var P=Object(r.useRef)(),V=Object(r.useRef)(f),N=Object(r.useRef)(),I=Object(r.useRef)(!1),R=u((function(){return N.current&&f===V.current?N.current:q(C.getState(),f)}),[C,L,f]);k((function(){V.current=f,P.current=R,I.current=!1,N.current&&(N.current=null,T())})),k((function(){if(p){var e=!1,t=null,n=function(){if(!e){var n,r,i=C.getState();try{n=q(i,V.current)}catch(o){r=o,t=o}r||(t=null),n===P.current?I.current||T():(P.current=n,N.current=n,I.current=!0,D({type:"STORE_UPDATED",payload:{error:r}}))}};O.onStateChange=n,O.trySubscribe(),n();return function(){if(e=!0,O.tryUnsubscribe(),O.onStateChange=null,t)throw t}}}),[C,O,q]);var B=Object(r.useMemo)((function(){return i.a.createElement(t,Object(z.a)({},R,{ref:l}))}),[l,t,R]);return Object(r.useMemo)((function(){return p?i.a.createElement(d.Provider,{value:E},B):B}),[d,B,E])}var g=s?i.a.memo(d):d;if(g.WrappedComponent=t,g.displayName=o,O){var C=i.a.forwardRef((function(e,t){return i.a.createElement(g,Object(z.a)({},e,{forwardedRef:t}))}));return C.displayName=o,C.WrappedComponent=t,m()(C,t)}return m()(g,t)}}var q=Object.prototype.hasOwnProperty;function S(e,t){return e===t?0!==e||0!==t||1/e===1/t:e!==e&&t!==t}function O(e,t){if(S(e,t))return!0;if("object"!==typeof e||null===e||"object"!==typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(var i=0;i=0;r--){var i=t[r](e);if(i)return i}return function(t,r){throw new Error("Invalid value of type "+typeof e+" for "+n+" argument when connecting component "+r.wrappedComponentName+".")}}function F(e,t){return e===t}!function(e){var t=void 0===e?{}:e,n=t.connectHOC,r=void 0===n?C:n,i=t.mapStateToPropsFactories,o=void 0===i?D:i,a=t.mapDispatchToPropsFactories,c=void 0===a?L:a,s=t.mergePropsFactories,l=void 0===s?V:s,u=t.selectorFactory,f=void 0===u?R:u}();function U(){var e=Object(r.useContext)(c);return b()(e,"could not find react-redux context value; please ensure the component is wrapped in a "),e}function W(e){void 0===e&&(e=c);var t=e===c?U:function(){return Object(r.useContext)(e)};return function(){return t().store}}var G=W();function Y(e){void 0===e&&(e=c);var t=e===c?G:W(e);return function(){return t().dispatch}}var Z=Y(),$=function(e,t){return e===t};function X(e){void 0===e&&(e=c);var t=e===c?U:function(){return Object(r.useContext)(e)};return function(e,n){void 0===n&&(n=$),b()(e,"You must pass a selector to useSelectors");var i=t();return function(e,t,n,i){var o,a=Object(r.useReducer)((function(e){return e+1}),0)[1],c=Object(r.useMemo)((function(){return new h(n,i)}),[n,i]),s=Object(r.useRef)(),l=Object(r.useRef)(),u=Object(r.useRef)();try{o=e!==l.current||s.current?e(n.getState()):u.current}catch(d){var f="An error occurred while selecting the store state: "+d.message+".";throw s.current&&(f+="\nThe error may be correlated with this previous error:\n"+s.current.stack+"\n\nOriginal stack trace:"),new Error(f)}return k((function(){l.current=e,u.current=o,s.current=void 0})),k((function(){function e(){try{var e=l.current(n.getState());if(t(e,u.current))return;u.current=e}catch(d){s.current=d}a({})}return c.onStateChange=e,c.trySubscribe(),e(),function(){return c.tryUnsubscribe()}}),[n,c]),o}(e,n,i.store,i.subscription)}}var K,Q=X(),J=n(32);n.d(t,"a",(function(){return p})),n.d(t,"d",(function(){return Z})),n.d(t,"b",(function(){return Y})),n.d(t,"e",(function(){return Q})),n.d(t,"c",(function(){return X})),n.d(t,"f",(function(){return G})),K=J.unstable_batchedUpdates,s=K},function(e,t,n){"use strict";function r(){return(r=Object.assign||function(e){for(var t=1;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}}},function(e,t,n){"use strict";!function e(){if("undefined"!==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE){0;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}}(),e.exports=n(302)},function(e,t,n){"use strict";n.d(t,"a",(function(){return i})),n.d(t,"b",(function(){return o})),n.d(t,"c",(function(){return a})),n.d(t,"d",(function(){return c})),n.d(t,"e",(function(){return s})),n.d(t,"f",(function(){return l})),n.d(t,"g",(function(){return p})),n.d(t,"h",(function(){return u})),n.d(t,"i",(function(){return f})),n.d(t,"j",(function(){return h})),n.d(t,"k",(function(){return d}));var r=function(e){return"@@redux-saga/"+e},i=r("CANCEL_PROMISE"),o=r("CHANNEL_END"),a=r("IO"),c=r("MATCH"),s=r("MULTICAST"),l=r("SAGA_ACTION"),u=r("SELF_CANCELLATION"),f=r("TASK"),h=r("TASK_CANCEL"),d=r("TERMINATE"),p=r("LOCATION")},function(e,t,n){var r;!function(){"use strict";var n={}.hasOwnProperty;function i(){for(var e=[],t=0;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";function r(e){return null!=e&&"object"===typeof e&&!0===e["@@functional/placeholder"]}n.d(t,"a",(function(){return r}))},,function(e,t,n){"use strict";n.d(t,"c",(function(){return o})),n.d(t,"e",(function(){return a})),n.d(t,"d",(function(){return c})),n.d(t,"b",(function(){return r})),n.d(t,"a",(function(){return i}));var r,i,o={ANCHOR:"mdc-menu-surface--anchor",ANIMATING_CLOSED:"mdc-menu-surface--animating-closed",ANIMATING_OPEN:"mdc-menu-surface--animating-open",FIXED:"mdc-menu-surface--fixed",OPEN:"mdc-menu-surface--open",ROOT:"mdc-menu-surface"},a={CLOSED_EVENT:"MDCMenuSurface:closed",OPENED_EVENT:"MDCMenuSurface:opened",FOCUSABLE_ELEMENTS:["button:not(:disabled)",'[href]:not([aria-disabled="true"])',"input:not(:disabled)","select:not(:disabled)","textarea:not(:disabled)",'[tabindex]:not([tabindex="-1"]):not([aria-disabled="true"])'].join(", ")},c={TRANSITION_OPEN_DURATION:120,TRANSITION_CLOSE_DURATION:75,MARGIN_TO_EDGE:32,ANCHOR_TO_MENU_SURFACE_WIDTH_RATIO:.67};!function(e){e[e.BOTTOM=1]="BOTTOM",e[e.CENTER=2]="CENTER",e[e.RIGHT=4]="RIGHT",e[e.FLIP_RTL=8]="FLIP_RTL"}(r||(r={})),function(e){e[e.TOP_LEFT=0]="TOP_LEFT",e[e.TOP_RIGHT=4]="TOP_RIGHT",e[e.BOTTOM_LEFT=1]="BOTTOM_LEFT",e[e.BOTTOM_RIGHT=5]="BOTTOM_RIGHT",e[e.TOP_START=8]="TOP_START",e[e.TOP_END=12]="TOP_END",e[e.BOTTOM_START=9]="BOTTOM_START",e[e.BOTTOM_END=13]="BOTTOM_END"}(i||(i={}))},,,function(e,t,n){"use strict";var r=n(52);var i=n(106),o=n(132);var a=n(77),c=n(76);function s(e){return e?1===e.length?e[0]:function(t){return e.reduce((function(e,t){return t(e)}),t)}:c.a}var l=n(59);n.d(t,"a",(function(){return u}));var u=function(){function e(e){this._isScalar=!1,e&&(this._subscribe=e)}return e.prototype.lift=function(t){var n=new e;return n.source=this,n.operator=t,n},e.prototype.subscribe=function(e,t,n){var a=this.operator,c=function(e,t,n){if(e){if(e instanceof r.a)return e;if(e[i.a])return e[i.a]()}return e||t||n?new r.a(e,t,n):new r.a(o.a)}(e,t,n);if(a?c.add(a.call(c,this.source)):c.add(this.source||l.a.useDeprecatedSynchronousErrorHandling&&!c.syncErrorThrowable?this._subscribe(c):this._trySubscribe(c)),l.a.useDeprecatedSynchronousErrorHandling&&c.syncErrorThrowable&&(c.syncErrorThrowable=!1,c.syncErrorThrown))throw c.syncErrorValue;return c},e.prototype._trySubscribe=function(e){try{return this._subscribe(e)}catch(t){l.a.useDeprecatedSynchronousErrorHandling&&(e.syncErrorThrown=!0,e.syncErrorValue=t),!function(e){for(;e;){var t=e,n=t.closed,i=t.destination,o=t.isStopped;if(n||o)return!1;e=i&&i instanceof r.a?i:null}return!0}(e)?console.warn(t):e.error(t)}},e.prototype.forEach=function(e,t){var n=this;return new(t=f(t))((function(t,r){var i;i=n.subscribe((function(t){try{e(t)}catch(n){r(n),i&&i.unsubscribe()}}),r,t)}))},e.prototype._subscribe=function(e){var t=this.source;return t&&t.subscribe(e)},e.prototype[a.a]=function(){return this},e.prototype.pipe=function(){for(var e=[],t=0;t=0||(i[n]=e[n]);return i}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";n.d(t,"a",(function(){return v})),n.d(t,"b",(function(){return f})),n.d(t,"c",(function(){return l})),n.d(t,"d",(function(){return z})),n.d(t,"e",(function(){return c}));var r=n(211),i=function(){return Math.random().toString(36).substring(7).split("").join(".")},o={INIT:"@@redux/INIT"+i(),REPLACE:"@@redux/REPLACE"+i(),PROBE_UNKNOWN_ACTION:function(){return"@@redux/PROBE_UNKNOWN_ACTION"+i()}};function a(e){if("object"!==typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function c(e,t,n){var i;if("function"===typeof t&&"function"===typeof n||"function"===typeof n&&"function"===typeof arguments[3])throw new Error("It looks like you are passing several store enhancers to createStore(). This is not supported. Instead, compose them together to a single function.");if("function"===typeof t&&"undefined"===typeof n&&(n=t,t=void 0),"undefined"!==typeof n){if("function"!==typeof n)throw new Error("Expected the enhancer to be a function.");return n(c)(e,t)}if("function"!==typeof e)throw new Error("Expected the reducer to be a function.");var s=e,l=t,u=[],f=u,h=!1;function d(){f===u&&(f=u.slice())}function p(){if(h)throw new Error("You may not call store.getState() while the reducer is executing. The reducer has already received the state as an argument. Pass it down from the top reducer instead of reading it from the store.");return l}function z(e){if("function"!==typeof e)throw new Error("Expected the listener to be a function.");if(h)throw new Error("You may not call store.subscribe() while the reducer is executing. If you would like to be notified after the store has been updated, subscribe from a component and invoke store.getState() in the callback to access the latest state. See https://redux.js.org/api-reference/store#subscribe(listener) for more details.");var t=!0;return d(),f.push(e),function(){if(t){if(h)throw new Error("You may not unsubscribe from a store listener while the reducer is executing. See https://redux.js.org/api-reference/store#subscribe(listener) for more details.");t=!1,d();var n=f.indexOf(e);f.splice(n,1)}}}function v(e){if(!a(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if("undefined"===typeof e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(h)throw new Error("Reducers may not dispatch actions.");try{h=!0,l=s(l,e)}finally{h=!1}for(var t=u=f,n=0;n=0;)t=c[n],Object(i.a)(t,e)&&!l(r,t)&&(r[r.length]=t),n-=1;return r})):Object(r.a)((function(e){return Object(e)!==e?[]:Object.keys(e)}));t.a=u},function(e,t,n){"use strict";var r=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable;function a(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(i){return!1}}()?Object.assign:function(e,t){for(var n,c,s=a(e),l=1;l8.64*1e15?NaN:function(e){var t=a(e);if(isNaN(t)||l(t,-0))return 0;if(isFinite(t))return t;var n=Math.floor(Math.abs(t));return t<0&&(n=-n),l(n,-0)?0:n}(e):NaN}function s(e){if(null==e)throw new TypeError("undefined/null cannot be converted to object");return Object(e)}function l(e,t){return Object.is?Object.is(e,t):e===t?0!==e||1/e===1/t:e!==e&&t!==t}function u(e){return new Array(e)}function f(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function h(e){return null===e?"Null":"undefined"===typeof e?"Undefined":"function"===typeof e||"object"===typeof e?"Object":"number"===typeof e?"Number":"boolean"===typeof e?"Boolean":"string"===typeof e?"String":"symbol"===typeof e?"Symbol":"bigint"===typeof e?"BigInt":void 0}n.r(t);var d=864e5;function p(e,t){return e-Math.floor(e/t)*t}function z(e){return Math.floor(e/d)}function v(e){return p(z(e)+4,7)}function g(e){return Date.UTC(e,0)/d}function m(e){return Date.UTC(e,0)}function y(e){return new Date(e).getUTCFullYear()}function b(e){return e%4!==0?365:e%100!==0?366:e%400!==0?365:366}function w(e){return z(e)-g(y(e))}function k(e){return 365===b(y(e))?0:1}function x(e){var t=w(e),n=k(e);if(t>=0&&t<31)return 0;if(t<59+n)return 1;if(t<90+n)return 2;if(t<120+n)return 3;if(t<151+n)return 4;if(t<181+n)return 5;if(t<212+n)return 6;if(t<243+n)return 7;if(t<273+n)return 8;if(t<304+n)return 9;if(t<334+n)return 10;if(t<365+n)return 11;throw new Error("Invalid time")}function j(e){var t=w(e),n=x(e),r=k(e);if(0===n)return t+1;if(1===n)return t-30;if(2===n)return t-58-r;if(3===n)return t-89-r;if(4===n)return t-119-r;if(5===n)return t-150-r;if(6===n)return t-180-r;if(7===n)return t-211-r;if(8===n)return t-242-r;if(9===n)return t-272-r;if(10===n)return t-303-r;if(11===n)return t-333-r;throw new Error("Invalid time")}var M=24,_=60,C=60,q=1e3,S=q*C,O=S*_;function T(e){return p(Math.floor(e/O),M)}function E(e){return p(Math.floor(e/S),_)}function A(e){return p(Math.floor(e/q),C)}function H(e,t,n){if("function"!==typeof e)return!1;if(null===n||void 0===n?void 0:n.boundTargetFunction)return t instanceof(null===n||void 0===n?void 0:n.boundTargetFunction);if("object"!==typeof t)return!1;var r=e.prototype;if("object"!==typeof r)throw new TypeError("OrdinaryHasInstance called on an object with an invalid prototype property.");return Object.prototype.isPrototypeOf.call(r,t)}function L(e){return p(e,q)}function D(e){return"undefined"===typeof e?Object.create(null):s(e)}function P(e,t,n,r){if(void 0!==e){if(e=Number(e),isNaN(e)||en)throw new RangeError(e+" is outside of range ["+t+", "+n+"]");return Math.floor(e)}return r}function V(e,t,n,r,i){return P(e[t],n,r,i)}function N(e,t,n,r,i){if("object"!==typeof e)throw new TypeError("Options must be an object");var a=e[t];if(void 0!==a){if("boolean"!==n&&"string"!==n)throw new TypeError("invalid type");if("boolean"===n&&(a=Boolean(a)),"string"===n&&(a=o(a)),void 0!==r&&!r.filter((function(e){return e==a})).length)throw new RangeError(a+" is not within "+r.join(", "));return a}return i}function I(e){if("undefined"===typeof e)return Object.create(null);if("object"===typeof e)return e;throw new TypeError("Options must be an object")}var R=["angle-degree","area-acre","area-hectare","concentr-percent","digital-bit","digital-byte","digital-gigabit","digital-gigabyte","digital-kilobit","digital-kilobyte","digital-megabit","digital-megabyte","digital-petabyte","digital-terabit","digital-terabyte","duration-day","duration-hour","duration-millisecond","duration-minute","duration-month","duration-second","duration-week","duration-year","length-centimeter","length-foot","length-inch","length-kilometer","length-meter","length-mile-scandinavian","length-mile","length-millimeter","length-yard","mass-gram","mass-kilogram","mass-ounce","mass-pound","mass-stone","temperature-celsius","temperature-fahrenheit","volume-fluid-ounce","volume-gallon","volume-liter","volume-milliliter"];function B(e){return e.slice(e.indexOf("-")+1)}var F=R.map(B);function U(e){return F.indexOf(e)>-1}function W(e,t){var n=t.tzData,r=t.uppercaseLinks,i=e.toUpperCase(),o=new Set,a=new Set;return Object.keys(n).map((function(e){return e.toUpperCase()})).forEach((function(e){return o.add(e)})),Object.keys(r).forEach((function(e){a.add(e.toUpperCase()),o.add(r[e].toUpperCase())})),o.has(i)||a.has(i)}var G=/[^A-Z]/;function Y(e){return 3===(e=e.replace(/([a-z])/g,(function(e,t){return t.toUpperCase()}))).length&&!G.test(e)}function Z(e){if(U(e=e.replace(/([A-Z])/g,(function(e,t){return t.toLowerCase()}))))return!0;var t=e.split("-per-");if(2!==t.length)return!1;var n=t[0],r=t[1];return!(!U(n)||!U(r))}function $(e){return Math.floor(Math.log(e)*Math.LOG10E)}function X(e,t){if("function"===typeof e.repeat)return e.repeat(t);for(var n=new Array(t),r=0;rd[d.length-1])return d[d.length-1].length-1;var p=d.indexOf(h);if(-1===p)return 0;var z=d[p];return"0"===u[z].other?0:z.length-u[z].other.match(/0+/)[0].length}}function oe(e,t,n){var r,i,o,a,c=n;if(0===e)r=X("0",c),i=0,o=0;else{var s=e.toString(),l=s.indexOf("e"),u=s.split("e"),f=u[0],h=u[1],d=f.replace(".","");if(l>=0&&d.length<=c)i=+h,r=d+X("0",c-d.length),o=e;else{var p=(i=$(e))-c+1,z=Math.round(g(e,p));g(z,c-1)>=10&&(i+=1,z=Math.floor(z/10)),r=z.toString(),o=g(z,c-1-i)}}if(i>=c-1?(r+=X("0",i-c+1),a=i+1):i>=0?(r=r.slice(0,i+1)+"."+r.slice(i+1),a=i+1):(r="0."+X("0",-i-1)+r,a=1),r.indexOf(".")>=0&&n>t){for(var v=n-t;v>0&&"0"===r[r.length-1];)r=r.slice(0,-1),v--;"."===r[r.length-1]&&(r=r.slice(0,-1))}return{formattedString:r,roundedNumber:o,integerDigitsCount:a};function g(e,t){return t<0?e*Math.pow(10,-t):e/Math.pow(10,t)}}function ae(e,t,n){var r,i,o=n,a=Math.round(e*Math.pow(10,o)),c=a/Math.pow(10,o);if(a<1e21)r=a.toString();else{var s=(r=a.toString()).split("e"),l=s[0],u=s[1];r=l.replace(".",""),r+=X("0",Math.max(+u-r.length+1,0))}if(0!==o){var f=r.length;if(f<=o)r=X("0",o+1-f)+r,f=o+1;var h=r.slice(0,f-o),d=r.slice(f-o);r=h+"."+d,i=h.length}else i=r.length;for(var p=n-t;p>0&&"0"===r[r.length-1];)r=r.slice(0,-1),p--;return"."===r[r.length-1]&&(r=r.slice(0,-1)),{formattedString:r,roundedNumber:c,integerDigitsCount:i}}function ce(e,t){var n,r=t<0||l(t,-0);switch(r&&(t=-t),e.roundingType){case"significantDigits":n=oe(t,e.minimumSignificantDigits,e.maximumSignificantDigits);break;case"fractionDigits":n=ae(t,e.minimumFractionDigits,e.maximumFractionDigits);break;default:(n=oe(t,1,2)).integerDigitsCount>1&&(n=ae(t,0,0))}t=n.roundedNumber;var i=n.formattedString,o=n.integerDigitsCount,a=e.minimumIntegerDigits;o\^`\|~\xA2-\xA6\xA8\xA9\xAC\xAE-\xB1\xB4\xB8\xD7\xF7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0384\u0385\u03F6\u0482\u058D-\u058F\u0606-\u0608\u060B\u060E\u060F\u06DE\u06E9\u06FD\u06FE\u07F6\u07FE\u07FF\u09F2\u09F3\u09FA\u09FB\u0AF1\u0B70\u0BF3-\u0BFA\u0C7F\u0D4F\u0D79\u0E3F\u0F01-\u0F03\u0F13\u0F15-\u0F17\u0F1A-\u0F1F\u0F34\u0F36\u0F38\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE\u0FCF\u0FD5-\u0FD8\u109E\u109F\u1390-\u1399\u166D\u17DB\u1940\u19DE-\u19FF\u1B61-\u1B6A\u1B74-\u1B7C\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2044\u2052\u207A-\u207C\u208A-\u208C\u20A0-\u20BF\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F\u218A\u218B\u2190-\u2307\u230C-\u2328\u232B-\u2426\u2440-\u244A\u249C-\u24E9\u2500-\u2767\u2794-\u27C4\u27C7-\u27E5\u27F0-\u2982\u2999-\u29D7\u29DC-\u29FB\u29FE-\u2B73\u2B76-\u2B95\u2B97-\u2BFF\u2CE5-\u2CEA\u2E50\u2E51\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3004\u3012\u3013\u3020\u3036\u3037\u303E\u303F\u309B\u309C\u3190\u3191\u3196-\u319F\u31C0-\u31E3\u3200-\u321E\u322A-\u3247\u3250\u3260-\u327F\u328A-\u32B0\u32C0-\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA700-\uA716\uA720\uA721\uA789\uA78A\uA828-\uA82B\uA836-\uA839\uAA77-\uAA79\uAB5B\uAB6A\uAB6B\uFB29\uFBB2-\uFBC1\uFDFC\uFDFD\uFE62\uFE64-\uFE66\uFE69\uFF04\uFF0B\uFF1C-\uFF1E\uFF3E\uFF40\uFF5C\uFF5E\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFFC\uFFFD]|\uD800[\uDD37-\uDD3F\uDD79-\uDD89\uDD8C-\uDD8E\uDD90-\uDD9C\uDDA0\uDDD0-\uDDFC]|\uD802[\uDC77\uDC78\uDEC8]|\uD805\uDF3F|\uD807[\uDFD5-\uDFF1]|\uD81A[\uDF3C-\uDF3F\uDF45]|\uD82F\uDC9C|\uD834[\uDC00-\uDCF5\uDD00-\uDD26\uDD29-\uDD64\uDD6A-\uDD6C\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDDE8\uDE00-\uDE41\uDE45\uDF00-\uDF56]|\uD835[\uDEC1\uDEDB\uDEFB\uDF15\uDF35\uDF4F\uDF6F\uDF89\uDFA9\uDFC3]|\uD836[\uDC00-\uDDFF\uDE37-\uDE3A\uDE6D-\uDE74\uDE76-\uDE83\uDE85\uDE86]|\uD838[\uDD4F\uDEFF]|\uD83B[\uDCAC\uDCB0\uDD2E\uDEF0\uDEF1]|\uD83C[\uDC00-\uDC2B\uDC30-\uDC93\uDCA0-\uDCAE\uDCB1-\uDCBF\uDCC1-\uDCCF\uDCD1-\uDCF5\uDD0D-\uDDAD\uDDE6-\uDE02\uDE10-\uDE3B\uDE40-\uDE48\uDE50\uDE51\uDE60-\uDE65\uDF00-\uDFFF]|\uD83D[\uDC00-\uDED7\uDEE0-\uDEEC\uDEF0-\uDEFC\uDF00-\uDF73\uDF80-\uDFD8\uDFE0-\uDFEB]|\uD83E[\uDC00-\uDC0B\uDC10-\uDC47\uDC50-\uDC59\uDC60-\uDC87\uDC90-\uDCAD\uDCB0\uDCB1\uDD00-\uDD78\uDD7A-\uDDCB\uDDCD-\uDE53\uDE60-\uDE6D\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6\uDF00-\uDF92\uDF94-\uDFCA]/,he=new RegExp("^"+fe.source),de=new RegExp(fe.source+"$"),pe=/[#0](?:[\.,][#0]+)*/g;function ze(e,t,n,r){var i,o,a=e.sign,c=e.exponent,s=e.magnitude,l=r.notation,u=r.style,f=r.numberingSystem,h=t.numbers.nu[0],d=null;if("compact"===l&&s&&(d=function(e,t,n,r,i,o,a){var c,s,l=e.roundedNumber,u=e.sign,f=e.magnitude,h=String(Math.pow(10,f)),d=n.numbers.nu[0];if("currency"===r&&"name"!==o){var p=(v=n.numbers.currency)[a]||v[d],z=null===(c=p.short)||void 0===c?void 0:c[h];if(!z)return null;s=me(t,l,z)}else{var v,g=((v=n.numbers.decimal)[a]||v[d])[i][h];if(!g)return null;s=me(t,l,g)}if("0"===s)return null;return s=ge(s,u).replace(/([^\s;\-\+\d\xa4]+)/g,"{c:$1}").replace(/0+/,"0")}(e,n,t,u,r.compactDisplay,r.currencyDisplay,f)),"currency"===u&&"name"!==r.currencyDisplay){var p=t.currencies[r.currency];if(p)switch(r.currencyDisplay){case"code":i=r.currency;break;case"symbol":i=p.symbol;break;default:i=p.narrow}else i=r.currency}if(d)o=d;else if("decimal"===u||"unit"===u||"currency"===u&&"name"===r.currencyDisplay)o=ge((t.numbers.decimal[f]||t.numbers.decimal[h]).standard,a);else if("currency"===u){o=ge((v=t.numbers.currency[f]||t.numbers.currency[h])[r.currencySign],a)}else{o=ge(t.numbers.percent[f]||t.numbers.percent[h],a)}var z=pe.exec(o)[0];if(o=o.replace(pe,"{0}").replace(/'(.)'/g,"$1"),"currency"===u&&"name"!==r.currencyDisplay){var v,g=(v=t.numbers.currency[f]||t.numbers.currency[h]).currencySpacing.afterInsertBetween;g&&!de.test(i)&&(o=o.replace("\xa4{0}","\xa4"+g+"{0}"));var m=v.currencySpacing.beforeInsertBetween;m&&!he.test(i)&&(o=o.replace("{0}\xa4","{0}"+m+"\xa4"))}for(var y=o.split(/({c:[^}]+}|\{0\}|[\xa4%\-\+])/g),b=[],w=t.numbers.symbols[f]||t.numbers.symbols[h],k=0,x=y;k0?(f=s.slice(0,d),h=s.slice(d+1)):f=s,o&&("compact"!==n||l>=1e4)){var p=e.group,z=[],v=a.split(".")[0].split(","),g=3,m=3;v.length>1&&(g=v[v.length-1].length),v.length>2&&(m=v[v.length-2].length);var y=f.length-g;if(y>0){for(z.push(f.slice(y,y+g)),y-=m;y>0;y-=m)z.push(f.slice(y,y+m));z.push(f.slice(0,y+m))}else z.push(f);for(;z.length>0;){var b=z.pop();c.push({type:"integer",value:b}),z.length>0&&c.push({type:"group",value:p})}}else c.push({type:"integer",value:f});if(void 0!==h&&c.push({type:"decimal",value:e.decimal},{type:"fraction",value:h}),("scientific"===n||"engineering"===n)&&isFinite(l)){c.push({type:"exponentSeparator",value:e.exponential}),r<0&&(c.push({type:"exponentMinusSign",value:e.minusSign}),r=-r);var w=ae(r,0,0);c.push({type:"exponentInteger",value:w.formattedString})}return c}function ge(e,t){e.indexOf(";")<0&&(e=e+";-"+e);var n=e.split(";"),r=n[0],i=n[1];switch(t){case 0:return r;case-1:return i;default:return i.indexOf("-")>=0?i.replace(/-/g,"+"):"+"+r}}function me(e,t,n){return n[e.select(t)]||n.other}function ye(e,t,n){var r,i,o,a=n.getInternalSlots,c=a(e),s=c.pl,u=c.dataLocaleData,f=c.numberingSystem,h=u.numbers.symbols[f]||u.numbers.symbols[u.numbers.nu[0]],d=0,p=0;if(isNaN(t))i=h.nan;else if(isFinite(t)){"percent"===c.style&&(t*=100),p=(r=se(e,t,{getInternalSlots:a}))[0],d=r[1];var z=ce(c,t=p<0?t*Math.pow(10,-p):t/Math.pow(10,p));i=z.formattedString,t=z.roundedNumber}else i=h.infinity;switch(c.signDisplay){case"never":o=0;break;case"auto":o=l(t,0)||t>0||isNaN(t)?0:-1;break;case"always":o=l(t,0)||t>0||isNaN(t)?1:-1;break;default:o=0===t||isNaN(t)?0:t>0?1:-1}return ze({roundedNumber:t,formattedString:i,exponent:p,magnitude:d,sign:o},c.dataLocaleData,s,c)}function be(e,t,n){for(var r=ye(e,t,n),i=u(0),o=0,a=r;o-1;)re((r=e.indexOf("}",n))>n,"Invalid pattern "+e),n>i&&t.push({type:"literal",value:e.substring(i,n)}),t.push({type:e.substring(n+1,r),value:void 0}),i=r+1,n=e.indexOf("{",i);return iu)return-1;null!==s&&void 0!==s||(s=0);var f,h=function(e){return e>=0&&ea){if(s>0&&h(f=d-1)&&c[f]a)return d;e=a,t=c,n=s,r=d+1,i=u,o=!0,h=d=p=f=void 0}}},t.dateParser=function(e){var t,n;if((-1==e.search("-")||-1!=e.search("T")||-1!=e.search("Z"))&&(n=z(e))&&!isNaN(n))return n;if(-1!=e.search("-")){for(t=e.replace("-","/","g");-1!=t.search("-");)t=t.replace("-","/");n=z(t)}else 8==e.length?(t=e.substr(0,4)+"/"+e.substr(4,2)+"/"+e.substr(6,2),n=z(t)):n=z(e);n&&!isNaN(n)||console.error("Couldn't parse "+e+" as a date");return n},t.dateStrToMillis=z,t.update=function(e,t){if("undefined"!=typeof t&&null!==t)for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e},t.updateDeep=function e(t,n){function r(e){return"object"===typeof Node?e instanceof Node:"object"===typeof e&&"number"===typeof e.nodeType&&"string"===typeof e.nodeName}if("undefined"!=typeof n&&null!==n)for(var i in n)n.hasOwnProperty(i)&&(null===n[i]?t[i]=null:v(n[i])?t[i]=n[i].slice():r(n[i])?t[i]=n[i]:"object"==typeof n[i]?("object"==typeof t[i]&&null!==t[i]||(t[i]={}),e(t[i],n[i])):t[i]=n[i]);return t},t.isArrayLike=v,t.isDateLike=function(e){if("object"!=typeof e||null===e||"function"!=typeof e.getTime)return!1;return!0},t.clone=function e(t){for(var n=[],r=0;r=t||m.call(window,(function(){var t=(new Date).getTime()-a;i=o;var l=(o=Math.floor(t/n))-i;o+l>c||o>=c?(e(c),r()):(0!==l&&e(o),s())}))}()},t.isPixelChangingOptionList=function(e,t){var n={};if(e)for(var r=1;r=r.Granularity.DECADAL)return""+o;if(t>=r.Granularity.MONTHLY)return q[a]+" "+o;if(0===3600*s+60*d+p+.001*z||t>=r.Granularity.DAILY)return l(c)+" "+q[a];if(tr.Granularity.MINUTELY?h(s,d,p,0):h(s,d,p,z)},t.dateValueFormatter=function(e,t){return d(e,t("labelsUTC"))};var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}(n(202));t.LOG_SCALE=10;var i=Math.log(10);t.LN_TEN=i;var o=function(e){return Math.log(e)/i};t.log10=o;t.logRangeFraction=function(e,t,n){var r=o(e),i=r+n*(o(t)-r);return Math.pow(10,i)};t.DOTTED_LINE=[2,2];t.DASHED_LINE=[7,3];t.DOT_DASH_LINE=[7,2,2,2];t.HORIZONTAL=1;t.VERTICAL=2;t.getContext=function(e){return e.getContext("2d")};function a(e){return!e.pageX||e.pageX<0?0:e.pageX}function c(e){return!e.pageY||e.pageY<0?0:e.pageY}function s(e,t){var n=Math.min(Math.max(1,t||2),21);return Math.abs(e)<.001&&0!==e?e.toExponential(n-1):e.toPrecision(n)}function l(e){return e<10?"0"+e:""+e}t.addEvent=function(e,t,n){e.addEventListener(t,n,!1)};var u={getFullYear:function(e){return e.getFullYear()},getMonth:function(e){return e.getMonth()},getDate:function(e){return e.getDate()},getHours:function(e){return e.getHours()},getMinutes:function(e){return e.getMinutes()},getSeconds:function(e){return e.getSeconds()},getMilliseconds:function(e){return e.getMilliseconds()},getDay:function(e){return e.getDay()},makeDate:function(e,t,n,r,i,o,a){return new Date(e,t,n,r,i,o,a)}};t.DateAccessorsLocal=u;var f={getFullYear:function(e){return e.getUTCFullYear()},getMonth:function(e){return e.getUTCMonth()},getDate:function(e){return e.getUTCDate()},getHours:function(e){return e.getUTCHours()},getMinutes:function(e){return e.getUTCMinutes()},getSeconds:function(e){return e.getUTCSeconds()},getMilliseconds:function(e){return e.getUTCMilliseconds()},getDay:function(e){return e.getUTCDay()},makeDate:function(e,t,n,r,i,o,a){return new Date(Date.UTC(e,t,n,r,i,o,a))}};function h(e,t,n,r){var i=l(e)+":"+l(t);if(n&&(i+=":"+l(n),r)){var o=""+r;i+="."+("000"+o).substring(o.length)}return i}function d(e,t){var n=t?f:u,r=new Date(e),i=n.getFullYear(r),o=n.getMonth(r),a=n.getDate(r),c=n.getHours(r),s=n.getMinutes(r),d=n.getSeconds(r),p=n.getMilliseconds(r),z=""+i+"/"+l(o+1)+"/"+l(a);return 3600*c+60*s+d+.001*p&&(z+=" "+h(c,s,d,p)),z}function p(e,t){var n=Math.pow(10,t);return Math.round(e*n)/n}function z(e){return new Date(e).getTime()}function v(e){var t=typeof e;return("object"==t||"function"==t&&"function"==typeof e.item)&&null!==e&&"number"==typeof e.length&&3!==e.nodeType}function g(e,t,n,r){t=t||0,n=n||e.length,this.hasNext=!0,this.peek=null,this.start_=t,this.array_=e,this.predicate_=r,this.end_=Math.min(e.length,t+n),this.nextIdx_=t-1,this.next()}t.DateAccessorsUTC=f,g.prototype.next=function(){if(!this.hasNext)return null;for(var e=this.peek,t=this.nextIdx_+1,n=!1;t=Math.pow(10,o)||Math.abs(e)=0;z--,d/=l)if(h>=d){r=p(e/d,i)+u[z];break}if(c){var v=String(e.toExponential()).split("e-");2===v.length&&v[1]>=3&&v[1]<=24&&(r=v[1]%3>0?p(v[0]/w(10,v[1]%3),i):Number(v[0]).toFixed(2),r+=f[Math.floor(v[1]/3)-1])}}return r}var q=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]},function(e,t,n){"use strict";t.a=Array.isArray||function(e){return null!=e&&e.length>=0&&"[object Array]"===Object.prototype.toString.call(e)}},function(e,t,n){"use strict";function r(e){return"[object String]"===Object.prototype.toString.call(e)}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";n.d(t,"a",(function(){return a}));var r=n(25),i=n(24),o=n(20);function a(e,t){Object(o.a)(1,arguments);var n=t||{},a=n.locale,c=a&&a.options&&a.options.weekStartsOn,s=null==c?0:Object(r.a)(c),l=null==n.weekStartsOn?s:Object(r.a)(n.weekStartsOn);if(!(l>=0&&l<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");var u=Object(i.a)(e),f=u.getUTCDay(),h=(f=0&&l<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");var u=Object(i.default)(e),f=u.getUTCDay(),h=(f0&&t.forEach((function(e){o.remove(e)})),o.appendTo(n.scrollbarXRail,e)),e.contains(n.scrollbarYRail)||((t=o.queryChildren(e,".ps-scrollbar-y-rail")).length>0&&t.forEach((function(e){o.remove(e)})),o.appendTo(n.scrollbarYRail,e)),!n.settings.suppressScrollX&&n.containerWidth+n.settings.scrollXMarginOffset=n.railXWidth-n.scrollbarXWidth&&(n.scrollbarXLeft=n.railXWidth-n.scrollbarXWidth),n.scrollbarYTop>=n.railYHeight-n.scrollbarYHeight&&(n.scrollbarYTop=n.railYHeight-n.scrollbarYHeight),function(e,t){var n={width:t.railXWidth};t.isRtl?n.left=t.negativeScrollAdjustment+e.scrollLeft+t.containerWidth-t.contentWidth:n.left=e.scrollLeft,t.isScrollbarXUsingBottom?n.bottom=t.scrollbarXBottom-e.scrollTop:n.top=t.scrollbarXTop+e.scrollTop,o.css(t.scrollbarXRail,n);var r={top:e.scrollTop,height:t.railYHeight};t.isScrollbarYUsingRight?t.isRtl?r.right=t.contentWidth-(t.negativeScrollAdjustment+e.scrollLeft)-t.scrollbarYRight-t.scrollbarYOuterWidth:r.right=t.scrollbarYRight-e.scrollLeft:t.isRtl?r.left=t.negativeScrollAdjustment+e.scrollLeft+2*t.containerWidth-t.contentWidth-t.scrollbarYLeft-t.scrollbarYOuterWidth:r.left=t.scrollbarYLeft+e.scrollLeft,o.css(t.scrollbarYRail,r),o.css(t.scrollbarX,{left:t.scrollbarXLeft,width:t.scrollbarXWidth-t.railBorderXWidth}),o.css(t.scrollbarY,{top:t.scrollbarYTop,height:t.scrollbarYHeight-t.railBorderYWidth})}(e,n),n.scrollbarXActive?i.add(e,"ps-active-x"):(i.remove(e,"ps-active-x"),n.scrollbarXWidth=0,n.scrollbarXLeft=0,c(e,"left",0)),n.scrollbarYActive?i.add(e,"ps-active-y"):(i.remove(e,"ps-active-y"),n.scrollbarYHeight=0,n.scrollbarYTop=0,c(e,"top",0))}},function(e,t,n){"use strict";e.exports=function(e,t,n,r,i,o,a,c){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,r,i,o,a,c],u=0;(s=new Error(t.replace(/%s/g,(function(){return l[u++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},function(e,t,n){"use strict";function r(e){setTimeout((function(){throw e}),0)}n.d(t,"a",(function(){return r}))},,,function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(r){"object"===typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";var r,i,o=n(64),a=function(e){var t=document.createEvent("Event");return t.initEvent(e,!0,!0),t};e.exports=function(e,t,n){if("undefined"===typeof e)throw"You must provide an element to the update-scroll function";if("undefined"===typeof t)throw"You must provide an axis to the update-scroll function";if("undefined"===typeof n)throw"You must provide a value to the update-scroll function";"top"===t&&n<=0&&(e.scrollTop=n=0,e.dispatchEvent(a("ps-y-reach-start"))),"left"===t&&n<=0&&(e.scrollLeft=n=0,e.dispatchEvent(a("ps-x-reach-start")));var c=o.get(e);"top"===t&&n>=c.contentHeight-c.containerHeight&&((n=c.contentHeight-c.containerHeight)-e.scrollTop<=1?n=e.scrollTop:e.scrollTop=n,e.dispatchEvent(a("ps-y-reach-end"))),"left"===t&&n>=c.contentWidth-c.containerWidth&&((n=c.contentWidth-c.containerWidth)-e.scrollLeft<=1?n=e.scrollLeft:e.scrollLeft=n,e.dispatchEvent(a("ps-x-reach-end"))),r||(r=e.scrollTop),i||(i=e.scrollLeft),"top"===t&&nr&&e.dispatchEvent(a("ps-scroll-down")),"left"===t&&ni&&e.dispatchEvent(a("ps-scroll-right")),"top"===t&&(e.scrollTop=r=n,e.dispatchEvent(a("ps-scroll-y"))),"left"===t&&(e.scrollLeft=i=n,e.dispatchEvent(a("ps-scroll-x")))}},function(e,t,n){e.exports=n(318)},function(e,t,n){"use strict";function r(){return"function"===typeof Symbol&&Symbol.iterator?Symbol.iterator:"@@iterator"}n.d(t,"a",(function(){return i}));var i=r()},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=n(31),i=function(e){function t(t,n,r){var i=e.call(this)||this;return i.parent=t,i.outerValue=n,i.outerIndex=r,i.index=0,i}return r.b(t,e),t.prototype._next=function(e){this.parent.notifyNext(this.outerValue,e,this.outerIndex,this.index++,this)},t.prototype._error=function(e){this.parent.notifyError(e,this),this.unsubscribe()},t.prototype._complete=function(){this.parent.notifyComplete(this),this.unsubscribe()},t}(n(52).a)},function(e,t,n){"use strict";n.d(t,"a",(function(){return r}));var r=function(){function e(e){void 0===e&&(e={}),this.adapter_=e}return Object.defineProperty(e,"cssClasses",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return{}},enumerable:!0,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{}},enumerable:!0,configurable:!0}),e.prototype.init=function(){},e.prototype.destroy=function(){},e}()},function(e,t,n){"use strict";var r=n(30),i=n(69),o=n(70),a=Object(r.a)((function(e){return!!Object(i.a)(e)||!!e&&("object"===typeof e&&(!Object(o.a)(e)&&(1===e.nodeType?!!e.length:0===e.length||e.length>0&&(e.hasOwnProperty(0)&&e.hasOwnProperty(e.length-1)))))})),c=function(){function e(e){this.f=e}return e.prototype["@@transducer/init"]=function(){throw new Error("init not implemented on XWrap")},e.prototype["@@transducer/result"]=function(e){return e},e.prototype["@@transducer/step"]=function(e,t){return this.f(e,t)},e}();var s=n(67),l=n(19),u=Object(l.a)((function(e,t){return Object(s.a)(e.length,(function(){return e.apply(t,arguments)}))}));function f(e,t,n){for(var r=n.next();!r.done;){if((t=e["@@transducer/step"](t,r.value))&&t["@@transducer/reduced"]){t=t["@@transducer/value"];break}r=n.next()}return e["@@transducer/result"](t)}function h(e,t,n,r){return e["@@transducer/result"](n[r](u(e["@@transducer/step"],e),t))}n.d(t,"a",(function(){return p}));var d="undefined"!==typeof Symbol?Symbol.iterator:"@@iterator";function p(e,t,n){if("function"===typeof e&&(e=function(e){return new c(e)}(e)),a(n))return function(e,t,n){for(var r=0,i=n.length;r>>0;for(t=0;t0)for(n=0;n=0?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}r.suppressDeprecationWarnings=!1,r.deprecationHandler=null,j=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)a(e,t)&&n.push(t);return n};var T=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,E=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,A={},H={};function L(e,t,n,r){var i=r;"string"===typeof r&&(i=function(){return this[r]()}),e&&(H[e]=i),t&&(H[t[0]]=function(){return O(i.apply(this,arguments),t[1],t[2])}),n&&(H[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function D(e,t){return e.isValid()?(t=P(t,e.localeData()),A[t]=A[t]||function(e){var t,n,r,i=e.match(T);for(t=0,n=i.length;t=0&&E.test(e);)e=e.replace(E,r),E.lastIndex=0,n-=1;return e}var V={};function N(e,t){var n=e.toLowerCase();V[n]=V[n+"s"]=V[t]=e}function I(e){return"string"===typeof e?V[e]||V[e.toLowerCase()]:void 0}function R(e){var t,n,r={};for(n in e)a(e,n)&&(t=I(n))&&(r[t]=e[n]);return r}var B={};function F(e,t){B[e]=t}function U(e){return e%4===0&&e%100!==0||e%400===0}function W(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function G(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=W(t)),n}function Y(e,t){return function(n){return null!=n?($(this,e,n),r.updateOffset(this,t),this):Z(this,e)}}function Z(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function $(e,t,n){e.isValid()&&!isNaN(n)&&("FullYear"===t&&U(e.year())&&1===e.month()&&29===e.date()?(n=G(n),e._d["set"+(e._isUTC?"UTC":"")+t](n,e.month(),Oe(n,e.month()))):e._d["set"+(e._isUTC?"UTC":"")+t](n))}var X,K=/\d/,Q=/\d\d/,J=/\d{3}/,ee=/\d{4}/,te=/[+-]?\d{6}/,ne=/\d\d?/,re=/\d\d\d\d?/,ie=/\d\d\d\d\d\d?/,oe=/\d{1,3}/,ae=/\d{1,4}/,ce=/[+-]?\d{1,6}/,se=/\d+/,le=/[+-]?\d+/,ue=/Z|[+-]\d\d:?\d\d/gi,fe=/Z|[+-]\d\d(?::?\d\d)?/gi,he=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i;function de(e,t,n){X[e]=C(t)?t:function(e,r){return e&&n?n:t}}function pe(e,t){return a(X,e)?X[e](t._strict,t._locale):new RegExp(ze(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(e,t,n,r,i){return t||n||r||i}))))}function ze(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}X={};var ve={};function ge(e,t){var n,r=t;for("string"===typeof e&&(e=[e]),l(t)&&(r=function(e,n){n[t]=G(e)}),n=0;n68?1900:2e3)};var Re=Y("FullYear",!0);function Be(e,t,n,r,i,o,a){var c;return e<100&&e>=0?(c=new Date(e+400,t,n,r,i,o,a),isFinite(c.getFullYear())&&c.setFullYear(e)):c=new Date(e,t,n,r,i,o,a),c}function Fe(e){var t,n;return e<100&&e>=0?((n=Array.prototype.slice.call(arguments))[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)):t=new Date(Date.UTC.apply(null,arguments)),t}function Ue(e,t,n){var r=7+t-n;return-(7+Fe(e,0,r).getUTCDay()-t)%7+r-1}function We(e,t,n,r,i){var o,a,c=1+7*(t-1)+(7+n-r)%7+Ue(e,r,i);return c<=0?a=Ie(o=e-1)+c:c>Ie(e)?(o=e+1,a=c-Ie(e)):(o=e,a=c),{year:o,dayOfYear:a}}function Ge(e,t,n){var r,i,o=Ue(e.year(),t,n),a=Math.floor((e.dayOfYear()-o-1)/7)+1;return a<1?r=a+Ye(i=e.year()-1,t,n):a>Ye(e.year(),t,n)?(r=a-Ye(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function Ye(e,t,n){var r=Ue(e,t,n),i=Ue(e+1,t,n);return(Ie(e)-r+i)/7}function Ze(e,t){return e.slice(t,7).concat(e.slice(0,t))}L("w",["ww",2],"wo","week"),L("W",["WW",2],"Wo","isoWeek"),N("week","w"),N("isoWeek","W"),F("week",5),F("isoWeek",5),de("w",ne),de("ww",ne,Q),de("W",ne),de("WW",ne,Q),me(["w","ww","W","WW"],(function(e,t,n,r){t[r.substr(0,1)]=G(e)})),L("d",0,"do","day"),L("dd",0,0,(function(e){return this.localeData().weekdaysMin(this,e)})),L("ddd",0,0,(function(e){return this.localeData().weekdaysShort(this,e)})),L("dddd",0,0,(function(e){return this.localeData().weekdays(this,e)})),L("e",0,0,"weekday"),L("E",0,0,"isoWeekday"),N("day","d"),N("weekday","e"),N("isoWeekday","E"),F("day",11),F("weekday",11),F("isoWeekday",11),de("d",ne),de("e",ne),de("E",ne),de("dd",(function(e,t){return t.weekdaysMinRegex(e)})),de("ddd",(function(e,t){return t.weekdaysShortRegex(e)})),de("dddd",(function(e,t){return t.weekdaysRegex(e)})),me(["dd","ddd","dddd"],(function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:p(n).invalidWeekday=e})),me(["d","e","E"],(function(e,t,n,r){t[r]=G(e)}));var $e="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Xe="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Ke="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),Qe=he,Je=he,et=he;function tt(e,t,n){var r,i,o,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)o=d([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(o,"").toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(o,"").toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(o,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(i=be.call(this._weekdaysParse,a))?i:null:"ddd"===t?-1!==(i=be.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=be.call(this._minWeekdaysParse,a))?i:null:"dddd"===t?-1!==(i=be.call(this._weekdaysParse,a))?i:-1!==(i=be.call(this._shortWeekdaysParse,a))?i:-1!==(i=be.call(this._minWeekdaysParse,a))?i:null:"ddd"===t?-1!==(i=be.call(this._shortWeekdaysParse,a))?i:-1!==(i=be.call(this._weekdaysParse,a))?i:-1!==(i=be.call(this._minWeekdaysParse,a))?i:null:-1!==(i=be.call(this._minWeekdaysParse,a))?i:-1!==(i=be.call(this._weekdaysParse,a))?i:-1!==(i=be.call(this._shortWeekdaysParse,a))?i:null}function nt(){function e(e,t){return t.length-e.length}var t,n,r,i,o,a=[],c=[],s=[],l=[];for(t=0;t<7;t++)n=d([2e3,1]).day(t),r=ze(this.weekdaysMin(n,"")),i=ze(this.weekdaysShort(n,"")),o=ze(this.weekdays(n,"")),a.push(r),c.push(i),s.push(o),l.push(r),l.push(i),l.push(o);a.sort(e),c.sort(e),s.sort(e),l.sort(e),this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+c.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function rt(){return this.hours()%12||12}function it(e,t){L(e,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)}))}function ot(e,t){return t._meridiemParse}L("H",["HH",2],0,"hour"),L("h",["hh",2],0,rt),L("k",["kk",2],0,(function(){return this.hours()||24})),L("hmm",0,0,(function(){return""+rt.apply(this)+O(this.minutes(),2)})),L("hmmss",0,0,(function(){return""+rt.apply(this)+O(this.minutes(),2)+O(this.seconds(),2)})),L("Hmm",0,0,(function(){return""+this.hours()+O(this.minutes(),2)})),L("Hmmss",0,0,(function(){return""+this.hours()+O(this.minutes(),2)+O(this.seconds(),2)})),it("a",!0),it("A",!1),N("hour","h"),F("hour",13),de("a",ot),de("A",ot),de("H",ne),de("h",ne),de("k",ne),de("HH",ne,Q),de("hh",ne,Q),de("kk",ne,Q),de("hmm",re),de("hmmss",ie),de("Hmm",re),de("Hmmss",ie),ge(["H","HH"],je),ge(["k","kk"],(function(e,t,n){var r=G(e);t[je]=24===r?0:r})),ge(["a","A"],(function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e})),ge(["h","hh"],(function(e,t,n){t[je]=G(e),p(n).bigHour=!0})),ge("hmm",(function(e,t,n){var r=e.length-2;t[je]=G(e.substr(0,r)),t[Me]=G(e.substr(r)),p(n).bigHour=!0})),ge("hmmss",(function(e,t,n){var r=e.length-4,i=e.length-2;t[je]=G(e.substr(0,r)),t[Me]=G(e.substr(r,2)),t[_e]=G(e.substr(i)),p(n).bigHour=!0})),ge("Hmm",(function(e,t,n){var r=e.length-2;t[je]=G(e.substr(0,r)),t[Me]=G(e.substr(r))})),ge("Hmmss",(function(e,t,n){var r=e.length-4,i=e.length-2;t[je]=G(e.substr(0,r)),t[Me]=G(e.substr(r,2)),t[_e]=G(e.substr(i))}));var at,ct=Y("Hours",!0),st={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Te,monthsShort:Ee,week:{dow:0,doy:6},weekdays:$e,weekdaysMin:Ke,weekdaysShort:Xe,meridiemParse:/[ap]\.?m?\.?/i},lt={},ut={};function ft(e,t){var n,r=Math.min(e.length,t.length);for(n=0;n0;){if(r=dt(i.slice(0,t).join("-")))return r;if(n&&n.length>=t&&ft(i,n)>=t-1)break;t--}o++}return at}(e)}function gt(e){var t,n=e._a;return n&&-2===p(e).overflow&&(t=n[ke]<0||n[ke]>11?ke:n[xe]<1||n[xe]>Oe(n[we],n[ke])?xe:n[je]<0||n[je]>24||24===n[je]&&(0!==n[Me]||0!==n[_e]||0!==n[Ce])?je:n[Me]<0||n[Me]>59?Me:n[_e]<0||n[_e]>59?_e:n[Ce]<0||n[Ce]>999?Ce:-1,p(e)._overflowDayOfYear&&(txe)&&(t=xe),p(e)._overflowWeeks&&-1===t&&(t=qe),p(e)._overflowWeekday&&-1===t&&(t=Se),p(e).overflow=t),e}var mt=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,yt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,bt=/Z|[+-]\d\d(?::?\d\d)?/,wt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/],["YYYYMM",/\d{6}/,!1],["YYYY",/\d{4}/,!1]],kt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],xt=/^\/?Date\((-?\d+)/i,jt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,Mt={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function _t(e){var t,n,r,i,o,a,c=e._i,s=mt.exec(c)||yt.exec(c);if(s){for(p(e).iso=!0,t=0,n=wt.length;t7)&&(s=!0)):(o=e._locale._week.dow,a=e._locale._week.doy,l=Ge(Ht(),o,a),n=St(t.gg,e._a[we],l.year),r=St(t.w,l.week),null!=t.d?((i=t.d)<0||i>6)&&(s=!0):null!=t.e?(i=t.e+o,(t.e<0||t.e>6)&&(s=!0)):i=o),r<1||r>Ye(n,o,a)?p(e)._overflowWeeks=!0:null!=s?p(e)._overflowWeekday=!0:(c=We(n,r,i,o,a),e._a[we]=c.year,e._dayOfYear=c.dayOfYear)}(e),null!=e._dayOfYear&&(a=St(e._a[we],i[we]),(e._dayOfYear>Ie(a)||0===e._dayOfYear)&&(p(e)._overflowDayOfYear=!0),n=Fe(a,0,e._dayOfYear),e._a[ke]=n.getUTCMonth(),e._a[xe]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=c[t]=i[t];for(;t<7;t++)e._a[t]=c[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[je]&&0===e._a[Me]&&0===e._a[_e]&&0===e._a[Ce]&&(e._nextDay=!0,e._a[je]=0),e._d=(e._useUTC?Fe:Be).apply(null,c),o=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[je]=24),e._w&&"undefined"!==typeof e._w.d&&e._w.d!==o&&(p(e).weekdayMismatch=!0)}}function Tt(e){if(e._f!==r.ISO_8601)if(e._f!==r.RFC_2822){e._a=[],p(e).empty=!0;var t,n,i,o,a,c,s=""+e._i,l=s.length,u=0;for(i=P(e._f,e._locale).match(T)||[],t=0;t0&&p(e).unusedInput.push(a),s=s.slice(s.indexOf(n)+n.length),u+=n.length),H[o]?(n?p(e).empty=!1:p(e).unusedTokens.push(o),ye(o,n,e)):e._strict&&!n&&p(e).unusedTokens.push(o);p(e).charsLeftOver=l-u,s.length>0&&p(e).unusedInput.push(s),e._a[je]<=12&&!0===p(e).bigHour&&e._a[je]>0&&(p(e).bigHour=void 0),p(e).parsedDateParts=e._a.slice(0),p(e).meridiem=e._meridiem,e._a[je]=function(e,t,n){var r;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?((r=e.isPM(n))&&t<12&&(t+=12),r||12!==t||(t=0),t):t}(e._locale,e._a[je],e._meridiem),null!==(c=p(e).era)&&(e._a[we]=e._locale.erasConvertYear(c,e._a[we])),Ot(e),gt(e)}else qt(e);else _t(e)}function Et(e){var t=e._i,n=e._f;return e._locale=e._locale||vt(e._l),null===t||void 0===n&&""===t?v({nullInput:!0}):("string"===typeof t&&(e._i=t=e._locale.preparse(t)),w(t)?new b(gt(t)):(u(t)?e._d=t:i(n)?function(e){var t,n,r,i,o,a,c=!1;if(0===e._f.length)return p(e).invalidFormat=!0,void(e._d=new Date(NaN));for(i=0;ithis?this:e:v()}));function Pt(e,t){var n,r;if(1===t.length&&i(t[0])&&(t=t[0]),!t.length)return Ht();for(n=t[0],r=1;r=0?new Date(e+400,t,n)-hn:new Date(e,t,n).valueOf()}function zn(e,t,n){return e<100&&e>=0?Date.UTC(e+400,t,n)-hn:Date.UTC(e,t,n)}function vn(e,t){return t.erasAbbrRegex(e)}function gn(){var e,t,n=[],r=[],i=[],o=[],a=this.eras();for(e=0,t=a.length;e(o=Ye(e,r,i))&&(t=o),bn.call(this,e,t,n,r,i))}function bn(e,t,n,r,i){var o=We(e,t,n,r,i),a=Fe(o.year,0,o.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}L("N",0,0,"eraAbbr"),L("NN",0,0,"eraAbbr"),L("NNN",0,0,"eraAbbr"),L("NNNN",0,0,"eraName"),L("NNNNN",0,0,"eraNarrow"),L("y",["y",1],"yo","eraYear"),L("y",["yy",2],0,"eraYear"),L("y",["yyy",3],0,"eraYear"),L("y",["yyyy",4],0,"eraYear"),de("N",vn),de("NN",vn),de("NNN",vn),de("NNNN",(function(e,t){return t.erasNameRegex(e)})),de("NNNNN",(function(e,t){return t.erasNarrowRegex(e)})),ge(["N","NN","NNN","NNNN","NNNNN"],(function(e,t,n,r){var i=n._locale.erasParse(e,r,n._strict);i?p(n).era=i:p(n).invalidEra=e})),de("y",se),de("yy",se),de("yyy",se),de("yyyy",se),de("yo",(function(e,t){return t._eraYearOrdinalRegex||se})),ge(["y","yy","yyy","yyyy"],we),ge(["yo"],(function(e,t,n,r){var i;n._locale._eraYearOrdinalRegex&&(i=e.match(n._locale._eraYearOrdinalRegex)),n._locale.eraYearOrdinalParse?t[we]=n._locale.eraYearOrdinalParse(e,i):t[we]=parseInt(e,10)})),L(0,["gg",2],0,(function(){return this.weekYear()%100})),L(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),mn("gggg","weekYear"),mn("ggggg","weekYear"),mn("GGGG","isoWeekYear"),mn("GGGGG","isoWeekYear"),N("weekYear","gg"),N("isoWeekYear","GG"),F("weekYear",1),F("isoWeekYear",1),de("G",le),de("g",le),de("GG",ne,Q),de("gg",ne,Q),de("GGGG",ae,ee),de("gggg",ae,ee),de("GGGGG",ce,te),de("ggggg",ce,te),me(["gggg","ggggg","GGGG","GGGGG"],(function(e,t,n,r){t[r.substr(0,2)]=G(e)})),me(["gg","GG"],(function(e,t,n,i){t[i]=r.parseTwoDigitYear(e)})),L("Q",0,"Qo","quarter"),N("quarter","Q"),F("quarter",7),de("Q",K),ge("Q",(function(e,t){t[ke]=3*(G(e)-1)})),L("D",["DD",2],"Do","date"),N("date","D"),F("date",9),de("D",ne),de("DD",ne,Q),de("Do",(function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient})),ge(["D","DD"],xe),ge("Do",(function(e,t){t[xe]=G(e.match(ne)[0])}));var wn=Y("Date",!0);L("DDD",["DDDD",3],"DDDo","dayOfYear"),N("dayOfYear","DDD"),F("dayOfYear",4),de("DDD",oe),de("DDDD",J),ge(["DDD","DDDD"],(function(e,t,n){n._dayOfYear=G(e)})),L("m",["mm",2],0,"minute"),N("minute","m"),F("minute",14),de("m",ne),de("mm",ne,Q),ge(["m","mm"],Me);var kn=Y("Minutes",!1);L("s",["ss",2],0,"second"),N("second","s"),F("second",15),de("s",ne),de("ss",ne,Q),ge(["s","ss"],_e);var xn,jn,Mn=Y("Seconds",!1);for(L("S",0,0,(function(){return~~(this.millisecond()/100)})),L(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),L(0,["SSS",3],0,"millisecond"),L(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),L(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),L(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),L(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),L(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),L(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),N("millisecond","ms"),F("millisecond",16),de("S",oe,K),de("SS",oe,Q),de("SSS",oe,J),xn="SSSS";xn.length<=9;xn+="S")de(xn,se);function _n(e,t){t[Ce]=G(1e3*("0."+e))}for(xn="S";xn.length<=9;xn+="S")ge(xn,_n);jn=Y("Milliseconds",!1),L("z",0,0,"zoneAbbr"),L("zz",0,0,"zoneName");var Cn=b.prototype;function qn(e){return e}Cn.add=tn,Cn.calendar=function(e,t){var n;1===arguments.length&&(arguments[0]?w(n=arguments[0])||u(n)||rn(n)||l(n)||function(e){var t=i(e),n=!1;return t&&(n=0===e.filter((function(t){return!l(t)&&rn(e)})).length),t&&n}(n)||function(e){var t,n,r=o(e)&&!c(e),i=!1,s=["years","year","y","months","month","M","days","day","d","dates","date","D","hours","hour","h","minutes","minute","m","seconds","second","s","milliseconds","millisecond","ms"];for(t=0;tn.valueOf():n.valueOf()9999?D(n,t?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):C(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",D(n,"Z")):D(n,t?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},Cn.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e,t,n,r="moment",i="";return this.isLocal()||(r=0===this.utcOffset()?"moment.utc":"moment.parseZone",i="Z"),e="["+r+'("]',t=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",n=i+'[")]',this.format(e+t+"-MM-DD[T]HH:mm:ss.SSS"+n)},"undefined"!==typeof Symbol&&null!=Symbol.for&&(Cn[Symbol.for("nodejs.util.inspect.custom")]=function(){return"Moment<"+this.format()+">"}),Cn.toJSON=function(){return this.isValid()?this.toISOString():null},Cn.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},Cn.unix=function(){return Math.floor(this.valueOf()/1e3)},Cn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},Cn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},Cn.eraName=function(){var e,t,n,r=this.localeData().eras();for(e=0,t=r.length;ethis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},Cn.isLocal=function(){return!!this.isValid()&&!this._isUTC},Cn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},Cn.isUtc=Yt,Cn.isUTC=Yt,Cn.zoneAbbr=function(){return this._isUTC?"UTC":""},Cn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},Cn.dates=x("dates accessor is deprecated. Use date instead.",wn),Cn.months=x("months accessor is deprecated. Use month instead",Ve),Cn.years=x("years accessor is deprecated. Use year instead",Re),Cn.zone=x("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",(function(e,t){return null!=e?("string"!==typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()})),Cn.isDSTShifted=x("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",(function(){if(!s(this._isDSTShifted))return this._isDSTShifted;var e,t={};return y(t,this),(t=Et(t))._a?(e=t._isUTC?d(t._a):Ht(t._a),this._isDSTShifted=this.isValid()&&function(e,t,n){var r,i=Math.min(e.length,t.length),o=Math.abs(e.length-t.length),a=0;for(r=0;r0):this._isDSTShifted=!1,this._isDSTShifted}));var Sn=S.prototype;function On(e,t,n,r){var i=vt(),o=d().set(r,t);return i[n](o,e)}function Tn(e,t,n){if(l(e)&&(t=e,e=void 0),e=e||"",null!=t)return On(e,t,n,"month");var r,i=[];for(r=0;r<12;r++)i[r]=On(e,r,n,"month");return i}function En(e,t,n,r){"boolean"===typeof e?(l(t)&&(n=t,t=void 0),t=t||""):(n=t=e,e=!1,l(t)&&(n=t,t=void 0),t=t||"");var i,o=vt(),a=e?o._week.dow:0,c=[];if(null!=n)return On(t,(n+a)%7,r,"day");for(i=0;i<7;i++)c[i]=On(t,(i+a)%7,r,"day");return c}Sn.calendar=function(e,t,n){var r=this._calendar[e]||this._calendar.sameElse;return C(r)?r.call(t,n):r},Sn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.match(T).map((function(e){return"MMMM"===e||"MM"===e||"DD"===e||"dddd"===e?e.slice(1):e})).join(""),this._longDateFormat[e])},Sn.invalidDate=function(){return this._invalidDate},Sn.ordinal=function(e){return this._ordinal.replace("%d",e)},Sn.preparse=qn,Sn.postformat=qn,Sn.relativeTime=function(e,t,n,r){var i=this._relativeTime[n];return C(i)?i(e,t,n,r):i.replace(/%d/i,e)},Sn.pastFuture=function(e,t){var n=this._relativeTime[e>0?"future":"past"];return C(n)?n(t):n.replace(/%s/i,t)},Sn.set=function(e){var t,n;for(n in e)a(e,n)&&(C(t=e[n])?this[n]=t:this["_"+n]=t);this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},Sn.eras=function(e,t){var n,i,o,a=this._eras||vt("en")._eras;for(n=0,i=a.length;n=0)return s[r]},Sn.erasConvertYear=function(e,t){var n=e.since<=e.until?1:-1;return void 0===t?r(e.since).year():r(e.since).year()+(t-e.offset)*n},Sn.erasAbbrRegex=function(e){return a(this,"_erasAbbrRegex")||gn.call(this),e?this._erasAbbrRegex:this._erasRegex},Sn.erasNameRegex=function(e){return a(this,"_erasNameRegex")||gn.call(this),e?this._erasNameRegex:this._erasRegex},Sn.erasNarrowRegex=function(e){return a(this,"_erasNarrowRegex")||gn.call(this),e?this._erasNarrowRegex:this._erasRegex},Sn.months=function(e,t){return e?i(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||Ae).test(t)?"format":"standalone"][e.month()]:i(this._months)?this._months:this._months.standalone},Sn.monthsShort=function(e,t){return e?i(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[Ae.test(t)?"format":"standalone"][e.month()]:i(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},Sn.monthsParse=function(e,t,n){var r,i,o;if(this._monthsParseExact)return De.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=d([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(o="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[r]=new RegExp(o.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[r].test(e))return r;if(n&&"MMM"===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}},Sn.monthsRegex=function(e){return this._monthsParseExact?(a(this,"_monthsRegex")||Ne.call(this),e?this._monthsStrictRegex:this._monthsRegex):(a(this,"_monthsRegex")||(this._monthsRegex=Le),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},Sn.monthsShortRegex=function(e){return this._monthsParseExact?(a(this,"_monthsRegex")||Ne.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(a(this,"_monthsShortRegex")||(this._monthsShortRegex=He),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},Sn.week=function(e){return Ge(e,this._week.dow,this._week.doy).week},Sn.firstDayOfYear=function(){return this._week.doy},Sn.firstDayOfWeek=function(){return this._week.dow},Sn.weekdays=function(e,t){var n=i(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?"format":"standalone"];return!0===e?Ze(n,this._week.dow):e?n[e.day()]:n},Sn.weekdaysMin=function(e){return!0===e?Ze(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},Sn.weekdaysShort=function(e){return!0===e?Ze(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},Sn.weekdaysParse=function(e,t,n){var r,i,o;if(this._weekdaysParseExact)return tt.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=d([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[r]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[r]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[r]||(o="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[r]=new RegExp(o.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&"ddd"===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&"dd"===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}},Sn.weekdaysRegex=function(e){return this._weekdaysParseExact?(a(this,"_weekdaysRegex")||nt.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(a(this,"_weekdaysRegex")||(this._weekdaysRegex=Qe),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},Sn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(a(this,"_weekdaysRegex")||nt.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(a(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Je),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},Sn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(a(this,"_weekdaysRegex")||nt.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(a(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=et),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},Sn.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},Sn.meridiem=function(e,t,n){return e>11?n?"pm":"PM":n?"am":"AM"},pt("en",{eras:[{since:"0001-01-01",until:1/0,offset:1,name:"Anno Domini",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"Before Christ",narrow:"BC",abbr:"BC"}],dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===G(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")}}),r.lang=x("moment.lang is deprecated. Use moment.locale instead.",pt),r.langData=x("moment.langData is deprecated. Use moment.localeData instead.",vt);var An=Math.abs;function Hn(e,t,n,r){var i=Xt(t,n);return e._milliseconds+=r*i._milliseconds,e._days+=r*i._days,e._months+=r*i._months,e._bubble()}function Ln(e){return e<0?Math.floor(e):Math.ceil(e)}function Dn(e){return 4800*e/146097}function Pn(e){return 146097*e/4800}function Vn(e){return function(){return this.as(e)}}var Nn=Vn("ms"),In=Vn("s"),Rn=Vn("m"),Bn=Vn("h"),Fn=Vn("d"),Un=Vn("w"),Wn=Vn("M"),Gn=Vn("Q"),Yn=Vn("y");function Zn(e){return function(){return this.isValid()?this._data[e]:NaN}}var $n=Zn("milliseconds"),Xn=Zn("seconds"),Kn=Zn("minutes"),Qn=Zn("hours"),Jn=Zn("days"),er=Zn("months"),tr=Zn("years"),nr=Math.round,rr={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function ir(e,t,n,r,i){return i.relativeTime(t||1,!!n,e,r)}var or=Math.abs;function ar(e){return(e>0)-(e<0)||+e}function cr(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n,r,i,o,a,c,s=or(this._milliseconds)/1e3,l=or(this._days),u=or(this._months),f=this.asSeconds();return f?(e=W(s/60),t=W(e/60),s%=60,e%=60,n=W(u/12),u%=12,r=s?s.toFixed(3).replace(/\.?0+$/,""):"",i=f<0?"-":"",o=ar(this._months)!==ar(f)?"-":"",a=ar(this._days)!==ar(f)?"-":"",c=ar(this._milliseconds)!==ar(f)?"-":"",i+"P"+(n?o+n+"Y":"")+(u?o+u+"M":"")+(l?a+l+"D":"")+(t||e||s?"T":"")+(t?c+t+"H":"")+(e?c+e+"M":"")+(s?c+r+"S":"")):"P0D"}var sr=Nt.prototype;return sr.isValid=function(){return this._isValid},sr.abs=function(){var e=this._data;return this._milliseconds=An(this._milliseconds),this._days=An(this._days),this._months=An(this._months),e.milliseconds=An(e.milliseconds),e.seconds=An(e.seconds),e.minutes=An(e.minutes),e.hours=An(e.hours),e.months=An(e.months),e.years=An(e.years),this},sr.add=function(e,t){return Hn(this,e,t,1)},sr.subtract=function(e,t){return Hn(this,e,t,-1)},sr.as=function(e){if(!this.isValid())return NaN;var t,n,r=this._milliseconds;if("month"===(e=I(e))||"quarter"===e||"year"===e)switch(t=this._days+r/864e5,n=this._months+Dn(t),e){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(t=this._days+Math.round(Pn(this._months)),e){case"week":return t/7+r/6048e5;case"day":return t+r/864e5;case"hour":return 24*t+r/36e5;case"minute":return 1440*t+r/6e4;case"second":return 86400*t+r/1e3;case"millisecond":return Math.floor(864e5*t)+r;default:throw new Error("Unknown unit "+e)}},sr.asMilliseconds=Nn,sr.asSeconds=In,sr.asMinutes=Rn,sr.asHours=Bn,sr.asDays=Fn,sr.asWeeks=Un,sr.asMonths=Wn,sr.asQuarters=Gn,sr.asYears=Yn,sr.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*G(this._months/12):NaN},sr._bubble=function(){var e,t,n,r,i,o=this._milliseconds,a=this._days,c=this._months,s=this._data;return o>=0&&a>=0&&c>=0||o<=0&&a<=0&&c<=0||(o+=864e5*Ln(Pn(c)+a),a=0,c=0),s.milliseconds=o%1e3,e=W(o/1e3),s.seconds=e%60,t=W(e/60),s.minutes=t%60,n=W(t/60),s.hours=n%24,a+=W(n/24),i=W(Dn(a)),c+=i,a-=Ln(Pn(i)),r=W(c/12),c%=12,s.days=a,s.months=c,s.years=r,this},sr.clone=function(){return Xt(this)},sr.get=function(e){return e=I(e),this.isValid()?this[e+"s"]():NaN},sr.milliseconds=$n,sr.seconds=Xn,sr.minutes=Kn,sr.hours=Qn,sr.days=Jn,sr.weeks=function(){return W(this.days()/7)},sr.months=er,sr.years=tr,sr.humanize=function(e,t){if(!this.isValid())return this.localeData().invalidDate();var n,r,i=!1,o=rr;return"object"===typeof e&&(t=e,e=!1),"boolean"===typeof e&&(i=e),"object"===typeof t&&(o=Object.assign({},rr,t),null!=t.s&&null==t.ss&&(o.ss=t.s-1)),n=this.localeData(),r=function(e,t,n,r){var i=Xt(e).abs(),o=nr(i.as("s")),a=nr(i.as("m")),c=nr(i.as("h")),s=nr(i.as("d")),l=nr(i.as("M")),u=nr(i.as("w")),f=nr(i.as("y")),h=o<=n.ss&&["s",o]||o0,h[4]=r,ir.apply(null,h)}(this,!i,o,n),i&&(r=n.pastFuture(+this,r)),n.postformat(r)},sr.toISOString=cr,sr.toString=cr,sr.toJSON=cr,sr.locale=an,sr.localeData=sn,sr.toIsoString=x("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",cr),sr.lang=cn,L("X",0,0,"unix"),L("x",0,0,"valueOf"),de("x",le),de("X",/[+-]?\d+(\.\d{1,3})?/),ge("X",(function(e,t,n){n._d=new Date(1e3*parseFloat(e))})),ge("x",(function(e,t,n){n._d=new Date(G(e))})),r.version="2.29.1",t=Ht,r.fn=Cn,r.min=function(){return Pt("isBefore",[].slice.call(arguments,0))},r.max=function(){return Pt("isAfter",[].slice.call(arguments,0))},r.now=function(){return Date.now?Date.now():+new Date},r.utc=d,r.unix=function(e){return Ht(1e3*e)},r.months=function(e,t){return Tn(e,t,"months")},r.isDate=u,r.locale=pt,r.invalid=v,r.duration=Xt,r.isMoment=w,r.weekdays=function(e,t,n){return En(e,t,n,"weekdays")},r.parseZone=function(){return Ht.apply(null,arguments).parseZone()},r.localeData=vt,r.isDuration=It,r.monthsShort=function(e,t){return Tn(e,t,"monthsShort")},r.weekdaysMin=function(e,t,n){return En(e,t,n,"weekdaysMin")},r.defineLocale=zt,r.updateLocale=function(e,t){if(null!=t){var n,r,i=st;null!=lt[e]&&null!=lt[e].parentLocale?lt[e].set(q(lt[e]._config,t)):(null!=(r=dt(e))&&(i=r._config),t=q(i,t),null==r&&(t.abbr=e),(n=new S(t)).parentLocale=lt[e],lt[e]=n),pt(e)}else null!=lt[e]&&(null!=lt[e].parentLocale?(lt[e]=lt[e].parentLocale,e===pt()&&pt(e)):null!=lt[e]&&delete lt[e]);return lt[e]},r.locales=function(){return j(lt)},r.weekdaysShort=function(e,t,n){return En(e,t,n,"weekdaysShort")},r.normalizeUnits=I,r.relativeTimeRounding=function(e){return void 0===e?nr:"function"===typeof e&&(nr=e,!0)},r.relativeTimeThreshold=function(e,t){return void 0!==rr[e]&&(void 0===t?rr[e]:(rr[e]=t,"s"===e&&(rr.ss=t-1),!0))},r.calendarFormat=function(e,t){var n=e.diff(t,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},r.prototype=Cn,r.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},r}()}).call(this,n(417)(e))},,,function(e,t){var n,r,i=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function c(e){if(n===setTimeout)return setTimeout(e,0);if((n===o||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"===typeof setTimeout?setTimeout:o}catch(e){n=o}try{r="function"===typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var s,l=[],u=!1,f=-1;function h(){u&&s&&(u=!1,s.length?l=s.concat(l):f=-1,l.length&&d())}function d(){if(!u){var e=c(h);u=!0;for(var t=l.length;t;){for(s=l,l=[];++f1)for(var n=1;n=0;c--)(i=e[c])&&(a=(o<3?i(a):o>3?i(t,n,a):i(t,n))||a);return o>3&&a&&Object.defineProperty(t,n,a),a}function s(e,t){return function(n,r){t(n,r,e)}}function l(e,t){if("object"===typeof Reflect&&"function"===typeof Reflect.metadata)return Reflect.metadata(e,t)}function u(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{s(r.next(e))}catch(t){o(t)}}function c(e){try{s(r.throw(e))}catch(t){o(t)}}function s(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,c)}s((r=r.apply(e,t||[])).next())}))}function f(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:c(0),throw:c(1),return:c(2)},"function"===typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function c(o){return function(c){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!(i=(i=a.trys).length>0&&i[i.length-1])&&(6===o[0]||2===o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function z(e,t){var n="function"===typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,o=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=o.next()).done;)a.push(r.value)}catch(c){i={error:c}}finally{try{r&&!r.done&&(n=o.return)&&n.call(o)}finally{if(i)throw i.error}}return a}function v(){for(var e=[],t=0;t1||c(e,t)}))})}function c(e,t){try{(n=i[e](t)).value instanceof y?Promise.resolve(n.value.v).then(s,l):u(o[0][2],n)}catch(r){u(o[0][3],r)}var n}function s(e){c("next",e)}function l(e){c("throw",e)}function u(e,t){e(t),o.shift(),o.length&&c(o[0][0],o[0][1])}}function w(e){var t,n;return t={},r("next"),r("throw",(function(e){throw e})),r("return"),t[Symbol.iterator]=function(){return this},t;function r(r,i){t[r]=e[r]?function(t){return(n=!n)?{value:y(e[r](t)),done:"return"===r}:i?i(t):t}:i}}function k(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e=p(e),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise((function(r,i){(function(e,t,n,r){Promise.resolve(r).then((function(t){e({value:t,done:n})}),t)})(r,i,(t=e[n](t)).done,t.value)}))}}}function x(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e}var j=Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t};function M(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&h(t,e,n);return j(t,e),t}function _(e){return e&&e.__esModule?e:{default:e}}function C(e,t,n,r){if("a"===n&&!r)throw new TypeError("Private accessor was defined without a getter");if("function"===typeof t?e!==t||!r:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?r:"a"===n?r.call(e):r?r.value:t.get(e)}function q(e,t,n,r,i){if("m"===r)throw new TypeError("Private method is not writable");if("a"===r&&!i)throw new TypeError("Private accessor was defined without a setter");if("function"===typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===r?i.call(e,n):i?i.value=n:t.set(e,n),n}},function(e,t,n){"use strict";e.exports=n(307)},,function(e,t,n){"use strict";var r=n(66),i=n(97),o=Object(r.a)(i.a);t.a=o},function(e,t,n){"use strict";var r=n(160),i=n(66),o=Object(i.a)(Object(r.a)("slice",(function(e,t,n){return Array.prototype.slice.call(n,e,t)})));t.a=o},function(e,t,n){"use strict";n.d(t,"a",(function(){return o}));var r=n(43),i=n(53);function o(e,t){return new r.a((function(n){var r=new i.a,o=0;return r.add(t.schedule((function(){o!==e.length?(n.next(e[o++]),n.closed||r.add(this.schedule())):n.complete()}))),r}))}},function(e,t,n){"use strict";n.d(t,"a",(function(){return a}));var r=n(95),i=n(185),o=n(43);function a(e,t,n,a,c){if(void 0===c&&(c=new r.a(e,n,a)),!c.closed)return t instanceof o.a?t.subscribe(c):Object(i.a)(t)(c)}},function(e,t,n){"use strict";n.d(t,"a",(function(){return i}));var r=n(31),i=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return r.b(t,e),t.prototype.notifyNext=function(e,t,n,r,i){this.destination.next(t)},t.prototype.notifyError=function(e,t){this.destination.error(e)},t.prototype.notifyComplete=function(e){this.destination.complete()},t}(n(52).a)},function(e,t,n){var r,i;window,e.exports=(r=n(0),i=n(32),function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=9)}([function(e,t,n){(function(e,r){var i;(function(){var o="Expected a function",a="__lodash_placeholder__",c=[["ary",128],["bind",1],["bindKey",2],["curry",8],["curryRight",16],["flip",512],["partial",32],["partialRight",64],["rearg",256]],s="[object Arguments]",l="[object Array]",u="[object Boolean]",f="[object Date]",h="[object Error]",d="[object Function]",p="[object GeneratorFunction]",z="[object Map]",v="[object Number]",g="[object Object]",m="[object RegExp]",y="[object Set]",b="[object String]",w="[object Symbol]",k="[object WeakMap]",x="[object ArrayBuffer]",j="[object DataView]",M="[object Float32Array]",_="[object Float64Array]",C="[object Int8Array]",q="[object Int16Array]",S="[object Int32Array]",O="[object Uint8Array]",T="[object Uint16Array]",E="[object Uint32Array]",A=/\b__p \+= '';/g,H=/\b(__p \+=) '' \+/g,L=/(__e\(.*?\)|\b__t\)) \+\n'';/g,D=/&(?:amp|lt|gt|quot|#39);/g,P=/[&<>"']/g,V=RegExp(D.source),N=RegExp(P.source),I=/<%-([\s\S]+?)%>/g,R=/<%([\s\S]+?)%>/g,B=/<%=([\s\S]+?)%>/g,F=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,U=/^\w*$/,W=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,G=/[\\^$.*+?()[\]{}|]/g,Y=RegExp(G.source),Z=/^\s+|\s+$/g,$=/^\s+/,X=/\s+$/,K=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,Q=/\{\n\/\* \[wrapped with (.+)\] \*/,J=/,? & /,ee=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,te=/\\(\\)?/g,ne=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,re=/\w*$/,ie=/^[-+]0x[0-9a-f]+$/i,oe=/^0b[01]+$/i,ae=/^\[object .+?Constructor\]$/,ce=/^0o[0-7]+$/i,se=/^(?:0|[1-9]\d*)$/,le=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,ue=/($^)/,fe=/['\n\r\u2028\u2029\\]/g,he="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",de="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",pe="["+de+"]",ze="["+he+"]",ve="\\d+",ge="[a-z\\xdf-\\xf6\\xf8-\\xff]",me="[^\\ud800-\\udfff"+de+ve+"\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde]",ye="\\ud83c[\\udffb-\\udfff]",be="[^\\ud800-\\udfff]",we="(?:\\ud83c[\\udde6-\\uddff]){2}",ke="[\\ud800-\\udbff][\\udc00-\\udfff]",xe="[A-Z\\xc0-\\xd6\\xd8-\\xde]",je="(?:"+ge+"|"+me+")",Me="(?:"+xe+"|"+me+")",_e="(?:"+ze+"|"+ye+")?",Ce="[\\ufe0e\\ufe0f]?"+_e+"(?:\\u200d(?:"+[be,we,ke].join("|")+")[\\ufe0e\\ufe0f]?"+_e+")*",qe="(?:"+["[\\u2700-\\u27bf]",we,ke].join("|")+")"+Ce,Se="(?:"+[be+ze+"?",ze,we,ke,"[\\ud800-\\udfff]"].join("|")+")",Oe=RegExp("['\u2019]","g"),Te=RegExp(ze,"g"),Ee=RegExp(ye+"(?="+ye+")|"+Se+Ce,"g"),Ae=RegExp([xe+"?"+ge+"+(?:['\u2019](?:d|ll|m|re|s|t|ve))?(?="+[pe,xe,"$"].join("|")+")",Me+"+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?(?="+[pe,xe+je,"$"].join("|")+")",xe+"?"+je+"+(?:['\u2019](?:d|ll|m|re|s|t|ve))?",xe+"+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?","\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",ve,qe].join("|"),"g"),He=RegExp("[\\u200d\\ud800-\\udfff"+he+"\\ufe0e\\ufe0f]"),Le=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,De=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Pe=-1,Ve={};Ve[M]=Ve[_]=Ve[C]=Ve[q]=Ve[S]=Ve[O]=Ve["[object Uint8ClampedArray]"]=Ve[T]=Ve[E]=!0,Ve[s]=Ve[l]=Ve[x]=Ve[u]=Ve[j]=Ve[f]=Ve[h]=Ve[d]=Ve[z]=Ve[v]=Ve[g]=Ve[m]=Ve[y]=Ve[b]=Ve[k]=!1;var Ne={};Ne[s]=Ne[l]=Ne[x]=Ne[j]=Ne[u]=Ne[f]=Ne[M]=Ne[_]=Ne[C]=Ne[q]=Ne[S]=Ne[z]=Ne[v]=Ne[g]=Ne[m]=Ne[y]=Ne[b]=Ne[w]=Ne[O]=Ne["[object Uint8ClampedArray]"]=Ne[T]=Ne[E]=!0,Ne[h]=Ne[d]=Ne[k]=!1;var Ie={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Re=parseFloat,Be=parseInt,Fe="object"==typeof e&&e&&e.Object===Object&&e,Ue="object"==typeof self&&self&&self.Object===Object&&self,We=Fe||Ue||Function("return this")(),Ge=t&&!t.nodeType&&t,Ye=Ge&&"object"==typeof r&&r&&!r.nodeType&&r,Ze=Ye&&Ye.exports===Ge,$e=Ze&&Fe.process,Xe=function(){try{return Ye&&Ye.require&&Ye.require("util").types||$e&&$e.binding&&$e.binding("util")}catch(e){}}(),Ke=Xe&&Xe.isArrayBuffer,Qe=Xe&&Xe.isDate,Je=Xe&&Xe.isMap,et=Xe&&Xe.isRegExp,tt=Xe&&Xe.isSet,nt=Xe&&Xe.isTypedArray;function rt(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function it(e,t,n,r){for(var i=-1,o=null==e?0:e.length;++i-1}function ut(e,t,n){for(var r=-1,i=null==e?0:e.length;++r-1;);return n}function Et(e,t){for(var n=e.length;n--&&yt(t,e[n],0)>-1;);return n}function At(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}var Ht=jt({"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"}),Lt=jt({"&":"&","<":"<",">":">",'"':""","'":"'"});function Dt(e){return"\\"+Ie[e]}function Pt(e){return He.test(e)}function Vt(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}function Nt(e,t){return function(n){return e(t(n))}}function It(e,t){for(var n=-1,r=e.length,i=0,o=[];++n",""":'"',"'":"'"}),Wt=function e(t){var n,r=(t=null==t?We:Wt.defaults(We.Object(),t,Wt.pick(We,De))).Array,i=t.Date,he=t.Error,de=t.Function,pe=t.Math,ze=t.Object,ve=t.RegExp,ge=t.String,me=t.TypeError,ye=r.prototype,be=de.prototype,we=ze.prototype,ke=t["__core-js_shared__"],xe=be.toString,je=we.hasOwnProperty,Me=0,_e=(n=/[^.]+$/.exec(ke&&ke.keys&&ke.keys.IE_PROTO||""))?"Symbol(src)_1."+n:"",Ce=we.toString,qe=xe.call(ze),Se=We._,Ee=ve("^"+xe.call(je).replace(G,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),He=Ze?t.Buffer:void 0,Ie=t.Symbol,Fe=t.Uint8Array,Ue=He?He.allocUnsafe:void 0,Ge=Nt(ze.getPrototypeOf,ze),Ye=ze.create,$e=we.propertyIsEnumerable,Xe=ye.splice,vt=Ie?Ie.isConcatSpreadable:void 0,jt=Ie?Ie.iterator:void 0,Gt=Ie?Ie.toStringTag:void 0,Yt=function(){try{var e=Ki(ze,"defineProperty");return e({},"",{}),e}catch(e){}}(),Zt=t.clearTimeout!==We.clearTimeout&&t.clearTimeout,$t=i&&i.now!==We.Date.now&&i.now,Xt=t.setTimeout!==We.setTimeout&&t.setTimeout,Kt=pe.ceil,Qt=pe.floor,Jt=ze.getOwnPropertySymbols,en=He?He.isBuffer:void 0,tn=t.isFinite,nn=ye.join,rn=Nt(ze.keys,ze),on=pe.max,an=pe.min,cn=i.now,sn=t.parseInt,ln=pe.random,un=ye.reverse,fn=Ki(t,"DataView"),hn=Ki(t,"Map"),dn=Ki(t,"Promise"),pn=Ki(t,"Set"),zn=Ki(t,"WeakMap"),vn=Ki(ze,"create"),gn=zn&&new zn,mn={},yn=Mo(fn),bn=Mo(hn),wn=Mo(dn),kn=Mo(pn),xn=Mo(zn),jn=Ie?Ie.prototype:void 0,Mn=jn?jn.valueOf:void 0,_n=jn?jn.toString:void 0;function Cn(e){if(Ba(e)&&!Ta(e)&&!(e instanceof Tn)){if(e instanceof On)return e;if(je.call(e,"__wrapped__"))return _o(e)}return new On(e)}var qn=function(){function e(){}return function(t){if(!Ra(t))return{};if(Ye)return Ye(t);e.prototype=t;var n=new e;return e.prototype=void 0,n}}();function Sn(){}function On(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=void 0}function Tn(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=4294967295,this.__views__=[]}function En(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t=t?e:t)),e}function $n(e,t,n,r,i,o){var a,c=1&t,l=2&t,h=4&t;if(n&&(a=i?n(e,r,i,o):n(e)),void 0!==a)return a;if(!Ra(e))return e;var k=Ta(e);if(k){if(a=function(e){var t=e.length,n=new e.constructor(t);return t&&"string"==typeof e[0]&&je.call(e,"index")&&(n.index=e.index,n.input=e.input),n}(e),!c)return zi(e,a)}else{var A=eo(e),H=A==d||A==p;if(La(e))return li(e,c);if(A==g||A==s||H&&!i){if(a=l||H?{}:no(e),!c)return l?function(e,t){return vi(e,Ji(e),t)}(e,function(e,t){return e&&vi(t,yc(t),e)}(a,e)):function(e,t){return vi(e,Qi(e),t)}(e,Wn(a,e))}else{if(!Ne[A])return i?e:{};a=function(e,t,n){var r,i=e.constructor;switch(t){case x:return ui(e);case u:case f:return new i(+e);case j:return function(e,t){var n=t?ui(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}(e,n);case M:case _:case C:case q:case S:case O:case"[object Uint8ClampedArray]":case T:case E:return fi(e,n);case z:return new i;case v:case b:return new i(e);case m:return function(e){var t=new e.constructor(e.source,re.exec(e));return t.lastIndex=e.lastIndex,t}(e);case y:return new i;case w:return r=e,Mn?ze(Mn.call(r)):{}}}(e,A,c)}}o||(o=new Dn);var L=o.get(e);if(L)return L;o.set(e,a),Ya(e)?e.forEach((function(r){a.add($n(r,t,n,r,e,o))})):Fa(e)&&e.forEach((function(r,i){a.set(i,$n(r,t,n,i,e,o))}));var D=k?void 0:(h?l?Ui:Fi:l?yc:mc)(e);return ot(D||e,(function(r,i){D&&(r=e[i=r]),Bn(a,i,$n(r,t,n,i,e,o))})),a}function Xn(e,t,n){var r=n.length;if(null==e)return!r;for(e=ze(e);r--;){var i=n[r],o=t[i],a=e[i];if(void 0===a&&!(i in e)||!o(a))return!1}return!0}function Kn(e,t,n){if("function"!=typeof e)throw new me(o);return mo((function(){e.apply(void 0,n)}),t)}function Qn(e,t,n,r){var i=-1,o=lt,a=!0,c=e.length,s=[],l=t.length;if(!c)return s;n&&(t=ft(t,qt(n))),r?(o=ut,a=!1):t.length>=200&&(o=Ot,a=!1,t=new Ln(t));e:for(;++i-1},An.prototype.set=function(e,t){var n=this.__data__,r=Fn(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},Hn.prototype.clear=function(){this.size=0,this.__data__={hash:new En,map:new(hn||An),string:new En}},Hn.prototype.delete=function(e){var t=$i(this,e).delete(e);return this.size-=t?1:0,t},Hn.prototype.get=function(e){return $i(this,e).get(e)},Hn.prototype.has=function(e){return $i(this,e).has(e)},Hn.prototype.set=function(e,t){var n=$i(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},Ln.prototype.add=Ln.prototype.push=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this},Ln.prototype.has=function(e){return this.__data__.has(e)},Dn.prototype.clear=function(){this.__data__=new An,this.size=0},Dn.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},Dn.prototype.get=function(e){return this.__data__.get(e)},Dn.prototype.has=function(e){return this.__data__.has(e)},Dn.prototype.set=function(e,t){var n=this.__data__;if(n instanceof An){var r=n.__data__;if(!hn||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new Hn(r)}return n.set(e,t),this.size=n.size,this};var Jn=yi(cr),er=yi(sr,!0);function tr(e,t){var n=!0;return Jn(e,(function(e,r,i){return n=!!t(e,r,i)})),n}function nr(e,t,n){for(var r=-1,i=e.length;++r0&&n(c)?t>1?ir(c,t-1,n,r,i):ht(i,c):r||(i[i.length]=c)}return i}var or=bi(),ar=bi(!0);function cr(e,t){return e&&or(e,t,mc)}function sr(e,t){return e&&ar(e,t,mc)}function lr(e,t){return st(t,(function(t){return Va(e[t])}))}function ur(e,t){for(var n=0,r=(t=oi(t,e)).length;null!=e&&nt}function pr(e,t){return null!=e&&je.call(e,t)}function zr(e,t){return null!=e&&t in ze(e)}function vr(e,t,n){for(var i=n?ut:lt,o=e[0].length,a=e.length,c=a,s=r(a),l=1/0,u=[];c--;){var f=e[c];c&&t&&(f=ft(f,qt(t))),l=an(f.length,l),s[c]=!n&&(t||o>=120&&f.length>=120)?new Ln(c&&f):void 0}f=e[0];var h=-1,d=s[0];e:for(;++h=c?s:s*("desc"==n[r]?-1:1)}return e.index-t.index}(e,t,n)}))}function Tr(e,t,n){for(var r=-1,i=t.length,o={};++r-1;)c!==e&&Xe.call(c,s,1),Xe.call(e,s,1);return e}function Ar(e,t){for(var n=e?t.length:0,r=n-1;n--;){var i=t[n];if(n==r||i!==o){var o=i;io(i)?Xe.call(e,i,1):Kr(e,i)}}return e}function Hr(e,t){return e+Qt(ln()*(t-e+1))}function Lr(e,t){var n="";if(!e||t<1||t>9007199254740991)return n;do{t%2&&(n+=e),(t=Qt(t/2))&&(e+=e)}while(t);return n}function Dr(e,t){return yo(ho(e,t,Uc),e+"")}function Pr(e){return Vn(Cc(e))}function Vr(e,t){var n=Cc(e);return ko(n,Zn(t,0,n.length))}function Nr(e,t,n,r){if(!Ra(e))return e;for(var i=-1,o=(t=oi(t,e)).length,a=o-1,c=e;null!=c&&++io?0:o+t),(n=n>o?o:n)<0&&(n+=o),o=t>n?0:n-t>>>0,t>>>=0;for(var a=r(o);++i>>1,a=e[o];null!==a&&!$a(a)&&(n?a<=t:a=200){var l=t?null:Li(e);if(l)return Rt(l);a=!1,i=Ot,s=new Ln}else s=t?[]:c;e:for(;++r=r?e:Fr(e,t,n)}var si=Zt||function(e){return We.clearTimeout(e)};function li(e,t){if(t)return e.slice();var n=e.length,r=Ue?Ue(n):new e.constructor(n);return e.copy(r),r}function ui(e){var t=new e.constructor(e.byteLength);return new Fe(t).set(new Fe(e)),t}function fi(e,t){var n=t?ui(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function hi(e,t){if(e!==t){var n=void 0!==e,r=null===e,i=e==e,o=$a(e),a=void 0!==t,c=null===t,s=t==t,l=$a(t);if(!c&&!l&&!o&&e>t||o&&a&&s&&!c&&!l||r&&a&&s||!n&&s||!i)return 1;if(!r&&!o&&!l&&e1?n[i-1]:void 0,a=i>2?n[2]:void 0;for(o=e.length>3&&"function"==typeof o?(i--,o):void 0,a&&oo(n[0],n[1],a)&&(o=i<3?void 0:o,i=1),t=ze(t);++r-1?i[o?t[a]:a]:void 0}}function Mi(e){return Bi((function(t){var n=t.length,r=n,i=On.prototype.thru;for(e&&t.reverse();r--;){var a=t[r];if("function"!=typeof a)throw new me(o);if(i&&!c&&"wrapper"==Gi(a))var c=new On([],!0)}for(r=c?r:n;++r1&&y.reverse(),f&&lc))return!1;var l=o.get(e),u=o.get(t);if(l&&u)return l==t&&u==e;var f=-1,h=!0,d=2&n?new Ln:void 0;for(o.set(e,t),o.set(t,e);++f-1&&e%1==0&&e1?"& ":"")+t[r],t=t.join(n>2?", ":" "),e.replace(K,"{\n/* [wrapped with "+t+"] */\n")}(r,function(e,t){return ot(c,(function(n){var r="_."+n[0];t&n[1]&&!lt(e,r)&&e.push(r)})),e.sort()}(function(e){var t=e.match(Q);return t?t[1].split(J):[]}(r),n)))}function wo(e){var t=0,n=0;return function(){var r=cn(),i=16-(r-n);if(n=r,i>0){if(++t>=800)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}function ko(e,t){var n=-1,r=e.length,i=r-1;for(t=void 0===t?r:t;++n1?e[t-1]:void 0;return n="function"==typeof n?(e.pop(),n):void 0,Go(e,n)}));function Jo(e){var t=Cn(e);return t.__chain__=!0,t}function ea(e,t){return t(e)}var ta=Bi((function(e){var t=e.length,n=t?e[0]:0,r=this.__wrapped__,i=function(t){return Yn(t,e)};return!(t>1||this.__actions__.length)&&r instanceof Tn&&io(n)?((r=r.slice(n,+n+(t?1:0))).__actions__.push({func:ea,args:[i],thisArg:void 0}),new On(r,this.__chain__).thru((function(e){return t&&!e.length&&e.push(void 0),e}))):this.thru(i)})),na=gi((function(e,t,n){je.call(e,n)?++e[n]:Gn(e,n,1)})),ra=ji(Oo),ia=ji(To);function oa(e,t){return(Ta(e)?ot:Jn)(e,Zi(t,3))}function aa(e,t){return(Ta(e)?at:er)(e,Zi(t,3))}var ca=gi((function(e,t,n){je.call(e,n)?e[n].push(t):Gn(e,n,[t])})),sa=Dr((function(e,t,n){var i=-1,o="function"==typeof t,a=Aa(e)?r(e.length):[];return Jn(e,(function(e){a[++i]=o?rt(t,e,n):gr(e,t,n)})),a})),la=gi((function(e,t,n){Gn(e,n,t)}));function ua(e,t){return(Ta(e)?ft:Mr)(e,Zi(t,3))}var fa=gi((function(e,t,n){e[n?0:1].push(t)}),(function(){return[[],[]]})),ha=Dr((function(e,t){if(null==e)return[];var n=t.length;return n>1&&oo(e,t[0],t[1])?t=[]:n>2&&oo(t[0],t[1],t[2])&&(t=[t[0]]),Or(e,ir(t,1),[])})),da=$t||function(){return We.Date.now()};function pa(e,t,n){return t=n?void 0:t,Pi(e,128,void 0,void 0,void 0,void 0,t=e&&null==t?e.length:t)}function za(e,t){var n;if("function"!=typeof t)throw new me(o);return e=tc(e),function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=void 0),n}}var va=Dr((function(e,t,n){var r=1;if(n.length){var i=It(n,Yi(va));r|=32}return Pi(e,r,t,n,i)})),ga=Dr((function(e,t,n){var r=3;if(n.length){var i=It(n,Yi(ga));r|=32}return Pi(t,r,e,n,i)}));function ma(e,t,n){var r,i,a,c,s,l,u=0,f=!1,h=!1,d=!0;if("function"!=typeof e)throw new me(o);function p(t){var n=r,o=i;return r=i=void 0,u=t,c=e.apply(o,n)}function z(e){var n=e-l;return void 0===l||n>=t||n<0||h&&e-u>=a}function v(){var e=da();if(z(e))return g(e);s=mo(v,function(e){var n=t-(e-l);return h?an(n,a-(e-u)):n}(e))}function g(e){return s=void 0,d&&r?p(e):(r=i=void 0,c)}function m(){var e=da(),n=z(e);if(r=arguments,i=this,l=e,n){if(void 0===s)return function(e){return u=e,s=mo(v,t),f?p(e):c}(l);if(h)return si(s),s=mo(v,t),p(l)}return void 0===s&&(s=mo(v,t)),c}return t=rc(t)||0,Ra(n)&&(f=!!n.leading,a=(h="maxWait"in n)?on(rc(n.maxWait)||0,t):a,d="trailing"in n?!!n.trailing:d),m.cancel=function(){void 0!==s&&si(s),u=0,r=l=i=s=void 0},m.flush=function(){return void 0===s?c:g(da())},m}var ya=Dr((function(e,t){return Kn(e,1,t)})),ba=Dr((function(e,t,n){return Kn(e,rc(t)||0,n)}));function wa(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new me(o);var n=function n(){var r=arguments,i=t?t.apply(this,r):r[0],o=n.cache;if(o.has(i))return o.get(i);var a=e.apply(this,r);return n.cache=o.set(i,a)||o,a};return n.cache=new(wa.Cache||Hn),n}function ka(e){if("function"!=typeof e)throw new me(o);return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}wa.Cache=Hn;var xa=ai((function(e,t){var n=(t=1==t.length&&Ta(t[0])?ft(t[0],qt(Zi())):ft(ir(t,1),qt(Zi()))).length;return Dr((function(r){for(var i=-1,o=an(r.length,n);++i=t})),Oa=mr(function(){return arguments}())?mr:function(e){return Ba(e)&&je.call(e,"callee")&&!$e.call(e,"callee")},Ta=r.isArray,Ea=Ke?qt(Ke):function(e){return Ba(e)&&hr(e)==x};function Aa(e){return null!=e&&Ia(e.length)&&!Va(e)}function Ha(e){return Ba(e)&&Aa(e)}var La=en||rs,Da=Qe?qt(Qe):function(e){return Ba(e)&&hr(e)==f};function Pa(e){if(!Ba(e))return!1;var t=hr(e);return t==h||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!Wa(e)}function Va(e){if(!Ra(e))return!1;var t=hr(e);return t==d||t==p||"[object AsyncFunction]"==t||"[object Proxy]"==t}function Na(e){return"number"==typeof e&&e==tc(e)}function Ia(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=9007199254740991}function Ra(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function Ba(e){return null!=e&&"object"==typeof e}var Fa=Je?qt(Je):function(e){return Ba(e)&&eo(e)==z};function Ua(e){return"number"==typeof e||Ba(e)&&hr(e)==v}function Wa(e){if(!Ba(e)||hr(e)!=g)return!1;var t=Ge(e);if(null===t)return!0;var n=je.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&xe.call(n)==qe}var Ga=et?qt(et):function(e){return Ba(e)&&hr(e)==m},Ya=tt?qt(tt):function(e){return Ba(e)&&eo(e)==y};function Za(e){return"string"==typeof e||!Ta(e)&&Ba(e)&&hr(e)==b}function $a(e){return"symbol"==typeof e||Ba(e)&&hr(e)==w}var Xa=nt?qt(nt):function(e){return Ba(e)&&Ia(e.length)&&!!Ve[hr(e)]},Ka=Ei(jr),Qa=Ei((function(e,t){return e<=t}));function Ja(e){if(!e)return[];if(Aa(e))return Za(e)?Ft(e):zi(e);if(jt&&e[jt])return function(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}(e[jt]());var t=eo(e);return(t==z?Vt:t==y?Rt:Cc)(e)}function ec(e){return e?(e=rc(e))===1/0||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}function tc(e){var t=ec(e),n=t%1;return t==t?n?t-n:t:0}function nc(e){return e?Zn(tc(e),0,4294967295):0}function rc(e){if("number"==typeof e)return e;if($a(e))return NaN;if(Ra(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=Ra(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(Z,"");var n=oe.test(e);return n||ce.test(e)?Be(e.slice(2),n?2:8):ie.test(e)?NaN:+e}function ic(e){return vi(e,yc(e))}function oc(e){return null==e?"":$r(e)}var ac=mi((function(e,t){if(lo(t)||Aa(t))vi(t,mc(t),e);else for(var n in t)je.call(t,n)&&Bn(e,n,t[n])})),cc=mi((function(e,t){vi(t,yc(t),e)})),sc=mi((function(e,t,n,r){vi(t,yc(t),e,r)})),lc=mi((function(e,t,n,r){vi(t,mc(t),e,r)})),uc=Bi(Yn),fc=Dr((function(e,t){e=ze(e);var n=-1,r=t.length,i=r>2?t[2]:void 0;for(i&&oo(t[0],t[1],i)&&(r=1);++n1),t})),vi(e,Ui(e),n),r&&(n=$n(n,7,Ii));for(var i=t.length;i--;)Kr(n,t[i]);return n})),xc=Bi((function(e,t){return null==e?{}:function(e,t){return Tr(e,t,(function(t,n){return pc(e,n)}))}(e,t)}));function jc(e,t){if(null==e)return{};var n=ft(Ui(e),(function(e){return[e]}));return t=Zi(t),Tr(e,n,(function(e,n){return t(e,n[0])}))}var Mc=Di(mc),_c=Di(yc);function Cc(e){return null==e?[]:St(e,mc(e))}var qc=ki((function(e,t,n){return t=t.toLowerCase(),e+(n?Sc(t):t)}));function Sc(e){return Pc(oc(e).toLowerCase())}function Oc(e){return(e=oc(e))&&e.replace(le,Ht).replace(Te,"")}var Tc=ki((function(e,t,n){return e+(n?"-":"")+t.toLowerCase()})),Ec=ki((function(e,t,n){return e+(n?" ":"")+t.toLowerCase()})),Ac=wi("toLowerCase"),Hc=ki((function(e,t,n){return e+(n?"_":"")+t.toLowerCase()})),Lc=ki((function(e,t,n){return e+(n?" ":"")+Pc(t)})),Dc=ki((function(e,t,n){return e+(n?" ":"")+t.toUpperCase()})),Pc=wi("toUpperCase");function Vc(e,t,n){return e=oc(e),void 0===(t=n?void 0:t)?function(e){return Le.test(e)}(e)?function(e){return e.match(Ae)||[]}(e):function(e){return e.match(ee)||[]}(e):e.match(t)||[]}var Nc=Dr((function(e,t){try{return rt(e,void 0,t)}catch(e){return Pa(e)?e:new he(e)}})),Ic=Bi((function(e,t){return ot(t,(function(t){t=jo(t),Gn(e,t,va(e[t],e))})),e}));function Rc(e){return function(){return e}}var Bc=Mi(),Fc=Mi(!0);function Uc(e){return e}function Wc(e){return kr("function"==typeof e?e:$n(e,1))}var Gc=Dr((function(e,t){return function(n){return gr(n,e,t)}})),Yc=Dr((function(e,t){return function(n){return gr(e,n,t)}}));function Zc(e,t,n){var r=mc(t),i=lr(t,r);null!=n||Ra(t)&&(i.length||!r.length)||(n=t,t=e,e=this,i=lr(t,mc(t)));var o=!(Ra(n)&&"chain"in n&&!n.chain),a=Va(e);return ot(i,(function(n){var r=t[n];e[n]=r,a&&(e.prototype[n]=function(){var t=this.__chain__;if(o||t){var n=e(this.__wrapped__),i=n.__actions__=zi(this.__actions__);return i.push({func:r,args:arguments,thisArg:e}),n.__chain__=t,n}return r.apply(e,ht([this.value()],arguments))})})),e}function $c(){}var Xc=Si(ft),Kc=Si(ct),Qc=Si(zt);function Jc(e){return ao(e)?xt(jo(e)):function(e){return function(t){return ur(t,e)}}(e)}var es=Ti(),ts=Ti(!0);function ns(){return[]}function rs(){return!1}var is,os=qi((function(e,t){return e+t}),0),as=Hi("ceil"),cs=qi((function(e,t){return e/t}),1),ss=Hi("floor"),ls=qi((function(e,t){return e*t}),1),us=Hi("round"),fs=qi((function(e,t){return e-t}),0);return Cn.after=function(e,t){if("function"!=typeof t)throw new me(o);return e=tc(e),function(){if(--e<1)return t.apply(this,arguments)}},Cn.ary=pa,Cn.assign=ac,Cn.assignIn=cc,Cn.assignInWith=sc,Cn.assignWith=lc,Cn.at=uc,Cn.before=za,Cn.bind=va,Cn.bindAll=Ic,Cn.bindKey=ga,Cn.castArray=function(){if(!arguments.length)return[];var e=arguments[0];return Ta(e)?e:[e]},Cn.chain=Jo,Cn.chunk=function(e,t,n){t=(n?oo(e,t,n):void 0===t)?1:on(tc(t),0);var i=null==e?0:e.length;if(!i||t<1)return[];for(var o=0,a=0,c=r(Kt(i/t));oi?0:i+n),(r=void 0===r||r>i?i:tc(r))<0&&(r+=i),r=n>r?0:nc(r);n>>0)?(e=oc(e))&&("string"==typeof t||null!=t&&!Ga(t))&&!(t=$r(t))&&Pt(e)?ci(Ft(e),0,n):e.split(t,n):[]},Cn.spread=function(e,t){if("function"!=typeof e)throw new me(o);return t=null==t?0:on(tc(t),0),Dr((function(n){var r=n[t],i=ci(n,0,t);return r&&ht(i,r),rt(e,this,i)}))},Cn.tail=function(e){var t=null==e?0:e.length;return t?Fr(e,1,t):[]},Cn.take=function(e,t,n){return e&&e.length?Fr(e,0,(t=n||void 0===t?1:tc(t))<0?0:t):[]},Cn.takeRight=function(e,t,n){var r=null==e?0:e.length;return r?Fr(e,(t=r-(t=n||void 0===t?1:tc(t)))<0?0:t,r):[]},Cn.takeRightWhile=function(e,t){return e&&e.length?Jr(e,Zi(t,3),!1,!0):[]},Cn.takeWhile=function(e,t){return e&&e.length?Jr(e,Zi(t,3)):[]},Cn.tap=function(e,t){return t(e),e},Cn.throttle=function(e,t,n){var r=!0,i=!0;if("function"!=typeof e)throw new me(o);return Ra(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),ma(e,t,{leading:r,maxWait:t,trailing:i})},Cn.thru=ea,Cn.toArray=Ja,Cn.toPairs=Mc,Cn.toPairsIn=_c,Cn.toPath=function(e){return Ta(e)?ft(e,jo):$a(e)?[e]:zi(xo(oc(e)))},Cn.toPlainObject=ic,Cn.transform=function(e,t,n){var r=Ta(e),i=r||La(e)||Xa(e);if(t=Zi(t,4),null==n){var o=e&&e.constructor;n=i?r?new o:[]:Ra(e)&&Va(o)?qn(Ge(e)):{}}return(i?ot:cr)(e,(function(e,r,i){return t(n,e,r,i)})),n},Cn.unary=function(e){return pa(e,1)},Cn.union=Bo,Cn.unionBy=Fo,Cn.unionWith=Uo,Cn.uniq=function(e){return e&&e.length?Xr(e):[]},Cn.uniqBy=function(e,t){return e&&e.length?Xr(e,Zi(t,2)):[]},Cn.uniqWith=function(e,t){return t="function"==typeof t?t:void 0,e&&e.length?Xr(e,void 0,t):[]},Cn.unset=function(e,t){return null==e||Kr(e,t)},Cn.unzip=Wo,Cn.unzipWith=Go,Cn.update=function(e,t,n){return null==e?e:Qr(e,t,ii(n))},Cn.updateWith=function(e,t,n,r){return r="function"==typeof r?r:void 0,null==e?e:Qr(e,t,ii(n),r)},Cn.values=Cc,Cn.valuesIn=function(e){return null==e?[]:St(e,yc(e))},Cn.without=Yo,Cn.words=Vc,Cn.wrap=function(e,t){return ja(ii(t),e)},Cn.xor=Zo,Cn.xorBy=$o,Cn.xorWith=Xo,Cn.zip=Ko,Cn.zipObject=function(e,t){return ni(e||[],t||[],Bn)},Cn.zipObjectDeep=function(e,t){return ni(e||[],t||[],Nr)},Cn.zipWith=Qo,Cn.entries=Mc,Cn.entriesIn=_c,Cn.extend=cc,Cn.extendWith=sc,Zc(Cn,Cn),Cn.add=os,Cn.attempt=Nc,Cn.camelCase=qc,Cn.capitalize=Sc,Cn.ceil=as,Cn.clamp=function(e,t,n){return void 0===n&&(n=t,t=void 0),void 0!==n&&(n=(n=rc(n))==n?n:0),void 0!==t&&(t=(t=rc(t))==t?t:0),Zn(rc(e),t,n)},Cn.clone=function(e){return $n(e,4)},Cn.cloneDeep=function(e){return $n(e,5)},Cn.cloneDeepWith=function(e,t){return $n(e,5,t="function"==typeof t?t:void 0)},Cn.cloneWith=function(e,t){return $n(e,4,t="function"==typeof t?t:void 0)},Cn.conformsTo=function(e,t){return null==t||Xn(e,t,mc(t))},Cn.deburr=Oc,Cn.defaultTo=function(e,t){return null==e||e!=e?t:e},Cn.divide=cs,Cn.endsWith=function(e,t,n){e=oc(e),t=$r(t);var r=e.length,i=n=void 0===n?r:Zn(tc(n),0,r);return(n-=t.length)>=0&&e.slice(n,i)==t},Cn.eq=Ca,Cn.escape=function(e){return(e=oc(e))&&N.test(e)?e.replace(P,Lt):e},Cn.escapeRegExp=function(e){return(e=oc(e))&&Y.test(e)?e.replace(G,"\\$&"):e},Cn.every=function(e,t,n){var r=Ta(e)?ct:tr;return n&&oo(e,t,n)&&(t=void 0),r(e,Zi(t,3))},Cn.find=ra,Cn.findIndex=Oo,Cn.findKey=function(e,t){return gt(e,Zi(t,3),cr)},Cn.findLast=ia,Cn.findLastIndex=To,Cn.findLastKey=function(e,t){return gt(e,Zi(t,3),sr)},Cn.floor=ss,Cn.forEach=oa,Cn.forEachRight=aa,Cn.forIn=function(e,t){return null==e?e:or(e,Zi(t,3),yc)},Cn.forInRight=function(e,t){return null==e?e:ar(e,Zi(t,3),yc)},Cn.forOwn=function(e,t){return e&&cr(e,Zi(t,3))},Cn.forOwnRight=function(e,t){return e&&sr(e,Zi(t,3))},Cn.get=dc,Cn.gt=qa,Cn.gte=Sa,Cn.has=function(e,t){return null!=e&&to(e,t,pr)},Cn.hasIn=pc,Cn.head=Ao,Cn.identity=Uc,Cn.includes=function(e,t,n,r){e=Aa(e)?e:Cc(e),n=n&&!r?tc(n):0;var i=e.length;return n<0&&(n=on(i+n,0)),Za(e)?n<=i&&e.indexOf(t,n)>-1:!!i&&yt(e,t,n)>-1},Cn.indexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var i=null==n?0:tc(n);return i<0&&(i=on(r+i,0)),yt(e,t,i)},Cn.inRange=function(e,t,n){return t=ec(t),void 0===n?(n=t,t=0):n=ec(n),function(e,t,n){return e>=an(t,n)&&e=-9007199254740991&&e<=9007199254740991},Cn.isSet=Ya,Cn.isString=Za,Cn.isSymbol=$a,Cn.isTypedArray=Xa,Cn.isUndefined=function(e){return void 0===e},Cn.isWeakMap=function(e){return Ba(e)&&eo(e)==k},Cn.isWeakSet=function(e){return Ba(e)&&"[object WeakSet]"==hr(e)},Cn.join=function(e,t){return null==e?"":nn.call(e,t)},Cn.kebabCase=Tc,Cn.last=Po,Cn.lastIndexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var i=r;return void 0!==n&&(i=(i=tc(n))<0?on(r+i,0):an(i,r-1)),t==t?function(e,t,n){for(var r=n+1;r--;)if(e[r]===t)return r;return r}(e,t,i):mt(e,wt,i,!0)},Cn.lowerCase=Ec,Cn.lowerFirst=Ac,Cn.lt=Ka,Cn.lte=Qa,Cn.max=function(e){return e&&e.length?nr(e,Uc,dr):void 0},Cn.maxBy=function(e,t){return e&&e.length?nr(e,Zi(t,2),dr):void 0},Cn.mean=function(e){return kt(e,Uc)},Cn.meanBy=function(e,t){return kt(e,Zi(t,2))},Cn.min=function(e){return e&&e.length?nr(e,Uc,jr):void 0},Cn.minBy=function(e,t){return e&&e.length?nr(e,Zi(t,2),jr):void 0},Cn.stubArray=ns,Cn.stubFalse=rs,Cn.stubObject=function(){return{}},Cn.stubString=function(){return""},Cn.stubTrue=function(){return!0},Cn.multiply=ls,Cn.nth=function(e,t){return e&&e.length?Sr(e,tc(t)):void 0},Cn.noConflict=function(){return We._===this&&(We._=Se),this},Cn.noop=$c,Cn.now=da,Cn.pad=function(e,t,n){e=oc(e);var r=(t=tc(t))?Bt(e):0;if(!t||r>=t)return e;var i=(t-r)/2;return Oi(Qt(i),n)+e+Oi(Kt(i),n)},Cn.padEnd=function(e,t,n){e=oc(e);var r=(t=tc(t))?Bt(e):0;return t&&rt){var r=e;e=t,t=r}if(n||e%1||t%1){var i=ln();return an(e+i*(t-e+Re("1e-"+((i+"").length-1))),t)}return Hr(e,t)},Cn.reduce=function(e,t,n){var r=Ta(e)?dt:Mt,i=arguments.length<3;return r(e,Zi(t,4),n,i,Jn)},Cn.reduceRight=function(e,t,n){var r=Ta(e)?pt:Mt,i=arguments.length<3;return r(e,Zi(t,4),n,i,er)},Cn.repeat=function(e,t,n){return t=(n?oo(e,t,n):void 0===t)?1:tc(t),Lr(oc(e),t)},Cn.replace=function(){var e=arguments,t=oc(e[0]);return e.length<3?t:t.replace(e[1],e[2])},Cn.result=function(e,t,n){var r=-1,i=(t=oi(t,e)).length;for(i||(i=1,e=void 0);++r9007199254740991)return[];var n=4294967295,r=an(e,4294967295);e-=4294967295;for(var i=Ct(r,t=Zi(t));++n=o)return e;var c=n-Bt(r);if(c<1)return r;var s=a?ci(a,0,c).join(""):e.slice(0,c);if(void 0===i)return s+r;if(a&&(c+=s.length-c),Ga(i)){if(e.slice(c).search(i)){var l,u=s;for(i.global||(i=ve(i.source,oc(re.exec(i))+"g")),i.lastIndex=0;l=i.exec(u);)var f=l.index;s=s.slice(0,void 0===f?c:f)}}else if(e.indexOf($r(i),c)!=c){var h=s.lastIndexOf(i);h>-1&&(s=s.slice(0,h))}return s+r},Cn.unescape=function(e){return(e=oc(e))&&V.test(e)?e.replace(D,Ut):e},Cn.uniqueId=function(e){var t=++Me;return oc(e)+t},Cn.upperCase=Dc,Cn.upperFirst=Pc,Cn.each=oa,Cn.eachRight=aa,Cn.first=Ao,Zc(Cn,(is={},cr(Cn,(function(e,t){je.call(Cn.prototype,t)||(is[t]=e)})),is),{chain:!1}),Cn.VERSION="4.17.20",ot(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(e){Cn[e].placeholder=Cn})),ot(["drop","take"],(function(e,t){Tn.prototype[e]=function(n){n=void 0===n?1:on(tc(n),0);var r=this.__filtered__&&!t?new Tn(this):this.clone();return r.__filtered__?r.__takeCount__=an(n,r.__takeCount__):r.__views__.push({size:an(n,4294967295),type:e+(r.__dir__<0?"Right":"")}),r},Tn.prototype[e+"Right"]=function(t){return this.reverse()[e](t).reverse()}})),ot(["filter","map","takeWhile"],(function(e,t){var n=t+1,r=1==n||3==n;Tn.prototype[e]=function(e){var t=this.clone();return t.__iteratees__.push({iteratee:Zi(e,3),type:n}),t.__filtered__=t.__filtered__||r,t}})),ot(["head","last"],(function(e,t){var n="take"+(t?"Right":"");Tn.prototype[e]=function(){return this[n](1).value()[0]}})),ot(["initial","tail"],(function(e,t){var n="drop"+(t?"":"Right");Tn.prototype[e]=function(){return this.__filtered__?new Tn(this):this[n](1)}})),Tn.prototype.compact=function(){return this.filter(Uc)},Tn.prototype.find=function(e){return this.filter(e).head()},Tn.prototype.findLast=function(e){return this.reverse().find(e)},Tn.prototype.invokeMap=Dr((function(e,t){return"function"==typeof e?new Tn(this):this.map((function(n){return gr(n,e,t)}))})),Tn.prototype.reject=function(e){return this.filter(ka(Zi(e)))},Tn.prototype.slice=function(e,t){e=tc(e);var n=this;return n.__filtered__&&(e>0||t<0)?new Tn(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),void 0!==t&&(n=(t=tc(t))<0?n.dropRight(-t):n.take(t-e)),n)},Tn.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},Tn.prototype.toArray=function(){return this.take(4294967295)},cr(Tn.prototype,(function(e,t){var n=/^(?:filter|find|map|reject)|While$/.test(t),r=/^(?:head|last)$/.test(t),i=Cn[r?"take"+("last"==t?"Right":""):t],o=r||/^find/.test(t);i&&(Cn.prototype[t]=function(){var t=this.__wrapped__,a=r?[1]:arguments,c=t instanceof Tn,s=a[0],l=c||Ta(t),u=function(e){var t=i.apply(Cn,ht([e],a));return r&&f?t[0]:t};l&&n&&"function"==typeof s&&1!=s.length&&(c=l=!1);var f=this.__chain__,h=!!this.__actions__.length,d=o&&!f,p=c&&!h;if(!o&&l){t=p?t:new Tn(this);var z=e.apply(t,a);return z.__actions__.push({func:ea,args:[u],thisArg:void 0}),new On(z,f)}return d&&p?e.apply(this,a):(z=this.thru(u),d?r?z.value()[0]:z.value():z)})})),ot(["pop","push","shift","sort","splice","unshift"],(function(e){var t=ye[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",r=/^(?:pop|shift)$/.test(e);Cn.prototype[e]=function(){var e=arguments;if(r&&!this.__chain__){var i=this.value();return t.apply(Ta(i)?i:[],e)}return this[n]((function(n){return t.apply(Ta(n)?n:[],e)}))}})),cr(Tn.prototype,(function(e,t){var n=Cn[t];if(n){var r=n.name+"";je.call(mn,r)||(mn[r]=[]),mn[r].push({name:t,func:n})}})),mn[_i(void 0,2).name]=[{name:"wrapper",func:void 0}],Tn.prototype.clone=function(){var e=new Tn(this.__wrapped__);return e.__actions__=zi(this.__actions__),e.__dir__=this.__dir__,e.__filtered__=this.__filtered__,e.__iteratees__=zi(this.__iteratees__),e.__takeCount__=this.__takeCount__,e.__views__=zi(this.__views__),e},Tn.prototype.reverse=function(){if(this.__filtered__){var e=new Tn(this);e.__dir__=-1,e.__filtered__=!0}else(e=this.clone()).__dir__*=-1;return e},Tn.prototype.value=function(){var e=this.__wrapped__.value(),t=this.__dir__,n=Ta(e),r=t<0,i=n?e.length:0,o=function(e,t,n){for(var r=-1,i=n.length;++r=this.__values__.length;return{done:e,value:e?void 0:this.__values__[this.__index__++]}},Cn.prototype.plant=function(e){for(var t,n=this;n instanceof Sn;){var r=_o(n);r.__index__=0,r.__values__=void 0,t?i.__wrapped__=r:t=r;var i=r;n=n.__wrapped__}return i.__wrapped__=e,t},Cn.prototype.reverse=function(){var e=this.__wrapped__;if(e instanceof Tn){var t=e;return this.__actions__.length&&(t=new Tn(this)),(t=t.reverse()).__actions__.push({func:ea,args:[Ro],thisArg:void 0}),new On(t,this.__chain__)}return this.thru(Ro)},Cn.prototype.toJSON=Cn.prototype.valueOf=Cn.prototype.value=function(){return ei(this.__wrapped__,this.__actions__)},Cn.prototype.first=Cn.prototype.head,jt&&(Cn.prototype[jt]=function(){return this}),Cn}();We._=Wt,void 0===(i=function(){return Wt}.call(t,n,t,r))||(r.exports=i)}).call(this)}).call(this,n(7),n(21)(e))},function(e,t){e.exports=r},function(e,t,n){e.exports=function(){"use strict";var e=navigator.userAgent,t=navigator.platform,n=/gecko\/\d/i.test(e),r=/MSIE \d/.test(e),i=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(e),o=/Edge\/(\d+)/.exec(e),a=r||i||o,c=a&&(r?document.documentMode||6:+(o||i)[1]),s=!o&&/WebKit\//.test(e),l=s&&/Qt\/\d+\.\d+/.test(e),u=!o&&/Chrome\//.test(e),f=/Opera\//.test(e),h=/Apple Computer/.test(navigator.vendor),d=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(e),p=/PhantomJS/.test(e),z=!o&&/AppleWebKit/.test(e)&&/Mobile\/\w+/.test(e),v=/Android/.test(e),g=z||v||/webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(e),m=z||/Mac/.test(t),y=/\bCrOS\b/.test(e),b=/win/i.test(t),w=f&&e.match(/Version\/(\d*\.\d*)/);w&&(w=Number(w[1])),w&&w>=15&&(f=!1,s=!0);var k=m&&(l||f&&(null==w||w<12.11)),x=n||a&&c>=9;function j(e){return new RegExp("(^|\\s)"+e+"(?:$|\\s)\\s*")}var M,_=function(e,t){var n=e.className,r=j(t).exec(n);if(r){var i=n.slice(r.index+r[0].length);e.className=n.slice(0,r.index)+(i?r[1]+i:"")}};function C(e){for(var t=e.childNodes.length;t>0;--t)e.removeChild(e.firstChild);return e}function q(e,t){return C(e).appendChild(t)}function S(e,t,n,r){var i=document.createElement(e);if(n&&(i.className=n),r&&(i.style.cssText=r),"string"==typeof t)i.appendChild(document.createTextNode(t));else if(t)for(var o=0;o=t)return a+(t-o);a+=c-o,a+=n-a%n,o=c+1}}z?L=function(e){e.selectionStart=0,e.selectionEnd=e.value.length}:a&&(L=function(e){try{e.select()}catch(e){}});var N=function(){this.id=null,this.f=null,this.time=0,this.handler=D(this.onTimeout,this)};function I(e,t){for(var n=0;n=t)return r+Math.min(a,t-i);if(i+=o-r,r=o+1,(i+=n-i%n)>=t)return r}}var G=[""];function Y(e){for(;G.length<=e;)G.push(Z(G)+" ");return G[e]}function Z(e){return e[e.length-1]}function $(e,t){for(var n=[],r=0;r"\x80"&&(e.toUpperCase()!=e.toLowerCase()||Q.test(e))}function ee(e,t){return t?!!(t.source.indexOf("\\w")>-1&&J(e))||t.test(e):J(e)}function te(e){for(var t in e)if(e.hasOwnProperty(t)&&e[t])return!1;return!0}var ne=/[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;function re(e){return e.charCodeAt(0)>=768&&ne.test(e)}function ie(e,t,n){for(;(n<0?t>0:tn?-1:1;;){if(t==n)return t;var i=(t+n)/2,o=r<0?Math.ceil(i):Math.floor(i);if(o==t)return e(o)?t:n;e(o)?n=o:t=o+r}}var ae=null;function ce(e,t,n){var r;ae=null;for(var i=0;it)return i;o.to==t&&(o.from!=o.to&&"before"==n?r=i:ae=i),o.from==t&&(o.from!=o.to&&"before"!=n?r=i:ae=i)}return null!=r?r:ae}var se=function(){var e=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/,t=/[stwN]/,n=/[LRr]/,r=/[Lb1n]/,i=/[1n]/;function o(e,t,n){this.level=e,this.from=t,this.to=n}return function(a,c){var s="ltr"==c?"L":"R";if(0==a.length||"ltr"==c&&!e.test(a))return!1;for(var l,u=a.length,f=[],h=0;h-1&&(r[t]=i.slice(0,o).concat(i.slice(o+1)))}}}function pe(e,t){var n=he(e,t);if(n.length)for(var r=Array.prototype.slice.call(arguments,2),i=0;i0}function me(e){e.prototype.on=function(e,t){fe(this,e,t)},e.prototype.off=function(e,t){de(this,e,t)}}function ye(e){e.preventDefault?e.preventDefault():e.returnValue=!1}function be(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0}function we(e){return null!=e.defaultPrevented?e.defaultPrevented:0==e.returnValue}function ke(e){ye(e),be(e)}function xe(e){return e.target||e.srcElement}function je(e){var t=e.which;return null==t&&(1&e.button?t=1:2&e.button?t=3:4&e.button&&(t=2)),m&&e.ctrlKey&&1==t&&(t=3),t}var Me,_e,Ce=function(){if(a&&c<9)return!1;var e=S("div");return"draggable"in e||"dragDrop"in e}();function qe(e){if(null==Me){var t=S("span","\u200b");q(e,S("span",[t,document.createTextNode("x")])),0!=e.firstChild.offsetHeight&&(Me=t.offsetWidth<=1&&t.offsetHeight>2&&!(a&&c<8))}var n=Me?S("span","\u200b"):S("span","\xa0",null,"display: inline-block; width: 1px; margin-right: -1px");return n.setAttribute("cm-text",""),n}function Se(e){if(null!=_e)return _e;var t=q(e,document.createTextNode("A\u062eA")),n=M(t,0,1).getBoundingClientRect(),r=M(t,1,2).getBoundingClientRect();return C(e),!(!n||n.left==n.right)&&(_e=r.right-n.right<3)}var Oe,Te=3!="\n\nb".split(/\n/).length?function(e){for(var t=0,n=[],r=e.length;t<=r;){var i=e.indexOf("\n",t);-1==i&&(i=e.length);var o=e.slice(t,"\r"==e.charAt(i-1)?i-1:i),a=o.indexOf("\r");-1!=a?(n.push(o.slice(0,a)),t+=a+1):(n.push(o),t=i+1)}return n}:function(e){return e.split(/\r\n?|\n/)},Ee=window.getSelection?function(e){try{return e.selectionStart!=e.selectionEnd}catch(e){return!1}}:function(e){var t;try{t=e.ownerDocument.selection.createRange()}catch(e){}return!(!t||t.parentElement()!=e)&&0!=t.compareEndPoints("StartToEnd",t)},Ae="oncopy"in(Oe=S("div"))||(Oe.setAttribute("oncopy","return;"),"function"==typeof Oe.oncopy),He=null,Le={},De={};function Pe(e,t){arguments.length>2&&(t.dependencies=Array.prototype.slice.call(arguments,2)),Le[e]=t}function Ve(e){if("string"==typeof e&&De.hasOwnProperty(e))e=De[e];else if(e&&"string"==typeof e.name&&De.hasOwnProperty(e.name)){var t=De[e.name];"string"==typeof t&&(t={name:t}),(e=K(t,e)).name=t.name}else{if("string"==typeof e&&/^[\w\-]+\/[\w\-]+\+xml$/.test(e))return Ve("application/xml");if("string"==typeof e&&/^[\w\-]+\/[\w\-]+\+json$/.test(e))return Ve("application/json")}return"string"==typeof e?{name:e}:e||{name:"null"}}function Ne(e,t){t=Ve(t);var n=Le[t.name];if(!n)return Ne(e,"text/plain");var r=n(e,t);if(Ie.hasOwnProperty(t.name)){var i=Ie[t.name];for(var o in i)i.hasOwnProperty(o)&&(r.hasOwnProperty(o)&&(r["_"+o]=r[o]),r[o]=i[o])}if(r.name=t.name,t.helperType&&(r.helperType=t.helperType),t.modeProps)for(var a in t.modeProps)r[a]=t.modeProps[a];return r}var Ie={};function Re(e,t){P(t,Ie.hasOwnProperty(e)?Ie[e]:Ie[e]={})}function Be(e,t){if(!0===t)return t;if(e.copyState)return e.copyState(t);var n={};for(var r in t){var i=t[r];i instanceof Array&&(i=i.concat([])),n[r]=i}return n}function Fe(e,t){for(var n;e.innerMode&&(n=e.innerMode(t))&&n.mode!=e;)t=n.state,e=n.mode;return n||{mode:e,state:t}}function Ue(e,t,n){return!e.startState||e.startState(t,n)}var We=function(e,t,n){this.pos=this.start=0,this.string=e,this.tabSize=t||8,this.lastColumnPos=this.lastColumnValue=0,this.lineStart=0,this.lineOracle=n};function Ge(e,t){if((t-=e.first)<0||t>=e.size)throw new Error("There is no line "+(t+e.first)+" in the document.");for(var n=e;!n.lines;)for(var r=0;;++r){var i=n.children[r],o=i.chunkSize();if(t=e.first&&tn?et(n,Ge(e,n).text.length):function(e,t){var n=e.ch;return null==n||n>t?et(e.line,t):n<0?et(e.line,0):e}(t,Ge(e,t.line).text.length)}function st(e,t){for(var n=[],r=0;r=this.string.length},We.prototype.sol=function(){return this.pos==this.lineStart},We.prototype.peek=function(){return this.string.charAt(this.pos)||void 0},We.prototype.next=function(){if(this.post},We.prototype.eatSpace=function(){for(var e=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>e},We.prototype.skipToEnd=function(){this.pos=this.string.length},We.prototype.skipTo=function(e){var t=this.string.indexOf(e,this.pos);if(t>-1)return this.pos=t,!0},We.prototype.backUp=function(e){this.pos-=e},We.prototype.column=function(){return this.lastColumnPos0?null:(r&&!1!==t&&(this.pos+=r[0].length),r)}var i=function(e){return n?e.toLowerCase():e};if(i(this.string.substr(this.pos,e.length))==i(e))return!1!==t&&(this.pos+=e.length),!0},We.prototype.current=function(){return this.string.slice(this.start,this.pos)},We.prototype.hideFirstChars=function(e,t){this.lineStart+=e;try{return t()}finally{this.lineStart-=e}},We.prototype.lookAhead=function(e){var t=this.lineOracle;return t&&t.lookAhead(e)},We.prototype.baseToken=function(){var e=this.lineOracle;return e&&e.baseToken(this.pos)};var lt=function(e,t){this.state=e,this.lookAhead=t},ut=function(e,t,n,r){this.state=t,this.doc=e,this.line=n,this.maxLookAhead=r||0,this.baseTokens=null,this.baseTokenPos=1};function ft(e,t,n,r){var i=[e.state.modeGen],o={};bt(e,t.text,e.doc.mode,n,(function(e,t){return i.push(e,t)}),o,r);for(var a=n.state,c=function(r){n.baseTokens=i;var c=e.state.overlays[r],s=1,l=0;n.state=!0,bt(e,t.text,c.mode,n,(function(e,t){for(var n=s;le&&i.splice(s,1,e,i[s+1],r),s+=2,l=Math.min(e,r)}if(t)if(c.opaque)i.splice(n,s-n,e,"overlay "+t),s=n+2;else for(;ne.options.maxHighlightLength&&Be(e.doc.mode,r.state),o=ft(e,t,r);i&&(r.state=i),t.stateAfter=r.save(!i),t.styles=o.styles,o.classes?t.styleClasses=o.classes:t.styleClasses&&(t.styleClasses=null),n===e.doc.highlightFrontier&&(e.doc.modeFrontier=Math.max(e.doc.modeFrontier,++e.doc.highlightFrontier))}return t.styles}function dt(e,t,n){var r=e.doc,i=e.display;if(!r.mode.startState)return new ut(r,!0,t);var o=function(e,t,n){for(var r,i,o=e.doc,a=n?-1:t-(e.doc.mode.innerMode?1e3:100),c=t;c>a;--c){if(c<=o.first)return o.first;var s=Ge(o,c-1),l=s.stateAfter;if(l&&(!n||c+(l instanceof lt?l.lookAhead:0)<=o.modeFrontier))return c;var u=V(s.text,null,e.options.tabSize);(null==i||r>u)&&(i=c-1,r=u)}return i}(e,t,n),a=o>r.first&&Ge(r,o-1).stateAfter,c=a?ut.fromSaved(r,a,o):new ut(r,Ue(r.mode),o);return r.iter(o,t,(function(n){pt(e,n.text,c);var r=c.line;n.stateAfter=r==t-1||r%5==0||r>=i.viewFrom&&rt.start)return o}throw new Error("Mode "+e.name+" failed to advance stream.")}ut.prototype.lookAhead=function(e){var t=this.doc.getLine(this.line+e);return null!=t&&e>this.maxLookAhead&&(this.maxLookAhead=e),t},ut.prototype.baseToken=function(e){if(!this.baseTokens)return null;for(;this.baseTokens[this.baseTokenPos]<=e;)this.baseTokenPos+=2;var t=this.baseTokens[this.baseTokenPos+1];return{type:t&&t.replace(/( |^)overlay .*/,""),size:this.baseTokens[this.baseTokenPos]-e}},ut.prototype.nextLine=function(){this.line++,this.maxLookAhead>0&&this.maxLookAhead--},ut.fromSaved=function(e,t,n){return t instanceof lt?new ut(e,Be(e.mode,t.state),n,t.lookAhead):new ut(e,Be(e.mode,t),n)},ut.prototype.save=function(e){var t=!1!==e?Be(this.doc.mode,this.state):this.state;return this.maxLookAhead>0?new lt(t,this.maxLookAhead):t};var gt=function(e,t,n){this.start=e.start,this.end=e.pos,this.string=e.current(),this.type=t||null,this.state=n};function mt(e,t,n,r){var i,o,a=e.doc,c=a.mode,s=Ge(a,(t=ct(a,t)).line),l=dt(e,t.line,n),u=new We(s.text,e.options.tabSize,l);for(r&&(o=[]);(r||u.pose.options.maxHighlightLength?(c=!1,a&&pt(e,t,r,f.pos),f.pos=t.length,s=null):s=yt(vt(n,f,r.state,h),o),h){var d=h[0].name;d&&(s="m-"+(s?d+" "+s:d))}if(!c||u!=s){for(;l=t:o.to>t);(r||(r=[])).push(new xt(a,o.from,c?null:o.to))}}return r}(n,i,a),s=function(e,t,n){var r;if(e)for(var i=0;i=t:o.to>t)||o.from==t&&"bookmark"==a.type&&(!n||o.marker.insertLeft)){var c=null==o.from||(a.inclusiveLeft?o.from<=t:o.from0&&c)for(var y=0;yt)&&(!n||Et(n,o.marker)<0)&&(n=o.marker)}return n}function Pt(e,t,n,r,i){var o=Ge(e,t),a=kt&&o.markedSpans;if(a)for(var c=0;c=0&&f<=0||u<=0&&f>=0)&&(u<=0&&(s.marker.inclusiveRight&&i.inclusiveLeft?tt(l.to,n)>=0:tt(l.to,n)>0)||u>=0&&(s.marker.inclusiveRight&&i.inclusiveLeft?tt(l.from,r)<=0:tt(l.from,r)<0)))return!0}}}function Vt(e){for(var t;t=Ht(e);)e=t.find(-1,!0).line;return e}function Nt(e,t){var n=Ge(e,t),r=Vt(n);return n==r?t:Xe(r)}function It(e,t){if(t>e.lastLine())return t;var n,r=Ge(e,t);if(!Rt(e,r))return t;for(;n=Lt(r);)r=n.find(1,!0).line;return Xe(r)+1}function Rt(e,t){var n=kt&&t.markedSpans;if(n)for(var r=void 0,i=0;it.maxLineLength&&(t.maxLineLength=n,t.maxLine=e)}))}var Gt=function(e,t,n){this.text=e,St(this,t),this.height=n?n(this):1};function Yt(e){e.parent=null,qt(e)}Gt.prototype.lineNo=function(){return Xe(this)},me(Gt);var Zt={},$t={};function Xt(e,t){if(!e||/^\s*$/.test(e))return null;var n=t.addModeClass?$t:Zt;return n[e]||(n[e]=e.replace(/\S+/g,"cm-$&"))}function Kt(e,t){var n=O("span",null,null,s?"padding-right: .1px":null),r={pre:O("pre",[n],"CodeMirror-line"),content:n,col:0,pos:0,cm:e,trailingSpace:!1,splitSpaces:e.getOption("lineWrapping")};t.measure={};for(var i=0;i<=(t.rest?t.rest.length:0);i++){var o=i?t.rest[i-1]:t.line,a=void 0;r.pos=0,r.addToken=Jt,Se(e.display.measure)&&(a=le(o,e.doc.direction))&&(r.addToken=en(r.addToken,a)),r.map=[],nn(o,r,ht(e,o,t!=e.display.externalMeasured&&Xe(o))),o.styleClasses&&(o.styleClasses.bgClass&&(r.bgClass=H(o.styleClasses.bgClass,r.bgClass||"")),o.styleClasses.textClass&&(r.textClass=H(o.styleClasses.textClass,r.textClass||""))),0==r.map.length&&r.map.push(0,0,r.content.appendChild(qe(e.display.measure))),0==i?(t.measure.map=r.map,t.measure.cache={}):((t.measure.maps||(t.measure.maps=[])).push(r.map),(t.measure.caches||(t.measure.caches=[])).push({}))}if(s){var c=r.content.lastChild;(/\bcm-tab\b/.test(c.className)||c.querySelector&&c.querySelector(".cm-tab"))&&(r.content.className="cm-tab-wrap-hack")}return pe(e,"renderLine",e,t.line,r.pre),r.pre.className&&(r.textClass=H(r.pre.className,r.textClass||"")),r}function Qt(e){var t=S("span","\u2022","cm-invalidchar");return t.title="\\u"+e.charCodeAt(0).toString(16),t.setAttribute("aria-label",t.title),t}function Jt(e,t,n,r,i,o,s){if(t){var l,u=e.splitSpaces?function(e,t){if(e.length>1&&!/ /.test(e))return e;for(var n=t,r="",i=0;il&&f.from<=l);h++);if(f.to>=u)return e(n,r,i,o,a,c,s);e(n,r.slice(0,f.to-l),i,o,null,c,s),o=null,r=r.slice(f.to-l),l=f.to}}}function tn(e,t,n,r){var i=!r&&n.widgetNode;i&&e.map.push(e.pos,e.pos+t,i),!r&&e.cm.display.input.needsContentAttribute&&(i||(i=e.content.appendChild(document.createElement("span"))),i.setAttribute("cm-marker",n.id)),i&&(e.cm.display.input.setUneditable(i),e.content.appendChild(i)),e.pos+=t,e.trailingSpace=!1}function nn(e,t,n){var r=e.markedSpans,i=e.text,o=0;if(r)for(var a,c,s,l,u,f,h,d=i.length,p=0,z=1,v="",g=0;;){if(g==p){s=l=u=c="",h=null,f=null,g=1/0;for(var m=[],y=void 0,b=0;bp||k.collapsed&&w.to==p&&w.from==p)){if(null!=w.to&&w.to!=p&&g>w.to&&(g=w.to,l=""),k.className&&(s+=" "+k.className),k.css&&(c=(c?c+";":"")+k.css),k.startStyle&&w.from==p&&(u+=" "+k.startStyle),k.endStyle&&w.to==g&&(y||(y=[])).push(k.endStyle,w.to),k.title&&((h||(h={})).title=k.title),k.attributes)for(var x in k.attributes)(h||(h={}))[x]=k.attributes[x];k.collapsed&&(!f||Et(f.marker,k)<0)&&(f=w)}else w.from>p&&g>w.from&&(g=w.from)}if(y)for(var j=0;j=d)break;for(var _=Math.min(d,g);;){if(v){var C=p+v.length;if(!f){var q=C>_?v.slice(0,_-p):v;t.addToken(t,q,a?a+s:s,u,p+q.length==g?l:"",c,h)}if(C>=_){v=v.slice(_-p),p=_;break}p=C,u=""}v=i.slice(o,o=n[z++]),a=Xt(n[z++],t.cm.options)}}else for(var S=1;Sn)return{map:e.measure.maps[i],cache:e.measure.caches[i],before:!0}}function On(e,t,n,r){return An(e,En(e,t),n,r)}function Tn(e,t){if(t>=e.display.viewFrom&&t=n.lineN&&t2&&o.push((s.bottom+l.top)/2-n.top)}}o.push(n.bottom-n.top)}}(e,t.view,t.rect),t.hasHeights=!0),(o=function(e,t,n,r){var i,o=Dn(t.map,n,r),s=o.node,l=o.start,u=o.end,f=o.collapse;if(3==s.nodeType){for(var h=0;h<4;h++){for(;l&&re(t.line.text.charAt(o.coverStart+l));)--l;for(;o.coverStart+u1}(e))return t;var n=screen.logicalXDPI/screen.deviceXDPI,r=screen.logicalYDPI/screen.deviceYDPI;return{left:t.left*n,right:t.right*n,top:t.top*r,bottom:t.bottom*r}}(e.display.measure,i))}else{var d;l>0&&(f=r="right"),i=e.options.lineWrapping&&(d=s.getClientRects()).length>1?d["right"==r?d.length-1:0]:s.getBoundingClientRect()}if(a&&c<9&&!l&&(!i||!i.left&&!i.right)){var p=s.parentNode.getClientRects()[0];i=p?{left:p.left,right:p.left+ir(e.display),top:p.top,bottom:p.bottom}:Ln}for(var z=i.top-t.rect.top,v=i.bottom-t.rect.top,g=(z+v)/2,m=t.view.measure.heights,y=0;yt)&&(i=(o=s-c)-1,t>=s&&(a="right")),null!=i){if(r=e[l+2],c==s&&n==(r.insertLeft?"left":"right")&&(a=n),"left"==n&&0==i)for(;l&&e[l-2]==e[l-3]&&e[l-1].insertLeft;)r=e[2+(l-=3)],a="left";if("right"==n&&i==s-c)for(;l=0&&(n=e[i]).left==n.right;i--);return n}function Vn(e){if(e.measure&&(e.measure.cache={},e.measure.heights=null,e.rest))for(var t=0;t=r.text.length?(s=r.text.length,l="before"):s<=0&&(s=0,l="after"),!c)return a("before"==l?s-1:s,"before"==l);function u(e,t,n){return a(n?e-1:e,1==c[t].level!=n)}var f=ce(c,s,l),h=ae,d=u(s,f,"before"==l);return null!=h&&(d.other=u(s,h,"before"!=l)),d}function Zn(e,t){var n=0;t=ct(e.doc,t),e.options.lineWrapping||(n=ir(e.display)*t.ch);var r=Ge(e.doc,t.line),i=Ft(r)+xn(e.display);return{left:n,right:n,top:i,bottom:i+r.height}}function $n(e,t,n,r,i){var o=et(e,t,n);return o.xRel=i,r&&(o.outside=r),o}function Xn(e,t,n){var r=e.doc;if((n+=e.display.viewOffset)<0)return $n(r.first,0,null,-1,-1);var i=Ke(r,n),o=r.first+r.size-1;if(i>o)return $n(r.first+r.size-1,Ge(r,o).text.length,null,1,1);t<0&&(t=0);for(var a=Ge(r,i);;){var c=er(e,a,i,t,n),s=Dt(a,c.ch+(c.xRel>0||c.outside>0?1:0));if(!s)return c;var l=s.find(1);if(l.line==i)return l;a=Ge(r,i=l.line)}}function Kn(e,t,n,r){r-=Fn(t);var i=t.text.length,o=oe((function(t){return An(e,n,t-1).bottom<=r}),i,0);return{begin:o,end:i=oe((function(t){return An(e,n,t).top>r}),o,i)}}function Qn(e,t,n,r){return n||(n=En(e,t)),Kn(e,t,n,Un(e,t,An(e,n,r),"line").top)}function Jn(e,t,n,r){return!(e.bottom<=n)&&(e.top>n||(r?e.left:e.right)>t)}function er(e,t,n,r,i){i-=Ft(t);var o=En(e,t),a=Fn(t),c=0,s=t.text.length,l=!0,u=le(t,e.doc.direction);if(u){var f=(e.options.lineWrapping?nr:tr)(e,t,n,o,u,r,i);c=(l=1!=f.level)?f.from:f.to-1,s=l?f.to:f.from-1}var h,d,p=null,z=null,v=oe((function(t){var n=An(e,o,t);return n.top+=a,n.bottom+=a,!!Jn(n,r,i,!1)&&(n.top<=i&&n.left<=r&&(p=t,z=n),!0)}),c,s),g=!1;if(z){var m=r-z.left=b.bottom?1:0}return $n(n,v=ie(t.text,v,1),d,g,r-h)}function tr(e,t,n,r,i,o,a){var c=oe((function(c){var s=i[c],l=1!=s.level;return Jn(Yn(e,et(n,l?s.to:s.from,l?"before":"after"),"line",t,r),o,a,!0)}),0,i.length-1),s=i[c];if(c>0){var l=1!=s.level,u=Yn(e,et(n,l?s.from:s.to,l?"after":"before"),"line",t,r);Jn(u,o,a,!0)&&u.top>a&&(s=i[c-1])}return s}function nr(e,t,n,r,i,o,a){var c=Kn(e,t,r,a),s=c.begin,l=c.end;/\s/.test(t.text.charAt(l-1))&&l--;for(var u=null,f=null,h=0;h=l||d.to<=s)){var p=An(e,r,1!=d.level?Math.min(l,d.to)-1:Math.max(s,d.from)).right,z=pz)&&(u=d,f=z)}}return u||(u=i[i.length-1]),u.froml&&(u={from:u.from,to:l,level:u.level}),u}function rr(e){if(null!=e.cachedTextHeight)return e.cachedTextHeight;if(null==Hn){Hn=S("pre",null,"CodeMirror-line-like");for(var t=0;t<49;++t)Hn.appendChild(document.createTextNode("x")),Hn.appendChild(S("br"));Hn.appendChild(document.createTextNode("x"))}q(e.measure,Hn);var n=Hn.offsetHeight/50;return n>3&&(e.cachedTextHeight=n),C(e.measure),n||1}function ir(e){if(null!=e.cachedCharWidth)return e.cachedCharWidth;var t=S("span","xxxxxxxxxx"),n=S("pre",[t],"CodeMirror-line-like");q(e.measure,n);var r=t.getBoundingClientRect(),i=(r.right-r.left)/10;return i>2&&(e.cachedCharWidth=i),i||10}function or(e){for(var t=e.display,n={},r={},i=t.gutters.clientLeft,o=t.gutters.firstChild,a=0;o;o=o.nextSibling,++a){var c=e.display.gutterSpecs[a].className;n[c]=o.offsetLeft+o.clientLeft+i,r[c]=o.clientWidth}return{fixedPos:ar(t),gutterTotalWidth:t.gutters.offsetWidth,gutterLeft:n,gutterWidth:r,wrapperWidth:t.wrapper.clientWidth}}function ar(e){return e.scroller.getBoundingClientRect().left-e.sizer.getBoundingClientRect().left}function cr(e){var t=rr(e.display),n=e.options.lineWrapping,r=n&&Math.max(5,e.display.scroller.clientWidth/ir(e.display)-3);return function(i){if(Rt(e.doc,i))return 0;var o=0;if(i.widgets)for(var a=0;a0&&(s=Ge(e.doc,l.line).text).length==l.ch){var u=V(s,s.length,e.options.tabSize)-s.length;l=et(l.line,Math.max(0,Math.round((o-Mn(e.display).left)/ir(e.display))-u))}return l}function ur(e,t){if(t>=e.display.viewTo)return null;if((t-=e.display.viewFrom)<0)return null;for(var n=e.display.view,r=0;rt)&&(i.updateLineNumbers=t),e.curOp.viewChanged=!0,t>=i.viewTo)kt&&Nt(e.doc,t)i.viewFrom?dr(e):(i.viewFrom+=r,i.viewTo+=r);else if(t<=i.viewFrom&&n>=i.viewTo)dr(e);else if(t<=i.viewFrom){var o=pr(e,n,n+r,1);o?(i.view=i.view.slice(o.index),i.viewFrom=o.lineN,i.viewTo+=r):dr(e)}else if(n>=i.viewTo){var a=pr(e,t,t,-1);a?(i.view=i.view.slice(0,a.index),i.viewTo=a.lineN):dr(e)}else{var c=pr(e,t,t,-1),s=pr(e,n,n+r,1);c&&s?(i.view=i.view.slice(0,c.index).concat(on(e,c.lineN,s.lineN)).concat(i.view.slice(s.index)),i.viewTo+=r):dr(e)}var l=i.externalMeasured;l&&(n=i.lineN&&t=r.viewTo)){var o=r.view[ur(e,t)];if(null!=o.node){var a=o.changes||(o.changes=[]);-1==I(a,n)&&a.push(n)}}}function dr(e){e.display.viewFrom=e.display.viewTo=e.doc.first,e.display.view=[],e.display.viewOffset=0}function pr(e,t,n,r){var i,o=ur(e,t),a=e.display.view;if(!kt||n==e.doc.first+e.doc.size)return{index:o,lineN:n};for(var c=e.display.viewFrom,s=0;s0){if(o==a.length-1)return null;i=c+a[o].size-t,o++}else i=c-t;t+=i,n+=i}for(;Nt(e.doc,n)!=n;){if(o==(r<0?0:a.length-1))return null;n+=r*a[o-(r<0?1:0)].size,o+=r}return{index:o,lineN:n}}function zr(e){for(var t=e.display.view,n=0,r=0;r=e.display.viewTo||c.to().linet||t==n&&a.to==t)&&(r(Math.max(a.from,t),Math.min(a.to,n),1==a.level?"rtl":"ltr",o),i=!0)}i||r(t,n,"ltr")}(z,n||0,null==r?h:r,(function(e,t,i,f){var v="ltr"==i,g=d(e,v?"left":"right"),m=d(t-1,v?"right":"left"),y=null==n&&0==e,b=null==r&&t==h,w=0==f,k=!z||f==z.length-1;if(m.top-g.top<=3){var x=(l?b:y)&&k,j=(l?y:b)&&w?c:(v?g:m).left,M=x?s:(v?m:g).right;u(j,g.top,M-j,g.bottom)}else{var _,C,q,S;v?(_=l&&y&&w?c:g.left,C=l?s:p(e,i,"before"),q=l?c:p(t,i,"after"),S=l&&b&&k?s:m.right):(_=l?p(e,i,"before"):c,C=!l&&y&&w?s:g.right,q=!l&&b&&k?c:m.left,S=l?p(t,i,"after"):s),u(_,g.top,C-_,g.bottom),g.bottom0?t.blinker=setInterval((function(){return t.cursorDiv.style.visibility=(n=!n)?"":"hidden"}),e.options.cursorBlinkRate):e.options.cursorBlinkRate<0&&(t.cursorDiv.style.visibility="hidden")}}function kr(e){e.state.focused||(e.display.input.focus(),jr(e))}function xr(e){e.state.delayingBlurEvent=!0,setTimeout((function(){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1,Mr(e))}),100)}function jr(e,t){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1),"nocursor"!=e.options.readOnly&&(e.state.focused||(pe(e,"focus",e,t),e.state.focused=!0,A(e.display.wrapper,"CodeMirror-focused"),e.curOp||e.display.selForContextMenu==e.doc.sel||(e.display.input.reset(),s&&setTimeout((function(){return e.display.input.reset(!0)}),20)),e.display.input.receivedFocus()),wr(e))}function Mr(e,t){e.state.delayingBlurEvent||(e.state.focused&&(pe(e,"blur",e,t),e.state.focused=!1,_(e.display.wrapper,"CodeMirror-focused")),clearInterval(e.display.blinker),setTimeout((function(){e.state.focused||(e.display.shift=!1)}),150))}function _r(e){for(var t=e.display,n=t.lineDiv.offsetTop,r=0;r.005||h<-.005)&&($e(i.line,s),Cr(i.line),i.rest))for(var d=0;de.display.sizerWidth){var p=Math.ceil(l/ir(e.display));p>e.display.maxLineLength&&(e.display.maxLineLength=p,e.display.maxLine=i.line,e.display.maxLineChanged=!0)}}}}function Cr(e){if(e.widgets)for(var t=0;t=a&&(o=Ke(t,Ft(Ge(t,s))-e.wrapper.clientHeight),a=s)}return{from:o,to:Math.max(a,o+1)}}function Sr(e,t){var n=e.display,r=rr(e.display);t.top<0&&(t.top=0);var i=e.curOp&&null!=e.curOp.scrollTop?e.curOp.scrollTop:n.scroller.scrollTop,o=qn(e),a={};t.bottom-t.top>o&&(t.bottom=t.top+o);var c=e.doc.height+jn(n),s=t.topc-r;if(t.topi+o){var u=Math.min(t.top,(l?c:t.bottom)-o);u!=i&&(a.scrollTop=u)}var f=e.curOp&&null!=e.curOp.scrollLeft?e.curOp.scrollLeft:n.scroller.scrollLeft,h=Cn(e)-(e.options.fixedGutter?n.gutters.offsetWidth:0),d=t.right-t.left>h;return d&&(t.right=t.left+h),t.left<10?a.scrollLeft=0:t.lefth+f-3&&(a.scrollLeft=t.right+(d?0:10)-h),a}function Or(e,t){null!=t&&(Ar(e),e.curOp.scrollTop=(null==e.curOp.scrollTop?e.doc.scrollTop:e.curOp.scrollTop)+t)}function Tr(e){Ar(e);var t=e.getCursor();e.curOp.scrollToPos={from:t,to:t,margin:e.options.cursorScrollMargin}}function Er(e,t,n){null==t&&null==n||Ar(e),null!=t&&(e.curOp.scrollLeft=t),null!=n&&(e.curOp.scrollTop=n)}function Ar(e){var t=e.curOp.scrollToPos;t&&(e.curOp.scrollToPos=null,Hr(e,Zn(e,t.from),Zn(e,t.to),t.margin))}function Hr(e,t,n,r){var i=Sr(e,{left:Math.min(t.left,n.left),top:Math.min(t.top,n.top)-r,right:Math.max(t.right,n.right),bottom:Math.max(t.bottom,n.bottom)+r});Er(e,i.scrollLeft,i.scrollTop)}function Lr(e,t){Math.abs(e.doc.scrollTop-t)<2||(n||si(e,{top:t}),Dr(e,t,!0),n&&si(e),ri(e,100))}function Dr(e,t,n){t=Math.max(0,Math.min(e.display.scroller.scrollHeight-e.display.scroller.clientHeight,t)),(e.display.scroller.scrollTop!=t||n)&&(e.doc.scrollTop=t,e.display.scrollbars.setScrollTop(t),e.display.scroller.scrollTop!=t&&(e.display.scroller.scrollTop=t))}function Pr(e,t,n,r){t=Math.max(0,Math.min(t,e.display.scroller.scrollWidth-e.display.scroller.clientWidth)),(n?t==e.doc.scrollLeft:Math.abs(e.doc.scrollLeft-t)<2)&&!r||(e.doc.scrollLeft=t,fi(e),e.display.scroller.scrollLeft!=t&&(e.display.scroller.scrollLeft=t),e.display.scrollbars.setScrollLeft(t))}function Vr(e){var t=e.display,n=t.gutters.offsetWidth,r=Math.round(e.doc.height+jn(e.display));return{clientHeight:t.scroller.clientHeight,viewHeight:t.wrapper.clientHeight,scrollWidth:t.scroller.scrollWidth,clientWidth:t.scroller.clientWidth,viewWidth:t.wrapper.clientWidth,barLeft:e.options.fixedGutter?n:0,docHeight:r,scrollHeight:r+_n(e)+t.barHeight,nativeBarWidth:t.nativeBarWidth,gutterWidth:n}}var Nr=function(e,t,n){this.cm=n;var r=this.vert=S("div",[S("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar"),i=this.horiz=S("div",[S("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar");r.tabIndex=i.tabIndex=-1,e(r),e(i),fe(r,"scroll",(function(){r.clientHeight&&t(r.scrollTop,"vertical")})),fe(i,"scroll",(function(){i.clientWidth&&t(i.scrollLeft,"horizontal")})),this.checkedZeroWidth=!1,a&&c<8&&(this.horiz.style.minHeight=this.vert.style.minWidth="18px")};Nr.prototype.update=function(e){var t=e.scrollWidth>e.clientWidth+1,n=e.scrollHeight>e.clientHeight+1,r=e.nativeBarWidth;if(n){this.vert.style.display="block",this.vert.style.bottom=t?r+"px":"0";var i=e.viewHeight-(t?r:0);this.vert.firstChild.style.height=Math.max(0,e.scrollHeight-e.clientHeight+i)+"px"}else this.vert.style.display="",this.vert.firstChild.style.height="0";if(t){this.horiz.style.display="block",this.horiz.style.right=n?r+"px":"0",this.horiz.style.left=e.barLeft+"px";var o=e.viewWidth-e.barLeft-(n?r:0);this.horiz.firstChild.style.width=Math.max(0,e.scrollWidth-e.clientWidth+o)+"px"}else this.horiz.style.display="",this.horiz.firstChild.style.width="0";return!this.checkedZeroWidth&&e.clientHeight>0&&(0==r&&this.zeroWidthHack(),this.checkedZeroWidth=!0),{right:n?r:0,bottom:t?r:0}},Nr.prototype.setScrollLeft=function(e){this.horiz.scrollLeft!=e&&(this.horiz.scrollLeft=e),this.disableHoriz&&this.enableZeroWidthBar(this.horiz,this.disableHoriz,"horiz")},Nr.prototype.setScrollTop=function(e){this.vert.scrollTop!=e&&(this.vert.scrollTop=e),this.disableVert&&this.enableZeroWidthBar(this.vert,this.disableVert,"vert")},Nr.prototype.zeroWidthHack=function(){var e=m&&!d?"12px":"18px";this.horiz.style.height=this.vert.style.width=e,this.horiz.style.pointerEvents=this.vert.style.pointerEvents="none",this.disableHoriz=new N,this.disableVert=new N},Nr.prototype.enableZeroWidthBar=function(e,t,n){e.style.pointerEvents="auto",t.set(1e3,(function r(){var i=e.getBoundingClientRect();("vert"==n?document.elementFromPoint(i.right-1,(i.top+i.bottom)/2):document.elementFromPoint((i.right+i.left)/2,i.bottom-1))!=e?e.style.pointerEvents="none":t.set(1e3,r)}))},Nr.prototype.clear=function(){var e=this.horiz.parentNode;e.removeChild(this.horiz),e.removeChild(this.vert)};var Ir=function(){};function Rr(e,t){t||(t=Vr(e));var n=e.display.barWidth,r=e.display.barHeight;Br(e,t);for(var i=0;i<4&&n!=e.display.barWidth||r!=e.display.barHeight;i++)n!=e.display.barWidth&&e.options.lineWrapping&&_r(e),Br(e,Vr(e)),n=e.display.barWidth,r=e.display.barHeight}function Br(e,t){var n=e.display,r=n.scrollbars.update(t);n.sizer.style.paddingRight=(n.barWidth=r.right)+"px",n.sizer.style.paddingBottom=(n.barHeight=r.bottom)+"px",n.heightForcer.style.borderBottom=r.bottom+"px solid transparent",r.right&&r.bottom?(n.scrollbarFiller.style.display="block",n.scrollbarFiller.style.height=r.bottom+"px",n.scrollbarFiller.style.width=r.right+"px"):n.scrollbarFiller.style.display="",r.bottom&&e.options.coverGutterNextToScrollbar&&e.options.fixedGutter?(n.gutterFiller.style.display="block",n.gutterFiller.style.height=r.bottom+"px",n.gutterFiller.style.width=t.gutterWidth+"px"):n.gutterFiller.style.display=""}Ir.prototype.update=function(){return{bottom:0,right:0}},Ir.prototype.setScrollLeft=function(){},Ir.prototype.setScrollTop=function(){},Ir.prototype.clear=function(){};var Fr={native:Nr,null:Ir};function Ur(e){e.display.scrollbars&&(e.display.scrollbars.clear(),e.display.scrollbars.addClass&&_(e.display.wrapper,e.display.scrollbars.addClass)),e.display.scrollbars=new Fr[e.options.scrollbarStyle]((function(t){e.display.wrapper.insertBefore(t,e.display.scrollbarFiller),fe(t,"mousedown",(function(){e.state.focused&&setTimeout((function(){return e.display.input.focus()}),0)})),t.setAttribute("cm-not-content","true")}),(function(t,n){"horizontal"==n?Pr(e,t):Lr(e,t)}),e),e.display.scrollbars.addClass&&A(e.display.wrapper,e.display.scrollbars.addClass)}var Wr=0;function Gr(e){var t;e.curOp={cm:e,viewChanged:!1,startHeight:e.doc.height,forceUpdate:!1,updateInput:0,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++Wr},t=e.curOp,an?an.ops.push(t):t.ownsGroup=an={ops:[t],delayedCallbacks:[]}}function Yr(e){var t=e.curOp;t&&function(e,t){var n=e.ownsGroup;if(n)try{!function(e){var t=e.delayedCallbacks,n=0;do{for(;n=n.viewTo)||n.maxLineChanged&&t.options.lineWrapping,e.update=e.mustUpdate&&new oi(t,e.mustUpdate&&{top:e.scrollTop,ensure:e.scrollToPos},e.forceUpdate)}function $r(e){e.updatedDisplay=e.mustUpdate&&ai(e.cm,e.update)}function Xr(e){var t=e.cm,n=t.display;e.updatedDisplay&&_r(t),e.barMeasure=Vr(t),n.maxLineChanged&&!t.options.lineWrapping&&(e.adjustWidthTo=On(t,n.maxLine,n.maxLine.text.length).left+3,t.display.sizerWidth=e.adjustWidthTo,e.barMeasure.scrollWidth=Math.max(n.scroller.clientWidth,n.sizer.offsetLeft+e.adjustWidthTo+_n(t)+t.display.barWidth),e.maxScrollLeft=Math.max(0,n.sizer.offsetLeft+e.adjustWidthTo-Cn(t))),(e.updatedDisplay||e.selectionChanged)&&(e.preparedSelection=n.input.prepareSelection())}function Kr(e){var t=e.cm;null!=e.adjustWidthTo&&(t.display.sizer.style.minWidth=e.adjustWidthTo+"px",e.maxScrollLeft(window.innerHeight||document.documentElement.clientHeight)&&(i=!1),null!=i&&!p){var o=S("div","\u200b",null,"position: absolute;\n top: "+(t.top-n.viewOffset-xn(e.display))+"px;\n height: "+(t.bottom-t.top+_n(e)+n.barHeight)+"px;\n left: "+t.left+"px; width: "+Math.max(2,t.right-t.left)+"px;");e.display.lineSpace.appendChild(o),o.scrollIntoView(i),e.display.lineSpace.removeChild(o)}}}(t,function(e,t,n,r){var i;null==r&&(r=0),e.options.lineWrapping||t!=n||(n="before"==(t=t.ch?et(t.line,"before"==t.sticky?t.ch-1:t.ch,"after"):t).sticky?et(t.line,t.ch+1,"before"):t);for(var o=0;o<5;o++){var a=!1,c=Yn(e,t),s=n&&n!=t?Yn(e,n):c,l=Sr(e,i={left:Math.min(c.left,s.left),top:Math.min(c.top,s.top)-r,right:Math.max(c.left,s.left),bottom:Math.max(c.bottom,s.bottom)+r}),u=e.doc.scrollTop,f=e.doc.scrollLeft;if(null!=l.scrollTop&&(Lr(e,l.scrollTop),Math.abs(e.doc.scrollTop-u)>1&&(a=!0)),null!=l.scrollLeft&&(Pr(e,l.scrollLeft),Math.abs(e.doc.scrollLeft-f)>1&&(a=!0)),!a)break}return i}(t,ct(r,e.scrollToPos.from),ct(r,e.scrollToPos.to),e.scrollToPos.margin));var i=e.maybeHiddenMarkers,o=e.maybeUnhiddenMarkers;if(i)for(var a=0;a=e.display.viewTo)){var n=+new Date+e.options.workTime,r=dt(e,t.highlightFrontier),i=[];t.iter(r.line,Math.min(t.first+t.size,e.display.viewTo+500),(function(o){if(r.line>=e.display.viewFrom){var a=o.styles,c=o.text.length>e.options.maxHighlightLength?Be(t.mode,r.state):null,s=ft(e,o,r,!0);c&&(r.state=c),o.styles=s.styles;var l=o.styleClasses,u=s.classes;u?o.styleClasses=u:l&&(o.styleClasses=null);for(var f=!a||a.length!=o.styles.length||l!=u&&(!l||!u||l.bgClass!=u.bgClass||l.textClass!=u.textClass),h=0;!f&&hn)return ri(e,e.options.workDelay),!0})),t.highlightFrontier=r.line,t.modeFrontier=Math.max(t.modeFrontier,r.line),i.length&&Jr(e,(function(){for(var t=0;t=n.viewFrom&&t.visible.to<=n.viewTo&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo)&&n.renderedView==n.view&&0==zr(e))return!1;hi(e)&&(dr(e),t.dims=or(e));var i=r.first+r.size,o=Math.max(t.visible.from-e.options.viewportMargin,r.first),a=Math.min(i,t.visible.to+e.options.viewportMargin);n.viewFroma&&n.viewTo-a<20&&(a=Math.min(i,n.viewTo)),kt&&(o=Nt(e.doc,o),a=It(e.doc,a));var c=o!=n.viewFrom||a!=n.viewTo||n.lastWrapHeight!=t.wrapperHeight||n.lastWrapWidth!=t.wrapperWidth;!function(e,t,n){var r=e.display;0==r.view.length||t>=r.viewTo||n<=r.viewFrom?(r.view=on(e,t,n),r.viewFrom=t):(r.viewFrom>t?r.view=on(e,t,r.viewFrom).concat(r.view):r.viewFromn&&(r.view=r.view.slice(0,ur(e,n)))),r.viewTo=n}(e,o,a),n.viewOffset=Ft(Ge(e.doc,n.viewFrom)),e.display.mover.style.top=n.viewOffset+"px";var l=zr(e);if(!c&&0==l&&!t.force&&n.renderedView==n.view&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo))return!1;var u=function(e){if(e.hasFocus())return null;var t=E();if(!t||!T(e.display.lineDiv,t))return null;var n={activeElt:t};if(window.getSelection){var r=window.getSelection();r.anchorNode&&r.extend&&T(e.display.lineDiv,r.anchorNode)&&(n.anchorNode=r.anchorNode,n.anchorOffset=r.anchorOffset,n.focusNode=r.focusNode,n.focusOffset=r.focusOffset)}return n}(e);return l>4&&(n.lineDiv.style.display="none"),function(e,t,n){var r=e.display,i=e.options.lineNumbers,o=r.lineDiv,a=o.firstChild;function c(t){var n=t.nextSibling;return s&&m&&e.display.currentWheelTarget==t?t.style.display="none":t.parentNode.removeChild(t),n}for(var l=r.view,u=r.viewFrom,f=0;f-1&&(d=!1),un(e,h,u,n)),d&&(C(h.lineNumber),h.lineNumber.appendChild(document.createTextNode(Je(e.options,u)))),a=h.node.nextSibling}else{var p=gn(e,h,u,n);o.insertBefore(p,a)}u+=h.size}for(;a;)a=c(a)}(e,n.updateLineNumbers,t.dims),l>4&&(n.lineDiv.style.display=""),n.renderedView=n.view,function(e){if(e&&e.activeElt&&e.activeElt!=E()&&(e.activeElt.focus(),!/^(INPUT|TEXTAREA)$/.test(e.activeElt.nodeName)&&e.anchorNode&&T(document.body,e.anchorNode)&&T(document.body,e.focusNode))){var t=window.getSelection(),n=document.createRange();n.setEnd(e.anchorNode,e.anchorOffset),n.collapse(!1),t.removeAllRanges(),t.addRange(n),t.extend(e.focusNode,e.focusOffset)}}(u),C(n.cursorDiv),C(n.selectionDiv),n.gutters.style.height=n.sizer.style.minHeight=0,c&&(n.lastWrapHeight=t.wrapperHeight,n.lastWrapWidth=t.wrapperWidth,ri(e,400)),n.updateLineNumbers=null,!0}function ci(e,t){for(var n=t.viewport,r=!0;;r=!1){if(r&&e.options.lineWrapping&&t.oldDisplayWidth!=Cn(e))r&&(t.visible=qr(e.display,e.doc,n));else if(n&&null!=n.top&&(n={top:Math.min(e.doc.height+jn(e.display)-qn(e),n.top)}),t.visible=qr(e.display,e.doc,n),t.visible.from>=e.display.viewFrom&&t.visible.to<=e.display.viewTo)break;if(!ai(e,t))break;_r(e);var i=Vr(e);vr(e),Rr(e,i),ui(e,i),t.force=!1}t.signal(e,"update",e),e.display.viewFrom==e.display.reportedViewFrom&&e.display.viewTo==e.display.reportedViewTo||(t.signal(e,"viewportChange",e,e.display.viewFrom,e.display.viewTo),e.display.reportedViewFrom=e.display.viewFrom,e.display.reportedViewTo=e.display.viewTo)}function si(e,t){var n=new oi(e,t);if(ai(e,n)){_r(e),ci(e,n);var r=Vr(e);vr(e),Rr(e,r),ui(e,r),n.finish()}}function li(e){var t=e.gutters.offsetWidth;e.sizer.style.marginLeft=t+"px"}function ui(e,t){e.display.sizer.style.minHeight=t.docHeight+"px",e.display.heightForcer.style.top=t.docHeight+"px",e.display.gutters.style.height=t.docHeight+e.display.barHeight+_n(e)+"px"}function fi(e){var t=e.display,n=t.view;if(t.alignWidgets||t.gutters.firstChild&&e.options.fixedGutter){for(var r=ar(t)-t.scroller.scrollLeft+e.doc.scrollLeft,i=t.gutters.offsetWidth,o=r+"px",a=0;ac.clientWidth,u=c.scrollHeight>c.clientHeight;if(i&&l||o&&u){if(o&&m&&s)e:for(var h=t.target,d=a.view;h!=c;h=h.parentNode)for(var p=0;p=0&&tt(e,r.to())<=0)return n}return-1};var xi=function(e,t){this.anchor=e,this.head=t};function ji(e,t,n){var r=e&&e.options.selectionsMayTouch,i=t[n];t.sort((function(e,t){return tt(e.from(),t.from())})),n=I(t,i);for(var o=1;o0:s>=0){var l=ot(c.from(),a.from()),u=it(c.to(),a.to()),f=c.empty()?a.from()==a.head:c.from()==c.head;o<=n&&--n,t.splice(--o,2,new xi(f?u:l,f?l:u))}}return new ki(t,n)}function Mi(e,t){return new ki([new xi(e,t||e)],0)}function _i(e){return e.text?et(e.from.line+e.text.length-1,Z(e.text).length+(1==e.text.length?e.from.ch:0)):e.to}function Ci(e,t){if(tt(e,t.from)<0)return e;if(tt(e,t.to)<=0)return _i(t);var n=e.line+t.text.length-(t.to.line-t.from.line)-1,r=e.ch;return e.line==t.to.line&&(r+=_i(t).ch-t.to.ch),et(n,r)}function qi(e,t){for(var n=[],r=0;r1&&e.remove(c.line+1,p-1),e.insert(c.line+1,g)}sn(e,"change",e,t)}function Hi(e,t,n){!function e(r,i,o){if(r.linked)for(var a=0;ac-(e.cm?e.cm.options.historyEventDelay:500)||"*"==t.origin.charAt(0)))&&(o=function(e,t){return t?(Ni(e.done),Z(e.done)):e.done.length&&!Z(e.done).ranges?Z(e.done):e.done.length>1&&!e.done[e.done.length-2].ranges?(e.done.pop(),Z(e.done)):void 0}(i,i.lastOp==r)))a=Z(o.changes),0==tt(t.from,t.to)&&0==tt(t.from,a.to)?a.to=_i(t):o.changes.push(Vi(e,t));else{var s=Z(i.done);for(s&&s.ranges||Ri(e.sel,i.done),o={changes:[Vi(e,t)],generation:i.generation},i.done.push(o);i.done.length>i.undoDepth;)i.done.shift(),i.done[0].ranges||i.done.shift()}i.done.push(n),i.generation=++i.maxGeneration,i.lastModTime=i.lastSelTime=c,i.lastOp=i.lastSelOp=r,i.lastOrigin=i.lastSelOrigin=t.origin,a||pe(e,"historyAdded")}function Ri(e,t){var n=Z(t);n&&n.ranges&&n.equals(e)||t.push(e)}function Bi(e,t,n,r){var i=t["spans_"+e.id],o=0;e.iter(Math.max(e.first,n),Math.min(e.first+e.size,r),(function(n){n.markedSpans&&((i||(i=t["spans_"+e.id]={}))[o]=n.markedSpans),++o}))}function Fi(e){if(!e)return null;for(var t,n=0;n-1&&(Z(c)[f]=l[f],delete l[f])}}}return r}function Gi(e,t,n,r){if(r){var i=e.anchor;if(n){var o=tt(t,i)<0;o!=tt(n,i)<0?(i=t,t=n):o!=tt(t,n)<0&&(t=n)}return new xi(i,t)}return new xi(n||t,t)}function Yi(e,t,n,r,i){null==i&&(i=e.cm&&(e.cm.display.shift||e.extend)),Qi(e,new ki([Gi(e.sel.primary(),t,n,i)],0),r)}function Zi(e,t,n){for(var r=[],i=e.cm&&(e.cm.display.shift||e.extend),o=0;o=t.ch:c.to>t.ch))){if(i&&(pe(s,"beforeCursorEnter"),s.explicitlyCleared)){if(o.markedSpans){--a;continue}break}if(!s.atomic)continue;if(n){var f=s.find(r<0?1:-1),h=void 0;if((r<0?u:l)&&(f=oo(e,f,-r,f&&f.line==t.line?o:null)),f&&f.line==t.line&&(h=tt(f,n))&&(r<0?h<0:h>0))return ro(e,f,t,r,i)}var d=s.find(r<0?-1:1);return(r<0?l:u)&&(d=oo(e,d,r,d.line==t.line?o:null)),d?ro(e,d,t,r,i):null}}return t}function io(e,t,n,r,i){var o=r||1;return ro(e,t,n,o,i)||!i&&ro(e,t,n,o,!0)||ro(e,t,n,-o,i)||!i&&ro(e,t,n,-o,!0)||(e.cantEdit=!0,et(e.first,0))}function oo(e,t,n,r){return n<0&&0==t.ch?t.line>e.first?ct(e,et(t.line-1)):null:n>0&&t.ch==(r||Ge(e,t.line)).text.length?t.line0)){var u=[s,1],f=tt(l.from,c.from),h=tt(l.to,c.to);(f<0||!a.inclusiveLeft&&!f)&&u.push({from:l.from,to:c.from}),(h>0||!a.inclusiveRight&&!h)&&u.push({from:c.to,to:l.to}),i.splice.apply(i,u),s+=u.length-3}}return i}(e,t.from,t.to);if(r)for(var i=r.length-1;i>=0;--i)lo(e,{from:r[i].from,to:r[i].to,text:i?[""]:t.text,origin:t.origin});else lo(e,t)}}function lo(e,t){if(1!=t.text.length||""!=t.text[0]||0!=tt(t.from,t.to)){var n=qi(e,t);Ii(e,t,n,e.cm?e.cm.curOp.id:NaN),ho(e,t,n,_t(e,t));var r=[];Hi(e,(function(e,n){n||-1!=I(r,e.history)||(go(e.history,t),r.push(e.history)),ho(e,t,null,_t(e,t))}))}}function uo(e,t,n){var r=e.cm&&e.cm.state.suppressEdits;if(!r||n){for(var i,o=e.history,a=e.sel,c="undo"==t?o.done:o.undone,s="undo"==t?o.undone:o.done,l=0;l=0;--d){var p=h(d);if(p)return p.v}}}}function fo(e,t){if(0!=t&&(e.first+=t,e.sel=new ki($(e.sel.ranges,(function(e){return new xi(et(e.anchor.line+t,e.anchor.ch),et(e.head.line+t,e.head.ch))})),e.sel.primIndex),e.cm)){fr(e.cm,e.first,e.first-t,t);for(var n=e.cm.display,r=n.viewFrom;re.lastLine())){if(t.from.lineo&&(t={from:t.from,to:et(o,Ge(e,o).text.length),text:[t.text[0]],origin:t.origin}),t.removed=Ye(e,t.from,t.to),n||(n=qi(e,t)),e.cm?function(e,t,n){var r=e.doc,i=e.display,o=t.from,a=t.to,c=!1,s=o.line;e.options.lineWrapping||(s=Xe(Vt(Ge(r,o.line))),r.iter(s,a.line+1,(function(e){if(e==i.maxLine)return c=!0,!0}))),r.sel.contains(t.from,t.to)>-1&&ve(e),Ai(r,t,n,cr(e)),e.options.lineWrapping||(r.iter(s,o.line+t.text.length,(function(e){var t=Ut(e);t>i.maxLineLength&&(i.maxLine=e,i.maxLineLength=t,i.maxLineChanged=!0,c=!1)})),c&&(e.curOp.updateMaxLine=!0)),function(e,t){if(e.modeFrontier=Math.min(e.modeFrontier,t),!(e.highlightFrontiern;r--){var i=Ge(e,r).stateAfter;if(i&&(!(i instanceof lt)||r+i.lookAhead1||!(this.children[0]instanceof yo))){var c=[];this.collapse(c),this.children=[new yo(c)],this.children[0].parent=this}},collapse:function(e){for(var t=0;t50){for(var a=i.lines.length%25+25,c=a;c10);e.parent.maybeSpill()}},iterN:function(e,t,n){for(var r=0;r0||0==a&&!1!==o.clearWhenEmpty)return o;if(o.replacedWith&&(o.collapsed=!0,o.widgetNode=O("span",[o.replacedWith],"CodeMirror-widget"),r.handleMouseEvents||o.widgetNode.setAttribute("cm-ignore-events","true"),r.insertLeft&&(o.widgetNode.insertLeft=!0)),o.collapsed){if(Pt(e,t.line,t,n,o)||t.line!=n.line&&Pt(e,n.line,t,n,o))throw new Error("Inserting collapsed marker partially overlapping an existing one");kt=!0}o.addToHistory&&Ii(e,{from:t,to:n,origin:"markText"},e.sel,NaN);var c,s=t.line,l=e.cm;if(e.iter(s,n.line+1,(function(e){l&&o.collapsed&&!l.options.lineWrapping&&Vt(e)==l.display.maxLine&&(c=!0),o.collapsed&&s!=t.line&&$e(e,0),function(e,t){e.markedSpans=e.markedSpans?e.markedSpans.concat([t]):[t],t.marker.attachLine(e)}(e,new xt(o,s==t.line?t.ch:null,s==n.line?n.ch:null)),++s})),o.collapsed&&e.iter(t.line,n.line+1,(function(t){Rt(e,t)&&$e(t,0)})),o.clearOnEnter&&fe(o,"beforeCursorEnter",(function(){return o.clear()})),o.readOnly&&(wt=!0,(e.history.done.length||e.history.undone.length)&&e.clearHistory()),o.collapsed&&(o.id=++xo,o.atomic=!0),l){if(c&&(l.curOp.updateMaxLine=!0),o.collapsed)fr(l,t.line,n.line+1);else if(o.className||o.startStyle||o.endStyle||o.css||o.attributes||o.title)for(var u=t.line;u<=n.line;u++)hr(l,u,"text");o.atomic&&to(l.doc),sn(l,"markerAdded",l,o)}return o}jo.prototype.clear=function(){if(!this.explicitlyCleared){var e=this.doc.cm,t=e&&!e.curOp;if(t&&Gr(e),ge(this,"clear")){var n=this.find();n&&sn(this,"clear",n.from,n.to)}for(var r=null,i=null,o=0;oe.display.maxLineLength&&(e.display.maxLine=l,e.display.maxLineLength=u,e.display.maxLineChanged=!0)}null!=r&&e&&this.collapsed&&fr(e,r,i+1),this.lines.length=0,this.explicitlyCleared=!0,this.atomic&&this.doc.cantEdit&&(this.doc.cantEdit=!1,e&&to(e.doc)),e&&sn(e,"markerCleared",e,this,r,i),t&&Yr(e),this.parent&&this.parent.clear()}},jo.prototype.find=function(e,t){var n,r;null==e&&"bookmark"==this.type&&(e=1);for(var i=0;i=0;s--)so(this,r[s]);c?Ki(this,c):this.cm&&Tr(this.cm)})),undo:ni((function(){uo(this,"undo")})),redo:ni((function(){uo(this,"redo")})),undoSelection:ni((function(){uo(this,"undo",!0)})),redoSelection:ni((function(){uo(this,"redo",!0)})),setExtending:function(e){this.extend=e},getExtending:function(){return this.extend},historySize:function(){for(var e=this.history,t=0,n=0,r=0;r=e.ch)&&t.push(i.marker.parent||i.marker)}return t},findMarks:function(e,t,n){e=ct(this,e),t=ct(this,t);var r=[],i=e.line;return this.iter(e.line,t.line+1,(function(o){var a=o.markedSpans;if(a)for(var c=0;c=s.to||null==s.from&&i!=e.line||null!=s.from&&i==t.line&&s.from>=t.ch||n&&!n(s.marker)||r.push(s.marker.parent||s.marker)}++i})),r},getAllMarks:function(){var e=[];return this.iter((function(t){var n=t.markedSpans;if(n)for(var r=0;re)return t=e,!0;e-=o,++n})),ct(this,et(n,t))},indexFromPos:function(e){var t=(e=ct(this,e)).ch;if(e.linet&&(t=e.from),null!=e.to&&e.to-1)return t.state.draggingText(e),void setTimeout((function(){return t.display.input.focus()}),20);try{var f=e.dataTransfer.getData("Text");if(f){var h;if(t.state.draggingText&&!t.state.draggingText.copy&&(h=t.listSelections()),Ji(t.doc,Mi(n,n)),h)for(var d=0;d=0;t--)po(e.doc,"",r[t].from,r[t].to,"+delete");Tr(e)}))}function Xo(e,t,n){var r=ie(e.text,t+n,n);return r<0||r>e.text.length?null:r}function Ko(e,t,n){var r=Xo(e,t.ch,n);return null==r?null:new et(t.line,r,n<0?"after":"before")}function Qo(e,t,n,r,i){if(e){"rtl"==t.doc.direction&&(i=-i);var o=le(n,t.doc.direction);if(o){var a,c=i<0?Z(o):o[0],s=i<0==(1==c.level)?"after":"before";if(c.level>0||"rtl"==t.doc.direction){var l=En(t,n);a=i<0?n.text.length-1:0;var u=An(t,l,a).top;a=oe((function(e){return An(t,l,e).top==u}),i<0==(1==c.level)?c.from:c.to-1,a),"before"==s&&(a=Xo(n,a,1))}else a=i<0?c.to:c.from;return new et(r,a,s)}}return new et(r,i<0?n.text.length:0,i<0?"before":"after")}Ro.basic={Left:"goCharLeft",Right:"goCharRight",Up:"goLineUp",Down:"goLineDown",End:"goLineEnd",Home:"goLineStartSmart",PageUp:"goPageUp",PageDown:"goPageDown",Delete:"delCharAfter",Backspace:"delCharBefore","Shift-Backspace":"delCharBefore",Tab:"defaultTab","Shift-Tab":"indentAuto",Enter:"newlineAndIndent",Insert:"toggleOverwrite",Esc:"singleSelection"},Ro.pcDefault={"Ctrl-A":"selectAll","Ctrl-D":"deleteLine","Ctrl-Z":"undo","Shift-Ctrl-Z":"redo","Ctrl-Y":"redo","Ctrl-Home":"goDocStart","Ctrl-End":"goDocEnd","Ctrl-Up":"goLineUp","Ctrl-Down":"goLineDown","Ctrl-Left":"goGroupLeft","Ctrl-Right":"goGroupRight","Alt-Left":"goLineStart","Alt-Right":"goLineEnd","Ctrl-Backspace":"delGroupBefore","Ctrl-Delete":"delGroupAfter","Ctrl-S":"save","Ctrl-F":"find","Ctrl-G":"findNext","Shift-Ctrl-G":"findPrev","Shift-Ctrl-F":"replace","Shift-Ctrl-R":"replaceAll","Ctrl-[":"indentLess","Ctrl-]":"indentMore","Ctrl-U":"undoSelection","Shift-Ctrl-U":"redoSelection","Alt-U":"redoSelection",fallthrough:"basic"},Ro.emacsy={"Ctrl-F":"goCharRight","Ctrl-B":"goCharLeft","Ctrl-P":"goLineUp","Ctrl-N":"goLineDown","Alt-F":"goWordRight","Alt-B":"goWordLeft","Ctrl-A":"goLineStart","Ctrl-E":"goLineEnd","Ctrl-V":"goPageDown","Shift-Ctrl-V":"goPageUp","Ctrl-D":"delCharAfter","Ctrl-H":"delCharBefore","Alt-D":"delWordAfter","Alt-Backspace":"delWordBefore","Ctrl-K":"killLine","Ctrl-T":"transposeChars","Ctrl-O":"openLine"},Ro.macDefault={"Cmd-A":"selectAll","Cmd-D":"deleteLine","Cmd-Z":"undo","Shift-Cmd-Z":"redo","Cmd-Y":"redo","Cmd-Home":"goDocStart","Cmd-Up":"goDocStart","Cmd-End":"goDocEnd","Cmd-Down":"goDocEnd","Alt-Left":"goGroupLeft","Alt-Right":"goGroupRight","Cmd-Left":"goLineLeft","Cmd-Right":"goLineRight","Alt-Backspace":"delGroupBefore","Ctrl-Alt-Backspace":"delGroupAfter","Alt-Delete":"delGroupAfter","Cmd-S":"save","Cmd-F":"find","Cmd-G":"findNext","Shift-Cmd-G":"findPrev","Cmd-Alt-F":"replace","Shift-Cmd-Alt-F":"replaceAll","Cmd-[":"indentLess","Cmd-]":"indentMore","Cmd-Backspace":"delWrappedLineLeft","Cmd-Delete":"delWrappedLineRight","Cmd-U":"undoSelection","Shift-Cmd-U":"redoSelection","Ctrl-Up":"goDocStart","Ctrl-Down":"goDocEnd",fallthrough:["basic","emacsy"]},Ro.default=m?Ro.macDefault:Ro.pcDefault;var Jo={selectAll:ao,singleSelection:function(e){return e.setSelection(e.getCursor("anchor"),e.getCursor("head"),B)},killLine:function(e){return $o(e,(function(t){if(t.empty()){var n=Ge(e.doc,t.head.line).text.length;return t.head.ch==n&&t.head.line0)i=new et(i.line,i.ch+1),e.replaceRange(o.charAt(i.ch-1)+o.charAt(i.ch-2),et(i.line,i.ch-2),i,"+transpose");else if(i.line>e.doc.first){var a=Ge(e.doc,i.line-1).text;a&&(i=new et(i.line,1),e.replaceRange(o.charAt(0)+e.doc.lineSeparator()+a.charAt(a.length-1),et(i.line-1,a.length-1),i,"+transpose"))}n.push(new xi(i,i))}e.setSelections(n)}))},newlineAndIndent:function(e){return Jr(e,(function(){for(var t=e.listSelections(),n=t.length-1;n>=0;n--)e.replaceRange(e.doc.lineSeparator(),t[n].anchor,t[n].head,"+input");t=e.listSelections();for(var r=0;r-1&&(tt((i=l.ranges[i]).from(),t)<0||t.xRel>0)&&(tt(i.to(),t)>0||t.xRel<0)?function(e,t,n,r){var i=e.display,o=!1,l=ei(e,(function(t){s&&(i.scroller.draggable=!1),e.state.draggingText=!1,de(i.wrapper.ownerDocument,"mouseup",l),de(i.wrapper.ownerDocument,"mousemove",u),de(i.scroller,"dragstart",f),de(i.scroller,"drop",l),o||(ye(t),r.addNew||Yi(e.doc,n,null,null,r.extend),s&&!h||a&&9==c?setTimeout((function(){i.wrapper.ownerDocument.body.focus({preventScroll:!0}),i.input.focus()}),20):i.input.focus())})),u=function(e){o=o||Math.abs(t.clientX-e.clientX)+Math.abs(t.clientY-e.clientY)>=10},f=function(){return o=!0};s&&(i.scroller.draggable=!0),e.state.draggingText=l,l.copy=!r.moveOnDrag,i.scroller.dragDrop&&i.scroller.dragDrop(),fe(i.wrapper.ownerDocument,"mouseup",l),fe(i.wrapper.ownerDocument,"mousemove",u),fe(i.scroller,"dragstart",f),fe(i.scroller,"drop",l),xr(e),setTimeout((function(){return i.input.focus()}),20)}(e,r,t,o):function(e,t,n,r){var i=e.display,o=e.doc;ye(t);var a,c,s=o.sel,l=s.ranges;if(r.addNew&&!r.extend?(c=o.sel.contains(n),a=c>-1?l[c]:new xi(n,n)):(a=o.sel.primary(),c=o.sel.primIndex),"rectangle"==r.unit)r.addNew||(a=new xi(n,n)),n=lr(e,t,!0,!0),c=-1;else{var u=za(e,n,r.unit);a=r.extend?Gi(a,u.anchor,u.head,r.extend):u}r.addNew?-1==c?(c=l.length,Qi(o,ji(e,l.concat([a]),c),{scroll:!1,origin:"*mouse"})):l.length>1&&l[c].empty()&&"char"==r.unit&&!r.extend?(Qi(o,ji(e,l.slice(0,c).concat(l.slice(c+1)),0),{scroll:!1,origin:"*mouse"}),s=o.sel):$i(o,c,a,F):(c=0,Qi(o,new ki([a],0),F),s=o.sel);var f=n;function h(t){if(0!=tt(f,t))if(f=t,"rectangle"==r.unit){for(var i=[],l=e.options.tabSize,u=V(Ge(o,n.line).text,n.ch,l),h=V(Ge(o,t.line).text,t.ch,l),d=Math.min(u,h),p=Math.max(u,h),z=Math.min(n.line,t.line),v=Math.min(e.lastLine(),Math.max(n.line,t.line));z<=v;z++){var g=Ge(o,z).text,m=W(g,d,l);d==p?i.push(new xi(et(z,m),et(z,m))):g.length>m&&i.push(new xi(et(z,m),et(z,W(g,p,l))))}i.length||i.push(new xi(n,n)),Qi(o,ji(e,s.ranges.slice(0,c).concat(i),c),{origin:"*mouse",scroll:!1}),e.scrollIntoView(t)}else{var y,b=a,w=za(e,t,r.unit),k=b.anchor;tt(w.anchor,k)>0?(y=w.head,k=ot(b.from(),w.anchor)):(y=w.anchor,k=it(b.to(),w.head));var x=s.ranges.slice(0);x[c]=function(e,t){var n=t.anchor,r=t.head,i=Ge(e.doc,n.line);if(0==tt(n,r)&&n.sticky==r.sticky)return t;var o=le(i);if(!o)return t;var a=ce(o,n.ch,n.sticky),c=o[a];if(c.from!=n.ch&&c.to!=n.ch)return t;var s,l=a+(c.from==n.ch==(1!=c.level)?0:1);if(0==l||l==o.length)return t;if(r.line!=n.line)s=(r.line-n.line)*("ltr"==e.doc.direction?1:-1)>0;else{var u=ce(o,r.ch,r.sticky),f=u-a||(r.ch-n.ch)*(1==c.level?-1:1);s=u==l-1||u==l?f<0:f>0}var h=o[l+(s?-1:0)],d=s==(1==h.level),p=d?h.from:h.to,z=d?"after":"before";return n.ch==p&&n.sticky==z?t:new xi(new et(n.line,p,z),r)}(e,new xi(ct(o,k),y)),Qi(o,ji(e,x,c),F)}}var d=i.wrapper.getBoundingClientRect(),p=0;function z(t){e.state.selectingText=!1,p=1/0,t&&(ye(t),i.input.focus()),de(i.wrapper.ownerDocument,"mousemove",v),de(i.wrapper.ownerDocument,"mouseup",g),o.history.lastSelOrigin=null}var v=ei(e,(function(t){0!==t.buttons&&je(t)?function t(n){var a=++p,c=lr(e,n,!0,"rectangle"==r.unit);if(c)if(0!=tt(c,f)){e.curOp.focus=E(),h(c);var s=qr(i,o);(c.line>=s.to||c.lined.bottom?20:0;l&&setTimeout(ei(e,(function(){p==a&&(i.scroller.scrollTop+=l,t(n))})),50)}}(t):z(t)})),g=ei(e,z);e.state.selectingText=g,fe(i.wrapper.ownerDocument,"mousemove",v),fe(i.wrapper.ownerDocument,"mouseup",g)}(e,r,t,o)}(t,r,o,e):xe(e)==n.scroller&&ye(e):2==i?(r&&Yi(t.doc,r),setTimeout((function(){return n.input.focus()}),20)):3==i&&(x?t.display.input.onContextMenu(e):xr(t)))}}function za(e,t,n){if("char"==n)return new xi(t,t);if("word"==n)return e.findWordAt(t);if("line"==n)return new xi(et(t.line,0),ct(e.doc,et(t.line+1,0)));var r=n(e,t);return new xi(r.from,r.to)}function va(e,t,n,r){var i,o;if(t.touches)i=t.touches[0].clientX,o=t.touches[0].clientY;else try{i=t.clientX,o=t.clientY}catch(e){return!1}if(i>=Math.floor(e.display.gutters.getBoundingClientRect().right))return!1;r&&ye(t);var a=e.display,c=a.lineDiv.getBoundingClientRect();if(o>c.bottom||!ge(e,n))return we(t);o-=c.top-a.viewOffset;for(var s=0;s=i)return pe(e,n,e,Ke(e.doc,o),e.display.gutterSpecs[s].className,t),we(t)}}function ga(e,t){return va(e,t,"gutterClick",!0)}function ma(e,t){kn(e.display,t)||function(e,t){return!!ge(e,"gutterContextMenu")&&va(e,t,"gutterContextMenu",!1)}(e,t)||ze(e,t,"contextmenu")||x||e.display.input.onContextMenu(t)}function ya(e){e.display.wrapper.className=e.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+e.options.theme.replace(/(^|\s)\s*/g," cm-s-"),In(e)}da.prototype.compare=function(e,t,n){return this.time+400>e&&0==tt(t,this.pos)&&n==this.button};var ba={toString:function(){return"CodeMirror.Init"}},wa={},ka={};function xa(e,t,n){if(!t!=!(n&&n!=ba)){var r=e.display.dragFunctions,i=t?fe:de;i(e.display.scroller,"dragstart",r.start),i(e.display.scroller,"dragenter",r.enter),i(e.display.scroller,"dragover",r.over),i(e.display.scroller,"dragleave",r.leave),i(e.display.scroller,"drop",r.drop)}}function ja(e){e.options.lineWrapping?(A(e.display.wrapper,"CodeMirror-wrap"),e.display.sizer.style.minWidth="",e.display.sizerWidth=null):(_(e.display.wrapper,"CodeMirror-wrap"),Wt(e)),sr(e),fr(e),In(e),setTimeout((function(){return Rr(e)}),100)}function Ma(e,t){var n=this;if(!(this instanceof Ma))return new Ma(e,t);this.options=t=t?P(t):{},P(wa,t,!1);var r=t.value;"string"==typeof r?r=new Oo(r,t.mode,null,t.lineSeparator,t.direction):t.mode&&(r.modeOption=t.mode),this.doc=r;var i=new Ma.inputStyles[t.inputStyle](this),o=this.display=new vi(e,r,i,t);for(var l in o.wrapper.CodeMirror=this,ya(this),t.lineWrapping&&(this.display.wrapper.className+=" CodeMirror-wrap"),Ur(this),this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,delayingBlurEvent:!1,focused:!1,suppressEdits:!1,pasteIncoming:-1,cutIncoming:-1,selectingText:!1,draggingText:!1,highlight:new N,keySeq:null,specialChars:null},t.autofocus&&!g&&o.input.focus(),a&&c<11&&setTimeout((function(){return n.display.input.reset(!0)}),20),function(e){var t=e.display;fe(t.scroller,"mousedown",ei(e,pa)),fe(t.scroller,"dblclick",a&&c<11?ei(e,(function(t){if(!ze(e,t)){var n=lr(e,t);if(n&&!ga(e,t)&&!kn(e.display,t)){ye(t);var r=e.findWordAt(n);Yi(e.doc,r.anchor,r.head)}}})):function(t){return ze(e,t)||ye(t)}),fe(t.scroller,"contextmenu",(function(t){return ma(e,t)})),fe(t.input.getField(),"contextmenu",(function(n){t.scroller.contains(n.target)||ma(e,n)}));var n,r={end:0};function i(){t.activeTouch&&(n=setTimeout((function(){return t.activeTouch=null}),1e3),(r=t.activeTouch).end=+new Date)}function o(e,t){if(null==t.left)return!0;var n=t.left-e.left,r=t.top-e.top;return n*n+r*r>400}fe(t.scroller,"touchstart",(function(i){if(!ze(e,i)&&!function(e){if(1!=e.touches.length)return!1;var t=e.touches[0];return t.radiusX<=1&&t.radiusY<=1}(i)&&!ga(e,i)){t.input.ensurePolled(),clearTimeout(n);var o=+new Date;t.activeTouch={start:o,moved:!1,prev:o-r.end<=300?r:null},1==i.touches.length&&(t.activeTouch.left=i.touches[0].pageX,t.activeTouch.top=i.touches[0].pageY)}})),fe(t.scroller,"touchmove",(function(){t.activeTouch&&(t.activeTouch.moved=!0)})),fe(t.scroller,"touchend",(function(n){var r=t.activeTouch;if(r&&!kn(t,n)&&null!=r.left&&!r.moved&&new Date-r.start<300){var a,c=e.coordsChar(t.activeTouch,"page");a=!r.prev||o(r,r.prev)?new xi(c,c):!r.prev.prev||o(r,r.prev.prev)?e.findWordAt(c):new xi(et(c.line,0),ct(e.doc,et(c.line+1,0))),e.setSelection(a.anchor,a.head),e.focus(),ye(n)}i()})),fe(t.scroller,"touchcancel",i),fe(t.scroller,"scroll",(function(){t.scroller.clientHeight&&(Lr(e,t.scroller.scrollTop),Pr(e,t.scroller.scrollLeft,!0),pe(e,"scroll",e))})),fe(t.scroller,"mousewheel",(function(t){return wi(e,t)})),fe(t.scroller,"DOMMouseScroll",(function(t){return wi(e,t)})),fe(t.wrapper,"scroll",(function(){return t.wrapper.scrollTop=t.wrapper.scrollLeft=0})),t.dragFunctions={enter:function(t){ze(e,t)||ke(t)},over:function(t){ze(e,t)||(function(e,t){var n=lr(e,t);if(n){var r=document.createDocumentFragment();mr(e,n,r),e.display.dragCursor||(e.display.dragCursor=S("div",null,"CodeMirror-cursors CodeMirror-dragcursors"),e.display.lineSpace.insertBefore(e.display.dragCursor,e.display.cursorDiv)),q(e.display.dragCursor,r)}}(e,t),ke(t))},start:function(t){return function(e,t){if(a&&(!e.state.draggingText||+new Date-To<100))ke(t);else if(!ze(e,t)&&!kn(e.display,t)&&(t.dataTransfer.setData("Text",e.getSelection()),t.dataTransfer.effectAllowed="copyMove",t.dataTransfer.setDragImage&&!h)){var n=S("img",null,null,"position: fixed; left: 0; top: 0;");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",f&&(n.width=n.height=1,e.display.wrapper.appendChild(n),n._top=n.offsetTop),t.dataTransfer.setDragImage(n,0,0),f&&n.parentNode.removeChild(n)}}(e,t)},drop:ei(e,Eo),leave:function(t){ze(e,t)||Ao(e)}};var s=t.input.getField();fe(s,"keyup",(function(t){return la.call(e,t)})),fe(s,"keydown",ei(e,sa)),fe(s,"keypress",ei(e,ua)),fe(s,"focus",(function(t){return jr(e,t)})),fe(s,"blur",(function(t){return Mr(e,t)}))}(this),function(){var e;Lo||(fe(window,"resize",(function(){null==e&&(e=setTimeout((function(){e=null,Ho(Do)}),100))})),fe(window,"blur",(function(){return Ho(Mr)})),Lo=!0)}(),Gr(this),this.curOp.forceUpdate=!0,Li(this,r),t.autofocus&&!g||this.hasFocus()?setTimeout(D(jr,this),20):Mr(this),ka)ka.hasOwnProperty(l)&&ka[l](this,t[l],ba);hi(this),t.finishInit&&t.finishInit(this);for(var u=0;u<_a.length;++u)_a[u](this);Yr(this),s&&t.lineWrapping&&"optimizelegibility"==getComputedStyle(o.lineDiv).textRendering&&(o.lineDiv.style.textRendering="auto")}Ma.defaults=wa,Ma.optionHandlers=ka;var _a=[];function Ca(e,t,n,r){var i,o=e.doc;null==n&&(n="add"),"smart"==n&&(o.mode.indent?i=dt(e,t).state:n="prev");var a=e.options.tabSize,c=Ge(o,t),s=V(c.text,null,a);c.stateAfter&&(c.stateAfter=null);var l,u=c.text.match(/^\s*/)[0];if(r||/\S/.test(c.text)){if("smart"==n&&((l=o.mode.indent(i,c.text.slice(u.length),c.text))==R||l>150)){if(!r)return;n="prev"}}else l=0,n="not";"prev"==n?l=t>o.first?V(Ge(o,t-1).text,null,a):0:"add"==n?l=s+e.options.indentUnit:"subtract"==n?l=s-e.options.indentUnit:"number"==typeof n&&(l=s+n),l=Math.max(0,l);var f="",h=0;if(e.options.indentWithTabs)for(var d=Math.floor(l/a);d;--d)h+=a,f+="\t";if(ha,s=Te(t),l=null;if(c&&r.ranges.length>1)if(qa&&qa.text.join("\n")==t){if(r.ranges.length%qa.text.length==0){l=[];for(var u=0;u=0;h--){var d=r.ranges[h],p=d.from(),z=d.to();d.empty()&&(n&&n>0?p=et(p.line,p.ch-n):e.state.overwrite&&!c?z=et(z.line,Math.min(Ge(o,z.line).text.length,z.ch+Z(s).length)):c&&qa&&qa.lineWise&&qa.text.join("\n")==s.join("\n")&&(p=z=et(p.line,0)));var v={from:p,to:z,text:l?l[h%l.length]:s,origin:i||(c?"paste":e.state.cutIncoming>a?"cut":"+input")};so(e.doc,v),sn(e,"inputRead",e,v)}t&&!c&&Ea(e,t),Tr(e),e.curOp.updateInput<2&&(e.curOp.updateInput=f),e.curOp.typing=!0,e.state.pasteIncoming=e.state.cutIncoming=-1}function Ta(e,t){var n=e.clipboardData&&e.clipboardData.getData("Text");if(n)return e.preventDefault(),t.isReadOnly()||t.options.disableInput||Jr(t,(function(){return Oa(t,n,0,null,"paste")})),!0}function Ea(e,t){if(e.options.electricChars&&e.options.smartIndent)for(var n=e.doc.sel,r=n.ranges.length-1;r>=0;r--){var i=n.ranges[r];if(!(i.head.ch>100||r&&n.ranges[r-1].head.line==i.head.line)){var o=e.getModeAt(i.head),a=!1;if(o.electricChars){for(var c=0;c-1){a=Ca(e,i.head.line,"smart");break}}else o.electricInput&&o.electricInput.test(Ge(e.doc,i.head.line).text.slice(0,i.head.ch))&&(a=Ca(e,i.head.line,"smart"));a&&sn(e,"electricInput",e,i.head.line)}}}function Aa(e){for(var t=[],n=[],r=0;r=t.text.length?(n.ch=t.text.length,n.sticky="before"):n.ch<=0&&(n.ch=0,n.sticky="after");var o=ce(i,n.ch,n.sticky),a=i[o];if("ltr"==e.doc.direction&&a.level%2==0&&(r>0?a.to>n.ch:a.from=a.from&&h>=u.begin)){var d=f?"before":"after";return new et(n.line,h,d)}}var p=function(e,t,r){for(var o=function(e,t){return t?new et(n.line,s(e,1),"before"):new et(n.line,e,"after")};e>=0&&e0==(1!=a.level),l=c?r.begin:s(r.end,-1);if(a.from<=l&&l0?u.end:s(u.begin,-1);return null==v||r>0&&v==t.text.length||!(z=p(r>0?0:i.length-1,r,l(v)))?null:z}(e.cm,c,t,n):Ko(c,t,n))){if(r||(a=t.line+s)=e.first+e.size||(t=new et(a,t.ch,t.sticky),!(c=Ge(e,a))))return!1;t=Qo(i,e.cm,c,t.line,s)}else t=o;return!0}if("char"==r)l();else if("column"==r)l(!0);else if("word"==r||"group"==r)for(var u=null,f="group"==r,h=e.cm&&e.cm.getHelper(t,"wordChars"),d=!0;!(n<0)||l(!d);d=!1){var p=c.text.charAt(t.ch)||"\n",z=ee(p,h)?"w":f&&"\n"==p?"n":!f||/\s/.test(p)?null:"p";if(!f||d||z||(z="s"),u&&u!=z){n<0&&(n=1,l(),t.sticky="after");break}if(z&&(u=z),n>0&&!l(!d))break}var v=io(e,t,o,a,!0);return nt(o,v)&&(v.hitSide=!0),v}function Pa(e,t,n,r){var i,o,a=e.doc,c=t.left;if("page"==r){var s=Math.min(e.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight),l=Math.max(s-.5*rr(e.display),3);i=(n>0?t.bottom:t.top)+n*l}else"line"==r&&(i=n>0?t.bottom+3:t.top-3);for(;(o=Xn(e,c,i)).outside;){if(n<0?i<=0:i>=a.height){o.hitSide=!0;break}i+=5*n}return o}var Va=function(e){this.cm=e,this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null,this.polling=new N,this.composing=null,this.gracePeriod=!1,this.readDOMTimeout=null};function Na(e,t){var n=Tn(e,t.line);if(!n||n.hidden)return null;var r=Ge(e.doc,t.line),i=Sn(n,r,t.line),o=le(r,e.doc.direction),a="left";o&&(a=ce(o,t.ch)%2?"right":"left");var c=Dn(i.map,t.ch,a);return c.offset="right"==c.collapse?c.end:c.start,c}function Ia(e,t){return t&&(e.bad=!0),e}function Ra(e,t,n){var r;if(t==e.display.lineDiv){if(!(r=e.display.lineDiv.childNodes[n]))return Ia(e.clipPos(et(e.display.viewTo-1)),!0);t=null,n=0}else for(r=t;;r=r.parentNode){if(!r||r==e.display.lineDiv)return null;if(r.parentNode&&r.parentNode==e.display.lineDiv)break}for(var i=0;i=t.display.viewTo||o.line=t.display.viewFrom&&Na(t,i)||{node:s[0].measure.map[2],offset:0},u=o.liner.firstLine()&&(a=et(a.line-1,Ge(r.doc,a.line-1).length)),c.ch==Ge(r.doc,c.line).text.length&&c.linei.viewTo-1)return!1;a.line==i.viewFrom||0==(e=ur(r,a.line))?(t=Xe(i.view[0].line),n=i.view[0].node):(t=Xe(i.view[e].line),n=i.view[e-1].node.nextSibling);var s,l,u=ur(r,c.line);if(u==i.view.length-1?(s=i.viewTo-1,l=i.lineDiv.lastChild):(s=Xe(i.view[u+1].line)-1,l=i.view[u+1].node.previousSibling),!n)return!1;for(var f=r.doc.splitLines(function(e,t,n,r,i){var o="",a=!1,c=e.doc.lineSeparator(),s=!1;function l(){a&&(o+=c,s&&(o+=c),a=s=!1)}function u(e){e&&(l(),o+=e)}function f(t){if(1==t.nodeType){var n=t.getAttribute("cm-text");if(n)return void u(n);var o,h=t.getAttribute("cm-marker");if(h){var d=e.findMarks(et(r,0),et(i+1,0),(v=+h,function(e){return e.id==v}));return void(d.length&&(o=d[0].find(0))&&u(Ye(e.doc,o.from,o.to).join(c)))}if("false"==t.getAttribute("contenteditable"))return;var p=/^(pre|div|p|li|table|br)$/i.test(t.nodeName);if(!/^br$/i.test(t.nodeName)&&0==t.textContent.length)return;p&&l();for(var z=0;z1&&h.length>1;)if(Z(f)==Z(h))f.pop(),h.pop(),s--;else{if(f[0]!=h[0])break;f.shift(),h.shift(),t++}for(var d=0,p=0,z=f[0],v=h[0],g=Math.min(z.length,v.length);da.ch&&m.charCodeAt(m.length-p-1)==y.charCodeAt(y.length-p-1);)d--,p++;f[f.length-1]=m.slice(0,m.length-p).replace(/^\u200b+/,""),f[0]=f[0].slice(d).replace(/\u200b+$/,"");var w=et(t,d),k=et(s,h.length?Z(h).length-p:0);return f.length>1||f[0]||tt(w,k)?(po(r.doc,f,w,k,"+input"),!0):void 0},Va.prototype.ensurePolled=function(){this.forceCompositionEnd()},Va.prototype.reset=function(){this.forceCompositionEnd()},Va.prototype.forceCompositionEnd=function(){this.composing&&(clearTimeout(this.readDOMTimeout),this.composing=null,this.updateFromDOM(),this.div.blur(),this.div.focus())},Va.prototype.readFromDOMSoon=function(){var e=this;null==this.readDOMTimeout&&(this.readDOMTimeout=setTimeout((function(){if(e.readDOMTimeout=null,e.composing){if(!e.composing.done)return;e.composing=null}e.updateFromDOM()}),80))},Va.prototype.updateFromDOM=function(){var e=this;!this.cm.isReadOnly()&&this.pollContent()||Jr(this.cm,(function(){return fr(e.cm)}))},Va.prototype.setUneditable=function(e){e.contentEditable="false"},Va.prototype.onKeyPress=function(e){0==e.charCode||this.composing||(e.preventDefault(),this.cm.isReadOnly()||ei(this.cm,Oa)(this.cm,String.fromCharCode(null==e.charCode?e.keyCode:e.charCode),0))},Va.prototype.readOnlyChanged=function(e){this.div.contentEditable=String("nocursor"!=e)},Va.prototype.onContextMenu=function(){},Va.prototype.resetPosition=function(){},Va.prototype.needsContentAttribute=!0;var Fa=function(e){this.cm=e,this.prevInput="",this.pollingFast=!1,this.polling=new N,this.hasSelection=!1,this.composing=null};Fa.prototype.init=function(e){var t=this,n=this,r=this.cm;this.createField(e);var i=this.textarea;function o(e){if(!ze(r,e)){if(r.somethingSelected())Sa({lineWise:!1,text:r.getSelections()});else{if(!r.options.lineWiseCopyCut)return;var t=Aa(r);Sa({lineWise:!0,text:t.text}),"cut"==e.type?r.setSelections(t.ranges,null,B):(n.prevInput="",i.value=t.text.join("\n"),L(i))}"cut"==e.type&&(r.state.cutIncoming=+new Date)}}e.wrapper.insertBefore(this.wrapper,e.wrapper.firstChild),z&&(i.style.width="0px"),fe(i,"input",(function(){a&&c>=9&&t.hasSelection&&(t.hasSelection=null),n.poll()})),fe(i,"paste",(function(e){ze(r,e)||Ta(e,r)||(r.state.pasteIncoming=+new Date,n.fastPoll())})),fe(i,"cut",o),fe(i,"copy",o),fe(e.scroller,"paste",(function(t){if(!kn(e,t)&&!ze(r,t)){if(!i.dispatchEvent)return r.state.pasteIncoming=+new Date,void n.focus();var o=new Event("paste");o.clipboardData=t.clipboardData,i.dispatchEvent(o)}})),fe(e.lineSpace,"selectstart",(function(t){kn(e,t)||ye(t)})),fe(i,"compositionstart",(function(){var e=r.getCursor("from");n.composing&&n.composing.range.clear(),n.composing={start:e,range:r.markText(e,r.getCursor("to"),{className:"CodeMirror-composing"})}})),fe(i,"compositionend",(function(){n.composing&&(n.poll(),n.composing.range.clear(),n.composing=null)}))},Fa.prototype.createField=function(e){this.wrapper=La(),this.textarea=this.wrapper.firstChild},Fa.prototype.screenReaderLabelChanged=function(e){e?this.textarea.setAttribute("aria-label",e):this.textarea.removeAttribute("aria-label")},Fa.prototype.prepareSelection=function(){var e=this.cm,t=e.display,n=e.doc,r=gr(e);if(e.options.moveInputWithCursor){var i=Yn(e,n.sel.primary().head,"div"),o=t.wrapper.getBoundingClientRect(),a=t.lineDiv.getBoundingClientRect();r.teTop=Math.max(0,Math.min(t.wrapper.clientHeight-10,i.top+a.top-o.top)),r.teLeft=Math.max(0,Math.min(t.wrapper.clientWidth-10,i.left+a.left-o.left))}return r},Fa.prototype.showSelection=function(e){var t=this.cm.display;q(t.cursorDiv,e.cursors),q(t.selectionDiv,e.selection),null!=e.teTop&&(this.wrapper.style.top=e.teTop+"px",this.wrapper.style.left=e.teLeft+"px")},Fa.prototype.reset=function(e){if(!this.contextMenuPending&&!this.composing){var t=this.cm;if(t.somethingSelected()){this.prevInput="";var n=t.getSelection();this.textarea.value=n,t.state.focused&&L(this.textarea),a&&c>=9&&(this.hasSelection=n)}else e||(this.prevInput=this.textarea.value="",a&&c>=9&&(this.hasSelection=null))}},Fa.prototype.getField=function(){return this.textarea},Fa.prototype.supportsTouch=function(){return!1},Fa.prototype.focus=function(){if("nocursor"!=this.cm.options.readOnly&&(!g||E()!=this.textarea))try{this.textarea.focus()}catch(e){}},Fa.prototype.blur=function(){this.textarea.blur()},Fa.prototype.resetPosition=function(){this.wrapper.style.top=this.wrapper.style.left=0},Fa.prototype.receivedFocus=function(){this.slowPoll()},Fa.prototype.slowPoll=function(){var e=this;this.pollingFast||this.polling.set(this.cm.options.pollInterval,(function(){e.poll(),e.cm.state.focused&&e.slowPoll()}))},Fa.prototype.fastPoll=function(){var e=!1,t=this;t.pollingFast=!0,t.polling.set(20,(function n(){t.poll()||e?(t.pollingFast=!1,t.slowPoll()):(e=!0,t.polling.set(60,n))}))},Fa.prototype.poll=function(){var e=this,t=this.cm,n=this.textarea,r=this.prevInput;if(this.contextMenuPending||!t.state.focused||Ee(n)&&!r&&!this.composing||t.isReadOnly()||t.options.disableInput||t.state.keySeq)return!1;var i=n.value;if(i==r&&!t.somethingSelected())return!1;if(a&&c>=9&&this.hasSelection===i||m&&/[\uf700-\uf7ff]/.test(i))return t.display.input.reset(),!1;if(t.doc.sel==t.display.selForContextMenu){var o=i.charCodeAt(0);if(8203!=o||r||(r="\u200b"),8666==o)return this.reset(),this.cm.execCommand("undo")}for(var s=0,l=Math.min(r.length,i.length);s1e3||i.indexOf("\n")>-1?n.value=e.prevInput="":e.prevInput=i,e.composing&&(e.composing.range.clear(),e.composing.range=t.markText(e.composing.start,t.getCursor("to"),{className:"CodeMirror-composing"}))})),!0},Fa.prototype.ensurePolled=function(){this.pollingFast&&this.poll()&&(this.pollingFast=!1)},Fa.prototype.onKeyPress=function(){a&&c>=9&&(this.hasSelection=null),this.fastPoll()},Fa.prototype.onContextMenu=function(e){var t=this,n=t.cm,r=n.display,i=t.textarea;t.contextMenuPending&&t.contextMenuPending();var o=lr(n,e),l=r.scroller.scrollTop;if(o&&!f){n.options.resetSelectionOnContextMenu&&-1==n.doc.sel.contains(o)&&ei(n,Qi)(n.doc,Mi(o),B);var u,h=i.style.cssText,d=t.wrapper.style.cssText,p=t.wrapper.offsetParent.getBoundingClientRect();t.wrapper.style.cssText="position: static",i.style.cssText="position: absolute; width: 30px; height: 30px;\n top: "+(e.clientY-p.top-5)+"px; left: "+(e.clientX-p.left-5)+"px;\n z-index: 1000; background: "+(a?"rgba(255, 255, 255, .05)":"transparent")+";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);",s&&(u=window.scrollY),r.input.focus(),s&&window.scrollTo(null,u),r.input.reset(),n.somethingSelected()||(i.value=t.prevInput=" "),t.contextMenuPending=v,r.selForContextMenu=n.doc.sel,clearTimeout(r.detectingSelectAll),a&&c>=9&&z(),x?(ke(e),fe(window,"mouseup",(function e(){de(window,"mouseup",e),setTimeout(v,20)}))):setTimeout(v,50)}function z(){if(null!=i.selectionStart){var e=n.somethingSelected(),o="\u200b"+(e?i.value:"");i.value="\u21da",i.value=o,t.prevInput=e?"":"\u200b",i.selectionStart=1,i.selectionEnd=o.length,r.selForContextMenu=n.doc.sel}}function v(){if(t.contextMenuPending==v&&(t.contextMenuPending=!1,t.wrapper.style.cssText=d,i.style.cssText=h,a&&c<9&&r.scrollbars.setScrollTop(r.scroller.scrollTop=l),null!=i.selectionStart)){(!a||a&&c<9)&&z();var e=0;r.detectingSelectAll=setTimeout((function o(){r.selForContextMenu==n.doc.sel&&0==i.selectionStart&&i.selectionEnd>0&&"\u200b"==t.prevInput?ei(n,ao)(n):e++<10?r.detectingSelectAll=setTimeout(o,500):(r.selForContextMenu=null,r.input.reset())}),200)}}},Fa.prototype.readOnlyChanged=function(e){e||this.reset(),this.textarea.disabled="nocursor"==e},Fa.prototype.setUneditable=function(){},Fa.prototype.needsContentAttribute=!1,function(e){var t=e.optionHandlers;function n(n,r,i,o){e.defaults[n]=r,i&&(t[n]=o?function(e,t,n){n!=ba&&i(e,t,n)}:i)}e.defineOption=n,e.Init=ba,n("value","",(function(e,t){return e.setValue(t)}),!0),n("mode",null,(function(e,t){e.doc.modeOption=t,Oi(e)}),!0),n("indentUnit",2,Oi,!0),n("indentWithTabs",!1),n("smartIndent",!0),n("tabSize",4,(function(e){Ti(e),In(e),fr(e)}),!0),n("lineSeparator",null,(function(e,t){if(e.doc.lineSep=t,t){var n=[],r=e.doc.first;e.doc.iter((function(e){for(var i=0;;){var o=e.text.indexOf(t,i);if(-1==o)break;i=o+t.length,n.push(et(r,o))}r++}));for(var i=n.length-1;i>=0;i--)po(e.doc,t,n[i],et(n[i].line,n[i].ch+t.length))}})),n("specialChars",/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g,(function(e,t,n){e.state.specialChars=new RegExp(t.source+(t.test("\t")?"":"|\t"),"g"),n!=ba&&e.refresh()})),n("specialCharPlaceholder",Qt,(function(e){return e.refresh()}),!0),n("electricChars",!0),n("inputStyle",g?"contenteditable":"textarea",(function(){throw new Error("inputStyle can not (yet) be changed in a running editor")}),!0),n("spellcheck",!1,(function(e,t){return e.getInputField().spellcheck=t}),!0),n("autocorrect",!1,(function(e,t){return e.getInputField().autocorrect=t}),!0),n("autocapitalize",!1,(function(e,t){return e.getInputField().autocapitalize=t}),!0),n("rtlMoveVisually",!b),n("wholeLineUpdateBefore",!0),n("theme","default",(function(e){ya(e),zi(e)}),!0),n("keyMap","default",(function(e,t,n){var r=Zo(t),i=n!=ba&&Zo(n);i&&i.detach&&i.detach(e,r),r.attach&&r.attach(e,i||null)})),n("extraKeys",null),n("configureMouse",null),n("lineWrapping",!1,ja,!0),n("gutters",[],(function(e,t){e.display.gutterSpecs=di(t,e.options.lineNumbers),zi(e)}),!0),n("fixedGutter",!0,(function(e,t){e.display.gutters.style.left=t?ar(e.display)+"px":"0",e.refresh()}),!0),n("coverGutterNextToScrollbar",!1,(function(e){return Rr(e)}),!0),n("scrollbarStyle","native",(function(e){Ur(e),Rr(e),e.display.scrollbars.setScrollTop(e.doc.scrollTop),e.display.scrollbars.setScrollLeft(e.doc.scrollLeft)}),!0),n("lineNumbers",!1,(function(e,t){e.display.gutterSpecs=di(e.options.gutters,t),zi(e)}),!0),n("firstLineNumber",1,zi,!0),n("lineNumberFormatter",(function(e){return e}),zi,!0),n("showCursorWhenSelecting",!1,vr,!0),n("resetSelectionOnContextMenu",!0),n("lineWiseCopyCut",!0),n("pasteLinesPerSelection",!0),n("selectionsMayTouch",!1),n("readOnly",!1,(function(e,t){"nocursor"==t&&(Mr(e),e.display.input.blur()),e.display.input.readOnlyChanged(t)})),n("screenReaderLabel",null,(function(e,t){t=""===t?null:t,e.display.input.screenReaderLabelChanged(t)})),n("disableInput",!1,(function(e,t){t||e.display.input.reset()}),!0),n("dragDrop",!0,xa),n("allowDropFileTypes",null),n("cursorBlinkRate",530),n("cursorScrollMargin",0),n("cursorHeight",1,vr,!0),n("singleCursorHeightPerLine",!0,vr,!0),n("workTime",100),n("workDelay",100),n("flattenSpans",!0,Ti,!0),n("addModeClass",!1,Ti,!0),n("pollInterval",100),n("undoDepth",200,(function(e,t){return e.doc.history.undoDepth=t})),n("historyEventDelay",1250),n("viewportMargin",10,(function(e){return e.refresh()}),!0),n("maxHighlightLength",1e4,Ti,!0),n("moveInputWithCursor",!0,(function(e,t){t||e.display.input.resetPosition()})),n("tabindex",null,(function(e,t){return e.display.input.getField().tabIndex=t||""})),n("autofocus",null),n("direction","ltr",(function(e,t){return e.doc.setDirection(t)}),!0),n("phrases",null)}(Ma),function(e){var t=e.optionHandlers,n=e.helpers={};e.prototype={constructor:e,focus:function(){window.focus(),this.display.input.focus()},setOption:function(e,n){var r=this.options,i=r[e];r[e]==n&&"mode"!=e||(r[e]=n,t.hasOwnProperty(e)&&ei(this,t[e])(this,n,i),pe(this,"optionChange",this,e))},getOption:function(e){return this.options[e]},getDoc:function(){return this.doc},addKeyMap:function(e,t){this.state.keyMaps[t?"push":"unshift"](Zo(e))},removeKeyMap:function(e){for(var t=this.state.keyMaps,n=0;nn&&(Ca(this,i.head.line,e,!0),n=i.head.line,r==this.doc.sel.primIndex&&Tr(this));else{var o=i.from(),a=i.to(),c=Math.max(n,o.line);n=Math.min(this.lastLine(),a.line-(a.ch?0:1))+1;for(var s=c;s0&&$i(this.doc,r,new xi(o,l[r].to()),B)}}})),getTokenAt:function(e,t){return mt(this,e,t)},getLineTokens:function(e,t){return mt(this,et(e),t,!0)},getTokenTypeAt:function(e){e=ct(this.doc,e);var t,n=ht(this,Ge(this.doc,e.line)),r=0,i=(n.length-1)/2,o=e.ch;if(0==o)t=n[2];else for(;;){var a=r+i>>1;if((a?n[2*a-1]:0)>=o)i=a;else{if(!(n[2*a+1]o&&(e=o,i=!0),r=Ge(this.doc,e)}else r=e;return Un(this,r,{top:0,left:0},t||"page",n||i).top+(i?this.doc.height-Ft(r):0)},defaultTextHeight:function(){return rr(this.display)},defaultCharWidth:function(){return ir(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(e,t,n,r,i){var o,a,c=this.display,s=(e=Yn(this,ct(this.doc,e))).bottom,l=e.left;if(t.style.position="absolute",t.setAttribute("cm-ignore-events","true"),this.display.input.setUneditable(t),c.sizer.appendChild(t),"over"==r)s=e.top;else if("above"==r||"near"==r){var u=Math.max(c.wrapper.clientHeight,this.doc.height),f=Math.max(c.sizer.clientWidth,c.lineSpace.clientWidth);("above"==r||e.bottom+t.offsetHeight>u)&&e.top>t.offsetHeight?s=e.top-t.offsetHeight:e.bottom+t.offsetHeight<=u&&(s=e.bottom),l+t.offsetWidth>f&&(l=f-t.offsetWidth)}t.style.top=s+"px",t.style.left=t.style.right="","right"==i?(l=c.sizer.clientWidth-t.offsetWidth,t.style.right="0px"):("left"==i?l=0:"middle"==i&&(l=(c.sizer.clientWidth-t.offsetWidth)/2),t.style.left=l+"px"),n&&(null!=(a=Sr(o=this,{left:l,top:s,right:l+t.offsetWidth,bottom:s+t.offsetHeight})).scrollTop&&Lr(o,a.scrollTop),null!=a.scrollLeft&&Pr(o,a.scrollLeft))},triggerOnKeyDown:ti(sa),triggerOnKeyPress:ti(ua),triggerOnKeyUp:la,triggerOnMouseDown:ti(pa),execCommand:function(e){if(Jo.hasOwnProperty(e))return Jo[e].call(null,this)},triggerElectric:ti((function(e){Ea(this,e)})),findPosH:function(e,t,n,r){var i=1;t<0&&(i=-1,t=-t);for(var o=ct(this.doc,e),a=0;a0&&a(t.charAt(n-1));)--n;for(;r.5||this.options.lineWrapping)&&sr(this),pe(this,"refresh",this)})),swapDoc:ti((function(e){var t=this.doc;return t.cm=null,this.state.selectingText&&this.state.selectingText(),Li(this,e),In(this),this.display.input.reset(),Er(this,e.scrollLeft,e.scrollTop),this.curOp.forceScroll=!0,sn(this,"swapDoc",this,t),t})),phrase:function(e){var t=this.options.phrases;return t&&Object.prototype.hasOwnProperty.call(t,e)?t[e]:e},getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}},me(e),e.registerHelper=function(t,r,i){n.hasOwnProperty(t)||(n[t]=e[t]={_global:[]}),n[t][r]=i},e.registerGlobalHelper=function(t,r,i,o){e.registerHelper(t,r,o),n[t]._global.push({pred:i,val:o})}}(Ma);var Ua="iter insert remove copy getEditor constructor".split(" ");for(var Wa in Oo.prototype)Oo.prototype.hasOwnProperty(Wa)&&I(Ua,Wa)<0&&(Ma.prototype[Wa]=function(e){return function(){return e.apply(this.doc,arguments)}}(Oo.prototype[Wa]));return me(Oo),Ma.inputStyles={textarea:Fa,contenteditable:Va},Ma.defineMode=function(e){Ma.defaults.mode||"null"==e||(Ma.defaults.mode=e),Pe.apply(this,arguments)},Ma.defineMIME=function(e,t){De[e]=t},Ma.defineMode("null",(function(){return{token:function(e){return e.skipToEnd()}}})),Ma.defineMIME("text/plain","null"),Ma.defineExtension=function(e,t){Ma.prototype[e]=t},Ma.defineDocExtension=function(e,t){Oo.prototype[e]=t},Ma.fromTextArea=function(e,t){if((t=t?P(t):{}).value=e.value,!t.tabindex&&e.tabIndex&&(t.tabindex=e.tabIndex),!t.placeholder&&e.placeholder&&(t.placeholder=e.placeholder),null==t.autofocus){var n=E();t.autofocus=n==e||null!=e.getAttribute("autofocus")&&n==document.body}function r(){e.value=c.getValue()}var i;if(e.form&&(fe(e.form,"submit",r),!t.leaveSubmitMethodAlone)){var o=e.form;i=o.submit;try{var a=o.submit=function(){r(),o.submit=i,o.submit(),o.submit=a}}catch(e){}}t.finishInit=function(n){n.save=r,n.getTextArea=function(){return e},n.toTextArea=function(){n.toTextArea=isNaN,r(),e.parentNode.removeChild(n.getWrapperElement()),e.style.display="",e.form&&(de(e.form,"submit",r),t.leaveSubmitMethodAlone||"function"!=typeof e.form.submit||(e.form.submit=i))}},e.style.display="none";var c=Ma((function(t){return e.parentNode.insertBefore(t,e.nextSibling)}),t);return c},function(e){e.off=de,e.on=fe,e.wheelEventPixels=bi,e.Doc=Oo,e.splitLines=Te,e.countColumn=V,e.findColumn=W,e.isWordChar=J,e.Pass=R,e.signal=pe,e.Line=Gt,e.changeEnd=_i,e.scrollbarModel=Fr,e.Pos=et,e.cmpPos=tt,e.modes=Le,e.mimeModes=De,e.resolveMode=Ve,e.getMode=Ne,e.modeExtensions=Ie,e.extendMode=Re,e.copyState=Be,e.startState=Ue,e.innerMode=Fe,e.commands=Jo,e.keyMap=Ro,e.keyName=Yo,e.isModifierKey=Wo,e.lookupKey=Uo,e.normalizeKeyMap=Fo,e.StringStream=We,e.SharedTextMarker=_o,e.TextMarker=jo,e.LineWidget=wo,e.e_preventDefault=ye,e.e_stopPropagation=be,e.e_stop=ke,e.addClass=A,e.contains=T,e.rmClass=_,e.keyNames=Po}(Ma),Ma.version="5.57.0",Ma}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),i=function(){function e(){}return e.prototype.isSeparator=function(e){return" "==e||"\r"==e||"\n"==e||"\t"==e||"("==e||")"==e},e.prototype.isWhiteSpace=function(e){return" "==e||"\r"==e||"\n"==e||"\t"==e},e.prototype.findLastSeparatorIndex=function(e){var t=this;return r.findLastIndex(e,(function(e){return t.isSeparator(e)}))},e.prototype.needSpaceAfter=function(e){return!("("==e)},e.prototype.isLastCharacterWhiteSpace=function(e){return!!e&&this.isWhiteSpace(e[e.length-1])},e.prototype.stripEndWithNonSeparatorCharacters=function(e){if(!e)return e;if(this.isSeparator(e[e.length-1]))return e;var t=this.findLastSeparatorIndex(e);return t<0?"":e.substr(0,t+1)},e.prototype.getEndNotSeparatorCharacers=function(e){if(!e)return e;if(this.isSeparator(e[e.length-1]))return"";var t=this.findLastSeparatorIndex(e);return t<0?e:e.substr(t+1)},e}();t.default=new i},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),i=function(){function e(){}return e.prototype.quote=function(e){return/\s/g.test(e)?'"'+e+'"':e},e.prototype.buildDefaultObjOrGetOriginal=function(e,t){return r.isString(e)?{value:this.quote(e),type:t}:{value:e,type:t}},e.prototype.handleParseError=function(e,t,n){var i=this,o=t;return r.flatMap(n.expected,(function(e){var t=[];if("literal"==e.type&&(t=r.map([e.text||e.value],(function(e){return{value:e,type:"literal"}}))),"other"==e.type){var n=o.getLastTokenType()||"value";"value"==n&&(t=r.map(i.needCategories(),(function(e){return i.buildDefaultObjOrGetOriginal(e,"category")}))),"category"==n&&(t=r.map(i.needOperators(o.getLastCategory()),(function(e){return i.buildDefaultObjOrGetOriginal(e,"operator")}))),"operator"==n&&(t=r.map(i.needValues(o.getLastCategory(),o.getLastOperator()),(function(e){return i.buildDefaultObjOrGetOriginal(e,"value")})))}return t}))},e.prototype.hasCategory=function(e){return!1},e.prototype.hasOperator=function(e,t){return!1},e.prototype.needCategories=function(){return[]},e.prototype.needOperators=function(e){return[]},e.prototype.needValues=function(e,t){return[]},e}();t.default=i},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=function(e,t){var n,r=e[1]||"",i=e[3];if(!i)return r;if(t&&"function"==typeof btoa){var o=(n=i,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(n))))+" */"),a=i.sources.map((function(e){return"/*# sourceURL="+i.sourceRoot+e+" */"}));return[r].concat(a).concat([o]).join("\n")}return[r].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n})).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},i=0;i=0&&f.splice(t,1)}function g(e){var t=document.createElement("style");if(void 0===e.attrs.type&&(e.attrs.type="text/css"),void 0===e.attrs.nonce){var r=n.nc;r&&(e.attrs.nonce=r)}return m(t,e.attrs),z(e,t),t}function m(e,t){Object.keys(t).forEach((function(n){e.setAttribute(n,t[n])}))}function y(e,t){var n,r,i,o;if(t.transform&&e.css){if(!(o="function"==typeof t.transform?t.transform(e.css):t.transform.default(e.css)))return function(){};e.css=o}if(t.singleton){var a=u++;n=l||(l=g(t)),r=k.bind(null,n,a,!1),i=k.bind(null,n,a,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",m(t,e.attrs),z(e,t),t}(t),r=j.bind(null,n,t),i=function(){v(n),n.href&&URL.revokeObjectURL(n.href)}):(n=g(t),r=x.bind(null,n),i=function(){v(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else i()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=a()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=p(e,t);return d(n,t),function(e){for(var r=[],i=0;i1)){if(this.somethingSelected()){if(!n.hint.supportsSelection)return;for(var i=0;il.clientHeight+1,T=a.getScrollInfo();if(S>0){var E=q.bottom-q.top;if(g.top-(g.bottom-q.top)-E>0)l.style.top=(y=g.top-E-k)+"px",b=!1;else if(E>C){l.style.height=C-5+"px",l.style.top=(y=g.bottom-q.top-k)+"px";var A=a.getCursor();n.from.ch!=A.ch&&(g=a.cursorCoords(A),l.style.left=(m=g.left-w)+"px",q=l.getBoundingClientRect())}}var H,L=q.right-_;if(L>0&&(q.right-q.left>_&&(l.style.width=_-5+"px",L-=q.right-q.left-_),l.style.left=(m=g.left-L-w)+"px"),O)for(var D=l.firstChild;D;D=D.nextSibling)D.style.paddingRight=a.display.nativeBarWidth+"px";return a.addKeyMap(this.keyMap=function(e,t){var n={Up:function(){t.moveFocus(-1)},Down:function(){t.moveFocus(1)},PageUp:function(){t.moveFocus(1-t.menuSize(),!0)},PageDown:function(){t.moveFocus(t.menuSize()-1,!0)},Home:function(){t.setFocus(0)},End:function(){t.setFocus(t.length-1)},Enter:t.pick,Tab:t.pick,Esc:t.close};/Mac/.test(navigator.platform)&&(n["Ctrl-P"]=function(){t.moveFocus(-1)},n["Ctrl-N"]=function(){t.moveFocus(1)});var r=e.options.customKeys,i=r?{}:n;function o(e,r){var o;o="string"!=typeof r?function(e){return r(e,t)}:n.hasOwnProperty(r)?n[r]:r,i[e]=o}if(r)for(var a in r)r.hasOwnProperty(a)&&o(a,r[a]);var c=e.options.extraKeys;if(c)for(var a in c)c.hasOwnProperty(a)&&o(a,c[a]);return i}(t,{moveFocus:function(e,t){r.changeActive(r.selectedHint+e,t)},setFocus:function(e){r.changeActive(e)},menuSize:function(){return r.screenAmount()},length:f.length,close:function(){t.close()},pick:function(){r.pick()},data:n})),t.options.closeOnUnfocus&&(a.on("blur",this.onBlur=function(){H=setTimeout((function(){t.close()}),100)}),a.on("focus",this.onFocus=function(){clearTimeout(H)})),a.on("scroll",this.onScroll=function(){var e=a.getScrollInfo(),n=a.getWrapperElement().getBoundingClientRect(),r=y+T.top-e.top,i=r-(s.pageYOffset||(c.documentElement||c.body).scrollTop);if(b||(i+=l.offsetHeight),i<=n.top||i>=n.bottom)return t.close();l.style.top=r+"px",l.style.left=m+T.left-e.left+"px"}),e.on(l,"dblclick",(function(e){var t=o(l,e.target||e.srcElement);t&&null!=t.hintId&&(r.changeActive(t.hintId),r.pick())})),e.on(l,"click",(function(e){var n=o(l,e.target||e.srcElement);n&&null!=n.hintId&&(r.changeActive(n.hintId),t.options.completeOnSingleClick&&r.pick())})),e.on(l,"mousedown",(function(){setTimeout((function(){a.focus()}),20)})),this.scrollToActive(),e.signal(n,"select",f[this.selectedHint],l.childNodes[this.selectedHint]),!0}function c(e,t,n,r){if(e.async)e(t,r,n);else{var i=e(t,n);i&&i.then?i.then(r):r(i)}}t.prototype={close:function(){this.active()&&(this.cm.state.completionActive=null,this.tick=null,this.cm.off("cursorActivity",this.activityFunc),this.widget&&this.data&&e.signal(this.data,"close"),this.widget&&this.widget.close(),e.signal(this.cm,"endCompletion",this.cm))},active:function(){return this.cm.state.completionActive==this},pick:function(t,n){var r=t.list[n],o=this;this.cm.operation((function(){r.hint?r.hint(o.cm,t,r):o.cm.replaceRange(i(r),r.from||t.from,r.to||t.to,"complete"),e.signal(t,"pick",r),o.cm.scrollIntoView()})),this.close()},cursorActivity:function(){this.debounce&&(r(this.debounce),this.debounce=0);var e=this.startPos;this.data&&(e=this.data.from);var t=this.cm.getCursor(),i=this.cm.getLine(t.line);if(t.line!=this.startPos.line||i.length-t.ch!=this.startLen-this.startPos.ch||t.ch=this.data.list.length?t=n?this.data.list.length-1:0:t<0&&(t=n?0:this.data.list.length-1),this.selectedHint!=t){var r=this.hints.childNodes[this.selectedHint];r&&(r.className=r.className.replace(" CodeMirror-hint-active","")),(r=this.hints.childNodes[this.selectedHint=t]).className+=" CodeMirror-hint-active",this.scrollToActive(),e.signal(this.data,"select",this.data.list[this.selectedHint],r)}},scrollToActive:function(){var e=this.completion.options.scrollMargin||0,t=this.hints.childNodes[Math.max(0,this.selectedHint-e)],n=this.hints.childNodes[Math.min(this.data.list.length-1,this.selectedHint+e)],r=this.hints.firstChild;t.offsetTopthis.hints.scrollTop+this.hints.clientHeight&&(this.hints.scrollTop=n.offsetTop+n.offsetHeight-this.hints.clientHeight+r.offsetTop)},screenAmount:function(){return Math.floor(this.hints.clientHeight/this.hints.firstChild.offsetHeight)||1}},e.registerHelper("hint","auto",{resolve:function(t,n){var r,i=t.getHelpers(n,"hint");if(i.length){var o=function(e,t,n){var r=function(e,t){if(!e.somethingSelected())return t;for(var n=[],r=0;r0?t(e):i(o+1)}))}(0)};return o.async=!0,o.supportsSelection=!0,o}return(r=t.getHelper(t.getCursor(),"hintWords"))?function(t){return e.hint.fromList(t,{words:r})}:e.hint.anyword?function(t,n){return e.hint.anyword(t,n)}:function(){}}}),e.registerHelper("hint","fromList",(function(t,n){var r,i=t.getCursor(),o=t.getTokenAt(i),a=e.Pos(i.line,o.start),c=i;o.start,]/,closeOnUnfocus:!0,completeOnSingleClick:!0,container:null,customKeys:null,extraKeys:null};e.defineOption("hintOptions",null)}(n(2))},function(e,t,n){!function(e){function t(e){e.state.placeholder&&(e.state.placeholder.parentNode.removeChild(e.state.placeholder),e.state.placeholder=null)}function n(e){t(e);var n=e.state.placeholder=document.createElement("pre");n.style.cssText="height: 0; overflow: visible",n.style.direction=e.getOption("direction"),n.className="CodeMirror-placeholder CodeMirror-line-like";var r=e.getOption("placeholder");"string"==typeof r&&(r=document.createTextNode(r)),n.appendChild(r),e.display.lineSpace.insertBefore(n,e.display.lineSpace.firstChild)}function r(e){o(e)&&n(e)}function i(e){var r=e.getWrapperElement(),i=o(e);r.className=r.className.replace(" CodeMirror-empty","")+(i?" CodeMirror-empty":""),i?n(e):t(e)}function o(e){return 1===e.lineCount()&&""===e.getLine(0)}e.defineOption("placeholder","",(function(n,o,a){var c=a&&a!=e.Init;if(o&&!c)n.on("blur",r),n.on("change",i),n.on("swapDoc",i),i(n);else if(!o&&c){n.off("blur",r),n.off("change",i),n.off("swapDoc",i),t(n);var s=n.getWrapperElement();s.className=s.className.replace(" CodeMirror-empty","")}o&&!n.hasFocus()&&r(n)}))}(n(2))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(2).defineMode("filter-mode",(function(e,t){function n(e){var t,n=(t=e.fieldState)==r.category?r.operator:t==r.operator?r.value:t==r.value?r.category:void 0,i=e.fieldState;return e.fieldState=n,i.toString()}return{startState:function(){return{inString:!1,fieldState:r.category}},token:function(e,t){return" "==(r=e.peek())||"\r"==r||"\n"==r||"\t"==r?(e.eatSpace(),null):"("==e.peek()||")"==e.peek()?(e.next(),"bracket"):e.match("AND",!0,!0)||e.match("OR",!0,!0)?"condition":(t.inString||'"'!=e.peek()||(e.next(),t.inString=!0),t.inString?(e.skipTo('"')?(e.next(),t.inString=!1):e.skipToEnd(),n(t)):(e.eatWhile(/[^\r\n\t\s\(\)]+/),n(t)));var r}}}));var r=function(){function e(){}return e.none="none",e.category="category",e.operator="operator",e.value="value",e}()},function(e,t,n){var r=n(16);"string"==typeof r&&(r=[[e.i,r,""]]),n(6)(r,{hmr:!0,transform:void 0,insertInto:void 0}),r.locals&&(e.exports=r.locals)},function(e,t,n){(e.exports=n(5)(!1)).push([e.i,".CodeMirror{font-family:monospace;height:300px;color:black;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:white}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:black}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid black;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0 !important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor-mark{background-color:rgba(20,255,20,0.5);-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:bold}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3,.cm-s-default .cm-type{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:#f00}.cm-invalidchar{color:#f00}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,0.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:white}.CodeMirror-scroll{overflow:scroll !important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:none;position:relative}.CodeMirror-sizer{position:relative;border-right:50px solid transparent}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none !important;border:none !important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:none}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,0.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:none}",""])},function(e,t){e.exports=function(e){var t="undefined"!=typeof window&&window.location;if(!t)throw new Error("fixUrls requires window.location");if(!e||"string"!=typeof e)return e;var n=t.protocol+"//"+t.host,r=n+t.pathname.replace(/\/[^\/]*$/,"/");return e.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi,(function(e,t){var i,o=t.trim().replace(/^"(.*)"$/,(function(e,t){return t})).replace(/^'(.*)'$/,(function(e,t){return t}));return/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test(o)?e:(i=0===o.indexOf("//")?o:0===o.indexOf("/")?n+o:r+o.replace(/^\.\//,""),"url("+JSON.stringify(i)+")")}))}},function(e,t,n){var r=n(19);"string"==typeof r&&(r=[[e.i,r,""]]),n(6)(r,{hmr:!0,transform:void 0,insertInto:void 0}),r.locals&&(e.exports=r.locals)},function(e,t,n){(e.exports=n(5)(!1)).push([e.i,".CodeMirror-hints{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,0.2);-moz-box-shadow:2px 3px 5px rgba(0,0,0,0.2);box-shadow:2px 3px 5px rgba(0,0,0,0.2);border-radius:3px;border:1px solid silver;background:white;font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:black;cursor:pointer}li.CodeMirror-hint-active{background:#08f;color:white}",""])},function(e,t,n){"use strict";(function(e){var r,i=Object.assign||function(e){for(var t=1;t=0;case"!contains":return e[i].toLowerCase().indexOf(r.toLowerCase())<0}return!1},t}(n(8).default);t.default=a},function(e,t,n){"use strict";var r,i=this&&this.__extends||(r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},function(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)});Object.defineProperty(t,"__esModule",{value:!0});var o=n(4),a=n(0),c=function(e){function t(t,n){var r=e.call(this)||this;return r.data=t,r.options=n,r.cache={},r.parseResult=null,r.categories=a.map(r.options,(function(e){return e.columnText?e.columnText:e.columnField})),r}return i(t,e),t.prototype.hasCategory=function(e){return void 0!==a.find(this.options,(function(t){return e===t.columnField||e===t.columnText}))},t.prototype.hasOperator=function(e,t){return this.needOperators(e).indexOf(t)>=0},t.prototype.needCategories=function(){return this.categories},t.prototype.needOperators=function(e){var t=a.find(this.options,(function(t){return null!=t.customOperatorFunc&&(t.columnText==e||t.columnField==e)}));return t?t.customOperatorFunc(e):["==","!=","contains","!contains"]},t.prototype.needValues=function(e,t){var n=a.find(this.options,(function(t){return t.columnField==e||t.columnText==e}));return null!=n&&"selection"==n.type&&null!=this.data?(this.cache[e]||(this.cache[e]=a.chain(this.data).map((function(t){return t[e]})).uniq().value()),this.cache[e]):null!=n&&n.customValuesFunc?n.customValuesFunc(e,t):[]},t}(o.default);t.default=c},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(28),i=n(0),o=n(4),a=n(29),c=n(3),s=function(){function e(){this.autoCompleteHandler=new o.default,this.lastError=null,this.parseTrace=new a.default}return e.prototype.parse=function(e){if(e=i.trim(e),i.isEmpty(e))return[];try{return this.parseQuery(e)}catch(e){return e.isError=!0,e}},e.prototype.parseQuery=function(e){return this.parseTrace.clear(),r.parse(e,{parseTrace:this.parseTrace})},e.prototype.getSuggestions=function(e){e=c.default.stripEndWithNonSeparatorCharacters(e);try{return this.parseQuery(e),!e||c.default.isLastCharacterWhiteSpace(e)?i.map(["AND","OR"],(function(e){return{value:e,type:"literal"}})):[]}catch(e){return this.autoCompleteHandler.handleParseError(r,this.parseTrace,e)}},e.prototype.setAutoCompleteHandler=function(e){this.autoCompleteHandler=e},e}();t.default=s},function(e,t,n){"use strict";function r(e,t,n,i){this.message=e,this.expected=t,this.found=n,this.location=i,this.name="SyntaxError","function"==typeof Error.captureStackTrace&&Error.captureStackTrace(this,r)}!function(e,t){function n(){this.constructor=e}n.prototype=t.prototype,e.prototype=new n}(r,Error),r.buildMessage=function(e,t){var n={literal:function(e){return'"'+i(e.text)+'"'},class:function(e){var t,n="";for(t=0;t0){for(t=1,r=1;tM&&(M=k,_=[]),_.push(e))}function L(){var t,n,r,o,a,l,u,f;if(t=k,N()!==i)if((n=D())!==i){for(r=[],o=k,(a=V())!==i?("and"===e.substr(k,3).toLowerCase()?(l=e.substr(k,3),k+=3):(l=i,0===C&&H(c)),l===i&&("or"===e.substr(k,2).toLowerCase()?(l=e.substr(k,2),k+=2):(l=i,0===C&&H(s))),l!==i&&(u=V())!==i&&(f=D())!==i?o=a=[a,l,u,f]:(k=o,o=i)):(k=o,o=i);o!==i;)r.push(o),o=k,(a=V())!==i?("and"===e.substr(k,3).toLowerCase()?(l=e.substr(k,3),k+=3):(l=i,0===C&&H(c)),l===i&&("or"===e.substr(k,2).toLowerCase()?(l=e.substr(k,2),k+=2):(l=i,0===C&&H(s))),l!==i&&(u=V())!==i&&(f=D())!==i?o=a=[a,l,u,f]:(k=o,o=i)):(k=o,o=i);r!==i&&(o=N())!==i?(x=t,t=function(e,t){for(var n=[e],r=0;r=1&&h<=7))throw new RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var d=new Date(0);d.setUTCFullYear(c+1,0,h),d.setUTCHours(0,0,0,0);var p=Object(o.a)(d,t),z=new Date(0);z.setUTCFullYear(c,0,h),z.setUTCHours(0,0,0,0);var v=Object(o.a)(z,t);return n.getTime()>=p.getTime()?c+1:n.getTime()>=v.getTime()?c:c-1}},function(e,t,n){"use strict";n.d(t,"a",(function(){return a}));var r=n(15),i=n(10),o=n(6);function a(e,t){Object(o.a)(2,arguments);var n=Object(i.default)(e).getTime(),a=Object(r.a)(t);return new Date(n+a)}},function(e,t,n){"use strict";n.d(t,"a",(function(){return c}));var r=n(15),i=n(10),o=n(72),a=n(6);function c(e,t){Object(a.a)(1,arguments);var n=Object(i.default)(e,t),c=n.getUTCFullYear(),s=t||{},l=s.locale,u=l&&l.options&&l.options.firstWeekContainsDate,f=null==u?1:Object(r.a)(u),h=null==s.firstWeekContainsDate?f:Object(r.a)(s.firstWeekContainsDate);if(!(h>=1&&h<=7))throw new RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var d=new Date(0);d.setUTCFullYear(c+1,0,h),d.setUTCHours(0,0,0,0);var p=Object(o.a)(d,t),z=new Date(0);z.setUTCFullYear(c,0,h),z.setUTCHours(0,0,0,0);var v=Object(o.a)(z,t);return n.getTime()>=p.getTime()?c+1:n.getTime()>=v.getTime()?c:c-1}},function(e,t,n){"use strict";n.r(t),n.d(t,"default",(function(){return a}));var r=n(10),i=n(15),o=n(6);function a(e,t){Object(o.a)(1,arguments);var n=t||{},a=n.locale,c=a&&a.options&&a.options.weekStartsOn,s=null==c?0:Object(i.a)(c),l=null==n.weekStartsOn?s:Object(i.a)(n.weekStartsOn);if(!(l>=0&&l<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");var u=Object(r.default)(e),f=u.getDay(),h=(f0&&void 0!==arguments[0]?arguments[0]:{},n=t.width?String(t.width):e.defaultWidth,r=e.formats[n]||e.formats[e.defaultWidth];return r}}var o={date:i({formats:{full:"EEEE, MMMM do, y",long:"MMMM do, y",medium:"MMM d, y",short:"MM/dd/yyyy"},defaultWidth:"full"}),time:i({formats:{full:"h:mm:ss a zzzz",long:"h:mm:ss a z",medium:"h:mm:ss a",short:"h:mm a"},defaultWidth:"full"}),dateTime:i({formats:{full:"{{date}} 'at' {{time}}",long:"{{date}} 'at' {{time}}",medium:"{{date}}, {{time}}",short:"{{date}}, {{time}}"},defaultWidth:"full"})},a={lastWeek:"'last' eeee 'at' p",yesterday:"'yesterday at' p",today:"'today at' p",tomorrow:"'tomorrow at' p",nextWeek:"eeee 'at' p",other:"P"};function c(e){return function(t,n){var r,i=n||{};if("formatting"===(i.context?String(i.context):"standalone")&&e.formattingValues){var o=e.defaultFormattingWidth||e.defaultWidth,a=i.width?String(i.width):o;r=e.formattingValues[a]||e.formattingValues[o]}else{var c=e.defaultWidth,s=i.width?String(i.width):e.defaultWidth;r=e.values[s]||e.values[c]}return r[e.argumentCallback?e.argumentCallback(t):t]}}function s(e){return function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=n.width,i=r&&e.matchPatterns[r]||e.matchPatterns[e.defaultMatchWidth],o=t.match(i);if(!o)return null;var a,c=o[0],s=r&&e.parsePatterns[r]||e.parsePatterns[e.defaultParseWidth],f=Array.isArray(s)?u(s,(function(e){return e.test(c)})):l(s,(function(e){return e.test(c)}));a=e.valueCallback?e.valueCallback(f):f,a=n.valueCallback?n.valueCallback(a):a;var h=t.slice(c.length);return{value:a,rest:h}}}function l(e,t){for(var n in e)if(e.hasOwnProperty(n)&&t(e[n]))return n}function u(e,t){for(var n=0;n0?"in "+i:i+" ago":i},formatLong:o,formatRelative:function(e,t,n,r){return a[e]},localize:{ordinalNumber:function(e,t){var n=Number(e),r=n%100;if(r>20||r<10)switch(r%10){case 1:return n+"st";case 2:return n+"nd";case 3:return n+"rd"}return n+"th"},era:c({values:{narrow:["B","A"],abbreviated:["BC","AD"],wide:["Before Christ","Anno Domini"]},defaultWidth:"wide"}),quarter:c({values:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["1st quarter","2nd quarter","3rd quarter","4th quarter"]},defaultWidth:"wide",argumentCallback:function(e){return Number(e)-1}}),month:c({values:{narrow:["J","F","M","A","M","J","J","A","S","O","N","D"],abbreviated:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],wide:["January","February","March","April","May","June","July","August","September","October","November","December"]},defaultWidth:"wide"}),day:c({values:{narrow:["S","M","T","W","T","F","S"],short:["Su","Mo","Tu","We","Th","Fr","Sa"],abbreviated:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],wide:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},defaultWidth:"wide"}),dayPeriod:c({values:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"}},defaultWidth:"wide",formattingValues:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:(f={matchPattern:/^(\d+)(th|st|nd|rd)?/i,parsePattern:/\d+/i,valueCallback:function(e){return parseInt(e,10)}},function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=e.match(f.matchPattern);if(!n)return null;var r=n[0],i=e.match(f.parsePattern);if(!i)return null;var o=f.valueCallback?f.valueCallback(i[0]):i[0];o=t.valueCallback?t.valueCallback(o):o;var a=e.slice(r.length);return{value:o,rest:a}}),era:s({matchPatterns:{narrow:/^(b|a)/i,abbreviated:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,wide:/^(before christ|before common era|anno domini|common era)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^b/i,/^(a|c)/i]},defaultParseWidth:"any"}),quarter:s({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234](th|st|nd|rd)? quarter/i},defaultMatchWidth:"wide",parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:"any",valueCallback:function(e){return e+1}}),month:s({matchPatterns:{narrow:/^[jfmasond]/i,abbreviated:/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,wide:/^(january|february|march|april|may|june|july|august|september|october|november|december)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^j/i,/^f/i,/^m/i,/^a/i,/^m/i,/^j/i,/^j/i,/^a/i,/^s/i,/^o/i,/^n/i,/^d/i],any:[/^ja/i,/^f/i,/^mar/i,/^ap/i,/^may/i,/^jun/i,/^jul/i,/^au/i,/^s/i,/^o/i,/^n/i,/^d/i]},defaultParseWidth:"any"}),day:s({matchPatterns:{narrow:/^[smtwf]/i,short:/^(su|mo|tu|we|th|fr|sa)/i,abbreviated:/^(sun|mon|tue|wed|thu|fri|sat)/i,wide:/^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^s/i,/^m/i,/^t/i,/^w/i,/^t/i,/^f/i,/^s/i],any:[/^su/i,/^m/i,/^tu/i,/^w/i,/^th/i,/^f/i,/^sa/i]},defaultParseWidth:"any"}),dayPeriod:s({matchPatterns:{narrow:/^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,any:/^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^a/i,pm:/^p/i,midnight:/^mi/i,noon:/^no/i,morning:/morning/i,afternoon:/afternoon/i,evening:/evening/i,night:/night/i}},defaultParseWidth:"any"})},options:{weekStartsOn:0,firstWeekContainsDate:1}};t.a=h},function(e,t,n){"use strict";var r=n(33),i=n(29),o=n(47),a=n(23),c=n(11),s=n(48);function l(){var e={};return e.promise=new Promise((function(t,n){e.resolve=t,e.reject=n})),e}var u=l,f=(n(151),[]),h=0;function d(e){try{v(),e()}finally{g()}}function p(e){f.push(e),h||(v(),m())}function z(e){try{return v(),e()}finally{m()}}function v(){h++}function g(){h--}function m(){var e;for(g();!h&&void 0!==(e=f.shift());)d(e)}var y=function(e){return function(t){return e.some((function(e){return j(e)(t)}))}},b=function(e){return function(t){return e(t)}},w=function(e){return function(t){return t.type===String(e)}},k=function(e){return function(t){return t.type===e}},x=function(){return c.H};function j(e){var t="*"===e?x:Object(a.k)(e)?w:Object(a.a)(e)?y:Object(a.l)(e)?w:Object(a.d)(e)?b:Object(a.m)(e)?k:null;if(null===t)throw new Error("invalid pattern: "+e);return t(e)}var M={type:r.b},_=function(e){return e&&e.type===r.b};function C(e){void 0===e&&(e=Object(c.B)());var t=!1,n=[];return{take:function(r){t&&e.isEmpty()?r(M):e.isEmpty()?(n.push(r),r.cancel=function(){Object(c.O)(n,r)}):r(e.take())},put:function(r){if(!t){if(0===n.length)return e.put(r);n.shift()(r)}},flush:function(n){t&&e.isEmpty()?n(M):n(e.flush())},close:function(){if(!t){t=!0;var e=n;n=[];for(var r=0,i=e.length;r2?p-2:0),g=2;g=arguments.length)?u=n[l]:(u=arguments[c],c+=1),o[l]=u,Object(a.a)(u)||(s-=1),l+=1}return s<=0?i.apply(this,o):Object(r.a)(s,e(t,o,i))}}(e,[],t))}));t.a=c},function(e,t,n){"use strict";var r=n(112),i={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},a={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},c={};function s(e){return r.isMemo(e)?a:c[e.$$typeof]||i}c[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0};var l=Object.defineProperty,u=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,h=Object.getOwnPropertyDescriptor,d=Object.getPrototypeOf,p=Object.prototype;e.exports=function e(t,n,r){if("string"!==typeof n){if(p){var i=d(n);i&&i!==p&&e(t,i,r)}var a=u(n);f&&(a=a.concat(f(n)));for(var c=s(t),z=s(n),v=0;v=0;){if(n[o]===e)return r[o]===t;o-=1}switch(i){case"Map":return e.size===t.size&&f(e.entries(),t.entries(),n.concat([e]),r.concat([t]));case"Set":return e.size===t.size&&f(e.values(),t.values(),n.concat([e]),r.concat([t]));case"Arguments":case"Array":case"Object":case"Boolean":case"Number":case"String":case"Date":case"Error":case"RegExp":case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"ArrayBuffer":break;default:return!1}var l=Object(s.a)(e);if(l.length!==Object(s.a)(t).length)return!1;var d=n.concat([e]),p=r.concat([t]);for(o=l.length-1;o>=0;){var z=l[o];if(!Object(a.a)(z,t)||!h(t[z],e[z],d,p))return!1;o-=1}return!0}var d=Object(r.a)((function(e,t){return h(e,t,[],[])}));t.a=d},function(e,t,n){"use strict";var r=n(30),i=n(145),o=n(131);function a(e){return'"'+e.replace(/\\/g,"\\\\").replace(/[\b]/g,"\\b").replace(/\f/g,"\\f").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t").replace(/\v/g,"\\v").replace(/\0/g,"\\0").replace(/"/g,'\\"')+'"'}var c=function(e){return(e<10?"0":"")+e},s="function"===typeof Date.prototype.toISOString?function(e){return e.toISOString()}:function(e){return e.getUTCFullYear()+"-"+c(e.getUTCMonth()+1)+"-"+c(e.getUTCDate())+"T"+c(e.getUTCHours())+":"+c(e.getUTCMinutes())+":"+c(e.getUTCSeconds())+"."+(e.getUTCMilliseconds()/1e3).toFixed(3).slice(2,5)+"Z"},l=n(57);var u=n(19),f=n(284),h=Object(u.a)((function(e,t){return Object(f.a)((n=e,function(){return!n.apply(this,arguments)}),t);var n}));var d=Object(r.a)((function(e){return function e(t,n){var r=function(r){var o=n.concat([t]);return Object(i.a)(r,o)?"":e(r,o)},c=function(e,t){return Object(o.a)((function(t){return a(t)+": "+r(e[t])}),t.slice().sort())};switch(Object.prototype.toString.call(t)){case"[object Arguments]":return"(function() { return arguments; }("+Object(o.a)(r,t).join(", ")+"))";case"[object Array]":return"["+Object(o.a)(r,t).concat(c(t,h((function(e){return/^\d+$/.test(e)}),Object(l.a)(t)))).join(", ")+"]";case"[object Boolean]":return"object"===typeof t?"new Boolean("+r(t.valueOf())+")":t.toString();case"[object Date]":return"new Date("+(isNaN(t.valueOf())?r(NaN):a(s(t)))+")";case"[object Null]":return"null";case"[object Number]":return"object"===typeof t?"new Number("+r(t.valueOf())+")":1/t===-1/0?"-0":t.toString(10);case"[object String]":return"object"===typeof t?"new String("+r(t.valueOf())+")":a(t);case"[object Undefined]":return"undefined";default:if("function"===typeof t.toString){var u=t.toString();if("[object Object]"!==u)return u}return"{"+c(t,Object(l.a)(t)).join(", ")+"}"}}(e,[])}));t.a=d},function(e,t,n){"use strict";var r=n(29),i=n(47),o=n(78),a=n(0),c=n.n(a),s=n(13),l=n.n(s),u=n(34),f=n.n(u),h=n(32),d=n.n(h),p=!1,z=c.a.createContext(null),v="unmounted",g="exited",m="entering",y="entered",b=function(e){function t(t,n){var r;r=e.call(this,t,n)||this;var i,o=n&&!n.isMounting?t.enter:t.appear;return r.appearStatus=null,t.in?o?(i=g,r.appearStatus=m):i=y:i=t.unmountOnExit||t.mountOnEnter?v:g,r.state={status:i},r.nextCallback=null,r}Object(o.a)(t,e),t.getDerivedStateFromProps=function(e,t){return e.in&&t.status===v?{status:g}:null};var n=t.prototype;return n.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},n.componentDidUpdate=function(e){var t=null;if(e!==this.props){var n=this.state.status;this.props.in?n!==m&&n!==y&&(t=m):n!==m&&n!==y||(t="exiting")}this.updateStatus(!1,t)},n.componentWillUnmount=function(){this.cancelNextCallback()},n.getTimeouts=function(){var e,t,n,r=this.props.timeout;return e=t=n=r,null!=r&&"number"!==typeof r&&(e=r.exit,t=r.enter,n=void 0!==r.appear?r.appear:t),{exit:e,enter:t,appear:n}},n.updateStatus=function(e,t){if(void 0===e&&(e=!1),null!==t){this.cancelNextCallback();var n=d.a.findDOMNode(this);t===m?this.performEnter(n,e):this.performExit(n)}else this.props.unmountOnExit&&this.state.status===g&&this.setState({status:v})},n.performEnter=function(e,t){var n=this,r=this.props.enter,i=this.context?this.context.isMounting:t,o=this.getTimeouts(),a=i?o.appear:o.enter;!t&&!r||p?this.safeSetState({status:y},(function(){n.props.onEntered(e)})):(this.props.onEnter(e,i),this.safeSetState({status:m},(function(){n.props.onEntering(e,i),n.onTransitionEnd(e,a,(function(){n.safeSetState({status:y},(function(){n.props.onEntered(e,i)}))}))})))},n.performExit=function(e){var t=this,n=this.props.exit,r=this.getTimeouts();n&&!p?(this.props.onExit(e),this.safeSetState({status:"exiting"},(function(){t.props.onExiting(e),t.onTransitionEnd(e,r.exit,(function(){t.safeSetState({status:g},(function(){t.props.onExited(e)}))}))}))):this.safeSetState({status:g},(function(){t.props.onExited(e)}))},n.cancelNextCallback=function(){null!==this.nextCallback&&(this.nextCallback.cancel(),this.nextCallback=null)},n.safeSetState=function(e,t){t=this.setNextCallback(t),this.setState(e,t)},n.setNextCallback=function(e){var t=this,n=!0;return this.nextCallback=function(r){n&&(n=!1,t.nextCallback=null,e(r))},this.nextCallback.cancel=function(){n=!1},this.nextCallback},n.onTransitionEnd=function(e,t,n){this.setNextCallback(n);var r=null==t&&!this.props.addEndListener;e&&!r?(this.props.addEndListener&&this.props.addEndListener(e,this.nextCallback),null!=t&&setTimeout(this.nextCallback,t)):setTimeout(this.nextCallback,0)},n.render=function(){var e=this.state.status;if(e===v)return null;var t=this.props,n=t.children,r=Object(i.a)(t,["children"]);if(delete r.in,delete r.mountOnEnter,delete r.unmountOnExit,delete r.appear,delete r.enter,delete r.exit,delete r.timeout,delete r.addEndListener,delete r.onEnter,delete r.onEntering,delete r.onEntered,delete r.onExit,delete r.onExiting,delete r.onExited,"function"===typeof n)return c.a.createElement(z.Provider,{value:null},n(e,r));var o=c.a.Children.only(n);return(c.a.createElement(z.Provider,{value:null},c.a.cloneElement(o,r)))},t}(c.a.Component);function w(){}b.contextType=z,b.propTypes={},b.defaultProps={in:!1,mountOnEnter:!1,unmountOnExit:!1,appear:!1,enter:!0,exit:!0,onEnter:w,onEntering:w,onEntered:w,onExit:w,onExiting:w,onExited:w},b.UNMOUNTED=0,b.EXITED=1,b.ENTERING=2,b.ENTERED=3,b.EXITING=4;var k=b,x=n(99);function j(e,t){var n=Object.create(null);return e&&a.Children.map(e,(function(e){return e})).forEach((function(e){n[e.key]=function(e){return t&&Object(a.isValidElement)(e)?t(e):e}(e)})),n}function M(e,t,n){return null!=n[t]?n[t]:e.props[t]}function _(e,t,n){var r=j(e.children),i=function(e,t){function n(n){return n in t?t[n]:e[n]}e=e||{},t=t||{};var r,i=Object.create(null),o=[];for(var a in e)a in t?o.length&&(i[a]=o,o=[]):o.push(a);var c={};for(var s in t){if(i[s])for(r=0;r0}function D(e){return Object.keys(e).map((function(t){return e[t]}))}var P=!("undefined"===typeof window||!window.document||!window.document.createElement);var V,N=((V=function(e,t,n){var r=e[t];return!1===r||L(r)?null:new Error(n+" expect "+t+" \n to be a valid Number > 0 or equal to false. "+r+" given.")}).isRequired=function(e,t,n){if("undefined"===typeof e[t])return new Error("The prop "+t+" is marked as required in \n "+n+", but its value is undefined.");V(e,t,n)},V),I={list:new Map,emitQueue:new Map,on:function(e,t){return this.list.has(e)||this.list.set(e,[]),this.list.get(e).push(t),this},off:function(e){return this.list.delete(e),this},cancelEmit:function(e){var t=this.emitQueue.get(e);return t&&(t.forEach((function(e){return clearTimeout(e)})),this.emitQueue.delete(e)),this},emit:function(e){for(var t=this,n=arguments.length,r=new Array(n>1?n-1:0),i=1;i=1?"onTransitionEnd":"onAnimationEnd"]=d&&p<1?null:a,n);return c.a.createElement("div",Object(r.a)({className:g,style:v},m))}function F(e){return e.targetTouches&&e.targetTouches.length>=1?e.targetTouches[0].clientX:e.clientX}B.propTypes={delay:N.isRequired,isRunning:l.a.bool.isRequired,closeToast:l.a.func.isRequired,rtl:l.a.bool.isRequired,type:l.a.string,hide:l.a.bool,className:l.a.oneOfType([l.a.string,l.a.object]),progress:l.a.number,controlledProgress:l.a.bool},B.defaultProps={type:T.DEFAULT,hide:!1};var U=P&&/(msie|trident)/i.test(navigator.userAgent),W=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),i=0;i=1?e.targetTouches[0].clientY:e.clientY}(e),t.drag.start!==t.drag.x&&(t.flag.canCloseOnClick=!1),t.ref.style.transform="translateX("+t.drag.deltaX+"px)",t.ref.style.opacity=1-Math.abs(t.drag.deltaX/t.drag.removalDistance))},t.onDragEnd=function(e){if(t.flag.canDrag){if(t.flag.canDrag=!1,Math.abs(t.drag.deltaX)>t.drag.removalDistance)return void t.setState({preventExitTransition:!0},t.props.closeToast);t.ref.style.transition="transform 0.2s, opacity 0.2s",t.ref.style.transform="translateX(0)",t.ref.style.opacity=1}},t.onDragTransitionEnd=function(){if(t.boundingRect){var e=t.boundingRect,n=e.top,r=e.bottom,i=e.left,o=e.right;t.props.pauseOnHover&&t.drag.x>=i&&t.drag.x<=o&&t.drag.y>=n&&t.drag.y<=r?t.pauseToast():t.playToast()}},t.onExitTransitionEnd=function(){if(U)t.props.onExited();else{var e=t.ref.scrollHeight,n=t.ref.style;requestAnimationFrame((function(){n.minHeight="initial",n.height=e+"px",n.transition="all 0.4s ",requestAnimationFrame((function(){n.height=0,n.padding=0,n.margin=0})),setTimeout((function(){return t.props.onExited()}),400)}))}},t}Object(o.a)(t,e);var n=t.prototype;return n.componentDidMount=function(){this.props.onOpen(this.props.children.props),this.props.draggable&&this.bindDragEvents(),this.props.pauseOnFocusLoss&&this.bindFocusEvents()},n.componentDidUpdate=function(e){e.draggable!==this.props.draggable&&(this.props.draggable?this.bindDragEvents():this.unbindDragEvents()),e.pauseOnFocusLoss!==this.props.pauseOnFocusLoss&&(this.props.pauseOnFocusLoss?this.bindFocusEvents():this.unbindFocusEvents())},n.componentWillUnmount=function(){this.props.onClose(this.props.children.props),this.props.draggable&&this.unbindDragEvents(),this.props.pauseOnFocusLoss&&this.unbindFocusEvents()},n.bindFocusEvents=function(){window.addEventListener("focus",this.playToast),window.addEventListener("blur",this.pauseToast)},n.unbindFocusEvents=function(){window.removeEventListener("focus",this.playToast),window.removeEventListener("blur",this.pauseToast)},n.bindDragEvents=function(){document.addEventListener("mousemove",this.onDragMove),document.addEventListener("mouseup",this.onDragEnd),document.addEventListener("touchmove",this.onDragMove),document.addEventListener("touchend",this.onDragEnd)},n.unbindDragEvents=function(){document.removeEventListener("mousemove",this.onDragMove),document.removeEventListener("mouseup",this.onDragEnd),document.removeEventListener("touchmove",this.onDragMove),document.removeEventListener("touchend",this.onDragEnd)},n.render=function(){var e,t=this,n=this.props,i=n.closeButton,o=n.children,a=n.autoClose,s=n.pauseOnHover,l=n.onClick,u=n.closeOnClick,h=n.type,d=n.hideProgressBar,p=n.closeToast,z=n.transition,v=n.position,g=n.className,m=n.bodyClassName,y=n.progressClassName,b=n.progressStyle,w=n.updateId,k=n.role,x=n.progress,j=n.rtl,M={className:f()(H+"__toast",H+"__toast--"+h,(e={},e[H+"__toast--rtl"]=j,e),g)};a&&s&&(M.onMouseEnter=this.pauseToast,M.onMouseLeave=this.playToast),u&&(M.onClick=function(e){l&&l(e),t.flag.canCloseOnClick&&p()});var _=parseFloat(x)===x;return c.a.createElement(z,{in:this.props.in,appear:!0,onExited:this.onExitTransitionEnd,position:v,preventExitTransition:this.state.preventExitTransition},c.a.createElement("div",Object(r.a)({onClick:l},M,{ref:function(e){return t.ref=e},onMouseDown:this.onDragStart,onTouchStart:this.onDragStart,onMouseUp:this.onDragTransitionEnd,onTouchEnd:this.onDragTransitionEnd}),c.a.createElement("div",Object(r.a)({},this.props.in&&{role:k},{className:f()(H+"__toast-body",m)}),o),i&&i,(a||_)&&c.a.createElement(B,Object(r.a)({},w&&!_?{key:"pb-"+w}:{},{rtl:j,delay:a,isRunning:this.state.isRunning,closeToast:p,hide:d,type:h,style:b,className:y,controlledProgress:_,progress:x}))))},t}(a.Component);function G(e){var t=e.closeToast,n=e.type,r=e.ariaLabel;return c.a.createElement("button",{className:H+"__close-button "+H+"__close-button--"+n,type:"button",onClick:function(e){e.stopPropagation(),t(e)},"aria-label":r},"\u2716\ufe0e")}W.propTypes={closeButton:l.a.oneOfType([l.a.node,l.a.bool]).isRequired,autoClose:N.isRequired,children:l.a.node.isRequired,closeToast:l.a.func.isRequired,position:l.a.oneOf(D(O)).isRequired,pauseOnHover:l.a.bool.isRequired,pauseOnFocusLoss:l.a.bool.isRequired,closeOnClick:l.a.bool.isRequired,transition:l.a.func.isRequired,rtl:l.a.bool.isRequired,hideProgressBar:l.a.bool.isRequired,draggable:l.a.bool.isRequired,draggablePercent:l.a.number.isRequired,in:l.a.bool,onExited:l.a.func,onOpen:l.a.func,onClose:l.a.func,type:l.a.oneOf(D(T)),className:l.a.oneOfType([l.a.string,l.a.object]),bodyClassName:l.a.oneOfType([l.a.string,l.a.object]),progressClassName:l.a.oneOfType([l.a.string,l.a.object]),progressStyle:l.a.object,progress:l.a.number,updateId:l.a.oneOfType([l.a.string,l.a.number]),ariaLabel:l.a.string,containerId:l.a.oneOfType([l.a.string,l.a.number]),role:l.a.string},W.defaultProps={type:T.DEFAULT,in:!0,onOpen:A,onClose:A,className:null,bodyClassName:null,progressClassName:null,updateId:null},G.propTypes={closeToast:l.a.func,arialLabel:l.a.string},G.defaultProps={ariaLabel:"close"};var Y=R({enter:H+"__bounce-enter",exit:H+"__bounce-exit",appendPosition:!0}),Z=(R({enter:H+"__slide-enter",exit:H+"__slide-exit",duration:[450,750],appendPosition:!0}),R({enter:H+"__zoom-enter",exit:H+"__zoom-exit"}),R({enter:H+"__flip-enter",exit:H+"__flip-exit"}),function(e){function t(){for(var t,n=arguments.length,r=new Array(n),i=0;i0}function ne(e,t){var n=function(e){return te()?e?$.get(e):$.get(X):null}(t.containerId);if(!n)return null;var r=n.collection[e];return"undefined"===typeof r?null:r}function re(e,t){return Object(r.a)({},e,{type:t,toastId:oe(e)})}function ie(){return(Math.random().toString(36)+Date.now().toString(36)).substr(2,10)}function oe(e){return e&&("string"===typeof e.toastId||"number"===typeof e.toastId&&!isNaN(e.toastId))?e.toastId:ie()}function ae(e,t){return te()?I.emit(E.SHOW,e,t):(J.push({action:E.SHOW,content:e,options:t}),ee&&P&&(ee=!1,K=document.createElement("div"),document.body.appendChild(K),Object(h.render)(c.a.createElement(Z,Q),K))),t.toastId}var ce=function(e,t){return ae(e,re(t,t&&t.type||T.DEFAULT))},se=function(e){T[e]!==T.DEFAULT&&(ce[T[e].toLowerCase()]=function(t,n){return ae(t,re(n,n&&n.type||T[e]))})};for(var le in T)se(le);ce.warn=ce.warning,ce.dismiss=function(e){return void 0===e&&(e=null),te()&&I.emit(E.CLEAR,e)},ce.isActive=function(e){var t=!1;return $.size>0&&$.forEach((function(n){n.isToastActive(e)&&(t=!0)})),t},ce.update=function(e,t){void 0===t&&(t={}),setTimeout((function(){var n=ne(e,t);if(n){var i=n.options,o=n.content,a=Object(r.a)({},i,{},t,{toastId:t.toastId||e});t.toastId&&t.toastId!==e?a.staleToastId=e:a.updateId=ie();var c="undefined"!==typeof a.render?a.render:o;delete a.render,ae(c,a)}}),0)},ce.done=function(e){ce.update(e,{progress:1})},ce.onChange=function(e){"function"===typeof e&&I.on(E.ON_CHANGE,e)},ce.configure=function(e){ee=!0,Q=e},ce.POSITION=O,ce.TYPE=T,I.on(E.DID_MOUNT,(function(e){X=e.props.containerId||e,$.set(X,e),J.forEach((function(e){I.emit(e.action,e.content,e.options)})),J=[]})).on(E.WILL_UNMOUNT,(function(e){e?$.delete(e.props.containerId||e):$.clear(),0===$.size&&I.off(E.SHOW).off(E.CLEAR),P&&K&&document.body.removeChild(K)}))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.invariant=t.UNICODE_EXTENSION_SEQUENCE_REGEX=void 0,t.UNICODE_EXTENSION_SEQUENCE_REGEX=/-u(?:-[0-9a-z]{2,8})+/gi,t.invariant=function(e,t,n){if(void 0===n&&(n=Error),!e)throw new n(t)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.shortMorePenalty=t.shortLessPenalty=t.longMorePenalty=t.longLessPenalty=t.differentNumericTypePenalty=t.additionPenalty=t.removalPenalty=t.DATE_TIME_PROPS=void 0,t.DATE_TIME_PROPS=["weekday","era","year","month","day","hour","minute","second","timeZoneName"],t.removalPenalty=120,t.additionPenalty=20,t.differentNumericTypePenalty=15,t.longLessPenalty=8,t.longMorePenalty=6,t.shortLessPenalty=6,t.shortMorePenalty=3},function(e,t,n){"use strict";n.r(t),n.d(t,"default",(function(){return a}));var r=n(15),i=n(10),o=n(6);function a(e,t){Object(o.a)(2,arguments);var n=Object(i.default)(e),a=Object(r.a)(t);return isNaN(a)?new Date(NaN):a?(n.setDate(n.getDate()+a),n):n}},function(e,t,n){"use strict";n.r(t),n.d(t,"default",(function(){return a}));var r=n(15),i=n(10),o=n(6);function a(e,t){Object(o.a)(2,arguments);var n=Object(i.default)(e),a=Object(r.a)(t);if(isNaN(a))return new Date(NaN);if(!a)return n;var c=n.getDate(),s=new Date(n.getTime());return s.setMonth(n.getMonth()+a+1,0),c>=s.getDate()?s:(n.setFullYear(s.getFullYear(),s.getMonth(),c),n)}},,function(e,t,n){"use strict";function r(e){return"[object Function]"===Object.prototype.toString.call(e)}n.d(t,"a",(function(){return r}))},,function(e,t,n){"use strict";(function(e){n.d(t,"a",(function(){return i})),n.d(t,"b",(function(){return o}));var r=function(){return(r=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt?1:e>=t?0:NaN},i=function(e){var t;return 1===e.length&&(t=e,e=function(e,n){return r(t(e),n)}),{left:function(t,n,r,i){for(null==r&&(r=0),null==i&&(i=t.length);r>>1;e(t[o],n)<0?r=o+1:i=o}return r},right:function(t,n,r,i){for(null==r&&(r=0),null==i&&(i=t.length);r>>1;e(t[o],n)>0?i=o:r=o+1}return r}}};var o=i(r),a=o.right,c=o.left,s=a,l=function(e,t){null==t&&(t=u);for(var n=0,r=e.length-1,i=e[0],o=new Array(r<0?0:r);ne?1:t>=e?0:NaN},d=function(e){return null===e?NaN:+e},p=function(e,t){var n,r,i=e.length,o=0,a=-1,c=0,s=0;if(null==t)for(;++a1)return s/(o-1)},z=function(e,t){var n=p(e,t);return n?Math.sqrt(n):n},v=function(e,t){var n,r,i,o=e.length,a=-1;if(null==t){for(;++a=n)for(r=i=n;++an&&(r=n),i=n)for(r=i=n;++an&&(r=n),i0)return[e];if((r=t0)for(e=Math.ceil(e/a),t=Math.floor(t/a),o=new Array(i=Math.ceil(t-e+1));++c=0?(o>=x?10:o>=j?5:o>=M?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=x?10:o>=j?5:o>=M?2:1)}function q(e,t,n){var r=Math.abs(t-e)/Math.max(0,n),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=x?i*=10:o>=j?i*=5:o>=M&&(i*=2),tf;)h.pop(),--d;var p,z=new Array(d+1);for(i=0;i<=d;++i)(p=z[i]=[]).x0=i>0?h[i-1]:u,p.x1=i=1)return+n(e[r-1],r-1,e);var r,i=(r-1)*t,o=Math.floor(i),a=+n(e[o],o,e);return a+(+n(e[o+1],o+1,e)-a)*(i-o)}},E=function(e,t,n){return e=y.call(e,d).sort(r),Math.ceil((n-t)/(2*(T(e,.75)-T(e,.25))*Math.pow(e.length,-1/3)))},A=function(e,t,n){return Math.ceil((n-t)/(3.5*z(e)*Math.pow(e.length,-1/3)))},H=function(e,t){var n,r,i=e.length,o=-1;if(null==t){for(;++o=n)for(r=n;++or&&(r=n)}else for(;++o=n)for(r=n;++or&&(r=n);return r},L=function(e,t){var n,r=e.length,i=r,o=-1,a=0;if(null==t)for(;++o=0;)for(t=(r=e[i]).length;--t>=0;)n[--a]=r[t];return n},V=function(e,t){var n,r,i=e.length,o=-1;if(null==t){for(;++o=n)for(r=n;++on&&(r=n)}else for(;++o=n)for(r=n;++on&&(r=n);return r},N=function(e,t){for(var n=t.length,r=new Array(n);n--;)r[n]=e[t[n]];return r},I=function(e,t){if(n=e.length){var n,i,o=0,a=0,c=e[a];for(null==t&&(t=r);++o=0&&(n=e.slice(r+1),e=e.slice(0,r)),e&&!t.hasOwnProperty(e))throw new Error("unknown type: "+e);return{type:e,name:n}}))}function de(e,t){for(var n,r=0,i=e.length;r0)for(var n,r,i=new Array(n),o=0;o=0&&"xmlns"!==(t=e.slice(0,n))&&(e=e.slice(n+1)),ge.hasOwnProperty(t)?{space:ge[t],local:e}:e};function ye(e){return function(){var t=this.ownerDocument,n=this.namespaceURI;return n===ve&&t.documentElement.namespaceURI===ve?t.createElement(e):t.createElementNS(n,e)}}function be(e){return function(){return this.ownerDocument.createElementNS(e.space,e.local)}}var we=function(e){var t=me(e);return(t.local?be:ye)(t)},ke=0;function xe(){return new je}function je(){this._="@"+(++ke).toString(36)}je.prototype=xe.prototype={constructor:je,get:function(e){for(var t=this._;!(t in e);)if(!(e=e.parentNode))return;return e[t]},set:function(e,t){return e[this._]=t},remove:function(e){return this._ in e&&delete e[this._]},toString:function(){return this._}};var Me=function(e){return function(){return this.matches(e)}};if("undefined"!==typeof document){var _e=document.documentElement;if(!_e.matches){var Ce=_e.webkitMatchesSelector||_e.msMatchesSelector||_e.mozMatchesSelector||_e.oMatchesSelector;Me=function(e){return function(){return Ce.call(this,e)}}}}var qe=Me,Se={},Oe=null;"undefined"!==typeof document&&("onmouseenter"in document.documentElement||(Se={mouseenter:"mouseover",mouseleave:"mouseout"}));function Te(e,t,n){return e=Ee(e,t,n),function(t){var n=t.relatedTarget;n&&(n===this||8&n.compareDocumentPosition(this))||e.call(this,t)}}function Ee(e,t,n){return function(r){var i=Oe;Oe=r;try{e.call(this,this.__data__,t,n)}finally{Oe=i}}}function Ae(e){return e.trim().split(/^|\s+/).map((function(e){var t="",n=e.indexOf(".");return n>=0&&(t=e.slice(n+1),e=e.slice(0,n)),{type:e,name:t}}))}function He(e){return function(){var t=this.__on;if(t){for(var n,r=0,i=-1,o=t.length;rt?1:e>=t?0:NaN}function Xe(e){return function(){this.removeAttribute(e)}}function Ke(e){return function(){this.removeAttributeNS(e.space,e.local)}}function Qe(e,t){return function(){this.setAttribute(e,t)}}function Je(e,t){return function(){this.setAttributeNS(e.space,e.local,t)}}function et(e,t){return function(){var n=t.apply(this,arguments);null==n?this.removeAttribute(e):this.setAttribute(e,n)}}function tt(e,t){return function(){var n=t.apply(this,arguments);null==n?this.removeAttributeNS(e.space,e.local):this.setAttributeNS(e.space,e.local,n)}}var nt=function(e){return e.ownerDocument&&e.ownerDocument.defaultView||e.document&&e||e.defaultView};function rt(e){return function(){this.style.removeProperty(e)}}function it(e,t,n){return function(){this.style.setProperty(e,t,n)}}function ot(e,t,n){return function(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(e):this.style.setProperty(e,r,n)}}function at(e,t){return e.style.getPropertyValue(t)||nt(e).getComputedStyle(e,null).getPropertyValue(t)}function ct(e){return function(){delete this[e]}}function st(e,t){return function(){this[e]=t}}function lt(e,t){return function(){var n=t.apply(this,arguments);null==n?delete this[e]:this[e]=n}}function ut(e){return e.trim().split(/^|\s+/)}function ft(e){return e.classList||new ht(e)}function ht(e){this._node=e,this._names=ut(e.getAttribute("class")||"")}function dt(e,t){for(var n=ft(e),r=-1,i=t.length;++r=0&&(this._names.splice(t,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(e){return this._names.indexOf(e)>=0}};function mt(){this.textContent=""}function yt(e){return function(){this.textContent=e}}function bt(e){return function(){var t=e.apply(this,arguments);this.textContent=null==t?"":t}}function wt(){this.innerHTML=""}function kt(e){return function(){this.innerHTML=e}}function xt(e){return function(){var t=e.apply(this,arguments);this.innerHTML=null==t?"":t}}function jt(){this.nextSibling&&this.parentNode.appendChild(this)}function Mt(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function _t(){return null}function Ct(){var e=this.parentNode;e&&e.removeChild(this)}function qt(e,t,n){var r=nt(e),i=r.CustomEvent;"function"===typeof i?i=new i(t,n):(i=r.document.createEvent("Event"),n?(i.initEvent(t,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(t,!1,!1)),e.dispatchEvent(i)}function St(e,t){return function(){return qt(this,e,t)}}function Ot(e,t){return function(){return qt(this,e,t.apply(this,arguments))}}var Tt=[null];function Et(e,t){this._groups=e,this._parents=t}function At(){return new Et([[document.documentElement]],Tt)}Et.prototype=At.prototype={constructor:Et,select:function(e){"function"!==typeof e&&(e=Re(e));for(var t=this._groups,n=t.length,r=new Array(n),i=0;i=w&&(w=b+1);!(y=g[w])&&++w=0;)(r=i[o])&&(a&&a!==r.nextSibling&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(e){function t(t,n){return t&&n?e(t.__data__,n.__data__):!t-!n}e||(e=$e);for(var n=this._groups,r=n.length,i=new Array(r),o=0;o1?this.each((null==t?rt:"function"===typeof t?ot:it)(e,t,null==n?"":n)):at(this.node(),e)},property:function(e,t){return arguments.length>1?this.each((null==t?ct:"function"===typeof t?lt:st)(e,t)):this.node()[e]},classed:function(e,t){var n=ut(e+"");if(arguments.length<2){for(var r=ft(this.node()),i=-1,o=n.length;++if}s.mouse("drag")}function z(){Lt(Oe.view).on("mousemove.drag mouseup.drag",null),Bt(Oe.view,n),It(),s.mouse("end")}function v(){if(i.apply(this,arguments)){var e,t,n=Oe.changedTouches,r=o.apply(this,arguments),a=n.length;for(e=0;e>8&15|t>>4&240,t>>4&15|240&t,(15&t)<<4|15&t,1):(t=rn.exec(e))?dn(parseInt(t[1],16)):(t=on.exec(e))?new gn(t[1],t[2],t[3],1):(t=an.exec(e))?new gn(255*t[1]/100,255*t[2]/100,255*t[3]/100,1):(t=cn.exec(e))?pn(t[1],t[2],t[3],t[4]):(t=sn.exec(e))?pn(255*t[1]/100,255*t[2]/100,255*t[3]/100,t[4]):(t=ln.exec(e))?mn(t[1],t[2]/100,t[3]/100,1):(t=un.exec(e))?mn(t[1],t[2]/100,t[3]/100,t[4]):fn.hasOwnProperty(e)?dn(fn[e]):"transparent"===e?new gn(NaN,NaN,NaN,0):null}function dn(e){return new gn(e>>16&255,e>>8&255,255&e,1)}function pn(e,t,n,r){return r<=0&&(e=t=n=NaN),new gn(e,t,n,r)}function zn(e){return e instanceof Qt||(e=hn(e)),e?new gn((e=e.rgb()).r,e.g,e.b,e.opacity):new gn}function vn(e,t,n,r){return 1===arguments.length?zn(e):new gn(e,t,n,null==r?1:r)}function gn(e,t,n,r){this.r=+e,this.g=+t,this.b=+n,this.opacity=+r}function mn(e,t,n,r){return r<=0?e=t=n=NaN:n<=0||n>=1?e=t=NaN:t<=0&&(e=NaN),new bn(e,t,n,r)}function yn(e,t,n,r){return 1===arguments.length?function(e){if(e instanceof bn)return new bn(e.h,e.s,e.l,e.opacity);if(e instanceof Qt||(e=hn(e)),!e)return new bn;if(e instanceof bn)return e;var t=(e=e.rgb()).r/255,n=e.g/255,r=e.b/255,i=Math.min(t,n,r),o=Math.max(t,n,r),a=NaN,c=o-i,s=(o+i)/2;return c?(a=t===o?(n-r)/c+6*(n0&&s<1?0:a,new bn(a,c,s,e.opacity)}(e):new bn(e,t,n,null==r?1:r)}function bn(e,t,n,r){this.h=+e,this.s=+t,this.l=+n,this.opacity=+r}function wn(e,t,n){return 255*(e<60?t+(n-t)*e/60:e<180?n:e<240?t+(n-t)*(240-e)/60:t)}Xt(Qt,hn,{displayable:function(){return this.rgb().displayable()},toString:function(){return this.rgb()+""}}),Xt(gn,vn,Kt(Qt,{brighter:function(e){return e=null==e?1/.7:Math.pow(1/.7,e),new gn(this.r*e,this.g*e,this.b*e,this.opacity)},darker:function(e){return e=null==e?.7:Math.pow(.7,e),new gn(this.r*e,this.g*e,this.b*e,this.opacity)},rgb:function(){return this},displayable:function(){return 0<=this.r&&this.r<=255&&0<=this.g&&this.g<=255&&0<=this.b&&this.b<=255&&0<=this.opacity&&this.opacity<=1},toString:function(){var e=this.opacity;return(1===(e=isNaN(e)?1:Math.max(0,Math.min(1,e)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===e?")":", "+e+")")}})),Xt(bn,yn,Kt(Qt,{brighter:function(e){return e=null==e?1/.7:Math.pow(1/.7,e),new bn(this.h,this.s,this.l*e,this.opacity)},darker:function(e){return e=null==e?.7:Math.pow(.7,e),new bn(this.h,this.s,this.l*e,this.opacity)},rgb:function(){var e=this.h%360+360*(this.h<0),t=isNaN(e)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*t,i=2*n-r;return new gn(wn(e>=240?e-240:e+120,i,r),wn(e,i,r),wn(e<120?e+240:e-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var kn=Math.PI/180,xn=180/Math.PI,jn=.95047,Mn=1,_n=1.08883,Cn=4/29,qn=6/29,Sn=3*qn*qn,On=qn*qn*qn;function Tn(e){if(e instanceof An)return new An(e.l,e.a,e.b,e.opacity);if(e instanceof Nn){var t=e.h*kn;return new An(e.l,Math.cos(t)*e.c,Math.sin(t)*e.c,e.opacity)}e instanceof gn||(e=zn(e));var n=Pn(e.r),r=Pn(e.g),i=Pn(e.b),o=Hn((.4124564*n+.3575761*r+.1804375*i)/jn),a=Hn((.2126729*n+.7151522*r+.072175*i)/Mn);return new An(116*a-16,500*(o-a),200*(a-Hn((.0193339*n+.119192*r+.9503041*i)/_n)),e.opacity)}function En(e,t,n,r){return 1===arguments.length?Tn(e):new An(e,t,n,null==r?1:r)}function An(e,t,n,r){this.l=+e,this.a=+t,this.b=+n,this.opacity=+r}function Hn(e){return e>On?Math.pow(e,1/3):e/Sn+Cn}function Ln(e){return e>qn?e*e*e:Sn*(e-Cn)}function Dn(e){return 255*(e<=.0031308?12.92*e:1.055*Math.pow(e,1/2.4)-.055)}function Pn(e){return(e/=255)<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)}function Vn(e,t,n,r){return 1===arguments.length?function(e){if(e instanceof Nn)return new Nn(e.h,e.c,e.l,e.opacity);e instanceof An||(e=Tn(e));var t=Math.atan2(e.b,e.a)*xn;return new Nn(t<0?t+360:t,Math.sqrt(e.a*e.a+e.b*e.b),e.l,e.opacity)}(e):new Nn(e,t,n,null==r?1:r)}function Nn(e,t,n,r){this.h=+e,this.c=+t,this.l=+n,this.opacity=+r}Xt(An,En,Kt(Qt,{brighter:function(e){return new An(this.l+18*(null==e?1:e),this.a,this.b,this.opacity)},darker:function(e){return new An(this.l-18*(null==e?1:e),this.a,this.b,this.opacity)},rgb:function(){var e=(this.l+16)/116,t=isNaN(this.a)?e:e+this.a/500,n=isNaN(this.b)?e:e-this.b/200;return e=Mn*Ln(e),new gn(Dn(3.2404542*(t=jn*Ln(t))-1.5371385*e-.4985314*(n=_n*Ln(n))),Dn(-.969266*t+1.8760108*e+.041556*n),Dn(.0556434*t-.2040259*e+1.0572252*n),this.opacity)}})),Xt(Nn,Vn,Kt(Qt,{brighter:function(e){return new Nn(this.h,this.c,this.l+18*(null==e?1:e),this.opacity)},darker:function(e){return new Nn(this.h,this.c,this.l-18*(null==e?1:e),this.opacity)},rgb:function(){return Tn(this).rgb()}}));var In=-.29227,Rn=-.90649,Bn=1.97294,Fn=Bn*Rn,Un=1.78277*Bn,Wn=1.78277*In- -.14861*Rn;function Gn(e,t,n,r){return 1===arguments.length?function(e){if(e instanceof Yn)return new Yn(e.h,e.s,e.l,e.opacity);e instanceof gn||(e=zn(e));var t=e.r/255,n=e.g/255,r=e.b/255,i=(Wn*r+Fn*t-Un*n)/(Wn+Fn-Un),o=r-i,a=(Bn*(n-i)-In*o)/Rn,c=Math.sqrt(a*a+o*o)/(Bn*i*(1-i)),s=c?Math.atan2(a,o)*xn-120:NaN;return new Yn(s<0?s+360:s,c,i,e.opacity)}(e):new Yn(e,t,n,null==r?1:r)}function Yn(e,t,n,r){this.h=+e,this.s=+t,this.l=+n,this.opacity=+r}function Zn(e,t,n,r,i){var o=e*e,a=o*e;return((1-3*e+3*o-a)*t+(4-6*o+3*a)*n+(1+3*e+3*o-3*a)*r+a*i)/6}Xt(Yn,Gn,Kt(Qt,{brighter:function(e){return e=null==e?1/.7:Math.pow(1/.7,e),new Yn(this.h,this.s,this.l*e,this.opacity)},darker:function(e){return e=null==e?.7:Math.pow(.7,e),new Yn(this.h,this.s,this.l*e,this.opacity)},rgb:function(){var e=isNaN(this.h)?0:(this.h+120)*kn,t=+this.l,n=isNaN(this.s)?0:this.s*t*(1-t),r=Math.cos(e),i=Math.sin(e);return new gn(255*(t+n*(-.14861*r+1.78277*i)),255*(t+n*(In*r+Rn*i)),255*(t+n*(Bn*r)),this.opacity)}}));var $n=function(e){var t=e.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,t-1):Math.floor(n*t),i=e[r],o=e[r+1],a=r>0?e[r-1]:2*i-o,c=r180||n<-180?n-360*Math.round(n/360):n):Kn(isNaN(e)?t:e)}function er(e){return 1===(e=+e)?tr:function(t,n){return n-t?function(e,t,n){return e=Math.pow(e,n),t=Math.pow(t,n)-e,n=1/n,function(r){return Math.pow(e+r*t,n)}}(t,n,e):Kn(isNaN(t)?n:t)}}function tr(e,t){var n=t-e;return n?Qn(e,n):Kn(isNaN(e)?t:e)}var nr=function e(t){var n=er(t);function r(e,t){var r=n((e=vn(e)).r,(t=vn(t)).r),i=n(e.g,t.g),o=n(e.b,t.b),a=tr(e.opacity,t.opacity);return function(t){return e.r=r(t),e.g=i(t),e.b=o(t),e.opacity=a(t),e+""}}return r.gamma=e,r}(1);function rr(e){return function(t){var n,r,i=t.length,o=new Array(i),a=new Array(i),c=new Array(i);for(n=0;no&&(i=t.slice(o,i),c[a]?c[a]+=i:c[++a]=i),(n=n[0])===(r=r[0])?c[a]?c[a]+=r:c[++a]=r:(c[++a]=null,s.push({i:a,x:sr(n,r)})),o=fr.lastIndex;return o180?t+=360:t-e>180&&(e+=360),o.push({i:n.push(i(n)+"rotate(",null,r)-2,x:sr(e,t)})):t&&n.push(i(n)+"rotate("+t+r)}(o.rotate,a.rotate,c,s),function(e,t,n,o){e!==t?o.push({i:n.push(i(n)+"skewX(",null,r)-2,x:sr(e,t)}):t&&n.push(i(n)+"skewX("+t+r)}(o.skewX,a.skewX,c,s),function(e,t,n,r,o,a){if(e!==n||t!==r){var c=o.push(i(o)+"scale(",null,",",null,")");a.push({i:c-4,x:sr(e,n)},{i:c-2,x:sr(t,r)})}else 1===n&&1===r||o.push(i(o)+"scale("+n+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,c,s),o=a=null,function(e){for(var t,n=-1,r=s.length;++n=0&&t._call.call(null,e),t=t._next;--Rr}function ti(){Gr=(Wr=Zr.now())+Yr,Rr=Br=0;try{ei()}finally{Rr=0,function(){var e,t,n=Dr,r=1/0;for(;n;)n._call?(r>n._time&&(r=n._time),e=n,n=n._next):(t=n._next,n._next=null,n=e?e._next=t:Dr=t);Pr=e,ri(r)}(),Gr=0}}function ni(){var e=Zr.now(),t=e-Wr;t>Ur&&(Yr-=t,Wr=e)}function ri(e){Rr||(Br&&(Br=clearTimeout(Br)),e-Gr>24?(e<1/0&&(Br=setTimeout(ti,e-Zr.now()-Yr)),Fr&&(Fr=clearInterval(Fr))):(Fr||(Wr=Zr.now(),Fr=setInterval(ni,Ur)),Rr=1,$r(ti)))}Qr.prototype=Jr.prototype={constructor:Qr,restart:function(e,t,n){if("function"!==typeof e)throw new TypeError("callback is not a function");n=(null==n?Xr():+n)+(null==t?0:+t),this._next||Pr===this||(Pr?Pr._next=this:Dr=this,Pr=this),this._call=e,this._time=n,ri()},stop:function(){this._call&&(this._call=null,this._time=1/0,ri())}};var ii=function(e,t,n){var r=new Qr;return t=null==t?0:+t,r.restart((function(n){r.stop(),e(n+t)}),t,n),r},oi=function(e,t,n){var r=new Qr,i=t;return null==t?(r.restart(e,t,n),r):(t=+t,n=null==n?Xr():+n,r.restart((function o(a){a+=i,r.restart(o,i+=t,n),e(a)}),t,n),r)},ai=ze("start","end","interrupt"),ci=[],si=0,li=1,ui=2,fi=3,hi=4,di=5,pi=6,zi=function(e,t,n,r,i,o){var a=e.__transition;if(a){if(n in a)return}else e.__transition={};!function(e,t,n){var r,i=e.__transition;function o(s){var l,u,f,h;if(n.state!==li)return c();for(l in i)if((h=i[l]).name===n.name){if(h.state===fi)return ii(o);h.state===hi?(h.state=pi,h.timer.stop(),h.on.call("interrupt",e,e.__data__,h.index,h.group),delete i[l]):+lsi)throw new Error("too late; already scheduled");return n}function gi(e,t){var n=mi(e,t);if(n.state>ui)throw new Error("too late; already started");return n}function mi(e,t){var n=e.__transition;if(!n||!(n=n[t]))throw new Error("transition not found");return n}var yi=function(e,t){var n,r,i,o=e.__transition,a=!0;if(o){for(i in t=null==t?null:t+"",o)(n=o[i]).name===t?(r=n.state>ui&&n.state=0&&(e=e.slice(0,t)),!e||"start"===e}))}(t)?vi:gi;return function(){var a=o(this,e),c=a.on;c!==r&&(i=(r=c).copy()).on(t,n),a.on=i}}var Vi=Ht.prototype.constructor;function Ni(e,t,n){function r(){var r=this,i=t.apply(r,arguments);return i&&function(t){r.style.setProperty(e,i(t),n)}}return r._value=t,r}var Ii=0;function Ri(e,t,n,r){this._groups=e,this._parents=t,this._name=n,this._id=r}function Bi(e){return Ht().transition(e)}function Fi(){return++Ii}var Ui=Ht.prototype;function Wi(e){return+e}function Gi(e){return e*e}function Yi(e){return e*(2-e)}function Zi(e){return((e*=2)<=1?e*e:--e*(2-e)+1)/2}function $i(e){return e*e*e}function Xi(e){return--e*e*e+1}function Ki(e){return((e*=2)<=1?e*e*e:(e-=2)*e*e+2)/2}Ri.prototype=Bi.prototype={constructor:Ri,select:function(e){var t=this._name,n=this._id;"function"!==typeof e&&(e=Re(e));for(var r=this._groups,i=r.length,o=new Array(i),a=0;ali&&n.name===t)return new Ri([[e]],Lo,t,+r);return null},Po=function(e){return function(){return e}},Vo=function(e,t,n){this.target=e,this.type=t,this.selection=n};function No(){Oe.stopImmediatePropagation()}var Io=function(){Oe.preventDefault(),Oe.stopImmediatePropagation()},Ro={name:"drag"},Bo={name:"space"},Fo={name:"handle"},Uo={name:"center"},Wo={name:"x",handles:["e","w"].map(Jo),input:function(e,t){return e&&[[e[0],t[0][1]],[e[1],t[1][1]]]},output:function(e){return e&&[e[0][0],e[1][0]]}},Go={name:"y",handles:["n","s"].map(Jo),input:function(e,t){return e&&[[t[0][0],e[0]],[t[1][0],e[1]]]},output:function(e){return e&&[e[0][1],e[1][1]]}},Yo={name:"xy",handles:["n","e","s","w","nw","ne","se","sw"].map(Jo),input:function(e){return e},output:function(e){return e}},Zo={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},$o={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},Xo={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},Ko={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},Qo={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function Jo(e){return{type:e}}function ea(){return!Oe.button}function ta(){var e=this.ownerSVGElement||this;return[[0,0],[e.width.baseVal.value,e.height.baseVal.value]]}function na(e){for(;!e.__brush;)if(!(e=e.parentNode))return;return e.__brush}function ra(e){return e[0][0]===e[1][0]||e[0][1]===e[1][1]}function ia(e){var t=e.__brush;return t?t.dim.output(t.selection):null}function oa(){return sa(Wo)}function aa(){return sa(Go)}var ca=function(){return sa(Yo)};function sa(e){var t,n=ta,r=ea,i=ze(a,"start","brush","end"),o=6;function a(t){var n=t.property("__brush",f).selectAll(".overlay").data([Jo("overlay")]);n.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",Zo.overlay).merge(n).each((function(){var e=na(this).extent;Lt(this).attr("x",e[0][0]).attr("y",e[0][1]).attr("width",e[1][0]-e[0][0]).attr("height",e[1][1]-e[0][1])})),t.selectAll(".selection").data([Jo("selection")]).enter().append("rect").attr("class","selection").attr("cursor",Zo.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=t.selectAll(".handle").data(e.handles,(function(e){return e.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(e){return"handle handle--"+e.type})).attr("cursor",(function(e){return Zo[e.type]})),t.each(c).attr("fill","none").attr("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush touchstart.brush",u)}function c(){var e=Lt(this),t=na(this).selection;t?(e.selectAll(".selection").style("display",null).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1]),e.selectAll(".handle").style("display",null).attr("x",(function(e){return"e"===e.type[e.type.length-1]?t[1][0]-o/2:t[0][0]-o/2})).attr("y",(function(e){return"s"===e.type[0]?t[1][1]-o/2:t[0][1]-o/2})).attr("width",(function(e){return"n"===e.type||"s"===e.type?t[1][0]-t[0][0]+o:o})).attr("height",(function(e){return"e"===e.type||"w"===e.type?t[1][1]-t[0][1]+o:o}))):e.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function s(e,t){return e.__brush.emitter||new l(e,t)}function l(e,t){this.that=e,this.args=t,this.state=e.__brush,this.active=0}function u(){if(Oe.touches){if(Oe.changedTouches.length0&&(n=i-d),k<0?f=h-p:k>0&&(o=a-p),b=Bo,L.attr("cursor",Zo.selection),V());break;default:return}Io()}),!0).on("keyup.brush",(function(){switch(Oe.keyCode){case 16:O&&(v=g=O=!1,V());break;case 18:b===Uo&&(w<0?l=u:w>0&&(n=i),k<0?f=h:k>0&&(o=a),b=Fo,V());break;case 32:b===Bo&&(Oe.altKey?(w&&(l=u-d*w,n=i+d*w),k&&(f=h-p*k,o=a+p*k),b=Uo):(w<0?l=u:w>0&&(n=i),k<0?f=h:k>0&&(o=a),b=Fo),L.attr("cursor",Zo[y]),V());break;default:return}Io()}),!0).on("mousemove.brush",P,!0).on("mouseup.brush",N,!0);Rt(Oe.view)}No(),yi(m),c.call(m),A.start()}function P(){var e=Ne(m);!O||v||g||(Math.abs(e[0]-E[0])>Math.abs(e[1]-E[1])?g=!0:v=!0),E=e,z=!0,Io(),V()}function V(){var e;switch(d=E[0]-T[0],p=E[1]-T[1],b){case Bo:case Ro:w&&(d=Math.max(_-n,Math.min(q-l,d)),i=n+d,u=l+d),k&&(p=Math.max(C-o,Math.min(S-f,p)),a=o+p,h=f+p);break;case Fo:w<0?(d=Math.max(_-n,Math.min(q-n,d)),i=n+d,u=l):w>0&&(d=Math.max(_-l,Math.min(q-l,d)),i=n,u=l+d),k<0?(p=Math.max(C-o,Math.min(S-o,p)),a=o+p,h=f):k>0&&(p=Math.max(C-f,Math.min(S-f,p)),a=o,h=f+p);break;case Uo:w&&(i=Math.max(_,Math.min(q,n-d*w)),u=Math.max(_,Math.min(q,l+d*w))),k&&(a=Math.max(C,Math.min(S,o-p*k)),h=Math.max(C,Math.min(S,f+p*k)))}u1e-6)if(Math.abs(u*c-s*l)>1e-6&&i){var h=n-o,d=r-a,p=c*c+s*s,z=h*h+d*d,v=Math.sqrt(p),g=Math.sqrt(f),m=i*Math.tan((ya-Math.acos((p+f-z)/(2*v*g)))/2),y=m/g,b=m/v;Math.abs(y-1)>1e-6&&(this._+="L"+(e+y*l)+","+(t+y*u)),this._+="A"+i+","+i+",0,0,"+ +(u*h>l*d)+","+(this._x1=e+b*c)+","+(this._y1=t+b*s)}else this._+="L"+(this._x1=e)+","+(this._y1=t);else;},arc:function(e,t,n,r,i,o){e=+e,t=+t;var a=(n=+n)*Math.cos(r),c=n*Math.sin(r),s=e+a,l=t+c,u=1^o,f=o?r-i:i-r;if(n<0)throw new Error("negative radius: "+n);null===this._x1?this._+="M"+s+","+l:(Math.abs(this._x1-s)>1e-6||Math.abs(this._y1-l)>1e-6)&&(this._+="L"+s+","+l),n&&(f<0&&(f=f%ba+ba),f>wa?this._+="A"+n+","+n+",0,1,"+u+","+(e-a)+","+(t-c)+"A"+n+","+n+",0,1,"+u+","+(this._x1=s)+","+(this._y1=l):f>1e-6&&(this._+="A"+n+","+n+",0,"+ +(f>=ya)+","+u+","+(this._x1=e+n*Math.cos(i))+","+(this._y1=t+n*Math.sin(i))))},rect:function(e,t,n,r){this._+="M"+(this._x0=this._x1=+e)+","+(this._y0=this._y1=+t)+"h"+ +n+"v"+ +r+"h"+-n+"Z"},toString:function(){return this._}};var ja=xa;function Ma(e){return e.source}function _a(e){return e.target}function Ca(e){return e.radius}function qa(e){return e.startAngle}function Sa(e){return e.endAngle}var Oa=function(){var e=Ma,t=_a,n=Ca,r=qa,i=Sa,o=null;function a(){var a,c=ga.call(arguments),s=e.apply(this,c),l=t.apply(this,c),u=+n.apply(this,(c[0]=s,c)),f=r.apply(this,c)-ha,h=i.apply(this,c)-ha,d=u*la(f),p=u*ua(f),z=+n.apply(this,(c[0]=l,c)),v=r.apply(this,c)-ha,g=i.apply(this,c)-ha;if(o||(o=a=ja()),o.moveTo(d,p),o.arc(0,0,u,f,h),f===v&&h===g||(o.quadraticCurveTo(0,0,z*la(v),z*ua(v)),o.arc(0,0,z,v,g)),o.quadraticCurveTo(0,0,d,p),o.closePath(),a)return o=null,a+""||null}return a.radius=function(e){return arguments.length?(n="function"===typeof e?e:ma(+e),a):n},a.startAngle=function(e){return arguments.length?(r="function"===typeof e?e:ma(+e),a):r},a.endAngle=function(e){return arguments.length?(i="function"===typeof e?e:ma(+e),a):i},a.source=function(t){return arguments.length?(e=t,a):e},a.target=function(e){return arguments.length?(t=e,a):t},a.context=function(e){return arguments.length?(o=null==e?null:e,a):o},a};function Ta(){}function Ea(e,t){var n=new Ta;if(e instanceof Ta)e.each((function(e,t){n.set(t,e)}));else if(Array.isArray(e)){var r,i=-1,o=e.length;if(null==t)for(;++i=r.length)return null!=e&&n.sort(e),null!=t?t(n):n;for(var s,l,u,f=-1,h=n.length,d=r[i++],p=Aa(),z=a();++fr.length)return n;var a,c=i[o-1];return null!=t&&o>=r.length?a=n.entries():(a=[],n.each((function(t,n){a.push({key:n,values:e(t,o)})}))),null!=c?a.sort((function(e,t){return c(e.key,t.key)})):a}(o(e,0,Pa,Va),0)},key:function(e){return r.push(e),n},sortKeys:function(e){return i[r.length-1]=e,n},sortValues:function(t){return e=t,n},rollup:function(e){return t=e,n}}};function La(){return{}}function Da(e,t,n){e[t]=n}function Pa(){return Aa()}function Va(e,t,n){e.set(t,n)}function Na(){}var Ia=Aa.prototype;function Ra(e,t){var n=new Na;if(e instanceof Na)e.each((function(e){n.add(e)}));else if(e){var r=-1,i=e.length;if(null==t)for(;++r=o?s=!0:(r=e.charCodeAt(a++))===$a?l=!0:r===Xa&&(l=!0,e.charCodeAt(a)===$a&&++a),e.slice(i+1,t-1).replace(/""/g,'"')}for(;a=(o=(z+g)/2))?z=o:g=o,(u=n>=(a=(v+m)/2))?v=a:m=a,i=d,!(d=d[f=u<<1|l]))return i[f]=p,e;if(c=+e._x.call(null,d.data),s=+e._y.call(null,d.data),t===c&&n===s)return p.next=d,i?i[f]=p:e._root=p,e;do{i=i?i[f]=new Array(4):e._root=new Array(4),(l=t>=(o=(z+g)/2))?z=o:g=o,(u=n>=(a=(v+m)/2))?v=a:m=a}while((f=u<<1|l)===(h=(s>=a)<<1|c>=o));return i[h]=d,i[f]=p,e}var dc=function(e,t,n,r,i){this.node=e,this.x0=t,this.y0=n,this.x1=r,this.y1=i};function pc(e){return e[0]}function zc(e){return e[1]}function vc(e,t,n){var r=new gc(null==t?pc:t,null==n?zc:n,NaN,NaN,NaN,NaN);return null==e?r:r.addAll(e)}function gc(e,t,n,r,i,o){this._x=e,this._y=t,this._x0=n,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function mc(e){for(var t={data:e.data},n=t;e=e.next;)n=n.next={data:e.data};return t}var yc=vc.prototype=gc.prototype;function bc(e){return e.x+e.vx}function wc(e){return e.y+e.vy}yc.copy=function(){var e,t,n=new gc(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return n;if(!r.length)return n._root=mc(r),n;for(e=[{source:r,target:n._root=new Array(4)}];r=e.pop();)for(var i=0;i<4;++i)(t=r.source[i])&&(t.length?e.push({source:t,target:r.target[i]=new Array(4)}):r.target[i]=mc(t));return n},yc.add=function(e){var t=+this._x.call(null,e),n=+this._y.call(null,e);return hc(this.cover(t,n),t,n,e)},yc.addAll=function(e){var t,n,r,i,o=e.length,a=new Array(o),c=new Array(o),s=1/0,l=1/0,u=-1/0,f=-1/0;for(n=0;nu&&(u=r),if&&(f=i));for(ue||e>i||r>t||t>o))return this;var a,c,s=i-n,l=this._root;switch(c=(t<(r+o)/2)<<1|e<(n+i)/2){case 0:do{(a=new Array(4))[c]=l,l=a}while(o=r+(s*=2),e>(i=n+s)||t>o);break;case 1:do{(a=new Array(4))[c]=l,l=a}while(o=r+(s*=2),(n=i-s)>e||t>o);break;case 2:do{(a=new Array(4))[c]=l,l=a}while(r=o-(s*=2),e>(i=n+s)||r>t);break;case 3:do{(a=new Array(4))[c]=l,l=a}while(r=o-(s*=2),(n=i-s)>e||r>t)}this._root&&this._root.length&&(this._root=l)}return this._x0=n,this._y0=r,this._x1=i,this._y1=o,this},yc.data=function(){var e=[];return this.visit((function(t){if(!t.length)do{e.push(t.data)}while(t=t.next)})),e},yc.extent=function(e){return arguments.length?this.cover(+e[0][0],+e[0][1]).cover(+e[1][0],+e[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},yc.find=function(e,t,n){var r,i,o,a,c,s,l,u=this._x0,f=this._y0,h=this._x1,d=this._y1,p=[],z=this._root;for(z&&p.push(new dc(z,u,f,h,d)),null==n?n=1/0:(u=e-n,f=t-n,h=e+n,d=t+n,n*=n);s=p.pop();)if(!(!(z=s.node)||(i=s.x0)>h||(o=s.y0)>d||(a=s.x1)=g)<<1|e>=v)&&(s=p[p.length-1],p[p.length-1]=p[p.length-1-l],p[p.length-1-l]=s)}else{var m=e-+this._x.call(null,z.data),y=t-+this._y.call(null,z.data),b=m*m+y*y;if(b=(c=(p+v)/2))?p=c:v=c,(u=a>=(s=(z+g)/2))?z=s:g=s,t=d,!(d=d[f=u<<1|l]))return this;if(!d.length)break;(t[f+1&3]||t[f+2&3]||t[f+3&3])&&(n=t,h=f)}for(;d.data!==e;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):t?(i?t[f]=i:delete t[f],(d=t[0]||t[1]||t[2]||t[3])&&d===(t[3]||t[2]||t[1]||t[0])&&!d.length&&(n?n[h]=d:this._root=d),this):(this._root=i,this)},yc.removeAll=function(e){for(var t=0,n=e.length;ts+d||il+d||oc.index){var p=s-a.x-a.vx,z=l-a.y-a.vy,v=p*p+z*z;ve.r&&(e.r=e[t].r)}function c(){if(t){var r,i,o=t.length;for(n=new Array(o),r=0;r1?(null==n?c.remove(e):c.set(e,d(n)),t):c.get(e)},find:function(t,n,r){var i,o,a,c,s,l=0,u=e.length;for(null==r?r=1/0:r*=r,l=0;l1?(l.on(e,n),t):l.on(e)}}},Ec=function(){var e,t,n,r,i=uc(-30),o=1,a=1/0,c=.81;function s(r){var i,o=e.length,a=vc(e,_c,Cc).visitAfter(u);for(n=r,i=0;i=a)){(e.data!==t||e.next)&&(0===u&&(d+=(u=fc())*u),0===f&&(d+=(f=fc())*f),d1?r[0]+r.slice(2):r,+e.slice(n+1)]},Pc=function(e){return(e=Dc(Math.abs(e)))?e[1]:NaN},Vc=function(e,t){var n=Dc(e,t);if(!n)return e+"";var r=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")},Nc={"":function(e,t){e:for(var n,r=(e=e.toPrecision(t)).length,i=1,o=-1;i0&&(o=0)}return o>0?e.slice(0,o)+e.slice(n+1):e},"%":function(e,t){return(100*e).toFixed(t)},b:function(e){return Math.round(e).toString(2)},c:function(e){return e+""},d:function(e){return Math.round(e).toString(10)},e:function(e,t){return e.toExponential(t)},f:function(e,t){return e.toFixed(t)},g:function(e,t){return e.toPrecision(t)},o:function(e){return Math.round(e).toString(8)},p:function(e,t){return Vc(100*e,t)},r:Vc,s:function(e,t){var n=Dc(e,t);if(!n)return e+"";var r=n[0],i=n[1],o=i-(qc=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+Dc(e,Math.max(0,t+o-1))[0]},X:function(e){return Math.round(e).toString(16).toUpperCase()},x:function(e){return Math.round(e).toString(16)}},Ic=/^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i;function Rc(e){return new Bc(e)}function Bc(e){if(!(t=Ic.exec(e)))throw new Error("invalid format: "+e);var t,n=t[1]||" ",r=t[2]||">",i=t[3]||"-",o=t[4]||"",a=!!t[5],c=t[6]&&+t[6],s=!!t[7],l=t[8]&&+t[8].slice(1),u=t[9]||"";"n"===u?(s=!0,u="g"):Nc[u]||(u=""),(a||"0"===n&&"="===r)&&(a=!0,n="0",r="="),this.fill=n,this.align=r,this.sign=i,this.symbol=o,this.zero=a,this.width=c,this.comma=s,this.precision=l,this.type=u}Rc.prototype=Bc.prototype,Bc.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(null==this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(null==this.precision?"":"."+Math.max(0,0|this.precision))+this.type};var Fc,Uc,Wc,Gc=function(e){return e},Yc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"],Zc=function(e){var t,n,r=e.grouping&&e.thousands?(t=e.grouping,n=e.thousands,function(e,r){for(var i=e.length,o=[],a=0,c=t[0],s=0;i>0&&c>0&&(s+c+1>r&&(c=Math.max(1,r-s)),o.push(e.substring(i-=c,i+c)),!((s+=c+1)>r));)c=t[a=(a+1)%t.length];return o.reverse().join(n)}):Gc,i=e.currency,o=e.decimal,a=e.numerals?function(e){return function(t){return t.replace(/[0-9]/g,(function(t){return e[+t]}))}}(e.numerals):Gc,c=e.percent||"%";function s(e){var t=(e=Rc(e)).fill,n=e.align,s=e.sign,l=e.symbol,u=e.zero,f=e.width,h=e.comma,d=e.precision,p=e.type,z="$"===l?i[0]:"#"===l&&/[boxX]/.test(p)?"0"+p.toLowerCase():"",v="$"===l?i[1]:/[%p]/.test(p)?c:"",g=Nc[p],m=!p||/[defgprs%]/.test(p);function y(e){var i,c,l,y=z,b=v;if("c"===p)b=g(e)+b,e="";else{var w=(e=+e)<0;if(e=g(Math.abs(e),d),w&&0===+e&&(w=!1),y=(w?"("===s?s:"-":"-"===s||"("===s?"":s)+y,b=b+("s"===p?Yc[8+qc/3]:"")+(w&&"("===s?")":""),m)for(i=-1,c=e.length;++i(l=e.charCodeAt(i))||l>57){b=(46===l?o+e.slice(i+1):e.slice(i))+b,e=e.slice(0,i);break}}h&&!u&&(e=r(e,1/0));var k=y.length+e.length+b.length,x=k>1)+y+e+b+x.slice(k);break;default:e=x+y+e+b}return a(e)}return d=null==d?p?6:12:/[gprs]/.test(p)?Math.max(1,Math.min(21,d)):Math.max(0,Math.min(20,d)),y.toString=function(){return e+""},y}return{format:s,formatPrefix:function(e,t){var n=s(((e=Rc(e)).type="f",e)),r=3*Math.max(-8,Math.min(8,Math.floor(Pc(t)/3))),i=Math.pow(10,-r),o=Yc[8+r/3];return function(e){return n(i*e)+o}}}};function $c(e){return Fc=Zc(e),Uc=Fc.format,Wc=Fc.formatPrefix,Fc}$c({decimal:".",thousands:",",grouping:[3],currency:["$",""]});var Xc=function(e){return Math.max(0,-Pc(Math.abs(e)))},Kc=function(e,t){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Pc(t)/3)))-Pc(Math.abs(e)))},Qc=function(e,t){return e=Math.abs(e),t=Math.abs(t)-e,Math.max(0,Pc(t)-Pc(e))+1},Jc=function(){return new es};function es(){this.reset()}es.prototype={constructor:es,reset:function(){this.s=this.t=0},add:function(e){ns(ts,e,this.t),ns(this,ts.s,this.s),this.s?this.t+=ts.t:this.s=ts.t},valueOf:function(){return this.s}};var ts=new es;function ns(e,t,n){var r=e.s=t+n,i=r-t,o=r-i;e.t=t-o+(n-i)}var rs=1e-6,is=Math.PI,os=is/2,as=is/4,cs=2*is,ss=180/is,ls=is/180,us=Math.abs,fs=Math.atan,hs=Math.atan2,ds=Math.cos,ps=Math.ceil,zs=Math.exp,vs=(Math.floor,Math.log),gs=Math.pow,ms=Math.sin,ys=Math.sign||function(e){return e>0?1:e<0?-1:0},bs=Math.sqrt,ws=Math.tan;function ks(e){return e>1?0:e<-1?is:Math.acos(e)}function xs(e){return e>1?os:e<-1?-os:Math.asin(e)}function js(e){return(e=ms(e/2))*e}function Ms(){}function _s(e,t){e&&qs.hasOwnProperty(e.type)&&qs[e.type](e,t)}var Cs={Feature:function(e,t){_s(e.geometry,t)},FeatureCollection:function(e,t){for(var n=e.features,r=-1,i=n.length;++r=0?1:-1,i=r*n,o=ds(t=(t*=ls)/2+as),a=ms(t),c=Ls*a,s=Hs*o+c*ds(i),l=c*r*ms(i);Ps.add(hs(l,s)),As=e,Hs=o,Ls=a}var Us=function(e){return Vs.reset(),Ds(e,Ns),2*Vs};function Ws(e){return[hs(e[1],e[0]),xs(e[2])]}function Gs(e){var t=e[0],n=e[1],r=ds(n);return[r*ds(t),r*ms(t),ms(n)]}function Ys(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]}function Zs(e,t){return[e[1]*t[2]-e[2]*t[1],e[2]*t[0]-e[0]*t[2],e[0]*t[1]-e[1]*t[0]]}function $s(e,t){e[0]+=t[0],e[1]+=t[1],e[2]+=t[2]}function Xs(e,t){return[e[0]*t,e[1]*t,e[2]*t]}function Ks(e){var t=bs(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]);e[0]/=t,e[1]/=t,e[2]/=t}var Qs,Js,el,tl,nl,rl,il,ol,al,cl,sl=Jc(),ll={point:ul,lineStart:hl,lineEnd:dl,polygonStart:function(){ll.point=pl,ll.lineStart=zl,ll.lineEnd=vl,sl.reset(),Ns.polygonStart()},polygonEnd:function(){Ns.polygonEnd(),ll.point=ul,ll.lineStart=hl,ll.lineEnd=dl,Ps<0?(Qs=-(el=180),Js=-(tl=90)):sl>rs?tl=90:sl<-rs&&(Js=-90),cl[0]=Qs,cl[1]=el}};function ul(e,t){al.push(cl=[Qs=e,el=e]),ttl&&(tl=t)}function fl(e,t){var n=Gs([e*ls,t*ls]);if(ol){var r=Zs(ol,n),i=Zs([r[1],-r[0],0],r);Ks(i),i=Ws(i);var o,a=e-nl,c=a>0?1:-1,s=i[0]*ss*c,l=us(a)>180;l^(c*nltl&&(tl=o):l^(c*nl<(s=(s+360)%360-180)&&stl&&(tl=t)),l?egl(Qs,el)&&(el=e):gl(e,el)>gl(Qs,el)&&(Qs=e):el>=Qs?(eel&&(el=e)):e>nl?gl(Qs,e)>gl(Qs,el)&&(el=e):gl(e,el)>gl(Qs,el)&&(Qs=e)}else al.push(cl=[Qs=e,el=e]);ttl&&(tl=t),ol=n,nl=e}function hl(){ll.point=fl}function dl(){cl[0]=Qs,cl[1]=el,ll.point=ul,ol=null}function pl(e,t){if(ol){var n=e-nl;sl.add(us(n)>180?n+(n>0?360:-360):n)}else rl=e,il=t;Ns.point(e,t),fl(e,t)}function zl(){Ns.lineStart()}function vl(){pl(rl,il),Ns.lineEnd(),us(sl)>rs&&(Qs=-(el=180)),cl[0]=Qs,cl[1]=el,ol=null}function gl(e,t){return(t-=e)<0?t+360:t}function ml(e,t){return e[0]-t[0]}function yl(e,t){return e[0]<=e[1]?e[0]<=t&&t<=e[1]:tgl(r[0],r[1])&&(r[1]=i[1]),gl(i[0],r[1])>gl(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(a=-1/0,t=0,r=o[n=o.length-1];t<=n;r=i,++t)i=o[t],(c=gl(r[1],i[0]))>a&&(a=c,Qs=i[0],el=r[1])}return al=cl=null,Qs===1/0||Js===1/0?[[NaN,NaN],[NaN,NaN]]:[[Qs,Js],[el,tl]]},Pl={sphere:Ms,point:Vl,lineStart:Il,lineEnd:Fl,polygonStart:function(){Pl.lineStart=Ul,Pl.lineEnd=Wl},polygonEnd:function(){Pl.lineStart=Il,Pl.lineEnd=Fl}};function Vl(e,t){e*=ls;var n=ds(t*=ls);Nl(n*ds(e),n*ms(e),ms(t))}function Nl(e,t,n){++bl,kl+=(e-kl)/bl,xl+=(t-xl)/bl,jl+=(n-jl)/bl}function Il(){Pl.point=Rl}function Rl(e,t){e*=ls;var n=ds(t*=ls);Al=n*ds(e),Hl=n*ms(e),Ll=ms(t),Pl.point=Bl,Nl(Al,Hl,Ll)}function Bl(e,t){e*=ls;var n=ds(t*=ls),r=n*ds(e),i=n*ms(e),o=ms(t),a=hs(bs((a=Hl*o-Ll*i)*a+(a=Ll*r-Al*o)*a+(a=Al*i-Hl*r)*a),Al*r+Hl*i+Ll*o);wl+=a,Ml+=a*(Al+(Al=r)),_l+=a*(Hl+(Hl=i)),Cl+=a*(Ll+(Ll=o)),Nl(Al,Hl,Ll)}function Fl(){Pl.point=Vl}function Ul(){Pl.point=Gl}function Wl(){Yl(Tl,El),Pl.point=Vl}function Gl(e,t){Tl=e,El=t,e*=ls,t*=ls,Pl.point=Yl;var n=ds(t);Al=n*ds(e),Hl=n*ms(e),Ll=ms(t),Nl(Al,Hl,Ll)}function Yl(e,t){e*=ls;var n=ds(t*=ls),r=n*ds(e),i=n*ms(e),o=ms(t),a=Hl*o-Ll*i,c=Ll*r-Al*o,s=Al*i-Hl*r,l=bs(a*a+c*c+s*s),u=xs(l),f=l&&-u/l;ql+=f*a,Sl+=f*c,Ol+=f*s,wl+=u,Ml+=u*(Al+(Al=r)),_l+=u*(Hl+(Hl=i)),Cl+=u*(Ll+(Ll=o)),Nl(Al,Hl,Ll)}var Zl=function(e){bl=wl=kl=xl=jl=Ml=_l=Cl=ql=Sl=Ol=0,Ds(e,Pl);var t=ql,n=Sl,r=Ol,i=t*t+n*n+r*r;return i<1e-12&&(t=Ml,n=_l,r=Cl,wlis?e-cs:e<-is?e+cs:e,t]}function Ql(e,t,n){return(e%=cs)?t||n?Xl(eu(e),tu(t,n)):eu(e):t||n?tu(t,n):Kl}function Jl(e){return function(t,n){return[(t+=e)>is?t-cs:t<-is?t+cs:t,n]}}function eu(e){var t=Jl(e);return t.invert=Jl(-e),t}function tu(e,t){var n=ds(e),r=ms(e),i=ds(t),o=ms(t);function a(e,t){var a=ds(t),c=ds(e)*a,s=ms(e)*a,l=ms(t),u=l*n+c*r;return[hs(s*i-u*o,c*n-l*r),xs(u*i+s*o)]}return a.invert=function(e,t){var a=ds(t),c=ds(e)*a,s=ms(e)*a,l=ms(t),u=l*i-s*o;return[hs(s*i+l*o,c*n+u*r),xs(u*n-c*r)]},a}Kl.invert=Kl;var nu=function(e){function t(t){return(t=e(t[0]*ls,t[1]*ls))[0]*=ss,t[1]*=ss,t}return e=Ql(e[0]*ls,e[1]*ls,e.length>2?e[2]*ls:0),t.invert=function(t){return(t=e.invert(t[0]*ls,t[1]*ls))[0]*=ss,t[1]*=ss,t},t};function ru(e,t,n,r,i,o){if(n){var a=ds(t),c=ms(t),s=r*n;null==i?(i=t+r*cs,o=t-s/2):(i=iu(a,i),o=iu(a,o),(r>0?io)&&(i+=r*cs));for(var l,u=i;r>0?u>o:u1&&t.push(t.pop().concat(t.shift()))},result:function(){var n=t;return t=[],e=null,n}}},cu=function(e,t){return us(e[0]-t[0])=0;--o)i.point((u=l[o])[0],u[1]);else r(h.x,h.p.x,-1,i);h=h.p}l=(h=h.o).z,d=!d}while(!h.v);i.lineEnd()}}};function uu(e){if(t=e.length){for(var t,n,r=0,i=e[0];++r=0?1:-1,j=x*k,M=j>is,_=p*b;if(fu.add(hs(_*x*ms(j),z*w+_*ds(j))),o+=M?k+x*cs:k,M^h>=n^m>=n){var C=Zs(Gs(f),Gs(g));Ks(C);var q=Zs(i,C);Ks(q);var S=(M^k>=0?-1:1)*xs(q[2]);(r>S||r===S&&(C[0]||C[1]))&&(a+=M^k>=0?1:-1)}}return(o<-rs||o0){for(f||(i.polygonStart(),f=!0),i.lineStart(),e=0;e1&&2&s&&h.push(h.pop().concat(h.shift())),a.push(h.filter(pu))}return h}};function pu(e){return e.length>1}function zu(e,t){return((e=e.x)[0]<0?e[1]-os-rs:os-e[1])-((t=t.x)[0]<0?t[1]-os-rs:os-t[1])}var vu=du((function(){return!0}),(function(e){var t,n=NaN,r=NaN,i=NaN;return{lineStart:function(){e.lineStart(),t=1},point:function(o,a){var c=o>0?is:-is,s=us(o-n);us(s-is)0?os:-os),e.point(i,r),e.lineEnd(),e.lineStart(),e.point(c,r),e.point(o,r),t=0):i!==c&&s>=is&&(us(n-i)rs?fs((ms(t)*(o=ds(r))*ms(n)-ms(r)*(i=ds(t))*ms(e))/(i*o*a)):(t+r)/2}(n,r,o,a),e.point(i,r),e.lineEnd(),e.lineStart(),e.point(c,r),t=0),e.point(n=o,r=a),i=c},lineEnd:function(){e.lineEnd(),n=r=NaN},clean:function(){return 2-t}}}),(function(e,t,n,r){var i;if(null==e)i=n*os,r.point(-is,i),r.point(0,i),r.point(is,i),r.point(is,0),r.point(is,-i),r.point(0,-i),r.point(-is,-i),r.point(-is,0),r.point(-is,i);else if(us(e[0]-t[0])>rs){var o=e[0]0,i=us(t)>rs;function o(e,n){return ds(e)*ds(n)>t}function a(e,n,r){var i=[1,0,0],o=Zs(Gs(e),Gs(n)),a=Ys(o,o),c=o[0],s=a-c*c;if(!s)return!r&&e;var l=t*a/s,u=-t*c/s,f=Zs(i,o),h=Xs(i,l);$s(h,Xs(o,u));var d=f,p=Ys(h,d),z=Ys(d,d),v=p*p-z*(Ys(h,h)-1);if(!(v<0)){var g=bs(v),m=Xs(d,(-p-g)/z);if($s(m,h),m=Ws(m),!r)return m;var y,b=e[0],w=n[0],k=e[1],x=n[1];w0^m[1]<(us(m[0]-b)is^(b<=m[0]&&m[0]<=w)){var _=Xs(d,(-p+g)/z);return $s(_,h),[m,Ws(_)]}}}function c(t,n){var i=r?e:is-e,o=0;return t<-i?o|=1:t>i&&(o|=2),n<-i?o|=4:n>i&&(o|=8),o}return du(o,(function(e){var t,n,s,l,u;return{lineStart:function(){l=s=!1,u=1},point:function(f,h){var d,p=[f,h],z=o(f,h),v=r?z?0:c(f,h):z?c(f+(f<0?is:-is),h):0;if(!t&&(l=s=z)&&e.lineStart(),z!==s&&(!(d=a(t,p))||cu(t,d)||cu(p,d))&&(p[0]+=rs,p[1]+=rs,z=o(p[0],p[1])),z!==s)u=0,z?(e.lineStart(),d=a(p,t),e.point(d[0],d[1])):(d=a(t,p),e.point(d[0],d[1]),e.lineEnd()),t=d;else if(i&&t&&r^z){var g;v&n||!(g=a(p,t,!0))||(u=0,r?(e.lineStart(),e.point(g[0][0],g[0][1]),e.point(g[1][0],g[1][1]),e.lineEnd()):(e.point(g[1][0],g[1][1]),e.lineEnd(),e.lineStart(),e.point(g[0][0],g[0][1])))}!z||t&&cu(t,p)||e.point(p[0],p[1]),t=p,s=z,n=v},lineEnd:function(){s&&e.lineEnd(),t=null},clean:function(){return u|(l&&s)<<1}}}),(function(t,r,i,o){ru(o,e,n,i,t,r)}),r?[0,-e]:[-is,e-is])},mu=function(e,t,n,r,i,o){var a,c=e[0],s=e[1],l=0,u=1,f=t[0]-c,h=t[1]-s;if(a=n-c,f||!(a>0)){if(a/=f,f<0){if(a0){if(a>u)return;a>l&&(l=a)}if(a=i-c,f||!(a<0)){if(a/=f,f<0){if(a>u)return;a>l&&(l=a)}else if(f>0){if(a0)){if(a/=h,h<0){if(a0){if(a>u)return;a>l&&(l=a)}if(a=o-s,h||!(a<0)){if(a/=h,h<0){if(a>u)return;a>l&&(l=a)}else if(h>0){if(a0&&(e[0]=c+l*f,e[1]=s+l*h),u<1&&(t[0]=c+u*f,t[1]=s+u*h),!0}}}}},yu=1e9,bu=-yu;function wu(e,t,n,r){function i(i,o){return e<=i&&i<=n&&t<=o&&o<=r}function o(i,o,c,l){var u=0,f=0;if(null==i||(u=a(i,c))!==(f=a(o,c))||s(i,o)<0^c>0)do{l.point(0===u||3===u?e:n,u>1?r:t)}while((u=(u+c+4)%4)!==f);else l.point(o[0],o[1])}function a(r,i){return us(r[0]-e)0?0:3:us(r[0]-n)0?2:1:us(r[1]-t)0?1:0:i>0?3:2}function c(e,t){return s(e.x,t.x)}function s(e,t){var n=a(e,1),r=a(t,1);return n!==r?n-r:0===n?t[1]-e[1]:1===n?e[0]-t[0]:2===n?e[1]-t[1]:t[0]-e[0]}return function(a){var s,l,u,f,h,d,p,z,v,g,m,y=a,b=au(),w={point:k,lineStart:function(){w.point=x,l&&l.push(u=[]);g=!0,v=!1,p=z=NaN},lineEnd:function(){s&&(x(f,h),d&&v&&b.rejoin(),s.push(b.result()));w.point=k,v&&y.lineEnd()},polygonStart:function(){y=b,s=[],l=[],m=!0},polygonEnd:function(){var t=function(){for(var t=0,n=0,i=l.length;nr&&(h-o)*(r-a)>(d-a)*(e-o)&&++t:d<=r&&(h-o)*(r-a)<(d-a)*(e-o)&&--t;return t}(),n=m&&t,i=(s=P(s)).length;(n||i)&&(a.polygonStart(),n&&(a.lineStart(),o(null,null,1,a),a.lineEnd()),i&&lu(s,c,t,o,a),a.polygonEnd());y=a,s=l=u=null}};function k(e,t){i(e,t)&&y.point(e,t)}function x(o,a){var c=i(o,a);if(l&&u.push([o,a]),g)f=o,h=a,d=c,g=!1,c&&(y.lineStart(),y.point(o,a));else if(c&&v)y.point(o,a);else{var s=[p=Math.max(bu,Math.min(yu,p)),z=Math.max(bu,Math.min(yu,z))],b=[o=Math.max(bu,Math.min(yu,o)),a=Math.max(bu,Math.min(yu,a))];mu(s,b,e,t,n,r)?(v||(y.lineStart(),y.point(s[0],s[1])),y.point(b[0],b[1]),c||y.lineEnd(),m=!1):c&&(y.lineStart(),y.point(o,a),m=!1)}p=o,z=a,v=c}return w}}var ku,xu,ju,Mu=function(){var e,t,n,r=0,i=0,o=960,a=500;return n={stream:function(n){return e&&t===n?e:e=wu(r,i,o,a)(t=n)},extent:function(c){return arguments.length?(r=+c[0][0],i=+c[0][1],o=+c[1][0],a=+c[1][1],e=t=null,n):[[r,i],[o,a]]}}},_u=Jc(),Cu={sphere:Ms,point:Ms,lineStart:function(){Cu.point=Su,Cu.lineEnd=qu},lineEnd:Ms,polygonStart:Ms,polygonEnd:Ms};function qu(){Cu.point=Cu.lineEnd=Ms}function Su(e,t){ku=e*=ls,xu=ms(t*=ls),ju=ds(t),Cu.point=Ou}function Ou(e,t){e*=ls;var n=ms(t*=ls),r=ds(t),i=us(e-ku),o=ds(i),a=r*ms(i),c=ju*n-xu*r*o,s=xu*n+ju*r*o;_u.add(hs(bs(a*a+c*c),s)),ku=e,xu=n,ju=r}var Tu=function(e){return _u.reset(),Ds(e,Cu),+_u},Eu=[null,null],Au={type:"LineString",coordinates:Eu},Hu=function(e,t){return Eu[0]=e,Eu[1]=t,Tu(Au)},Lu={Feature:function(e,t){return Pu(e.geometry,t)},FeatureCollection:function(e,t){for(var n=e.features,r=-1,i=n.length;++rrs})).map(s)).concat(k(ps(o/d)*d,i,d).filter((function(e){return us(e%z)>rs})).map(l))}return g.lines=function(){return m().map((function(e){return{type:"LineString",coordinates:e}}))},g.outline=function(){return{type:"Polygon",coordinates:[u(r).concat(f(a).slice(1),u(n).reverse().slice(1),f(c).reverse().slice(1))]}},g.extent=function(e){return arguments.length?g.extentMajor(e).extentMinor(e):g.extentMinor()},g.extentMajor=function(e){return arguments.length?(r=+e[0][0],n=+e[1][0],c=+e[0][1],a=+e[1][1],r>n&&(e=r,r=n,n=e),c>a&&(e=c,c=a,a=e),g.precision(v)):[[r,c],[n,a]]},g.extentMinor=function(n){return arguments.length?(t=+n[0][0],e=+n[1][0],o=+n[0][1],i=+n[1][1],t>e&&(n=t,t=e,e=n),o>i&&(n=o,o=i,i=n),g.precision(v)):[[t,o],[e,i]]},g.step=function(e){return arguments.length?g.stepMajor(e).stepMinor(e):g.stepMinor()},g.stepMajor=function(e){return arguments.length?(p=+e[0],z=+e[1],g):[p,z]},g.stepMinor=function(e){return arguments.length?(h=+e[0],d=+e[1],g):[h,d]},g.precision=function(h){return arguments.length?(v=+h,s=Uu(o,i,90),l=Wu(t,e,v),u=Uu(c,a,90),f=Wu(r,n,v),g):v},g.extentMajor([[-180,-90+rs],[180,90-rs]]).extentMinor([[-180,-80-rs],[180,80+rs]])}function Yu(){return Gu()()}var Zu,$u,Xu,Ku,Qu=function(e,t){var n=e[0]*ls,r=e[1]*ls,i=t[0]*ls,o=t[1]*ls,a=ds(r),c=ms(r),s=ds(o),l=ms(o),u=a*ds(n),f=a*ms(n),h=s*ds(i),d=s*ms(i),p=2*xs(bs(js(o-r)+a*s*js(i-n))),z=ms(p),v=p?function(e){var t=ms(e*=p)/z,n=ms(p-e)/z,r=n*u+t*h,i=n*f+t*d,o=n*c+t*l;return[hs(i,r)*ss,hs(o,bs(r*r+i*i))*ss]}:function(){return[n*ss,r*ss]};return v.distance=p,v},Ju=function(e){return e},ef=Jc(),tf=Jc(),nf={point:Ms,lineStart:Ms,lineEnd:Ms,polygonStart:function(){nf.lineStart=rf,nf.lineEnd=cf},polygonEnd:function(){nf.lineStart=nf.lineEnd=nf.point=Ms,ef.add(us(tf)),tf.reset()},result:function(){var e=ef/2;return ef.reset(),e}};function rf(){nf.point=of}function of(e,t){nf.point=af,Zu=Xu=e,$u=Ku=t}function af(e,t){tf.add(Ku*e-Xu*t),Xu=e,Ku=t}function cf(){af(Zu,$u)}var sf=nf,lf=1/0,uf=lf,ff=-lf,hf=ff;var df,pf,zf,vf,gf={point:function(e,t){eff&&(ff=e);thf&&(hf=t)},lineStart:Ms,lineEnd:Ms,polygonStart:Ms,polygonEnd:Ms,result:function(){var e=[[lf,uf],[ff,hf]];return ff=hf=-(uf=lf=1/0),e}},mf=0,yf=0,bf=0,wf=0,kf=0,xf=0,jf=0,Mf=0,_f=0,Cf={point:qf,lineStart:Sf,lineEnd:Ef,polygonStart:function(){Cf.lineStart=Af,Cf.lineEnd=Hf},polygonEnd:function(){Cf.point=qf,Cf.lineStart=Sf,Cf.lineEnd=Ef},result:function(){var e=_f?[jf/_f,Mf/_f]:xf?[wf/xf,kf/xf]:bf?[mf/bf,yf/bf]:[NaN,NaN];return mf=yf=bf=wf=kf=xf=jf=Mf=_f=0,e}};function qf(e,t){mf+=e,yf+=t,++bf}function Sf(){Cf.point=Of}function Of(e,t){Cf.point=Tf,qf(zf=e,vf=t)}function Tf(e,t){var n=e-zf,r=t-vf,i=bs(n*n+r*r);wf+=i*(zf+e)/2,kf+=i*(vf+t)/2,xf+=i,qf(zf=e,vf=t)}function Ef(){Cf.point=qf}function Af(){Cf.point=Lf}function Hf(){Df(df,pf)}function Lf(e,t){Cf.point=Df,qf(df=zf=e,pf=vf=t)}function Df(e,t){var n=e-zf,r=t-vf,i=bs(n*n+r*r);wf+=i*(zf+e)/2,kf+=i*(vf+t)/2,xf+=i,jf+=(i=vf*e-zf*t)*(zf+e),Mf+=i*(vf+t),_f+=3*i,qf(zf=e,vf=t)}var Pf=Cf;function Vf(e){this._context=e}Vf.prototype={_radius:4.5,pointRadius:function(e){return this._radius=e,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(e,t){switch(this._point){case 0:this._context.moveTo(e,t),this._point=1;break;case 1:this._context.lineTo(e,t);break;default:this._context.moveTo(e+this._radius,t),this._context.arc(e,t,this._radius,0,cs)}},result:Ms};var Nf,If,Rf,Bf,Ff,Uf=Jc(),Wf={point:Ms,lineStart:function(){Wf.point=Gf},lineEnd:function(){Nf&&Yf(If,Rf),Wf.point=Ms},polygonStart:function(){Nf=!0},polygonEnd:function(){Nf=null},result:function(){var e=+Uf;return Uf.reset(),e}};function Gf(e,t){Wf.point=Yf,If=Bf=e,Rf=Ff=t}function Yf(e,t){Bf-=e,Ff-=t,Uf.add(bs(Bf*Bf+Ff*Ff)),Bf=e,Ff=t}var Zf=Wf;function $f(){this._string=[]}function Xf(e){return"m0,"+e+"a"+e+","+e+" 0 1,1 0,"+-2*e+"a"+e+","+e+" 0 1,1 0,"+2*e+"z"}$f.prototype={_radius:4.5,_circle:Xf(4.5),pointRadius:function(e){return(e=+e)!==this._radius&&(this._radius=e,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(e,t){switch(this._point){case 0:this._string.push("M",e,",",t),this._point=1;break;case 1:this._string.push("L",e,",",t);break;default:null==this._circle&&(this._circle=Xf(this._radius)),this._string.push("M",e,",",t,this._circle)}},result:function(){if(this._string.length){var e=this._string.join("");return this._string=[],e}return null}};var Kf=function(e,t){var n,r,i=4.5;function o(e){return e&&("function"===typeof i&&r.pointRadius(+i.apply(this,arguments)),Ds(e,n(r))),r.result()}return o.area=function(e){return Ds(e,n(sf)),sf.result()},o.measure=function(e){return Ds(e,n(Zf)),Zf.result()},o.bounds=function(e){return Ds(e,n(gf)),gf.result()},o.centroid=function(e){return Ds(e,n(Pf)),Pf.result()},o.projection=function(t){return arguments.length?(n=null==t?(e=null,Ju):(e=t).stream,o):e},o.context=function(e){return arguments.length?(r=null==e?(t=null,new $f):new Vf(t=e),"function"!==typeof i&&r.pointRadius(i),o):t},o.pointRadius=function(e){return arguments.length?(i="function"===typeof e?e:(r.pointRadius(+e),+e),o):i},o.projection(e).context(t)},Qf=function(e){return{stream:Jf(e)}};function Jf(e){return function(t){var n=new eh;for(var r in e)n[r]=e[r];return n.stream=t,n}}function eh(){}function th(e,t,n){var r=e.clipExtent&&e.clipExtent();return e.scale(150).translate([0,0]),null!=r&&e.clipExtent(null),Ds(n,e.stream(gf)),t(gf.result()),null!=r&&e.clipExtent(r),e}function nh(e,t,n){return th(e,(function(n){var r=t[1][0]-t[0][0],i=t[1][1]-t[0][1],o=Math.min(r/(n[1][0]-n[0][0]),i/(n[1][1]-n[0][1])),a=+t[0][0]+(r-o*(n[1][0]+n[0][0]))/2,c=+t[0][1]+(i-o*(n[1][1]+n[0][1]))/2;e.scale(150*o).translate([a,c])}),n)}function rh(e,t,n){return nh(e,[[0,0],t],n)}function ih(e,t,n){return th(e,(function(n){var r=+t,i=r/(n[1][0]-n[0][0]),o=(r-i*(n[1][0]+n[0][0]))/2,a=-i*n[0][1];e.scale(150*i).translate([o,a])}),n)}function oh(e,t,n){return th(e,(function(n){var r=+t,i=r/(n[1][1]-n[0][1]),o=-i*n[0][0],a=(r-i*(n[1][1]+n[0][1]))/2;e.scale(150*i).translate([o,a])}),n)}eh.prototype={constructor:eh,point:function(e,t){this.stream.point(e,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var ah=16,ch=ds(30*ls),sh=function(e,t){return+t?function(e,t){function n(r,i,o,a,c,s,l,u,f,h,d,p,z,v){var g=l-r,m=u-i,y=g*g+m*m;if(y>4*t&&z--){var b=a+h,w=c+d,k=s+p,x=bs(b*b+w*w+k*k),j=xs(k/=x),M=us(us(k)-1)t||us((g*S+m*O)/y-.5)>.3||a*h+c*d+s*p2?e[2]%360*ls:0,q()):[v*ss,g*ss,m*ss]},M.precision=function(e){return arguments.length?(j=sh(C,x=e*e),S()):bs(x)},M.fitExtent=function(e,t){return nh(M,e,t)},M.fitSize=function(e,t){return rh(M,e,t)},M.fitWidth=function(e,t){return ih(M,e,t)},M.fitHeight=function(e,t){return oh(M,e,t)},function(){return t=e.apply(this,arguments),M.invert=t.invert&&_,q()}}function hh(e){var t=0,n=is/3,r=fh(e),i=r(t,n);return i.parallels=function(e){return arguments.length?r(t=e[0]*ls,n=e[1]*ls):[t*ss,n*ss]},i}function dh(e,t){var n=ms(e),r=(n+ms(t))/2;if(us(r)=.12&&i<.234&&r>=-.425&&r<-.214?c:i>=.166&&i<.234&&r>=-.214&&r<-.115?s:a).invert(e)},u.stream=function(n){return e&&t===n?e:e=function(e){var t=e.length;return{point:function(n,r){for(var i=-1;++i0?t<-os+rs&&(t=-os+rs):t>os-rs&&(t=os-rs);var n=i/gs(_h(t),r);return[n*ms(r*e),i-n*ds(r*e)]}return o.invert=function(e,t){var n=i-t,o=ys(r)*bs(e*e+n*n);return[hs(e,us(n))/r*ys(n),2*fs(gs(i/o,1/r))-os]},o}var qh=function(){return hh(Ch).scale(109.5).parallels([30,30])};function Sh(e,t){return[e,t]}Sh.invert=Sh;var Oh=function(){return uh(Sh).scale(152.63)};function Th(e,t){var n=ds(e),r=e===t?ms(e):(n-ds(t))/(t-e),i=n/r+e;if(us(r)rs&&--i>0);return[e/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]};var Vh=function(){return uh(Ph).scale(175.295)};function Nh(e,t){return[ds(t)*ms(e),ms(t)]}Nh.invert=mh(xs);var Ih=function(){return uh(Nh).scale(249.5).clipAngle(90+rs)};function Rh(e,t){var n=ds(t),r=1+ds(e)*n;return[n*ms(e)/r,ms(t)/r]}Rh.invert=mh((function(e){return 2*fs(e)}));var Bh=function(){return uh(Rh).scale(250).clipAngle(142)};function Fh(e,t){return[vs(ws((os+t)/2)),-e]}Fh.invert=function(e,t){return[-t,2*fs(zs(e))-os]};var Uh=function(){var e=Mh(Fh),t=e.center,n=e.rotate;return e.center=function(e){return arguments.length?t([-e[1],e[0]]):[(e=t())[1],-e[0]]},e.rotate=function(e){return arguments.length?n([e[0],e[1],e.length>2?e[2]+90:90]):[(e=n())[0],e[1],e[2]-90]},n([0,0,90]).scale(159.155)};function Wh(e,t){return e.parent===t.parent?1:2}function Gh(e,t){return e+t.x}function Yh(e,t){return Math.max(e,t.y)}var Zh=function(){var e=Wh,t=1,n=1,r=!1;function i(i){var o,a=0;i.eachAfter((function(t){var n=t.children;n?(t.x=function(e){return e.reduce(Gh,0)/e.length}(n),t.y=function(e){return 1+e.reduce(Yh,0)}(n)):(t.x=o?a+=e(t,o):0,t.y=0,o=t)}));var c=function(e){for(var t;t=e.children;)e=t[0];return e}(i),s=function(e){for(var t;t=e.children;)e=t[t.length-1];return e}(i),l=c.x-e(c,s)/2,u=s.x+e(s,c)/2;return i.eachAfter(r?function(e){e.x=(e.x-i.x)*t,e.y=(i.y-e.y)*n}:function(e){e.x=(e.x-l)/(u-l)*t,e.y=(1-(i.y?e.y/i.y:1))*n})}return i.separation=function(t){return arguments.length?(e=t,i):e},i.size=function(e){return arguments.length?(r=!1,t=+e[0],n=+e[1],i):r?null:[t,n]},i.nodeSize=function(e){return arguments.length?(r=!0,t=+e[0],n=+e[1],i):r?[t,n]:null},i};function $h(e){var t=0,n=e.children,r=n&&n.length;if(r)for(;--r>=0;)t+=n[r].value;else t=1;e.value=t}function Xh(e,t){var n,r,i,o,a,c=new ed(e),s=+e.value&&(c.value=e.value),l=[c];for(null==t&&(t=Kh);n=l.pop();)if(s&&(n.value=+n.data.value),(i=t(n.data))&&(a=i.length))for(n.children=new Array(a),o=a-1;o>=0;--o)l.push(r=n.children[o]=new ed(i[o])),r.parent=n,r.depth=n.depth+1;return c.eachBefore(Jh)}function Kh(e){return e.children}function Qh(e){e.data=e.data.data}function Jh(e){var t=0;do{e.height=t}while((e=e.parent)&&e.height<++t)}function ed(e){this.data=e,this.depth=this.height=0,this.parent=null}ed.prototype=Xh.prototype={constructor:ed,count:function(){return this.eachAfter($h)},each:function(e){var t,n,r,i,o=this,a=[o];do{for(t=a.reverse(),a=[];o=t.pop();)if(e(o),n=o.children)for(r=0,i=n.length;r=0;--n)i.push(t[n]);return this},sum:function(e){return this.eachAfter((function(t){for(var n=+e(t.data)||0,r=t.children,i=r&&r.length;--i>=0;)n+=r[i].value;t.value=n}))},sort:function(e){return this.eachBefore((function(t){t.children&&t.children.sort(e)}))},path:function(e){for(var t=this,n=function(e,t){if(e===t)return e;var n=e.ancestors(),r=t.ancestors(),i=null;e=n.pop(),t=r.pop();for(;e===t;)i=e,e=n.pop(),t=r.pop();return i}(t,e),r=[t];t!==n;)t=t.parent,r.push(t);for(var i=r.length;e!==n;)r.splice(i,0,e),e=e.parent;return r},ancestors:function(){for(var e=this,t=[e];e=e.parent;)t.push(e);return t},descendants:function(){var e=[];return this.each((function(t){e.push(t)})),e},leaves:function(){var e=[];return this.eachBefore((function(t){t.children||e.push(t)})),e},links:function(){var e=this,t=[];return e.each((function(n){n!==e&&t.push({source:n.parent,target:n})})),t},copy:function(){return Xh(this).eachBefore(Qh)}};var td=Array.prototype.slice;var nd=function(e){for(var t,n,r=0,i=(e=function(e){for(var t,n,r=e.length;r;)n=Math.random()*r--|0,t=e[r],e[r]=e[n],e[n]=t;return e}(td.call(e))).length,o=[];r0&&n*n>r*r+i*i}function ad(e,t){for(var n=0;nn*n+r*r}function hd(e){var t=e._,n=e.next._,r=t.r+n.r,i=(t.x*n.r+n.x*t.r)/r,o=(t.y*n.r+n.y*t.r)/r;return i*i+o*o}function dd(e){this._=e,this.next=null,this.previous=null}function pd(e){if(!(i=e.length))return 0;var t,n,r,i,o,a,c,s,l,u,f;if((t=e[0]).x=0,t.y=0,!(i>1))return t.r;if(n=e[1],t.x=-n.r,n.x=t.r,n.y=0,!(i>2))return t.r+n.r;ud(n,t,r=e[2]),t=new dd(t),n=new dd(n),r=new dd(r),t.next=r.previous=n,n.next=t.previous=r,r.next=n.previous=t;e:for(c=3;c0)throw new Error("cycle");return o}return n.id=function(t){return arguments.length?(e=gd(t),n):e},n.parentId=function(e){return arguments.length?(t=gd(e),n):t},n};function Hd(e,t){return e.parent===t.parent?1:2}function Ld(e){var t=e.children;return t?t[0]:e.t}function Dd(e){var t=e.children;return t?t[t.length-1]:e.t}function Pd(e,t,n){var r=n/(t.i-e.i);t.c-=r,t.s+=n,e.c+=r,t.z+=n,t.m+=n}function Vd(e,t,n){return e.a.parent===t.parent?e.a:n}function Nd(e,t){this._=e,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=t}Nd.prototype=Object.create(ed.prototype);var Id=function(){var e=Hd,t=1,n=1,r=null;function i(i){var s=function(e){for(var t,n,r,i,o,a=new Nd(e,0),c=[a];t=c.pop();)if(r=t._.children)for(t.children=new Array(o=r.length),i=o-1;i>=0;--i)c.push(n=t.children[i]=new Nd(r[i],i)),n.parent=t;return(a.parent=new Nd(null,0)).children=[a],a}(i);if(s.eachAfter(o),s.parent.m=-s.z,s.eachBefore(a),r)i.eachBefore(c);else{var l=i,u=i,f=i;i.eachBefore((function(e){e.xu.x&&(u=e),e.depth>f.depth&&(f=e)}));var h=l===u?1:e(l,u)/2,d=h-l.x,p=t/(u.x+h+d),z=n/(f.depth||1);i.eachBefore((function(e){e.x=(e.x+d)*p,e.y=e.depth*z}))}return i}function o(t){var n=t.children,r=t.parent.children,i=t.i?r[t.i-1]:null;if(n){!function(e){for(var t,n=0,r=0,i=e.children,o=i.length;--o>=0;)(t=i[o]).z+=n,t.m+=n,n+=t.s+(r+=t.c)}(t);var o=(n[0].z+n[n.length-1].z)/2;i?(t.z=i.z+e(t._,i._),t.m=t.z-o):t.z=o}else i&&(t.z=i.z+e(t._,i._));t.parent.A=function(t,n,r){if(n){for(var i,o=t,a=t,c=n,s=o.parent.children[0],l=o.m,u=a.m,f=c.m,h=s.m;c=Dd(c),o=Ld(o),c&&o;)s=Ld(s),(a=Dd(a)).a=t,(i=c.z+f-o.z-l+e(c._,o._))>0&&(Pd(Vd(c,t,r),t,i),l+=i,u+=i),f+=c.m,l+=o.m,h+=s.m,u+=a.m;c&&!Dd(a)&&(a.t=c,a.m+=f-u),o&&!Ld(s)&&(s.t=o,s.m+=l-h,r=t)}return r}(t,i,t.parent.A||r[0])}function a(e){e._.x=e.z+e.parent.m,e.m+=e.parent.m}function c(e){e.x*=t,e.y=e.depth*n}return i.separation=function(t){return arguments.length?(e=t,i):e},i.size=function(e){return arguments.length?(r=!1,t=+e[0],n=+e[1],i):r?null:[t,n]},i.nodeSize=function(e){return arguments.length?(r=!0,t=+e[0],n=+e[1],i):r?[t,n]:null},i},Rd=function(e,t,n,r,i){for(var o,a=e.children,c=-1,s=a.length,l=e.value&&(i-n)/e.value;++ch&&(h=c),v=u*u*z,(d=Math.max(h/v,v/f))>p){u-=c;break}p=d}g.push(a={value:u,dice:s1?t:1)},n}(Bd),Wd=function(){var e=Ud,t=!1,n=1,r=1,i=[0],o=md,a=md,c=md,s=md,l=md;function u(e){return e.x0=e.y0=0,e.x1=n,e.y1=r,e.eachBefore(f),i=[0],t&&e.eachBefore(Md),e}function f(t){var n=i[t.depth],r=t.x0+n,u=t.y0+n,f=t.x1-n,h=t.y1-n;f=n-1){var u=c[t];return u.x0=i,u.y0=o,u.x1=a,void(u.y1=s)}var f=l[t],h=r/2+f,d=t+1,p=n-1;for(;d>>1;l[z]s-o){var m=(i*g+a*v)/r;e(t,d,v,i,o,m,s),e(d,n,g,m,o,a,s)}else{var y=(o*g+s*v)/r;e(t,d,v,i,o,a,y),e(d,n,g,i,y,a,s)}}(0,s,e.value,t,n,r,i)},Yd=function(e,t,n,r,i){(1&e.depth?Rd:_d)(e,t,n,r,i)},Zd=function e(t){function n(e,n,r,i,o){if((a=e._squarify)&&a.ratio===t)for(var a,c,s,l,u,f=-1,h=a.length,d=e.value;++f1?t:1)},n}(Bd),$d=function(e){for(var t,n=-1,r=e.length,i=e[r-1],o=0;++n1&&Kd(e[n[r-2]],e[n[r-1]],e[i])<=0;)--r;n[r++]=i}return n.slice(0,r)}var ep=function(e){if((n=e.length)<3)return null;var t,n,r=new Array(n),i=new Array(n);for(t=0;t=0;--t)l.push(e[r[o[t]][2]]);for(t=+c;tc!==l>c&&a<(s-n)*(c-r)/(l-r)+n&&(u=!u),s=n,l=r;return u},np=function(e){for(var t,n,r=-1,i=e.length,o=e[i-1],a=o[0],c=o[1],s=0;++r=0;)if((n=e._tasks[r])&&(e._tasks[r]=null,n.abort))try{n.abort()}catch(t){}e._active=NaN,lp(e)}function lp(e){if(!e._active&&e._call){var t=e._data;e._data=void 0,e._call(e._error,t)}}function up(e){if(null==e)e=1/0;else if(!((e=+e)>=1))throw new Error("invalid concurrency");return new op(e)}op.prototype=up.prototype={constructor:op,defer:function(e){if("function"!==typeof e)throw new Error("invalid callback");if(this._call)throw new Error("defer after await");if(null!=this._error)return this;var t=rp.call(arguments,1);return t.push(e),++this._waiting,this._tasks.push(t),ap(this),this},abort:function(){return null==this._error&&sp(this,new Error("abort")),this},await:function(e){if("function"!==typeof e)throw new Error("invalid callback");if(this._call)throw new Error("multiple await");return this._call=function(t,n){e.apply(null,[t].concat(n))},lp(this),this},awaitAll:function(e){if("function"!==typeof e)throw new Error("invalid callback");if(this._call)throw new Error("multiple await");return this._call=e,lp(this),this}};var fp=function(){return Math.random()},hp=function e(t){function n(e,n){return e=null==e?0:+e,n=null==n?1:+n,1===arguments.length?(n=e,e=0):n-=e,function(){return t()*n+e}}return n.source=e,n}(fp),dp=function e(t){function n(e,n){var r,i;return e=null==e?0:+e,n=null==n?1:+n,function(){var o;if(null!=r)o=r,r=null;else do{r=2*t()-1,o=2*t()-1,i=r*r+o*o}while(!i||i>1);return e+n*o*Math.sqrt(-2*Math.log(i)/i)}}return n.source=e,n}(fp),pp=function e(t){function n(){var e=dp.source(t).apply(this,arguments);return function(){return Math.exp(e())}}return n.source=e,n}(fp),zp=function e(t){function n(e){return function(){for(var n=0,r=0;r=200&&r<300||304===r){if(i)try{t=i.call(n,s)}catch(o){return void a.call("error",n,o)}else t=s;a.call("load",n,t)}else a.call("error",n,e)}if("undefined"===typeof XDomainRequest||"withCredentials"in s||!/^(http(s)?:)?\/\//.test(e)||(s=new XDomainRequest),"onload"in s?s.onload=s.onerror=s.ontimeout=h:s.onreadystatechange=function(e){s.readyState>3&&h(e)},s.onprogress=function(e){a.call("progress",n,e)},n={header:function(e,t){return e=(e+"").toLowerCase(),arguments.length<2?c.get(e):(null==t?c.remove(e):c.set(e,t+""),n)},mimeType:function(e){return arguments.length?(r=null==e?null:e+"",n):r},responseType:function(e){return arguments.length?(o=e,n):o},timeout:function(e){return arguments.length?(f=+e,n):f},user:function(e){return arguments.length<1?l:(l=null==e?null:e+"",n)},password:function(e){return arguments.length<1?u:(u=null==e?null:e+"",n)},response:function(e){return i=e,n},get:function(e,t){return n.send("GET",e,t)},post:function(e,t){return n.send("POST",e,t)},send:function(t,i,h){return s.open(t,e,!0,l,u),null==r||c.has("accept")||c.set("accept",r+",*/*"),s.setRequestHeader&&c.each((function(e,t){s.setRequestHeader(t,e)})),null!=r&&s.overrideMimeType&&s.overrideMimeType(r),null!=o&&(s.responseType=o),f>0&&(s.timeout=f),null==h&&"function"===typeof i&&(h=i,i=null),null!=h&&1===h.length&&(h=function(e){return function(t,n){e(null==t?n:null)}}(h)),null!=h&&n.on("error",h).on("load",(function(e){h(null,e)})),a.call("beforesend",n,s),s.send(null==i?null:i),n},abort:function(){return s.abort(),n},on:function(){var e=a.on.apply(a,arguments);return e===a?n:e}},null!=t){if("function"!==typeof t)throw new Error("invalid callback: "+t);return n.get(t)}return n};var yp=function(e,t){return function(n,r){var i=mp(n).mimeType(e).response(t);if(null!=r){if("function"!==typeof r)throw new Error("invalid callback: "+r);return i.get(r)}return i}},bp=yp("text/html",(function(e){return document.createRange().createContextualFragment(e.responseText)})),wp=yp("application/json",(function(e){return JSON.parse(e.responseText)})),kp=yp("text/plain",(function(e){return e.responseText})),xp=yp("application/xml",(function(e){var t=e.responseXML;if(!t)throw new Error("parse error");return t})),jp=function(e,t){return function(n,r,i){arguments.length<3&&(i=r,r=null);var o=mp(n).mimeType(e);return o.row=function(e){return arguments.length?o.response(Mp(t,r=e)):r},o.row(r),i?o.get(i):o}};function Mp(e,t){return function(n){return e(n.responseText,t)}}var _p=jp("text/csv",ec),Cp=jp("text/tab-separated-values",oc),qp=Array.prototype,Sp=qp.map,Op=qp.slice,Tp={name:"implicit"};function Ep(e){var t=Aa(),n=[],r=Tp;function i(i){var o=i+"",a=t.get(o);if(!a){if(r!==Tp)return r;t.set(o,a=n.push(i))}return e[(a-1)%e.length]}return e=null==e?[]:Op.call(e),i.domain=function(e){if(!arguments.length)return n.slice();n=[],t=Aa();for(var r,o,a=-1,c=e.length;++a2?Ip:Np,r=i=null,u}function u(t){return(r||(r=n(o,a,s?function(e){return function(t,n){var r=e(t=+t,n=+n);return function(e){return e<=t?0:e>=n?1:r(e)}}}(e):e,c)))(+t)}return u.invert=function(e){return(i||(i=n(a,o,Vp,s?function(e){return function(t,n){var r=e(t=+t,n=+n);return function(e){return e<=0?t:e>=1?n:r(e)}}}(t):t)))(+e)},u.domain=function(e){return arguments.length?(o=Sp.call(e,Dp),l()):o.slice()},u.range=function(e){return arguments.length?(a=Op.call(e),l()):a.slice()},u.rangeRound=function(e){return a=Op.call(e),c=mr,l()},u.clamp=function(e){return arguments.length?(s=!!e,l()):s},u.interpolate=function(e){return arguments.length?(c=e,l()):c},l()}var Fp=function(e,t,n){var r,i=e[0],o=e[e.length-1],a=q(i,o,null==t?10:t);switch((n=Rc(null==n?",f":n)).type){case"s":var c=Math.max(Math.abs(i),Math.abs(o));return null!=n.precision||isNaN(r=Kc(a,c))||(n.precision=r),Wc(n,c);case"":case"e":case"g":case"p":case"r":null!=n.precision||isNaN(r=Qc(a,Math.max(Math.abs(i),Math.abs(o))))||(n.precision=r-("e"===n.type));break;case"f":case"%":null!=n.precision||isNaN(r=Xc(a))||(n.precision=r-2*("%"===n.type))}return Uc(n)};function Up(e){var t=e.domain;return e.ticks=function(e){var n=t();return _(n[0],n[n.length-1],null==e?10:e)},e.tickFormat=function(e,n){return Fp(t(),e,n)},e.nice=function(n){null==n&&(n=10);var r,i=t(),o=0,a=i.length-1,c=i[o],s=i[a];return s0?r=C(c=Math.floor(c/r)*r,s=Math.ceil(s/r)*r,n):r<0&&(r=C(c=Math.ceil(c*r)/r,s=Math.floor(s*r)/r,n)),r>0?(i[o]=Math.floor(c/r)*r,i[a]=Math.ceil(s/r)*r,t(i)):r<0&&(i[o]=Math.ceil(c*r)/r,i[a]=Math.floor(s*r)/r,t(i)),e},e}function Wp(){var e=Bp(Vp,sr);return e.copy=function(){return Rp(e,Wp())},Up(e)}function Gp(){var e=[0,1];function t(e){return+e}return t.invert=t,t.domain=t.range=function(n){return arguments.length?(e=Sp.call(n,Dp),t):e.slice()},t.copy=function(){return Gp().domain(e)},Up(t)}var Yp=function(e,t){var n,r=0,i=(e=e.slice()).length-1,o=e[r],a=e[i];return a0){for(;hs)break;z.push(f)}}else for(;h=1;--u)if(!((f=l*u)s)break;z.push(f)}}else z=_(h,d,Math.min(d-h,p)).map(i);return o?z.reverse():z},e.tickFormat=function(t,o){if(null==o&&(o=10===n?".0e":","),"function"!==typeof o&&(o=Uc(o)),t===1/0)return o;null==t&&(t=10);var a=Math.max(1,n*t/e.ticks().length);return function(e){var t=e/i(Math.round(r(e)));return t*n0?n[i-1]:e[0],i=n?[r[n-1],t]:[r[a-1],r[a]]},o.copy=function(){return oz().domain([e,t]).range(i)},Up(o)}function az(){var e=[.5],t=[0,1],n=1;function r(r){if(r<=r)return t[s(e,r,0,n)]}return r.domain=function(i){return arguments.length?(e=Op.call(i),n=Math.min(e.length,t.length-1),r):e.slice()},r.range=function(i){return arguments.length?(t=Op.call(i),n=Math.min(e.length,t.length-1),r):t.slice()},r.invertExtent=function(n){var r=t.indexOf(n);return[e[r-1],e[r]]},r.copy=function(){return az().domain(e).range(t)},r}var cz=new Date,sz=new Date;function lz(e,t,n,r){function i(t){return e(t=new Date(+t)),t}return i.floor=i,i.ceil=function(n){return e(n=new Date(n-1)),t(n,1),e(n),n},i.round=function(e){var t=i(e),n=i.ceil(e);return e-t0))return c;do{c.push(a=new Date(+n)),t(n,o),e(n)}while(a=t)for(;e(t),!n(t);)t.setTime(t-1)}),(function(e,r){if(e>=e)if(r<0)for(;++r<=0;)for(;t(e,-1),!n(e););else for(;--r>=0;)for(;t(e,1),!n(e););}))},n&&(i.count=function(t,r){return cz.setTime(+t),sz.setTime(+r),e(cz),e(sz),Math.floor(n(cz,sz))},i.every=function(e){return e=Math.floor(e),isFinite(e)&&e>0?e>1?i.filter(r?function(t){return r(t)%e===0}:function(t){return i.count(0,t)%e===0}):i:null}),i}var uz=lz((function(){}),(function(e,t){e.setTime(+e+t)}),(function(e,t){return t-e}));uz.every=function(e){return e=Math.floor(e),isFinite(e)&&e>0?e>1?lz((function(t){t.setTime(Math.floor(t/e)*e)}),(function(t,n){t.setTime(+t+n*e)}),(function(t,n){return(n-t)/e})):uz:null};var fz=uz,hz=uz.range,dz=6e4,pz=6048e5,zz=lz((function(e){e.setTime(1e3*Math.floor(e/1e3))}),(function(e,t){e.setTime(+e+1e3*t)}),(function(e,t){return(t-e)/1e3}),(function(e){return e.getUTCSeconds()})),vz=zz,gz=zz.range,mz=lz((function(e){e.setTime(Math.floor(e/dz)*dz)}),(function(e,t){e.setTime(+e+t*dz)}),(function(e,t){return(t-e)/dz}),(function(e){return e.getMinutes()})),yz=mz,bz=mz.range,wz=lz((function(e){var t=e.getTimezoneOffset()*dz%36e5;t<0&&(t+=36e5),e.setTime(36e5*Math.floor((+e-t)/36e5)+t)}),(function(e,t){e.setTime(+e+36e5*t)}),(function(e,t){return(t-e)/36e5}),(function(e){return e.getHours()})),kz=wz,xz=wz.range,jz=lz((function(e){e.setHours(0,0,0,0)}),(function(e,t){e.setDate(e.getDate()+t)}),(function(e,t){return(t-e-(t.getTimezoneOffset()-e.getTimezoneOffset())*dz)/864e5}),(function(e){return e.getDate()-1})),Mz=jz,_z=jz.range;function Cz(e){return lz((function(t){t.setDate(t.getDate()-(t.getDay()+7-e)%7),t.setHours(0,0,0,0)}),(function(e,t){e.setDate(e.getDate()+7*t)}),(function(e,t){return(t-e-(t.getTimezoneOffset()-e.getTimezoneOffset())*dz)/pz}))}var qz=Cz(0),Sz=Cz(1),Oz=Cz(2),Tz=Cz(3),Ez=Cz(4),Az=Cz(5),Hz=Cz(6),Lz=qz.range,Dz=Sz.range,Pz=Oz.range,Vz=Tz.range,Nz=Ez.range,Iz=Az.range,Rz=Hz.range,Bz=lz((function(e){e.setDate(1),e.setHours(0,0,0,0)}),(function(e,t){e.setMonth(e.getMonth()+t)}),(function(e,t){return t.getMonth()-e.getMonth()+12*(t.getFullYear()-e.getFullYear())}),(function(e){return e.getMonth()})),Fz=Bz,Uz=Bz.range,Wz=lz((function(e){e.setMonth(0,1),e.setHours(0,0,0,0)}),(function(e,t){e.setFullYear(e.getFullYear()+t)}),(function(e,t){return t.getFullYear()-e.getFullYear()}),(function(e){return e.getFullYear()}));Wz.every=function(e){return isFinite(e=Math.floor(e))&&e>0?lz((function(t){t.setFullYear(Math.floor(t.getFullYear()/e)*e),t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,n){t.setFullYear(t.getFullYear()+n*e)})):null};var Gz=Wz,Yz=Wz.range,Zz=lz((function(e){e.setUTCSeconds(0,0)}),(function(e,t){e.setTime(+e+t*dz)}),(function(e,t){return(t-e)/dz}),(function(e){return e.getUTCMinutes()})),$z=Zz,Xz=Zz.range,Kz=lz((function(e){e.setUTCMinutes(0,0,0)}),(function(e,t){e.setTime(+e+36e5*t)}),(function(e,t){return(t-e)/36e5}),(function(e){return e.getUTCHours()})),Qz=Kz,Jz=Kz.range,ev=lz((function(e){e.setUTCHours(0,0,0,0)}),(function(e,t){e.setUTCDate(e.getUTCDate()+t)}),(function(e,t){return(t-e)/864e5}),(function(e){return e.getUTCDate()-1})),tv=ev,nv=ev.range;function rv(e){return lz((function(t){t.setUTCDate(t.getUTCDate()-(t.getUTCDay()+7-e)%7),t.setUTCHours(0,0,0,0)}),(function(e,t){e.setUTCDate(e.getUTCDate()+7*t)}),(function(e,t){return(t-e)/pz}))}var iv=rv(0),ov=rv(1),av=rv(2),cv=rv(3),sv=rv(4),lv=rv(5),uv=rv(6),fv=iv.range,hv=ov.range,dv=av.range,pv=cv.range,zv=sv.range,vv=lv.range,gv=uv.range,mv=lz((function(e){e.setUTCDate(1),e.setUTCHours(0,0,0,0)}),(function(e,t){e.setUTCMonth(e.getUTCMonth()+t)}),(function(e,t){return t.getUTCMonth()-e.getUTCMonth()+12*(t.getUTCFullYear()-e.getUTCFullYear())}),(function(e){return e.getUTCMonth()})),yv=mv,bv=mv.range,wv=lz((function(e){e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)}),(function(e,t){e.setUTCFullYear(e.getUTCFullYear()+t)}),(function(e,t){return t.getUTCFullYear()-e.getUTCFullYear()}),(function(e){return e.getUTCFullYear()}));wv.every=function(e){return isFinite(e=Math.floor(e))&&e>0?lz((function(t){t.setUTCFullYear(Math.floor(t.getUTCFullYear()/e)*e),t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n*e)})):null};var kv=wv,xv=wv.range;function jv(e){if(0<=e.y&&e.y<100){var t=new Date(-1,e.m,e.d,e.H,e.M,e.S,e.L);return t.setFullYear(e.y),t}return new Date(e.y,e.m,e.d,e.H,e.M,e.S,e.L)}function Mv(e){if(0<=e.y&&e.y<100){var t=new Date(Date.UTC(-1,e.m,e.d,e.H,e.M,e.S,e.L));return t.setUTCFullYear(e.y),t}return new Date(Date.UTC(e.y,e.m,e.d,e.H,e.M,e.S,e.L))}function _v(e){return{y:e,m:0,d:1,H:0,M:0,S:0,L:0}}function Cv(e){var t=e.dateTime,n=e.date,r=e.time,i=e.periods,o=e.days,a=e.shortDays,c=e.months,s=e.shortMonths,l=Nv(i),u=Iv(i),f=Nv(o),h=Iv(o),d=Nv(a),p=Iv(a),z=Nv(c),v=Iv(c),g=Nv(s),m=Iv(s),y={a:function(e){return a[e.getDay()]},A:function(e){return o[e.getDay()]},b:function(e){return s[e.getMonth()]},B:function(e){return c[e.getMonth()]},c:null,d:ag,e:ag,f:fg,H:cg,I:sg,j:lg,L:ug,m:hg,M:dg,p:function(e){return i[+(e.getHours()>=12)]},Q:Rg,s:Bg,S:pg,u:zg,U:vg,V:gg,w:mg,W:yg,x:null,X:null,y:bg,Y:wg,Z:kg,"%":Ig},b={a:function(e){return a[e.getUTCDay()]},A:function(e){return o[e.getUTCDay()]},b:function(e){return s[e.getUTCMonth()]},B:function(e){return c[e.getUTCMonth()]},c:null,d:xg,e:xg,f:qg,H:jg,I:Mg,j:_g,L:Cg,m:Sg,M:Og,p:function(e){return i[+(e.getUTCHours()>=12)]},Q:Rg,s:Bg,S:Tg,u:Eg,U:Ag,V:Hg,w:Lg,W:Dg,x:null,X:null,y:Pg,Y:Vg,Z:Ng,"%":Ig},w={a:function(e,t,n){var r=d.exec(t.slice(n));return r?(e.w=p[r[0].toLowerCase()],n+r[0].length):-1},A:function(e,t,n){var r=f.exec(t.slice(n));return r?(e.w=h[r[0].toLowerCase()],n+r[0].length):-1},b:function(e,t,n){var r=g.exec(t.slice(n));return r?(e.m=m[r[0].toLowerCase()],n+r[0].length):-1},B:function(e,t,n){var r=z.exec(t.slice(n));return r?(e.m=v[r[0].toLowerCase()],n+r[0].length):-1},c:function(e,n,r){return j(e,t,n,r)},d:Xv,e:Xv,f:ng,H:Qv,I:Qv,j:Kv,L:tg,m:$v,M:Jv,p:function(e,t,n){var r=l.exec(t.slice(n));return r?(e.p=u[r[0].toLowerCase()],n+r[0].length):-1},Q:ig,s:og,S:eg,u:Bv,U:Fv,V:Uv,w:Rv,W:Wv,x:function(e,t,r){return j(e,n,t,r)},X:function(e,t,n){return j(e,r,t,n)},y:Yv,Y:Gv,Z:Zv,"%":rg};function k(e,t){return function(n){var r,i,o,a=[],c=-1,s=0,l=e.length;for(n instanceof Date||(n=new Date(+n));++c53)return null;"w"in o||(o.w=1),"Z"in o?(i=(r=Mv(_v(o.y))).getUTCDay(),r=i>4||0===i?ov.ceil(r):ov(r),r=tv.offset(r,7*(o.V-1)),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(i=(r=t(_v(o.y))).getDay(),r=i>4||0===i?Sz.ceil(r):Sz(r),r=Mz.offset(r,7*(o.V-1)),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:"W"in o?1:0),i="Z"in o?Mv(_v(o.y)).getUTCDay():t(_v(o.y)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,Mv(o)):t(o)}}function j(e,t,n,r){for(var i,o,a=0,c=t.length,s=n.length;a=s)return-1;if(37===(i=t.charCodeAt(a++))){if(i=t.charAt(a++),!(o=w[i in Av?t.charAt(a++):i])||(r=o(e,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}return(y.x=k(n,y),y.X=k(r,y),y.c=k(t,y),b.x=k(n,b),b.X=k(r,b),b.c=k(t,b),{format:function(e){var t=k(e+="",y);return t.toString=function(){return e},t},parse:function(e){var t=x(e+="",jv);return t.toString=function(){return e},t},utcFormat:function(e){var t=k(e+="",b);return t.toString=function(){return e},t},utcParse:function(e){var t=x(e,Mv);return t.toString=function(){return e},t}})}var qv,Sv,Ov,Tv,Ev,Av={"-":"",_:" ",0:"0"},Hv=/^\s*\d+/,Lv=/^%/,Dv=/[\\^$*+?|[\]().{}]/g;function Pv(e,t,n){var r=e<0?"-":"",i=(r?-e:e)+"",o=i.length;return r+(o68?1900:2e3),n+r[0].length):-1}function Zv(e,t,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(t.slice(n,n+6));return r?(e.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function $v(e,t,n){var r=Hv.exec(t.slice(n,n+2));return r?(e.m=r[0]-1,n+r[0].length):-1}function Xv(e,t,n){var r=Hv.exec(t.slice(n,n+2));return r?(e.d=+r[0],n+r[0].length):-1}function Kv(e,t,n){var r=Hv.exec(t.slice(n,n+3));return r?(e.m=0,e.d=+r[0],n+r[0].length):-1}function Qv(e,t,n){var r=Hv.exec(t.slice(n,n+2));return r?(e.H=+r[0],n+r[0].length):-1}function Jv(e,t,n){var r=Hv.exec(t.slice(n,n+2));return r?(e.M=+r[0],n+r[0].length):-1}function eg(e,t,n){var r=Hv.exec(t.slice(n,n+2));return r?(e.S=+r[0],n+r[0].length):-1}function tg(e,t,n){var r=Hv.exec(t.slice(n,n+3));return r?(e.L=+r[0],n+r[0].length):-1}function ng(e,t,n){var r=Hv.exec(t.slice(n,n+6));return r?(e.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function rg(e,t,n){var r=Lv.exec(t.slice(n,n+1));return r?n+r[0].length:-1}function ig(e,t,n){var r=Hv.exec(t.slice(n));return r?(e.Q=+r[0],n+r[0].length):-1}function og(e,t,n){var r=Hv.exec(t.slice(n));return r?(e.Q=1e3*+r[0],n+r[0].length):-1}function ag(e,t){return Pv(e.getDate(),t,2)}function cg(e,t){return Pv(e.getHours(),t,2)}function sg(e,t){return Pv(e.getHours()%12||12,t,2)}function lg(e,t){return Pv(1+Mz.count(Gz(e),e),t,3)}function ug(e,t){return Pv(e.getMilliseconds(),t,3)}function fg(e,t){return ug(e,t)+"000"}function hg(e,t){return Pv(e.getMonth()+1,t,2)}function dg(e,t){return Pv(e.getMinutes(),t,2)}function pg(e,t){return Pv(e.getSeconds(),t,2)}function zg(e){var t=e.getDay();return 0===t?7:t}function vg(e,t){return Pv(qz.count(Gz(e),e),t,2)}function gg(e,t){var n=e.getDay();return e=n>=4||0===n?Ez(e):Ez.ceil(e),Pv(Ez.count(Gz(e),e)+(4===Gz(e).getDay()),t,2)}function mg(e){return e.getDay()}function yg(e,t){return Pv(Sz.count(Gz(e),e),t,2)}function bg(e,t){return Pv(e.getFullYear()%100,t,2)}function wg(e,t){return Pv(e.getFullYear()%1e4,t,4)}function kg(e){var t=e.getTimezoneOffset();return(t>0?"-":(t*=-1,"+"))+Pv(t/60|0,"0",2)+Pv(t%60,"0",2)}function xg(e,t){return Pv(e.getUTCDate(),t,2)}function jg(e,t){return Pv(e.getUTCHours(),t,2)}function Mg(e,t){return Pv(e.getUTCHours()%12||12,t,2)}function _g(e,t){return Pv(1+tv.count(kv(e),e),t,3)}function Cg(e,t){return Pv(e.getUTCMilliseconds(),t,3)}function qg(e,t){return Cg(e,t)+"000"}function Sg(e,t){return Pv(e.getUTCMonth()+1,t,2)}function Og(e,t){return Pv(e.getUTCMinutes(),t,2)}function Tg(e,t){return Pv(e.getUTCSeconds(),t,2)}function Eg(e){var t=e.getUTCDay();return 0===t?7:t}function Ag(e,t){return Pv(iv.count(kv(e),e),t,2)}function Hg(e,t){var n=e.getUTCDay();return e=n>=4||0===n?sv(e):sv.ceil(e),Pv(sv.count(kv(e),e)+(4===kv(e).getUTCDay()),t,2)}function Lg(e){return e.getUTCDay()}function Dg(e,t){return Pv(ov.count(kv(e),e),t,2)}function Pg(e,t){return Pv(e.getUTCFullYear()%100,t,2)}function Vg(e,t){return Pv(e.getUTCFullYear()%1e4,t,4)}function Ng(){return"+0000"}function Ig(){return"%"}function Rg(e){return+e}function Bg(e){return Math.floor(+e/1e3)}function Fg(e){return qv=Cv(e),Sv=qv.format,Ov=qv.parse,Tv=qv.utcFormat,Ev=qv.utcParse,qv}Fg({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var Ug=Date.prototype.toISOString?function(e){return e.toISOString()}:Tv("%Y-%m-%dT%H:%M:%S.%LZ");var Wg=+new Date("2000-01-01T00:00:00.000Z")?function(e){var t=new Date(e);return isNaN(t)?null:t}:Ev("%Y-%m-%dT%H:%M:%S.%LZ"),Gg=1e3,Yg=60*Gg,Zg=60*Yg,$g=24*Zg,Xg=7*$g,Kg=30*$g,Qg=365*$g;function Jg(e){return new Date(e)}function em(e){return e instanceof Date?+e:+new Date(+e)}function tm(e,t,n,r,o,a,c,s,l){var u=Bp(Vp,sr),f=u.invert,h=u.domain,d=l(".%L"),p=l(":%S"),z=l("%I:%M"),v=l("%I %p"),g=l("%a %d"),m=l("%b %d"),y=l("%B"),b=l("%Y"),w=[[c,1,Gg],[c,5,5*Gg],[c,15,15*Gg],[c,30,30*Gg],[a,1,Yg],[a,5,5*Yg],[a,15,15*Yg],[a,30,30*Yg],[o,1,Zg],[o,3,3*Zg],[o,6,6*Zg],[o,12,12*Zg],[r,1,$g],[r,2,2*$g],[n,1,Xg],[t,1,Kg],[t,3,3*Kg],[e,1,Qg]];function k(i){return(c(i)1)&&(e-=Math.floor(e));var t=Math.abs(e-.5);return hm.h=360*e-100,hm.s=1.5-1.5*t,hm.l=.8-.9*t,hm+""};function pm(e){var t=e.length;return function(n){return e[Math.max(0,Math.min(t-1,Math.floor(n*t)))]}}var zm=pm(im("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),vm=pm(im("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),gm=pm(im("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),mm=pm(im("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function ym(e){var t=0,n=1,r=!1;function i(i){var o=(i-t)/(n-t);return e(r?Math.max(0,Math.min(1,o)):o)}return i.domain=function(e){return arguments.length?(t=+e[0],n=+e[1],i):[t,n]},i.clamp=function(e){return arguments.length?(r=!!e,i):r},i.interpolator=function(t){return arguments.length?(e=t,i):e},i.copy=function(){return ym(e).domain([t,n]).clamp(r)},Up(i)}var bm=function(e){return function(){return e}},wm=Math.abs,km=Math.atan2,xm=Math.cos,jm=Math.max,Mm=Math.min,_m=Math.sin,Cm=Math.sqrt,qm=1e-12,Sm=Math.PI,Om=Sm/2,Tm=2*Sm;function Em(e){return e>=1?Om:e<=-1?-Om:Math.asin(e)}function Am(e){return e.innerRadius}function Hm(e){return e.outerRadius}function Lm(e){return e.startAngle}function Dm(e){return e.endAngle}function Pm(e){return e&&e.padAngle}function Vm(e,t,n,r,i,o,a){var c=e-n,s=t-r,l=(a?o:-o)/Cm(c*c+s*s),u=l*s,f=-l*c,h=e+u,d=t+f,p=n+u,z=r+f,v=(h+p)/2,g=(d+z)/2,m=p-h,y=z-d,b=m*m+y*y,w=i-o,k=h*z-p*d,x=(y<0?-1:1)*Cm(jm(0,w*w*b-k*k)),j=(k*y-m*x)/b,M=(-k*m-y*x)/b,_=(k*y+m*x)/b,C=(-k*m+y*x)/b,q=j-v,S=M-g,O=_-v,T=C-g;return q*q+S*S>O*O+T*T&&(j=_,M=C),{cx:j,cy:M,x01:-u,y01:-f,x11:j*(i/w-1),y11:M*(i/w-1)}}var Nm=function(){var e=Am,t=Hm,n=bm(0),r=null,i=Lm,o=Dm,a=Pm,c=null;function s(){var s,l,u,f=+e.apply(this,arguments),h=+t.apply(this,arguments),d=i.apply(this,arguments)-Om,p=o.apply(this,arguments)-Om,z=wm(p-d),v=p>d;if(c||(c=s=ja()),hqm)if(z>Tm-qm)c.moveTo(h*xm(d),h*_m(d)),c.arc(0,0,h,d,p,!v),f>qm&&(c.moveTo(f*xm(p),f*_m(p)),c.arc(0,0,f,p,d,v));else{var g,m,y=d,b=p,w=d,k=p,x=z,j=z,M=a.apply(this,arguments)/2,_=M>qm&&(r?+r.apply(this,arguments):Cm(f*f+h*h)),C=Mm(wm(h-f)/2,+n.apply(this,arguments)),q=C,S=C;if(_>qm){var O=Em(_/f*_m(M)),T=Em(_/h*_m(M));(x-=2*O)>qm?(w+=O*=v?1:-1,k-=O):(x=0,w=k=(d+p)/2),(j-=2*T)>qm?(y+=T*=v?1:-1,b-=T):(j=0,y=b=(d+p)/2)}var E=h*xm(y),A=h*_m(y),H=f*xm(k),L=f*_m(k);if(C>qm){var D=h*xm(b),P=h*_m(b),V=f*xm(w),N=f*_m(w);if(zqm?function(e,t,n,r,i,o,a,c){var s=n-e,l=r-t,u=a-i,f=c-o,h=(u*(t-o)-f*(e-i))/(f*s-u*l);return[e+h*s,t+h*l]}(E,A,V,N,D,P,H,L):[H,L],R=E-I[0],B=A-I[1],F=D-I[0],U=P-I[1],W=1/_m(((u=(R*F+B*U)/(Cm(R*R+B*B)*Cm(F*F+U*U)))>1?0:u<-1?Sm:Math.acos(u))/2),G=Cm(I[0]*I[0]+I[1]*I[1]);q=Mm(C,(f-G)/(W-1)),S=Mm(C,(h-G)/(W+1))}}j>qm?S>qm?(g=Vm(V,N,E,A,h,S,v),m=Vm(D,P,H,L,h,S,v),c.moveTo(g.cx+g.x01,g.cy+g.y01),Sqm&&x>qm?q>qm?(g=Vm(H,L,D,P,f,-q,v),m=Vm(E,A,V,N,f,-q,v),c.lineTo(g.cx+g.x01,g.cy+g.y01),q=u;--f)c.point(v[f],g[f]);c.lineEnd(),c.areaEnd()}z&&(v[l]=+e(h,l,s),g[l]=+n(h,l,s),c.point(t?+t(h,l,s):v[l],r?+r(h,l,s):g[l]))}if(d)return c=null,d+""||null}function l(){return Um().defined(i).curve(a).context(o)}return s.x=function(n){return arguments.length?(e="function"===typeof n?n:bm(+n),t=null,s):e},s.x0=function(t){return arguments.length?(e="function"===typeof t?t:bm(+t),s):e},s.x1=function(e){return arguments.length?(t=null==e?null:"function"===typeof e?e:bm(+e),s):t},s.y=function(e){return arguments.length?(n="function"===typeof e?e:bm(+e),r=null,s):n},s.y0=function(e){return arguments.length?(n="function"===typeof e?e:bm(+e),s):n},s.y1=function(e){return arguments.length?(r=null==e?null:"function"===typeof e?e:bm(+e),s):r},s.lineX0=s.lineY0=function(){return l().x(e).y(n)},s.lineY1=function(){return l().x(e).y(r)},s.lineX1=function(){return l().x(t).y(n)},s.defined=function(e){return arguments.length?(i="function"===typeof e?e:bm(!!e),s):i},s.curve=function(e){return arguments.length?(a=e,null!=o&&(c=a(o)),s):a},s.context=function(e){return arguments.length?(null==e?o=c=null:c=a(o=e),s):o},s},Gm=function(e,t){return te?1:t>=e?0:NaN},Ym=function(e){return e},Zm=function(){var e=Ym,t=Gm,n=null,r=bm(0),i=bm(Tm),o=bm(0);function a(a){var c,s,l,u,f,h=a.length,d=0,p=new Array(h),z=new Array(h),v=+r.apply(this,arguments),g=Math.min(Tm,Math.max(-Tm,i.apply(this,arguments)-v)),m=Math.min(Math.abs(g)/h,o.apply(this,arguments)),y=m*(g<0?-1:1);for(c=0;c0&&(d+=f);for(null!=t?p.sort((function(e,n){return t(z[e],z[n])})):null!=n&&p.sort((function(e,t){return n(a[e],a[t])})),c=0,l=d?(g-h*y)/d:0;c0?f*l:0)+y,z[s]={data:a[s],index:c,value:f,startAngle:v,endAngle:u,padAngle:m};return z}return a.value=function(t){return arguments.length?(e="function"===typeof t?t:bm(+t),a):e},a.sortValues=function(e){return arguments.length?(t=e,n=null,a):t},a.sort=function(e){return arguments.length?(n=e,t=null,a):n},a.startAngle=function(e){return arguments.length?(r="function"===typeof e?e:bm(+e),a):r},a.endAngle=function(e){return arguments.length?(i="function"===typeof e?e:bm(+e),a):i},a.padAngle=function(e){return arguments.length?(o="function"===typeof e?e:bm(+e),a):o},a},$m=Km(Rm);function Xm(e){this._curve=e}function Km(e){function t(t){return new Xm(e(t))}return t._curve=e,t}function Qm(e){var t=e.curve;return e.angle=e.x,delete e.x,e.radius=e.y,delete e.y,e.curve=function(e){return arguments.length?t(Km(e)):t()._curve},e}Xm.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(e,t){this._curve.point(t*Math.sin(e),t*-Math.cos(e))}};var Jm=function(){return Qm(Um().curve($m))},ey=function(){var e=Wm().curve($m),t=e.curve,n=e.lineX0,r=e.lineX1,i=e.lineY0,o=e.lineY1;return e.angle=e.x,delete e.x,e.startAngle=e.x0,delete e.x0,e.endAngle=e.x1,delete e.x1,e.radius=e.y,delete e.y,e.innerRadius=e.y0,delete e.y0,e.outerRadius=e.y1,delete e.y1,e.lineStartAngle=function(){return Qm(n())},delete e.lineX0,e.lineEndAngle=function(){return Qm(r())},delete e.lineX1,e.lineInnerRadius=function(){return Qm(i())},delete e.lineY0,e.lineOuterRadius=function(){return Qm(o())},delete e.lineY1,e.curve=function(e){return arguments.length?t(Km(e)):t()._curve},e},ty=function(e,t){return[(t=+t)*Math.cos(e-=Math.PI/2),t*Math.sin(e)]},ny=Array.prototype.slice;function ry(e){return e.source}function iy(e){return e.target}function oy(e){var t=ry,n=iy,r=Bm,i=Fm,o=null;function a(){var a,c=ny.call(arguments),s=t.apply(this,c),l=n.apply(this,c);if(o||(o=a=ja()),e(o,+r.apply(this,(c[0]=s,c)),+i.apply(this,c),+r.apply(this,(c[0]=l,c)),+i.apply(this,c)),a)return o=null,a+""||null}return a.source=function(e){return arguments.length?(t=e,a):t},a.target=function(e){return arguments.length?(n=e,a):n},a.x=function(e){return arguments.length?(r="function"===typeof e?e:bm(+e),a):r},a.y=function(e){return arguments.length?(i="function"===typeof e?e:bm(+e),a):i},a.context=function(e){return arguments.length?(o=null==e?null:e,a):o},a}function ay(e,t,n,r,i){e.moveTo(t,n),e.bezierCurveTo(t=(t+r)/2,n,t,i,r,i)}function cy(e,t,n,r,i){e.moveTo(t,n),e.bezierCurveTo(t,n=(n+i)/2,r,n,r,i)}function sy(e,t,n,r,i){var o=ty(t,n),a=ty(t,n=(n+i)/2),c=ty(r,n),s=ty(r,i);e.moveTo(o[0],o[1]),e.bezierCurveTo(a[0],a[1],c[0],c[1],s[0],s[1])}function ly(){return oy(ay)}function uy(){return oy(cy)}function fy(){var e=oy(sy);return e.angle=e.x,delete e.x,e.radius=e.y,delete e.y,e}var hy={draw:function(e,t){var n=Math.sqrt(t/Sm);e.moveTo(n,0),e.arc(0,0,n,0,Tm)}},dy={draw:function(e,t){var n=Math.sqrt(t/5)/2;e.moveTo(-3*n,-n),e.lineTo(-n,-n),e.lineTo(-n,-3*n),e.lineTo(n,-3*n),e.lineTo(n,-n),e.lineTo(3*n,-n),e.lineTo(3*n,n),e.lineTo(n,n),e.lineTo(n,3*n),e.lineTo(-n,3*n),e.lineTo(-n,n),e.lineTo(-3*n,n),e.closePath()}},py=Math.sqrt(1/3),zy=2*py,vy={draw:function(e,t){var n=Math.sqrt(t/zy),r=n*py;e.moveTo(0,-n),e.lineTo(r,0),e.lineTo(0,n),e.lineTo(-r,0),e.closePath()}},gy=Math.sin(Sm/10)/Math.sin(7*Sm/10),my=Math.sin(Tm/10)*gy,yy=-Math.cos(Tm/10)*gy,by={draw:function(e,t){var n=Math.sqrt(.8908130915292852*t),r=my*n,i=yy*n;e.moveTo(0,-n),e.lineTo(r,i);for(var o=1;o<5;++o){var a=Tm*o/5,c=Math.cos(a),s=Math.sin(a);e.lineTo(s*n,-c*n),e.lineTo(c*r-s*i,s*r+c*i)}e.closePath()}},wy={draw:function(e,t){var n=Math.sqrt(t),r=-n/2;e.rect(r,r,n,n)}},ky=Math.sqrt(3),xy={draw:function(e,t){var n=-Math.sqrt(t/(3*ky));e.moveTo(0,2*n),e.lineTo(-ky*n,-n),e.lineTo(ky*n,-n),e.closePath()}},jy=Math.sqrt(3)/2,My=1/Math.sqrt(12),_y=3*(My/2+1),Cy={draw:function(e,t){var n=Math.sqrt(t/_y),r=n/2,i=n*My,o=r,a=n*My+n,c=-o,s=a;e.moveTo(r,i),e.lineTo(o,a),e.lineTo(c,s),e.lineTo(-.5*r-jy*i,jy*r+-.5*i),e.lineTo(-.5*o-jy*a,jy*o+-.5*a),e.lineTo(-.5*c-jy*s,jy*c+-.5*s),e.lineTo(-.5*r+jy*i,-.5*i-jy*r),e.lineTo(-.5*o+jy*a,-.5*a-jy*o),e.lineTo(-.5*c+jy*s,-.5*s-jy*c),e.closePath()}},qy=[hy,dy,vy,wy,by,xy,Cy],Sy=function(){var e=bm(hy),t=bm(64),n=null;function r(){var r;if(n||(n=r=ja()),e.apply(this,arguments).draw(n,+t.apply(this,arguments)),r)return n=null,r+""||null}return r.type=function(t){return arguments.length?(e="function"===typeof t?t:bm(t),r):e},r.size=function(e){return arguments.length?(t="function"===typeof e?e:bm(+e),r):t},r.context=function(e){return arguments.length?(n=null==e?null:e,r):n},r},Oy=function(){};function Ty(e,t,n){e._context.bezierCurveTo((2*e._x0+e._x1)/3,(2*e._y0+e._y1)/3,(e._x0+2*e._x1)/3,(e._y0+2*e._y1)/3,(e._x0+4*e._x1+t)/6,(e._y0+4*e._y1+n)/6)}function Ey(e){this._context=e}Ey.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Ty(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Ty(this,e,t)}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};var Ay=function(e){return new Ey(e)};function Hy(e){this._context=e}Hy.prototype={areaStart:Oy,areaEnd:Oy,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x2=e,this._y2=t;break;case 1:this._point=2,this._x3=e,this._y3=t;break;case 2:this._point=3,this._x4=e,this._y4=t,this._context.moveTo((this._x0+4*this._x1+e)/6,(this._y0+4*this._y1+t)/6);break;default:Ty(this,e,t)}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};var Ly=function(e){return new Hy(e)};function Dy(e){this._context=e}Dy.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+e)/6,r=(this._y0+4*this._y1+t)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:Ty(this,e,t)}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};var Py=function(e){return new Dy(e)};function Vy(e,t){this._basis=new Ey(e),this._beta=t}Vy.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var e=this._x,t=this._y,n=e.length-1;if(n>0)for(var r,i=e[0],o=t[0],a=e[n]-i,c=t[n]-o,s=-1;++s<=n;)r=s/n,this._basis.point(this._beta*e[s]+(1-this._beta)*(i+r*a),this._beta*t[s]+(1-this._beta)*(o+r*c));this._x=this._y=null,this._basis.lineEnd()},point:function(e,t){this._x.push(+e),this._y.push(+t)}};var Ny=function e(t){function n(e){return 1===t?new Ey(e):new Vy(e,t)}return n.beta=function(t){return e(+t)},n}(.85);function Iy(e,t,n){e._context.bezierCurveTo(e._x1+e._k*(e._x2-e._x0),e._y1+e._k*(e._y2-e._y0),e._x2+e._k*(e._x1-t),e._y2+e._k*(e._y1-n),e._x2,e._y2)}function Ry(e,t){this._context=e,this._k=(1-t)/6}Ry.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Iy(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2,this._x1=e,this._y1=t;break;case 2:this._point=3;default:Iy(this,e,t)}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};var By=function e(t){function n(e){return new Ry(e,t)}return n.tension=function(t){return e(+t)},n}(0);function Fy(e,t){this._context=e,this._k=(1-t)/6}Fy.prototype={areaStart:Oy,areaEnd:Oy,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x3=e,this._y3=t;break;case 1:this._point=2,this._context.moveTo(this._x4=e,this._y4=t);break;case 2:this._point=3,this._x5=e,this._y5=t;break;default:Iy(this,e,t)}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};var Uy=function e(t){function n(e){return new Fy(e,t)}return n.tension=function(t){return e(+t)},n}(0);function Wy(e,t){this._context=e,this._k=(1-t)/6}Wy.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Iy(this,e,t)}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};var Gy=function e(t){function n(e){return new Wy(e,t)}return n.tension=function(t){return e(+t)},n}(0);function Yy(e,t,n){var r=e._x1,i=e._y1,o=e._x2,a=e._y2;if(e._l01_a>qm){var c=2*e._l01_2a+3*e._l01_a*e._l12_a+e._l12_2a,s=3*e._l01_a*(e._l01_a+e._l12_a);r=(r*c-e._x0*e._l12_2a+e._x2*e._l01_2a)/s,i=(i*c-e._y0*e._l12_2a+e._y2*e._l01_2a)/s}if(e._l23_a>qm){var l=2*e._l23_2a+3*e._l23_a*e._l12_a+e._l12_2a,u=3*e._l23_a*(e._l23_a+e._l12_a);o=(o*l+e._x1*e._l23_2a-t*e._l12_2a)/u,a=(a*l+e._y1*e._l23_2a-n*e._l12_2a)/u}e._context.bezierCurveTo(r,i,o,a,e._x2,e._y2)}function Zy(e,t){this._context=e,this._alpha=t}Zy.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){if(e=+e,t=+t,this._point){var n=this._x2-e,r=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3;default:Yy(this,e,t)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};var $y=function e(t){function n(e){return t?new Zy(e,t):new Ry(e,0)}return n.alpha=function(t){return e(+t)},n}(.5);function Xy(e,t){this._context=e,this._alpha=t}Xy.prototype={areaStart:Oy,areaEnd:Oy,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(e,t){if(e=+e,t=+t,this._point){var n=this._x2-e,r=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=e,this._y3=t;break;case 1:this._point=2,this._context.moveTo(this._x4=e,this._y4=t);break;case 2:this._point=3,this._x5=e,this._y5=t;break;default:Yy(this,e,t)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};var Ky=function e(t){function n(e){return t?new Xy(e,t):new Fy(e,0)}return n.alpha=function(t){return e(+t)},n}(.5);function Qy(e,t){this._context=e,this._alpha=t}Qy.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){if(e=+e,t=+t,this._point){var n=this._x2-e,r=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Yy(this,e,t)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};var Jy=function e(t){function n(e){return t?new Qy(e,t):new Wy(e,0)}return n.alpha=function(t){return e(+t)},n}(.5);function eb(e){this._context=e}eb.prototype={areaStart:Oy,areaEnd:Oy,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(e,t){e=+e,t=+t,this._point?this._context.lineTo(e,t):(this._point=1,this._context.moveTo(e,t))}};var tb=function(e){return new eb(e)};function nb(e){return e<0?-1:1}function rb(e,t,n){var r=e._x1-e._x0,i=t-e._x1,o=(e._y1-e._y0)/(r||i<0&&-0),a=(n-e._y1)/(i||r<0&&-0),c=(o*i+a*r)/(r+i);return(nb(o)+nb(a))*Math.min(Math.abs(o),Math.abs(a),.5*Math.abs(c))||0}function ib(e,t){var n=e._x1-e._x0;return n?(3*(e._y1-e._y0)/n-t)/2:t}function ob(e,t,n){var r=e._x0,i=e._y0,o=e._x1,a=e._y1,c=(o-r)/3;e._context.bezierCurveTo(r+c,i+c*t,o-c,a-c*n,o,a)}function ab(e){this._context=e}function cb(e){this._context=new sb(e)}function sb(e){this._context=e}function lb(e){return new ab(e)}function ub(e){return new cb(e)}function fb(e){this._context=e}function hb(e){var t,n,r=e.length-1,i=new Array(r),o=new Array(r),a=new Array(r);for(i[0]=0,o[0]=2,a[0]=e[0]+2*e[1],t=1;t=0;--t)i[t]=(a[t]-i[t+1])/o[t];for(o[r-1]=(e[r]+i[r-1])/2,t=0;t=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,t),this._context.lineTo(e,t);else{var n=this._x*(1-this._t)+e*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,t)}}this._x=e,this._y=t}};var zb=function(e){return new pb(e,.5)};function vb(e){return new pb(e,0)}function gb(e){return new pb(e,1)}var mb=function(e,t){if((i=e.length)>1)for(var n,r,i,o=1,a=e[t[0]],c=a.length;o=0;)n[t]=t;return n};function bb(e,t){return e[t]}var wb=function(){var e=bm([]),t=yb,n=mb,r=bb;function i(i){var o,a,c=e.apply(this,arguments),s=i.length,l=c.length,u=new Array(l);for(o=0;o0){for(var n,r,i,o=0,a=e[0].length;o1)for(var n,r,i,o,a,c,s=0,l=e[t[0]].length;s=0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=a,r[0]=a+=i):r[0]=o},jb=function(e,t){if((n=e.length)>0){for(var n,r=0,i=e[t[0]],o=i.length;r0&&(r=(n=e[t[0]]).length)>0){for(var n,r,i,o=0,a=1;a0)){if(o/=h,h<0){if(o0){if(o>f)return;o>u&&(u=o)}if(o=r-s,h||!(o<0)){if(o/=h,h<0){if(o>f)return;o>u&&(u=o)}else if(h>0){if(o0)){if(o/=d,d<0){if(o0){if(o>f)return;o>u&&(u=o)}if(o=i-l,d||!(o<0)){if(o/=d,d<0){if(o>f)return;o>u&&(u=o)}else if(d>0){if(o0||f<1)||(u>0&&(e[0]=[s+u*h,l+u*d]),f<1&&(e[1]=[s+f*h,l+f*d]),!0)}}}}}function Ub(e,t,n,r,i){var o=e[1];if(o)return!0;var a,c,s=e[0],l=e.left,u=e.right,f=l[0],h=l[1],d=u[0],p=u[1],z=(f+d)/2,v=(h+p)/2;if(p===h){if(z=r)return;if(f>d){if(s){if(s[1]>=i)return}else s=[z,n];o=[z,i]}else{if(s){if(s[1]1)if(f>d){if(s){if(s[1]>=i)return}else s=[(n-c)/a,n];o=[(i-c)/a,i]}else{if(s){if(s[1]=r)return}else s=[t,a*t+c];o=[r,a*r+c]}else{if(s){if(s[0]=-hw)){var d=s*s+l*l,p=u*u+f*f,z=(f*d-l*p)/h,v=(s*p-u*d)/h,g=$b.pop()||new Xb;g.arc=e,g.site=i,g.x=z+a,g.y=(g.cy=v+c)+Math.sqrt(z*z+v*v),e.circle=g;for(var m=null,y=lw._;y;)if(g.yfw)c=c.L;else{if(!((i=o-aw(c,a))>fw)){r>-fw?(t=c.P,n=c):i>-fw?(t=c,n=c.N):t=n=c;break}if(!c.R){t=c;break}c=c.R}!function(e){sw[e.index]={site:e,halfedges:[]}}(e);var s=tw(e);if(cw.insert(t,s),t||n){if(t===n)return Qb(t),n=tw(t.site),cw.insert(s,n),s.edge=n.edge=Ib(t.site,s.site),Kb(t),void Kb(n);if(n){Qb(t),Qb(n);var l=t.site,u=l[0],f=l[1],h=e[0]-u,d=e[1]-f,p=n.site,z=p[0]-u,v=p[1]-f,g=2*(h*v-d*z),m=h*h+d*d,y=z*z+v*v,b=[(v*m-d*y)/g+u,(h*y-z*m)/g+f];Bb(n.edge,l,p,b),s.edge=Ib(l,e,null,b),n.edge=Ib(e,p,null,b),Kb(t),Kb(n)}else s.edge=Ib(t.site,s.site)}}function ow(e,t){var n=e.site,r=n[0],i=n[1],o=i-t;if(!o)return r;var a=e.P;if(!a)return-1/0;var c=(n=a.site)[0],s=n[1],l=s-t;if(!l)return c;var u=c-r,f=1/o-1/l,h=u/l;return f?(-h+Math.sqrt(h*h-2*f*(u*u/(-2*l)-s+l/2+i-o/2)))/f+r:(r+c)/2}function aw(e,t){var n=e.N;if(n)return ow(n,t);var r=e.site;return r[1]===t?r[0]:1/0}var cw,sw,lw,uw,fw=1e-6,hw=1e-12;function dw(e,t){return t[1]-e[1]||t[0]-e[0]}function pw(e,t){var n,r,i,o=e.sort(dw).pop();for(uw=[],sw=new Array(e.length),cw=new Nb,lw=new Nb;;)if(i=Zb,o&&(!i||o[1]fw||Math.abs(i[0][1]-i[1][1])>fw)||delete uw[o]}(a,c,s,l),function(e,t,n,r){var i,o,a,c,s,l,u,f,h,d,p,z,v=sw.length,g=!0;for(i=0;ifw||Math.abs(z-h)>fw)&&(s.splice(c,0,uw.push(Rb(a,d,Math.abs(p-e)fw?[e,Math.abs(f-e)fw?[Math.abs(h-r)fw?[n,Math.abs(f-n)fw?[Math.abs(h-t)=c)return null;var s=e-i.site[0],l=t-i.site[1],u=s*s+l*l;do{i=o.cells[r=a],a=null,i.halfedges.forEach((function(n){var r=o.edges[n],c=r.left;if(c!==i.site&&c||(c=r.right)){var s=e-c[0],l=t-c[1],f=s*s+l*l;fr?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}var Sw=function(){var e,t,n=xw,r=jw,i=qw,o=_w,a=Cw,c=[0,1/0],s=[[-1/0,-1/0],[1/0,1/0]],l=250,u=Cr,f=[],h=ze("start","zoom","end"),d=500,p=150,z=0;function v(e){e.property("__zoom",Mw).on("wheel.zoom",x).on("mousedown.zoom",j).on("dblclick.zoom",M).filter(a).on("touchstart.zoom",_).on("touchmove.zoom",C).on("touchend.zoom touchcancel.zoom",q).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function g(e,t){return(t=Math.max(c[0],Math.min(c[1],t)))===e.k?e:new mw(t,e.x,e.y)}function m(e,t,n){var r=t[0]-n[0]*e.k,i=t[1]-n[1]*e.k;return r===e.x&&i===e.y?e:new mw(e.k,r,i)}function y(e){return[(+e[0][0]+ +e[1][0])/2,(+e[0][1]+ +e[1][1])/2]}function b(e,t,n){e.on("start.zoom",(function(){w(this,arguments).start()})).on("interrupt.zoom end.zoom",(function(){w(this,arguments).end()})).tween("zoom",(function(){var e=this,i=arguments,o=w(e,i),a=r.apply(e,i),c=n||y(a),s=Math.max(a[1][0]-a[0][0],a[1][1]-a[0][1]),l=e.__zoom,f="function"===typeof t?t.apply(e,i):t,h=u(l.invert(c).concat(s/l.k),f.invert(c).concat(s/f.k));return function(e){if(1===e)e=f;else{var t=h(e),n=s/t[2];e=new mw(n,c[0]-t[0]*n,c[1]-t[1]*n)}o.zoom(null,e)}}))}function w(e,t){for(var n,r=0,i=f.length;rz}e.zoom("mouse",i(m(e.that.__zoom,e.mouse[0]=Ne(e.that),e.mouse[1]),e.extent,s))}),!0).on("mouseup.zoom",(function(){r.on("mousemove.zoom mouseup.zoom",null),Bt(Oe.view,e.moved),kw(),e.end()}),!0),o=Ne(this),a=Oe.clientX,c=Oe.clientY;Rt(Oe.view),ww(),e.mouse=[o,this.__zoom.invert(o)],yi(this),e.start()}}function M(){if(n.apply(this,arguments)){var e=this.__zoom,t=Ne(this),o=e.invert(t),a=e.k*(Oe.shiftKey?.5:2),c=i(m(g(e,a),t,o),r.apply(this,arguments),s);kw(),l>0?Lt(this).transition().duration(l).call(b,c,t):Lt(this).call(v.transform,c)}}function _(){if(n.apply(this,arguments)){var t,r,i,o,a=w(this,arguments),c=Oe.changedTouches,s=c.length;for(ww(),r=0;r=0}n.d(t,"a",(function(){return i}))},function(e,t,n){"use strict";var r=n(30);function i(e){return e}var o=Object(r.a)(i);t.a=o},,function(e,t,n){"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=(0,((r=n(223))&&r.__esModule?r:{default:r}).default)("Batch",(function(){for(var e=arguments.length,t=new Array(e),n=0;n=0&&n.splice(r,1),e.className=n.join(" ")}(e,t)},t.list=function(e){return e.classList?Array.prototype.slice.apply(e.classList):e.className.split(" ")}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(204)),o=r(n(246)),a=function(){i.default.call(this)};(a.prototype=new i.default).extractSeries=function(e,t,n){},a.prototype.rollingAverage=function(e,t,n){},a.prototype.onPointsCreated_=function(e,t){for(var n=0;nr&&(s=r),lo)&&(o=l),(null===i||s0&&t-1 in e)}x.fn=x.prototype={jquery:"3.6.0",constructor:x,length:0,toArray:function(){return c.call(this)},get:function(e){return null==e?c.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return x.each(this,e)},map:function(e){return this.pushStack(x.map(this,(function(t,n){return e.call(t,n,t)})))},slice:function(){return this.pushStack(c.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(x.grep(this,(function(e,t){return(t+1)%2})))},odd:function(){return this.pushStack(x.grep(this,(function(e,t){return t%2})))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|"+P+")"+P+"*"),W=new RegExp(P+"|>"),G=new RegExp(I),Y=new RegExp("^"+V+"$"),Z={ID:new RegExp("^#("+V+")"),CLASS:new RegExp("^\\.("+V+")"),TAG:new RegExp("^("+V+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+I),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:new RegExp("^(?:"+D+")$","i"),needsContext:new RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},$=/HTML$/i,X=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+P+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){h()},ae=be((function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()}),{dir:"parentNode",next:"legend"});try{A.apply(O=H.call(w.childNodes),w.childNodes),O[w.childNodes.length].nodeType}catch(Me){A={apply:O.length?function(e,t){E.apply(e,H.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function ce(e,t,r,i){var o,c,l,u,f,p,g,m=t&&t.ownerDocument,w=t?t.nodeType:9;if(r=r||[],"string"!==typeof e||!e||1!==w&&9!==w&&11!==w)return r;if(!i&&(h(t),t=t||d,z)){if(11!==w&&(f=J.exec(e)))if(o=f[1]){if(9===w){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&y(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return A.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return A.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!C[e+" "]&&(!v||!v.test(e))&&(1!==w||"object"!==t.nodeName.toLowerCase())){if(g=e,m=t,1===w&&(W.test(e)||U.test(e))){for((m=ee.test(e)&&ge(t.parentNode)||t)===t&&n.scope||((u=t.getAttribute("id"))?u=u.replace(re,ie):t.setAttribute("id",u=b)),c=(p=a(e)).length;c--;)p[c]=(u?"#"+u:":scope")+" "+ye(p[c]);g=p.join(",")}try{return A.apply(r,m.querySelectorAll(g)),r}catch(k){C(e,!0)}finally{u===b&&t.removeAttribute("id")}}}return s(e.replace(B,"$1"),t,r,i)}function se(){var e=[];return function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}}function le(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(Me){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){for(var n=e.split("|"),i=n.length;i--;)r.attrHandle[n[i]]=t}function he(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function de(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ze(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ae(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function ve(e){return le((function(t){return t=+t,le((function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))}))}))}function ge(e){return e&&"undefined"!==typeof e.getElementsByTagName&&e}for(t in n=ce.support={},o=ce.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!$.test(t||n&&n.nodeName||"HTML")},h=ce.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!=d&&9===a.nodeType&&a.documentElement?(p=(d=a).documentElement,z=!o(d),w!=d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",oe,!1):i.attachEvent&&i.attachEvent("onunload",oe)),n.scope=ue((function(e){return p.appendChild(e).appendChild(d.createElement("div")),"undefined"!==typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length})),n.attributes=ue((function(e){return e.className="i",!e.getAttribute("className")})),n.getElementsByTagName=ue((function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length})),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue((function(e){return p.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length})),n.getById?(r.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!==typeof t.getElementById&&z){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(te,ne);return function(e){var n="undefined"!==typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!==typeof t.getElementById&&z){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!==typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!==typeof t.getElementsByClassName&&z)return t.getElementsByClassName(e)},g=[],v=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue((function(e){var t;p.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+P+"*(?:value|"+D+")"),e.querySelectorAll("[id~="+b+"-]").length||v.push("~="),(t=d.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+P+"*name"+P+"*="+P+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")})),ue((function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+P+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),p.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")}))),(n.matchesSelector=Q.test(m=p.matches||p.webkitMatchesSelector||p.mozMatchesSelector||p.oMatchesSelector||p.msMatchesSelector))&&ue((function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),g.push("!=",I)})),v=v.length&&new RegExp(v.join("|")),g=g.length&&new RegExp(g.join("|")),t=Q.test(p.compareDocumentPosition),y=t||Q.test(p.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},q=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e==d||e.ownerDocument==w&&y(w,e)?-1:t==d||t.ownerDocument==w&&y(w,t)?1:u?L(u,e)-L(u,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],c=[t];if(!i||!o)return e==d?-1:t==d?1:i?-1:o?1:u?L(u,e)-L(u,t):0;if(i===o)return he(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)c.unshift(n);for(;a[r]===c[r];)r++;return r?he(a[r],c[r]):a[r]==w?-1:c[r]==w?1:0},d):d},ce.matches=function(e,t){return ce(e,null,null,t)},ce.matchesSelector=function(e,t){if(h(e),n.matchesSelector&&z&&!C[t+" "]&&(!g||!g.test(t))&&(!v||!v.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(Me){C(t,!0)}return ce(t,d,null,[e]).length>0},ce.contains=function(e,t){return(e.ownerDocument||e)!=d&&h(e),y(e,t)},ce.attr=function(e,t){(e.ownerDocument||e)!=d&&h(e);var i=r.attrHandle[t.toLowerCase()],o=i&&S.call(r.attrHandle,t.toLowerCase())?i(e,t,!z):void 0;return void 0!==o?o:n.attributes||!z?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},ce.escape=function(e){return(e+"").replace(re,ie)},ce.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},ce.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,u=!n.sortStable&&e.slice(0),e.sort(q),f){for(;t=e[o++];)t===e[o]&&(i=r.push(o));for(;i--;)e.splice(r[i],1)}return u=null,e},i=ce.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"===typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r++];)n+=i(t);return n},(r=ce.selectors={cacheLength:50,createPseudo:le,match:Z,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ce.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ce.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Z.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&G.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=j[e+" "];return t||(t=new RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&j(e,(function(e){return t.test("string"===typeof e.className&&e.className||"undefined"!==typeof e.getAttribute&&e.getAttribute("class")||"")}))},ATTR:function(e,t,n){return function(r){var i=ce.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace(R," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),c="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,s){var l,u,f,h,d,p,z=o!==a?"nextSibling":"previousSibling",v=t.parentNode,g=c&&t.nodeName.toLowerCase(),m=!s&&!c,y=!1;if(v){if(o){for(;z;){for(h=t;h=h[z];)if(c?h.nodeName.toLowerCase()===g:1===h.nodeType)return!1;p=z="only"===e&&!p&&"nextSibling"}return!0}if(p=[a?v.firstChild:v.lastChild],a&&m){for(y=(d=(l=(u=(f=(h=v)[b]||(h[b]={}))[h.uniqueID]||(f[h.uniqueID]={}))[e]||[])[0]===k&&l[1])&&l[2],h=d&&v.childNodes[d];h=++d&&h&&h[z]||(y=d=0)||p.pop();)if(1===h.nodeType&&++y&&h===t){u[e]=[k,d,y];break}}else if(m&&(y=d=(l=(u=(f=(h=t)[b]||(h[b]={}))[h.uniqueID]||(f[h.uniqueID]={}))[e]||[])[0]===k&&l[1]),!1===y)for(;(h=++d&&h&&h[z]||(y=d=0)||p.pop())&&((c?h.nodeName.toLowerCase()!==g:1!==h.nodeType)||!++y||(m&&((u=(f=h[b]||(h[b]={}))[h.uniqueID]||(f[h.uniqueID]={}))[e]=[k,y]),h!==t)););return(y-=i)===r||y%r===0&&y/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||ce.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?le((function(e,n){for(var r,o=i(e,t),a=o.length;a--;)e[r=L(e,o[a])]=!(n[r]=o[a])})):function(e){return i(e,0,n)}):i}},pseudos:{not:le((function(e){var t=[],n=[],r=c(e.replace(B,"$1"));return r[b]?le((function(e,t,n,i){for(var o,a=r(e,null,i,[]),c=e.length;c--;)(o=a[c])&&(e[c]=!(t[c]=o))})):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}})),has:le((function(e){return function(t){return ce(e,t).length>0}})),contains:le((function(e){return e=e.replace(te,ne),function(t){return(t.textContent||i(t)).indexOf(e)>-1}})),lang:le((function(e){return Y.test(e||"")||ce.error("unsupported lang: "+e),e=e.replace(te,ne).toLowerCase(),function(t){var n;do{if(n=z?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}})),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===p},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ze(!1),disabled:ze(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return K.test(e.nodeName)},input:function(e){return X.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve((function(){return[0]})),last:ve((function(e,t){return[t-1]})),eq:ve((function(e,t,n){return[n<0?n+t:n]})),even:ve((function(e,t){for(var n=0;nt?t:n;--r>=0;)e.push(r);return e})),gt:ve((function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function ke(e,t,n,r,i){for(var o,a=[],c=0,s=e.length,l=null!=t;c-1&&(o[l]=!(a[l]=f))}}else g=ke(g===a?g.splice(p,g.length):g),i?i(null,a,g,s):A.apply(a,g)}))}function je(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],c=a||r.relative[" "],s=a?1:0,u=be((function(e){return e===t}),c,!0),f=be((function(e){return L(t,e)>-1}),c,!0),h=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?u(e,n,r):f(e,n,r));return t=null,i}];s1&&we(h),s>1&&ye(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(B,"$1"),n,s0,i=e.length>0,o=function(o,a,c,s,u){var f,p,v,g=0,m="0",y=o&&[],b=[],w=l,x=o||i&&r.find.TAG("*",u),j=k+=null==w?1:Math.random()||.1,M=x.length;for(u&&(l=a==d||a||u);m!==M&&null!=(f=x[m]);m++){if(i&&f){for(p=0,a||f.ownerDocument==d||(h(f),c=!z);v=e[p++];)if(v(f,a||d,c)){s.push(f);break}u&&(k=j)}n&&((f=!v&&f)&&g--,o&&y.push(f))}if(g+=m,n&&m!==g){for(p=0;v=t[p++];)v(y,b,a,c);if(o){if(g>0)for(;m--;)y[m]||b[m]||(b[m]=T.call(s));b=ke(b)}A.apply(s,b),u&&!o&&b.length>0&&g+t.length>1&&ce.uniqueSort(s)}return u&&(k=j,l=w),y};return n?le(o):o}(o,i))).selector=e}return c},s=ce.select=function(e,t,n,i){var o,s,l,u,f,h="function"===typeof e&&e,d=!i&&a(e=h.selector||e);if(n=n||[],1===d.length){if((s=d[0]=d[0].slice(0)).length>2&&"ID"===(l=s[0]).type&&9===t.nodeType&&z&&r.relative[s[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(te,ne),t)||[])[0]))return n;h&&(t=t.parentNode),e=e.slice(s.shift().value.length)}for(o=Z.needsContext.test(e)?0:s.length;o--&&(l=s[o],!r.relative[u=l.type]);)if((f=r.find[u])&&(i=f(l.matches[0].replace(te,ne),ee.test(s[0].type)&&ge(t.parentNode)||t))){if(s.splice(o,1),!(e=i.length&&ye(s)))return A.apply(n,i),n;break}}return(h||c(e,d))(i,t,!z,n,!t||ee.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(q).join("")===b,n.detectDuplicates=!!f,h(),n.sortDetached=ue((function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))})),ue((function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")}))||fe("type|href|height|width",(function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)})),n.attributes&&ue((function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}))||fe("value",(function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue})),ue((function(e){return null==e.getAttribute("disabled")}))||fe(D,(function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null})),ce}(n);x.find=M,x.expr=M.selectors,x.expr[":"]=x.expr.pseudos,x.uniqueSort=x.unique=M.uniqueSort,x.text=M.getText,x.isXMLDoc=M.isXML,x.contains=M.contains,x.escapeSelector=M.escape;var _=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},C=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},q=x.expr.match.needsContext;function S(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var O=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,t,n){return g(t)?x.grep(e,(function(e,r){return!!t.call(e,r,e)!==n})):t.nodeType?x.grep(e,(function(e){return e===t!==n})):"string"!==typeof t?x.grep(e,(function(e){return u.call(t,e)>-1!==n})):x.filter(t,e,n)}x.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,(function(e){return 1===e.nodeType})))},x.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!==typeof e)return this.pushStack(x(e).filter((function(){for(t=0;t1?x.uniqueSort(n):n},filter:function(e){return this.pushStack(T(this,e||[],!1))},not:function(e){return this.pushStack(T(this,e||[],!0))},is:function(e){return!!T(this,"string"===typeof e&&q.test(e)?x(e):e||[],!1).length}});var E,A=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(x.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||E,"string"===typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:A.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:y,!0)),O.test(r[1])&&x.isPlainObject(t))for(r in t)g(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=y.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(x):x.makeArray(e,this)}).prototype=x.fn,E=x(y);var H=/^(?:parents|prev(?:Until|All))/,L={children:!0,contents:!0,next:!0,prev:!0};function D(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}x.fn.extend({has:function(e){var t=x(e,this),n=t.length;return this.filter((function(){for(var e=0;e-1:1===n.nodeType&&x.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?x.uniqueSort(o):o)},index:function(e){return e?"string"===typeof e?u.call(x(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(x.uniqueSort(x.merge(this.get(),x(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return _(e,"parentNode")},parentsUntil:function(e,t,n){return _(e,"parentNode",n)},next:function(e){return D(e,"nextSibling")},prev:function(e){return D(e,"previousSibling")},nextAll:function(e){return _(e,"nextSibling")},prevAll:function(e){return _(e,"previousSibling")},nextUntil:function(e,t,n){return _(e,"nextSibling",n)},prevUntil:function(e,t,n){return _(e,"previousSibling",n)},siblings:function(e){return C((e.parentNode||{}).firstChild,e)},children:function(e){return C(e.firstChild)},contents:function(e){return null!=e.contentDocument&&a(e.contentDocument)?e.contentDocument:(S(e,"template")&&(e=e.content||e),x.merge([],e.childNodes))}},(function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"===typeof r&&(i=x.filter(r,i)),this.length>1&&(L[e]||x.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}}));var P=/[^\x20\t\r\n\f]+/g;function V(e){return e}function N(e){throw e}function I(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}x.Callbacks=function(e){e="string"===typeof e?function(e){var t={};return x.each(e.match(P)||[],(function(e,n){t[n]=!0})),t}(e):x.extend({},e);var t,n,r,i,o=[],a=[],c=-1,s=function(){for(i=i||e.once,r=t=!0;a.length;c=-1)for(n=a.shift();++c-1;)o.splice(n,1),n<=c&&c--})),this},has:function(e){return e?x.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||s()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l},x.extend({Deferred:function(e){var t=[["notify","progress",x.Callbacks("memory"),x.Callbacks("memory"),2],["resolve","done",x.Callbacks("once memory"),x.Callbacks("once memory"),0,"resolved"],["reject","fail",x.Callbacks("once memory"),x.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return x.Deferred((function(n){x.each(t,(function(t,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]]((function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[r[0]+"With"](this,i?[e]:arguments)}))})),e=null})).promise()},then:function(e,r,i){var o=0;function a(e,t,r,i){return function(){var c=this,s=arguments,l=function(){var n,l;if(!(e=o&&(r!==N&&(c=void 0,s=[n]),t.rejectWith(c,s))}};e?u():(x.Deferred.getStackHook&&(u.stackTrace=x.Deferred.getStackHook()),n.setTimeout(u))}}return x.Deferred((function(n){t[0][3].add(a(0,n,g(i)?i:V,n.notifyWith)),t[1][3].add(a(0,n,g(e)?e:V)),t[2][3].add(a(0,n,g(r)?r:N))})).promise()},promise:function(e){return null!=e?x.extend(e,i):i}},o={};return x.each(t,(function(e,n){var a=n[2],c=n[5];i[n[1]]=a.add,c&&a.add((function(){r=c}),t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),a.add(n[3].fire),o[n[0]]=function(){return o[n[0]+"With"](this===o?void 0:this,arguments),this},o[n[0]+"With"]=a.fireWith})),i.promise(o),e&&e.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=c.call(arguments),o=x.Deferred(),a=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?c.call(arguments):n,--t||o.resolveWith(r,i)}};if(t<=1&&(I(e,o.done(a(n)).resolve,o.reject,!t),"pending"===o.state()||g(i[n]&&i[n].then)))return o.then();for(;n--;)I(i[n],a(n),o.reject);return o.promise()}});var R=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;x.Deferred.exceptionHook=function(e,t){n.console&&n.console.warn&&e&&R.test(e.name)&&n.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},x.readyException=function(e){n.setTimeout((function(){throw e}))};var B=x.Deferred();function F(){y.removeEventListener("DOMContentLoaded",F),n.removeEventListener("load",F),x.ready()}x.fn.ready=function(e){return B.then(e).catch((function(e){x.readyException(e)})),this},x.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--x.readyWait:x.isReady)||(x.isReady=!0,!0!==e&&--x.readyWait>0||B.resolveWith(y,[x]))}}),x.ready.then=B.then,"complete"===y.readyState||"loading"!==y.readyState&&!y.documentElement.doScroll?n.setTimeout(x.ready):(y.addEventListener("DOMContentLoaded",F),n.addEventListener("load",F));var U=function e(t,n,r,i,o,a,c){var s=0,l=t.length,u=null==r;if("object"===k(r))for(s in o=!0,r)e(t,n,s,r[s],!0,a,c);else if(void 0!==i&&(o=!0,g(i)||(c=!0),u&&(c?(n.call(t,i),n=null):(u=n,n=function(e,t,n){return u.call(x(e),n)})),n))for(;s1,null,!0)},removeData:function(e){return this.each((function(){Q.remove(this,e)}))}}),x.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=K.get(e,t),n&&(!r||Array.isArray(n)?r=K.access(e,t,x.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,(function(){x.dequeue(e,t)}),o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return K.get(e,n)||K.access(e,n,{empty:x.Callbacks("once memory").add((function(){K.remove(e,[t+"queue",n])}))})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!==typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]*)/i,ze=/^$|^module$|\/(?:java|ecma)script/i;!function(){var e=y.createDocumentFragment().appendChild(y.createElement("div")),t=y.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),v.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",v.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,e.innerHTML="",v.option=!!e.lastChild}();var ve={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ge(e,t){var n;return n="undefined"!==typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!==typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?x.merge([e],n):n}function me(e,t){for(var n=0,r=e.length;n",""]);var ye=/<|&#?\w+;/;function be(e,t,n,r,i){for(var o,a,c,s,l,u,f=t.createDocumentFragment(),h=[],d=0,p=e.length;d-1)i&&i.push(o);else if(l=ae(o),a=ge(f.appendChild(o),"script"),l&&me(a),n)for(u=0;o=a[u++];)ze.test(o.type||"")&&n.push(o);return f}var we=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function xe(){return!1}function je(e,t){return e===function(){try{return y.activeElement}catch(e){}}()===("focus"===t)}function Me(e,t,n,r,i,o){var a,c;if("object"===typeof t){for(c in"string"!==typeof n&&(r=r||n,n=void 0),t)Me(e,c,n,r,t[c],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"===typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=xe;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return x().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=x.guid++)),e.each((function(){x.event.add(this,t,i,r,n)}))}function _e(e,t,n){n?(K.set(e,t,!1),x.event.add(e,t,{namespace:!1,handler:function(e){var r,i,o=K.get(this,t);if(1&e.isTrigger&&this[t]){if(o.length)(x.event.special[t]||{}).delegateType&&e.stopPropagation();else if(o=c.call(arguments),K.set(this,t,o),r=n(this,t),this[t](),o!==(i=K.get(this,t))||r?K.set(this,t,!1):i={},o!==i)return e.stopImmediatePropagation(),e.preventDefault(),i&&i.value}else o.length&&(K.set(this,t,{value:x.event.trigger(x.extend(o[0],x.Event.prototype),o.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===K.get(e,t)&&x.event.add(e,t,ke)}x.event={global:{},add:function(e,t,n,r,i){var o,a,c,s,l,u,f,h,d,p,z,v=K.get(e);if($(e))for(n.handler&&(n=(o=n).handler,i=o.selector),i&&x.find.matchesSelector(oe,i),n.guid||(n.guid=x.guid++),(s=v.events)||(s=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(t){return"undefined"!==typeof x&&x.event.triggered!==t.type?x.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(P)||[""]).length;l--;)d=z=(c=we.exec(t[l])||[])[1],p=(c[2]||"").split(".").sort(),d&&(f=x.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=x.event.special[d]||{},u=x.extend({type:d,origType:z,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&x.expr.match.needsContext.test(i),namespace:p.join(".")},o),(h=s[d])||((h=s[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,p,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,u),u.handler.guid||(u.handler.guid=n.guid)),i?h.splice(h.delegateCount++,0,u):h.push(u),x.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,c,s,l,u,f,h,d,p,z,v=K.hasData(e)&&K.get(e);if(v&&(s=v.events)){for(l=(t=(t||"").match(P)||[""]).length;l--;)if(d=z=(c=we.exec(t[l])||[])[1],p=(c[2]||"").split(".").sort(),d){for(f=x.event.special[d]||{},h=s[d=(r?f.delegateType:f.bindType)||d]||[],c=c[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=h.length;o--;)u=h[o],!i&&z!==u.origType||n&&n.guid!==u.guid||c&&!c.test(u.namespace)||r&&r!==u.selector&&("**"!==r||!u.selector)||(h.splice(o,1),u.selector&&h.delegateCount--,f.remove&&f.remove.call(e,u));a&&!h.length&&(f.teardown&&!1!==f.teardown.call(e,p,v.handle)||x.removeEvent(e,d,v.handle),delete s[d])}else for(d in s)x.event.remove(e,d+t[l],n,r,!0);x.isEmptyObject(s)&&K.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,c=new Array(arguments.length),s=x.event.fix(e),l=(K.get(this,"events")||Object.create(null))[s.type]||[],u=x.event.special[s.type]||{};for(c[0]=s,t=1;t=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:x.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&c.push({elem:l,handlers:o})}return l=this,s\s*$/g;function Oe(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&x(e).children("tbody")[0]||e}function Te(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Ee(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Ae(e,t){var n,r,i,o,a,c;if(1===t.nodeType){if(K.hasData(e)&&(c=K.get(e).events))for(i in K.remove(t,"handle events"),c)for(n=0,r=c[i].length;n1&&"string"===typeof p&&!v.checkClone&&qe.test(p))return e.each((function(i){var o=e.eq(i);z&&(t[0]=p.call(this,i,o.html())),Le(o,t,n,r)}));if(h&&(o=(i=be(t,e[0].ownerDocument,!1,e,r)).firstChild,1===i.childNodes.length&&(i=o),o||r)){for(c=(a=x.map(ge(i,"script"),Te)).length;f0&&me(a,!s&&ge(e,"script")),c},cleanData:function(e){for(var t,n,r,i=x.event.special,o=0;void 0!==(n=e[o]);o++)if($(n)){if(t=n[K.expando]){if(t.events)for(r in t.events)i[r]?x.event.remove(n,r):x.removeEvent(n,r,t.handle);n[K.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),x.fn.extend({detach:function(e){return De(this,e,!0)},remove:function(e){return De(this,e)},text:function(e){return U(this,(function(e){return void 0===e?x.text(this):this.empty().each((function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)}))}),null,e,arguments.length)},append:function(){return Le(this,arguments,(function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Oe(this,e).appendChild(e)}))},prepend:function(){return Le(this,arguments,(function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Oe(this,e);t.insertBefore(e,t.firstChild)}}))},before:function(){return Le(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this)}))},after:function(){return Le(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)}))},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(ge(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map((function(){return x.clone(this,e,t)}))},html:function(e){return U(this,(function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"===typeof e&&!Ce.test(e)&&!ve[(pe.exec(e)||["",""])[1].toLowerCase()]){e=x.htmlPrefilter(e);try{for(;n=0&&(s+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-s-c-.5))||0),s}function Je(e,t,n){var r=Ve(e),i=(!v.boxSizingReliable()||n)&&"border-box"===x.css(e,"boxSizing",!1,r),o=i,a=Re(e,t,r),c="offset"+t[0].toUpperCase()+t.slice(1);if(Pe.test(a)){if(!n)return a;a="auto"}return(!v.boxSizingReliable()&&i||!v.reliableTrDimensions()&&S(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===x.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===x.css(e,"boxSizing",!1,r),(o=c in e)&&(a=e[c])),(a=parseFloat(a)||0)+Qe(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Re(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,c=Z(t),s=Ze.test(t),l=e.style;if(s||(t=Ge(c)),a=x.cssHooks[t]||x.cssHooks[c],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=re.exec(n))&&i[1]&&(n=le(e,t,i),o="number"),null!=n&&n===n&&("number"!==o||s||(n+=i&&i[3]||(x.cssNumber[c]?"":"px")),v.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(s?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,c=Z(t);return Ze.test(t)||(t=Ge(c)),(a=x.cssHooks[t]||x.cssHooks[c])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Re(e,t,r)),"normal"===i&&t in Xe&&(i=Xe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),x.each(["height","width"],(function(e,t){x.cssHooks[t]={get:function(e,n,r){if(n)return!Ye.test(x.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Je(e,t,r):Ne(e,$e,(function(){return Je(e,t,r)}))},set:function(e,n,r){var i,o=Ve(e),a=!v.scrollboxSize()&&"absolute"===o.position,c=(a||r)&&"border-box"===x.css(e,"boxSizing",!1,o),s=r?Qe(e,t,r,c,o):0;return c&&a&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Qe(e,t,"border",!1,o)-.5)),s&&(i=re.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=x.css(e,t)),Ke(0,n,s)}}})),x.cssHooks.marginLeft=Be(v.reliableMarginLeft,(function(e,t){if(t)return(parseFloat(Re(e,"marginLeft"))||e.getBoundingClientRect().left-Ne(e,{marginLeft:0},(function(){return e.getBoundingClientRect().left})))+"px"})),x.each({margin:"",padding:"",border:"Width"},(function(e,t){x.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"===typeof n?n.split(" "):[n];r<4;r++)i[e+ie[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(x.cssHooks[e+t].set=Ke)})),x.fn.extend({css:function(e,t){return U(this,(function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ve(e),i=t.length;a1)}}),x.Tween=et,et.prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||x.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}},et.prototype.init.prototype=et.prototype,et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=x.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):1!==e.elem.nodeType||!x.cssHooks[e.prop]&&null==e.elem.style[Ge(e.prop)]?e.elem[e.prop]=e.now:x.style(e.elem,e.prop,e.now+e.unit)}}},et.propHooks.scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},x.fx=et.prototype.init,x.fx.step={};var tt,nt,rt=/^(?:toggle|show|hide)$/,it=/queueHooks$/;function ot(){nt&&(!1===y.hidden&&n.requestAnimationFrame?n.requestAnimationFrame(ot):n.setTimeout(ot,x.fx.interval),x.fx.tick())}function at(){return n.setTimeout((function(){tt=void 0})),tt=Date.now()}function ct(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ie[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function st(e,t,n){for(var r,i=(lt.tweeners[t]||[]).concat(lt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each((function(){x.removeAttr(this,e)}))}}),x.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"===typeof e.getAttribute?x.prop(e,t,n):(1===o&&x.isXMLDoc(e)||(i=x.attrHooks[t.toLowerCase()]||(x.expr.match.bool.test(t)?ut:void 0)),void 0!==n?null===n?void x.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=x.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!v.radioValue&&"radio"===t&&S(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),ut={set:function(e,t,n){return!1===t?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.bool.source.match(/\w+/g),(function(e,t){var n=ft[t]||x.find.attr;ft[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ft[a],ft[a]=i,i=null!=n(e,t,r)?a:null,ft[a]=o),i}}));var ht=/^(?:input|select|textarea|button)$/i,dt=/^(?:a|area)$/i;function pt(e){return(e.match(P)||[]).join(" ")}function zt(e){return e.getAttribute&&e.getAttribute("class")||""}function vt(e){return Array.isArray(e)?e:"string"===typeof e&&e.match(P)||[]}x.fn.extend({prop:function(e,t){return U(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each((function(){delete this[x.propFix[e]||e]}))}}),x.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&x.isXMLDoc(e)||(t=x.propFix[t]||t,i=x.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||dt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),v.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],(function(){x.propFix[this.toLowerCase()]=this})),x.fn.extend({addClass:function(e){var t,n,r,i,o,a,c,s=0;if(g(e))return this.each((function(t){x(this).addClass(e.call(this,t,zt(this)))}));if((t=vt(e)).length)for(;n=this[s++];)if(i=zt(n),r=1===n.nodeType&&" "+pt(i)+" "){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(c=pt(r))&&n.setAttribute("class",c)}return this},removeClass:function(e){var t,n,r,i,o,a,c,s=0;if(g(e))return this.each((function(t){x(this).removeClass(e.call(this,t,zt(this)))}));if(!arguments.length)return this.attr("class","");if((t=vt(e)).length)for(;n=this[s++];)if(i=zt(n),r=1===n.nodeType&&" "+pt(i)+" "){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");i!==(c=pt(r))&&n.setAttribute("class",c)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"===typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each((function(n){x(this).toggleClass(e.call(this,n,zt(this),t),t)})):this.each((function(){var t,i,o,a;if(r)for(i=0,o=x(this),a=vt(e);t=a[i++];)o.hasClass(t)?o.removeClass(t):o.addClass(t);else void 0!==e&&"boolean"!==n||((t=zt(this))&&K.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":K.get(this,"__className__")||""))}))},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+pt(zt(n))+" ").indexOf(t)>-1)return!0;return!1}});var gt=/\r/g;x.fn.extend({val:function(e){var t,n,r,i=this[0];return arguments.length?(r=g(e),this.each((function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,x(this).val()):e)?i="":"number"===typeof i?i+="":Array.isArray(i)&&(i=x.map(i,(function(e){return null==e?"":e+""}))),(t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))}))):i?(t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"===typeof(n=i.value)?n.replace(gt,""):null==n?"":n:void 0}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:pt(x.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,c=a?null:[],s=a?o+1:i.length;for(r=o<0?s:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),x.each(["radio","checkbox"],(function(){x.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=x.inArray(x(e).val(),t)>-1}},v.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})})),v.focusin="onfocusin"in n;var mt=/^(?:focusinfocus|focusoutblur)$/,yt=function(e){e.stopPropagation()};x.extend(x.event,{trigger:function(e,t,r,i){var o,a,c,s,l,u,f,h,p=[r||y],z=d.call(e,"type")?e.type:e,v=d.call(e,"namespace")?e.namespace.split("."):[];if(a=h=c=r=r||y,3!==r.nodeType&&8!==r.nodeType&&!mt.test(z+x.event.triggered)&&(z.indexOf(".")>-1&&(v=z.split("."),z=v.shift(),v.sort()),l=z.indexOf(":")<0&&"on"+z,(e=e[x.expando]?e:new x.Event(z,"object"===typeof e&&e)).isTrigger=i?2:3,e.namespace=v.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+v.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=r),t=null==t?[e]:x.makeArray(t,[e]),f=x.event.special[z]||{},i||!f.trigger||!1!==f.trigger.apply(r,t))){if(!i&&!f.noBubble&&!m(r)){for(s=f.delegateType||z,mt.test(s+z)||(a=a.parentNode);a;a=a.parentNode)p.push(a),c=a;c===(r.ownerDocument||y)&&p.push(c.defaultView||c.parentWindow||n)}for(o=0;(a=p[o++])&&!e.isPropagationStopped();)h=a,e.type=o>1?s:f.bindType||z,(u=(K.get(a,"events")||Object.create(null))[e.type]&&K.get(a,"handle"))&&u.apply(a,t),(u=l&&a[l])&&u.apply&&$(a)&&(e.result=u.apply(a,t),!1===e.result&&e.preventDefault());return e.type=z,i||e.isDefaultPrevented()||f._default&&!1!==f._default.apply(p.pop(),t)||!$(r)||l&&g(r[z])&&!m(r)&&((c=r[l])&&(r[l]=null),x.event.triggered=z,e.isPropagationStopped()&&h.addEventListener(z,yt),r[z](),e.isPropagationStopped()&&h.removeEventListener(z,yt),x.event.triggered=void 0,c&&(r[l]=c)),e.result}},simulate:function(e,t,n){var r=x.extend(new x.Event,n,{type:e,isSimulated:!0});x.event.trigger(r,null,t)}}),x.fn.extend({trigger:function(e,t){return this.each((function(){x.event.trigger(e,t,this)}))},triggerHandler:function(e,t){var n=this[0];if(n)return x.event.trigger(e,t,n,!0)}}),v.focusin||x.each({focus:"focusin",blur:"focusout"},(function(e,t){var n=function(e){x.event.simulate(t,e.target,x.event.fix(e))};x.event.special[t]={setup:function(){var r=this.ownerDocument||this.document||this,i=K.access(r,t);i||r.addEventListener(e,n,!0),K.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this.document||this,i=K.access(r,t)-1;i?K.access(r,t,i):(r.removeEventListener(e,n,!0),K.remove(r,t))}}}));var bt=n.location,wt={guid:Date.now()},kt=/\?/;x.parseXML=function(e){var t,r;if(!e||"string"!==typeof e)return null;try{t=(new n.DOMParser).parseFromString(e,"text/xml")}catch(i){}return r=t&&t.getElementsByTagName("parsererror")[0],t&&!r||x.error("Invalid XML: "+(r?x.map(r.childNodes,(function(e){return e.textContent})).join("\n"):e)),t};var xt=/\[\]$/,jt=/\r?\n/g,Mt=/^(?:submit|button|image|reset|file)$/i,_t=/^(?:input|select|textarea|keygen)/i;function Ct(e,t,n,r){var i;if(Array.isArray(t))x.each(t,(function(t,i){n||xt.test(e)?r(e,i):Ct(e+"["+("object"===typeof i&&null!=i?t:"")+"]",i,n,r)}));else if(n||"object"!==k(t))r(e,t);else for(i in t)Ct(e+"["+i+"]",t[i],n,r)}x.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,(function(){i(this.name,this.value)}));else for(n in e)Ct(n,e[n],t,i);return r.join("&")},x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map((function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this})).filter((function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&_t.test(this.nodeName)&&!Mt.test(e)&&(this.checked||!de.test(e))})).map((function(e,t){var n=x(this).val();return null==n?null:Array.isArray(n)?x.map(n,(function(e){return{name:t.name,value:e.replace(jt,"\r\n")}})):{name:t.name,value:n.replace(jt,"\r\n")}})).get()}});var qt=/%20/g,St=/#.*$/,Ot=/([?&])_=[^&]*/,Tt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Et=/^(?:GET|HEAD)$/,At=/^\/\//,Ht={},Lt={},Dt="*/".concat("*"),Pt=y.createElement("a");function Vt(e){return function(t,n){"string"!==typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(P)||[];if(g(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function Nt(e,t,n,r){var i={},o=e===Lt;function a(c){var s;return i[c]=!0,x.each(e[c]||[],(function(e,c){var l=c(t,n,r);return"string"!==typeof l||o||i[l]?o?!(s=l):void 0:(t.dataTypes.unshift(l),a(l),!1)})),s}return a(t.dataTypes[0])||!i["*"]&&a("*")}function It(e,t){var n,r,i=x.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&x.extend(!0,e,r),e}Pt.href=bt.href,x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:bt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(bt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?It(It(e,x.ajaxSettings),t):It(x.ajaxSettings,e)},ajaxPrefilter:Vt(Ht),ajaxTransport:Vt(Lt),ajax:function(e,t){"object"===typeof e&&(t=e,e=void 0),t=t||{};var r,i,o,a,c,s,l,u,f,h,d=x.ajaxSetup({},t),p=d.context||d,z=d.context&&(p.nodeType||p.jquery)?x(p):x.event,v=x.Deferred(),g=x.Callbacks("once memory"),m=d.statusCode||{},b={},w={},k="canceled",j={readyState:0,getResponseHeader:function(e){var t;if(l){if(!a)for(a={};t=Tt.exec(o);)a[t[1].toLowerCase()+" "]=(a[t[1].toLowerCase()+" "]||[]).concat(t[2]);t=a[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return l?o:null},setRequestHeader:function(e,t){return null==l&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==l&&(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(l)j.always(e[j.status]);else for(t in e)m[t]=[m[t],e[t]];return this},abort:function(e){var t=e||k;return r&&r.abort(t),M(0,t),this}};if(v.promise(j),d.url=((e||d.url||bt.href)+"").replace(At,bt.protocol+"//"),d.type=t.method||t.type||d.method||d.type,d.dataTypes=(d.dataType||"*").toLowerCase().match(P)||[""],null==d.crossDomain){s=y.createElement("a");try{s.href=d.url,s.href=s.href,d.crossDomain=Pt.protocol+"//"+Pt.host!==s.protocol+"//"+s.host}catch(_){d.crossDomain=!0}}if(d.data&&d.processData&&"string"!==typeof d.data&&(d.data=x.param(d.data,d.traditional)),Nt(Ht,d,t,j),l)return j;for(f in(u=x.event&&d.global)&&0===x.active++&&x.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Et.test(d.type),i=d.url.replace(St,""),d.hasContent?d.data&&d.processData&&0===(d.contentType||"").indexOf("application/x-www-form-urlencoded")&&(d.data=d.data.replace(qt,"+")):(h=d.url.slice(i.length),d.data&&(d.processData||"string"===typeof d.data)&&(i+=(kt.test(i)?"&":"?")+d.data,delete d.data),!1===d.cache&&(i=i.replace(Ot,"$1"),h=(kt.test(i)?"&":"?")+"_="+wt.guid+++h),d.url=i+h),d.ifModified&&(x.lastModified[i]&&j.setRequestHeader("If-Modified-Since",x.lastModified[i]),x.etag[i]&&j.setRequestHeader("If-None-Match",x.etag[i])),(d.data&&d.hasContent&&!1!==d.contentType||t.contentType)&&j.setRequestHeader("Content-Type",d.contentType),j.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Dt+"; q=0.01":""):d.accepts["*"]),d.headers)j.setRequestHeader(f,d.headers[f]);if(d.beforeSend&&(!1===d.beforeSend.call(p,j,d)||l))return j.abort();if(k="abort",g.add(d.complete),j.done(d.success),j.fail(d.error),r=Nt(Lt,d,t,j)){if(j.readyState=1,u&&z.trigger("ajaxSend",[j,d]),l)return j;d.async&&d.timeout>0&&(c=n.setTimeout((function(){j.abort("timeout")}),d.timeout));try{l=!1,r.send(b,M)}catch(_){if(l)throw _;M(-1,_)}}else M(-1,"No Transport");function M(e,t,a,s){var f,h,y,b,w,k=t;l||(l=!0,c&&n.clearTimeout(c),r=void 0,o=s||"",j.readyState=e>0?4:0,f=e>=200&&e<300||304===e,a&&(b=function(e,t,n){for(var r,i,o,a,c=e.contents,s=e.dataTypes;"*"===s[0];)s.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in c)if(c[i]&&c[i].test(r)){s.unshift(i);break}if(s[0]in n)o=s[0];else{for(i in n){if(!s[0]||e.converters[i+" "+s[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==s[0]&&s.unshift(o),n[o]}(d,j,a)),!f&&x.inArray("script",d.dataTypes)>-1&&x.inArray("json",d.dataTypes)<0&&(d.converters["text script"]=function(){}),b=function(e,t,n,r){var i,o,a,c,s,l={},u=e.dataTypes.slice();if(u[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(o=u.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!s&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),s=o,o=u.shift())if("*"===o)o=s;else if("*"!==s&&s!==o){if(!(a=l[s+" "+o]||l["* "+o]))for(i in l)if((c=i.split(" "))[1]===o&&(a=l[s+" "+c[0]]||l["* "+c[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=c[0],u.unshift(c[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(_){return{state:"parsererror",error:a?_:"No conversion from "+s+" to "+o}}}return{state:"success",data:t}}(d,b,j,f),f?(d.ifModified&&((w=j.getResponseHeader("Last-Modified"))&&(x.lastModified[i]=w),(w=j.getResponseHeader("etag"))&&(x.etag[i]=w)),204===e||"HEAD"===d.type?k="nocontent":304===e?k="notmodified":(k=b.state,h=b.data,f=!(y=b.error))):(y=k,!e&&k||(k="error",e<0&&(e=0))),j.status=e,j.statusText=(t||k)+"",f?v.resolveWith(p,[h,k,j]):v.rejectWith(p,[j,k,y]),j.statusCode(m),m=void 0,u&&z.trigger(f?"ajaxSuccess":"ajaxError",[j,d,f?h:y]),g.fireWith(p,[j,k]),u&&(z.trigger("ajaxComplete",[j,d]),--x.active||x.event.trigger("ajaxStop")))}return j},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,t){return x.get(e,void 0,t,"script")}}),x.each(["get","post"],(function(e,t){x[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),x.ajax(x.extend({url:e,type:t,dataType:i,data:n,success:r},x.isPlainObject(e)&&e))}})),x.ajaxPrefilter((function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")})),x._evalUrl=function(e,t,n){return x.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){x.globalEval(e,t,n)}})},x.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map((function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e})).append(this)),this},wrapInner:function(e){return g(e)?this.each((function(t){x(this).wrapInner(e.call(this,t))})):this.each((function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)}))},wrap:function(e){var t=g(e);return this.each((function(n){x(this).wrapAll(t?e.call(this,n):e)}))},unwrap:function(e){return this.parent(e).not("body").each((function(){x(this).replaceWith(this.childNodes)})),this}}),x.expr.pseudos.hidden=function(e){return!x.expr.pseudos.visible(e)},x.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},x.ajaxSettings.xhr=function(){try{return new n.XMLHttpRequest}catch(e){}};var Rt={0:200,1223:204},Bt=x.ajaxSettings.xhr();v.cors=!!Bt&&"withCredentials"in Bt,v.ajax=Bt=!!Bt,x.ajaxTransport((function(e){var t,r;if(v.cors||Bt&&!e.crossDomain)return{send:function(i,o){var a,c=e.xhr();if(c.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(a in e.xhrFields)c[a]=e.xhrFields[a];for(a in e.mimeType&&c.overrideMimeType&&c.overrideMimeType(e.mimeType),e.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest"),i)c.setRequestHeader(a,i[a]);t=function(e){return function(){t&&(t=r=c.onload=c.onerror=c.onabort=c.ontimeout=c.onreadystatechange=null,"abort"===e?c.abort():"error"===e?"number"!==typeof c.status?o(0,"error"):o(c.status,c.statusText):o(Rt[c.status]||c.status,c.statusText,"text"!==(c.responseType||"text")||"string"!==typeof c.responseText?{binary:c.response}:{text:c.responseText},c.getAllResponseHeaders()))}},c.onload=t(),r=c.onerror=c.ontimeout=t("error"),void 0!==c.onabort?c.onabort=r:c.onreadystatechange=function(){4===c.readyState&&n.setTimeout((function(){t&&r()}))},t=t("abort");try{c.send(e.hasContent&&e.data||null)}catch(s){if(t)throw s}},abort:function(){t&&t()}}})),x.ajaxPrefilter((function(e){e.crossDomain&&(e.contents.script=!1)})),x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",(function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")})),x.ajaxTransport("script",(function(e){var t,n;if(e.crossDomain||e.scriptAttrs)return{send:function(r,i){t=x("\n\n The CSV file is of the form\n\n Date,SeriesA,SeriesB,SeriesC\n YYYYMMDD,A1,B1,C1\n YYYYMMDD,A2,B2,C2\n\n If the 'errorBars' option is set in the constructor, the input should be of\n the form\n Date,SeriesA,SeriesB,...\n YYYYMMDD,A1,sigmaA1,B1,sigmaB1,...\n YYYYMMDD,A2,sigmaA2,B2,sigmaB2,...\n\n If the 'fractions' option is set, the input should be of the form:\n\n Date,SeriesA,SeriesB,...\n YYYYMMDD,A1/B1,A2/B2,...\n YYYYMMDD,A1/B1,A2/B2,...\n\n And error bars will be calculated automatically using a binomial distribution.\n\n For further documentation and examples, see http://dygraphs.com/\n */\n\n'use strict';\n\nObject.defineProperty(exports, '__esModule', {\n value: true\n});\n\nvar _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nvar _dygraphLayout = require('./dygraph-layout');\n\nvar _dygraphLayout2 = _interopRequireDefault(_dygraphLayout);\n\nvar _dygraphCanvas = require('./dygraph-canvas');\n\nvar _dygraphCanvas2 = _interopRequireDefault(_dygraphCanvas);\n\nvar _dygraphOptions = require('./dygraph-options');\n\nvar _dygraphOptions2 = _interopRequireDefault(_dygraphOptions);\n\nvar _dygraphInteractionModel = require('./dygraph-interaction-model');\n\nvar _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel);\n\nvar _dygraphTickers = require('./dygraph-tickers');\n\nvar DygraphTickers = _interopRequireWildcard(_dygraphTickers);\n\nvar _dygraphUtils = require('./dygraph-utils');\n\nvar utils = _interopRequireWildcard(_dygraphUtils);\n\nvar _dygraphDefaultAttrs = require('./dygraph-default-attrs');\n\nvar _dygraphDefaultAttrs2 = _interopRequireDefault(_dygraphDefaultAttrs);\n\nvar _dygraphOptionsReference = require('./dygraph-options-reference');\n\nvar _dygraphOptionsReference2 = _interopRequireDefault(_dygraphOptionsReference);\n\nvar _iframeTarp = require('./iframe-tarp');\n\nvar _iframeTarp2 = _interopRequireDefault(_iframeTarp);\n\nvar _datahandlerDefault = require('./datahandler/default');\n\nvar _datahandlerDefault2 = _interopRequireDefault(_datahandlerDefault);\n\nvar _datahandlerBarsError = require('./datahandler/bars-error');\n\nvar _datahandlerBarsError2 = _interopRequireDefault(_datahandlerBarsError);\n\nvar _datahandlerBarsCustom = require('./datahandler/bars-custom');\n\nvar _datahandlerBarsCustom2 = _interopRequireDefault(_datahandlerBarsCustom);\n\nvar _datahandlerDefaultFractions = require('./datahandler/default-fractions');\n\nvar _datahandlerDefaultFractions2 = _interopRequireDefault(_datahandlerDefaultFractions);\n\nvar _datahandlerBarsFractions = require('./datahandler/bars-fractions');\n\nvar _datahandlerBarsFractions2 = _interopRequireDefault(_datahandlerBarsFractions);\n\nvar _datahandlerBars = require('./datahandler/bars');\n\nvar _datahandlerBars2 = _interopRequireDefault(_datahandlerBars);\n\nvar _pluginsAnnotations = require('./plugins/annotations');\n\nvar _pluginsAnnotations2 = _interopRequireDefault(_pluginsAnnotations);\n\nvar _pluginsAxes = require('./plugins/axes');\n\nvar _pluginsAxes2 = _interopRequireDefault(_pluginsAxes);\n\nvar _pluginsChartLabels = require('./plugins/chart-labels');\n\nvar _pluginsChartLabels2 = _interopRequireDefault(_pluginsChartLabels);\n\nvar _pluginsGrid = require('./plugins/grid');\n\nvar _pluginsGrid2 = _interopRequireDefault(_pluginsGrid);\n\nvar _pluginsLegend = require('./plugins/legend');\n\nvar _pluginsLegend2 = _interopRequireDefault(_pluginsLegend);\n\nvar _pluginsRangeSelector = require('./plugins/range-selector');\n\nvar _pluginsRangeSelector2 = _interopRequireDefault(_pluginsRangeSelector);\n\nvar _dygraphGviz = require('./dygraph-gviz');\n\nvar _dygraphGviz2 = _interopRequireDefault(_dygraphGviz);\n\n\"use strict\";\n\n/**\n * Creates an interactive, zoomable chart.\n *\n * @constructor\n * @param {div | String} div A div or the id of a div into which to construct\n * the chart.\n * @param {String | Function} file A file containing CSV data or a function\n * that returns this data. The most basic expected format for each line is\n * \"YYYY/MM/DD,val1,val2,...\". For more information, see\n * http://dygraphs.com/data.html.\n * @param {Object} attrs Various other attributes, e.g. errorBars determines\n * whether the input data contains error ranges. For a complete list of\n * options, see http://dygraphs.com/options.html.\n */\nvar Dygraph = function Dygraph(div, data, opts) {\n this.__init__(div, data, opts);\n};\n\nDygraph.NAME = \"Dygraph\";\nDygraph.VERSION = \"2.0.0\";\n\n// Various default values\nDygraph.DEFAULT_ROLL_PERIOD = 1;\nDygraph.DEFAULT_WIDTH = 480;\nDygraph.DEFAULT_HEIGHT = 320;\n\n// For max 60 Hz. animation:\nDygraph.ANIMATION_STEPS = 12;\nDygraph.ANIMATION_DURATION = 200;\n\n/**\n * Standard plotters. These may be used by clients.\n * Available plotters are:\n * - Dygraph.Plotters.linePlotter: draws central lines (most common)\n * - Dygraph.Plotters.errorPlotter: draws error bars\n * - Dygraph.Plotters.fillPlotter: draws fills under lines (used with fillGraph)\n *\n * By default, the plotter is [fillPlotter, errorPlotter, linePlotter].\n * This causes all the lines to be drawn over all the fills/error bars.\n */\nDygraph.Plotters = _dygraphCanvas2['default']._Plotters;\n\n// Used for initializing annotation CSS rules only once.\nDygraph.addedAnnotationCSS = false;\n\n/**\n * Initializes the Dygraph. This creates a new DIV and constructs the PlotKit\n * and context <canvas> inside of it. See the constructor for details.\n * on the parameters.\n * @param {Element} div the Element to render the graph into.\n * @param {string | Function} file Source data\n * @param {Object} attrs Miscellaneous other options\n * @private\n */\nDygraph.prototype.__init__ = function (div, file, attrs) {\n this.is_initial_draw_ = true;\n this.readyFns_ = [];\n\n // Support two-argument constructor\n if (attrs === null || attrs === undefined) {\n attrs = {};\n }\n\n attrs = Dygraph.copyUserAttrs_(attrs);\n\n if (typeof div == 'string') {\n div = document.getElementById(div);\n }\n\n if (!div) {\n throw new Error('Constructing dygraph with a non-existent div!');\n }\n\n // Copy the important bits into the object\n // TODO(danvk): most of these should just stay in the attrs_ dictionary.\n this.maindiv_ = div;\n this.file_ = file;\n this.rollPeriod_ = attrs.rollPeriod || Dygraph.DEFAULT_ROLL_PERIOD;\n this.previousVerticalX_ = -1;\n this.fractions_ = attrs.fractions || false;\n this.dateWindow_ = attrs.dateWindow || null;\n\n this.annotations_ = [];\n\n // Clear the div. This ensure that, if multiple dygraphs are passed the same\n // div, then only one will be drawn.\n div.innerHTML = \"\";\n\n // For historical reasons, the 'width' and 'height' options trump all CSS\n // rules _except_ for an explicit 'width' or 'height' on the div.\n // As an added convenience, if the div has zero height (like
does\n // without any styles), then we use a default height/width.\n if (div.style.width === '' && attrs.width) {\n div.style.width = attrs.width + \"px\";\n }\n if (div.style.height === '' && attrs.height) {\n div.style.height = attrs.height + \"px\";\n }\n if (div.style.height === '' && div.clientHeight === 0) {\n div.style.height = Dygraph.DEFAULT_HEIGHT + \"px\";\n if (div.style.width === '') {\n div.style.width = Dygraph.DEFAULT_WIDTH + \"px\";\n }\n }\n // These will be zero if the dygraph's div is hidden. In that case,\n // use the user-specified attributes if present. If not, use zero\n // and assume the user will call resize to fix things later.\n this.width_ = div.clientWidth || attrs.width || 0;\n this.height_ = div.clientHeight || attrs.height || 0;\n\n // TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_.\n if (attrs.stackedGraph) {\n attrs.fillGraph = true;\n // TODO(nikhilk): Add any other stackedGraph checks here.\n }\n\n // DEPRECATION WARNING: All option processing should be moved from\n // attrs_ and user_attrs_ to options_, which holds all this information.\n //\n // Dygraphs has many options, some of which interact with one another.\n // To keep track of everything, we maintain two sets of options:\n //\n // this.user_attrs_ only options explicitly set by the user.\n // this.attrs_ defaults, options derived from user_attrs_, data.\n //\n // Options are then accessed this.attr_('attr'), which first looks at\n // user_attrs_ and then computed attrs_. This way Dygraphs can set intelligent\n // defaults without overriding behavior that the user specifically asks for.\n this.user_attrs_ = {};\n utils.update(this.user_attrs_, attrs);\n\n // This sequence ensures that Dygraph.DEFAULT_ATTRS is never modified.\n this.attrs_ = {};\n utils.updateDeep(this.attrs_, _dygraphDefaultAttrs2['default']);\n\n this.boundaryIds_ = [];\n this.setIndexByName_ = {};\n this.datasetIndex_ = [];\n\n this.registeredEvents_ = [];\n this.eventListeners_ = {};\n\n this.attributes_ = new _dygraphOptions2['default'](this);\n\n // Create the containing DIV and other interactive elements\n this.createInterface_();\n\n // Activate plugins.\n this.plugins_ = [];\n var plugins = Dygraph.PLUGINS.concat(this.getOption('plugins'));\n for (var i = 0; i < plugins.length; i++) {\n // the plugins option may contain either plugin classes or instances.\n // Plugin instances contain an activate method.\n var Plugin = plugins[i]; // either a constructor or an instance.\n var pluginInstance;\n if (typeof Plugin.activate !== 'undefined') {\n pluginInstance = Plugin;\n } else {\n pluginInstance = new Plugin();\n }\n\n var pluginDict = {\n plugin: pluginInstance,\n events: {},\n options: {},\n pluginOptions: {}\n };\n\n var handlers = pluginInstance.activate(this);\n for (var eventName in handlers) {\n if (!handlers.hasOwnProperty(eventName)) continue;\n // TODO(danvk): validate eventName.\n pluginDict.events[eventName] = handlers[eventName];\n }\n\n this.plugins_.push(pluginDict);\n }\n\n // At this point, plugins can no longer register event handlers.\n // Construct a map from event -> ordered list of [callback, plugin].\n for (var i = 0; i < this.plugins_.length; i++) {\n var plugin_dict = this.plugins_[i];\n for (var eventName in plugin_dict.events) {\n if (!plugin_dict.events.hasOwnProperty(eventName)) continue;\n var callback = plugin_dict.events[eventName];\n\n var pair = [plugin_dict.plugin, callback];\n if (!(eventName in this.eventListeners_)) {\n this.eventListeners_[eventName] = [pair];\n } else {\n this.eventListeners_[eventName].push(pair);\n }\n }\n }\n\n this.createDragInterface_();\n\n this.start_();\n};\n\n/**\n * Triggers a cascade of events to the various plugins which are interested in them.\n * Returns true if the \"default behavior\" should be prevented, i.e. if one\n * of the event listeners called event.preventDefault().\n * @private\n */\nDygraph.prototype.cascadeEvents_ = function (name, extra_props) {\n if (!(name in this.eventListeners_)) return false;\n\n // QUESTION: can we use objects & prototypes to speed this up?\n var e = {\n dygraph: this,\n cancelable: false,\n defaultPrevented: false,\n preventDefault: function preventDefault() {\n if (!e.cancelable) throw \"Cannot call preventDefault on non-cancelable event.\";\n e.defaultPrevented = true;\n },\n propagationStopped: false,\n stopPropagation: function stopPropagation() {\n e.propagationStopped = true;\n }\n };\n utils.update(e, extra_props);\n\n var callback_plugin_pairs = this.eventListeners_[name];\n if (callback_plugin_pairs) {\n for (var i = callback_plugin_pairs.length - 1; i >= 0; i--) {\n var plugin = callback_plugin_pairs[i][0];\n var callback = callback_plugin_pairs[i][1];\n callback.call(plugin, e);\n if (e.propagationStopped) break;\n }\n }\n return e.defaultPrevented;\n};\n\n/**\n * Fetch a plugin instance of a particular class. Only for testing.\n * @private\n * @param {!Class} type The type of the plugin.\n * @return {Object} Instance of the plugin, or null if there is none.\n */\nDygraph.prototype.getPluginInstance_ = function (type) {\n for (var i = 0; i < this.plugins_.length; i++) {\n var p = this.plugins_[i];\n if (p.plugin instanceof type) {\n return p.plugin;\n }\n }\n return null;\n};\n\n/**\n * Returns the zoomed status of the chart for one or both axes.\n *\n * Axis is an optional parameter. Can be set to 'x' or 'y'.\n *\n * The zoomed status for an axis is set whenever a user zooms using the mouse\n * or when the dateWindow or valueRange are updated. Double-clicking or calling\n * resetZoom() resets the zoom status for the chart.\n */\nDygraph.prototype.isZoomed = function (axis) {\n var isZoomedX = !!this.dateWindow_;\n if (axis === 'x') return isZoomedX;\n\n var isZoomedY = this.axes_.map(function (axis) {\n return !!axis.valueRange;\n }).indexOf(true) >= 0;\n if (axis === null || axis === undefined) {\n return isZoomedX || isZoomedY;\n }\n if (axis === 'y') return isZoomedY;\n\n throw new Error('axis parameter is [' + axis + '] must be null, \\'x\\' or \\'y\\'.');\n};\n\n/**\n * Returns information about the Dygraph object, including its containing ID.\n */\nDygraph.prototype.toString = function () {\n var maindiv = this.maindiv_;\n var id = maindiv && maindiv.id ? maindiv.id : maindiv;\n return \"[Dygraph \" + id + \"]\";\n};\n\n/**\n * @private\n * Returns the value of an option. This may be set by the user (either in the\n * constructor or by calling updateOptions) or by dygraphs, and may be set to a\n * per-series value.\n * @param {string} name The name of the option, e.g. 'rollPeriod'.\n * @param {string} [seriesName] The name of the series to which the option\n * will be applied. If no per-series value of this option is available, then\n * the global value is returned. This is optional.\n * @return { ... } The value of the option.\n */\nDygraph.prototype.attr_ = function (name, seriesName) {\n // For \"production\" code, this gets removed by uglifyjs.\n if (typeof process !== 'undefined') {\n if (process.env.NODE_ENV != 'production') {\n if (typeof _dygraphOptionsReference2['default'] === 'undefined') {\n console.error('Must include options reference JS for testing');\n } else if (!_dygraphOptionsReference2['default'].hasOwnProperty(name)) {\n console.error('Dygraphs is using property ' + name + ', which has no ' + 'entry in the Dygraphs.OPTIONS_REFERENCE listing.');\n // Only log this error once.\n _dygraphOptionsReference2['default'][name] = true;\n }\n }\n }\n return seriesName ? this.attributes_.getForSeries(name, seriesName) : this.attributes_.get(name);\n};\n\n/**\n * Returns the current value for an option, as set in the constructor or via\n * updateOptions. You may pass in an (optional) series name to get per-series\n * values for the option.\n *\n * All values returned by this method should be considered immutable. If you\n * modify them, there is no guarantee that the changes will be honored or that\n * dygraphs will remain in a consistent state. If you want to modify an option,\n * use updateOptions() instead.\n *\n * @param {string} name The name of the option (e.g. 'strokeWidth')\n * @param {string=} opt_seriesName Series name to get per-series values.\n * @return {*} The value of the option.\n */\nDygraph.prototype.getOption = function (name, opt_seriesName) {\n return this.attr_(name, opt_seriesName);\n};\n\n/**\n * Like getOption(), but specifically returns a number.\n * This is a convenience function for working with the Closure Compiler.\n * @param {string} name The name of the option (e.g. 'strokeWidth')\n * @param {string=} opt_seriesName Series name to get per-series values.\n * @return {number} The value of the option.\n * @private\n */\nDygraph.prototype.getNumericOption = function (name, opt_seriesName) {\n return (/** @type{number} */this.getOption(name, opt_seriesName)\n );\n};\n\n/**\n * Like getOption(), but specifically returns a string.\n * This is a convenience function for working with the Closure Compiler.\n * @param {string} name The name of the option (e.g. 'strokeWidth')\n * @param {string=} opt_seriesName Series name to get per-series values.\n * @return {string} The value of the option.\n * @private\n */\nDygraph.prototype.getStringOption = function (name, opt_seriesName) {\n return (/** @type{string} */this.getOption(name, opt_seriesName)\n );\n};\n\n/**\n * Like getOption(), but specifically returns a boolean.\n * This is a convenience function for working with the Closure Compiler.\n * @param {string} name The name of the option (e.g. 'strokeWidth')\n * @param {string=} opt_seriesName Series name to get per-series values.\n * @return {boolean} The value of the option.\n * @private\n */\nDygraph.prototype.getBooleanOption = function (name, opt_seriesName) {\n return (/** @type{boolean} */this.getOption(name, opt_seriesName)\n );\n};\n\n/**\n * Like getOption(), but specifically returns a function.\n * This is a convenience function for working with the Closure Compiler.\n * @param {string} name The name of the option (e.g. 'strokeWidth')\n * @param {string=} opt_seriesName Series name to get per-series values.\n * @return {function(...)} The value of the option.\n * @private\n */\nDygraph.prototype.getFunctionOption = function (name, opt_seriesName) {\n return (/** @type{function(...)} */this.getOption(name, opt_seriesName)\n );\n};\n\nDygraph.prototype.getOptionForAxis = function (name, axis) {\n return this.attributes_.getForAxis(name, axis);\n};\n\n/**\n * @private\n * @param {string} axis The name of the axis (i.e. 'x', 'y' or 'y2')\n * @return { ... } A function mapping string -> option value\n */\nDygraph.prototype.optionsViewForAxis_ = function (axis) {\n var self = this;\n return function (opt) {\n var axis_opts = self.user_attrs_.axes;\n if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) {\n return axis_opts[axis][opt];\n }\n\n // I don't like that this is in a second spot.\n if (axis === 'x' && opt === 'logscale') {\n // return the default value.\n // TODO(konigsberg): pull the default from a global default.\n return false;\n }\n\n // user-specified attributes always trump defaults, even if they're less\n // specific.\n if (typeof self.user_attrs_[opt] != 'undefined') {\n return self.user_attrs_[opt];\n }\n\n axis_opts = self.attrs_.axes;\n if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) {\n return axis_opts[axis][opt];\n }\n // check old-style axis options\n // TODO(danvk): add a deprecation warning if either of these match.\n if (axis == 'y' && self.axes_[0].hasOwnProperty(opt)) {\n return self.axes_[0][opt];\n } else if (axis == 'y2' && self.axes_[1].hasOwnProperty(opt)) {\n return self.axes_[1][opt];\n }\n return self.attr_(opt);\n };\n};\n\n/**\n * Returns the current rolling period, as set by the user or an option.\n * @return {number} The number of points in the rolling window\n */\nDygraph.prototype.rollPeriod = function () {\n return this.rollPeriod_;\n};\n\n/**\n * Returns the currently-visible x-range. This can be affected by zooming,\n * panning or a call to updateOptions.\n * Returns a two-element array: [left, right].\n * If the Dygraph has dates on the x-axis, these will be millis since epoch.\n */\nDygraph.prototype.xAxisRange = function () {\n return this.dateWindow_ ? this.dateWindow_ : this.xAxisExtremes();\n};\n\n/**\n * Returns the lower- and upper-bound x-axis values of the data set.\n */\nDygraph.prototype.xAxisExtremes = function () {\n var pad = this.getNumericOption('xRangePad') / this.plotter_.area.w;\n if (this.numRows() === 0) {\n return [0 - pad, 1 + pad];\n }\n var left = this.rawData_[0][0];\n var right = this.rawData_[this.rawData_.length - 1][0];\n if (pad) {\n // Must keep this in sync with dygraph-layout _evaluateLimits()\n var range = right - left;\n left -= range * pad;\n right += range * pad;\n }\n return [left, right];\n};\n\n/**\n * Returns the lower- and upper-bound y-axis values for each axis. These are\n * the ranges you'll get if you double-click to zoom out or call resetZoom().\n * The return value is an array of [low, high] tuples, one for each y-axis.\n */\nDygraph.prototype.yAxisExtremes = function () {\n // TODO(danvk): this is pretty inefficient\n var packed = this.gatherDatasets_(this.rolledSeries_, null);\n var extremes = packed.extremes;\n\n var saveAxes = this.axes_;\n this.computeYAxisRanges_(extremes);\n var newAxes = this.axes_;\n this.axes_ = saveAxes;\n return newAxes.map(function (axis) {\n return axis.extremeRange;\n });\n};\n\n/**\n * Returns the currently-visible y-range for an axis. This can be affected by\n * zooming, panning or a call to updateOptions. Axis indices are zero-based. If\n * called with no arguments, returns the range of the first axis.\n * Returns a two-element array: [bottom, top].\n */\nDygraph.prototype.yAxisRange = function (idx) {\n if (typeof idx == \"undefined\") idx = 0;\n if (idx < 0 || idx >= this.axes_.length) {\n return null;\n }\n var axis = this.axes_[idx];\n return [axis.computedValueRange[0], axis.computedValueRange[1]];\n};\n\n/**\n * Returns the currently-visible y-ranges for each axis. This can be affected by\n * zooming, panning, calls to updateOptions, etc.\n * Returns an array of [bottom, top] pairs, one for each y-axis.\n */\nDygraph.prototype.yAxisRanges = function () {\n var ret = [];\n for (var i = 0; i < this.axes_.length; i++) {\n ret.push(this.yAxisRange(i));\n }\n return ret;\n};\n\n// TODO(danvk): use these functions throughout dygraphs.\n/**\n * Convert from data coordinates to canvas/div X/Y coordinates.\n * If specified, do this conversion for the coordinate system of a particular\n * axis. Uses the first axis by default.\n * Returns a two-element array: [X, Y]\n *\n * Note: use toDomXCoord instead of toDomCoords(x, null) and use toDomYCoord\n * instead of toDomCoords(null, y, axis).\n */\nDygraph.prototype.toDomCoords = function (x, y, axis) {\n return [this.toDomXCoord(x), this.toDomYCoord(y, axis)];\n};\n\n/**\n * Convert from data x coordinates to canvas/div X coordinate.\n * If specified, do this conversion for the coordinate system of a particular\n * axis.\n * Returns a single value or null if x is null.\n */\nDygraph.prototype.toDomXCoord = function (x) {\n if (x === null) {\n return null;\n }\n\n var area = this.plotter_.area;\n var xRange = this.xAxisRange();\n return area.x + (x - xRange[0]) / (xRange[1] - xRange[0]) * area.w;\n};\n\n/**\n * Convert from data x coordinates to canvas/div Y coordinate and optional\n * axis. Uses the first axis by default.\n *\n * returns a single value or null if y is null.\n */\nDygraph.prototype.toDomYCoord = function (y, axis) {\n var pct = this.toPercentYCoord(y, axis);\n\n if (pct === null) {\n return null;\n }\n var area = this.plotter_.area;\n return area.y + pct * area.h;\n};\n\n/**\n * Convert from canvas/div coords to data coordinates.\n * If specified, do this conversion for the coordinate system of a particular\n * axis. Uses the first axis by default.\n * Returns a two-element array: [X, Y].\n *\n * Note: use toDataXCoord instead of toDataCoords(x, null) and use toDataYCoord\n * instead of toDataCoords(null, y, axis).\n */\nDygraph.prototype.toDataCoords = function (x, y, axis) {\n return [this.toDataXCoord(x), this.toDataYCoord(y, axis)];\n};\n\n/**\n * Convert from canvas/div x coordinate to data coordinate.\n *\n * If x is null, this returns null.\n */\nDygraph.prototype.toDataXCoord = function (x) {\n if (x === null) {\n return null;\n }\n\n var area = this.plotter_.area;\n var xRange = this.xAxisRange();\n\n if (!this.attributes_.getForAxis(\"logscale\", 'x')) {\n return xRange[0] + (x - area.x) / area.w * (xRange[1] - xRange[0]);\n } else {\n var pct = (x - area.x) / area.w;\n return utils.logRangeFraction(xRange[0], xRange[1], pct);\n }\n};\n\n/**\n * Convert from canvas/div y coord to value.\n *\n * If y is null, this returns null.\n * if axis is null, this uses the first axis.\n */\nDygraph.prototype.toDataYCoord = function (y, axis) {\n if (y === null) {\n return null;\n }\n\n var area = this.plotter_.area;\n var yRange = this.yAxisRange(axis);\n\n if (typeof axis == \"undefined\") axis = 0;\n if (!this.attributes_.getForAxis(\"logscale\", axis)) {\n return yRange[0] + (area.y + area.h - y) / area.h * (yRange[1] - yRange[0]);\n } else {\n // Computing the inverse of toDomCoord.\n var pct = (y - area.y) / area.h;\n // Note reversed yRange, y1 is on top with pct==0.\n return utils.logRangeFraction(yRange[1], yRange[0], pct);\n }\n};\n\n/**\n * Converts a y for an axis to a percentage from the top to the\n * bottom of the drawing area.\n *\n * If the coordinate represents a value visible on the canvas, then\n * the value will be between 0 and 1, where 0 is the top of the canvas.\n * However, this method will return values outside the range, as\n * values can fall outside the canvas.\n *\n * If y is null, this returns null.\n * if axis is null, this uses the first axis.\n *\n * @param {number} y The data y-coordinate.\n * @param {number} [axis] The axis number on which the data coordinate lives.\n * @return {number} A fraction in [0, 1] where 0 = the top edge.\n */\nDygraph.prototype.toPercentYCoord = function (y, axis) {\n if (y === null) {\n return null;\n }\n if (typeof axis == \"undefined\") axis = 0;\n\n var yRange = this.yAxisRange(axis);\n\n var pct;\n var logscale = this.attributes_.getForAxis(\"logscale\", axis);\n if (logscale) {\n var logr0 = utils.log10(yRange[0]);\n var logr1 = utils.log10(yRange[1]);\n pct = (logr1 - utils.log10(y)) / (logr1 - logr0);\n } else {\n // yRange[1] - y is unit distance from the bottom.\n // yRange[1] - yRange[0] is the scale of the range.\n // (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom.\n pct = (yRange[1] - y) / (yRange[1] - yRange[0]);\n }\n return pct;\n};\n\n/**\n * Converts an x value to a percentage from the left to the right of\n * the drawing area.\n *\n * If the coordinate represents a value visible on the canvas, then\n * the value will be between 0 and 1, where 0 is the left of the canvas.\n * However, this method will return values outside the range, as\n * values can fall outside the canvas.\n *\n * If x is null, this returns null.\n * @param {number} x The data x-coordinate.\n * @return {number} A fraction in [0, 1] where 0 = the left edge.\n */\nDygraph.prototype.toPercentXCoord = function (x) {\n if (x === null) {\n return null;\n }\n\n var xRange = this.xAxisRange();\n var pct;\n var logscale = this.attributes_.getForAxis(\"logscale\", 'x');\n if (logscale === true) {\n // logscale can be null so we test for true explicitly.\n var logr0 = utils.log10(xRange[0]);\n var logr1 = utils.log10(xRange[1]);\n pct = (utils.log10(x) - logr0) / (logr1 - logr0);\n } else {\n // x - xRange[0] is unit distance from the left.\n // xRange[1] - xRange[0] is the scale of the range.\n // The full expression below is the % from the left.\n pct = (x - xRange[0]) / (xRange[1] - xRange[0]);\n }\n return pct;\n};\n\n/**\n * Returns the number of columns (including the independent variable).\n * @return {number} The number of columns.\n */\nDygraph.prototype.numColumns = function () {\n if (!this.rawData_) return 0;\n return this.rawData_[0] ? this.rawData_[0].length : this.attr_(\"labels\").length;\n};\n\n/**\n * Returns the number of rows (excluding any header/label row).\n * @return {number} The number of rows, less any header.\n */\nDygraph.prototype.numRows = function () {\n if (!this.rawData_) return 0;\n return this.rawData_.length;\n};\n\n/**\n * Returns the value in the given row and column. If the row and column exceed\n * the bounds on the data, returns null. Also returns null if the value is\n * missing.\n * @param {number} row The row number of the data (0-based). Row 0 is the\n * first row of data, not a header row.\n * @param {number} col The column number of the data (0-based)\n * @return {number} The value in the specified cell or null if the row/col\n * were out of range.\n */\nDygraph.prototype.getValue = function (row, col) {\n if (row < 0 || row > this.rawData_.length) return null;\n if (col < 0 || col > this.rawData_[row].length) return null;\n\n return this.rawData_[row][col];\n};\n\n/**\n * Generates interface elements for the Dygraph: a containing div, a div to\n * display the current point, and a textbox to adjust the rolling average\n * period. Also creates the Renderer/Layout elements.\n * @private\n */\nDygraph.prototype.createInterface_ = function () {\n // Create the all-enclosing graph div\n var enclosing = this.maindiv_;\n\n this.graphDiv = document.createElement(\"div\");\n\n // TODO(danvk): any other styles that are useful to set here?\n this.graphDiv.style.textAlign = 'left'; // This is a CSS \"reset\"\n this.graphDiv.style.position = 'relative';\n enclosing.appendChild(this.graphDiv);\n\n // Create the canvas for interactive parts of the chart.\n this.canvas_ = utils.createCanvas();\n this.canvas_.style.position = \"absolute\";\n\n // ... and for static parts of the chart.\n this.hidden_ = this.createPlotKitCanvas_(this.canvas_);\n\n this.canvas_ctx_ = utils.getContext(this.canvas_);\n this.hidden_ctx_ = utils.getContext(this.hidden_);\n\n this.resizeElements_();\n\n // The interactive parts of the graph are drawn on top of the chart.\n this.graphDiv.appendChild(this.hidden_);\n this.graphDiv.appendChild(this.canvas_);\n this.mouseEventElement_ = this.createMouseEventElement_();\n\n // Create the grapher\n this.layout_ = new _dygraphLayout2['default'](this);\n\n var dygraph = this;\n\n this.mouseMoveHandler_ = function (e) {\n dygraph.mouseMove_(e);\n };\n\n this.mouseOutHandler_ = function (e) {\n // The mouse has left the chart if:\n // 1. e.target is inside the chart\n // 2. e.relatedTarget is outside the chart\n var target = e.target || e.fromElement;\n var relatedTarget = e.relatedTarget || e.toElement;\n if (utils.isNodeContainedBy(target, dygraph.graphDiv) && !utils.isNodeContainedBy(relatedTarget, dygraph.graphDiv)) {\n dygraph.mouseOut_(e);\n }\n };\n\n this.addAndTrackEvent(window, 'mouseout', this.mouseOutHandler_);\n this.addAndTrackEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_);\n\n // Don't recreate and register the resize handler on subsequent calls.\n // This happens when the graph is resized.\n if (!this.resizeHandler_) {\n this.resizeHandler_ = function (e) {\n dygraph.resize();\n };\n\n // Update when the window is resized.\n // TODO(danvk): drop frames depending on complexity of the chart.\n this.addAndTrackEvent(window, 'resize', this.resizeHandler_);\n }\n};\n\nDygraph.prototype.resizeElements_ = function () {\n this.graphDiv.style.width = this.width_ + \"px\";\n this.graphDiv.style.height = this.height_ + \"px\";\n\n var pixelRatioOption = this.getNumericOption('pixelRatio');\n\n var canvasScale = pixelRatioOption || utils.getContextPixelRatio(this.canvas_ctx_);\n this.canvas_.width = this.width_ * canvasScale;\n this.canvas_.height = this.height_ * canvasScale;\n this.canvas_.style.width = this.width_ + \"px\"; // for IE\n this.canvas_.style.height = this.height_ + \"px\"; // for IE\n if (canvasScale !== 1) {\n this.canvas_ctx_.scale(canvasScale, canvasScale);\n }\n\n var hiddenScale = pixelRatioOption || utils.getContextPixelRatio(this.hidden_ctx_);\n this.hidden_.width = this.width_ * hiddenScale;\n this.hidden_.height = this.height_ * hiddenScale;\n this.hidden_.style.width = this.width_ + \"px\"; // for IE\n this.hidden_.style.height = this.height_ + \"px\"; // for IE\n if (hiddenScale !== 1) {\n this.hidden_ctx_.scale(hiddenScale, hiddenScale);\n }\n};\n\n/**\n * Detach DOM elements in the dygraph and null out all data references.\n * Calling this when you're done with a dygraph can dramatically reduce memory\n * usage. See, e.g., the tests/perf.html example.\n */\nDygraph.prototype.destroy = function () {\n this.canvas_ctx_.restore();\n this.hidden_ctx_.restore();\n\n // Destroy any plugins, in the reverse order that they were registered.\n for (var i = this.plugins_.length - 1; i >= 0; i--) {\n var p = this.plugins_.pop();\n if (p.plugin.destroy) p.plugin.destroy();\n }\n\n var removeRecursive = function removeRecursive(node) {\n while (node.hasChildNodes()) {\n removeRecursive(node.firstChild);\n node.removeChild(node.firstChild);\n }\n };\n\n this.removeTrackedEvents_();\n\n // remove mouse event handlers (This may not be necessary anymore)\n utils.removeEvent(window, 'mouseout', this.mouseOutHandler_);\n utils.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_);\n\n // remove window handlers\n utils.removeEvent(window, 'resize', this.resizeHandler_);\n this.resizeHandler_ = null;\n\n removeRecursive(this.maindiv_);\n\n var nullOut = function nullOut(obj) {\n for (var n in obj) {\n if (typeof obj[n] === 'object') {\n obj[n] = null;\n }\n }\n };\n // These may not all be necessary, but it can't hurt...\n nullOut(this.layout_);\n nullOut(this.plotter_);\n nullOut(this);\n};\n\n/**\n * Creates the canvas on which the chart will be drawn. Only the Renderer ever\n * draws on this particular canvas. All Dygraph work (i.e. drawing hover dots\n * or the zoom rectangles) is done on this.canvas_.\n * @param {Object} canvas The Dygraph canvas over which to overlay the plot\n * @return {Object} The newly-created canvas\n * @private\n */\nDygraph.prototype.createPlotKitCanvas_ = function (canvas) {\n var h = utils.createCanvas();\n h.style.position = \"absolute\";\n // TODO(danvk): h should be offset from canvas. canvas needs to include\n // some extra area to make it easier to zoom in on the far left and far\n // right. h needs to be precisely the plot area, so that clipping occurs.\n h.style.top = canvas.style.top;\n h.style.left = canvas.style.left;\n h.width = this.width_;\n h.height = this.height_;\n h.style.width = this.width_ + \"px\"; // for IE\n h.style.height = this.height_ + \"px\"; // for IE\n return h;\n};\n\n/**\n * Creates an overlay element used to handle mouse events.\n * @return {Object} The mouse event element.\n * @private\n */\nDygraph.prototype.createMouseEventElement_ = function () {\n return this.canvas_;\n};\n\n/**\n * Generate a set of distinct colors for the data series. This is done with a\n * color wheel. Saturation/Value are customizable, and the hue is\n * equally-spaced around the color wheel. If a custom set of colors is\n * specified, that is used instead.\n * @private\n */\nDygraph.prototype.setColors_ = function () {\n var labels = this.getLabels();\n var num = labels.length - 1;\n this.colors_ = [];\n this.colorsMap_ = {};\n\n // These are used for when no custom colors are specified.\n var sat = this.getNumericOption('colorSaturation') || 1.0;\n var val = this.getNumericOption('colorValue') || 0.5;\n var half = Math.ceil(num / 2);\n\n var colors = this.getOption('colors');\n var visibility = this.visibility();\n for (var i = 0; i < num; i++) {\n if (!visibility[i]) {\n continue;\n }\n var label = labels[i + 1];\n var colorStr = this.attributes_.getForSeries('color', label);\n if (!colorStr) {\n if (colors) {\n colorStr = colors[i % colors.length];\n } else {\n // alternate colors for high contrast.\n var idx = i % 2 ? half + (i + 1) / 2 : Math.ceil((i + 1) / 2);\n var hue = 1.0 * idx / (1 + num);\n colorStr = utils.hsvToRGB(hue, sat, val);\n }\n }\n this.colors_.push(colorStr);\n this.colorsMap_[label] = colorStr;\n }\n};\n\n/**\n * Return the list of colors. This is either the list of colors passed in the\n * attributes or the autogenerated list of rgb(r,g,b) strings.\n * This does not return colors for invisible series.\n * @return {Array.} The list of colors.\n */\nDygraph.prototype.getColors = function () {\n return this.colors_;\n};\n\n/**\n * Returns a few attributes of a series, i.e. its color, its visibility, which\n * axis it's assigned to, and its column in the original data.\n * Returns null if the series does not exist.\n * Otherwise, returns an object with column, visibility, color and axis properties.\n * The \"axis\" property will be set to 1 for y1 and 2 for y2.\n * The \"column\" property can be fed back into getValue(row, column) to get\n * values for this series.\n */\nDygraph.prototype.getPropertiesForSeries = function (series_name) {\n var idx = -1;\n var labels = this.getLabels();\n for (var i = 1; i < labels.length; i++) {\n if (labels[i] == series_name) {\n idx = i;\n break;\n }\n }\n if (idx == -1) return null;\n\n return {\n name: series_name,\n column: idx,\n visible: this.visibility()[idx - 1],\n color: this.colorsMap_[series_name],\n axis: 1 + this.attributes_.axisForSeries(series_name)\n };\n};\n\n/**\n * Create the text box to adjust the averaging period\n * @private\n */\nDygraph.prototype.createRollInterface_ = function () {\n var _this = this;\n\n // Create a roller if one doesn't exist already.\n var roller = this.roller_;\n if (!roller) {\n this.roller_ = roller = document.createElement(\"input\");\n roller.type = \"text\";\n roller.style.display = \"none\";\n roller.className = 'dygraph-roller';\n this.graphDiv.appendChild(roller);\n }\n\n var display = this.getBooleanOption('showRoller') ? 'block' : 'none';\n\n var area = this.getArea();\n var textAttr = {\n \"top\": area.y + area.h - 25 + \"px\",\n \"left\": area.x + 1 + \"px\",\n \"display\": display\n };\n roller.size = \"2\";\n roller.value = this.rollPeriod_;\n utils.update(roller.style, textAttr);\n\n roller.onchange = function () {\n return _this.adjustRoll(roller.value);\n };\n};\n\n/**\n * Set up all the mouse handlers needed to capture dragging behavior for zoom\n * events.\n * @private\n */\nDygraph.prototype.createDragInterface_ = function () {\n var context = {\n // Tracks whether the mouse is down right now\n isZooming: false,\n isPanning: false, // is this drag part of a pan?\n is2DPan: false, // if so, is that pan 1- or 2-dimensional?\n dragStartX: null, // pixel coordinates\n dragStartY: null, // pixel coordinates\n dragEndX: null, // pixel coordinates\n dragEndY: null, // pixel coordinates\n dragDirection: null,\n prevEndX: null, // pixel coordinates\n prevEndY: null, // pixel coordinates\n prevDragDirection: null,\n cancelNextDblclick: false, // see comment in dygraph-interaction-model.js\n\n // The value on the left side of the graph when a pan operation starts.\n initialLeftmostDate: null,\n\n // The number of units each pixel spans. (This won't be valid for log\n // scales)\n xUnitsPerPixel: null,\n\n // TODO(danvk): update this comment\n // The range in second/value units that the viewport encompasses during a\n // panning operation.\n dateRange: null,\n\n // Top-left corner of the canvas, in DOM coords\n // TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY.\n px: 0,\n py: 0,\n\n // Values for use with panEdgeFraction, which limit how far outside the\n // graph's data boundaries it can be panned.\n boundedDates: null, // [minDate, maxDate]\n boundedValues: null, // [[minValue, maxValue] ...]\n\n // We cover iframes during mouse interactions. See comments in\n // dygraph-utils.js for more info on why this is a good idea.\n tarp: new _iframeTarp2['default'](),\n\n // contextB is the same thing as this context object but renamed.\n initializeMouseDown: function initializeMouseDown(event, g, contextB) {\n // prevents mouse drags from selecting page text.\n if (event.preventDefault) {\n event.preventDefault(); // Firefox, Chrome, etc.\n } else {\n event.returnValue = false; // IE\n event.cancelBubble = true;\n }\n\n var canvasPos = utils.findPos(g.canvas_);\n contextB.px = canvasPos.x;\n contextB.py = canvasPos.y;\n contextB.dragStartX = utils.dragGetX_(event, contextB);\n contextB.dragStartY = utils.dragGetY_(event, contextB);\n contextB.cancelNextDblclick = false;\n contextB.tarp.cover();\n },\n destroy: function destroy() {\n var context = this;\n if (context.isZooming || context.isPanning) {\n context.isZooming = false;\n context.dragStartX = null;\n context.dragStartY = null;\n }\n\n if (context.isPanning) {\n context.isPanning = false;\n context.draggingDate = null;\n context.dateRange = null;\n for (var i = 0; i < self.axes_.length; i++) {\n delete self.axes_[i].draggingValue;\n delete self.axes_[i].dragValueRange;\n }\n }\n\n context.tarp.uncover();\n }\n };\n\n var interactionModel = this.getOption(\"interactionModel\");\n\n // Self is the graph.\n var self = this;\n\n // Function that binds the graph and context to the handler.\n var bindHandler = function bindHandler(handler) {\n return function (event) {\n handler(event, self, context);\n };\n };\n\n for (var eventName in interactionModel) {\n if (!interactionModel.hasOwnProperty(eventName)) continue;\n this.addAndTrackEvent(this.mouseEventElement_, eventName, bindHandler(interactionModel[eventName]));\n }\n\n // If the user releases the mouse button during a drag, but not over the\n // canvas, then it doesn't count as a zooming action.\n if (!interactionModel.willDestroyContextMyself) {\n var mouseUpHandler = function mouseUpHandler(event) {\n context.destroy();\n };\n\n this.addAndTrackEvent(document, 'mouseup', mouseUpHandler);\n }\n};\n\n/**\n * Draw a gray zoom rectangle over the desired area of the canvas. Also clears\n * up any previous zoom rectangles that were drawn. This could be optimized to\n * avoid extra redrawing, but it's tricky to avoid interactions with the status\n * dots.\n *\n * @param {number} direction the direction of the zoom rectangle. Acceptable\n * values are utils.HORIZONTAL and utils.VERTICAL.\n * @param {number} startX The X position where the drag started, in canvas\n * coordinates.\n * @param {number} endX The current X position of the drag, in canvas coords.\n * @param {number} startY The Y position where the drag started, in canvas\n * coordinates.\n * @param {number} endY The current Y position of the drag, in canvas coords.\n * @param {number} prevDirection the value of direction on the previous call to\n * this function. Used to avoid excess redrawing\n * @param {number} prevEndX The value of endX on the previous call to this\n * function. Used to avoid excess redrawing\n * @param {number} prevEndY The value of endY on the previous call to this\n * function. Used to avoid excess redrawing\n * @private\n */\nDygraph.prototype.drawZoomRect_ = function (direction, startX, endX, startY, endY, prevDirection, prevEndX, prevEndY) {\n var ctx = this.canvas_ctx_;\n\n // Clean up from the previous rect if necessary\n if (prevDirection == utils.HORIZONTAL) {\n ctx.clearRect(Math.min(startX, prevEndX), this.layout_.getPlotArea().y, Math.abs(startX - prevEndX), this.layout_.getPlotArea().h);\n } else if (prevDirection == utils.VERTICAL) {\n ctx.clearRect(this.layout_.getPlotArea().x, Math.min(startY, prevEndY), this.layout_.getPlotArea().w, Math.abs(startY - prevEndY));\n }\n\n // Draw a light-grey rectangle to show the new viewing area\n if (direction == utils.HORIZONTAL) {\n if (endX && startX) {\n ctx.fillStyle = \"rgba(128,128,128,0.33)\";\n ctx.fillRect(Math.min(startX, endX), this.layout_.getPlotArea().y, Math.abs(endX - startX), this.layout_.getPlotArea().h);\n }\n } else if (direction == utils.VERTICAL) {\n if (endY && startY) {\n ctx.fillStyle = \"rgba(128,128,128,0.33)\";\n ctx.fillRect(this.layout_.getPlotArea().x, Math.min(startY, endY), this.layout_.getPlotArea().w, Math.abs(endY - startY));\n }\n }\n};\n\n/**\n * Clear the zoom rectangle (and perform no zoom).\n * @private\n */\nDygraph.prototype.clearZoomRect_ = function () {\n this.currentZoomRectArgs_ = null;\n this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);\n};\n\n/**\n * Zoom to something containing [lowX, highX]. These are pixel coordinates in\n * the canvas. The exact zoom window may be slightly larger if there are no data\n * points near lowX or highX. Don't confuse this function with doZoomXDates,\n * which accepts dates that match the raw data. This function redraws the graph.\n *\n * @param {number} lowX The leftmost pixel value that should be visible.\n * @param {number} highX The rightmost pixel value that should be visible.\n * @private\n */\nDygraph.prototype.doZoomX_ = function (lowX, highX) {\n this.currentZoomRectArgs_ = null;\n // Find the earliest and latest dates contained in this canvasx range.\n // Convert the call to date ranges of the raw data.\n var minDate = this.toDataXCoord(lowX);\n var maxDate = this.toDataXCoord(highX);\n this.doZoomXDates_(minDate, maxDate);\n};\n\n/**\n * Zoom to something containing [minDate, maxDate] values. Don't confuse this\n * method with doZoomX which accepts pixel coordinates. This function redraws\n * the graph.\n *\n * @param {number} minDate The minimum date that should be visible.\n * @param {number} maxDate The maximum date that should be visible.\n * @private\n */\nDygraph.prototype.doZoomXDates_ = function (minDate, maxDate) {\n var _this2 = this;\n\n // TODO(danvk): when xAxisRange is null (i.e. \"fit to data\", the animation\n // can produce strange effects. Rather than the x-axis transitioning slowly\n // between values, it can jerk around.)\n var old_window = this.xAxisRange();\n var new_window = [minDate, maxDate];\n var zoomCallback = this.getFunctionOption('zoomCallback');\n this.doAnimatedZoom(old_window, new_window, null, null, function () {\n if (zoomCallback) {\n zoomCallback.call(_this2, minDate, maxDate, _this2.yAxisRanges());\n }\n });\n};\n\n/**\n * Zoom to something containing [lowY, highY]. These are pixel coordinates in\n * the canvas. This function redraws the graph.\n *\n * @param {number} lowY The topmost pixel value that should be visible.\n * @param {number} highY The lowest pixel value that should be visible.\n * @private\n */\nDygraph.prototype.doZoomY_ = function (lowY, highY) {\n var _this3 = this;\n\n this.currentZoomRectArgs_ = null;\n // Find the highest and lowest values in pixel range for each axis.\n // Note that lowY (in pixels) corresponds to the max Value (in data coords).\n // This is because pixels increase as you go down on the screen, whereas data\n // coordinates increase as you go up the screen.\n var oldValueRanges = this.yAxisRanges();\n var newValueRanges = [];\n for (var i = 0; i < this.axes_.length; i++) {\n var hi = this.toDataYCoord(lowY, i);\n var low = this.toDataYCoord(highY, i);\n newValueRanges.push([low, hi]);\n }\n\n var zoomCallback = this.getFunctionOption('zoomCallback');\n this.doAnimatedZoom(null, null, oldValueRanges, newValueRanges, function () {\n if (zoomCallback) {\n var _xAxisRange = _this3.xAxisRange();\n\n var _xAxisRange2 = _slicedToArray(_xAxisRange, 2);\n\n var minX = _xAxisRange2[0];\n var maxX = _xAxisRange2[1];\n\n zoomCallback.call(_this3, minX, maxX, _this3.yAxisRanges());\n }\n });\n};\n\n/**\n * Transition function to use in animations. Returns values between 0.0\n * (totally old values) and 1.0 (totally new values) for each frame.\n * @private\n */\nDygraph.zoomAnimationFunction = function (frame, numFrames) {\n var k = 1.5;\n return (1.0 - Math.pow(k, -frame)) / (1.0 - Math.pow(k, -numFrames));\n};\n\n/**\n * Reset the zoom to the original view coordinates. This is the same as\n * double-clicking on the graph.\n */\nDygraph.prototype.resetZoom = function () {\n var _this4 = this;\n\n var dirtyX = this.isZoomed('x');\n var dirtyY = this.isZoomed('y');\n var dirty = dirtyX || dirtyY;\n\n // Clear any selection, since it's likely to be drawn in the wrong place.\n this.clearSelection();\n\n if (!dirty) return;\n\n // Calculate extremes to avoid lack of padding on reset.\n\n var _xAxisExtremes = this.xAxisExtremes();\n\n var _xAxisExtremes2 = _slicedToArray(_xAxisExtremes, 2);\n\n var minDate = _xAxisExtremes2[0];\n var maxDate = _xAxisExtremes2[1];\n\n var animatedZooms = this.getBooleanOption('animatedZooms');\n var zoomCallback = this.getFunctionOption('zoomCallback');\n\n // TODO(danvk): merge this block w/ the code below.\n // TODO(danvk): factor out a generic, public zoomTo method.\n if (!animatedZooms) {\n this.dateWindow_ = null;\n this.axes_.forEach(function (axis) {\n if (axis.valueRange) delete axis.valueRange;\n });\n\n this.drawGraph_();\n if (zoomCallback) {\n zoomCallback.call(this, minDate, maxDate, this.yAxisRanges());\n }\n return;\n }\n\n var oldWindow = null,\n newWindow = null,\n oldValueRanges = null,\n newValueRanges = null;\n if (dirtyX) {\n oldWindow = this.xAxisRange();\n newWindow = [minDate, maxDate];\n }\n\n if (dirtyY) {\n oldValueRanges = this.yAxisRanges();\n newValueRanges = this.yAxisExtremes();\n }\n\n this.doAnimatedZoom(oldWindow, newWindow, oldValueRanges, newValueRanges, function () {\n _this4.dateWindow_ = null;\n _this4.axes_.forEach(function (axis) {\n if (axis.valueRange) delete axis.valueRange;\n });\n if (zoomCallback) {\n zoomCallback.call(_this4, minDate, maxDate, _this4.yAxisRanges());\n }\n });\n};\n\n/**\n * Combined animation logic for all zoom functions.\n * either the x parameters or y parameters may be null.\n * @private\n */\nDygraph.prototype.doAnimatedZoom = function (oldXRange, newXRange, oldYRanges, newYRanges, callback) {\n var _this5 = this;\n\n var steps = this.getBooleanOption(\"animatedZooms\") ? Dygraph.ANIMATION_STEPS : 1;\n\n var windows = [];\n var valueRanges = [];\n var step, frac;\n\n if (oldXRange !== null && newXRange !== null) {\n for (step = 1; step <= steps; step++) {\n frac = Dygraph.zoomAnimationFunction(step, steps);\n windows[step - 1] = [oldXRange[0] * (1 - frac) + frac * newXRange[0], oldXRange[1] * (1 - frac) + frac * newXRange[1]];\n }\n }\n\n if (oldYRanges !== null && newYRanges !== null) {\n for (step = 1; step <= steps; step++) {\n frac = Dygraph.zoomAnimationFunction(step, steps);\n var thisRange = [];\n for (var j = 0; j < this.axes_.length; j++) {\n thisRange.push([oldYRanges[j][0] * (1 - frac) + frac * newYRanges[j][0], oldYRanges[j][1] * (1 - frac) + frac * newYRanges[j][1]]);\n }\n valueRanges[step - 1] = thisRange;\n }\n }\n\n utils.repeatAndCleanup(function (step) {\n if (valueRanges.length) {\n for (var i = 0; i < _this5.axes_.length; i++) {\n var w = valueRanges[step][i];\n _this5.axes_[i].valueRange = [w[0], w[1]];\n }\n }\n if (windows.length) {\n _this5.dateWindow_ = windows[step];\n }\n _this5.drawGraph_();\n }, steps, Dygraph.ANIMATION_DURATION / steps, callback);\n};\n\n/**\n * Get the current graph's area object.\n *\n * Returns: {x, y, w, h}\n */\nDygraph.prototype.getArea = function () {\n return this.plotter_.area;\n};\n\n/**\n * Convert a mouse event to DOM coordinates relative to the graph origin.\n *\n * Returns a two-element array: [X, Y].\n */\nDygraph.prototype.eventToDomCoords = function (event) {\n if (event.offsetX && event.offsetY) {\n return [event.offsetX, event.offsetY];\n } else {\n var eventElementPos = utils.findPos(this.mouseEventElement_);\n var canvasx = utils.pageX(event) - eventElementPos.x;\n var canvasy = utils.pageY(event) - eventElementPos.y;\n return [canvasx, canvasy];\n }\n};\n\n/**\n * Given a canvas X coordinate, find the closest row.\n * @param {number} domX graph-relative DOM X coordinate\n * Returns {number} row number.\n * @private\n */\nDygraph.prototype.findClosestRow = function (domX) {\n var minDistX = Infinity;\n var closestRow = -1;\n var sets = this.layout_.points;\n for (var i = 0; i < sets.length; i++) {\n var points = sets[i];\n var len = points.length;\n for (var j = 0; j < len; j++) {\n var point = points[j];\n if (!utils.isValidPoint(point, true)) continue;\n var dist = Math.abs(point.canvasx - domX);\n if (dist < minDistX) {\n minDistX = dist;\n closestRow = point.idx;\n }\n }\n }\n\n return closestRow;\n};\n\n/**\n * Given canvas X,Y coordinates, find the closest point.\n *\n * This finds the individual data point across all visible series\n * that's closest to the supplied DOM coordinates using the standard\n * Euclidean X,Y distance.\n *\n * @param {number} domX graph-relative DOM X coordinate\n * @param {number} domY graph-relative DOM Y coordinate\n * Returns: {row, seriesName, point}\n * @private\n */\nDygraph.prototype.findClosestPoint = function (domX, domY) {\n var minDist = Infinity;\n var dist, dx, dy, point, closestPoint, closestSeries, closestRow;\n for (var setIdx = this.layout_.points.length - 1; setIdx >= 0; --setIdx) {\n var points = this.layout_.points[setIdx];\n for (var i = 0; i < points.length; ++i) {\n point = points[i];\n if (!utils.isValidPoint(point)) continue;\n dx = point.canvasx - domX;\n dy = point.canvasy - domY;\n dist = dx * dx + dy * dy;\n if (dist < minDist) {\n minDist = dist;\n closestPoint = point;\n closestSeries = setIdx;\n closestRow = point.idx;\n }\n }\n }\n var name = this.layout_.setNames[closestSeries];\n return {\n row: closestRow,\n seriesName: name,\n point: closestPoint\n };\n};\n\n/**\n * Given canvas X,Y coordinates, find the touched area in a stacked graph.\n *\n * This first finds the X data point closest to the supplied DOM X coordinate,\n * then finds the series which puts the Y coordinate on top of its filled area,\n * using linear interpolation between adjacent point pairs.\n *\n * @param {number} domX graph-relative DOM X coordinate\n * @param {number} domY graph-relative DOM Y coordinate\n * Returns: {row, seriesName, point}\n * @private\n */\nDygraph.prototype.findStackedPoint = function (domX, domY) {\n var row = this.findClosestRow(domX);\n var closestPoint, closestSeries;\n for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) {\n var boundary = this.getLeftBoundary_(setIdx);\n var rowIdx = row - boundary;\n var points = this.layout_.points[setIdx];\n if (rowIdx >= points.length) continue;\n var p1 = points[rowIdx];\n if (!utils.isValidPoint(p1)) continue;\n var py = p1.canvasy;\n if (domX > p1.canvasx && rowIdx + 1 < points.length) {\n // interpolate series Y value using next point\n var p2 = points[rowIdx + 1];\n if (utils.isValidPoint(p2)) {\n var dx = p2.canvasx - p1.canvasx;\n if (dx > 0) {\n var r = (domX - p1.canvasx) / dx;\n py += r * (p2.canvasy - p1.canvasy);\n }\n }\n } else if (domX < p1.canvasx && rowIdx > 0) {\n // interpolate series Y value using previous point\n var p0 = points[rowIdx - 1];\n if (utils.isValidPoint(p0)) {\n var dx = p1.canvasx - p0.canvasx;\n if (dx > 0) {\n var r = (p1.canvasx - domX) / dx;\n py += r * (p0.canvasy - p1.canvasy);\n }\n }\n }\n // Stop if the point (domX, py) is above this series' upper edge\n if (setIdx === 0 || py < domY) {\n closestPoint = p1;\n closestSeries = setIdx;\n }\n }\n var name = this.layout_.setNames[closestSeries];\n return {\n row: row,\n seriesName: name,\n point: closestPoint\n };\n};\n\n/**\n * When the mouse moves in the canvas, display information about a nearby data\n * point and draw dots over those points in the data series. This function\n * takes care of cleanup of previously-drawn dots.\n * @param {Object} event The mousemove event from the browser.\n * @private\n */\nDygraph.prototype.mouseMove_ = function (event) {\n // This prevents JS errors when mousing over the canvas before data loads.\n var points = this.layout_.points;\n if (points === undefined || points === null) return;\n\n var canvasCoords = this.eventToDomCoords(event);\n var canvasx = canvasCoords[0];\n var canvasy = canvasCoords[1];\n\n var highlightSeriesOpts = this.getOption(\"highlightSeriesOpts\");\n var selectionChanged = false;\n if (highlightSeriesOpts && !this.isSeriesLocked()) {\n var closest;\n if (this.getBooleanOption(\"stackedGraph\")) {\n closest = this.findStackedPoint(canvasx, canvasy);\n } else {\n closest = this.findClosestPoint(canvasx, canvasy);\n }\n selectionChanged = this.setSelection(closest.row, closest.seriesName);\n } else {\n var idx = this.findClosestRow(canvasx);\n selectionChanged = this.setSelection(idx);\n }\n\n var callback = this.getFunctionOption(\"highlightCallback\");\n if (callback && selectionChanged) {\n callback.call(this, event, this.lastx_, this.selPoints_, this.lastRow_, this.highlightSet_);\n }\n};\n\n/**\n * Fetch left offset from the specified set index or if not passed, the\n * first defined boundaryIds record (see bug #236).\n * @private\n */\nDygraph.prototype.getLeftBoundary_ = function (setIdx) {\n if (this.boundaryIds_[setIdx]) {\n return this.boundaryIds_[setIdx][0];\n } else {\n for (var i = 0; i < this.boundaryIds_.length; i++) {\n if (this.boundaryIds_[i] !== undefined) {\n return this.boundaryIds_[i][0];\n }\n }\n return 0;\n }\n};\n\nDygraph.prototype.animateSelection_ = function (direction) {\n var totalSteps = 10;\n var millis = 30;\n if (this.fadeLevel === undefined) this.fadeLevel = 0;\n if (this.animateId === undefined) this.animateId = 0;\n var start = this.fadeLevel;\n var steps = direction < 0 ? start : totalSteps - start;\n if (steps <= 0) {\n if (this.fadeLevel) {\n this.updateSelection_(1.0);\n }\n return;\n }\n\n var thisId = ++this.animateId;\n var that = this;\n var cleanupIfClearing = function cleanupIfClearing() {\n // if we haven't reached fadeLevel 0 in the max frame time,\n // ensure that the clear happens and just go to 0\n if (that.fadeLevel !== 0 && direction < 0) {\n that.fadeLevel = 0;\n that.clearSelection();\n }\n };\n utils.repeatAndCleanup(function (n) {\n // ignore simultaneous animations\n if (that.animateId != thisId) return;\n\n that.fadeLevel += direction;\n if (that.fadeLevel === 0) {\n that.clearSelection();\n } else {\n that.updateSelection_(that.fadeLevel / totalSteps);\n }\n }, steps, millis, cleanupIfClearing);\n};\n\n/**\n * Draw dots over the selectied points in the data series. This function\n * takes care of cleanup of previously-drawn dots.\n * @private\n */\nDygraph.prototype.updateSelection_ = function (opt_animFraction) {\n /*var defaultPrevented = */\n this.cascadeEvents_('select', {\n selectedRow: this.lastRow_ === -1 ? undefined : this.lastRow_,\n selectedX: this.lastx_ === -1 ? undefined : this.lastx_,\n selectedPoints: this.selPoints_\n });\n // TODO(danvk): use defaultPrevented here?\n\n // Clear the previously drawn vertical, if there is one\n var i;\n var ctx = this.canvas_ctx_;\n if (this.getOption('highlightSeriesOpts')) {\n ctx.clearRect(0, 0, this.width_, this.height_);\n var alpha = 1.0 - this.getNumericOption('highlightSeriesBackgroundAlpha');\n var backgroundColor = utils.toRGB_(this.getOption('highlightSeriesBackgroundColor'));\n\n if (alpha) {\n // Activating background fade includes an animation effect for a gradual\n // fade. TODO(klausw): make this independently configurable if it causes\n // issues? Use a shared preference to control animations?\n var animateBackgroundFade = true;\n if (animateBackgroundFade) {\n if (opt_animFraction === undefined) {\n // start a new animation\n this.animateSelection_(1);\n return;\n }\n alpha *= opt_animFraction;\n }\n ctx.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + alpha + ')';\n ctx.fillRect(0, 0, this.width_, this.height_);\n }\n\n // Redraw only the highlighted series in the interactive canvas (not the\n // static plot canvas, which is where series are usually drawn).\n this.plotter_._renderLineChart(this.highlightSet_, ctx);\n } else if (this.previousVerticalX_ >= 0) {\n // Determine the maximum highlight circle size.\n var maxCircleSize = 0;\n var labels = this.attr_('labels');\n for (i = 1; i < labels.length; i++) {\n var r = this.getNumericOption('highlightCircleSize', labels[i]);\n if (r > maxCircleSize) maxCircleSize = r;\n }\n var px = this.previousVerticalX_;\n ctx.clearRect(px - maxCircleSize - 1, 0, 2 * maxCircleSize + 2, this.height_);\n }\n\n if (this.selPoints_.length > 0) {\n // Draw colored circles over the center of each selected point\n var canvasx = this.selPoints_[0].canvasx;\n ctx.save();\n for (i = 0; i < this.selPoints_.length; i++) {\n var pt = this.selPoints_[i];\n if (isNaN(pt.canvasy)) continue;\n\n var circleSize = this.getNumericOption('highlightCircleSize', pt.name);\n var callback = this.getFunctionOption(\"drawHighlightPointCallback\", pt.name);\n var color = this.plotter_.colors[pt.name];\n if (!callback) {\n callback = utils.Circles.DEFAULT;\n }\n ctx.lineWidth = this.getNumericOption('strokeWidth', pt.name);\n ctx.strokeStyle = color;\n ctx.fillStyle = color;\n callback.call(this, this, pt.name, ctx, canvasx, pt.canvasy, color, circleSize, pt.idx);\n }\n ctx.restore();\n\n this.previousVerticalX_ = canvasx;\n }\n};\n\n/**\n * Manually set the selected points and display information about them in the\n * legend. The selection can be cleared using clearSelection() and queried\n * using getSelection().\n *\n * To set a selected series but not a selected point, call setSelection with\n * row=false and the selected series name.\n *\n * @param {number} row Row number that should be highlighted (i.e. appear with\n * hover dots on the chart).\n * @param {seriesName} optional series name to highlight that series with the\n * the highlightSeriesOpts setting.\n * @param { locked } optional If true, keep seriesName selected when mousing\n * over the graph, disabling closest-series highlighting. Call clearSelection()\n * to unlock it.\n */\nDygraph.prototype.setSelection = function (row, opt_seriesName, opt_locked) {\n // Extract the points we've selected\n this.selPoints_ = [];\n\n var changed = false;\n if (row !== false && row >= 0) {\n if (row != this.lastRow_) changed = true;\n this.lastRow_ = row;\n for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) {\n var points = this.layout_.points[setIdx];\n // Check if the point at the appropriate index is the point we're looking\n // for. If it is, just use it, otherwise search the array for a point\n // in the proper place.\n var setRow = row - this.getLeftBoundary_(setIdx);\n if (setRow >= 0 && setRow < points.length && points[setRow].idx == row) {\n var point = points[setRow];\n if (point.yval !== null) this.selPoints_.push(point);\n } else {\n for (var pointIdx = 0; pointIdx < points.length; ++pointIdx) {\n var point = points[pointIdx];\n if (point.idx == row) {\n if (point.yval !== null) {\n this.selPoints_.push(point);\n }\n break;\n }\n }\n }\n }\n } else {\n if (this.lastRow_ >= 0) changed = true;\n this.lastRow_ = -1;\n }\n\n if (this.selPoints_.length) {\n this.lastx_ = this.selPoints_[0].xval;\n } else {\n this.lastx_ = -1;\n }\n\n if (opt_seriesName !== undefined) {\n if (this.highlightSet_ !== opt_seriesName) changed = true;\n this.highlightSet_ = opt_seriesName;\n }\n\n if (opt_locked !== undefined) {\n this.lockedSet_ = opt_locked;\n }\n\n if (changed) {\n this.updateSelection_(undefined);\n }\n return changed;\n};\n\n/**\n * The mouse has left the canvas. Clear out whatever artifacts remain\n * @param {Object} event the mouseout event from the browser.\n * @private\n */\nDygraph.prototype.mouseOut_ = function (event) {\n if (this.getFunctionOption(\"unhighlightCallback\")) {\n this.getFunctionOption(\"unhighlightCallback\").call(this, event);\n }\n\n if (this.getBooleanOption(\"hideOverlayOnMouseOut\") && !this.lockedSet_) {\n this.clearSelection();\n }\n};\n\n/**\n * Clears the current selection (i.e. points that were highlighted by moving\n * the mouse over the chart).\n */\nDygraph.prototype.clearSelection = function () {\n this.cascadeEvents_('deselect', {});\n\n this.lockedSet_ = false;\n // Get rid of the overlay data\n if (this.fadeLevel) {\n this.animateSelection_(-1);\n return;\n }\n this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);\n this.fadeLevel = 0;\n this.selPoints_ = [];\n this.lastx_ = -1;\n this.lastRow_ = -1;\n this.highlightSet_ = null;\n};\n\n/**\n * Returns the number of the currently selected row. To get data for this row,\n * you can use the getValue method.\n * @return {number} row number, or -1 if nothing is selected\n */\nDygraph.prototype.getSelection = function () {\n if (!this.selPoints_ || this.selPoints_.length < 1) {\n return -1;\n }\n\n for (var setIdx = 0; setIdx < this.layout_.points.length; setIdx++) {\n var points = this.layout_.points[setIdx];\n for (var row = 0; row < points.length; row++) {\n if (points[row].x == this.selPoints_[0].x) {\n return points[row].idx;\n }\n }\n }\n return -1;\n};\n\n/**\n * Returns the name of the currently-highlighted series.\n * Only available when the highlightSeriesOpts option is in use.\n */\nDygraph.prototype.getHighlightSeries = function () {\n return this.highlightSet_;\n};\n\n/**\n * Returns true if the currently-highlighted series was locked\n * via setSelection(..., seriesName, true).\n */\nDygraph.prototype.isSeriesLocked = function () {\n return this.lockedSet_;\n};\n\n/**\n * Fires when there's data available to be graphed.\n * @param {string} data Raw CSV data to be plotted\n * @private\n */\nDygraph.prototype.loadedEvent_ = function (data) {\n this.rawData_ = this.parseCSV_(data);\n this.cascadeDataDidUpdateEvent_();\n this.predraw_();\n};\n\n/**\n * Add ticks on the x-axis representing years, months, quarters, weeks, or days\n * @private\n */\nDygraph.prototype.addXTicks_ = function () {\n // Determine the correct ticks scale on the x-axis: quarterly, monthly, ...\n var range;\n if (this.dateWindow_) {\n range = [this.dateWindow_[0], this.dateWindow_[1]];\n } else {\n range = this.xAxisExtremes();\n }\n\n var xAxisOptionsView = this.optionsViewForAxis_('x');\n var xTicks = xAxisOptionsView('ticker')(range[0], range[1], this.plotter_.area.w, // TODO(danvk): should be area.width\n xAxisOptionsView, this);\n // var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks);\n // console.log(msg);\n this.layout_.setXTicks(xTicks);\n};\n\n/**\n * Returns the correct handler class for the currently set options.\n * @private\n */\nDygraph.prototype.getHandlerClass_ = function () {\n var handlerClass;\n if (this.attr_('dataHandler')) {\n handlerClass = this.attr_('dataHandler');\n } else if (this.fractions_) {\n if (this.getBooleanOption('errorBars')) {\n handlerClass = _datahandlerBarsFractions2['default'];\n } else {\n handlerClass = _datahandlerDefaultFractions2['default'];\n }\n } else if (this.getBooleanOption('customBars')) {\n handlerClass = _datahandlerBarsCustom2['default'];\n } else if (this.getBooleanOption('errorBars')) {\n handlerClass = _datahandlerBarsError2['default'];\n } else {\n handlerClass = _datahandlerDefault2['default'];\n }\n return handlerClass;\n};\n\n/**\n * @private\n * This function is called once when the chart's data is changed or the options\n * dictionary is updated. It is _not_ called when the user pans or zooms. The\n * idea is that values derived from the chart's data can be computed here,\n * rather than every time the chart is drawn. This includes things like the\n * number of axes, rolling averages, etc.\n */\nDygraph.prototype.predraw_ = function () {\n var start = new Date();\n\n // Create the correct dataHandler\n this.dataHandler_ = new (this.getHandlerClass_())();\n\n this.layout_.computePlotArea();\n\n // TODO(danvk): move more computations out of drawGraph_ and into here.\n this.computeYAxes_();\n\n if (!this.is_initial_draw_) {\n this.canvas_ctx_.restore();\n this.hidden_ctx_.restore();\n }\n\n this.canvas_ctx_.save();\n this.hidden_ctx_.save();\n\n // Create a new plotter.\n this.plotter_ = new _dygraphCanvas2['default'](this, this.hidden_, this.hidden_ctx_, this.layout_);\n\n // The roller sits in the bottom left corner of the chart. We don't know where\n // this will be until the options are available, so it's positioned here.\n this.createRollInterface_();\n\n this.cascadeEvents_('predraw');\n\n // Convert the raw data (a 2D array) into the internal format and compute\n // rolling averages.\n this.rolledSeries_ = [null]; // x-axis is the first series and it's special\n for (var i = 1; i < this.numColumns(); i++) {\n // var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong // konigsberg thinks so too.\n var series = this.dataHandler_.extractSeries(this.rawData_, i, this.attributes_);\n if (this.rollPeriod_ > 1) {\n series = this.dataHandler_.rollingAverage(series, this.rollPeriod_, this.attributes_);\n }\n\n this.rolledSeries_.push(series);\n }\n\n // If the data or options have changed, then we'd better redraw.\n this.drawGraph_();\n\n // This is used to determine whether to do various animations.\n var end = new Date();\n this.drawingTimeMs_ = end - start;\n};\n\n/**\n * Point structure.\n *\n * xval_* and yval_* are the original unscaled data values,\n * while x_* and y_* are scaled to the range (0.0-1.0) for plotting.\n * yval_stacked is the cumulative Y value used for stacking graphs,\n * and bottom/top/minus/plus are used for error bar graphs.\n *\n * @typedef {{\n * idx: number,\n * name: string,\n * x: ?number,\n * xval: ?number,\n * y_bottom: ?number,\n * y: ?number,\n * y_stacked: ?number,\n * y_top: ?number,\n * yval_minus: ?number,\n * yval: ?number,\n * yval_plus: ?number,\n * yval_stacked\n * }}\n */\nDygraph.PointType = undefined;\n\n/**\n * Calculates point stacking for stackedGraph=true.\n *\n * For stacking purposes, interpolate or extend neighboring data across\n * NaN values based on stackedGraphNaNFill settings. This is for display\n * only, the underlying data value as shown in the legend remains NaN.\n *\n * @param {Array.} points Point array for a single series.\n * Updates each Point's yval_stacked property.\n * @param {Array.} cumulativeYval Accumulated top-of-graph stacked Y\n * values for the series seen so far. Index is the row number. Updated\n * based on the current series's values.\n * @param {Array.} seriesExtremes Min and max values, updated\n * to reflect the stacked values.\n * @param {string} fillMethod Interpolation method, one of 'all', 'inside', or\n * 'none'.\n * @private\n */\nDygraph.stackPoints_ = function (points, cumulativeYval, seriesExtremes, fillMethod) {\n var lastXval = null;\n var prevPoint = null;\n var nextPoint = null;\n var nextPointIdx = -1;\n\n // Find the next stackable point starting from the given index.\n var updateNextPoint = function updateNextPoint(idx) {\n // If we've previously found a non-NaN point and haven't gone past it yet,\n // just use that.\n if (nextPointIdx >= idx) return;\n\n // We haven't found a non-NaN point yet or have moved past it,\n // look towards the right to find a non-NaN point.\n for (var j = idx; j < points.length; ++j) {\n // Clear out a previously-found point (if any) since it's no longer\n // valid, we shouldn't use it for interpolation anymore.\n nextPoint = null;\n if (!isNaN(points[j].yval) && points[j].yval !== null) {\n nextPointIdx = j;\n nextPoint = points[j];\n break;\n }\n }\n };\n\n for (var i = 0; i < points.length; ++i) {\n var point = points[i];\n var xval = point.xval;\n if (cumulativeYval[xval] === undefined) {\n cumulativeYval[xval] = 0;\n }\n\n var actualYval = point.yval;\n if (isNaN(actualYval) || actualYval === null) {\n if (fillMethod == 'none') {\n actualYval = 0;\n } else {\n // Interpolate/extend for stacking purposes if possible.\n updateNextPoint(i);\n if (prevPoint && nextPoint && fillMethod != 'none') {\n // Use linear interpolation between prevPoint and nextPoint.\n actualYval = prevPoint.yval + (nextPoint.yval - prevPoint.yval) * ((xval - prevPoint.xval) / (nextPoint.xval - prevPoint.xval));\n } else if (prevPoint && fillMethod == 'all') {\n actualYval = prevPoint.yval;\n } else if (nextPoint && fillMethod == 'all') {\n actualYval = nextPoint.yval;\n } else {\n actualYval = 0;\n }\n }\n } else {\n prevPoint = point;\n }\n\n var stackedYval = cumulativeYval[xval];\n if (lastXval != xval) {\n // If an x-value is repeated, we ignore the duplicates.\n stackedYval += actualYval;\n cumulativeYval[xval] = stackedYval;\n }\n lastXval = xval;\n\n point.yval_stacked = stackedYval;\n\n if (stackedYval > seriesExtremes[1]) {\n seriesExtremes[1] = stackedYval;\n }\n if (stackedYval < seriesExtremes[0]) {\n seriesExtremes[0] = stackedYval;\n }\n }\n};\n\n/**\n * Loop over all fields and create datasets, calculating extreme y-values for\n * each series and extreme x-indices as we go.\n *\n * dateWindow is passed in as an explicit parameter so that we can compute\n * extreme values \"speculatively\", i.e. without actually setting state on the\n * dygraph.\n *\n * @param {Array.)>>} rolledSeries, where\n * rolledSeries[seriesIndex][row] = raw point, where\n * seriesIndex is the column number starting with 1, and\n * rawPoint is [x,y] or [x, [y, err]] or [x, [y, yminus, yplus]].\n * @param {?Array.} dateWindow [xmin, xmax] pair, or null.\n * @return {{\n * points: Array.>,\n * seriesExtremes: Array.>,\n * boundaryIds: Array.}}\n * @private\n */\nDygraph.prototype.gatherDatasets_ = function (rolledSeries, dateWindow) {\n var boundaryIds = [];\n var points = [];\n var cumulativeYval = []; // For stacked series.\n var extremes = {}; // series name -> [low, high]\n var seriesIdx, sampleIdx;\n var firstIdx, lastIdx;\n var axisIdx;\n\n // Loop over the fields (series). Go from the last to the first,\n // because if they're stacked that's how we accumulate the values.\n var num_series = rolledSeries.length - 1;\n var series;\n for (seriesIdx = num_series; seriesIdx >= 1; seriesIdx--) {\n if (!this.visibility()[seriesIdx - 1]) continue;\n\n // Prune down to the desired range, if necessary (for zooming)\n // Because there can be lines going to points outside of the visible area,\n // we actually prune to visible points, plus one on either side.\n if (dateWindow) {\n series = rolledSeries[seriesIdx];\n var low = dateWindow[0];\n var high = dateWindow[1];\n\n // TODO(danvk): do binary search instead of linear search.\n // TODO(danvk): pass firstIdx and lastIdx directly to the renderer.\n firstIdx = null;\n lastIdx = null;\n for (sampleIdx = 0; sampleIdx < series.length; sampleIdx++) {\n if (series[sampleIdx][0] >= low && firstIdx === null) {\n firstIdx = sampleIdx;\n }\n if (series[sampleIdx][0] <= high) {\n lastIdx = sampleIdx;\n }\n }\n\n if (firstIdx === null) firstIdx = 0;\n var correctedFirstIdx = firstIdx;\n var isInvalidValue = true;\n while (isInvalidValue && correctedFirstIdx > 0) {\n correctedFirstIdx--;\n // check if the y value is null.\n isInvalidValue = series[correctedFirstIdx][1] === null;\n }\n\n if (lastIdx === null) lastIdx = series.length - 1;\n var correctedLastIdx = lastIdx;\n isInvalidValue = true;\n while (isInvalidValue && correctedLastIdx < series.length - 1) {\n correctedLastIdx++;\n isInvalidValue = series[correctedLastIdx][1] === null;\n }\n\n if (correctedFirstIdx !== firstIdx) {\n firstIdx = correctedFirstIdx;\n }\n if (correctedLastIdx !== lastIdx) {\n lastIdx = correctedLastIdx;\n }\n\n boundaryIds[seriesIdx - 1] = [firstIdx, lastIdx];\n\n // .slice's end is exclusive, we want to include lastIdx.\n series = series.slice(firstIdx, lastIdx + 1);\n } else {\n series = rolledSeries[seriesIdx];\n boundaryIds[seriesIdx - 1] = [0, series.length - 1];\n }\n\n var seriesName = this.attr_(\"labels\")[seriesIdx];\n var seriesExtremes = this.dataHandler_.getExtremeYValues(series, dateWindow, this.getBooleanOption(\"stepPlot\", seriesName));\n\n var seriesPoints = this.dataHandler_.seriesToPoints(series, seriesName, boundaryIds[seriesIdx - 1][0]);\n\n if (this.getBooleanOption(\"stackedGraph\")) {\n axisIdx = this.attributes_.axisForSeries(seriesName);\n if (cumulativeYval[axisIdx] === undefined) {\n cumulativeYval[axisIdx] = [];\n }\n Dygraph.stackPoints_(seriesPoints, cumulativeYval[axisIdx], seriesExtremes, this.getBooleanOption(\"stackedGraphNaNFill\"));\n }\n\n extremes[seriesName] = seriesExtremes;\n points[seriesIdx] = seriesPoints;\n }\n\n return { points: points, extremes: extremes, boundaryIds: boundaryIds };\n};\n\n/**\n * Update the graph with new data. This method is called when the viewing area\n * has changed. If the underlying data or options have changed, predraw_ will\n * be called before drawGraph_ is called.\n *\n * @private\n */\nDygraph.prototype.drawGraph_ = function () {\n var start = new Date();\n\n // This is used to set the second parameter to drawCallback, below.\n var is_initial_draw = this.is_initial_draw_;\n this.is_initial_draw_ = false;\n\n this.layout_.removeAllDatasets();\n this.setColors_();\n this.attrs_.pointSize = 0.5 * this.getNumericOption('highlightCircleSize');\n\n var packed = this.gatherDatasets_(this.rolledSeries_, this.dateWindow_);\n var points = packed.points;\n var extremes = packed.extremes;\n this.boundaryIds_ = packed.boundaryIds;\n\n this.setIndexByName_ = {};\n var labels = this.attr_(\"labels\");\n var dataIdx = 0;\n for (var i = 1; i < points.length; i++) {\n if (!this.visibility()[i - 1]) continue;\n this.layout_.addDataset(labels[i], points[i]);\n this.datasetIndex_[i] = dataIdx++;\n }\n for (var i = 0; i < labels.length; i++) {\n this.setIndexByName_[labels[i]] = i;\n }\n\n this.computeYAxisRanges_(extremes);\n this.layout_.setYAxes(this.axes_);\n\n this.addXTicks_();\n\n // Tell PlotKit to use this new data and render itself\n this.layout_.evaluate();\n this.renderGraph_(is_initial_draw);\n\n if (this.getStringOption(\"timingName\")) {\n var end = new Date();\n console.log(this.getStringOption(\"timingName\") + \" - drawGraph: \" + (end - start) + \"ms\");\n }\n};\n\n/**\n * This does the work of drawing the chart. It assumes that the layout and axis\n * scales have already been set (e.g. by predraw_).\n *\n * @private\n */\nDygraph.prototype.renderGraph_ = function (is_initial_draw) {\n this.cascadeEvents_('clearChart');\n this.plotter_.clear();\n\n var underlayCallback = this.getFunctionOption('underlayCallback');\n if (underlayCallback) {\n // NOTE: we pass the dygraph object to this callback twice to avoid breaking\n // users who expect a deprecated form of this callback.\n underlayCallback.call(this, this.hidden_ctx_, this.layout_.getPlotArea(), this, this);\n }\n\n var e = {\n canvas: this.hidden_,\n drawingContext: this.hidden_ctx_\n };\n this.cascadeEvents_('willDrawChart', e);\n this.plotter_.render();\n this.cascadeEvents_('didDrawChart', e);\n this.lastRow_ = -1; // because plugins/legend.js clears the legend\n\n // TODO(danvk): is this a performance bottleneck when panning?\n // The interaction canvas should already be empty in that situation.\n this.canvas_.getContext('2d').clearRect(0, 0, this.width_, this.height_);\n\n var drawCallback = this.getFunctionOption(\"drawCallback\");\n if (drawCallback !== null) {\n drawCallback.call(this, this, is_initial_draw);\n }\n if (is_initial_draw) {\n this.readyFired_ = true;\n while (this.readyFns_.length > 0) {\n var fn = this.readyFns_.pop();\n fn(this);\n }\n }\n};\n\n/**\n * @private\n * Determine properties of the y-axes which are independent of the data\n * currently being displayed. This includes things like the number of axes and\n * the style of the axes. It does not include the range of each axis and its\n * tick marks.\n * This fills in this.axes_.\n * axes_ = [ { options } ]\n * indices are into the axes_ array.\n */\nDygraph.prototype.computeYAxes_ = function () {\n var axis, index, opts, v;\n\n // this.axes_ doesn't match this.attributes_.axes_.options. It's used for\n // data computation as well as options storage.\n // Go through once and add all the axes.\n this.axes_ = [];\n\n for (axis = 0; axis < this.attributes_.numAxes(); axis++) {\n // Add a new axis, making a copy of its per-axis options.\n opts = { g: this };\n utils.update(opts, this.attributes_.axisOptions(axis));\n this.axes_[axis] = opts;\n }\n\n for (axis = 0; axis < this.axes_.length; axis++) {\n if (axis === 0) {\n opts = this.optionsViewForAxis_('y' + (axis ? '2' : ''));\n v = opts(\"valueRange\");\n if (v) this.axes_[axis].valueRange = v;\n } else {\n // To keep old behavior\n var axes = this.user_attrs_.axes;\n if (axes && axes.y2) {\n v = axes.y2.valueRange;\n if (v) this.axes_[axis].valueRange = v;\n }\n }\n }\n};\n\n/**\n * Returns the number of y-axes on the chart.\n * @return {number} the number of axes.\n */\nDygraph.prototype.numAxes = function () {\n return this.attributes_.numAxes();\n};\n\n/**\n * @private\n * Returns axis properties for the given series.\n * @param {string} setName The name of the series for which to get axis\n * properties, e.g. 'Y1'.\n * @return {Object} The axis properties.\n */\nDygraph.prototype.axisPropertiesForSeries = function (series) {\n // TODO(danvk): handle errors.\n return this.axes_[this.attributes_.axisForSeries(series)];\n};\n\n/**\n * @private\n * Determine the value range and tick marks for each axis.\n * @param {Object} extremes A mapping from seriesName -> [low, high]\n * This fills in the valueRange and ticks fields in each entry of this.axes_.\n */\nDygraph.prototype.computeYAxisRanges_ = function (extremes) {\n var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(num) {\n return isNaN(parseFloat(num));\n };\n var numAxes = this.attributes_.numAxes();\n var ypadCompat, span, series, ypad;\n\n var p_axis;\n\n // Compute extreme values, a span and tick marks for each axis.\n for (var i = 0; i < numAxes; i++) {\n var axis = this.axes_[i];\n var logscale = this.attributes_.getForAxis(\"logscale\", i);\n var includeZero = this.attributes_.getForAxis(\"includeZero\", i);\n var independentTicks = this.attributes_.getForAxis(\"independentTicks\", i);\n series = this.attributes_.seriesForAxis(i);\n\n // Add some padding. This supports two Y padding operation modes:\n //\n // - backwards compatible (yRangePad not set):\n // 10% padding for automatic Y ranges, but not for user-supplied\n // ranges, and move a close-to-zero edge to zero, since drawing at the edge\n // results in invisible lines. Unfortunately lines drawn at the edge of a\n // user-supplied range will still be invisible. If logscale is\n // set, add a variable amount of padding at the top but\n // none at the bottom.\n //\n // - new-style (yRangePad set by the user):\n // always add the specified Y padding.\n //\n ypadCompat = true;\n ypad = 0.1; // add 10%\n var yRangePad = this.getNumericOption('yRangePad');\n if (yRangePad !== null) {\n ypadCompat = false;\n // Convert pixel padding to ratio\n ypad = yRangePad / this.plotter_.area.h;\n }\n\n if (series.length === 0) {\n // If no series are defined or visible then use a reasonable default\n axis.extremeRange = [0, 1];\n } else {\n // Calculate the extremes of extremes.\n var minY = Infinity; // extremes[series[0]][0];\n var maxY = -Infinity; // extremes[series[0]][1];\n var extremeMinY, extremeMaxY;\n\n for (var j = 0; j < series.length; j++) {\n // this skips invisible series\n if (!extremes.hasOwnProperty(series[j])) continue;\n\n // Only use valid extremes to stop null data series' from corrupting the scale.\n extremeMinY = extremes[series[j]][0];\n if (extremeMinY !== null) {\n minY = Math.min(extremeMinY, minY);\n }\n extremeMaxY = extremes[series[j]][1];\n if (extremeMaxY !== null) {\n maxY = Math.max(extremeMaxY, maxY);\n }\n }\n\n // Include zero if requested by the user.\n if (includeZero && !logscale) {\n if (minY > 0) minY = 0;\n if (maxY < 0) maxY = 0;\n }\n\n // Ensure we have a valid scale, otherwise default to [0, 1] for safety.\n if (minY == Infinity) minY = 0;\n if (maxY == -Infinity) maxY = 1;\n\n span = maxY - minY;\n // special case: if we have no sense of scale, center on the sole value.\n if (span === 0) {\n if (maxY !== 0) {\n span = Math.abs(maxY);\n } else {\n // ... and if the sole value is zero, use range 0-1.\n maxY = 1;\n span = 1;\n }\n }\n\n var maxAxisY = maxY,\n minAxisY = minY;\n if (ypadCompat) {\n if (logscale) {\n maxAxisY = maxY + ypad * span;\n minAxisY = minY;\n } else {\n maxAxisY = maxY + ypad * span;\n minAxisY = minY - ypad * span;\n\n // Backwards-compatible behavior: Move the span to start or end at zero if it's\n // close to zero.\n if (minAxisY < 0 && minY >= 0) minAxisY = 0;\n if (maxAxisY > 0 && maxY <= 0) maxAxisY = 0;\n }\n }\n axis.extremeRange = [minAxisY, maxAxisY];\n }\n if (axis.valueRange) {\n // This is a user-set value range for this axis.\n var y0 = isNullUndefinedOrNaN(axis.valueRange[0]) ? axis.extremeRange[0] : axis.valueRange[0];\n var y1 = isNullUndefinedOrNaN(axis.valueRange[1]) ? axis.extremeRange[1] : axis.valueRange[1];\n axis.computedValueRange = [y0, y1];\n } else {\n axis.computedValueRange = axis.extremeRange;\n }\n if (!ypadCompat) {\n // When using yRangePad, adjust the upper/lower bounds to add\n // padding unless the user has zoomed/panned the Y axis range.\n if (logscale) {\n y0 = axis.computedValueRange[0];\n y1 = axis.computedValueRange[1];\n var y0pct = ypad / (2 * ypad - 1);\n var y1pct = (ypad - 1) / (2 * ypad - 1);\n axis.computedValueRange[0] = utils.logRangeFraction(y0, y1, y0pct);\n axis.computedValueRange[1] = utils.logRangeFraction(y0, y1, y1pct);\n } else {\n y0 = axis.computedValueRange[0];\n y1 = axis.computedValueRange[1];\n span = y1 - y0;\n axis.computedValueRange[0] = y0 - span * ypad;\n axis.computedValueRange[1] = y1 + span * ypad;\n }\n }\n\n if (independentTicks) {\n axis.independentTicks = independentTicks;\n var opts = this.optionsViewForAxis_('y' + (i ? '2' : ''));\n var ticker = opts('ticker');\n axis.ticks = ticker(axis.computedValueRange[0], axis.computedValueRange[1], this.plotter_.area.h, opts, this);\n // Define the first independent axis as primary axis.\n if (!p_axis) p_axis = axis;\n }\n }\n if (p_axis === undefined) {\n throw \"Configuration Error: At least one axis has to have the \\\"independentTicks\\\" option activated.\";\n }\n // Add ticks. By default, all axes inherit the tick positions of the\n // primary axis. However, if an axis is specifically marked as having\n // independent ticks, then that is permissible as well.\n for (var i = 0; i < numAxes; i++) {\n var axis = this.axes_[i];\n\n if (!axis.independentTicks) {\n var opts = this.optionsViewForAxis_('y' + (i ? '2' : ''));\n var ticker = opts('ticker');\n var p_ticks = p_axis.ticks;\n var p_scale = p_axis.computedValueRange[1] - p_axis.computedValueRange[0];\n var scale = axis.computedValueRange[1] - axis.computedValueRange[0];\n var tick_values = [];\n for (var k = 0; k < p_ticks.length; k++) {\n var y_frac = (p_ticks[k].v - p_axis.computedValueRange[0]) / p_scale;\n var y_val = axis.computedValueRange[0] + y_frac * scale;\n tick_values.push(y_val);\n }\n\n axis.ticks = ticker(axis.computedValueRange[0], axis.computedValueRange[1], this.plotter_.area.h, opts, this, tick_values);\n }\n }\n};\n\n/**\n * Detects the type of the str (date or numeric) and sets the various\n * formatting attributes in this.attrs_ based on this type.\n * @param {string} str An x value.\n * @private\n */\nDygraph.prototype.detectTypeFromString_ = function (str) {\n var isDate = false;\n var dashPos = str.indexOf('-'); // could be 2006-01-01 _or_ 1.0e-2\n if (dashPos > 0 && str[dashPos - 1] != 'e' && str[dashPos - 1] != 'E' || str.indexOf('/') >= 0 || isNaN(parseFloat(str))) {\n isDate = true;\n } else if (str.length == 8 && str > '19700101' && str < '20371231') {\n // TODO(danvk): remove support for this format.\n isDate = true;\n }\n\n this.setXAxisOptions_(isDate);\n};\n\nDygraph.prototype.setXAxisOptions_ = function (isDate) {\n if (isDate) {\n this.attrs_.xValueParser = utils.dateParser;\n this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;\n this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;\n this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;\n } else {\n /** @private (shut up, jsdoc!) */\n this.attrs_.xValueParser = function (x) {\n return parseFloat(x);\n };\n // TODO(danvk): use Dygraph.numberValueFormatter here?\n /** @private (shut up, jsdoc!) */\n this.attrs_.axes.x.valueFormatter = function (x) {\n return x;\n };\n this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;\n this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;\n }\n};\n\n/**\n * @private\n * Parses a string in a special csv format. We expect a csv file where each\n * line is a date point, and the first field in each line is the date string.\n * We also expect that all remaining fields represent series.\n * if the errorBars attribute is set, then interpret the fields as:\n * date, series1, stddev1, series2, stddev2, ...\n * @param {[Object]} data See above.\n *\n * @return [Object] An array with one entry for each row. These entries\n * are an array of cells in that row. The first entry is the parsed x-value for\n * the row. The second, third, etc. are the y-values. These can take on one of\n * three forms, depending on the CSV and constructor parameters:\n * 1. numeric value\n * 2. [ value, stddev ]\n * 3. [ low value, center value, high value ]\n */\nDygraph.prototype.parseCSV_ = function (data) {\n var ret = [];\n var line_delimiter = utils.detectLineDelimiter(data);\n var lines = data.split(line_delimiter || \"\\n\");\n var vals, j;\n\n // Use the default delimiter or fall back to a tab if that makes sense.\n var delim = this.getStringOption('delimiter');\n if (lines[0].indexOf(delim) == -1 && lines[0].indexOf('\\t') >= 0) {\n delim = '\\t';\n }\n\n var start = 0;\n if (!('labels' in this.user_attrs_)) {\n // User hasn't explicitly set labels, so they're (presumably) in the CSV.\n start = 1;\n this.attrs_.labels = lines[0].split(delim); // NOTE: _not_ user_attrs_.\n this.attributes_.reparseSeries();\n }\n var line_no = 0;\n\n var xParser;\n var defaultParserSet = false; // attempt to auto-detect x value type\n var expectedCols = this.attr_(\"labels\").length;\n var outOfOrder = false;\n for (var i = start; i < lines.length; i++) {\n var line = lines[i];\n line_no = i;\n if (line.length === 0) continue; // skip blank lines\n if (line[0] == '#') continue; // skip comment lines\n var inFields = line.split(delim);\n if (inFields.length < 2) continue;\n\n var fields = [];\n if (!defaultParserSet) {\n this.detectTypeFromString_(inFields[0]);\n xParser = this.getFunctionOption(\"xValueParser\");\n defaultParserSet = true;\n }\n fields[0] = xParser(inFields[0], this);\n\n // If fractions are expected, parse the numbers as \"A/B\"\n if (this.fractions_) {\n for (j = 1; j < inFields.length; j++) {\n // TODO(danvk): figure out an appropriate way to flag parse errors.\n vals = inFields[j].split(\"/\");\n if (vals.length != 2) {\n console.error('Expected fractional \"num/den\" values in CSV data ' + \"but found a value '\" + inFields[j] + \"' on line \" + (1 + i) + \" ('\" + line + \"') which is not of this form.\");\n fields[j] = [0, 0];\n } else {\n fields[j] = [utils.parseFloat_(vals[0], i, line), utils.parseFloat_(vals[1], i, line)];\n }\n }\n } else if (this.getBooleanOption(\"errorBars\")) {\n // If there are error bars, values are (value, stddev) pairs\n if (inFields.length % 2 != 1) {\n console.error('Expected alternating (value, stdev.) pairs in CSV data ' + 'but line ' + (1 + i) + ' has an odd number of values (' + (inFields.length - 1) + \"): '\" + line + \"'\");\n }\n for (j = 1; j < inFields.length; j += 2) {\n fields[(j + 1) / 2] = [utils.parseFloat_(inFields[j], i, line), utils.parseFloat_(inFields[j + 1], i, line)];\n }\n } else if (this.getBooleanOption(\"customBars\")) {\n // Bars are a low;center;high tuple\n for (j = 1; j < inFields.length; j++) {\n var val = inFields[j];\n if (/^ *$/.test(val)) {\n fields[j] = [null, null, null];\n } else {\n vals = val.split(\";\");\n if (vals.length == 3) {\n fields[j] = [utils.parseFloat_(vals[0], i, line), utils.parseFloat_(vals[1], i, line), utils.parseFloat_(vals[2], i, line)];\n } else {\n console.warn('When using customBars, values must be either blank ' + 'or \"low;center;high\" tuples (got \"' + val + '\" on line ' + (1 + i));\n }\n }\n }\n } else {\n // Values are just numbers\n for (j = 1; j < inFields.length; j++) {\n fields[j] = utils.parseFloat_(inFields[j], i, line);\n }\n }\n if (ret.length > 0 && fields[0] < ret[ret.length - 1][0]) {\n outOfOrder = true;\n }\n\n if (fields.length != expectedCols) {\n console.error(\"Number of columns in line \" + i + \" (\" + fields.length + \") does not agree with number of labels (\" + expectedCols + \") \" + line);\n }\n\n // If the user specified the 'labels' option and none of the cells of the\n // first row parsed correctly, then they probably double-specified the\n // labels. We go with the values set in the option, discard this row and\n // log a warning to the JS console.\n if (i === 0 && this.attr_('labels')) {\n var all_null = true;\n for (j = 0; all_null && j < fields.length; j++) {\n if (fields[j]) all_null = false;\n }\n if (all_null) {\n console.warn(\"The dygraphs 'labels' option is set, but the first row \" + \"of CSV data ('\" + line + \"') appears to also contain \" + \"labels. Will drop the CSV labels and use the option \" + \"labels.\");\n continue;\n }\n }\n ret.push(fields);\n }\n\n if (outOfOrder) {\n console.warn(\"CSV is out of order; order it correctly to speed loading.\");\n ret.sort(function (a, b) {\n return a[0] - b[0];\n });\n }\n\n return ret;\n};\n\n// In native format, all values must be dates or numbers.\n// This check isn't perfect but will catch most mistaken uses of strings.\nfunction validateNativeFormat(data) {\n var firstRow = data[0];\n var firstX = firstRow[0];\n if (typeof firstX !== 'number' && !utils.isDateLike(firstX)) {\n throw new Error('Expected number or date but got ' + typeof firstX + ': ' + firstX + '.');\n }\n for (var i = 1; i < firstRow.length; i++) {\n var val = firstRow[i];\n if (val === null || val === undefined) continue;\n if (typeof val === 'number') continue;\n if (utils.isArrayLike(val)) continue; // e.g. error bars or custom bars.\n throw new Error('Expected number or array but got ' + typeof val + ': ' + val + '.');\n }\n}\n\n/**\n * The user has provided their data as a pre-packaged JS array. If the x values\n * are numeric, this is the same as dygraphs' internal format. If the x values\n * are dates, we need to convert them from Date objects to ms since epoch.\n * @param {!Array} data\n * @return {Object} data with numeric x values.\n * @private\n */\nDygraph.prototype.parseArray_ = function (data) {\n // Peek at the first x value to see if it's numeric.\n if (data.length === 0) {\n console.error(\"Can't plot empty data set\");\n return null;\n }\n if (data[0].length === 0) {\n console.error(\"Data set cannot contain an empty row\");\n return null;\n }\n\n validateNativeFormat(data);\n\n var i;\n if (this.attr_(\"labels\") === null) {\n console.warn(\"Using default labels. Set labels explicitly via 'labels' \" + \"in the options parameter\");\n this.attrs_.labels = [\"X\"];\n for (i = 1; i < data[0].length; i++) {\n this.attrs_.labels.push(\"Y\" + i); // Not user_attrs_.\n }\n this.attributes_.reparseSeries();\n } else {\n var num_labels = this.attr_(\"labels\");\n if (num_labels.length != data[0].length) {\n console.error(\"Mismatch between number of labels (\" + num_labels + \")\" + \" and number of columns in array (\" + data[0].length + \")\");\n return null;\n }\n }\n\n if (utils.isDateLike(data[0][0])) {\n // Some intelligent defaults for a date x-axis.\n this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;\n this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;\n this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;\n\n // Assume they're all dates.\n var parsedData = utils.clone(data);\n for (i = 0; i < data.length; i++) {\n if (parsedData[i].length === 0) {\n console.error(\"Row \" + (1 + i) + \" of data is empty\");\n return null;\n }\n if (parsedData[i][0] === null || typeof parsedData[i][0].getTime != 'function' || isNaN(parsedData[i][0].getTime())) {\n console.error(\"x value in row \" + (1 + i) + \" is not a Date\");\n return null;\n }\n parsedData[i][0] = parsedData[i][0].getTime();\n }\n return parsedData;\n } else {\n // Some intelligent defaults for a numeric x-axis.\n /** @private (shut up, jsdoc!) */\n this.attrs_.axes.x.valueFormatter = function (x) {\n return x;\n };\n this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;\n this.attrs_.axes.x.axisLabelFormatter = utils.numberAxisLabelFormatter;\n return data;\n }\n};\n\n/**\n * Parses a DataTable object from gviz.\n * The data is expected to have a first column that is either a date or a\n * number. All subsequent columns must be numbers. If there is a clear mismatch\n * between this.xValueParser_ and the type of the first column, it will be\n * fixed. Fills out rawData_.\n * @param {!google.visualization.DataTable} data See above.\n * @private\n */\nDygraph.prototype.parseDataTable_ = function (data) {\n var shortTextForAnnotationNum = function shortTextForAnnotationNum(num) {\n // converts [0-9]+ [A-Z][a-z]*\n // example: 0=A, 1=B, 25=Z, 26=Aa, 27=Ab\n // and continues like.. Ba Bb .. Za .. Zz..Aaa...Zzz Aaaa Zzzz\n var shortText = String.fromCharCode(65 /* A */ + num % 26);\n num = Math.floor(num / 26);\n while (num > 0) {\n shortText = String.fromCharCode(65 /* A */ + (num - 1) % 26) + shortText.toLowerCase();\n num = Math.floor((num - 1) / 26);\n }\n return shortText;\n };\n\n var cols = data.getNumberOfColumns();\n var rows = data.getNumberOfRows();\n\n var indepType = data.getColumnType(0);\n if (indepType == 'date' || indepType == 'datetime') {\n this.attrs_.xValueParser = utils.dateParser;\n this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;\n this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;\n this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;\n } else if (indepType == 'number') {\n this.attrs_.xValueParser = function (x) {\n return parseFloat(x);\n };\n this.attrs_.axes.x.valueFormatter = function (x) {\n return x;\n };\n this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;\n this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;\n } else {\n throw new Error(\"only 'date', 'datetime' and 'number' types are supported \" + \"for column 1 of DataTable input (Got '\" + indepType + \"')\");\n }\n\n // Array of the column indices which contain data (and not annotations).\n var colIdx = [];\n var annotationCols = {}; // data index -> [annotation cols]\n var hasAnnotations = false;\n var i, j;\n for (i = 1; i < cols; i++) {\n var type = data.getColumnType(i);\n if (type == 'number') {\n colIdx.push(i);\n } else if (type == 'string' && this.getBooleanOption('displayAnnotations')) {\n // This is OK -- it's an annotation column.\n var dataIdx = colIdx[colIdx.length - 1];\n if (!annotationCols.hasOwnProperty(dataIdx)) {\n annotationCols[dataIdx] = [i];\n } else {\n annotationCols[dataIdx].push(i);\n }\n hasAnnotations = true;\n } else {\n throw new Error(\"Only 'number' is supported as a dependent type with Gviz.\" + \" 'string' is only supported if displayAnnotations is true\");\n }\n }\n\n // Read column labels\n // TODO(danvk): add support back for errorBars\n var labels = [data.getColumnLabel(0)];\n for (i = 0; i < colIdx.length; i++) {\n labels.push(data.getColumnLabel(colIdx[i]));\n if (this.getBooleanOption(\"errorBars\")) i += 1;\n }\n this.attrs_.labels = labels;\n cols = labels.length;\n\n var ret = [];\n var outOfOrder = false;\n var annotations = [];\n for (i = 0; i < rows; i++) {\n var row = [];\n if (typeof data.getValue(i, 0) === 'undefined' || data.getValue(i, 0) === null) {\n console.warn(\"Ignoring row \" + i + \" of DataTable because of undefined or null first column.\");\n continue;\n }\n\n if (indepType == 'date' || indepType == 'datetime') {\n row.push(data.getValue(i, 0).getTime());\n } else {\n row.push(data.getValue(i, 0));\n }\n if (!this.getBooleanOption(\"errorBars\")) {\n for (j = 0; j < colIdx.length; j++) {\n var col = colIdx[j];\n row.push(data.getValue(i, col));\n if (hasAnnotations && annotationCols.hasOwnProperty(col) && data.getValue(i, annotationCols[col][0]) !== null) {\n var ann = {};\n ann.series = data.getColumnLabel(col);\n ann.xval = row[0];\n ann.shortText = shortTextForAnnotationNum(annotations.length);\n ann.text = '';\n for (var k = 0; k < annotationCols[col].length; k++) {\n if (k) ann.text += \"\\n\";\n ann.text += data.getValue(i, annotationCols[col][k]);\n }\n annotations.push(ann);\n }\n }\n\n // Strip out infinities, which give dygraphs problems later on.\n for (j = 0; j < row.length; j++) {\n if (!isFinite(row[j])) row[j] = null;\n }\n } else {\n for (j = 0; j < cols - 1; j++) {\n row.push([data.getValue(i, 1 + 2 * j), data.getValue(i, 2 + 2 * j)]);\n }\n }\n if (ret.length > 0 && row[0] < ret[ret.length - 1][0]) {\n outOfOrder = true;\n }\n ret.push(row);\n }\n\n if (outOfOrder) {\n console.warn(\"DataTable is out of order; order it correctly to speed loading.\");\n ret.sort(function (a, b) {\n return a[0] - b[0];\n });\n }\n this.rawData_ = ret;\n\n if (annotations.length > 0) {\n this.setAnnotations(annotations, true);\n }\n this.attributes_.reparseSeries();\n};\n\n/**\n * Signals to plugins that the chart data has updated.\n * This happens after the data has updated but before the chart has redrawn.\n * @private\n */\nDygraph.prototype.cascadeDataDidUpdateEvent_ = function () {\n // TODO(danvk): there are some issues checking xAxisRange() and using\n // toDomCoords from handlers of this event. The visible range should be set\n // when the chart is drawn, not derived from the data.\n this.cascadeEvents_('dataDidUpdate', {});\n};\n\n/**\n * Get the CSV data. If it's in a function, call that function. If it's in a\n * file, do an XMLHttpRequest to get it.\n * @private\n */\nDygraph.prototype.start_ = function () {\n var data = this.file_;\n\n // Functions can return references of all other types.\n if (typeof data == 'function') {\n data = data();\n }\n\n if (utils.isArrayLike(data)) {\n this.rawData_ = this.parseArray_(data);\n this.cascadeDataDidUpdateEvent_();\n this.predraw_();\n } else if (typeof data == 'object' && typeof data.getColumnRange == 'function') {\n // must be a DataTable from gviz.\n this.parseDataTable_(data);\n this.cascadeDataDidUpdateEvent_();\n this.predraw_();\n } else if (typeof data == 'string') {\n // Heuristic: a newline means it's CSV data. Otherwise it's an URL.\n var line_delimiter = utils.detectLineDelimiter(data);\n if (line_delimiter) {\n this.loadedEvent_(data);\n } else {\n // REMOVE_FOR_IE\n var req;\n if (window.XMLHttpRequest) {\n // Firefox, Opera, IE7, and other browsers will use the native object\n req = new XMLHttpRequest();\n } else {\n // IE 5 and 6 will use the ActiveX control\n req = new ActiveXObject(\"Microsoft.XMLHTTP\");\n }\n\n var caller = this;\n req.onreadystatechange = function () {\n if (req.readyState == 4) {\n if (req.status === 200 || // Normal http\n req.status === 0) {\n // Chrome w/ --allow-file-access-from-files\n caller.loadedEvent_(req.responseText);\n }\n }\n };\n\n req.open(\"GET\", data, true);\n req.send(null);\n }\n } else {\n console.error(\"Unknown data format: \" + typeof data);\n }\n};\n\n/**\n * Changes various properties of the graph. These can include:\n *
    \n *
  • file: changes the source data for the graph
  • \n *
  • errorBars: changes whether the data contains stddev
  • \n *
\n *\n * There's a huge variety of options that can be passed to this method. For a\n * full list, see http://dygraphs.com/options.html.\n *\n * @param {Object} input_attrs The new properties and values\n * @param {boolean} block_redraw Usually the chart is redrawn after every\n * call to updateOptions(). If you know better, you can pass true to\n * explicitly block the redraw. This can be useful for chaining\n * updateOptions() calls, avoiding the occasional infinite loop and\n * preventing redraws when it's not necessary (e.g. when updating a\n * callback).\n */\nDygraph.prototype.updateOptions = function (input_attrs, block_redraw) {\n if (typeof block_redraw == 'undefined') block_redraw = false;\n\n // copyUserAttrs_ drops the \"file\" parameter as a convenience to us.\n var file = input_attrs.file;\n var attrs = Dygraph.copyUserAttrs_(input_attrs);\n\n // TODO(danvk): this is a mess. Move these options into attr_.\n if ('rollPeriod' in attrs) {\n this.rollPeriod_ = attrs.rollPeriod;\n }\n if ('dateWindow' in attrs) {\n this.dateWindow_ = attrs.dateWindow;\n }\n\n // TODO(danvk): validate per-series options.\n // Supported:\n // strokeWidth\n // pointSize\n // drawPoints\n // highlightCircleSize\n\n // Check if this set options will require new points.\n var requiresNewPoints = utils.isPixelChangingOptionList(this.attr_(\"labels\"), attrs);\n\n utils.updateDeep(this.user_attrs_, attrs);\n\n this.attributes_.reparseSeries();\n\n if (file) {\n // This event indicates that the data is about to change, but hasn't yet.\n // TODO(danvk): support cancellation of the update via this event.\n this.cascadeEvents_('dataWillUpdate', {});\n\n this.file_ = file;\n if (!block_redraw) this.start_();\n } else {\n if (!block_redraw) {\n if (requiresNewPoints) {\n this.predraw_();\n } else {\n this.renderGraph_(false);\n }\n }\n }\n};\n\n/**\n * Make a copy of input attributes, removing file as a convenience.\n * @private\n */\nDygraph.copyUserAttrs_ = function (attrs) {\n var my_attrs = {};\n for (var k in attrs) {\n if (!attrs.hasOwnProperty(k)) continue;\n if (k == 'file') continue;\n if (attrs.hasOwnProperty(k)) my_attrs[k] = attrs[k];\n }\n return my_attrs;\n};\n\n/**\n * Resizes the dygraph. If no parameters are specified, resizes to fill the\n * containing div (which has presumably changed size since the dygraph was\n * instantiated. If the width/height are specified, the div will be resized.\n *\n * This is far more efficient than destroying and re-instantiating a\n * Dygraph, since it doesn't have to reparse the underlying data.\n *\n * @param {number} width Width (in pixels)\n * @param {number} height Height (in pixels)\n */\nDygraph.prototype.resize = function (width, height) {\n if (this.resize_lock) {\n return;\n }\n this.resize_lock = true;\n\n if (width === null != (height === null)) {\n console.warn(\"Dygraph.resize() should be called with zero parameters or \" + \"two non-NULL parameters. Pretending it was zero.\");\n width = height = null;\n }\n\n var old_width = this.width_;\n var old_height = this.height_;\n\n if (width) {\n this.maindiv_.style.width = width + \"px\";\n this.maindiv_.style.height = height + \"px\";\n this.width_ = width;\n this.height_ = height;\n } else {\n this.width_ = this.maindiv_.clientWidth;\n this.height_ = this.maindiv_.clientHeight;\n }\n\n if (old_width != this.width_ || old_height != this.height_) {\n // Resizing a canvas erases it, even when the size doesn't change, so\n // any resize needs to be followed by a redraw.\n this.resizeElements_();\n this.predraw_();\n }\n\n this.resize_lock = false;\n};\n\n/**\n * Adjusts the number of points in the rolling average. Updates the graph to\n * reflect the new averaging period.\n * @param {number} length Number of points over which to average the data.\n */\nDygraph.prototype.adjustRoll = function (length) {\n this.rollPeriod_ = length;\n this.predraw_();\n};\n\n/**\n * Returns a boolean array of visibility statuses.\n */\nDygraph.prototype.visibility = function () {\n // Do lazy-initialization, so that this happens after we know the number of\n // data series.\n if (!this.getOption(\"visibility\")) {\n this.attrs_.visibility = [];\n }\n // TODO(danvk): it looks like this could go into an infinite loop w/ user_attrs.\n while (this.getOption(\"visibility\").length < this.numColumns() - 1) {\n this.attrs_.visibility.push(true);\n }\n return this.getOption(\"visibility\");\n};\n\n/**\n * Changes the visibility of one or more series.\n *\n * @param {number|number[]|object} num the series index or an array of series indices\n * or a boolean array of visibility states by index\n * or an object mapping series numbers, as keys, to\n * visibility state (boolean values)\n * @param {boolean} value the visibility state expressed as a boolean\n */\nDygraph.prototype.setVisibility = function (num, value) {\n var x = this.visibility();\n var numIsObject = false;\n\n if (!Array.isArray(num)) {\n if (num !== null && typeof num === 'object') {\n numIsObject = true;\n } else {\n num = [num];\n }\n }\n\n if (numIsObject) {\n for (var i in num) {\n if (num.hasOwnProperty(i)) {\n if (i < 0 || i >= x.length) {\n console.warn(\"Invalid series number in setVisibility: \" + i);\n } else {\n x[i] = num[i];\n }\n }\n }\n } else {\n for (var i = 0; i < num.length; i++) {\n if (typeof num[i] === 'boolean') {\n if (i >= x.length) {\n console.warn(\"Invalid series number in setVisibility: \" + i);\n } else {\n x[i] = num[i];\n }\n } else {\n if (num[i] < 0 || num[i] >= x.length) {\n console.warn(\"Invalid series number in setVisibility: \" + num[i]);\n } else {\n x[num[i]] = value;\n }\n }\n }\n }\n\n this.predraw_();\n};\n\n/**\n * How large of an area will the dygraph render itself in?\n * This is used for testing.\n * @return A {width: w, height: h} object.\n * @private\n */\nDygraph.prototype.size = function () {\n return { width: this.width_, height: this.height_ };\n};\n\n/**\n * Update the list of annotations and redraw the chart.\n * See dygraphs.com/annotations.html for more info on how to use annotations.\n * @param ann {Array} An array of annotation objects.\n * @param suppressDraw {Boolean} Set to \"true\" to block chart redraw (optional).\n */\nDygraph.prototype.setAnnotations = function (ann, suppressDraw) {\n // Only add the annotation CSS rule once we know it will be used.\n this.annotations_ = ann;\n if (!this.layout_) {\n console.warn(\"Tried to setAnnotations before dygraph was ready. \" + \"Try setting them in a ready() block. See \" + \"dygraphs.com/tests/annotation.html\");\n return;\n }\n\n this.layout_.setAnnotations(this.annotations_);\n if (!suppressDraw) {\n this.predraw_();\n }\n};\n\n/**\n * Return the list of annotations.\n */\nDygraph.prototype.annotations = function () {\n return this.annotations_;\n};\n\n/**\n * Get the list of label names for this graph. The first column is the\n * x-axis, so the data series names start at index 1.\n *\n * Returns null when labels have not yet been defined.\n */\nDygraph.prototype.getLabels = function () {\n var labels = this.attr_(\"labels\");\n return labels ? labels.slice() : null;\n};\n\n/**\n * Get the index of a series (column) given its name. The first column is the\n * x-axis, so the data series start with index 1.\n */\nDygraph.prototype.indexFromSetName = function (name) {\n return this.setIndexByName_[name];\n};\n\n/**\n * Find the row number corresponding to the given x-value.\n * Returns null if there is no such x-value in the data.\n * If there are multiple rows with the same x-value, this will return the\n * first one.\n * @param {number} xVal The x-value to look for (e.g. millis since epoch).\n * @return {?number} The row number, which you can pass to getValue(), or null.\n */\nDygraph.prototype.getRowForX = function (xVal) {\n var low = 0,\n high = this.numRows() - 1;\n\n while (low <= high) {\n var idx = high + low >> 1;\n var x = this.getValue(idx, 0);\n if (x < xVal) {\n low = idx + 1;\n } else if (x > xVal) {\n high = idx - 1;\n } else if (low != idx) {\n // equal, but there may be an earlier match.\n high = idx;\n } else {\n return idx;\n }\n }\n\n return null;\n};\n\n/**\n * Trigger a callback when the dygraph has drawn itself and is ready to be\n * manipulated. This is primarily useful when dygraphs has to do an XHR for the\n * data (i.e. a URL is passed as the data source) and the chart is drawn\n * asynchronously. If the chart has already drawn, the callback will fire\n * immediately.\n *\n * This is a good place to call setAnnotation().\n *\n * @param {function(!Dygraph)} callback The callback to trigger when the chart\n * is ready.\n */\nDygraph.prototype.ready = function (callback) {\n if (this.is_initial_draw_) {\n this.readyFns_.push(callback);\n } else {\n callback.call(this, this);\n }\n};\n\n/**\n * Add an event handler. This event handler is kept until the graph is\n * destroyed with a call to graph.destroy().\n *\n * @param {!Node} elem The element to add the event to.\n * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.\n * @param {function(Event):(boolean|undefined)} fn The function to call\n * on the event. The function takes one parameter: the event object.\n * @private\n */\nDygraph.prototype.addAndTrackEvent = function (elem, type, fn) {\n utils.addEvent(elem, type, fn);\n this.registeredEvents_.push({ elem: elem, type: type, fn: fn });\n};\n\nDygraph.prototype.removeTrackedEvents_ = function () {\n if (this.registeredEvents_) {\n for (var idx = 0; idx < this.registeredEvents_.length; idx++) {\n var reg = this.registeredEvents_[idx];\n utils.removeEvent(reg.elem, reg.type, reg.fn);\n }\n }\n\n this.registeredEvents_ = [];\n};\n\n// Installed plugins, in order of precedence (most-general to most-specific).\nDygraph.PLUGINS = [_pluginsLegend2['default'], _pluginsAxes2['default'], _pluginsRangeSelector2['default'], // Has to be before ChartLabels so that its callbacks are called after ChartLabels' callbacks.\n_pluginsChartLabels2['default'], _pluginsAnnotations2['default'], _pluginsGrid2['default']];\n\n// There are many symbols which have historically been available through the\n// Dygraph class. These are exported here for backwards compatibility.\nDygraph.GVizChart = _dygraphGviz2['default'];\nDygraph.DASHED_LINE = utils.DASHED_LINE;\nDygraph.DOT_DASH_LINE = utils.DOT_DASH_LINE;\nDygraph.dateAxisLabelFormatter = utils.dateAxisLabelFormatter;\nDygraph.toRGB_ = utils.toRGB_;\nDygraph.findPos = utils.findPos;\nDygraph.pageX = utils.pageX;\nDygraph.pageY = utils.pageY;\nDygraph.dateString_ = utils.dateString_;\nDygraph.defaultInteractionModel = _dygraphInteractionModel2['default'].defaultModel;\nDygraph.nonInteractiveModel = Dygraph.nonInteractiveModel_ = _dygraphInteractionModel2['default'].nonInteractiveModel_;\nDygraph.Circles = utils.Circles;\n\nDygraph.Plugins = {\n Legend: _pluginsLegend2['default'],\n Axes: _pluginsAxes2['default'],\n Annotations: _pluginsAnnotations2['default'],\n ChartLabels: _pluginsChartLabels2['default'],\n Grid: _pluginsGrid2['default'],\n RangeSelector: _pluginsRangeSelector2['default']\n};\n\nDygraph.DataHandlers = {\n DefaultHandler: _datahandlerDefault2['default'],\n BarsHandler: _datahandlerBars2['default'],\n CustomBarsHandler: _datahandlerBarsCustom2['default'],\n DefaultFractionHandler: _datahandlerDefaultFractions2['default'],\n ErrorBarsHandler: _datahandlerBarsError2['default'],\n FractionsBarsHandler: _datahandlerBarsFractions2['default']\n};\n\nDygraph.startPan = _dygraphInteractionModel2['default'].startPan;\nDygraph.startZoom = _dygraphInteractionModel2['default'].startZoom;\nDygraph.movePan = _dygraphInteractionModel2['default'].movePan;\nDygraph.moveZoom = _dygraphInteractionModel2['default'].moveZoom;\nDygraph.endPan = _dygraphInteractionModel2['default'].endPan;\nDygraph.endZoom = _dygraphInteractionModel2['default'].endZoom;\n\nDygraph.numericLinearTicks = DygraphTickers.numericLinearTicks;\nDygraph.numericTicks = DygraphTickers.numericTicks;\nDygraph.dateTicker = DygraphTickers.dateTicker;\nDygraph.Granularity = DygraphTickers.Granularity;\nDygraph.getDateAxis = DygraphTickers.getDateAxis;\nDygraph.floatFormat = utils.floatFormat;\n\nexports['default'] = Dygraph;\nmodule.exports = exports['default'];","/**\n * @license\n * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)\n * MIT-licensed (http://opensource.org/licenses/MIT)\n */\n\n/**\n * @fileoverview Description of this file.\n * @author danvk@google.com (Dan Vanderkam)\n *\n * A ticker is a function with the following interface:\n *\n * function(a, b, pixels, options_view, dygraph, forced_values);\n * -> [ { v: tick1_v, label: tick1_label[, label_v: label_v1] },\n * { v: tick2_v, label: tick2_label[, label_v: label_v2] },\n * ...\n * ]\n *\n * The returned value is called a \"tick list\".\n *\n * Arguments\n * ---------\n *\n * [a, b] is the range of the axis for which ticks are being generated. For a\n * numeric axis, these will simply be numbers. For a date axis, these will be\n * millis since epoch (convertable to Date objects using \"new Date(a)\" and \"new\n * Date(b)\").\n *\n * opts provides access to chart- and axis-specific options. It can be used to\n * access number/date formatting code/options, check for a log scale, etc.\n *\n * pixels is the length of the axis in pixels. opts('pixelsPerLabel') is the\n * minimum amount of space to be allotted to each label. For instance, if\n * pixels=400 and opts('pixelsPerLabel')=40 then the ticker should return\n * between zero and ten (400/40) ticks.\n *\n * dygraph is the Dygraph object for which an axis is being constructed.\n *\n * forced_values is used for secondary y-axes. The tick positions are typically\n * set by the primary y-axis, so the secondary y-axis has no choice in where to\n * put these. It simply has to generate labels for these data values.\n *\n * Tick lists\n * ----------\n * Typically a tick will have both a grid/tick line and a label at one end of\n * that line (at the bottom for an x-axis, at left or right for the y-axis).\n *\n * A tick may be missing one of these two components:\n * - If \"label_v\" is specified instead of \"v\", then there will be no tick or\n * gridline, just a label.\n * - Similarly, if \"label\" is not specified, then there will be a gridline\n * without a label.\n *\n * This flexibility is useful in a few situations:\n * - For log scales, some of the tick lines may be too close to all have labels.\n * - For date scales where years are being displayed, it is desirable to display\n * tick marks at the beginnings of years but labels (e.g. \"2006\") in the\n * middle of the years.\n */\n\n/*jshint sub:true */\n/*global Dygraph:false */\n\"use strict\";\n\nObject.defineProperty(exports, '__esModule', {\n value: true\n});\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }\n\nvar _dygraphUtils = require('./dygraph-utils');\n\nvar utils = _interopRequireWildcard(_dygraphUtils);\n\n/** @typedef {Array.<{v:number, label:string, label_v:(string|undefined)}>} */\nvar TickList = undefined; // the ' = undefined' keeps jshint happy.\n\n/** @typedef {function(\n * number,\n * number,\n * number,\n * function(string):*,\n * Dygraph=,\n * Array.=\n * ): TickList}\n */\nvar Ticker = undefined; // the ' = undefined' keeps jshint happy.\n\n/** @type {Ticker} */\nvar numericLinearTicks = function numericLinearTicks(a, b, pixels, opts, dygraph, vals) {\n var nonLogscaleOpts = function nonLogscaleOpts(opt) {\n if (opt === 'logscale') return false;\n return opts(opt);\n };\n return numericTicks(a, b, pixels, nonLogscaleOpts, dygraph, vals);\n};\n\nexports.numericLinearTicks = numericLinearTicks;\n/** @type {Ticker} */\nvar numericTicks = function numericTicks(a, b, pixels, opts, dygraph, vals) {\n var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel');\n var ticks = [];\n var i, j, tickV, nTicks;\n if (vals) {\n for (i = 0; i < vals.length; i++) {\n ticks.push({ v: vals[i] });\n }\n } else {\n // TODO(danvk): factor this log-scale block out into a separate function.\n if (opts(\"logscale\")) {\n nTicks = Math.floor(pixels / pixels_per_tick);\n var minIdx = utils.binarySearch(a, PREFERRED_LOG_TICK_VALUES, 1);\n var maxIdx = utils.binarySearch(b, PREFERRED_LOG_TICK_VALUES, -1);\n if (minIdx == -1) {\n minIdx = 0;\n }\n if (maxIdx == -1) {\n maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1;\n }\n // Count the number of tick values would appear, if we can get at least\n // nTicks / 4 accept them.\n var lastDisplayed = null;\n if (maxIdx - minIdx >= nTicks / 4) {\n for (var idx = maxIdx; idx >= minIdx; idx--) {\n var tickValue = PREFERRED_LOG_TICK_VALUES[idx];\n var pixel_coord = Math.log(tickValue / a) / Math.log(b / a) * pixels;\n var tick = { v: tickValue };\n if (lastDisplayed === null) {\n lastDisplayed = {\n tickValue: tickValue,\n pixel_coord: pixel_coord\n };\n } else {\n if (Math.abs(pixel_coord - lastDisplayed.pixel_coord) >= pixels_per_tick) {\n lastDisplayed = {\n tickValue: tickValue,\n pixel_coord: pixel_coord\n };\n } else {\n tick.label = \"\";\n }\n }\n ticks.push(tick);\n }\n // Since we went in backwards order.\n ticks.reverse();\n }\n }\n\n // ticks.length won't be 0 if the log scale function finds values to insert.\n if (ticks.length === 0) {\n // Basic idea:\n // Try labels every 1, 2, 5, 10, 20, 50, 100, etc.\n // Calculate the resulting tick spacing (i.e. this.height_ / nTicks).\n // The first spacing greater than pixelsPerYLabel is what we use.\n // TODO(danvk): version that works on a log scale.\n var kmg2 = opts(\"labelsKMG2\");\n var mults, base;\n if (kmg2) {\n mults = [1, 2, 4, 8, 16, 32, 64, 128, 256];\n base = 16;\n } else {\n mults = [1, 2, 5, 10, 20, 50, 100];\n base = 10;\n }\n\n // Get the maximum number of permitted ticks based on the\n // graph's pixel size and pixels_per_tick setting.\n var max_ticks = Math.ceil(pixels / pixels_per_tick);\n\n // Now calculate the data unit equivalent of this tick spacing.\n // Use abs() since graphs may have a reversed Y axis.\n var units_per_tick = Math.abs(b - a) / max_ticks;\n\n // Based on this, get a starting scale which is the largest\n // integer power of the chosen base (10 or 16) that still remains\n // below the requested pixels_per_tick spacing.\n var base_power = Math.floor(Math.log(units_per_tick) / Math.log(base));\n var base_scale = Math.pow(base, base_power);\n\n // Now try multiples of the starting scale until we find one\n // that results in tick marks spaced sufficiently far apart.\n // The \"mults\" array should cover the range 1 .. base^2 to\n // adjust for rounding and edge effects.\n var scale, low_val, high_val, spacing;\n for (j = 0; j < mults.length; j++) {\n scale = base_scale * mults[j];\n low_val = Math.floor(a / scale) * scale;\n high_val = Math.ceil(b / scale) * scale;\n nTicks = Math.abs(high_val - low_val) / scale;\n spacing = pixels / nTicks;\n if (spacing > pixels_per_tick) break;\n }\n\n // Construct the set of ticks.\n // Allow reverse y-axis if it's explicitly requested.\n if (low_val > high_val) scale *= -1;\n for (i = 0; i <= nTicks; i++) {\n tickV = low_val + i * scale;\n ticks.push({ v: tickV });\n }\n }\n }\n\n var formatter = /**@type{AxisLabelFormatter}*/opts('axisLabelFormatter');\n\n // Add labels to the ticks.\n for (i = 0; i < ticks.length; i++) {\n if (ticks[i].label !== undefined) continue; // Use current label.\n // TODO(danvk): set granularity to something appropriate here.\n ticks[i].label = formatter.call(dygraph, ticks[i].v, 0, opts, dygraph);\n }\n\n return ticks;\n};\n\nexports.numericTicks = numericTicks;\n/** @type {Ticker} */\nvar dateTicker = function dateTicker(a, b, pixels, opts, dygraph, vals) {\n var chosen = pickDateTickGranularity(a, b, pixels, opts);\n\n if (chosen >= 0) {\n return getDateAxis(a, b, chosen, opts, dygraph);\n } else {\n // this can happen if self.width_ is zero.\n return [];\n }\n};\n\nexports.dateTicker = dateTicker;\n// Time granularity enumeration\nvar Granularity = {\n MILLISECONDLY: 0,\n TWO_MILLISECONDLY: 1,\n FIVE_MILLISECONDLY: 2,\n TEN_MILLISECONDLY: 3,\n FIFTY_MILLISECONDLY: 4,\n HUNDRED_MILLISECONDLY: 5,\n FIVE_HUNDRED_MILLISECONDLY: 6,\n SECONDLY: 7,\n TWO_SECONDLY: 8,\n FIVE_SECONDLY: 9,\n TEN_SECONDLY: 10,\n THIRTY_SECONDLY: 11,\n MINUTELY: 12,\n TWO_MINUTELY: 13,\n FIVE_MINUTELY: 14,\n TEN_MINUTELY: 15,\n THIRTY_MINUTELY: 16,\n HOURLY: 17,\n TWO_HOURLY: 18,\n SIX_HOURLY: 19,\n DAILY: 20,\n TWO_DAILY: 21,\n WEEKLY: 22,\n MONTHLY: 23,\n QUARTERLY: 24,\n BIANNUAL: 25,\n ANNUAL: 26,\n DECADAL: 27,\n CENTENNIAL: 28,\n NUM_GRANULARITIES: 29\n};\n\nexports.Granularity = Granularity;\n// Date components enumeration (in the order of the arguments in Date)\n// TODO: make this an @enum\nvar DateField = {\n DATEFIELD_Y: 0,\n DATEFIELD_M: 1,\n DATEFIELD_D: 2,\n DATEFIELD_HH: 3,\n DATEFIELD_MM: 4,\n DATEFIELD_SS: 5,\n DATEFIELD_MS: 6,\n NUM_DATEFIELDS: 7\n};\n\n/**\n * The value of datefield will start at an even multiple of \"step\", i.e.\n * if datefield=SS and step=5 then the first tick will be on a multiple of 5s.\n *\n * For granularities <= HOURLY, ticks are generated every `spacing` ms.\n *\n * At coarser granularities, ticks are generated by incrementing `datefield` by\n * `step`. In this case, the `spacing` value is only used to estimate the\n * number of ticks. It should roughly correspond to the spacing between\n * adjacent ticks.\n *\n * @type {Array.<{datefield:number, step:number, spacing:number}>}\n */\nvar TICK_PLACEMENT = [];\nTICK_PLACEMENT[Granularity.MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 1, spacing: 1 };\nTICK_PLACEMENT[Granularity.TWO_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 2, spacing: 2 };\nTICK_PLACEMENT[Granularity.FIVE_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 5, spacing: 5 };\nTICK_PLACEMENT[Granularity.TEN_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 10, spacing: 10 };\nTICK_PLACEMENT[Granularity.FIFTY_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 50, spacing: 50 };\nTICK_PLACEMENT[Granularity.HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 100, spacing: 100 };\nTICK_PLACEMENT[Granularity.FIVE_HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 500, spacing: 500 };\nTICK_PLACEMENT[Granularity.SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 1, spacing: 1000 * 1 };\nTICK_PLACEMENT[Granularity.TWO_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 2, spacing: 1000 * 2 };\nTICK_PLACEMENT[Granularity.FIVE_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 5, spacing: 1000 * 5 };\nTICK_PLACEMENT[Granularity.TEN_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 10, spacing: 1000 * 10 };\nTICK_PLACEMENT[Granularity.THIRTY_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 30, spacing: 1000 * 30 };\nTICK_PLACEMENT[Granularity.MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 1, spacing: 1000 * 60 };\nTICK_PLACEMENT[Granularity.TWO_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 2, spacing: 1000 * 60 * 2 };\nTICK_PLACEMENT[Granularity.FIVE_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 5, spacing: 1000 * 60 * 5 };\nTICK_PLACEMENT[Granularity.TEN_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 10, spacing: 1000 * 60 * 10 };\nTICK_PLACEMENT[Granularity.THIRTY_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 30, spacing: 1000 * 60 * 30 };\nTICK_PLACEMENT[Granularity.HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 1, spacing: 1000 * 3600 };\nTICK_PLACEMENT[Granularity.TWO_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 2, spacing: 1000 * 3600 * 2 };\nTICK_PLACEMENT[Granularity.SIX_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 6, spacing: 1000 * 3600 * 6 };\nTICK_PLACEMENT[Granularity.DAILY] = { datefield: DateField.DATEFIELD_D, step: 1, spacing: 1000 * 86400 };\nTICK_PLACEMENT[Granularity.TWO_DAILY] = { datefield: DateField.DATEFIELD_D, step: 2, spacing: 1000 * 86400 * 2 };\nTICK_PLACEMENT[Granularity.WEEKLY] = { datefield: DateField.DATEFIELD_D, step: 7, spacing: 1000 * 604800 };\nTICK_PLACEMENT[Granularity.MONTHLY] = { datefield: DateField.DATEFIELD_M, step: 1, spacing: 1000 * 7200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 12\nTICK_PLACEMENT[Granularity.QUARTERLY] = { datefield: DateField.DATEFIELD_M, step: 3, spacing: 1000 * 21600 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 4\nTICK_PLACEMENT[Granularity.BIANNUAL] = { datefield: DateField.DATEFIELD_M, step: 6, spacing: 1000 * 43200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 2\nTICK_PLACEMENT[Granularity.ANNUAL] = { datefield: DateField.DATEFIELD_Y, step: 1, spacing: 1000 * 86400 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 1\nTICK_PLACEMENT[Granularity.DECADAL] = { datefield: DateField.DATEFIELD_Y, step: 10, spacing: 1000 * 864000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 10\nTICK_PLACEMENT[Granularity.CENTENNIAL] = { datefield: DateField.DATEFIELD_Y, step: 100, spacing: 1000 * 8640000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 100\n\n/**\n * This is a list of human-friendly values at which to show tick marks on a log\n * scale. It is k * 10^n, where k=1..9 and n=-39..+39, so:\n * ..., 1, 2, 3, 4, 5, ..., 9, 10, 20, 30, ..., 90, 100, 200, 300, ...\n * NOTE: this assumes that utils.LOG_SCALE = 10.\n * @type {Array.}\n */\nvar PREFERRED_LOG_TICK_VALUES = (function () {\n var vals = [];\n for (var power = -39; power <= 39; power++) {\n var range = Math.pow(10, power);\n for (var mult = 1; mult <= 9; mult++) {\n var val = range * mult;\n vals.push(val);\n }\n }\n return vals;\n})();\n\n/**\n * Determine the correct granularity of ticks on a date axis.\n *\n * @param {number} a Left edge of the chart (ms)\n * @param {number} b Right edge of the chart (ms)\n * @param {number} pixels Size of the chart in the relevant dimension (width).\n * @param {function(string):*} opts Function mapping from option name -> value.\n * @return {number} The appropriate axis granularity for this chart. See the\n * enumeration of possible values in dygraph-tickers.js.\n */\nvar pickDateTickGranularity = function pickDateTickGranularity(a, b, pixels, opts) {\n var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel');\n for (var i = 0; i < Granularity.NUM_GRANULARITIES; i++) {\n var num_ticks = numDateTicks(a, b, i);\n if (pixels / num_ticks >= pixels_per_tick) {\n return i;\n }\n }\n return -1;\n};\n\n/**\n * Compute the number of ticks on a date axis for a given granularity.\n * @param {number} start_time\n * @param {number} end_time\n * @param {number} granularity (one of the granularities enumerated above)\n * @return {number} (Approximate) number of ticks that would result.\n */\nvar numDateTicks = function numDateTicks(start_time, end_time, granularity) {\n var spacing = TICK_PLACEMENT[granularity].spacing;\n return Math.round(1.0 * (end_time - start_time) / spacing);\n};\n\n/**\n * Compute the positions and labels of ticks on a date axis for a given granularity.\n * @param {number} start_time\n * @param {number} end_time\n * @param {number} granularity (one of the granularities enumerated above)\n * @param {function(string):*} opts Function mapping from option name -> value.\n * @param {Dygraph=} dg\n * @return {!TickList}\n */\nvar getDateAxis = function getDateAxis(start_time, end_time, granularity, opts, dg) {\n var formatter = /** @type{AxisLabelFormatter} */opts(\"axisLabelFormatter\");\n var utc = opts(\"labelsUTC\");\n var accessors = utc ? utils.DateAccessorsUTC : utils.DateAccessorsLocal;\n\n var datefield = TICK_PLACEMENT[granularity].datefield;\n var step = TICK_PLACEMENT[granularity].step;\n var spacing = TICK_PLACEMENT[granularity].spacing;\n\n // Choose a nice tick position before the initial instant.\n // Currently, this code deals properly with the existent daily granularities:\n // DAILY (with step of 1) and WEEKLY (with step of 7 but specially handled).\n // Other daily granularities (say TWO_DAILY) should also be handled specially\n // by setting the start_date_offset to 0.\n var start_date = new Date(start_time);\n var date_array = [];\n date_array[DateField.DATEFIELD_Y] = accessors.getFullYear(start_date);\n date_array[DateField.DATEFIELD_M] = accessors.getMonth(start_date);\n date_array[DateField.DATEFIELD_D] = accessors.getDate(start_date);\n date_array[DateField.DATEFIELD_HH] = accessors.getHours(start_date);\n date_array[DateField.DATEFIELD_MM] = accessors.getMinutes(start_date);\n date_array[DateField.DATEFIELD_SS] = accessors.getSeconds(start_date);\n date_array[DateField.DATEFIELD_MS] = accessors.getMilliseconds(start_date);\n\n var start_date_offset = date_array[datefield] % step;\n if (granularity == Granularity.WEEKLY) {\n // This will put the ticks on Sundays.\n start_date_offset = accessors.getDay(start_date);\n }\n\n date_array[datefield] -= start_date_offset;\n for (var df = datefield + 1; df < DateField.NUM_DATEFIELDS; df++) {\n // The minimum value is 1 for the day of month, and 0 for all other fields.\n date_array[df] = df === DateField.DATEFIELD_D ? 1 : 0;\n }\n\n // Generate the ticks.\n // For granularities not coarser than HOURLY we use the fact that:\n // the number of milliseconds between ticks is constant\n // and equal to the defined spacing.\n // Otherwise we rely on the 'roll over' property of the Date functions:\n // when some date field is set to a value outside of its logical range,\n // the excess 'rolls over' the next (more significant) field.\n // However, when using local time with DST transitions,\n // there are dates that do not represent any time value at all\n // (those in the hour skipped at the 'spring forward'),\n // and the JavaScript engines usually return an equivalent value.\n // Hence we have to check that the date is properly increased at each step,\n // returning a date at a nice tick position.\n var ticks = [];\n var tick_date = accessors.makeDate.apply(null, date_array);\n var tick_time = tick_date.getTime();\n if (granularity <= Granularity.HOURLY) {\n if (tick_time < start_time) {\n tick_time += spacing;\n tick_date = new Date(tick_time);\n }\n while (tick_time <= end_time) {\n ticks.push({ v: tick_time,\n label: formatter.call(dg, tick_date, granularity, opts, dg)\n });\n tick_time += spacing;\n tick_date = new Date(tick_time);\n }\n } else {\n if (tick_time < start_time) {\n date_array[datefield] += step;\n tick_date = accessors.makeDate.apply(null, date_array);\n tick_time = tick_date.getTime();\n }\n while (tick_time <= end_time) {\n if (granularity >= Granularity.DAILY || accessors.getHours(tick_date) % step === 0) {\n ticks.push({ v: tick_time,\n label: formatter.call(dg, tick_date, granularity, opts, dg)\n });\n }\n date_array[datefield] += step;\n tick_date = accessors.makeDate.apply(null, date_array);\n tick_time = tick_date.getTime();\n }\n }\n return ticks;\n};\nexports.getDateAxis = getDateAxis;","/**\n * @license\n * Copyright 2011 Robert Konigsberg (konigsberg@google.com)\n * MIT-licensed (http://opensource.org/licenses/MIT)\n */\n\n/**\n * @fileoverview The default interaction model for Dygraphs. This is kept out\n * of dygraph.js for better navigability.\n * @author Robert Konigsberg (konigsberg@google.com)\n */\n\n/*global Dygraph:false */\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj[\"default\"] = obj; return newObj; } }\n\nvar _dygraphUtils = require('./dygraph-utils');\n\nvar utils = _interopRequireWildcard(_dygraphUtils);\n\n/**\n * You can drag this many pixels past the edge of the chart and still have it\n * be considered a zoom. This makes it easier to zoom to the exact edge of the\n * chart, a fairly common operation.\n */\nvar DRAG_EDGE_MARGIN = 100;\n\n/**\n * A collection of functions to facilitate build custom interaction models.\n * @class\n */\nvar DygraphInteraction = {};\n\n/**\n * Checks whether the beginning & ending of an event were close enough that it\n * should be considered a click. If it should, dispatch appropriate events.\n * Returns true if the event was treated as a click.\n *\n * @param {Event} event\n * @param {Dygraph} g\n * @param {Object} context\n */\nDygraphInteraction.maybeTreatMouseOpAsClick = function (event, g, context) {\n context.dragEndX = utils.dragGetX_(event, context);\n context.dragEndY = utils.dragGetY_(event, context);\n var regionWidth = Math.abs(context.dragEndX - context.dragStartX);\n var regionHeight = Math.abs(context.dragEndY - context.dragStartY);\n\n if (regionWidth < 2 && regionHeight < 2 && g.lastx_ !== undefined && g.lastx_ != -1) {\n DygraphInteraction.treatMouseOpAsClick(g, event, context);\n }\n\n context.regionWidth = regionWidth;\n context.regionHeight = regionHeight;\n};\n\n/**\n * Called in response to an interaction model operation that\n * should start the default panning behavior.\n *\n * It's used in the default callback for \"mousedown\" operations.\n * Custom interaction model builders can use it to provide the default\n * panning behavior.\n *\n * @param {Event} event the event object which led to the startPan call.\n * @param {Dygraph} g The dygraph on which to act.\n * @param {Object} context The dragging context object (with\n * dragStartX/dragStartY/etc. properties). This function modifies the\n * context.\n */\nDygraphInteraction.startPan = function (event, g, context) {\n var i, axis;\n context.isPanning = true;\n var xRange = g.xAxisRange();\n\n if (g.getOptionForAxis(\"logscale\", \"x\")) {\n context.initialLeftmostDate = utils.log10(xRange[0]);\n context.dateRange = utils.log10(xRange[1]) - utils.log10(xRange[0]);\n } else {\n context.initialLeftmostDate = xRange[0];\n context.dateRange = xRange[1] - xRange[0];\n }\n context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1);\n\n if (g.getNumericOption(\"panEdgeFraction\")) {\n var maxXPixelsToDraw = g.width_ * g.getNumericOption(\"panEdgeFraction\");\n var xExtremes = g.xAxisExtremes(); // I REALLY WANT TO CALL THIS xTremes!\n\n var boundedLeftX = g.toDomXCoord(xExtremes[0]) - maxXPixelsToDraw;\n var boundedRightX = g.toDomXCoord(xExtremes[1]) + maxXPixelsToDraw;\n\n var boundedLeftDate = g.toDataXCoord(boundedLeftX);\n var boundedRightDate = g.toDataXCoord(boundedRightX);\n context.boundedDates = [boundedLeftDate, boundedRightDate];\n\n var boundedValues = [];\n var maxYPixelsToDraw = g.height_ * g.getNumericOption(\"panEdgeFraction\");\n\n for (i = 0; i < g.axes_.length; i++) {\n axis = g.axes_[i];\n var yExtremes = axis.extremeRange;\n\n var boundedTopY = g.toDomYCoord(yExtremes[0], i) + maxYPixelsToDraw;\n var boundedBottomY = g.toDomYCoord(yExtremes[1], i) - maxYPixelsToDraw;\n\n var boundedTopValue = g.toDataYCoord(boundedTopY, i);\n var boundedBottomValue = g.toDataYCoord(boundedBottomY, i);\n\n boundedValues[i] = [boundedTopValue, boundedBottomValue];\n }\n context.boundedValues = boundedValues;\n }\n\n // Record the range of each y-axis at the start of the drag.\n // If any axis has a valueRange, then we want a 2D pan.\n // We can't store data directly in g.axes_, because it does not belong to us\n // and could change out from under us during a pan (say if there's a data\n // update).\n context.is2DPan = false;\n context.axes = [];\n for (i = 0; i < g.axes_.length; i++) {\n axis = g.axes_[i];\n var axis_data = {};\n var yRange = g.yAxisRange(i);\n // TODO(konigsberg): These values should be in |context|.\n // In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale.\n var logscale = g.attributes_.getForAxis(\"logscale\", i);\n if (logscale) {\n axis_data.initialTopValue = utils.log10(yRange[1]);\n axis_data.dragValueRange = utils.log10(yRange[1]) - utils.log10(yRange[0]);\n } else {\n axis_data.initialTopValue = yRange[1];\n axis_data.dragValueRange = yRange[1] - yRange[0];\n }\n axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1);\n context.axes.push(axis_data);\n\n // While calculating axes, set 2dpan.\n if (axis.valueRange) context.is2DPan = true;\n }\n};\n\n/**\n * Called in response to an interaction model operation that\n * responds to an event that pans the view.\n *\n * It's used in the default callback for \"mousemove\" operations.\n * Custom interaction model builders can use it to provide the default\n * panning behavior.\n *\n * @param {Event} event the event object which led to the movePan call.\n * @param {Dygraph} g The dygraph on which to act.\n * @param {Object} context The dragging context object (with\n * dragStartX/dragStartY/etc. properties). This function modifies the\n * context.\n */\nDygraphInteraction.movePan = function (event, g, context) {\n context.dragEndX = utils.dragGetX_(event, context);\n context.dragEndY = utils.dragGetY_(event, context);\n\n var minDate = context.initialLeftmostDate - (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel;\n if (context.boundedDates) {\n minDate = Math.max(minDate, context.boundedDates[0]);\n }\n var maxDate = minDate + context.dateRange;\n if (context.boundedDates) {\n if (maxDate > context.boundedDates[1]) {\n // Adjust minDate, and recompute maxDate.\n minDate = minDate - (maxDate - context.boundedDates[1]);\n maxDate = minDate + context.dateRange;\n }\n }\n\n if (g.getOptionForAxis(\"logscale\", \"x\")) {\n g.dateWindow_ = [Math.pow(utils.LOG_SCALE, minDate), Math.pow(utils.LOG_SCALE, maxDate)];\n } else {\n g.dateWindow_ = [minDate, maxDate];\n }\n\n // y-axis scaling is automatic unless this is a full 2D pan.\n if (context.is2DPan) {\n\n var pixelsDragged = context.dragEndY - context.dragStartY;\n\n // Adjust each axis appropriately.\n for (var i = 0; i < g.axes_.length; i++) {\n var axis = g.axes_[i];\n var axis_data = context.axes[i];\n var unitsDragged = pixelsDragged * axis_data.unitsPerPixel;\n\n var boundedValue = context.boundedValues ? context.boundedValues[i] : null;\n\n // In log scale, maxValue and minValue are the logs of those values.\n var maxValue = axis_data.initialTopValue + unitsDragged;\n if (boundedValue) {\n maxValue = Math.min(maxValue, boundedValue[1]);\n }\n var minValue = maxValue - axis_data.dragValueRange;\n if (boundedValue) {\n if (minValue < boundedValue[0]) {\n // Adjust maxValue, and recompute minValue.\n maxValue = maxValue - (minValue - boundedValue[0]);\n minValue = maxValue - axis_data.dragValueRange;\n }\n }\n if (g.attributes_.getForAxis(\"logscale\", i)) {\n axis.valueRange = [Math.pow(utils.LOG_SCALE, minValue), Math.pow(utils.LOG_SCALE, maxValue)];\n } else {\n axis.valueRange = [minValue, maxValue];\n }\n }\n }\n\n g.drawGraph_(false);\n};\n\n/**\n * Called in response to an interaction model operation that\n * responds to an event that ends panning.\n *\n * It's used in the default callback for \"mouseup\" operations.\n * Custom interaction model builders can use it to provide the default\n * panning behavior.\n *\n * @param {Event} event the event object which led to the endPan call.\n * @param {Dygraph} g The dygraph on which to act.\n * @param {Object} context The dragging context object (with\n * dragStartX/dragStartY/etc. properties). This function modifies the\n * context.\n */\nDygraphInteraction.endPan = DygraphInteraction.maybeTreatMouseOpAsClick;\n\n/**\n * Called in response to an interaction model operation that\n * responds to an event that starts zooming.\n *\n * It's used in the default callback for \"mousedown\" operations.\n * Custom interaction model builders can use it to provide the default\n * zooming behavior.\n *\n * @param {Event} event the event object which led to the startZoom call.\n * @param {Dygraph} g The dygraph on which to act.\n * @param {Object} context The dragging context object (with\n * dragStartX/dragStartY/etc. properties). This function modifies the\n * context.\n */\nDygraphInteraction.startZoom = function (event, g, context) {\n context.isZooming = true;\n context.zoomMoved = false;\n};\n\n/**\n * Called in response to an interaction model operation that\n * responds to an event that defines zoom boundaries.\n *\n * It's used in the default callback for \"mousemove\" operations.\n * Custom interaction model builders can use it to provide the default\n * zooming behavior.\n *\n * @param {Event} event the event object which led to the moveZoom call.\n * @param {Dygraph} g The dygraph on which to act.\n * @param {Object} context The dragging context object (with\n * dragStartX/dragStartY/etc. properties). This function modifies the\n * context.\n */\nDygraphInteraction.moveZoom = function (event, g, context) {\n context.zoomMoved = true;\n context.dragEndX = utils.dragGetX_(event, context);\n context.dragEndY = utils.dragGetY_(event, context);\n\n var xDelta = Math.abs(context.dragStartX - context.dragEndX);\n var yDelta = Math.abs(context.dragStartY - context.dragEndY);\n\n // drag direction threshold for y axis is twice as large as x axis\n context.dragDirection = xDelta < yDelta / 2 ? utils.VERTICAL : utils.HORIZONTAL;\n\n g.drawZoomRect_(context.dragDirection, context.dragStartX, context.dragEndX, context.dragStartY, context.dragEndY, context.prevDragDirection, context.prevEndX, context.prevEndY);\n\n context.prevEndX = context.dragEndX;\n context.prevEndY = context.dragEndY;\n context.prevDragDirection = context.dragDirection;\n};\n\n/**\n * TODO(danvk): move this logic into dygraph.js\n * @param {Dygraph} g\n * @param {Event} event\n * @param {Object} context\n */\nDygraphInteraction.treatMouseOpAsClick = function (g, event, context) {\n var clickCallback = g.getFunctionOption('clickCallback');\n var pointClickCallback = g.getFunctionOption('pointClickCallback');\n\n var selectedPoint = null;\n\n // Find out if the click occurs on a point.\n var closestIdx = -1;\n var closestDistance = Number.MAX_VALUE;\n\n // check if the click was on a particular point.\n for (var i = 0; i < g.selPoints_.length; i++) {\n var p = g.selPoints_[i];\n var distance = Math.pow(p.canvasx - context.dragEndX, 2) + Math.pow(p.canvasy - context.dragEndY, 2);\n if (!isNaN(distance) && (closestIdx == -1 || distance < closestDistance)) {\n closestDistance = distance;\n closestIdx = i;\n }\n }\n\n // Allow any click within two pixels of the dot.\n var radius = g.getNumericOption('highlightCircleSize') + 2;\n if (closestDistance <= radius * radius) {\n selectedPoint = g.selPoints_[closestIdx];\n }\n\n if (selectedPoint) {\n var e = {\n cancelable: true,\n point: selectedPoint,\n canvasx: context.dragEndX,\n canvasy: context.dragEndY\n };\n var defaultPrevented = g.cascadeEvents_('pointClick', e);\n if (defaultPrevented) {\n // Note: this also prevents click / clickCallback from firing.\n return;\n }\n if (pointClickCallback) {\n pointClickCallback.call(g, event, selectedPoint);\n }\n }\n\n var e = {\n cancelable: true,\n xval: g.lastx_, // closest point by x value\n pts: g.selPoints_,\n canvasx: context.dragEndX,\n canvasy: context.dragEndY\n };\n if (!g.cascadeEvents_('click', e)) {\n if (clickCallback) {\n // TODO(danvk): pass along more info about the points, e.g. 'x'\n clickCallback.call(g, event, g.lastx_, g.selPoints_);\n }\n }\n};\n\n/**\n * Called in response to an interaction model operation that\n * responds to an event that performs a zoom based on previously defined\n * bounds..\n *\n * It's used in the default callback for \"mouseup\" operations.\n * Custom interaction model builders can use it to provide the default\n * zooming behavior.\n *\n * @param {Event} event the event object which led to the endZoom call.\n * @param {Dygraph} g The dygraph on which to end the zoom.\n * @param {Object} context The dragging context object (with\n * dragStartX/dragStartY/etc. properties). This function modifies the\n * context.\n */\nDygraphInteraction.endZoom = function (event, g, context) {\n g.clearZoomRect_();\n context.isZooming = false;\n DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);\n\n // The zoom rectangle is visibly clipped to the plot area, so its behavior\n // should be as well.\n // See http://code.google.com/p/dygraphs/issues/detail?id=280\n var plotArea = g.getArea();\n if (context.regionWidth >= 10 && context.dragDirection == utils.HORIZONTAL) {\n var left = Math.min(context.dragStartX, context.dragEndX),\n right = Math.max(context.dragStartX, context.dragEndX);\n left = Math.max(left, plotArea.x);\n right = Math.min(right, plotArea.x + plotArea.w);\n if (left < right) {\n g.doZoomX_(left, right);\n }\n context.cancelNextDblclick = true;\n } else if (context.regionHeight >= 10 && context.dragDirection == utils.VERTICAL) {\n var top = Math.min(context.dragStartY, context.dragEndY),\n bottom = Math.max(context.dragStartY, context.dragEndY);\n top = Math.max(top, plotArea.y);\n bottom = Math.min(bottom, plotArea.y + plotArea.h);\n if (top < bottom) {\n g.doZoomY_(top, bottom);\n }\n context.cancelNextDblclick = true;\n }\n context.dragStartX = null;\n context.dragStartY = null;\n};\n\n/**\n * @private\n */\nDygraphInteraction.startTouch = function (event, g, context) {\n event.preventDefault(); // touch browsers are all nice.\n if (event.touches.length > 1) {\n // If the user ever puts two fingers down, it's not a double tap.\n context.startTimeForDoubleTapMs = null;\n }\n\n var touches = [];\n for (var i = 0; i < event.touches.length; i++) {\n var t = event.touches[i];\n // we dispense with 'dragGetX_' because all touchBrowsers support pageX\n touches.push({\n pageX: t.pageX,\n pageY: t.pageY,\n dataX: g.toDataXCoord(t.pageX),\n dataY: g.toDataYCoord(t.pageY)\n // identifier: t.identifier\n });\n }\n context.initialTouches = touches;\n\n if (touches.length == 1) {\n // This is just a swipe.\n context.initialPinchCenter = touches[0];\n context.touchDirections = { x: true, y: true };\n } else if (touches.length >= 2) {\n // It's become a pinch!\n // In case there are 3+ touches, we ignore all but the \"first\" two.\n\n // only screen coordinates can be averaged (data coords could be log scale).\n context.initialPinchCenter = {\n pageX: 0.5 * (touches[0].pageX + touches[1].pageX),\n pageY: 0.5 * (touches[0].pageY + touches[1].pageY),\n\n // TODO(danvk): remove\n dataX: 0.5 * (touches[0].dataX + touches[1].dataX),\n dataY: 0.5 * (touches[0].dataY + touches[1].dataY)\n };\n\n // Make pinches in a 45-degree swath around either axis 1-dimensional zooms.\n var initialAngle = 180 / Math.PI * Math.atan2(context.initialPinchCenter.pageY - touches[0].pageY, touches[0].pageX - context.initialPinchCenter.pageX);\n\n // use symmetry to get it into the first quadrant.\n initialAngle = Math.abs(initialAngle);\n if (initialAngle > 90) initialAngle = 90 - initialAngle;\n\n context.touchDirections = {\n x: initialAngle < 90 - 45 / 2,\n y: initialAngle > 45 / 2\n };\n }\n\n // save the full x & y ranges.\n context.initialRange = {\n x: g.xAxisRange(),\n y: g.yAxisRange()\n };\n};\n\n/**\n * @private\n */\nDygraphInteraction.moveTouch = function (event, g, context) {\n // If the tap moves, then it's definitely not part of a double-tap.\n context.startTimeForDoubleTapMs = null;\n\n var i,\n touches = [];\n for (i = 0; i < event.touches.length; i++) {\n var t = event.touches[i];\n touches.push({\n pageX: t.pageX,\n pageY: t.pageY\n });\n }\n var initialTouches = context.initialTouches;\n\n var c_now;\n\n // old and new centers.\n var c_init = context.initialPinchCenter;\n if (touches.length == 1) {\n c_now = touches[0];\n } else {\n c_now = {\n pageX: 0.5 * (touches[0].pageX + touches[1].pageX),\n pageY: 0.5 * (touches[0].pageY + touches[1].pageY)\n };\n }\n\n // this is the \"swipe\" component\n // we toss it out for now, but could use it in the future.\n var swipe = {\n pageX: c_now.pageX - c_init.pageX,\n pageY: c_now.pageY - c_init.pageY\n };\n var dataWidth = context.initialRange.x[1] - context.initialRange.x[0];\n var dataHeight = context.initialRange.y[0] - context.initialRange.y[1];\n swipe.dataX = swipe.pageX / g.plotter_.area.w * dataWidth;\n swipe.dataY = swipe.pageY / g.plotter_.area.h * dataHeight;\n var xScale, yScale;\n\n // The residual bits are usually split into scale & rotate bits, but we split\n // them into x-scale and y-scale bits.\n if (touches.length == 1) {\n xScale = 1.0;\n yScale = 1.0;\n } else if (touches.length >= 2) {\n var initHalfWidth = initialTouches[1].pageX - c_init.pageX;\n xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth;\n\n var initHalfHeight = initialTouches[1].pageY - c_init.pageY;\n yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight;\n }\n\n // Clip scaling to [1/8, 8] to prevent too much blowup.\n xScale = Math.min(8, Math.max(0.125, xScale));\n yScale = Math.min(8, Math.max(0.125, yScale));\n\n var didZoom = false;\n if (context.touchDirections.x) {\n g.dateWindow_ = [c_init.dataX - swipe.dataX + (context.initialRange.x[0] - c_init.dataX) / xScale, c_init.dataX - swipe.dataX + (context.initialRange.x[1] - c_init.dataX) / xScale];\n didZoom = true;\n }\n\n if (context.touchDirections.y) {\n for (i = 0; i < 1 /*g.axes_.length*/; i++) {\n var axis = g.axes_[i];\n var logscale = g.attributes_.getForAxis(\"logscale\", i);\n if (logscale) {\n // TODO(danvk): implement\n } else {\n axis.valueRange = [c_init.dataY - swipe.dataY + (context.initialRange.y[0] - c_init.dataY) / yScale, c_init.dataY - swipe.dataY + (context.initialRange.y[1] - c_init.dataY) / yScale];\n didZoom = true;\n }\n }\n }\n\n g.drawGraph_(false);\n\n // We only call zoomCallback on zooms, not pans, to mirror desktop behavior.\n if (didZoom && touches.length > 1 && g.getFunctionOption('zoomCallback')) {\n var viewWindow = g.xAxisRange();\n g.getFunctionOption(\"zoomCallback\").call(g, viewWindow[0], viewWindow[1], g.yAxisRanges());\n }\n};\n\n/**\n * @private\n */\nDygraphInteraction.endTouch = function (event, g, context) {\n if (event.touches.length !== 0) {\n // this is effectively a \"reset\"\n DygraphInteraction.startTouch(event, g, context);\n } else if (event.changedTouches.length == 1) {\n // Could be part of a \"double tap\"\n // The heuristic here is that it's a double-tap if the two touchend events\n // occur within 500ms and within a 50x50 pixel box.\n var now = new Date().getTime();\n var t = event.changedTouches[0];\n if (context.startTimeForDoubleTapMs && now - context.startTimeForDoubleTapMs < 500 && context.doubleTapX && Math.abs(context.doubleTapX - t.screenX) < 50 && context.doubleTapY && Math.abs(context.doubleTapY - t.screenY) < 50) {\n g.resetZoom();\n } else {\n context.startTimeForDoubleTapMs = now;\n context.doubleTapX = t.screenX;\n context.doubleTapY = t.screenY;\n }\n }\n};\n\n// Determine the distance from x to [left, right].\nvar distanceFromInterval = function distanceFromInterval(x, left, right) {\n if (x < left) {\n return left - x;\n } else if (x > right) {\n return x - right;\n } else {\n return 0;\n }\n};\n\n/**\n * Returns the number of pixels by which the event happens from the nearest\n * edge of the chart. For events in the interior of the chart, this returns zero.\n */\nvar distanceFromChart = function distanceFromChart(event, g) {\n var chartPos = utils.findPos(g.canvas_);\n var box = {\n left: chartPos.x,\n right: chartPos.x + g.canvas_.offsetWidth,\n top: chartPos.y,\n bottom: chartPos.y + g.canvas_.offsetHeight\n };\n\n var pt = {\n x: utils.pageX(event),\n y: utils.pageY(event)\n };\n\n var dx = distanceFromInterval(pt.x, box.left, box.right),\n dy = distanceFromInterval(pt.y, box.top, box.bottom);\n return Math.max(dx, dy);\n};\n\n/**\n * Default interation model for dygraphs. You can refer to specific elements of\n * this when constructing your own interaction model, e.g.:\n * g.updateOptions( {\n * interactionModel: {\n * mousedown: DygraphInteraction.defaultInteractionModel.mousedown\n * }\n * } );\n */\nDygraphInteraction.defaultModel = {\n // Track the beginning of drag events\n mousedown: function mousedown(event, g, context) {\n // Right-click should not initiate a zoom.\n if (event.button && event.button == 2) return;\n\n context.initializeMouseDown(event, g, context);\n\n if (event.altKey || event.shiftKey) {\n DygraphInteraction.startPan(event, g, context);\n } else {\n DygraphInteraction.startZoom(event, g, context);\n }\n\n // Note: we register mousemove/mouseup on document to allow some leeway for\n // events to move outside of the chart. Interaction model events get\n // registered on the canvas, which is too small to allow this.\n var mousemove = function mousemove(event) {\n if (context.isZooming) {\n // When the mouse moves >200px from the chart edge, cancel the zoom.\n var d = distanceFromChart(event, g);\n if (d < DRAG_EDGE_MARGIN) {\n DygraphInteraction.moveZoom(event, g, context);\n } else {\n if (context.dragEndX !== null) {\n context.dragEndX = null;\n context.dragEndY = null;\n g.clearZoomRect_();\n }\n }\n } else if (context.isPanning) {\n DygraphInteraction.movePan(event, g, context);\n }\n };\n var mouseup = function mouseup(event) {\n if (context.isZooming) {\n if (context.dragEndX !== null) {\n DygraphInteraction.endZoom(event, g, context);\n } else {\n DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);\n }\n } else if (context.isPanning) {\n DygraphInteraction.endPan(event, g, context);\n }\n\n utils.removeEvent(document, 'mousemove', mousemove);\n utils.removeEvent(document, 'mouseup', mouseup);\n context.destroy();\n };\n\n g.addAndTrackEvent(document, 'mousemove', mousemove);\n g.addAndTrackEvent(document, 'mouseup', mouseup);\n },\n willDestroyContextMyself: true,\n\n touchstart: function touchstart(event, g, context) {\n DygraphInteraction.startTouch(event, g, context);\n },\n touchmove: function touchmove(event, g, context) {\n DygraphInteraction.moveTouch(event, g, context);\n },\n touchend: function touchend(event, g, context) {\n DygraphInteraction.endTouch(event, g, context);\n },\n\n // Disable zooming out if panning.\n dblclick: function dblclick(event, g, context) {\n if (context.cancelNextDblclick) {\n context.cancelNextDblclick = false;\n return;\n }\n\n // Give plugins a chance to grab this event.\n var e = {\n canvasx: context.dragEndX,\n canvasy: context.dragEndY,\n cancelable: true\n };\n if (g.cascadeEvents_('dblclick', e)) {\n return;\n }\n\n if (event.altKey || event.shiftKey) {\n return;\n }\n g.resetZoom();\n }\n};\n\n/*\nDygraph.DEFAULT_ATTRS.interactionModel = DygraphInteraction.defaultModel;\n\n// old ways of accessing these methods/properties\nDygraph.defaultInteractionModel = DygraphInteraction.defaultModel;\nDygraph.endZoom = DygraphInteraction.endZoom;\nDygraph.moveZoom = DygraphInteraction.moveZoom;\nDygraph.startZoom = DygraphInteraction.startZoom;\nDygraph.endPan = DygraphInteraction.endPan;\nDygraph.movePan = DygraphInteraction.movePan;\nDygraph.startPan = DygraphInteraction.startPan;\n*/\n\nDygraphInteraction.nonInteractiveModel_ = {\n mousedown: function mousedown(event, g, context) {\n context.initializeMouseDown(event, g, context);\n },\n mouseup: DygraphInteraction.maybeTreatMouseOpAsClick\n};\n\n// Default interaction model when using the range selector.\nDygraphInteraction.dragIsPanInteractionModel = {\n mousedown: function mousedown(event, g, context) {\n context.initializeMouseDown(event, g, context);\n DygraphInteraction.startPan(event, g, context);\n },\n mousemove: function mousemove(event, g, context) {\n if (context.isPanning) {\n DygraphInteraction.movePan(event, g, context);\n }\n },\n mouseup: function mouseup(event, g, context) {\n if (context.isPanning) {\n DygraphInteraction.endPan(event, g, context);\n }\n }\n};\n\nexports[\"default\"] = DygraphInteraction;\nmodule.exports = exports[\"default\"];","/**\n * @license\n * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)\n * MIT-licensed (http://opensource.org/licenses/MIT)\n */\n\n/**\n * @fileoverview This file contains the managment of data handlers\n * @author David Eberlein (david.eberlein@ch.sauter-bc.com)\n *\n * The idea is to define a common, generic data format that works for all data\n * structures supported by dygraphs. To make this possible, the DataHandler\n * interface is introduced. This makes it possible, that dygraph itself can work\n * with the same logic for every data type independent of the actual format and\n * the DataHandler takes care of the data format specific jobs.\n * DataHandlers are implemented for all data types supported by Dygraphs and\n * return Dygraphs compliant formats.\n * By default the correct DataHandler is chosen based on the options set.\n * Optionally the user may use his own DataHandler (similar to the plugin\n * system).\n *\n *\n * The unified data format returend by each handler is defined as so:\n * series[n][point] = [x,y,(extras)]\n *\n * This format contains the common basis that is needed to draw a simple line\n * series extended by optional extras for more complex graphing types. It\n * contains a primitive x value as first array entry, a primitive y value as\n * second array entry and an optional extras object for additional data needed.\n *\n * x must always be a number.\n * y must always be a number, NaN of type number or null.\n * extras is optional and must be interpreted by the DataHandler. It may be of\n * any type.\n *\n * In practice this might look something like this:\n * default: [x, yVal]\n * errorBar / customBar: [x, yVal, [yTopVariance, yBottomVariance] ]\n *\n */\n/*global Dygraph:false */\n/*global DygraphLayout:false */\n\n\"use strict\";\n\n/**\n *\n * The data handler is responsible for all data specific operations. All of the\n * series data it receives and returns is always in the unified data format.\n * Initially the unified data is created by the extractSeries method\n * @constructor\n */\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nvar DygraphDataHandler = function DygraphDataHandler() {};\n\nvar handler = DygraphDataHandler;\n\n/**\n * X-value array index constant for unified data samples.\n * @const\n * @type {number}\n */\nhandler.X = 0;\n\n/**\n * Y-value array index constant for unified data samples.\n * @const\n * @type {number}\n */\nhandler.Y = 1;\n\n/**\n * Extras-value array index constant for unified data samples.\n * @const\n * @type {number}\n */\nhandler.EXTRAS = 2;\n\n/**\n * Extracts one series from the raw data (a 2D array) into an array of the\n * unified data format.\n * This is where undesirable points (i.e. negative values on log scales and\n * missing values through which we wish to connect lines) are dropped.\n * TODO(danvk): the \"missing values\" bit above doesn't seem right.\n *\n * @param {!Array.} rawData The raw data passed into dygraphs where\n * rawData[i] = [x,ySeries1,...,ySeriesN].\n * @param {!number} seriesIndex Index of the series to extract. All other\n * series should be ignored.\n * @param {!DygraphOptions} options Dygraph options.\n * @return {Array.<[!number,?number,?]>} The series in the unified data format\n * where series[i] = [x,y,{extras}].\n */\nhandler.prototype.extractSeries = function (rawData, seriesIndex, options) {};\n\n/**\n * Converts a series to a Point array. The resulting point array must be\n * returned in increasing order of idx property.\n *\n * @param {!Array.<[!number,?number,?]>} series The series in the unified\n * data format where series[i] = [x,y,{extras}].\n * @param {!string} setName Name of the series.\n * @param {!number} boundaryIdStart Index offset of the first point, equal to the\n * number of skipped points left of the date window minimum (if any).\n * @return {!Array.} List of points for this series.\n */\nhandler.prototype.seriesToPoints = function (series, setName, boundaryIdStart) {\n // TODO(bhs): these loops are a hot-spot for high-point-count charts. In\n // fact,\n // on chrome+linux, they are 6 times more expensive than iterating through\n // the\n // points and drawing the lines. The brunt of the cost comes from allocating\n // the |point| structures.\n var points = [];\n for (var i = 0; i < series.length; ++i) {\n var item = series[i];\n var yraw = item[1];\n var yval = yraw === null ? null : handler.parseFloat(yraw);\n var point = {\n x: NaN,\n y: NaN,\n xval: handler.parseFloat(item[0]),\n yval: yval,\n name: setName, // TODO(danvk): is this really necessary?\n idx: i + boundaryIdStart\n };\n points.push(point);\n }\n this.onPointsCreated_(series, points);\n return points;\n};\n\n/**\n * Callback called for each series after the series points have been generated\n * which will later be used by the plotters to draw the graph.\n * Here data may be added to the seriesPoints which is needed by the plotters.\n * The indexes of series and points are in sync meaning the original data\n * sample for series[i] is points[i].\n *\n * @param {!Array.<[!number,?number,?]>} series The series in the unified\n * data format where series[i] = [x,y,{extras}].\n * @param {!Array.} points The corresponding points passed\n * to the plotter.\n * @protected\n */\nhandler.prototype.onPointsCreated_ = function (series, points) {};\n\n/**\n * Calculates the rolling average of a data set.\n *\n * @param {!Array.<[!number,?number,?]>} series The series in the unified\n * data format where series[i] = [x,y,{extras}].\n * @param {!number} rollPeriod The number of points over which to average the data\n * @param {!DygraphOptions} options The dygraph options.\n * @return {!Array.<[!number,?number,?]>} the rolled series.\n */\nhandler.prototype.rollingAverage = function (series, rollPeriod, options) {};\n\n/**\n * Computes the range of the data series (including confidence intervals).\n *\n * @param {!Array.<[!number,?number,?]>} series The series in the unified\n * data format where series[i] = [x, y, {extras}].\n * @param {!Array.} dateWindow The x-value range to display with\n * the format: [min, max].\n * @param {!DygraphOptions} options The dygraph options.\n * @return {Array.} The low and high extremes of the series in the\n * given window with the format: [low, high].\n */\nhandler.prototype.getExtremeYValues = function (series, dateWindow, options) {};\n\n/**\n * Callback called for each series after the layouting data has been\n * calculated before the series is drawn. Here normalized positioning data\n * should be calculated for the extras of each point.\n *\n * @param {!Array.} points The points passed to\n * the plotter.\n * @param {!Object} axis The axis on which the series will be plotted.\n * @param {!boolean} logscale Weather or not to use a logscale.\n */\nhandler.prototype.onLineEvaluated = function (points, axis, logscale) {};\n\n/**\n * Optimized replacement for parseFloat, which was way too slow when almost\n * all values were type number, with few edge cases, none of which were strings.\n * @param {?number} val\n * @return {number}\n * @protected\n */\nhandler.parseFloat = function (val) {\n // parseFloat(null) is NaN\n if (val === null) {\n return NaN;\n }\n\n // Assume it's a number or NaN. If it's something else, I'll be shocked.\n return val;\n};\n\nexports[\"default\"] = DygraphDataHandler;\nmodule.exports = exports[\"default\"];","import toDate from \"../toDate/index.js\";\nimport requiredArgs from \"../_lib/requiredArgs/index.js\";\n/**\n * @name isValid\n * @category Common Helpers\n * @summary Is the given date valid?\n *\n * @description\n * Returns false if argument is Invalid Date and true otherwise.\n * Argument is converted to Date using `toDate`. See [toDate]{@link https://date-fns.org/docs/toDate}\n * Invalid Date is a Date, whose time value is NaN.\n *\n * Time value of Date: http://es5.github.io/#x15.9.1.1\n *\n * ### v2.0.0 breaking changes:\n *\n * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).\n *\n * - Now `isValid` doesn't throw an exception\n * if the first argument is not an instance of Date.\n * Instead, argument is converted beforehand using `toDate`.\n *\n * Examples:\n *\n * | `isValid` argument | Before v2.0.0 | v2.0.0 onward |\n * |---------------------------|---------------|---------------|\n * | `new Date()` | `true` | `true` |\n * | `new Date('2016-01-01')` | `true` | `true` |\n * | `new Date('')` | `false` | `false` |\n * | `new Date(1488370835081)` | `true` | `true` |\n * | `new Date(NaN)` | `false` | `false` |\n * | `'2016-01-01'` | `TypeError` | `false` |\n * | `''` | `TypeError` | `false` |\n * | `1488370835081` | `TypeError` | `true` |\n * | `NaN` | `TypeError` | `false` |\n *\n * We introduce this change to make *date-fns* consistent with ECMAScript behavior\n * that try to coerce arguments to the expected type\n * (which is also the case with other *date-fns* functions).\n *\n * @param {*} date - the date to check\n * @returns {Boolean} the date is valid\n * @throws {TypeError} 1 argument required\n *\n * @example\n * // For the valid date:\n * var result = isValid(new Date(2014, 1, 31))\n * //=> true\n *\n * @example\n * // For the value, convertable into a date:\n * var result = isValid(1393804800000)\n * //=> true\n *\n * @example\n * // For the invalid date:\n * var result = isValid(new Date(''))\n * //=> false\n */\n\nexport default function isValid(dirtyDate) {\n requiredArgs(1, arguments);\n var date = toDate(dirtyDate);\n return !isNaN(date);\n}","import toInteger from \"../_lib/toInteger/index.js\";\nimport addMilliseconds from \"../addMilliseconds/index.js\";\nimport requiredArgs from \"../_lib/requiredArgs/index.js\";\nvar MILLISECONDS_IN_MINUTE = 60000;\n/**\n * @name addMinutes\n * @category Minute Helpers\n * @summary Add the specified number of minutes to the given date.\n *\n * @description\n * Add the specified number of minutes to the given date.\n *\n * ### v2.0.0 breaking changes:\n *\n * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).\n *\n * @param {Date|Number} date - the date to be changed\n * @param {Number} amount - the amount of minutes to be added. Positive decimals will be rounded using `Math.floor`, decimals less than zero will be rounded using `Math.ceil`.\n * @returns {Date} the new date with the minutes added\n * @throws {TypeError} 2 arguments required\n *\n * @example\n * // Add 30 minutes to 10 July 2014 12:00:00:\n * const result = addMinutes(new Date(2014, 6, 10, 12, 0), 30)\n * //=> Thu Jul 10 2014 12:30:00\n */\n\nexport default function addMinutes(dirtyDate, dirtyAmount) {\n requiredArgs(2, arguments);\n var amount = toInteger(dirtyAmount);\n return addMilliseconds(dirtyDate, amount * MILLISECONDS_IN_MINUTE);\n}","import toInteger from \"../_lib/toInteger/index.js\";\nimport addMilliseconds from \"../addMilliseconds/index.js\";\nimport requiredArgs from \"../_lib/requiredArgs/index.js\";\nvar MILLISECONDS_IN_HOUR = 3600000;\n/**\n * @name addHours\n * @category Hour Helpers\n * @summary Add the specified number of hours to the given date.\n *\n * @description\n * Add the specified number of hours to the given date.\n *\n * ### v2.0.0 breaking changes:\n *\n * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).\n *\n * @param {Date|Number} date - the date to be changed\n * @param {Number} amount - the amount of hours to be added. Positive decimals will be rounded using `Math.floor`, decimals less than zero will be rounded using `Math.ceil`.\n * @returns {Date} the new date with the hours added\n * @throws {TypeError} 2 arguments required\n *\n * @example\n * // Add 2 hours to 10 July 2014 23:00:00:\n * const result = addHours(new Date(2014, 6, 10, 23, 0), 2)\n * //=> Fri Jul 11 2014 01:00:00\n */\n\nexport default function addHours(dirtyDate, dirtyAmount) {\n requiredArgs(2, arguments);\n var amount = toInteger(dirtyAmount);\n return addMilliseconds(dirtyDate, amount * MILLISECONDS_IN_HOUR);\n}","import toInteger from \"../_lib/toInteger/index.js\";\nimport addDays from \"../addDays/index.js\";\nimport requiredArgs from \"../_lib/requiredArgs/index.js\";\n/**\n * @name addWeeks\n * @category Week Helpers\n * @summary Add the specified number of weeks to the given date.\n *\n * @description\n * Add the specified number of week to the given date.\n *\n * ### v2.0.0 breaking changes:\n *\n * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).\n *\n * @param {Date|Number} date - the date to be changed\n * @param {Number} amount - the amount of weeks to be added. Positive decimals will be rounded using `Math.floor`, decimals less than zero will be rounded using `Math.ceil`.\n * @returns {Date} the new date with the weeks added\n * @throws {TypeError} 2 arguments required\n *\n * @example\n * // Add 4 weeks to 1 September 2014:\n * const result = addWeeks(new Date(2014, 8, 1), 4)\n * //=> Mon Sep 29 2014 00:00:00\n */\n\nexport default function addWeeks(dirtyDate, dirtyAmount) {\n requiredArgs(2, arguments);\n var amount = toInteger(dirtyAmount);\n var days = amount * 7;\n return addDays(dirtyDate, days);\n}","import toInteger from \"../_lib/toInteger/index.js\";\nimport addMonths from \"../addMonths/index.js\";\nimport requiredArgs from \"../_lib/requiredArgs/index.js\";\n/**\n * @name addYears\n * @category Year Helpers\n * @summary Add the specified number of years to the given date.\n *\n * @description\n * Add the specified number of years to the given date.\n *\n * ### v2.0.0 breaking changes:\n *\n * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).\n *\n * @param {Date|Number} date - the date to be changed\n * @param {Number} amount - the amount of years to be added. Positive decimals will be rounded using `Math.floor`, decimals less than zero will be rounded using `Math.ceil`.\n * @returns {Date} the new date with the years added\n * @throws {TypeError} 2 arguments required\n *\n * @example\n * // Add 5 years to 1 September 2014:\n * const result = addYears(new Date(2014, 8, 1), 5)\n * //=> Sun Sep 01 2019 00:00:00\n */\n\nexport default function addYears(dirtyDate, dirtyAmount) {\n requiredArgs(2, arguments);\n var amount = toInteger(dirtyAmount);\n return addMonths(dirtyDate, amount * 12);\n}","import toInteger from \"../_lib/toInteger/index.js\";\nimport toDate from \"../toDate/index.js\";\nimport getDaysInMonth from \"../getDaysInMonth/index.js\";\nimport requiredArgs from \"../_lib/requiredArgs/index.js\";\n/**\n * @name setMonth\n * @category Month Helpers\n * @summary Set the month to the given date.\n *\n * @description\n * Set the month to the given date.\n *\n * ### v2.0.0 breaking changes:\n *\n * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).\n *\n * @param {Date|Number} date - the date to be changed\n * @param {Number} month - the month of the new date\n * @returns {Date} the new date with the month set\n * @throws {TypeError} 2 arguments required\n *\n * @example\n * // Set February to 1 September 2014:\n * const result = setMonth(new Date(2014, 8, 1), 1)\n * //=> Sat Feb 01 2014 00:00:00\n */\n\nexport default function setMonth(dirtyDate, dirtyMonth) {\n requiredArgs(2, arguments);\n var date = toDate(dirtyDate);\n var month = toInteger(dirtyMonth);\n var year = date.getFullYear();\n var day = date.getDate();\n var dateWithDesiredMonth = new Date(0);\n dateWithDesiredMonth.setFullYear(year, month, 15);\n dateWithDesiredMonth.setHours(0, 0, 0, 0);\n var daysInMonth = getDaysInMonth(dateWithDesiredMonth); // Set the last day of the new month\n // if the original date was the last day of the longer month\n\n date.setMonth(month, Math.min(day, daysInMonth));\n return date;\n}","import toDate from \"../toDate/index.js\";\nimport requiredArgs from \"../_lib/requiredArgs/index.js\";\n/**\n * @name getDaysInMonth\n * @category Month Helpers\n * @summary Get the number of days in a month of the given date.\n *\n * @description\n * Get the number of days in a month of the given date.\n *\n * ### v2.0.0 breaking changes:\n *\n * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).\n *\n * @param {Date|Number} date - the given date\n * @returns {Number} the number of days in a month\n * @throws {TypeError} 1 argument required\n *\n * @example\n * // How many days are in February 2000?\n * const result = getDaysInMonth(new Date(2000, 1))\n * //=> 29\n */\n\nexport default function getDaysInMonth(dirtyDate) {\n requiredArgs(1, arguments);\n var date = toDate(dirtyDate);\n var year = date.getFullYear();\n var monthIndex = date.getMonth();\n var lastDayOfMonth = new Date(0);\n lastDayOfMonth.setFullYear(year, monthIndex + 1, 0);\n lastDayOfMonth.setHours(0, 0, 0, 0);\n return lastDayOfMonth.getDate();\n}","/* global window */\nimport ponyfill from './ponyfill.js';\n\nvar root;\n\nif (typeof self !== 'undefined') {\n root = self;\n} else if (typeof window !== 'undefined') {\n root = window;\n} else if (typeof global !== 'undefined') {\n root = global;\n} else if (typeof module !== 'undefined') {\n root = module;\n} else {\n root = Function('return this')();\n}\n\nvar result = ponyfill(root);\nexport default result;\n","import _curry2 from './internal/_curry2.js';\n\n/**\n * Retrieve the value at a given path.\n *\n * @func\n * @memberOf R\n * @since v0.2.0\n * @category Object\n * @typedefn Idx = String | Int\n * @sig [Idx] -> {a} -> a | Undefined\n * @param {Array} path The path to use.\n * @param {Object} obj The object to retrieve the nested property from.\n * @return {*} The data at `path`.\n * @see R.prop\n * @example\n *\n * R.path(['a', 'b'], {a: {b: 2}}); //=> 2\n * R.path(['a', 'b'], {c: {b: 2}}); //=> undefined\n */\nvar path = /*#__PURE__*/_curry2(function path(paths, obj) {\n var val = obj;\n var idx = 0;\n while (idx < paths.length) {\n if (val == null) {\n return;\n }\n val = val[paths[idx]];\n idx += 1;\n }\n return val;\n});\nexport default path;","'use strict';\n\nvar stringify = require('./stringify');\nvar parse = require('./parse');\nvar formats = require('./formats');\n\nmodule.exports = {\n formats: formats,\n parse: parse,\n stringify: stringify\n};\n","'use strict';\n\nvar reactIs = require('react-is');\n\n/**\n * Copyright 2015, Yahoo! Inc.\n * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.\n */\nvar REACT_STATICS = {\n childContextTypes: true,\n contextType: true,\n contextTypes: true,\n defaultProps: true,\n displayName: true,\n getDefaultProps: true,\n getDerivedStateFromError: true,\n getDerivedStateFromProps: true,\n mixins: true,\n propTypes: true,\n type: true\n};\nvar KNOWN_STATICS = {\n name: true,\n length: true,\n prototype: true,\n caller: true,\n callee: true,\n arguments: true,\n arity: true\n};\nvar FORWARD_REF_STATICS = {\n '$$typeof': true,\n render: true,\n defaultProps: true,\n displayName: true,\n propTypes: true\n};\nvar MEMO_STATICS = {\n '$$typeof': true,\n compare: true,\n defaultProps: true,\n displayName: true,\n propTypes: true,\n type: true\n};\nvar TYPE_STATICS = {};\nTYPE_STATICS[reactIs.ForwardRef] = FORWARD_REF_STATICS;\nTYPE_STATICS[reactIs.Memo] = MEMO_STATICS;\n\nfunction getStatics(component) {\n // React v16.11 and below\n if (reactIs.isMemo(component)) {\n return MEMO_STATICS;\n } // React v16.12 and above\n\n\n return TYPE_STATICS[component['$$typeof']] || REACT_STATICS;\n}\n\nvar defineProperty = Object.defineProperty;\nvar getOwnPropertyNames = Object.getOwnPropertyNames;\nvar getOwnPropertySymbols = Object.getOwnPropertySymbols;\nvar getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\nvar getPrototypeOf = Object.getPrototypeOf;\nvar objectPrototype = Object.prototype;\nfunction hoistNonReactStatics(targetComponent, sourceComponent, blacklist) {\n if (typeof sourceComponent !== 'string') {\n // don't hoist over string (html) components\n if (objectPrototype) {\n var inheritedComponent = getPrototypeOf(sourceComponent);\n\n if (inheritedComponent && inheritedComponent !== objectPrototype) {\n hoistNonReactStatics(targetComponent, inheritedComponent, blacklist);\n }\n }\n\n var keys = getOwnPropertyNames(sourceComponent);\n\n if (getOwnPropertySymbols) {\n keys = keys.concat(getOwnPropertySymbols(sourceComponent));\n }\n\n var targetStatics = getStatics(targetComponent);\n var sourceStatics = getStatics(sourceComponent);\n\n for (var i = 0; i < keys.length; ++i) {\n var key = keys[i];\n\n if (!KNOWN_STATICS[key] && !(blacklist && blacklist[key]) && !(sourceStatics && sourceStatics[key]) && !(targetStatics && targetStatics[key])) {\n var descriptor = getOwnPropertyDescriptor(sourceComponent, key);\n\n try {\n // Avoid failures from read-only properties\n defineProperty(targetComponent, key, descriptor);\n } catch (e) {}\n }\n }\n }\n\n return targetComponent;\n}\n\nmodule.exports = hoistNonReactStatics;\n","'use strict';\n\nvar colorString = require('color-string');\nvar convert = require('color-convert');\n\nvar _slice = [].slice;\n\nvar skippedModels = [\n\t// to be honest, I don't really feel like keyword belongs in color convert, but eh.\n\t'keyword',\n\n\t// gray conflicts with some method names, and has its own method defined.\n\t'gray',\n\n\t// shouldn't really be in color-convert either...\n\t'hex'\n];\n\nvar hashedModelKeys = {};\nObject.keys(convert).forEach(function (model) {\n\thashedModelKeys[_slice.call(convert[model].labels).sort().join('')] = model;\n});\n\nvar limiters = {};\n\nfunction Color(obj, model) {\n\tif (!(this instanceof Color)) {\n\t\treturn new Color(obj, model);\n\t}\n\n\tif (model && model in skippedModels) {\n\t\tmodel = null;\n\t}\n\n\tif (model && !(model in convert)) {\n\t\tthrow new Error('Unknown model: ' + model);\n\t}\n\n\tvar i;\n\tvar channels;\n\n\tif (obj == null) { // eslint-disable-line no-eq-null,eqeqeq\n\t\tthis.model = 'rgb';\n\t\tthis.color = [0, 0, 0];\n\t\tthis.valpha = 1;\n\t} else if (obj instanceof Color) {\n\t\tthis.model = obj.model;\n\t\tthis.color = obj.color.slice();\n\t\tthis.valpha = obj.valpha;\n\t} else if (typeof obj === 'string') {\n\t\tvar result = colorString.get(obj);\n\t\tif (result === null) {\n\t\t\tthrow new Error('Unable to parse color from string: ' + obj);\n\t\t}\n\n\t\tthis.model = result.model;\n\t\tchannels = convert[this.model].channels;\n\t\tthis.color = result.value.slice(0, channels);\n\t\tthis.valpha = typeof result.value[channels] === 'number' ? result.value[channels] : 1;\n\t} else if (obj.length) {\n\t\tthis.model = model || 'rgb';\n\t\tchannels = convert[this.model].channels;\n\t\tvar newArr = _slice.call(obj, 0, channels);\n\t\tthis.color = zeroArray(newArr, channels);\n\t\tthis.valpha = typeof obj[channels] === 'number' ? obj[channels] : 1;\n\t} else if (typeof obj === 'number') {\n\t\t// this is always RGB - can be converted later on.\n\t\tobj &= 0xFFFFFF;\n\t\tthis.model = 'rgb';\n\t\tthis.color = [\n\t\t\t(obj >> 16) & 0xFF,\n\t\t\t(obj >> 8) & 0xFF,\n\t\t\tobj & 0xFF\n\t\t];\n\t\tthis.valpha = 1;\n\t} else {\n\t\tthis.valpha = 1;\n\n\t\tvar keys = Object.keys(obj);\n\t\tif ('alpha' in obj) {\n\t\t\tkeys.splice(keys.indexOf('alpha'), 1);\n\t\t\tthis.valpha = typeof obj.alpha === 'number' ? obj.alpha : 0;\n\t\t}\n\n\t\tvar hashedKeys = keys.sort().join('');\n\t\tif (!(hashedKeys in hashedModelKeys)) {\n\t\t\tthrow new Error('Unable to parse color from object: ' + JSON.stringify(obj));\n\t\t}\n\n\t\tthis.model = hashedModelKeys[hashedKeys];\n\n\t\tvar labels = convert[this.model].labels;\n\t\tvar color = [];\n\t\tfor (i = 0; i < labels.length; i++) {\n\t\t\tcolor.push(obj[labels[i]]);\n\t\t}\n\n\t\tthis.color = zeroArray(color);\n\t}\n\n\t// perform limitations (clamping, etc.)\n\tif (limiters[this.model]) {\n\t\tchannels = convert[this.model].channels;\n\t\tfor (i = 0; i < channels; i++) {\n\t\t\tvar limit = limiters[this.model][i];\n\t\t\tif (limit) {\n\t\t\t\tthis.color[i] = limit(this.color[i]);\n\t\t\t}\n\t\t}\n\t}\n\n\tthis.valpha = Math.max(0, Math.min(1, this.valpha));\n\n\tif (Object.freeze) {\n\t\tObject.freeze(this);\n\t}\n}\n\nColor.prototype = {\n\ttoString: function () {\n\t\treturn this.string();\n\t},\n\n\ttoJSON: function () {\n\t\treturn this[this.model]();\n\t},\n\n\tstring: function (places) {\n\t\tvar self = this.model in colorString.to ? this : this.rgb();\n\t\tself = self.round(typeof places === 'number' ? places : 1);\n\t\tvar args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);\n\t\treturn colorString.to[self.model](args);\n\t},\n\n\tpercentString: function (places) {\n\t\tvar self = this.rgb().round(typeof places === 'number' ? places : 1);\n\t\tvar args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);\n\t\treturn colorString.to.rgb.percent(args);\n\t},\n\n\tarray: function () {\n\t\treturn this.valpha === 1 ? this.color.slice() : this.color.concat(this.valpha);\n\t},\n\n\tobject: function () {\n\t\tvar result = {};\n\t\tvar channels = convert[this.model].channels;\n\t\tvar labels = convert[this.model].labels;\n\n\t\tfor (var i = 0; i < channels; i++) {\n\t\t\tresult[labels[i]] = this.color[i];\n\t\t}\n\n\t\tif (this.valpha !== 1) {\n\t\t\tresult.alpha = this.valpha;\n\t\t}\n\n\t\treturn result;\n\t},\n\n\tunitArray: function () {\n\t\tvar rgb = this.rgb().color;\n\t\trgb[0] /= 255;\n\t\trgb[1] /= 255;\n\t\trgb[2] /= 255;\n\n\t\tif (this.valpha !== 1) {\n\t\t\trgb.push(this.valpha);\n\t\t}\n\n\t\treturn rgb;\n\t},\n\n\tunitObject: function () {\n\t\tvar rgb = this.rgb().object();\n\t\trgb.r /= 255;\n\t\trgb.g /= 255;\n\t\trgb.b /= 255;\n\n\t\tif (this.valpha !== 1) {\n\t\t\trgb.alpha = this.valpha;\n\t\t}\n\n\t\treturn rgb;\n\t},\n\n\tround: function (places) {\n\t\tplaces = Math.max(places || 0, 0);\n\t\treturn new Color(this.color.map(roundToPlace(places)).concat(this.valpha), this.model);\n\t},\n\n\talpha: function (val) {\n\t\tif (arguments.length) {\n\t\t\treturn new Color(this.color.concat(Math.max(0, Math.min(1, val))), this.model);\n\t\t}\n\n\t\treturn this.valpha;\n\t},\n\n\t// rgb\n\tred: getset('rgb', 0, maxfn(255)),\n\tgreen: getset('rgb', 1, maxfn(255)),\n\tblue: getset('rgb', 2, maxfn(255)),\n\n\thue: getset(['hsl', 'hsv', 'hsl', 'hwb', 'hcg'], 0, function (val) { return ((val % 360) + 360) % 360; }), // eslint-disable-line brace-style\n\n\tsaturationl: getset('hsl', 1, maxfn(100)),\n\tlightness: getset('hsl', 2, maxfn(100)),\n\n\tsaturationv: getset('hsv', 1, maxfn(100)),\n\tvalue: getset('hsv', 2, maxfn(100)),\n\n\tchroma: getset('hcg', 1, maxfn(100)),\n\tgray: getset('hcg', 2, maxfn(100)),\n\n\twhite: getset('hwb', 1, maxfn(100)),\n\twblack: getset('hwb', 2, maxfn(100)),\n\n\tcyan: getset('cmyk', 0, maxfn(100)),\n\tmagenta: getset('cmyk', 1, maxfn(100)),\n\tyellow: getset('cmyk', 2, maxfn(100)),\n\tblack: getset('cmyk', 3, maxfn(100)),\n\n\tx: getset('xyz', 0, maxfn(100)),\n\ty: getset('xyz', 1, maxfn(100)),\n\tz: getset('xyz', 2, maxfn(100)),\n\n\tl: getset('lab', 0, maxfn(100)),\n\ta: getset('lab', 1),\n\tb: getset('lab', 2),\n\n\tkeyword: function (val) {\n\t\tif (arguments.length) {\n\t\t\treturn new Color(val);\n\t\t}\n\n\t\treturn convert[this.model].keyword(this.color);\n\t},\n\n\thex: function (val) {\n\t\tif (arguments.length) {\n\t\t\treturn new Color(val);\n\t\t}\n\n\t\treturn colorString.to.hex(this.rgb().round().color);\n\t},\n\n\trgbNumber: function () {\n\t\tvar rgb = this.rgb().color;\n\t\treturn ((rgb[0] & 0xFF) << 16) | ((rgb[1] & 0xFF) << 8) | (rgb[2] & 0xFF);\n\t},\n\n\tluminosity: function () {\n\t\t// http://www.w3.org/TR/WCAG20/#relativeluminancedef\n\t\tvar rgb = this.rgb().color;\n\n\t\tvar lum = [];\n\t\tfor (var i = 0; i < rgb.length; i++) {\n\t\t\tvar chan = rgb[i] / 255;\n\t\t\tlum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);\n\t\t}\n\n\t\treturn 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];\n\t},\n\n\tcontrast: function (color2) {\n\t\t// http://www.w3.org/TR/WCAG20/#contrast-ratiodef\n\t\tvar lum1 = this.luminosity();\n\t\tvar lum2 = color2.luminosity();\n\n\t\tif (lum1 > lum2) {\n\t\t\treturn (lum1 + 0.05) / (lum2 + 0.05);\n\t\t}\n\n\t\treturn (lum2 + 0.05) / (lum1 + 0.05);\n\t},\n\n\tlevel: function (color2) {\n\t\tvar contrastRatio = this.contrast(color2);\n\t\tif (contrastRatio >= 7.1) {\n\t\t\treturn 'AAA';\n\t\t}\n\n\t\treturn (contrastRatio >= 4.5) ? 'AA' : '';\n\t},\n\n\tisDark: function () {\n\t\t// YIQ equation from http://24ways.org/2010/calculating-color-contrast\n\t\tvar rgb = this.rgb().color;\n\t\tvar yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;\n\t\treturn yiq < 128;\n\t},\n\n\tisLight: function () {\n\t\treturn !this.isDark();\n\t},\n\n\tnegate: function () {\n\t\tvar rgb = this.rgb();\n\t\tfor (var i = 0; i < 3; i++) {\n\t\t\trgb.color[i] = 255 - rgb.color[i];\n\t\t}\n\t\treturn rgb;\n\t},\n\n\tlighten: function (ratio) {\n\t\tvar hsl = this.hsl();\n\t\thsl.color[2] += hsl.color[2] * ratio;\n\t\treturn hsl;\n\t},\n\n\tdarken: function (ratio) {\n\t\tvar hsl = this.hsl();\n\t\thsl.color[2] -= hsl.color[2] * ratio;\n\t\treturn hsl;\n\t},\n\n\tsaturate: function (ratio) {\n\t\tvar hsl = this.hsl();\n\t\thsl.color[1] += hsl.color[1] * ratio;\n\t\treturn hsl;\n\t},\n\n\tdesaturate: function (ratio) {\n\t\tvar hsl = this.hsl();\n\t\thsl.color[1] -= hsl.color[1] * ratio;\n\t\treturn hsl;\n\t},\n\n\twhiten: function (ratio) {\n\t\tvar hwb = this.hwb();\n\t\thwb.color[1] += hwb.color[1] * ratio;\n\t\treturn hwb;\n\t},\n\n\tblacken: function (ratio) {\n\t\tvar hwb = this.hwb();\n\t\thwb.color[2] += hwb.color[2] * ratio;\n\t\treturn hwb;\n\t},\n\n\tgrayscale: function () {\n\t\t// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale\n\t\tvar rgb = this.rgb().color;\n\t\tvar val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;\n\t\treturn Color.rgb(val, val, val);\n\t},\n\n\tfade: function (ratio) {\n\t\treturn this.alpha(this.valpha - (this.valpha * ratio));\n\t},\n\n\topaquer: function (ratio) {\n\t\treturn this.alpha(this.valpha + (this.valpha * ratio));\n\t},\n\n\trotate: function (degrees) {\n\t\tvar hsl = this.hsl();\n\t\tvar hue = hsl.color[0];\n\t\thue = (hue + degrees) % 360;\n\t\thue = hue < 0 ? 360 + hue : hue;\n\t\thsl.color[0] = hue;\n\t\treturn hsl;\n\t},\n\n\tmix: function (mixinColor, weight) {\n\t\t// ported from sass implementation in C\n\t\t// https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209\n\t\tif (!mixinColor || !mixinColor.rgb) {\n\t\t\tthrow new Error('Argument to \"mix\" was not a Color instance, but rather an instance of ' + typeof mixinColor);\n\t\t}\n\t\tvar color1 = mixinColor.rgb();\n\t\tvar color2 = this.rgb();\n\t\tvar p = weight === undefined ? 0.5 : weight;\n\n\t\tvar w = 2 * p - 1;\n\t\tvar a = color1.alpha() - color2.alpha();\n\n\t\tvar w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;\n\t\tvar w2 = 1 - w1;\n\n\t\treturn Color.rgb(\n\t\t\t\tw1 * color1.red() + w2 * color2.red(),\n\t\t\t\tw1 * color1.green() + w2 * color2.green(),\n\t\t\t\tw1 * color1.blue() + w2 * color2.blue(),\n\t\t\t\tcolor1.alpha() * p + color2.alpha() * (1 - p));\n\t}\n};\n\n// model conversion methods and static constructors\nObject.keys(convert).forEach(function (model) {\n\tif (skippedModels.indexOf(model) !== -1) {\n\t\treturn;\n\t}\n\n\tvar channels = convert[model].channels;\n\n\t// conversion methods\n\tColor.prototype[model] = function () {\n\t\tif (this.model === model) {\n\t\t\treturn new Color(this);\n\t\t}\n\n\t\tif (arguments.length) {\n\t\t\treturn new Color(arguments, model);\n\t\t}\n\n\t\tvar newAlpha = typeof arguments[channels] === 'number' ? channels : this.valpha;\n\t\treturn new Color(assertArray(convert[this.model][model].raw(this.color)).concat(newAlpha), model);\n\t};\n\n\t// 'static' construction methods\n\tColor[model] = function (color) {\n\t\tif (typeof color === 'number') {\n\t\t\tcolor = zeroArray(_slice.call(arguments), channels);\n\t\t}\n\t\treturn new Color(color, model);\n\t};\n});\n\nfunction roundTo(num, places) {\n\treturn Number(num.toFixed(places));\n}\n\nfunction roundToPlace(places) {\n\treturn function (num) {\n\t\treturn roundTo(num, places);\n\t};\n}\n\nfunction getset(model, channel, modifier) {\n\tmodel = Array.isArray(model) ? model : [model];\n\n\tmodel.forEach(function (m) {\n\t\t(limiters[m] || (limiters[m] = []))[channel] = modifier;\n\t});\n\n\tmodel = model[0];\n\n\treturn function (val) {\n\t\tvar result;\n\n\t\tif (arguments.length) {\n\t\t\tif (modifier) {\n\t\t\t\tval = modifier(val);\n\t\t\t}\n\n\t\t\tresult = this[model]();\n\t\t\tresult.color[channel] = val;\n\t\t\treturn result;\n\t\t}\n\n\t\tresult = this[model]().color[channel];\n\t\tif (modifier) {\n\t\t\tresult = modifier(result);\n\t\t}\n\n\t\treturn result;\n\t};\n}\n\nfunction maxfn(max) {\n\treturn function (v) {\n\t\treturn Math.max(0, Math.min(max, v));\n\t};\n}\n\nfunction assertArray(val) {\n\treturn Array.isArray(val) ? val : [val];\n}\n\nfunction zeroArray(arr, length) {\n\tfor (var i = 0; i < length; i++) {\n\t\tif (typeof arr[i] !== 'number') {\n\t\t\tarr[i] = 0;\n\t\t}\n\t}\n\n\treturn arr;\n}\n\nmodule.exports = Color;\n","function memoize(fn) {\n var cache = Object.create(null);\n return function (arg) {\n if (cache[arg] === undefined) cache[arg] = fn(arg);\n return cache[arg];\n };\n}\n\nexport default memoize;\n","import memoize from '@emotion/memoize';\n\nvar reactPropsRegex = /^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|abbr|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|download|draggable|encType|enterKeyHint|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|translate|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|incremental|fallback|inert|itemProp|itemScope|itemType|itemID|itemRef|on|option|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/; // https://esbench.com/bench/5bfee68a4cd7e6009ef61d23\n\nvar isPropValid = /* #__PURE__ */memoize(function (prop) {\n return reactPropsRegex.test(prop) || prop.charCodeAt(0) === 111\n /* o */\n && prop.charCodeAt(1) === 110\n /* n */\n && prop.charCodeAt(2) < 91;\n}\n/* Z+1 */\n);\n\nexport default isPropValid;\n","import _objectAssign from './internal/_objectAssign.js';\nimport _curry1 from './internal/_curry1.js';\n\n/**\n * Merges a list of objects together into one object.\n *\n * @func\n * @memberOf R\n * @since v0.10.0\n * @category List\n * @sig [{k: v}] -> {k: v}\n * @param {Array} list An array of objects\n * @return {Object} A merged object.\n * @see R.reduce\n * @example\n *\n * R.mergeAll([{foo:1},{bar:2},{baz:3}]); //=> {foo:1,bar:2,baz:3}\n * R.mergeAll([{foo:1},{foo:2},{bar:2}]); //=> {foo:2,bar:2}\n * @symb R.mergeAll([{ x: 1 }, { y: 2 }, { z: 3 }]) = { x: 1, y: 2, z: 3 }\n */\nvar mergeAll = /*#__PURE__*/_curry1(function mergeAll(list) {\n return _objectAssign.apply(null, [{}].concat(list));\n});\nexport default mergeAll;","import nth from './nth.js';\n\n/**\n * Returns the last element of the given list or string.\n *\n * @func\n * @memberOf R\n * @since v0.1.4\n * @category List\n * @sig [a] -> a | Undefined\n * @sig String -> String\n * @param {*} list\n * @return {*}\n * @see R.init, R.head, R.tail\n * @example\n *\n * R.last(['fi', 'fo', 'fum']); //=> 'fum'\n * R.last([]); //=> undefined\n *\n * R.last('abc'); //=> 'c'\n * R.last(''); //=> ''\n */\nvar last = /*#__PURE__*/nth(-1);\nexport default last;","import _curry2 from './internal/_curry2.js';\nimport _isString from './internal/_isString.js';\n\n/**\n * Returns the nth element of the given list or string. If n is negative the\n * element at index length + n is returned.\n *\n * @func\n * @memberOf R\n * @since v0.1.0\n * @category List\n * @sig Number -> [a] -> a | Undefined\n * @sig Number -> String -> String\n * @param {Number} offset\n * @param {*} list\n * @return {*}\n * @example\n *\n * const list = ['foo', 'bar', 'baz', 'quux'];\n * R.nth(1, list); //=> 'bar'\n * R.nth(-1, list); //=> 'quux'\n * R.nth(-99, list); //=> undefined\n *\n * R.nth(2, 'abc'); //=> 'c'\n * R.nth(3, 'abc'); //=> ''\n * @symb R.nth(-1, [a, b, c]) = c\n * @symb R.nth(0, [a, b, c]) = a\n * @symb R.nth(1, [a, b, c]) = b\n */\nvar nth = /*#__PURE__*/_curry2(function nth(offset, list) {\n var idx = offset < 0 ? list.length + offset : offset;\n return _isString(list) ? list.charAt(idx) : list[idx];\n});\nexport default nth;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.add = add;\nexports.remove = remove;\nexports.has = has;\nexports.check = check;\nexports.all = all;\nexports.clear = clear;\nexports.enableChecking = enableChecking;\nexports.disableChecking = disableChecking;\nvar types = {};\nvar config = {\n checkExisting: true\n};\n\nfunction add(name) {\n types[name] = true;\n}\n\nfunction remove(name) {\n types[name] = false;\n}\n\nfunction has(name) {\n return !!types[name];\n}\n\nfunction check(name) {\n if (config.checkExisting && has(name)) {\n throw new TypeError(\"Duplicate action type: \".concat(name));\n }\n}\n\nfunction all() {\n return Object.keys(types).filter(has);\n}\n\nfunction clear() {\n all().forEach(remove);\n}\n\nfunction enableChecking() {\n config.checkExisting = true;\n}\n\nfunction disableChecking() {\n config.checkExisting = false;\n}","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = createAction;\n\nvar _types = require(\"./types\");\n\nvar id = 0;\n\nvar identity = function identity(arg) {\n return arg;\n};\n\nvar normalize = function normalize(dispatchOrStore) {\n if (dispatchOrStore && typeof dispatchOrStore.dispatch === 'function') {\n return dispatchOrStore.dispatch;\n } else {\n return dispatchOrStore;\n }\n};\n\nvar normalizeAll = function normalizeAll(dispatchOrStores) {\n if (Array.isArray(dispatchOrStores)) {\n return dispatchOrStores.map(normalize);\n } else {\n return normalize(dispatchOrStores);\n }\n};\n\nfunction createAction(description, payloadReducer, metaReducer) {\n if (typeof description === 'function') {\n metaReducer = payloadReducer;\n payloadReducer = description;\n description = undefined;\n }\n\n if (typeof payloadReducer !== 'function') {\n payloadReducer = identity;\n }\n\n if (typeof metaReducer !== 'function') {\n metaReducer = undefined;\n }\n\n var isSerializable = typeof description === 'string' && /^[0-9A-Z_]+$/.test(description);\n\n if (isSerializable) {\n (0, _types.check)(description);\n (0, _types.add)(description);\n } else {\n ++id;\n }\n\n var type = isSerializable ? description : \"[\".concat(id, \"]\").concat(description ? ' ' + description : '');\n var dispatchFunctions = undefined;\n\n function makeAction() {\n var payload = payloadReducer.apply(void 0, arguments);\n\n if (metaReducer) {\n return {\n type: type,\n payload: payload,\n error: payload instanceof Error,\n meta: metaReducer.apply(void 0, arguments)\n };\n }\n\n return {\n type: type,\n payload: payload,\n error: payload instanceof Error\n };\n }\n\n var makeAndDispatch = function makeAndDispatch(dispatchs, isError) {\n return function () {\n var payloadedAction = makeAction.apply(void 0, arguments);\n\n if (!payloadedAction.error) {\n payloadedAction.error = isError;\n }\n\n if (Array.isArray(dispatchs)) {\n return dispatchs.map(function (dispatch) {\n return dispatch(payloadedAction);\n });\n } else if (dispatchs) {\n return dispatchs(payloadedAction);\n } else {\n return payloadedAction;\n }\n };\n };\n\n function actionCreator() {\n return makeAndDispatch(dispatchFunctions, false).apply(void 0, arguments);\n }\n\n actionCreator.asError = function () {\n return makeAndDispatch(dispatchFunctions, true).apply(void 0, arguments);\n };\n\n actionCreator.getType = function () {\n return type;\n };\n\n actionCreator.toString = function () {\n return type;\n };\n\n actionCreator.raw = makeAction;\n\n actionCreator.assignTo = function (dispatchOrStores) {\n dispatchFunctions = normalizeAll(dispatchOrStores);\n return actionCreator;\n };\n\n actionCreator.assigned = function () {\n return !!dispatchFunctions;\n };\n\n actionCreator.bound = function () {\n return false;\n };\n\n actionCreator.dispatched = actionCreator.assigned;\n\n actionCreator.bindTo = function (dispatchOrStores) {\n var boundActionCreator = makeAndDispatch(normalizeAll(dispatchOrStores, false));\n boundActionCreator.asError = makeAndDispatch(normalizeAll(dispatchOrStores, true));\n boundActionCreator.raw = makeAction;\n boundActionCreator.getType = actionCreator.getType;\n boundActionCreator.toString = actionCreator.toString;\n\n boundActionCreator.assignTo = function () {\n return boundActionCreator;\n };\n\n boundActionCreator.bindTo = function () {\n return boundActionCreator;\n };\n\n boundActionCreator.assigned = function () {\n return false;\n };\n\n boundActionCreator.bound = function () {\n return true;\n };\n\n boundActionCreator.dispatched = boundActionCreator.bound;\n return boundActionCreator;\n };\n\n return actionCreator;\n}\n\n;","'use strict';\n\nmodule.exports = function bind(fn, thisArg) {\n return function wrap() {\n var args = new Array(arguments.length);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i];\n }\n return fn.apply(thisArg, args);\n };\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction encode(val) {\n return encodeURIComponent(val).\n replace(/%3A/gi, ':').\n replace(/%24/g, '$').\n replace(/%2C/gi, ',').\n replace(/%20/g, '+').\n replace(/%5B/gi, '[').\n replace(/%5D/gi, ']');\n}\n\n/**\n * Build a URL by appending params to the end\n *\n * @param {string} url The base of the url (e.g., http://www.google.com)\n * @param {object} [params] The params to be appended\n * @returns {string} The formatted url\n */\nmodule.exports = function buildURL(url, params, paramsSerializer) {\n /*eslint no-param-reassign:0*/\n if (!params) {\n return url;\n }\n\n var serializedParams;\n if (paramsSerializer) {\n serializedParams = paramsSerializer(params);\n } else if (utils.isURLSearchParams(params)) {\n serializedParams = params.toString();\n } else {\n var parts = [];\n\n utils.forEach(params, function serialize(val, key) {\n if (val === null || typeof val === 'undefined') {\n return;\n }\n\n if (utils.isArray(val)) {\n key = key + '[]';\n } else {\n val = [val];\n }\n\n utils.forEach(val, function parseValue(v) {\n if (utils.isDate(v)) {\n v = v.toISOString();\n } else if (utils.isObject(v)) {\n v = JSON.stringify(v);\n }\n parts.push(encode(key) + '=' + encode(v));\n });\n });\n\n serializedParams = parts.join('&');\n }\n\n if (serializedParams) {\n var hashmarkIndex = url.indexOf('#');\n if (hashmarkIndex !== -1) {\n url = url.slice(0, hashmarkIndex);\n }\n\n url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;\n }\n\n return url;\n};\n","'use strict';\n\n/**\n * Update an Error with the specified config, error code, and response.\n *\n * @param {Error} error The error to update.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The error.\n */\nmodule.exports = function enhanceError(error, config, code, request, response) {\n error.config = config;\n if (code) {\n error.code = code;\n }\n\n error.request = request;\n error.response = response;\n error.isAxiosError = true;\n\n error.toJSON = function toJSON() {\n return {\n // Standard\n message: this.message,\n name: this.name,\n // Microsoft\n description: this.description,\n number: this.number,\n // Mozilla\n fileName: this.fileName,\n lineNumber: this.lineNumber,\n columnNumber: this.columnNumber,\n stack: this.stack,\n // Axios\n config: this.config,\n code: this.code\n };\n };\n return error;\n};\n","'use strict';\n\nvar utils = require('./../utils');\nvar settle = require('./../core/settle');\nvar cookies = require('./../helpers/cookies');\nvar buildURL = require('./../helpers/buildURL');\nvar buildFullPath = require('../core/buildFullPath');\nvar parseHeaders = require('./../helpers/parseHeaders');\nvar isURLSameOrigin = require('./../helpers/isURLSameOrigin');\nvar createError = require('../core/createError');\n\nmodule.exports = function xhrAdapter(config) {\n return new Promise(function dispatchXhrRequest(resolve, reject) {\n var requestData = config.data;\n var requestHeaders = config.headers;\n var responseType = config.responseType;\n\n if (utils.isFormData(requestData)) {\n delete requestHeaders['Content-Type']; // Let the browser set it\n }\n\n var request = new XMLHttpRequest();\n\n // HTTP basic authentication\n if (config.auth) {\n var username = config.auth.username || '';\n var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';\n requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);\n }\n\n var fullPath = buildFullPath(config.baseURL, config.url);\n request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);\n\n // Set the request timeout in MS\n request.timeout = config.timeout;\n\n function onloadend() {\n if (!request) {\n return;\n }\n // Prepare the response\n var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;\n var responseData = !responseType || responseType === 'text' || responseType === 'json' ?\n request.responseText : request.response;\n var response = {\n data: responseData,\n status: request.status,\n statusText: request.statusText,\n headers: responseHeaders,\n config: config,\n request: request\n };\n\n settle(resolve, reject, response);\n\n // Clean up request\n request = null;\n }\n\n if ('onloadend' in request) {\n // Use onloadend if available\n request.onloadend = onloadend;\n } else {\n // Listen for ready state to emulate onloadend\n request.onreadystatechange = function handleLoad() {\n if (!request || request.readyState !== 4) {\n return;\n }\n\n // The request errored out and we didn't get a response, this will be\n // handled by onerror instead\n // With one exception: request that using file: protocol, most browsers\n // will return status as 0 even though it's a successful request\n if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {\n return;\n }\n // readystate handler is calling before onerror or ontimeout handlers,\n // so we should call onloadend on the next 'tick'\n setTimeout(onloadend);\n };\n }\n\n // Handle browser request cancellation (as opposed to a manual cancellation)\n request.onabort = function handleAbort() {\n if (!request) {\n return;\n }\n\n reject(createError('Request aborted', config, 'ECONNABORTED', request));\n\n // Clean up request\n request = null;\n };\n\n // Handle low level network errors\n request.onerror = function handleError() {\n // Real errors are hidden from us by the browser\n // onerror should only fire if it's a network error\n reject(createError('Network Error', config, null, request));\n\n // Clean up request\n request = null;\n };\n\n // Handle timeout\n request.ontimeout = function handleTimeout() {\n var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';\n if (config.timeoutErrorMessage) {\n timeoutErrorMessage = config.timeoutErrorMessage;\n }\n reject(createError(\n timeoutErrorMessage,\n config,\n config.transitional && config.transitional.clarifyTimeoutError ? 'ETIMEDOUT' : 'ECONNABORTED',\n request));\n\n // Clean up request\n request = null;\n };\n\n // Add xsrf header\n // This is only done if running in a standard browser environment.\n // Specifically not if we're in a web worker, or react-native.\n if (utils.isStandardBrowserEnv()) {\n // Add xsrf header\n var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?\n cookies.read(config.xsrfCookieName) :\n undefined;\n\n if (xsrfValue) {\n requestHeaders[config.xsrfHeaderName] = xsrfValue;\n }\n }\n\n // Add headers to the request\n if ('setRequestHeader' in request) {\n utils.forEach(requestHeaders, function setRequestHeader(val, key) {\n if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {\n // Remove Content-Type if data is undefined\n delete requestHeaders[key];\n } else {\n // Otherwise add header to the request\n request.setRequestHeader(key, val);\n }\n });\n }\n\n // Add withCredentials to request if needed\n if (!utils.isUndefined(config.withCredentials)) {\n request.withCredentials = !!config.withCredentials;\n }\n\n // Add responseType to request if needed\n if (responseType && responseType !== 'json') {\n request.responseType = config.responseType;\n }\n\n // Handle progress if needed\n if (typeof config.onDownloadProgress === 'function') {\n request.addEventListener('progress', config.onDownloadProgress);\n }\n\n // Not all browsers support upload events\n if (typeof config.onUploadProgress === 'function' && request.upload) {\n request.upload.addEventListener('progress', config.onUploadProgress);\n }\n\n if (config.cancelToken) {\n // Handle cancellation\n config.cancelToken.promise.then(function onCanceled(cancel) {\n if (!request) {\n return;\n }\n\n request.abort();\n reject(cancel);\n // Clean up request\n request = null;\n });\n }\n\n if (!requestData) {\n requestData = null;\n }\n\n // Send the request\n request.send(requestData);\n });\n};\n","'use strict';\n\nvar enhanceError = require('./enhanceError');\n\n/**\n * Create an Error with the specified message, config, error code, request and response.\n *\n * @param {string} message The error message.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The created error.\n */\nmodule.exports = function createError(message, config, code, request, response) {\n var error = new Error(message);\n return enhanceError(error, config, code, request, response);\n};\n","'use strict';\n\nmodule.exports = function isCancel(value) {\n return !!(value && value.__CANCEL__);\n};\n","'use strict';\n\nvar utils = require('../utils');\n\n/**\n * Config-specific merge-function which creates a new config-object\n * by merging two configuration objects together.\n *\n * @param {Object} config1\n * @param {Object} config2\n * @returns {Object} New object resulting from merging config2 to config1\n */\nmodule.exports = function mergeConfig(config1, config2) {\n // eslint-disable-next-line no-param-reassign\n config2 = config2 || {};\n var config = {};\n\n var valueFromConfig2Keys = ['url', 'method', 'data'];\n var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params'];\n var defaultToConfig2Keys = [\n 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',\n 'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',\n 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress',\n 'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent',\n 'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding'\n ];\n var directMergeKeys = ['validateStatus'];\n\n function getMergedValue(target, source) {\n if (utils.isPlainObject(target) && utils.isPlainObject(source)) {\n return utils.merge(target, source);\n } else if (utils.isPlainObject(source)) {\n return utils.merge({}, source);\n } else if (utils.isArray(source)) {\n return source.slice();\n }\n return source;\n }\n\n function mergeDeepProperties(prop) {\n if (!utils.isUndefined(config2[prop])) {\n config[prop] = getMergedValue(config1[prop], config2[prop]);\n } else if (!utils.isUndefined(config1[prop])) {\n config[prop] = getMergedValue(undefined, config1[prop]);\n }\n }\n\n utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {\n if (!utils.isUndefined(config2[prop])) {\n config[prop] = getMergedValue(undefined, config2[prop]);\n }\n });\n\n utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties);\n\n utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {\n if (!utils.isUndefined(config2[prop])) {\n config[prop] = getMergedValue(undefined, config2[prop]);\n } else if (!utils.isUndefined(config1[prop])) {\n config[prop] = getMergedValue(undefined, config1[prop]);\n }\n });\n\n utils.forEach(directMergeKeys, function merge(prop) {\n if (prop in config2) {\n config[prop] = getMergedValue(config1[prop], config2[prop]);\n } else if (prop in config1) {\n config[prop] = getMergedValue(undefined, config1[prop]);\n }\n });\n\n var axiosKeys = valueFromConfig2Keys\n .concat(mergeDeepPropertiesKeys)\n .concat(defaultToConfig2Keys)\n .concat(directMergeKeys);\n\n var otherKeys = Object\n .keys(config1)\n .concat(Object.keys(config2))\n .filter(function filterAxiosKeys(key) {\n return axiosKeys.indexOf(key) === -1;\n });\n\n utils.forEach(otherKeys, mergeDeepProperties);\n\n return config;\n};\n","'use strict';\n\n/**\n * A `Cancel` is an object that is thrown when an operation is canceled.\n *\n * @class\n * @param {string=} message The message.\n */\nfunction Cancel(message) {\n this.message = message;\n}\n\nCancel.prototype.toString = function toString() {\n return 'Cancel' + (this.message ? ': ' + this.message : '');\n};\n\nCancel.prototype.__CANCEL__ = true;\n\nmodule.exports = Cancel;\n","'use strict';\n\nvar slice = Array.prototype.slice;\nvar isArgs = require('./isArguments');\n\nvar origKeys = Object.keys;\nvar keysShim = origKeys ? function keys(o) { return origKeys(o); } : require('./implementation');\n\nvar originalKeys = Object.keys;\n\nkeysShim.shim = function shimObjectKeys() {\n\tif (Object.keys) {\n\t\tvar keysWorksWithArguments = (function () {\n\t\t\t// Safari 5.0 bug\n\t\t\tvar args = Object.keys(arguments);\n\t\t\treturn args && args.length === arguments.length;\n\t\t}(1, 2));\n\t\tif (!keysWorksWithArguments) {\n\t\t\tObject.keys = function keys(object) { // eslint-disable-line func-name-matching\n\t\t\t\tif (isArgs(object)) {\n\t\t\t\t\treturn originalKeys(slice.call(object));\n\t\t\t\t}\n\t\t\t\treturn originalKeys(object);\n\t\t\t};\n\t\t}\n\t} else {\n\t\tObject.keys = keysShim;\n\t}\n\treturn Object.keys || keysShim;\n};\n\nmodule.exports = keysShim;\n","'use strict';\n\nvar toStr = Object.prototype.toString;\n\nmodule.exports = function isArguments(value) {\n\tvar str = toStr.call(value);\n\tvar isArgs = str === '[object Arguments]';\n\tif (!isArgs) {\n\t\tisArgs = str !== '[object Array]' &&\n\t\t\tvalue !== null &&\n\t\t\ttypeof value === 'object' &&\n\t\t\ttypeof value.length === 'number' &&\n\t\t\tvalue.length >= 0 &&\n\t\t\ttoStr.call(value.callee) === '[object Function]';\n\t}\n\treturn isArgs;\n};\n","'use strict';\n\nvar bind = require('function-bind');\n\nmodule.exports = bind.call(Function.call, Object.prototype.hasOwnProperty);\n","'use strict';\n\nvar toObject = Object;\nvar TypeErr = TypeError;\n\nmodule.exports = function flags() {\n\tif (this != null && this !== toObject(this)) {\n\t\tthrow new TypeErr('RegExp.prototype.flags getter called on non-object');\n\t}\n\tvar result = '';\n\tif (this.global) {\n\t\tresult += 'g';\n\t}\n\tif (this.ignoreCase) {\n\t\tresult += 'i';\n\t}\n\tif (this.multiline) {\n\t\tresult += 'm';\n\t}\n\tif (this.dotAll) {\n\t\tresult += 's';\n\t}\n\tif (this.unicode) {\n\t\tresult += 'u';\n\t}\n\tif (this.sticky) {\n\t\tresult += 'y';\n\t}\n\treturn result;\n};\n","'use strict';\n\nvar implementation = require('./implementation');\n\nvar supportsDescriptors = require('define-properties').supportsDescriptors;\nvar gOPD = Object.getOwnPropertyDescriptor;\nvar TypeErr = TypeError;\n\nmodule.exports = function getPolyfill() {\n\tif (!supportsDescriptors) {\n\t\tthrow new TypeErr('RegExp.prototype.flags requires a true ES5 environment that supports property descriptors');\n\t}\n\tif (/a/mig.flags === 'gim') {\n\t\tvar descriptor = gOPD(RegExp.prototype, 'flags');\n\t\tif (descriptor && typeof descriptor.get === 'function' && typeof (/a/).dotAll === 'boolean') {\n\t\t\treturn descriptor.get;\n\t\t}\n\t}\n\treturn implementation;\n};\n","'use strict';\n\nvar formats = require('./formats');\n\nvar has = Object.prototype.hasOwnProperty;\nvar isArray = Array.isArray;\n\nvar hexTable = (function () {\n var array = [];\n for (var i = 0; i < 256; ++i) {\n array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase());\n }\n\n return array;\n}());\n\nvar compactQueue = function compactQueue(queue) {\n while (queue.length > 1) {\n var item = queue.pop();\n var obj = item.obj[item.prop];\n\n if (isArray(obj)) {\n var compacted = [];\n\n for (var j = 0; j < obj.length; ++j) {\n if (typeof obj[j] !== 'undefined') {\n compacted.push(obj[j]);\n }\n }\n\n item.obj[item.prop] = compacted;\n }\n }\n};\n\nvar arrayToObject = function arrayToObject(source, options) {\n var obj = options && options.plainObjects ? Object.create(null) : {};\n for (var i = 0; i < source.length; ++i) {\n if (typeof source[i] !== 'undefined') {\n obj[i] = source[i];\n }\n }\n\n return obj;\n};\n\nvar merge = function merge(target, source, options) {\n /* eslint no-param-reassign: 0 */\n if (!source) {\n return target;\n }\n\n if (typeof source !== 'object') {\n if (isArray(target)) {\n target.push(source);\n } else if (target && typeof target === 'object') {\n if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {\n target[source] = true;\n }\n } else {\n return [target, source];\n }\n\n return target;\n }\n\n if (!target || typeof target !== 'object') {\n return [target].concat(source);\n }\n\n var mergeTarget = target;\n if (isArray(target) && !isArray(source)) {\n mergeTarget = arrayToObject(target, options);\n }\n\n if (isArray(target) && isArray(source)) {\n source.forEach(function (item, i) {\n if (has.call(target, i)) {\n var targetItem = target[i];\n if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {\n target[i] = merge(targetItem, item, options);\n } else {\n target.push(item);\n }\n } else {\n target[i] = item;\n }\n });\n return target;\n }\n\n return Object.keys(source).reduce(function (acc, key) {\n var value = source[key];\n\n if (has.call(acc, key)) {\n acc[key] = merge(acc[key], value, options);\n } else {\n acc[key] = value;\n }\n return acc;\n }, mergeTarget);\n};\n\nvar assign = function assignSingleSource(target, source) {\n return Object.keys(source).reduce(function (acc, key) {\n acc[key] = source[key];\n return acc;\n }, target);\n};\n\nvar decode = function (str, decoder, charset) {\n var strWithoutPlus = str.replace(/\\+/g, ' ');\n if (charset === 'iso-8859-1') {\n // unescape never throws, no try...catch needed:\n return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape);\n }\n // utf-8\n try {\n return decodeURIComponent(strWithoutPlus);\n } catch (e) {\n return strWithoutPlus;\n }\n};\n\nvar encode = function encode(str, defaultEncoder, charset, kind, format) {\n // This code was originally written by Brian White (mscdex) for the io.js core querystring library.\n // It has been adapted here for stricter adherence to RFC 3986\n if (str.length === 0) {\n return str;\n }\n\n var string = str;\n if (typeof str === 'symbol') {\n string = Symbol.prototype.toString.call(str);\n } else if (typeof str !== 'string') {\n string = String(str);\n }\n\n if (charset === 'iso-8859-1') {\n return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) {\n return '%26%23' + parseInt($0.slice(2), 16) + '%3B';\n });\n }\n\n var out = '';\n for (var i = 0; i < string.length; ++i) {\n var c = string.charCodeAt(i);\n\n if (\n c === 0x2D // -\n || c === 0x2E // .\n || c === 0x5F // _\n || c === 0x7E // ~\n || (c >= 0x30 && c <= 0x39) // 0-9\n || (c >= 0x41 && c <= 0x5A) // a-z\n || (c >= 0x61 && c <= 0x7A) // A-Z\n || (format === formats.RFC1738 && (c === 0x28 || c === 0x29)) // ( )\n ) {\n out += string.charAt(i);\n continue;\n }\n\n if (c < 0x80) {\n out = out + hexTable[c];\n continue;\n }\n\n if (c < 0x800) {\n out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]);\n continue;\n }\n\n if (c < 0xD800 || c >= 0xE000) {\n out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]);\n continue;\n }\n\n i += 1;\n c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));\n /* eslint operator-linebreak: [2, \"before\"] */\n out += hexTable[0xF0 | (c >> 18)]\n + hexTable[0x80 | ((c >> 12) & 0x3F)]\n + hexTable[0x80 | ((c >> 6) & 0x3F)]\n + hexTable[0x80 | (c & 0x3F)];\n }\n\n return out;\n};\n\nvar compact = function compact(value) {\n var queue = [{ obj: { o: value }, prop: 'o' }];\n var refs = [];\n\n for (var i = 0; i < queue.length; ++i) {\n var item = queue[i];\n var obj = item.obj[item.prop];\n\n var keys = Object.keys(obj);\n for (var j = 0; j < keys.length; ++j) {\n var key = keys[j];\n var val = obj[key];\n if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) {\n queue.push({ obj: obj, prop: key });\n refs.push(val);\n }\n }\n }\n\n compactQueue(queue);\n\n return value;\n};\n\nvar isRegExp = function isRegExp(obj) {\n return Object.prototype.toString.call(obj) === '[object RegExp]';\n};\n\nvar isBuffer = function isBuffer(obj) {\n if (!obj || typeof obj !== 'object') {\n return false;\n }\n\n return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj));\n};\n\nvar combine = function combine(a, b) {\n return [].concat(a, b);\n};\n\nvar maybeMap = function maybeMap(val, fn) {\n if (isArray(val)) {\n var mapped = [];\n for (var i = 0; i < val.length; i += 1) {\n mapped.push(fn(val[i]));\n }\n return mapped;\n }\n return fn(val);\n};\n\nmodule.exports = {\n arrayToObject: arrayToObject,\n assign: assign,\n combine: combine,\n compact: compact,\n decode: decode,\n encode: encode,\n isBuffer: isBuffer,\n isRegExp: isRegExp,\n maybeMap: maybeMap,\n merge: merge\n};\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.DateTimeFormat = void 0;\nvar tslib_1 = require(\"tslib\");\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nvar get_internal_slots_1 = tslib_1.__importDefault(require(\"./get_internal_slots\"));\nvar links_1 = tslib_1.__importDefault(require(\"./data/links\"));\nvar packer_1 = require(\"./packer\");\nvar FormatDateTime_1 = require(\"./abstract/FormatDateTime\");\nvar InitializeDateTimeFormat_1 = require(\"./abstract/InitializeDateTimeFormat\");\nvar utils_1 = require(\"./abstract/utils\");\nvar FormatDateTimeToParts_1 = require(\"./abstract/FormatDateTimeToParts\");\nvar FormatDateTimeRangeToParts_1 = require(\"./abstract/FormatDateTimeRangeToParts\");\nvar FormatDateTimeRange_1 = require(\"./abstract/FormatDateTimeRange\");\nvar skeleton_1 = require(\"./abstract/skeleton\");\nvar UPPERCASED_LINKS = Object.keys(links_1.default).reduce(function (all, l) {\n all[l.toUpperCase()] = links_1.default[l];\n return all;\n}, {});\nvar RESOLVED_OPTIONS_KEYS = [\n 'locale',\n 'calendar',\n 'numberingSystem',\n 'dateStyle',\n 'timeStyle',\n 'timeZone',\n 'hourCycle',\n 'weekday',\n 'era',\n 'year',\n 'month',\n 'day',\n 'hour',\n 'minute',\n 'second',\n 'timeZoneName',\n];\nvar formatDescriptor = {\n enumerable: false,\n configurable: true,\n get: function () {\n if (typeof this !== 'object' ||\n !ecma402_abstract_1.OrdinaryHasInstance(exports.DateTimeFormat, this)) {\n throw TypeError('Intl.DateTimeFormat format property accessor called on incompatible receiver');\n }\n var internalSlots = get_internal_slots_1.default(this);\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n var dtf = this;\n var boundFormat = internalSlots.boundFormat;\n if (boundFormat === undefined) {\n // https://tc39.es/proposal-unified-intl-numberformat/section11/numberformat_diff_out.html#sec-number-format-functions\n boundFormat = function (date) {\n var x;\n if (date === undefined) {\n x = Date.now();\n }\n else {\n x = Number(date);\n }\n return FormatDateTime_1.FormatDateTime(dtf, x, {\n getInternalSlots: get_internal_slots_1.default,\n localeData: exports.DateTimeFormat.localeData,\n tzData: exports.DateTimeFormat.tzData,\n getDefaultTimeZone: exports.DateTimeFormat.getDefaultTimeZone,\n });\n };\n try {\n // https://github.com/tc39/test262/blob/master/test/intl402/NumberFormat/prototype/format/format-function-name.js\n Object.defineProperty(boundFormat, 'name', {\n configurable: true,\n enumerable: false,\n writable: false,\n value: '',\n });\n }\n catch (e) {\n // In older browser (e.g Chrome 36 like polyfill.io)\n // TypeError: Cannot redefine property: name\n }\n internalSlots.boundFormat = boundFormat;\n }\n return boundFormat;\n },\n};\ntry {\n // https://github.com/tc39/test262/blob/master/test/intl402/NumberFormat/prototype/format/name.js\n Object.defineProperty(formatDescriptor.get, 'name', {\n configurable: true,\n enumerable: false,\n writable: false,\n value: 'get format',\n });\n}\ncatch (e) {\n // In older browser (e.g Chrome 36 like polyfill.io)\n // TypeError: Cannot redefine property: name\n}\nexports.DateTimeFormat = function (locales, options) {\n // Cannot use `new.target` bc of IE11 & TS transpiles it to something else\n if (!this || !ecma402_abstract_1.OrdinaryHasInstance(exports.DateTimeFormat, this)) {\n return new exports.DateTimeFormat(locales, options);\n }\n InitializeDateTimeFormat_1.InitializeDateTimeFormat(this, locales, options, {\n tzData: exports.DateTimeFormat.tzData,\n uppercaseLinks: UPPERCASED_LINKS,\n availableLocales: exports.DateTimeFormat.availableLocales,\n relevantExtensionKeys: exports.DateTimeFormat.relevantExtensionKeys,\n getDefaultLocale: exports.DateTimeFormat.getDefaultLocale,\n getDefaultTimeZone: exports.DateTimeFormat.getDefaultTimeZone,\n getInternalSlots: get_internal_slots_1.default,\n localeData: exports.DateTimeFormat.localeData,\n });\n /** IMPL START */\n var internalSlots = get_internal_slots_1.default(this);\n var dataLocale = internalSlots.dataLocale;\n var dataLocaleData = exports.DateTimeFormat.localeData[dataLocale];\n ecma402_abstract_1.invariant(dataLocaleData !== undefined, \"Cannot load locale-dependent data for \" + dataLocale + \".\");\n /** IMPL END */\n};\n// Static properties\necma402_abstract_1.defineProperty(exports.DateTimeFormat, 'supportedLocalesOf', {\n value: function supportedLocalesOf(locales, options) {\n return ecma402_abstract_1.SupportedLocales(exports.DateTimeFormat.availableLocales, ecma402_abstract_1.CanonicalizeLocaleList(locales), options);\n },\n});\necma402_abstract_1.defineProperty(exports.DateTimeFormat.prototype, 'resolvedOptions', {\n value: function resolvedOptions() {\n if (typeof this !== 'object' ||\n !ecma402_abstract_1.OrdinaryHasInstance(exports.DateTimeFormat, this)) {\n throw TypeError('Method Intl.DateTimeFormat.prototype.resolvedOptions called on incompatible receiver');\n }\n var internalSlots = get_internal_slots_1.default(this);\n var ro = {};\n for (var _i = 0, RESOLVED_OPTIONS_KEYS_1 = RESOLVED_OPTIONS_KEYS; _i < RESOLVED_OPTIONS_KEYS_1.length; _i++) {\n var key = RESOLVED_OPTIONS_KEYS_1[_i];\n var value = internalSlots[key];\n if (key === 'hourCycle') {\n var hour12 = value === 'h11' || value === 'h12'\n ? true\n : value === 'h23' || value === 'h24'\n ? false\n : undefined;\n if (hour12 !== undefined) {\n ro.hour12 = hour12;\n }\n }\n if (utils_1.DATE_TIME_PROPS.indexOf(key) > -1) {\n if (internalSlots.dateStyle !== undefined ||\n internalSlots.timeStyle !== undefined) {\n value = undefined;\n }\n }\n if (value !== undefined) {\n ro[key] = value;\n }\n }\n return ro;\n },\n});\necma402_abstract_1.defineProperty(exports.DateTimeFormat.prototype, 'formatToParts', {\n value: function formatToParts(date) {\n if (date === undefined) {\n date = Date.now();\n }\n else {\n date = ecma402_abstract_1.ToNumber(date);\n }\n return FormatDateTimeToParts_1.FormatDateTimeToParts(this, date, {\n getInternalSlots: get_internal_slots_1.default,\n localeData: exports.DateTimeFormat.localeData,\n tzData: exports.DateTimeFormat.tzData,\n getDefaultTimeZone: exports.DateTimeFormat.getDefaultTimeZone,\n });\n },\n});\necma402_abstract_1.defineProperty(exports.DateTimeFormat.prototype, 'formatRangeToParts', {\n value: function formatRangeToParts(startDate, endDate) {\n var dtf = this;\n if (typeof dtf !== 'object') {\n throw new TypeError();\n }\n if (startDate === undefined || endDate === undefined) {\n throw new TypeError('startDate/endDate cannot be undefined');\n }\n var x = ecma402_abstract_1.ToNumber(startDate);\n var y = ecma402_abstract_1.ToNumber(endDate);\n return FormatDateTimeRangeToParts_1.FormatDateTimeRangeToParts(dtf, x, y, {\n getInternalSlots: get_internal_slots_1.default,\n localeData: exports.DateTimeFormat.localeData,\n tzData: exports.DateTimeFormat.tzData,\n getDefaultTimeZone: exports.DateTimeFormat.getDefaultTimeZone,\n });\n },\n});\necma402_abstract_1.defineProperty(exports.DateTimeFormat.prototype, 'formatRange', {\n value: function formatRange(startDate, endDate) {\n var dtf = this;\n if (typeof dtf !== 'object') {\n throw new TypeError();\n }\n if (startDate === undefined || endDate === undefined) {\n throw new TypeError('startDate/endDate cannot be undefined');\n }\n var x = ecma402_abstract_1.ToNumber(startDate);\n var y = ecma402_abstract_1.ToNumber(endDate);\n return FormatDateTimeRange_1.FormatDateTimeRange(dtf, x, y, {\n getInternalSlots: get_internal_slots_1.default,\n localeData: exports.DateTimeFormat.localeData,\n tzData: exports.DateTimeFormat.tzData,\n getDefaultTimeZone: exports.DateTimeFormat.getDefaultTimeZone,\n });\n },\n});\nvar DEFAULT_TIMEZONE = 'UTC';\nexports.DateTimeFormat.__setDefaultTimeZone = function (timeZone) {\n if (timeZone !== undefined) {\n timeZone = String(timeZone);\n if (!ecma402_abstract_1.IsValidTimeZoneName(timeZone, {\n tzData: exports.DateTimeFormat.tzData,\n uppercaseLinks: UPPERCASED_LINKS,\n })) {\n throw new RangeError('Invalid timeZoneName');\n }\n timeZone = ecma402_abstract_1.CanonicalizeTimeZoneName(timeZone, {\n tzData: exports.DateTimeFormat.tzData,\n uppercaseLinks: UPPERCASED_LINKS,\n });\n }\n else {\n timeZone = DEFAULT_TIMEZONE;\n }\n exports.DateTimeFormat.__defaultTimeZone = timeZone;\n};\nexports.DateTimeFormat.relevantExtensionKeys = ['nu', 'ca', 'hc'];\nexports.DateTimeFormat.__defaultTimeZone = DEFAULT_TIMEZONE;\nexports.DateTimeFormat.getDefaultTimeZone = function () { return exports.DateTimeFormat.__defaultTimeZone; };\nexports.DateTimeFormat.__addLocaleData = function __addLocaleData() {\n var data = [];\n for (var _i = 0; _i < arguments.length; _i++) {\n data[_i] = arguments[_i];\n }\n var _loop_1 = function (d, locale) {\n var dateFormat = d.dateFormat, timeFormat = d.timeFormat, dateTimeFormat = d.dateTimeFormat, formats = d.formats, intervalFormats = d.intervalFormats, rawData = tslib_1.__rest(d, [\"dateFormat\", \"timeFormat\", \"dateTimeFormat\", \"formats\", \"intervalFormats\"]);\n var processedData = tslib_1.__assign(tslib_1.__assign({}, rawData), { dateFormat: {\n full: skeleton_1.parseDateTimeSkeleton(dateFormat.full),\n long: skeleton_1.parseDateTimeSkeleton(dateFormat.long),\n medium: skeleton_1.parseDateTimeSkeleton(dateFormat.medium),\n short: skeleton_1.parseDateTimeSkeleton(dateFormat.short),\n }, timeFormat: {\n full: skeleton_1.parseDateTimeSkeleton(timeFormat.full),\n long: skeleton_1.parseDateTimeSkeleton(timeFormat.long),\n medium: skeleton_1.parseDateTimeSkeleton(timeFormat.medium),\n short: skeleton_1.parseDateTimeSkeleton(timeFormat.short),\n }, dateTimeFormat: {\n full: skeleton_1.parseDateTimeSkeleton(dateTimeFormat.full).pattern,\n long: skeleton_1.parseDateTimeSkeleton(dateTimeFormat.long).pattern,\n medium: skeleton_1.parseDateTimeSkeleton(dateTimeFormat.medium).pattern,\n short: skeleton_1.parseDateTimeSkeleton(dateTimeFormat.short).pattern,\n }, formats: {} });\n var _loop_2 = function (calendar) {\n processedData.formats[calendar] = Object.keys(formats[calendar]).map(function (skeleton) {\n return skeleton_1.parseDateTimeSkeleton(skeleton, formats[calendar][skeleton], intervalFormats[skeleton], intervalFormats.intervalFormatFallback);\n });\n };\n for (var calendar in formats) {\n _loop_2(calendar);\n }\n var minimizedLocale = new Intl.Locale(locale)\n .minimize()\n .toString();\n exports.DateTimeFormat.localeData[locale] = exports.DateTimeFormat.localeData[minimizedLocale] = processedData;\n exports.DateTimeFormat.availableLocales.add(locale);\n exports.DateTimeFormat.availableLocales.add(minimizedLocale);\n if (!exports.DateTimeFormat.__defaultLocale) {\n exports.DateTimeFormat.__defaultLocale = minimizedLocale;\n }\n };\n for (var _a = 0, data_1 = data; _a < data_1.length; _a++) {\n var _b = data_1[_a], d = _b.data, locale = _b.locale;\n _loop_1(d, locale);\n }\n};\nObject.defineProperty(exports.DateTimeFormat.prototype, 'format', formatDescriptor);\nexports.DateTimeFormat.__defaultLocale = '';\nexports.DateTimeFormat.localeData = {};\nexports.DateTimeFormat.availableLocales = new Set();\nexports.DateTimeFormat.getDefaultLocale = function () {\n return exports.DateTimeFormat.__defaultLocale;\n};\nexports.DateTimeFormat.polyfilled = true;\nexports.DateTimeFormat.tzData = {};\nexports.DateTimeFormat.__addTZData = function (d) {\n exports.DateTimeFormat.tzData = packer_1.unpack(d);\n};\ntry {\n if (typeof Symbol !== 'undefined') {\n Object.defineProperty(exports.DateTimeFormat.prototype, Symbol.toStringTag, {\n value: 'Intl.DateTimeFormat',\n writable: false,\n enumerable: false,\n configurable: true,\n });\n }\n Object.defineProperty(exports.DateTimeFormat.prototype.constructor, 'length', {\n value: 1,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n}\ncatch (e) {\n // Meta fix so we're test262-compliant, not important\n}\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.ResolveLocale = void 0;\nvar LookupMatcher_1 = require(\"./LookupMatcher\");\nvar BestFitMatcher_1 = require(\"./BestFitMatcher\");\nvar utils_1 = require(\"./utils\");\nvar UnicodeExtensionValue_1 = require(\"./UnicodeExtensionValue\");\n/**\n * https://tc39.es/ecma402/#sec-resolvelocale\n */\nfunction ResolveLocale(availableLocales, requestedLocales, options, relevantExtensionKeys, localeData, getDefaultLocale) {\n var matcher = options.localeMatcher;\n var r;\n if (matcher === 'lookup') {\n r = LookupMatcher_1.LookupMatcher(availableLocales, requestedLocales, getDefaultLocale);\n }\n else {\n r = BestFitMatcher_1.BestFitMatcher(availableLocales, requestedLocales, getDefaultLocale);\n }\n var foundLocale = r.locale;\n var result = { locale: '', dataLocale: foundLocale };\n var supportedExtension = '-u';\n for (var _i = 0, relevantExtensionKeys_1 = relevantExtensionKeys; _i < relevantExtensionKeys_1.length; _i++) {\n var key = relevantExtensionKeys_1[_i];\n utils_1.invariant(foundLocale in localeData, \"Missing locale data for \" + foundLocale);\n var foundLocaleData = localeData[foundLocale];\n utils_1.invariant(typeof foundLocaleData === 'object' && foundLocaleData !== null, \"locale data \" + key + \" must be an object\");\n var keyLocaleData = foundLocaleData[key];\n utils_1.invariant(Array.isArray(keyLocaleData), \"keyLocaleData for \" + key + \" must be an array\");\n var value = keyLocaleData[0];\n utils_1.invariant(typeof value === 'string' || value === null, \"value must be string or null but got \" + typeof value + \" in key \" + key);\n var supportedExtensionAddition = '';\n if (r.extension) {\n var requestedValue = UnicodeExtensionValue_1.UnicodeExtensionValue(r.extension, key);\n if (requestedValue !== undefined) {\n if (requestedValue !== '') {\n if (~keyLocaleData.indexOf(requestedValue)) {\n value = requestedValue;\n supportedExtensionAddition = \"-\" + key + \"-\" + value;\n }\n }\n else if (~requestedValue.indexOf('true')) {\n value = 'true';\n supportedExtensionAddition = \"-\" + key;\n }\n }\n }\n if (key in options) {\n var optionsValue = options[key];\n utils_1.invariant(typeof optionsValue === 'string' ||\n typeof optionsValue === 'undefined' ||\n optionsValue === null, 'optionsValue must be String, Undefined or Null');\n if (~keyLocaleData.indexOf(optionsValue)) {\n if (optionsValue !== value) {\n value = optionsValue;\n supportedExtensionAddition = '';\n }\n }\n }\n result[key] = value;\n supportedExtension += supportedExtensionAddition;\n }\n if (supportedExtension.length > 2) {\n var privateIndex = foundLocale.indexOf('-x-');\n if (privateIndex === -1) {\n foundLocale = foundLocale + supportedExtension;\n }\n else {\n var preExtension = foundLocale.slice(0, privateIndex);\n var postExtension = foundLocale.slice(privateIndex, foundLocale.length);\n foundLocale = preExtension + supportedExtension + postExtension;\n }\n foundLocale = Intl.getCanonicalLocales(foundLocale)[0];\n }\n result.locale = foundLocale;\n return result;\n}\nexports.ResolveLocale = ResolveLocale;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.PartitionDateTimePattern = void 0;\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nvar FormatDateTimePattern_1 = require(\"./FormatDateTimePattern\");\n/**\n * https://tc39.es/ecma402/#sec-partitiondatetimepattern\n * @param dtf\n * @param x\n */\nfunction PartitionDateTimePattern(dtf, x, implDetails) {\n x = ecma402_abstract_1.TimeClip(x);\n if (isNaN(x)) {\n throw new RangeError('invalid time');\n }\n /** IMPL START */\n var getInternalSlots = implDetails.getInternalSlots;\n var internalSlots = getInternalSlots(dtf);\n /** IMPL END */\n var pattern = internalSlots.pattern;\n return FormatDateTimePattern_1.FormatDateTimePattern(dtf, ecma402_abstract_1.PartitionPattern(pattern), x, implDetails);\n}\nexports.PartitionDateTimePattern = PartitionDateTimePattern;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.FormatDateTimePattern = void 0;\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nvar utils_1 = require(\"./utils\");\nvar ToLocalTime_1 = require(\"./ToLocalTime\");\nfunction pad(n) {\n if (n < 10) {\n return \"0\" + n;\n }\n return String(n);\n}\nfunction offsetToGmtString(gmtFormat, hourFormat, offsetInMs, style) {\n var offsetInMinutes = Math.floor(offsetInMs / 60000);\n var mins = Math.abs(offsetInMinutes) % 60;\n var hours = Math.floor(Math.abs(offsetInMinutes) / 60);\n var _a = hourFormat.split(';'), positivePattern = _a[0], negativePattern = _a[1];\n var offsetStr = '';\n var pattern = offsetInMs < 0 ? negativePattern : positivePattern;\n if (style === 'long') {\n offsetStr = pattern\n .replace('HH', pad(hours))\n .replace('H', String(hours))\n .replace('mm', pad(mins))\n .replace('m', String(mins));\n }\n else if (mins || hours) {\n if (!mins) {\n pattern = pattern.replace(/:?m+/, '');\n }\n offsetStr = pattern.replace(/H+/, String(hours)).replace(/m+/, String(mins));\n }\n return gmtFormat.replace('{0}', offsetStr);\n}\n/**\n * https://tc39.es/ecma402/#sec-partitiondatetimepattern\n * @param dtf\n * @param x\n */\nfunction FormatDateTimePattern(dtf, patternParts, x, _a) {\n var getInternalSlots = _a.getInternalSlots, localeData = _a.localeData, getDefaultTimeZone = _a.getDefaultTimeZone, tzData = _a.tzData;\n x = ecma402_abstract_1.TimeClip(x);\n /** IMPL START */\n var internalSlots = getInternalSlots(dtf);\n var dataLocale = internalSlots.dataLocale;\n var dataLocaleData = localeData[dataLocale];\n /** IMPL END */\n var locale = internalSlots.locale;\n var nfOptions = Object.create(null);\n nfOptions.useGrouping = false;\n var nf = new Intl.NumberFormat(locale, nfOptions);\n var nf2Options = Object.create(null);\n nf2Options.minimumIntegerDigits = 2;\n nf2Options.useGrouping = false;\n var nf2 = new Intl.NumberFormat(locale, nf2Options);\n var fractionalSecondDigits = internalSlots.fractionalSecondDigits;\n var nf3;\n if (fractionalSecondDigits !== undefined) {\n var nf3Options = Object.create(null);\n nf3Options.minimumIntegerDigits = fractionalSecondDigits;\n nf3Options.useGrouping = false;\n nf3 = new Intl.NumberFormat(locale, nf3Options);\n }\n var tm = ToLocalTime_1.ToLocalTime(x, \n // @ts-ignore\n internalSlots.calendar, internalSlots.timeZone, { tzData: tzData });\n var result = [];\n for (var _i = 0, patternParts_1 = patternParts; _i < patternParts_1.length; _i++) {\n var patternPart = patternParts_1[_i];\n var p = patternPart.type;\n if (p === 'literal') {\n result.push({\n type: 'literal',\n value: patternPart.value,\n });\n }\n else if (p === 'fractionalSecondDigits') {\n var v = Math.floor(tm.millisecond * Math.pow(10, ((fractionalSecondDigits || 0) - 3)));\n result.push({\n // @ts-expect-error Spec is not there yet\n type: 'fractionalSecond',\n value: nf3.format(v),\n });\n }\n else if (p === 'dayPeriod') {\n // TODO\n }\n else if (utils_1.DATE_TIME_PROPS.indexOf(p) > -1) {\n var fv = '';\n var f = internalSlots[p];\n // @ts-ignore\n var v = tm[p];\n if (p === 'year' && v <= 0) {\n v = 1 - v;\n }\n if (p === 'month') {\n v++;\n }\n var hourCycle = internalSlots.hourCycle;\n if (p === 'hour' && (hourCycle === 'h11' || hourCycle === 'h12')) {\n v = v % 12;\n if (v === 0 && hourCycle === 'h12') {\n v = 12;\n }\n }\n if (p === 'hour' && hourCycle === 'h24') {\n if (v === 0) {\n v = 24;\n }\n }\n if (f === 'numeric') {\n fv = nf.format(v);\n }\n else if (f === '2-digit') {\n fv = nf2.format(v);\n if (fv.length > 2) {\n fv = fv.slice(fv.length - 2, fv.length);\n }\n }\n else if (f === 'narrow' || f === 'short' || f === 'long') {\n if (p === 'era') {\n fv = dataLocaleData[p][f][v];\n }\n else if (p === 'timeZoneName') {\n var timeZoneName = dataLocaleData.timeZoneName, gmtFormat = dataLocaleData.gmtFormat, hourFormat = dataLocaleData.hourFormat;\n var timeZone = internalSlots.timeZone || getDefaultTimeZone();\n var timeZoneData = timeZoneName[timeZone];\n if (timeZoneData && timeZoneData[f]) {\n fv = timeZoneData[f][+tm.inDST];\n }\n else {\n // Fallback to gmtFormat\n fv = offsetToGmtString(gmtFormat, hourFormat, tm.timeZoneOffset, f);\n }\n }\n else if (p === 'month') {\n fv = dataLocaleData.month[f][v - 1];\n }\n else {\n fv = dataLocaleData[p][f][v];\n }\n }\n result.push({\n type: p,\n value: fv,\n });\n }\n else if (p === 'ampm') {\n var v = tm.hour;\n var fv = void 0;\n if (v > 11) {\n fv = dataLocaleData.pm;\n }\n else {\n fv = dataLocaleData.am;\n }\n result.push({\n type: 'dayPeriod',\n value: fv,\n });\n }\n else if (p === 'relatedYear') {\n var v = tm.relatedYear;\n // @ts-ignore\n var fv = nf.format(v);\n result.push({\n // @ts-ignore TODO: Fix TS type\n type: 'relatedYear',\n value: fv,\n });\n }\n else if (p === 'yearName') {\n var v = tm.yearName;\n // @ts-ignore\n var fv = nf.format(v);\n result.push({\n // @ts-ignore TODO: Fix TS type\n type: 'yearName',\n value: fv,\n });\n }\n }\n return result;\n}\nexports.FormatDateTimePattern = FormatDateTimePattern;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.ToLocalTime = void 0;\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nfunction getApplicableZoneData(t, timeZone, tzData) {\n var _a;\n var zoneData = tzData[timeZone];\n // We don't have data for this so just say it's UTC\n if (!zoneData) {\n return [0, false];\n }\n var i = 0;\n var offset = 0;\n var dst = false;\n for (; i <= zoneData.length; i++) {\n if (i === zoneData.length || zoneData[i][0] * 1e3 > t) {\n ;\n _a = zoneData[i - 1], offset = _a[2], dst = _a[3];\n break;\n }\n }\n return [offset * 1e3, dst];\n}\n/**\n * https://tc39.es/ecma402/#sec-tolocaltime\n * @param t\n * @param calendar\n * @param timeZone\n */\nfunction ToLocalTime(t, calendar, timeZone, _a) {\n var tzData = _a.tzData;\n ecma402_abstract_1.invariant(ecma402_abstract_1.Type(t) === 'Number', 'invalid time');\n ecma402_abstract_1.invariant(calendar === 'gregory', 'We only support Gregory calendar right now');\n var _b = getApplicableZoneData(t, timeZone, tzData), timeZoneOffset = _b[0], inDST = _b[1];\n var tz = t + timeZoneOffset;\n var year = ecma402_abstract_1.YearFromTime(tz);\n return {\n weekday: ecma402_abstract_1.WeekDay(tz),\n era: year < 0 ? 'BC' : 'AD',\n year: year,\n relatedYear: undefined,\n yearName: undefined,\n month: ecma402_abstract_1.MonthFromTime(tz),\n day: ecma402_abstract_1.DateFromTime(tz),\n hour: ecma402_abstract_1.HourFromTime(tz),\n minute: ecma402_abstract_1.MinFromTime(tz),\n second: ecma402_abstract_1.SecFromTime(tz),\n millisecond: ecma402_abstract_1.msFromTime(tz),\n inDST: inDST,\n // IMPORTANT: Not in spec\n timeZoneOffset: timeZoneOffset,\n };\n}\nexports.ToLocalTime = ToLocalTime;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.splitRangePattern = exports.splitFallbackRangePattern = exports.parseDateTimeSkeleton = exports.processDateTimePattern = void 0;\nvar tslib_1 = require(\"tslib\");\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\n/**\n * https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table\n * Credit: https://github.com/caridy/intl-datetimeformat-pattern/blob/master/index.js\n * with some tweaks\n */\nvar DATE_TIME_REGEX = /(?:[Eec]{1,6}|G{1,5}|[Qq]{1,5}|(?:[yYur]+|U{1,5})|[ML]{1,5}|d{1,2}|D{1,3}|F{1}|[abB]{1,5}|[hkHK]{1,2}|w{1,2}|W{1}|m{1,2}|s{1,2}|[zZOvVxX]{1,4})(?=([^']*'[^']*')*[^']*$)/g;\n// trim patterns after transformations\nvar expPatternTrimmer = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\nfunction matchSkeletonPattern(match, result) {\n var len = match.length;\n switch (match[0]) {\n // Era\n case 'G':\n result.era = len === 4 ? 'long' : len === 5 ? 'narrow' : 'short';\n return '{era}';\n // Year\n case 'y':\n case 'Y':\n case 'u':\n case 'U':\n case 'r':\n result.year = len === 2 ? '2-digit' : 'numeric';\n return '{year}';\n // Quarter\n case 'q':\n case 'Q':\n throw new RangeError('`w/Q` (quarter) patterns are not supported');\n // Month\n case 'M':\n case 'L':\n result.month = ['numeric', '2-digit', 'short', 'long', 'narrow'][len - 1];\n return '{month}';\n // Week\n case 'w':\n case 'W':\n throw new RangeError('`w/W` (week of year) patterns are not supported');\n case 'd':\n result.day = ['numeric', '2-digit'][len - 1];\n return '{day}';\n case 'D':\n case 'F':\n case 'g':\n result.day = 'numeric';\n return '{day}';\n // Weekday\n case 'E':\n result.weekday = len === 4 ? 'long' : len === 5 ? 'narrow' : 'short';\n return '{weekday}';\n case 'e':\n result.weekday = [\n undefined,\n undefined,\n 'short',\n 'long',\n 'narrow',\n 'short',\n ][len - 1];\n return '{weekday}';\n case 'c':\n result.weekday = [\n undefined,\n undefined,\n 'short',\n 'long',\n 'narrow',\n 'short',\n ][len - 1];\n return '{weekday}';\n // Period\n case 'a': // AM, PM\n case 'b': // am, pm, noon, midnight\n case 'B': // flexible day periods\n result.hour12 = true;\n return '{ampm}';\n // Hour\n case 'h':\n result.hour = ['numeric', '2-digit'][len - 1];\n result.hour12 = true;\n return '{hour}';\n case 'H':\n result.hour = ['numeric', '2-digit'][len - 1];\n return '{hour}';\n case 'K':\n result.hour = ['numeric', '2-digit'][len - 1];\n result.hour12 = true;\n return '{hour}';\n case 'k':\n result.hour = ['numeric', '2-digit'][len - 1];\n return '{hour}';\n case 'j':\n case 'J':\n case 'C':\n throw new RangeError('`j/J/C` (hour) patterns are not supported, use `h/H/K/k` instead');\n // Minute\n case 'm':\n result.minute = ['numeric', '2-digit'][len - 1];\n return '{minute}';\n // Second\n case 's':\n result.second = ['numeric', '2-digit'][len - 1];\n return '{second}';\n case 'S':\n case 'A':\n result.second = 'numeric';\n return '{second}';\n // Zone\n case 'z': // 1..3, 4: specific non-location format\n case 'Z': // 1..3, 4, 5: The ISO8601 varios formats\n case 'O': // 1, 4: miliseconds in day short, long\n case 'v': // 1, 4: generic non-location format\n case 'V': // 1, 2, 3, 4: time zone ID or city\n case 'X': // 1, 2, 3, 4: The ISO8601 varios formats\n case 'x': // 1, 2, 3, 4: The ISO8601 varios formats\n result.timeZoneName = len < 4 ? 'short' : 'long';\n return '{timeZoneName}';\n }\n return '';\n}\nfunction skeletonTokenToTable2(c) {\n switch (c) {\n // Era\n case 'G':\n return 'era';\n // Year\n case 'y':\n case 'Y':\n case 'u':\n case 'U':\n case 'r':\n return 'year';\n // Month\n case 'M':\n case 'L':\n return 'month';\n // Day\n case 'd':\n case 'D':\n case 'F':\n case 'g':\n return 'day';\n // Period\n case 'a': // AM, PM\n case 'b': // am, pm, noon, midnight\n case 'B': // flexible day periods\n return 'ampm';\n // Hour\n case 'h':\n case 'H':\n case 'K':\n case 'k':\n return 'hour';\n // Minute\n case 'm':\n return 'minute';\n // Second\n case 's':\n case 'S':\n case 'A':\n return 'second';\n default:\n throw new RangeError('Invalid range pattern token');\n }\n}\nfunction processDateTimePattern(pattern, result) {\n var literals = [];\n // Use skeleton to populate result, but use mapped pattern to populate pattern\n var pattern12 = pattern\n // Double apostrophe\n .replace(/'{2}/g, '{apostrophe}')\n // Apostrophe-escaped\n .replace(/'(.*?)'/g, function (_, literal) {\n literals.push(literal);\n return \"$$\" + (literals.length - 1) + \"$$\";\n })\n .replace(DATE_TIME_REGEX, function (m) { return matchSkeletonPattern(m, result || {}); });\n //Restore literals\n if (literals.length) {\n pattern12 = pattern12\n .replace(/\\$\\$(\\d+)\\$\\$/g, function (_, i) {\n return literals[+i];\n })\n .replace(/\\{apostrophe\\}/g, \"'\");\n }\n // Handle apostrophe-escaped things\n return [\n pattern12\n .replace(/([\\s\\uFEFF\\xA0])\\{ampm\\}([\\s\\uFEFF\\xA0])/, '$1')\n .replace('{ampm}', '')\n .replace(expPatternTrimmer, ''),\n pattern12,\n ];\n}\nexports.processDateTimePattern = processDateTimePattern;\n/**\n * Parse Date time skeleton into Intl.DateTimeFormatOptions\n * Ref: https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table\n * @public\n * @param skeleton skeleton string\n */\nfunction parseDateTimeSkeleton(skeleton, rawPattern, rangePatterns, intervalFormatFallback) {\n if (rawPattern === void 0) { rawPattern = skeleton; }\n var result = {\n pattern: '',\n pattern12: '',\n skeleton: skeleton,\n rawPattern: rawPattern,\n rangePatterns: {},\n rangePatterns12: {},\n };\n if (rangePatterns) {\n for (var k in rangePatterns) {\n var key = skeletonTokenToTable2(k);\n var rawPattern_1 = rangePatterns[k];\n var intervalResult = {\n patternParts: [],\n };\n var _a = processDateTimePattern(rawPattern_1, intervalResult), pattern_1 = _a[0], pattern12_1 = _a[1];\n result.rangePatterns[key] = tslib_1.__assign(tslib_1.__assign({}, intervalResult), { patternParts: splitRangePattern(pattern_1) });\n result.rangePatterns12[key] = tslib_1.__assign(tslib_1.__assign({}, intervalResult), { patternParts: splitRangePattern(pattern12_1) });\n }\n }\n if (intervalFormatFallback) {\n var patternParts = splitFallbackRangePattern(intervalFormatFallback);\n result.rangePatterns.default = {\n patternParts: patternParts,\n };\n result.rangePatterns12.default = {\n patternParts: patternParts,\n };\n }\n // Process skeleton\n skeleton.replace(DATE_TIME_REGEX, function (m) { return matchSkeletonPattern(m, result); });\n var _b = processDateTimePattern(rawPattern), pattern = _b[0], pattern12 = _b[1];\n result.pattern = pattern;\n result.pattern12 = pattern12;\n return result;\n}\nexports.parseDateTimeSkeleton = parseDateTimeSkeleton;\nfunction splitFallbackRangePattern(pattern) {\n var parts = pattern.split(/(\\{[0|1]\\})/g).filter(Boolean);\n return parts.map(function (pattern) {\n switch (pattern) {\n case '{0}':\n return {\n source: ecma402_abstract_1.RangePatternType.startRange,\n pattern: pattern,\n };\n case '{1}':\n return {\n source: ecma402_abstract_1.RangePatternType.endRange,\n pattern: pattern,\n };\n default:\n return {\n source: ecma402_abstract_1.RangePatternType.shared,\n pattern: pattern,\n };\n }\n });\n}\nexports.splitFallbackRangePattern = splitFallbackRangePattern;\nfunction splitRangePattern(pattern) {\n var PART_REGEX = /\\{(.*?)\\}/g;\n // Map of part and index within the string\n var parts = {};\n var match;\n var splitIndex = 0;\n while ((match = PART_REGEX.exec(pattern))) {\n if (!(match[0] in parts)) {\n parts[match[0]] = match.index;\n }\n else {\n splitIndex = match.index;\n break;\n }\n }\n if (!splitIndex) {\n return [\n {\n source: ecma402_abstract_1.RangePatternType.startRange,\n pattern: pattern,\n },\n ];\n }\n return [\n {\n source: ecma402_abstract_1.RangePatternType.startRange,\n pattern: pattern.slice(0, splitIndex),\n },\n {\n source: ecma402_abstract_1.RangePatternType.endRange,\n pattern: pattern.slice(splitIndex),\n },\n ];\n}\nexports.splitRangePattern = splitRangePattern;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.ToDateTimeOptions = void 0;\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\n/**\n * https://tc39.es/ecma402/#sec-todatetimeoptions\n * @param options\n * @param required\n * @param defaults\n */\nfunction ToDateTimeOptions(options, required, defaults) {\n if (options === undefined) {\n options = null;\n }\n else {\n options = ecma402_abstract_1.ToObject(options);\n }\n options = Object.create(options);\n var needDefaults = true;\n if (required === 'date' || required === 'any') {\n for (var _i = 0, _a = ['weekday', 'year', 'month', 'day']; _i < _a.length; _i++) {\n var prop = _a[_i];\n var value = options[prop];\n if (value !== undefined) {\n needDefaults = false;\n }\n }\n }\n if (required === 'time' || required === 'any') {\n for (var _b = 0, _c = [\n 'dayPeriod',\n 'hour',\n 'minute',\n 'second',\n 'fractionalSecondDigits',\n ]; _b < _c.length; _b++) {\n var prop = _c[_b];\n var value = options[prop];\n if (value !== undefined) {\n needDefaults = false;\n }\n }\n }\n if (options.dateStyle !== undefined || options.timeStyle !== undefined) {\n needDefaults = false;\n }\n if (required === 'date' && options.timeStyle) {\n throw new TypeError('Intl.DateTimeFormat date was required but timeStyle was included');\n }\n if (required === 'time' && options.dateStyle) {\n throw new TypeError('Intl.DateTimeFormat time was required but dateStyle was included');\n }\n if (needDefaults && (defaults === 'date' || defaults === 'all')) {\n for (var _d = 0, _e = ['year', 'month', 'day']; _d < _e.length; _d++) {\n var prop = _e[_d];\n options[prop] = 'numeric';\n }\n }\n if (needDefaults && (defaults === 'time' || defaults === 'all')) {\n for (var _f = 0, _g = ['hour', 'minute', 'second']; _f < _g.length; _f++) {\n var prop = _g[_f];\n options[prop] = 'numeric';\n }\n }\n return options;\n}\nexports.ToDateTimeOptions = ToDateTimeOptions;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.PartitionDateTimeRangePattern = void 0;\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nvar ToLocalTime_1 = require(\"./ToLocalTime\");\nvar FormatDateTimePattern_1 = require(\"./FormatDateTimePattern\");\nvar TABLE_2_FIELDS = [\n 'era',\n 'year',\n 'month',\n 'day',\n 'dayPeriod',\n 'ampm',\n 'hour',\n 'minute',\n 'second',\n 'fractionalSecondDigits',\n];\nfunction PartitionDateTimeRangePattern(dtf, x, y, implDetails) {\n x = ecma402_abstract_1.TimeClip(x);\n if (isNaN(x)) {\n throw new RangeError('Invalid start time');\n }\n y = ecma402_abstract_1.TimeClip(y);\n if (isNaN(y)) {\n throw new RangeError('Invalid end time');\n }\n /** IMPL START */\n var getInternalSlots = implDetails.getInternalSlots, tzData = implDetails.tzData;\n var internalSlots = getInternalSlots(dtf);\n /** IMPL END */\n var tm1 = ToLocalTime_1.ToLocalTime(x, \n // @ts-ignore\n internalSlots.calendar, internalSlots.timeZone, { tzData: tzData });\n var tm2 = ToLocalTime_1.ToLocalTime(y, \n // @ts-ignore\n internalSlots.calendar, internalSlots.timeZone, { tzData: tzData });\n var pattern = internalSlots.pattern, rangePatterns = internalSlots.rangePatterns;\n var rangePattern;\n var dateFieldsPracticallyEqual = true;\n var patternContainsLargerDateField = false;\n for (var _i = 0, TABLE_2_FIELDS_1 = TABLE_2_FIELDS; _i < TABLE_2_FIELDS_1.length; _i++) {\n var fieldName = TABLE_2_FIELDS_1[_i];\n if (dateFieldsPracticallyEqual && !patternContainsLargerDateField) {\n var rp = fieldName in rangePatterns ? rangePatterns[fieldName] : undefined;\n if (rangePattern !== undefined && rp === undefined) {\n patternContainsLargerDateField = true;\n }\n else {\n rangePattern = rp;\n if (fieldName === 'ampm') {\n var v1 = tm1.hour;\n var v2 = tm2.hour;\n if ((v1 > 11 && v2 < 11) || (v1 < 11 && v2 > 11)) {\n dateFieldsPracticallyEqual = false;\n }\n }\n else if (fieldName === 'dayPeriod') {\n // TODO\n }\n else if (fieldName === 'fractionalSecondDigits') {\n var fractionalSecondDigits = internalSlots.fractionalSecondDigits;\n if (fractionalSecondDigits === undefined) {\n fractionalSecondDigits = 3;\n }\n var v1 = Math.floor(tm1.millisecond * Math.pow(10, (fractionalSecondDigits - 3)));\n var v2 = Math.floor(tm2.millisecond * Math.pow(10, (fractionalSecondDigits - 3)));\n if (!ecma402_abstract_1.SameValue(v1, v2)) {\n dateFieldsPracticallyEqual = false;\n }\n }\n else {\n var v1 = tm1[fieldName];\n var v2 = tm2[fieldName];\n if (!ecma402_abstract_1.SameValue(v1, v2)) {\n dateFieldsPracticallyEqual = false;\n }\n }\n }\n }\n }\n if (dateFieldsPracticallyEqual) {\n var result_2 = FormatDateTimePattern_1.FormatDateTimePattern(dtf, ecma402_abstract_1.PartitionPattern(pattern), x, implDetails);\n for (var _a = 0, result_1 = result_2; _a < result_1.length; _a++) {\n var r = result_1[_a];\n r.source = ecma402_abstract_1.RangePatternType.shared;\n }\n return result_2;\n }\n var result = [];\n if (rangePattern === undefined) {\n rangePattern = rangePatterns.default;\n /** IMPL DETAILS */\n // Now we have to replace {0} & {1} with actual pattern\n for (var _b = 0, _c = rangePattern.patternParts; _b < _c.length; _b++) {\n var patternPart = _c[_b];\n if (patternPart.pattern === '{0}' || patternPart.pattern === '{1}') {\n patternPart.pattern = pattern;\n }\n }\n }\n for (var _d = 0, _e = rangePattern.patternParts; _d < _e.length; _d++) {\n var rangePatternPart = _e[_d];\n var source = rangePatternPart.source, pattern_1 = rangePatternPart.pattern;\n var z = void 0;\n if (source === ecma402_abstract_1.RangePatternType.startRange ||\n source === ecma402_abstract_1.RangePatternType.shared) {\n z = x;\n }\n else {\n z = y;\n }\n var patternParts = ecma402_abstract_1.PartitionPattern(pattern_1);\n var partResult = FormatDateTimePattern_1.FormatDateTimePattern(dtf, patternParts, z, implDetails);\n for (var _f = 0, partResult_1 = partResult; _f < partResult_1.length; _f++) {\n var r = partResult_1[_f];\n r.source = source;\n }\n result = result.concat(partResult);\n }\n return result;\n}\nexports.PartitionDateTimeRangePattern = PartitionDateTimeRangePattern;\n","/**\n * @license\n * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)\n * MIT-licensed (http://opensource.org/licenses/MIT)\n */\n\n/**\n * @fileoverview Based on PlotKitLayout, but modified to meet the needs of\n * dygraphs.\n */\n\n/*global Dygraph:false */\n\"use strict\";\n\nObject.defineProperty(exports, '__esModule', {\n value: true\n});\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }\n\nvar _dygraphUtils = require('./dygraph-utils');\n\nvar utils = _interopRequireWildcard(_dygraphUtils);\n\n/**\n * Creates a new DygraphLayout object.\n *\n * This class contains all the data to be charted.\n * It uses data coordinates, but also records the chart range (in data\n * coordinates) and hence is able to calculate percentage positions ('In this\n * view, Point A lies 25% down the x-axis.')\n *\n * Two things that it does not do are:\n * 1. Record pixel coordinates for anything.\n * 2. (oddly) determine anything about the layout of chart elements.\n *\n * The naming is a vestige of Dygraph's original PlotKit roots.\n *\n * @constructor\n */\nvar DygraphLayout = function DygraphLayout(dygraph) {\n this.dygraph_ = dygraph;\n /**\n * Array of points for each series.\n *\n * [series index][row index in series] = |Point| structure,\n * where series index refers to visible series only, and the\n * point index is for the reduced set of points for the current\n * zoom region (including one point just outside the window).\n * All points in the same row index share the same X value.\n *\n * @type {Array.>}\n */\n this.points = [];\n this.setNames = [];\n this.annotations = [];\n this.yAxes_ = null;\n\n // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs, but xticks and\n // yticks are outputs. Clean this up.\n this.xTicks_ = null;\n this.yTicks_ = null;\n};\n\n/**\n * Add points for a single series.\n *\n * @param {string} setname Name of the series.\n * @param {Array.} set_xy Points for the series.\n */\nDygraphLayout.prototype.addDataset = function (setname, set_xy) {\n this.points.push(set_xy);\n this.setNames.push(setname);\n};\n\n/**\n * Returns the box which the chart should be drawn in. This is the canvas's\n * box, less space needed for the axis and chart labels.\n *\n * @return {{x: number, y: number, w: number, h: number}}\n */\nDygraphLayout.prototype.getPlotArea = function () {\n return this.area_;\n};\n\n// Compute the box which the chart should be drawn in. This is the canvas's\n// box, less space needed for axis, chart labels, and other plug-ins.\n// NOTE: This should only be called by Dygraph.predraw_().\nDygraphLayout.prototype.computePlotArea = function () {\n var area = {\n // TODO(danvk): per-axis setting.\n x: 0,\n y: 0\n };\n\n area.w = this.dygraph_.width_ - area.x - this.dygraph_.getOption('rightGap');\n area.h = this.dygraph_.height_;\n\n // Let plugins reserve space.\n var e = {\n chart_div: this.dygraph_.graphDiv,\n reserveSpaceLeft: function reserveSpaceLeft(px) {\n var r = {\n x: area.x,\n y: area.y,\n w: px,\n h: area.h\n };\n area.x += px;\n area.w -= px;\n return r;\n },\n reserveSpaceRight: function reserveSpaceRight(px) {\n var r = {\n x: area.x + area.w - px,\n y: area.y,\n w: px,\n h: area.h\n };\n area.w -= px;\n return r;\n },\n reserveSpaceTop: function reserveSpaceTop(px) {\n var r = {\n x: area.x,\n y: area.y,\n w: area.w,\n h: px\n };\n area.y += px;\n area.h -= px;\n return r;\n },\n reserveSpaceBottom: function reserveSpaceBottom(px) {\n var r = {\n x: area.x,\n y: area.y + area.h - px,\n w: area.w,\n h: px\n };\n area.h -= px;\n return r;\n },\n chartRect: function chartRect() {\n return { x: area.x, y: area.y, w: area.w, h: area.h };\n }\n };\n this.dygraph_.cascadeEvents_('layout', e);\n\n this.area_ = area;\n};\n\nDygraphLayout.prototype.setAnnotations = function (ann) {\n // The Dygraph object's annotations aren't parsed. We parse them here and\n // save a copy. If there is no parser, then the user must be using raw format.\n this.annotations = [];\n var parse = this.dygraph_.getOption('xValueParser') || function (x) {\n return x;\n };\n for (var i = 0; i < ann.length; i++) {\n var a = {};\n if (!ann[i].xval && ann[i].x === undefined) {\n console.error(\"Annotations must have an 'x' property\");\n return;\n }\n if (ann[i].icon && !(ann[i].hasOwnProperty('width') && ann[i].hasOwnProperty('height'))) {\n console.error(\"Must set width and height when setting \" + \"annotation.icon property\");\n return;\n }\n utils.update(a, ann[i]);\n if (!a.xval) a.xval = parse(a.x);\n this.annotations.push(a);\n }\n};\n\nDygraphLayout.prototype.setXTicks = function (xTicks) {\n this.xTicks_ = xTicks;\n};\n\n// TODO(danvk): add this to the Dygraph object's API or move it into Layout.\nDygraphLayout.prototype.setYAxes = function (yAxes) {\n this.yAxes_ = yAxes;\n};\n\nDygraphLayout.prototype.evaluate = function () {\n this._xAxis = {};\n this._evaluateLimits();\n this._evaluateLineCharts();\n this._evaluateLineTicks();\n this._evaluateAnnotations();\n};\n\nDygraphLayout.prototype._evaluateLimits = function () {\n var xlimits = this.dygraph_.xAxisRange();\n this._xAxis.minval = xlimits[0];\n this._xAxis.maxval = xlimits[1];\n var xrange = xlimits[1] - xlimits[0];\n this._xAxis.scale = xrange !== 0 ? 1 / xrange : 1.0;\n\n if (this.dygraph_.getOptionForAxis(\"logscale\", 'x')) {\n this._xAxis.xlogrange = utils.log10(this._xAxis.maxval) - utils.log10(this._xAxis.minval);\n this._xAxis.xlogscale = this._xAxis.xlogrange !== 0 ? 1.0 / this._xAxis.xlogrange : 1.0;\n }\n for (var i = 0; i < this.yAxes_.length; i++) {\n var axis = this.yAxes_[i];\n axis.minyval = axis.computedValueRange[0];\n axis.maxyval = axis.computedValueRange[1];\n axis.yrange = axis.maxyval - axis.minyval;\n axis.yscale = axis.yrange !== 0 ? 1.0 / axis.yrange : 1.0;\n\n if (this.dygraph_.getOption(\"logscale\")) {\n axis.ylogrange = utils.log10(axis.maxyval) - utils.log10(axis.minyval);\n axis.ylogscale = axis.ylogrange !== 0 ? 1.0 / axis.ylogrange : 1.0;\n if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) {\n console.error('axis ' + i + ' of graph at ' + axis.g + ' can\\'t be displayed in log scale for range [' + axis.minyval + ' - ' + axis.maxyval + ']');\n }\n }\n }\n};\n\nDygraphLayout.calcXNormal_ = function (value, xAxis, logscale) {\n if (logscale) {\n return (utils.log10(value) - utils.log10(xAxis.minval)) * xAxis.xlogscale;\n } else {\n return (value - xAxis.minval) * xAxis.scale;\n }\n};\n\n/**\n * @param {DygraphAxisType} axis\n * @param {number} value\n * @param {boolean} logscale\n * @return {number}\n */\nDygraphLayout.calcYNormal_ = function (axis, value, logscale) {\n if (logscale) {\n var x = 1.0 - (utils.log10(value) - utils.log10(axis.minyval)) * axis.ylogscale;\n return isFinite(x) ? x : NaN; // shim for v8 issue; see pull request 276\n } else {\n return 1.0 - (value - axis.minyval) * axis.yscale;\n }\n};\n\nDygraphLayout.prototype._evaluateLineCharts = function () {\n var isStacked = this.dygraph_.getOption(\"stackedGraph\");\n var isLogscaleForX = this.dygraph_.getOptionForAxis(\"logscale\", 'x');\n\n for (var setIdx = 0; setIdx < this.points.length; setIdx++) {\n var points = this.points[setIdx];\n var setName = this.setNames[setIdx];\n var connectSeparated = this.dygraph_.getOption('connectSeparatedPoints', setName);\n var axis = this.dygraph_.axisPropertiesForSeries(setName);\n // TODO (konigsberg): use optionsForAxis instead.\n var logscale = this.dygraph_.attributes_.getForSeries(\"logscale\", setName);\n\n for (var j = 0; j < points.length; j++) {\n var point = points[j];\n\n // Range from 0-1 where 0 represents left and 1 represents right.\n point.x = DygraphLayout.calcXNormal_(point.xval, this._xAxis, isLogscaleForX);\n // Range from 0-1 where 0 represents top and 1 represents bottom\n var yval = point.yval;\n if (isStacked) {\n point.y_stacked = DygraphLayout.calcYNormal_(axis, point.yval_stacked, logscale);\n if (yval !== null && !isNaN(yval)) {\n yval = point.yval_stacked;\n }\n }\n if (yval === null) {\n yval = NaN;\n if (!connectSeparated) {\n point.yval = NaN;\n }\n }\n point.y = DygraphLayout.calcYNormal_(axis, yval, logscale);\n }\n\n this.dygraph_.dataHandler_.onLineEvaluated(points, axis, logscale);\n }\n};\n\nDygraphLayout.prototype._evaluateLineTicks = function () {\n var i, tick, label, pos, v, has_tick;\n this.xticks = [];\n for (i = 0; i < this.xTicks_.length; i++) {\n tick = this.xTicks_[i];\n label = tick.label;\n has_tick = !('label_v' in tick);\n v = has_tick ? tick.v : tick.label_v;\n pos = this.dygraph_.toPercentXCoord(v);\n if (pos >= 0.0 && pos < 1.0) {\n this.xticks.push({ pos: pos, label: label, has_tick: has_tick });\n }\n }\n\n this.yticks = [];\n for (i = 0; i < this.yAxes_.length; i++) {\n var axis = this.yAxes_[i];\n for (var j = 0; j < axis.ticks.length; j++) {\n tick = axis.ticks[j];\n label = tick.label;\n has_tick = !('label_v' in tick);\n v = has_tick ? tick.v : tick.label_v;\n pos = this.dygraph_.toPercentYCoord(v, i);\n if (pos > 0.0 && pos <= 1.0) {\n this.yticks.push({ axis: i, pos: pos, label: label, has_tick: has_tick });\n }\n }\n }\n};\n\nDygraphLayout.prototype._evaluateAnnotations = function () {\n // Add the annotations to the point to which they belong.\n // Make a map from (setName, xval) to annotation for quick lookups.\n var i;\n var annotations = {};\n for (i = 0; i < this.annotations.length; i++) {\n var a = this.annotations[i];\n annotations[a.xval + \",\" + a.series] = a;\n }\n\n this.annotated_points = [];\n\n // Exit the function early if there are no annotations.\n if (!this.annotations || !this.annotations.length) {\n return;\n }\n\n // TODO(antrob): loop through annotations not points.\n for (var setIdx = 0; setIdx < this.points.length; setIdx++) {\n var points = this.points[setIdx];\n for (i = 0; i < points.length; i++) {\n var p = points[i];\n var k = p.xval + \",\" + p.name;\n if (k in annotations) {\n p.annotation = annotations[k];\n this.annotated_points.push(p);\n }\n }\n }\n};\n\n/**\n * Convenience function to remove all the data sets from a graph\n */\nDygraphLayout.prototype.removeAllDatasets = function () {\n delete this.points;\n delete this.setNames;\n delete this.setPointsLengths;\n delete this.setPointsOffsets;\n this.points = [];\n this.setNames = [];\n this.setPointsLengths = [];\n this.setPointsOffsets = [];\n};\n\nexports['default'] = DygraphLayout;\nmodule.exports = exports['default'];","/**\n * @license\n * Copyright 2006 Dan Vanderkam (danvdk@gmail.com)\n * MIT-licensed (http://opensource.org/licenses/MIT)\n */\n\n/**\n * @fileoverview Based on PlotKit.CanvasRenderer, but modified to meet the\n * needs of dygraphs.\n *\n * In particular, support for:\n * - grid overlays\n * - error bars\n * - dygraphs attribute system\n */\n\n/**\n * The DygraphCanvasRenderer class does the actual rendering of the chart onto\n * a canvas. It's based on PlotKit.CanvasRenderer.\n * @param {Object} element The canvas to attach to\n * @param {Object} elementContext The 2d context of the canvas (injected so it\n * can be mocked for testing.)\n * @param {Layout} layout The DygraphLayout object for this graph.\n * @constructor\n */\n\n/*global Dygraph:false */\n\"use strict\";\n\nObject.defineProperty(exports, '__esModule', {\n value: true\n});\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }\n\nvar _dygraphUtils = require('./dygraph-utils');\n\nvar utils = _interopRequireWildcard(_dygraphUtils);\n\nvar _dygraph = require('./dygraph');\n\nvar _dygraph2 = _interopRequireDefault(_dygraph);\n\n/**\n * @constructor\n *\n * This gets called when there are \"new points\" to chart. This is generally the\n * case when the underlying data being charted has changed. It is _not_ called\n * in the common case that the user has zoomed or is panning the view.\n *\n * The chart canvas has already been created by the Dygraph object. The\n * renderer simply gets a drawing context.\n *\n * @param {Dygraph} dygraph The chart to which this renderer belongs.\n * @param {HTMLCanvasElement} element The <canvas> DOM element on which to draw.\n * @param {CanvasRenderingContext2D} elementContext The drawing context.\n * @param {DygraphLayout} layout The chart's DygraphLayout object.\n *\n * TODO(danvk): remove the elementContext property.\n */\nvar DygraphCanvasRenderer = function DygraphCanvasRenderer(dygraph, element, elementContext, layout) {\n this.dygraph_ = dygraph;\n\n this.layout = layout;\n this.element = element;\n this.elementContext = elementContext;\n\n this.height = dygraph.height_;\n this.width = dygraph.width_;\n\n // --- check whether everything is ok before we return\n if (!utils.isCanvasSupported(this.element)) {\n throw \"Canvas is not supported.\";\n }\n\n // internal state\n this.area = layout.getPlotArea();\n\n // Set up a clipping area for the canvas (and the interaction canvas).\n // This ensures that we don't overdraw.\n var ctx = this.dygraph_.canvas_ctx_;\n ctx.beginPath();\n ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);\n ctx.clip();\n\n ctx = this.dygraph_.hidden_ctx_;\n ctx.beginPath();\n ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);\n ctx.clip();\n};\n\n/**\n * Clears out all chart content and DOM elements.\n * This is called immediately before render() on every frame, including\n * during zooms and pans.\n * @private\n */\nDygraphCanvasRenderer.prototype.clear = function () {\n this.elementContext.clearRect(0, 0, this.width, this.height);\n};\n\n/**\n * This method is responsible for drawing everything on the chart, including\n * lines, error bars, fills and axes.\n * It is called immediately after clear() on every frame, including during pans\n * and zooms.\n * @private\n */\nDygraphCanvasRenderer.prototype.render = function () {\n // attaches point.canvas{x,y}\n this._updatePoints();\n\n // actually draws the chart.\n this._renderLineChart();\n};\n\n/**\n * Returns a predicate to be used with an iterator, which will\n * iterate over points appropriately, depending on whether\n * connectSeparatedPoints is true. When it's false, the predicate will\n * skip over points with missing yVals.\n */\nDygraphCanvasRenderer._getIteratorPredicate = function (connectSeparatedPoints) {\n return connectSeparatedPoints ? DygraphCanvasRenderer._predicateThatSkipsEmptyPoints : null;\n};\n\nDygraphCanvasRenderer._predicateThatSkipsEmptyPoints = function (array, idx) {\n return array[idx].yval !== null;\n};\n\n/**\n * Draws a line with the styles passed in and calls all the drawPointCallbacks.\n * @param {Object} e The dictionary passed to the plotter function.\n * @private\n */\nDygraphCanvasRenderer._drawStyledLine = function (e, color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize) {\n var g = e.dygraph;\n // TODO(konigsberg): Compute attributes outside this method call.\n var stepPlot = g.getBooleanOption(\"stepPlot\", e.setName);\n\n if (!utils.isArrayLike(strokePattern)) {\n strokePattern = null;\n }\n\n var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName);\n\n var points = e.points;\n var setName = e.setName;\n var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption(\"connectSeparatedPoints\", setName)));\n\n var stroking = strokePattern && strokePattern.length >= 2;\n\n var ctx = e.drawingContext;\n ctx.save();\n if (stroking) {\n if (ctx.setLineDash) ctx.setLineDash(strokePattern);\n }\n\n var pointsOnLine = DygraphCanvasRenderer._drawSeries(e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color);\n DygraphCanvasRenderer._drawPointsOnLine(e, pointsOnLine, drawPointCallback, color, pointSize);\n\n if (stroking) {\n if (ctx.setLineDash) ctx.setLineDash([]);\n }\n\n ctx.restore();\n};\n\n/**\n * This does the actual drawing of lines on the canvas, for just one series.\n * Returns a list of [canvasx, canvasy] pairs for points for which a\n * drawPointCallback should be fired. These include isolated points, or all\n * points if drawPoints=true.\n * @param {Object} e The dictionary passed to the plotter function.\n * @private\n */\nDygraphCanvasRenderer._drawSeries = function (e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) {\n\n var prevCanvasX = null;\n var prevCanvasY = null;\n var nextCanvasY = null;\n var isIsolated; // true if this point is isolated (no line segments)\n var point; // the point being processed in the while loop\n var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.\n var first = true; // the first cycle through the while loop\n\n var ctx = e.drawingContext;\n ctx.beginPath();\n ctx.strokeStyle = color;\n ctx.lineWidth = strokeWidth;\n\n // NOTE: we break the iterator's encapsulation here for about a 25% speedup.\n var arr = iter.array_;\n var limit = iter.end_;\n var predicate = iter.predicate_;\n\n for (var i = iter.start_; i < limit; i++) {\n point = arr[i];\n if (predicate) {\n while (i < limit && !predicate(arr, i)) {\n i++;\n }\n if (i == limit) break;\n point = arr[i];\n }\n\n // FIXME: The 'canvasy != canvasy' test here catches NaN values but the test\n // doesn't catch Infinity values. Could change this to\n // !isFinite(point.canvasy), but I assume it avoids isNaN for performance?\n if (point.canvasy === null || point.canvasy != point.canvasy) {\n if (stepPlot && prevCanvasX !== null) {\n // Draw a horizontal line to the start of the missing data\n ctx.moveTo(prevCanvasX, prevCanvasY);\n ctx.lineTo(point.canvasx, prevCanvasY);\n }\n prevCanvasX = prevCanvasY = null;\n } else {\n isIsolated = false;\n if (drawGapPoints || prevCanvasX === null) {\n iter.nextIdx_ = i;\n iter.next();\n nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;\n\n var isNextCanvasYNullOrNaN = nextCanvasY === null || nextCanvasY != nextCanvasY;\n isIsolated = prevCanvasX === null && isNextCanvasYNullOrNaN;\n if (drawGapPoints) {\n // Also consider a point to be \"isolated\" if it's adjacent to a\n // null point, excluding the graph edges.\n if (!first && prevCanvasX === null || iter.hasNext && isNextCanvasYNullOrNaN) {\n isIsolated = true;\n }\n }\n }\n\n if (prevCanvasX !== null) {\n if (strokeWidth) {\n if (stepPlot) {\n ctx.moveTo(prevCanvasX, prevCanvasY);\n ctx.lineTo(point.canvasx, prevCanvasY);\n }\n\n ctx.lineTo(point.canvasx, point.canvasy);\n }\n } else {\n ctx.moveTo(point.canvasx, point.canvasy);\n }\n if (drawPoints || isIsolated) {\n pointsOnLine.push([point.canvasx, point.canvasy, point.idx]);\n }\n prevCanvasX = point.canvasx;\n prevCanvasY = point.canvasy;\n }\n first = false;\n }\n ctx.stroke();\n return pointsOnLine;\n};\n\n/**\n * This fires the drawPointCallback functions, which draw dots on the points by\n * default. This gets used when the \"drawPoints\" option is set, or when there\n * are isolated points.\n * @param {Object} e The dictionary passed to the plotter function.\n * @private\n */\nDygraphCanvasRenderer._drawPointsOnLine = function (e, pointsOnLine, drawPointCallback, color, pointSize) {\n var ctx = e.drawingContext;\n for (var idx = 0; idx < pointsOnLine.length; idx++) {\n var cb = pointsOnLine[idx];\n ctx.save();\n drawPointCallback.call(e.dygraph, e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]);\n ctx.restore();\n }\n};\n\n/**\n * Attaches canvas coordinates to the points array.\n * @private\n */\nDygraphCanvasRenderer.prototype._updatePoints = function () {\n // Update Points\n // TODO(danvk): here\n //\n // TODO(bhs): this loop is a hot-spot for high-point-count charts. These\n // transformations can be pushed into the canvas via linear transformation\n // matrices.\n // NOTE(danvk): this is trickier than it sounds at first. The transformation\n // needs to be done before the .moveTo() and .lineTo() calls, but must be\n // undone before the .stroke() call to ensure that the stroke width is\n // unaffected. An alternative is to reduce the stroke width in the\n // transformed coordinate space, but you can't specify different values for\n // each dimension (as you can with .scale()). The speedup here is ~12%.\n var sets = this.layout.points;\n for (var i = sets.length; i--;) {\n var points = sets[i];\n for (var j = points.length; j--;) {\n var point = points[j];\n point.canvasx = this.area.w * point.x + this.area.x;\n point.canvasy = this.area.h * point.y + this.area.y;\n }\n }\n};\n\n/**\n * Add canvas Actually draw the lines chart, including error bars.\n *\n * This function can only be called if DygraphLayout's points array has been\n * updated with canvas{x,y} attributes, i.e. by\n * DygraphCanvasRenderer._updatePoints.\n *\n * @param {string=} opt_seriesName when specified, only that series will\n * be drawn. (This is used for expedited redrawing with highlightSeriesOpts)\n * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing\n * context. However, lines are typically drawn on the object's\n * elementContext.\n * @private\n */\nDygraphCanvasRenderer.prototype._renderLineChart = function (opt_seriesName, opt_ctx) {\n var ctx = opt_ctx || this.elementContext;\n var i;\n\n var sets = this.layout.points;\n var setNames = this.layout.setNames;\n var setName;\n\n this.colors = this.dygraph_.colorsMap_;\n\n // Determine which series have specialized plotters.\n var plotter_attr = this.dygraph_.getOption(\"plotter\");\n var plotters = plotter_attr;\n if (!utils.isArrayLike(plotters)) {\n plotters = [plotters];\n }\n\n var setPlotters = {}; // series name -> plotter fn.\n for (i = 0; i < setNames.length; i++) {\n setName = setNames[i];\n var setPlotter = this.dygraph_.getOption(\"plotter\", setName);\n if (setPlotter == plotter_attr) continue; // not specialized.\n\n setPlotters[setName] = setPlotter;\n }\n\n for (i = 0; i < plotters.length; i++) {\n var plotter = plotters[i];\n var is_last = i == plotters.length - 1;\n\n for (var j = 0; j < sets.length; j++) {\n setName = setNames[j];\n if (opt_seriesName && setName != opt_seriesName) continue;\n\n var points = sets[j];\n\n // Only throw in the specialized plotters on the last iteration.\n var p = plotter;\n if (setName in setPlotters) {\n if (is_last) {\n p = setPlotters[setName];\n } else {\n // Don't use the standard plotters in this case.\n continue;\n }\n }\n\n var color = this.colors[setName];\n var strokeWidth = this.dygraph_.getOption(\"strokeWidth\", setName);\n\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = strokeWidth;\n p({\n points: points,\n setName: setName,\n drawingContext: ctx,\n color: color,\n strokeWidth: strokeWidth,\n dygraph: this.dygraph_,\n axis: this.dygraph_.axisPropertiesForSeries(setName),\n plotArea: this.area,\n seriesIndex: j,\n seriesCount: sets.length,\n singleSeriesName: opt_seriesName,\n allSeriesPoints: sets\n });\n ctx.restore();\n }\n }\n};\n\n/**\n * Standard plotters. These may be used by clients via Dygraph.Plotters.\n * See comments there for more details.\n */\nDygraphCanvasRenderer._Plotters = {\n linePlotter: function linePlotter(e) {\n DygraphCanvasRenderer._linePlotter(e);\n },\n\n fillPlotter: function fillPlotter(e) {\n DygraphCanvasRenderer._fillPlotter(e);\n },\n\n errorPlotter: function errorPlotter(e) {\n DygraphCanvasRenderer._errorPlotter(e);\n }\n};\n\n/**\n * Plotter which draws the central lines for a series.\n * @private\n */\nDygraphCanvasRenderer._linePlotter = function (e) {\n var g = e.dygraph;\n var setName = e.setName;\n var strokeWidth = e.strokeWidth;\n\n // TODO(danvk): Check if there's any performance impact of just calling\n // getOption() inside of _drawStyledLine. Passing in so many parameters makes\n // this code a bit nasty.\n var borderWidth = g.getNumericOption(\"strokeBorderWidth\", setName);\n var drawPointCallback = g.getOption(\"drawPointCallback\", setName) || utils.Circles.DEFAULT;\n var strokePattern = g.getOption(\"strokePattern\", setName);\n var drawPoints = g.getBooleanOption(\"drawPoints\", setName);\n var pointSize = g.getNumericOption(\"pointSize\", setName);\n\n if (borderWidth && strokeWidth) {\n DygraphCanvasRenderer._drawStyledLine(e, g.getOption(\"strokeBorderColor\", setName), strokeWidth + 2 * borderWidth, strokePattern, drawPoints, drawPointCallback, pointSize);\n }\n\n DygraphCanvasRenderer._drawStyledLine(e, e.color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize);\n};\n\n/**\n * Draws the shaded error bars/confidence intervals for each series.\n * This happens before the center lines are drawn, since the center lines\n * need to be drawn on top of the error bars for all series.\n * @private\n */\nDygraphCanvasRenderer._errorPlotter = function (e) {\n var g = e.dygraph;\n var setName = e.setName;\n var errorBars = g.getBooleanOption(\"errorBars\") || g.getBooleanOption(\"customBars\");\n if (!errorBars) return;\n\n var fillGraph = g.getBooleanOption(\"fillGraph\", setName);\n if (fillGraph) {\n console.warn(\"Can't use fillGraph option with error bars\");\n }\n\n var ctx = e.drawingContext;\n var color = e.color;\n var fillAlpha = g.getNumericOption('fillAlpha', setName);\n var stepPlot = g.getBooleanOption(\"stepPlot\", setName);\n var points = e.points;\n\n var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption(\"connectSeparatedPoints\", setName)));\n\n var newYs;\n\n // setup graphics context\n var prevX = NaN;\n var prevY = NaN;\n var prevYs = [-1, -1];\n // should be same color as the lines but only 15% opaque.\n var rgb = utils.toRGB_(color);\n var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';\n ctx.fillStyle = err_color;\n ctx.beginPath();\n\n var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(x) {\n return x === null || x === undefined || isNaN(x);\n };\n\n while (iter.hasNext) {\n var point = iter.next();\n if (!stepPlot && isNullUndefinedOrNaN(point.y) || stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY)) {\n prevX = NaN;\n continue;\n }\n\n newYs = [point.y_bottom, point.y_top];\n if (stepPlot) {\n prevY = point.y;\n }\n\n // The documentation specifically disallows nulls inside the point arrays,\n // but in case it happens we should do something sensible.\n if (isNaN(newYs[0])) newYs[0] = point.y;\n if (isNaN(newYs[1])) newYs[1] = point.y;\n\n newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y;\n newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y;\n if (!isNaN(prevX)) {\n if (stepPlot) {\n ctx.moveTo(prevX, prevYs[0]);\n ctx.lineTo(point.canvasx, prevYs[0]);\n ctx.lineTo(point.canvasx, prevYs[1]);\n } else {\n ctx.moveTo(prevX, prevYs[0]);\n ctx.lineTo(point.canvasx, newYs[0]);\n ctx.lineTo(point.canvasx, newYs[1]);\n }\n ctx.lineTo(prevX, prevYs[1]);\n ctx.closePath();\n }\n prevYs = newYs;\n prevX = point.canvasx;\n }\n ctx.fill();\n};\n\n/**\n * Proxy for CanvasRenderingContext2D which drops moveTo/lineTo calls which are\n * superfluous. It accumulates all movements which haven't changed the x-value\n * and only applies the two with the most extreme y-values.\n *\n * Calls to lineTo/moveTo must have non-decreasing x-values.\n */\nDygraphCanvasRenderer._fastCanvasProxy = function (context) {\n var pendingActions = []; // array of [type, x, y] tuples\n var lastRoundedX = null;\n var lastFlushedX = null;\n\n var LINE_TO = 1,\n MOVE_TO = 2;\n\n var actionCount = 0; // number of moveTos and lineTos passed to context.\n\n // Drop superfluous motions\n // Assumes all pendingActions have the same (rounded) x-value.\n var compressActions = function compressActions(opt_losslessOnly) {\n if (pendingActions.length <= 1) return;\n\n // Lossless compression: drop inconsequential moveTos.\n for (var i = pendingActions.length - 1; i > 0; i--) {\n var action = pendingActions[i];\n if (action[0] == MOVE_TO) {\n var prevAction = pendingActions[i - 1];\n if (prevAction[1] == action[1] && prevAction[2] == action[2]) {\n pendingActions.splice(i, 1);\n }\n }\n }\n\n // Lossless compression: ... drop consecutive moveTos ...\n for (var i = 0; i < pendingActions.length - 1;) /* incremented internally */{\n var action = pendingActions[i];\n if (action[0] == MOVE_TO && pendingActions[i + 1][0] == MOVE_TO) {\n pendingActions.splice(i, 1);\n } else {\n i++;\n }\n }\n\n // Lossy compression: ... drop all but the extreme y-values ...\n if (pendingActions.length > 2 && !opt_losslessOnly) {\n // keep an initial moveTo, but drop all others.\n var startIdx = 0;\n if (pendingActions[0][0] == MOVE_TO) startIdx++;\n var minIdx = null,\n maxIdx = null;\n for (var i = startIdx; i < pendingActions.length; i++) {\n var action = pendingActions[i];\n if (action[0] != LINE_TO) continue;\n if (minIdx === null && maxIdx === null) {\n minIdx = i;\n maxIdx = i;\n } else {\n var y = action[2];\n if (y < pendingActions[minIdx][2]) {\n minIdx = i;\n } else if (y > pendingActions[maxIdx][2]) {\n maxIdx = i;\n }\n }\n }\n var minAction = pendingActions[minIdx],\n maxAction = pendingActions[maxIdx];\n pendingActions.splice(startIdx, pendingActions.length - startIdx);\n if (minIdx < maxIdx) {\n pendingActions.push(minAction);\n pendingActions.push(maxAction);\n } else if (minIdx > maxIdx) {\n pendingActions.push(maxAction);\n pendingActions.push(minAction);\n } else {\n pendingActions.push(minAction);\n }\n }\n };\n\n var flushActions = function flushActions(opt_noLossyCompression) {\n compressActions(opt_noLossyCompression);\n for (var i = 0, len = pendingActions.length; i < len; i++) {\n var action = pendingActions[i];\n if (action[0] == LINE_TO) {\n context.lineTo(action[1], action[2]);\n } else if (action[0] == MOVE_TO) {\n context.moveTo(action[1], action[2]);\n }\n }\n if (pendingActions.length) {\n lastFlushedX = pendingActions[pendingActions.length - 1][1];\n }\n actionCount += pendingActions.length;\n pendingActions = [];\n };\n\n var addAction = function addAction(action, x, y) {\n var rx = Math.round(x);\n if (lastRoundedX === null || rx != lastRoundedX) {\n // if there are large gaps on the x-axis, it's essential to keep the\n // first and last point as well.\n var hasGapOnLeft = lastRoundedX - lastFlushedX > 1,\n hasGapOnRight = rx - lastRoundedX > 1,\n hasGap = hasGapOnLeft || hasGapOnRight;\n flushActions(hasGap);\n lastRoundedX = rx;\n }\n pendingActions.push([action, x, y]);\n };\n\n return {\n moveTo: function moveTo(x, y) {\n addAction(MOVE_TO, x, y);\n },\n lineTo: function lineTo(x, y) {\n addAction(LINE_TO, x, y);\n },\n\n // for major operations like stroke/fill, we skip compression to ensure\n // that there are no artifacts at the right edge.\n stroke: function stroke() {\n flushActions(true);context.stroke();\n },\n fill: function fill() {\n flushActions(true);context.fill();\n },\n beginPath: function beginPath() {\n flushActions(true);context.beginPath();\n },\n closePath: function closePath() {\n flushActions(true);context.closePath();\n },\n\n _count: function _count() {\n return actionCount;\n }\n };\n};\n\n/**\n * Draws the shaded regions when \"fillGraph\" is set. Not to be confused with\n * error bars.\n *\n * For stacked charts, it's more convenient to handle all the series\n * simultaneously. So this plotter plots all the points on the first series\n * it's asked to draw, then ignores all the other series.\n *\n * @private\n */\nDygraphCanvasRenderer._fillPlotter = function (e) {\n // Skip if we're drawing a single series for interactive highlight overlay.\n if (e.singleSeriesName) return;\n\n // We'll handle all the series at once, not one-by-one.\n if (e.seriesIndex !== 0) return;\n\n var g = e.dygraph;\n var setNames = g.getLabels().slice(1); // remove x-axis\n\n // getLabels() includes names for invisible series, which are not included in\n // allSeriesPoints. We remove those to make the two match.\n // TODO(danvk): provide a simpler way to get this information.\n for (var i = setNames.length; i >= 0; i--) {\n if (!g.visibility()[i]) setNames.splice(i, 1);\n }\n\n var anySeriesFilled = (function () {\n for (var i = 0; i < setNames.length; i++) {\n if (g.getBooleanOption(\"fillGraph\", setNames[i])) return true;\n }\n return false;\n })();\n\n if (!anySeriesFilled) return;\n\n var area = e.plotArea;\n var sets = e.allSeriesPoints;\n var setCount = sets.length;\n\n var stackedGraph = g.getBooleanOption(\"stackedGraph\");\n var colors = g.getColors();\n\n // For stacked graphs, track the baseline for filling.\n //\n // The filled areas below graph lines are trapezoids with two\n // vertical edges. The top edge is the line segment being drawn, and\n // the baseline is the bottom edge. Each baseline corresponds to the\n // top line segment from the previous stacked line. In the case of\n // step plots, the trapezoids are rectangles.\n var baseline = {};\n var currBaseline;\n var prevStepPlot; // for different line drawing modes (line/step) per series\n\n // Helper function to trace a line back along the baseline.\n var traceBackPath = function traceBackPath(ctx, baselineX, baselineY, pathBack) {\n ctx.lineTo(baselineX, baselineY);\n if (stackedGraph) {\n for (var i = pathBack.length - 1; i >= 0; i--) {\n var pt = pathBack[i];\n ctx.lineTo(pt[0], pt[1]);\n }\n }\n };\n\n // process sets in reverse order (needed for stacked graphs)\n for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {\n var ctx = e.drawingContext;\n var setName = setNames[setIdx];\n if (!g.getBooleanOption('fillGraph', setName)) continue;\n\n var fillAlpha = g.getNumericOption('fillAlpha', setName);\n var stepPlot = g.getBooleanOption('stepPlot', setName);\n var color = colors[setIdx];\n var axis = g.axisPropertiesForSeries(setName);\n var axisY = 1.0 + axis.minyval * axis.yscale;\n if (axisY < 0.0) axisY = 0.0;else if (axisY > 1.0) axisY = 1.0;\n axisY = area.h * axisY + area.y;\n\n var points = sets[setIdx];\n var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption(\"connectSeparatedPoints\", setName)));\n\n // setup graphics context\n var prevX = NaN;\n var prevYs = [-1, -1];\n var newYs;\n // should be same color as the lines but only 15% opaque.\n var rgb = utils.toRGB_(color);\n var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';\n ctx.fillStyle = err_color;\n ctx.beginPath();\n var last_x,\n is_first = true;\n\n // If the point density is high enough, dropping segments on their way to\n // the canvas justifies the overhead of doing so.\n if (points.length > 2 * g.width_ || _dygraph2['default'].FORCE_FAST_PROXY) {\n ctx = DygraphCanvasRenderer._fastCanvasProxy(ctx);\n }\n\n // For filled charts, we draw points from left to right, then back along\n // the x-axis to complete a shape for filling.\n // For stacked plots, this \"back path\" is a more complex shape. This array\n // stores the [x, y] values needed to trace that shape.\n var pathBack = [];\n\n // TODO(danvk): there are a lot of options at play in this loop.\n // The logic would be much clearer if some (e.g. stackGraph and\n // stepPlot) were split off into separate sub-plotters.\n var point;\n while (iter.hasNext) {\n point = iter.next();\n if (!utils.isOK(point.y) && !stepPlot) {\n traceBackPath(ctx, prevX, prevYs[1], pathBack);\n pathBack = [];\n prevX = NaN;\n if (point.y_stacked !== null && !isNaN(point.y_stacked)) {\n baseline[point.canvasx] = area.h * point.y_stacked + area.y;\n }\n continue;\n }\n if (stackedGraph) {\n if (!is_first && last_x == point.xval) {\n continue;\n } else {\n is_first = false;\n last_x = point.xval;\n }\n\n currBaseline = baseline[point.canvasx];\n var lastY;\n if (currBaseline === undefined) {\n lastY = axisY;\n } else {\n if (prevStepPlot) {\n lastY = currBaseline[0];\n } else {\n lastY = currBaseline;\n }\n }\n newYs = [point.canvasy, lastY];\n\n if (stepPlot) {\n // Step plots must keep track of the top and bottom of\n // the baseline at each point.\n if (prevYs[0] === -1) {\n baseline[point.canvasx] = [point.canvasy, axisY];\n } else {\n baseline[point.canvasx] = [point.canvasy, prevYs[0]];\n }\n } else {\n baseline[point.canvasx] = point.canvasy;\n }\n } else {\n if (isNaN(point.canvasy) && stepPlot) {\n newYs = [area.y + area.h, axisY];\n } else {\n newYs = [point.canvasy, axisY];\n }\n }\n if (!isNaN(prevX)) {\n // Move to top fill point\n if (stepPlot) {\n ctx.lineTo(point.canvasx, prevYs[0]);\n ctx.lineTo(point.canvasx, newYs[0]);\n } else {\n ctx.lineTo(point.canvasx, newYs[0]);\n }\n\n // Record the baseline for the reverse path.\n if (stackedGraph) {\n pathBack.push([prevX, prevYs[1]]);\n if (prevStepPlot && currBaseline) {\n // Draw to the bottom of the baseline\n pathBack.push([point.canvasx, currBaseline[1]]);\n } else {\n pathBack.push([point.canvasx, newYs[1]]);\n }\n }\n } else {\n ctx.moveTo(point.canvasx, newYs[1]);\n ctx.lineTo(point.canvasx, newYs[0]);\n }\n prevYs = newYs;\n prevX = point.canvasx;\n }\n prevStepPlot = stepPlot;\n if (newYs && point) {\n traceBackPath(ctx, point.canvasx, newYs[1], pathBack);\n pathBack = [];\n }\n ctx.fill();\n }\n};\n\nexports['default'] = DygraphCanvasRenderer;\nmodule.exports = exports['default'];","'use strict';\n\nObject.defineProperty(exports, '__esModule', {\n value: true\n});\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }\n\nvar _dygraphTickers = require('./dygraph-tickers');\n\nvar DygraphTickers = _interopRequireWildcard(_dygraphTickers);\n\nvar _dygraphInteractionModel = require('./dygraph-interaction-model');\n\nvar _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel);\n\nvar _dygraphCanvas = require('./dygraph-canvas');\n\nvar _dygraphCanvas2 = _interopRequireDefault(_dygraphCanvas);\n\nvar _dygraphUtils = require('./dygraph-utils');\n\nvar utils = _interopRequireWildcard(_dygraphUtils);\n\n// Default attribute values.\nvar DEFAULT_ATTRS = {\n highlightCircleSize: 3,\n highlightSeriesOpts: null,\n highlightSeriesBackgroundAlpha: 0.5,\n highlightSeriesBackgroundColor: 'rgb(255, 255, 255)',\n\n labelsSeparateLines: false,\n labelsShowZeroValues: true,\n labelsKMB: false,\n labelsKMG2: false,\n showLabelsOnHighlight: true,\n\n digitsAfterDecimal: 2,\n maxNumberWidth: 6,\n sigFigs: null,\n\n strokeWidth: 1.0,\n strokeBorderWidth: 0,\n strokeBorderColor: \"white\",\n\n axisTickSize: 3,\n axisLabelFontSize: 14,\n rightGap: 5,\n\n showRoller: false,\n xValueParser: undefined,\n\n delimiter: ',',\n\n sigma: 2.0,\n errorBars: false,\n fractions: false,\n wilsonInterval: true, // only relevant if fractions is true\n customBars: false,\n fillGraph: false,\n fillAlpha: 0.15,\n connectSeparatedPoints: false,\n\n stackedGraph: false,\n stackedGraphNaNFill: 'all',\n hideOverlayOnMouseOut: true,\n\n legend: 'onmouseover',\n stepPlot: false,\n xRangePad: 0,\n yRangePad: null,\n drawAxesAtZero: false,\n\n // Sizes of the various chart labels.\n titleHeight: 28,\n xLabelHeight: 18,\n yLabelWidth: 18,\n\n axisLineColor: \"black\",\n axisLineWidth: 0.3,\n gridLineWidth: 0.3,\n axisLabelWidth: 50,\n gridLineColor: \"rgb(128,128,128)\",\n\n interactionModel: _dygraphInteractionModel2['default'].defaultModel,\n animatedZooms: false, // (for now)\n\n // Range selector options\n showRangeSelector: false,\n rangeSelectorHeight: 40,\n rangeSelectorPlotStrokeColor: \"#808FAB\",\n rangeSelectorPlotFillGradientColor: \"white\",\n rangeSelectorPlotFillColor: \"#A7B1C4\",\n rangeSelectorBackgroundStrokeColor: \"gray\",\n rangeSelectorBackgroundLineWidth: 1,\n rangeSelectorPlotLineWidth: 1.5,\n rangeSelectorForegroundStrokeColor: \"black\",\n rangeSelectorForegroundLineWidth: 1,\n rangeSelectorAlpha: 0.6,\n showInRangeSelector: null,\n\n // The ordering here ensures that central lines always appear above any\n // fill bars/error bars.\n plotter: [_dygraphCanvas2['default']._fillPlotter, _dygraphCanvas2['default']._errorPlotter, _dygraphCanvas2['default']._linePlotter],\n\n plugins: [],\n\n // per-axis options\n axes: {\n x: {\n pixelsPerLabel: 70,\n axisLabelWidth: 60,\n axisLabelFormatter: utils.dateAxisLabelFormatter,\n valueFormatter: utils.dateValueFormatter,\n drawGrid: true,\n drawAxis: true,\n independentTicks: true,\n ticker: DygraphTickers.dateTicker\n },\n y: {\n axisLabelWidth: 50,\n pixelsPerLabel: 30,\n valueFormatter: utils.numberValueFormatter,\n axisLabelFormatter: utils.numberAxisLabelFormatter,\n drawGrid: true,\n drawAxis: true,\n independentTicks: true,\n ticker: DygraphTickers.numericTicks\n },\n y2: {\n axisLabelWidth: 50,\n pixelsPerLabel: 30,\n valueFormatter: utils.numberValueFormatter,\n axisLabelFormatter: utils.numberAxisLabelFormatter,\n drawAxis: true, // only applies when there are two axes of data.\n drawGrid: false,\n independentTicks: false,\n ticker: DygraphTickers.numericTicks\n }\n }\n};\n\nexports['default'] = DEFAULT_ATTRS;\nmodule.exports = exports['default'];","/**\n * @license\n * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)\n * MIT-licensed (http://opensource.org/licenses/MIT)\n */\n\n\"use strict\";\n\nObject.defineProperty(exports, '__esModule', {\n value: true\n});\nvar OPTIONS_REFERENCE = null;\n\n// For \"production\" code, this gets removed by uglifyjs.\nif (typeof process !== 'undefined') {\n if (process.env.NODE_ENV != 'production') {\n\n // NOTE: in addition to parsing as JS, this snippet is expected to be valid\n // JSON. This assumption cannot be checked in JS, but it will be checked when\n // documentation is generated by the generate-documentation.py script. For the\n // most part, this just means that you should always use double quotes.\n OPTIONS_REFERENCE = // \n {\n \"xValueParser\": {\n \"default\": \"parseFloat() or Date.parse()*\",\n \"labels\": [\"CSV parsing\"],\n \"type\": \"function(str) -> number\",\n \"description\": \"A function which parses x-values (i.e. the dependent series). Must return a number, even when the values are dates. In this case, millis since epoch are used. This is used primarily for parsing CSV data. *=Dygraphs is slightly more accepting in the dates which it will parse. See code for details.\"\n },\n \"stackedGraph\": {\n \"default\": \"false\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"boolean\",\n \"description\": \"If set, stack series on top of one another rather than drawing them independently. The first series specified in the input data will wind up on top of the chart and the last will be on bottom. NaN values are drawn as white areas without a line on top, see stackedGraphNaNFill for details.\"\n },\n \"stackedGraphNaNFill\": {\n \"default\": \"all\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"string\",\n \"description\": \"Controls handling of NaN values inside a stacked graph. NaN values are interpolated/extended for stacking purposes, but the actual point value remains NaN in the legend display. Valid option values are \\\"all\\\" (interpolate internally, repeat leftmost and rightmost value as needed), \\\"inside\\\" (interpolate internally only, use zero outside leftmost and rightmost value), and \\\"none\\\" (treat NaN as zero everywhere).\"\n },\n \"pointSize\": {\n \"default\": \"1\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"integer\",\n \"description\": \"The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is \\\"isolated\\\", i.e. there is a missing point on either side of it. This also controls the size of those dots.\"\n },\n \"drawPoints\": {\n \"default\": \"false\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"boolean\",\n \"description\": \"Draw a small dot at each point, in addition to a line going through the point. This makes the individual data points easier to see, but can increase visual clutter in the chart. The small dot can be replaced with a custom rendering by supplying a drawPointCallback.\"\n },\n \"drawGapEdgePoints\": {\n \"default\": \"false\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"boolean\",\n \"description\": \"Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities.\"\n },\n \"drawPointCallback\": {\n \"default\": \"null\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"function(g, seriesName, canvasContext, cx, cy, color, pointSize)\",\n \"parameters\": [[\"g\", \"the reference graph\"], [\"seriesName\", \"the name of the series\"], [\"canvasContext\", \"the canvas to draw on\"], [\"cx\", \"center x coordinate\"], [\"cy\", \"center y coordinate\"], [\"color\", \"series color\"], [\"pointSize\", \"the radius of the image.\"], [\"idx\", \"the row-index of the point in the data.\"]],\n \"description\": \"Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy). Also see drawHighlightPointCallback\"\n },\n \"height\": {\n \"default\": \"320\",\n \"labels\": [\"Overall display\"],\n \"type\": \"integer\",\n \"description\": \"Height, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored.\"\n },\n \"zoomCallback\": {\n \"default\": \"null\",\n \"labels\": [\"Callbacks\"],\n \"type\": \"function(minDate, maxDate, yRanges)\",\n \"parameters\": [[\"minDate\", \"milliseconds since epoch\"], [\"maxDate\", \"milliseconds since epoch.\"], [\"yRanges\", \"is an array of [bottom, top] pairs, one for each y-axis.\"]],\n \"description\": \"A function to call when the zoom window is changed (either by zooming in or out). When animatedZooms is set, zoomCallback is called once at the end of the transition (it will not be called for intermediate frames).\"\n },\n \"pointClickCallback\": {\n \"snippet\": \"function(e, point){
  alert(point);
}\",\n \"default\": \"null\",\n \"labels\": [\"Callbacks\", \"Interactive Elements\"],\n \"type\": \"function(e, point)\",\n \"parameters\": [[\"e\", \"the event object for the click\"], [\"point\", \"the point that was clicked See Point properties for details\"]],\n \"description\": \"A function to call when a data point is clicked. and the point that was clicked.\"\n },\n \"color\": {\n \"default\": \"(see description)\",\n \"labels\": [\"Data Series Colors\"],\n \"type\": \"string\",\n \"example\": \"red\",\n \"description\": \"A per-series color definition. Used in conjunction with, and overrides, the colors option.\"\n },\n \"colors\": {\n \"default\": \"(see description)\",\n \"labels\": [\"Data Series Colors\"],\n \"type\": \"array\",\n \"example\": \"['red', '#00FF00']\",\n \"description\": \"List of colors for the data series. These can be of the form \\\"#AABBCC\\\" or \\\"rgb(255,100,200)\\\" or \\\"yellow\\\", etc. If not specified, equally-spaced points around a color wheel are used. Overridden by the 'color' option.\"\n },\n \"connectSeparatedPoints\": {\n \"default\": \"false\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"boolean\",\n \"description\": \"Usually, when Dygraphs encounters a missing value in a data series, it interprets this as a gap and draws it as such. If, instead, the missing values represents an x-value for which only a different series has data, then you'll want to connect the dots by setting this to true. To explicitly include a gap with this option set, use a value of NaN.\"\n },\n \"highlightCallback\": {\n \"default\": \"null\",\n \"labels\": [\"Callbacks\"],\n \"type\": \"function(event, x, points, row, seriesName)\",\n \"description\": \"When set, this callback gets called every time a new point is highlighted.\",\n \"parameters\": [[\"event\", \"the JavaScript mousemove event\"], [\"x\", \"the x-coordinate of the highlighted points\"], [\"points\", \"an array of highlighted points: [ {name: 'series', yval: y-value}, … ]\"], [\"row\", \"integer index of the highlighted row in the data table, starting from 0\"], [\"seriesName\", \"name of the highlighted series, only present if highlightSeriesOpts is set.\"]]\n },\n \"drawHighlightPointCallback\": {\n \"default\": \"null\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"function(g, seriesName, canvasContext, cx, cy, color, pointSize)\",\n \"parameters\": [[\"g\", \"the reference graph\"], [\"seriesName\", \"the name of the series\"], [\"canvasContext\", \"the canvas to draw on\"], [\"cx\", \"center x coordinate\"], [\"cy\", \"center y coordinate\"], [\"color\", \"series color\"], [\"pointSize\", \"the radius of the image.\"], [\"idx\", \"the row-index of the point in the data.\"]],\n \"description\": \"Draw a custom item when a point is highlighted. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see drawPointCallback\"\n },\n \"highlightSeriesOpts\": {\n \"default\": \"null\",\n \"labels\": [\"Interactive Elements\"],\n \"type\": \"Object\",\n \"description\": \"When set, the options from this object are applied to the timeseries closest to the mouse pointer for interactive highlighting. See also 'highlightCallback'. Example: highlightSeriesOpts: { strokeWidth: 3 }.\"\n },\n \"highlightSeriesBackgroundAlpha\": {\n \"default\": \"0.5\",\n \"labels\": [\"Interactive Elements\"],\n \"type\": \"float\",\n \"description\": \"Fade the background while highlighting series. 1=fully visible background (disable fading), 0=hiddden background (show highlighted series only).\"\n },\n \"highlightSeriesBackgroundColor\": {\n \"default\": \"rgb(255, 255, 255)\",\n \"labels\": [\"Interactive Elements\"],\n \"type\": \"string\",\n \"description\": \"Sets the background color used to fade out the series in conjunction with 'highlightSeriesBackgroundAlpha'.\"\n },\n \"includeZero\": {\n \"default\": \"false\",\n \"labels\": [\"Axis display\"],\n \"type\": \"boolean\",\n \"description\": \"Usually, dygraphs will use the range of the data plus some padding to set the range of the y-axis. If this option is set, the y-axis will always include zero, typically as the lowest value. This can be used to avoid exaggerating the variance in the data\"\n },\n \"rollPeriod\": {\n \"default\": \"1\",\n \"labels\": [\"Error Bars\", \"Rolling Averages\"],\n \"type\": \"integer >= 1\",\n \"description\": \"Number of days over which to average data. Discussed extensively above.\"\n },\n \"unhighlightCallback\": {\n \"default\": \"null\",\n \"labels\": [\"Callbacks\"],\n \"type\": \"function(event)\",\n \"parameters\": [[\"event\", \"the mouse event\"]],\n \"description\": \"When set, this callback gets called every time the user stops highlighting any point by mousing out of the graph.\"\n },\n \"axisTickSize\": {\n \"default\": \"3.0\",\n \"labels\": [\"Axis display\"],\n \"type\": \"number\",\n \"description\": \"The size of the line to display next to each tick mark on x- or y-axes.\"\n },\n \"labelsSeparateLines\": {\n \"default\": \"false\",\n \"labels\": [\"Legend\"],\n \"type\": \"boolean\",\n \"description\": \"Put <br/> between lines in the label string. Often used in conjunction with labelsDiv.\"\n },\n \"valueFormatter\": {\n \"default\": \"Depends on the type of your data.\",\n \"labels\": [\"Legend\", \"Value display/formatting\"],\n \"type\": \"function(num or millis, opts, seriesName, dygraph, row, col)\",\n \"description\": \"Function to provide a custom display format for the values displayed on mouseover. This does not affect the values that appear on tick marks next to the axes. To format those, see axisLabelFormatter. This is usually set on a per-axis basis. .\",\n \"parameters\": [[\"num_or_millis\", \"The value to be formatted. This is always a number. For date axes, it's millis since epoch. You can call new Date(millis) to get a Date object.\"], [\"opts\", \"This is a function you can call to access various options (e.g. opts('labelsKMB')). It returns per-axis values for the option when available.\"], [\"seriesName\", \"The name of the series from which the point came, e.g. 'X', 'Y', 'A', etc.\"], [\"dygraph\", \"The dygraph object for which the formatting is being done\"], [\"row\", \"The row of the data from which this point comes. g.getValue(row, 0) will return the x-value for this point.\"], [\"col\", \"The column of the data from which this point comes. g.getValue(row, col) will return the original y-value for this point. This can be used to get the full confidence interval for the point, or access un-rolled values for the point.\"]]\n },\n \"annotationMouseOverHandler\": {\n \"default\": \"null\",\n \"labels\": [\"Annotations\"],\n \"type\": \"function(annotation, point, dygraph, event)\",\n \"description\": \"If provided, this function is called whenever the user mouses over an annotation.\"\n },\n \"annotationMouseOutHandler\": {\n \"default\": \"null\",\n \"labels\": [\"Annotations\"],\n \"type\": \"function(annotation, point, dygraph, event)\",\n \"parameters\": [[\"annotation\", \"the annotation left\"], [\"point\", \"the point associated with the annotation\"], [\"dygraph\", \"the reference graph\"], [\"event\", \"the mouse event\"]],\n \"description\": \"If provided, this function is called whenever the user mouses out of an annotation.\"\n },\n \"annotationClickHandler\": {\n \"default\": \"null\",\n \"labels\": [\"Annotations\"],\n \"type\": \"function(annotation, point, dygraph, event)\",\n \"parameters\": [[\"annotation\", \"the annotation left\"], [\"point\", \"the point associated with the annotation\"], [\"dygraph\", \"the reference graph\"], [\"event\", \"the mouse event\"]],\n \"description\": \"If provided, this function is called whenever the user clicks on an annotation.\"\n },\n \"annotationDblClickHandler\": {\n \"default\": \"null\",\n \"labels\": [\"Annotations\"],\n \"type\": \"function(annotation, point, dygraph, event)\",\n \"parameters\": [[\"annotation\", \"the annotation left\"], [\"point\", \"the point associated with the annotation\"], [\"dygraph\", \"the reference graph\"], [\"event\", \"the mouse event\"]],\n \"description\": \"If provided, this function is called whenever the user double-clicks on an annotation.\"\n },\n \"drawCallback\": {\n \"default\": \"null\",\n \"labels\": [\"Callbacks\"],\n \"type\": \"function(dygraph, is_initial)\",\n \"parameters\": [[\"dygraph\", \"The graph being drawn\"], [\"is_initial\", \"True if this is the initial draw, false for subsequent draws.\"]],\n \"description\": \"When set, this callback gets called every time the dygraph is drawn. This includes the initial draw, after zooming and repeatedly while panning.\"\n },\n \"labelsKMG2\": {\n \"default\": \"false\",\n \"labels\": [\"Value display/formatting\"],\n \"type\": \"boolean\",\n \"description\": \"Show k/M/G for kilo/Mega/Giga on y-axis. This is different than labelsKMB in that it uses base 2, not 10.\"\n },\n \"delimiter\": {\n \"default\": \",\",\n \"labels\": [\"CSV parsing\"],\n \"type\": \"string\",\n \"description\": \"The delimiter to look for when separating fields of a CSV file. Setting this to a tab is not usually necessary, since tab-delimited data is auto-detected.\"\n },\n \"axisLabelFontSize\": {\n \"default\": \"14\",\n \"labels\": [\"Axis display\"],\n \"type\": \"integer\",\n \"description\": \"Size of the font (in pixels) to use in the axis labels, both x- and y-axis.\"\n },\n \"underlayCallback\": {\n \"default\": \"null\",\n \"labels\": [\"Callbacks\"],\n \"type\": \"function(context, area, dygraph)\",\n \"parameters\": [[\"context\", \"the canvas drawing context on which to draw\"], [\"area\", \"An object with {x,y,w,h} properties describing the drawing area.\"], [\"dygraph\", \"the reference graph\"]],\n \"description\": \"When set, this callback gets called before the chart is drawn. It details on how to use this.\"\n },\n \"width\": {\n \"default\": \"480\",\n \"labels\": [\"Overall display\"],\n \"type\": \"integer\",\n \"description\": \"Width, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored.\"\n },\n \"pixelRatio\": {\n \"default\": \"(devicePixelRatio / context.backingStoreRatio)\",\n \"labels\": [\"Overall display\"],\n \"type\": \"float\",\n \"description\": \"Overrides the pixel ratio scaling factor for the canvas's 2d context. Ordinarily, this is set to the devicePixelRatio / (context.backingStoreRatio || 1), so on mobile devices, where the devicePixelRatio can be somewhere around 3, performance can be improved by overriding this value to something less precise, like 1, at the expense of resolution.\"\n },\n \"interactionModel\": {\n \"default\": \"...\",\n \"labels\": [\"Interactive Elements\"],\n \"type\": \"Object\",\n \"description\": \"TODO(konigsberg): document this\"\n },\n \"ticker\": {\n \"default\": \"Dygraph.dateTicker or Dygraph.numericTicks\",\n \"labels\": [\"Axis display\"],\n \"type\": \"function(min, max, pixels, opts, dygraph, vals) -> [{v: ..., label: ...}, ...]\",\n \"parameters\": [[\"min\", \"\"], [\"max\", \"\"], [\"pixels\", \"\"], [\"opts\", \"\"], [\"dygraph\", \"the reference graph\"], [\"vals\", \"\"]],\n \"description\": \"This lets you specify an arbitrary function to generate tick marks on an axis. The tick marks are an array of (value, label) pairs. The built-in functions go to great lengths to choose good tick marks so, if you set this option, you'll most likely want to call one of them and modify the result. See dygraph-tickers.js for an extensive discussion. This is set on a per-axis basis.\"\n },\n \"xAxisHeight\": {\n \"default\": \"(null)\",\n \"labels\": [\"Axis display\"],\n \"type\": \"integer\",\n \"description\": \"Height, in pixels, of the x-axis. If not set explicitly, this is computed based on axisLabelFontSize and axisTickSize.\"\n },\n \"showLabelsOnHighlight\": {\n \"default\": \"true\",\n \"labels\": [\"Interactive Elements\", \"Legend\"],\n \"type\": \"boolean\",\n \"description\": \"Whether to show the legend upon mouseover.\"\n },\n \"axis\": {\n \"default\": \"(none)\",\n \"labels\": [\"Axis display\"],\n \"type\": \"string\",\n \"description\": \"Set to either 'y1' or 'y2' to assign a series to a y-axis (primary or secondary). Must be set per-series.\"\n },\n \"pixelsPerLabel\": {\n \"default\": \"70 (x-axis) or 30 (y-axes)\",\n \"labels\": [\"Axis display\", \"Grid\"],\n \"type\": \"integer\",\n \"description\": \"Number of pixels to require between each x- and y-label. Larger values will yield a sparser axis with fewer ticks. This is set on a per-axis basis.\"\n },\n \"labelsDiv\": {\n \"default\": \"null\",\n \"labels\": [\"Legend\"],\n \"type\": \"DOM element or string\",\n \"example\": \"document.getElementById('foo')or'foo'\",\n \"description\": \"Show data labels in an external div, rather than on the graph. This value can either be a div element or a div id.\"\n },\n \"fractions\": {\n \"default\": \"false\",\n \"labels\": [\"CSV parsing\", \"Error Bars\"],\n \"type\": \"boolean\",\n \"description\": \"When set, attempt to parse each cell in the CSV file as \\\"a/b\\\", where a and b are integers. The ratio will be plotted. This allows computation of Wilson confidence intervals (see below).\"\n },\n \"logscale\": {\n \"default\": \"false\",\n \"labels\": [\"Axis display\"],\n \"type\": \"boolean\",\n \"description\": \"When set for the y-axis or x-axis, the graph shows that axis in log scale. Any values less than or equal to zero are not displayed. Showing log scale with ranges that go below zero will result in an unviewable graph.\\n\\n Not compatible with showZero. connectSeparatedPoints is ignored. This is ignored for date-based x-axes.\"\n },\n \"strokeWidth\": {\n \"default\": \"1.0\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"float\",\n \"example\": \"0.5, 2.0\",\n \"description\": \"The width of the lines connecting data points. This can be used to increase the contrast or some graphs.\"\n },\n \"strokePattern\": {\n \"default\": \"null\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"array\",\n \"example\": \"[10, 2, 5, 2]\",\n \"description\": \"A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed lines.\"\n },\n \"strokeBorderWidth\": {\n \"default\": \"null\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"float\",\n \"example\": \"1.0\",\n \"description\": \"Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines.\"\n },\n \"strokeBorderColor\": {\n \"default\": \"white\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"string\",\n \"example\": \"red, #ccffdd\",\n \"description\": \"Color for the line border used if strokeBorderWidth is set.\"\n },\n \"wilsonInterval\": {\n \"default\": \"true\",\n \"labels\": [\"Error Bars\"],\n \"type\": \"boolean\",\n \"description\": \"Use in conjunction with the \\\"fractions\\\" option. Instead of plotting +/- N standard deviations, dygraphs will compute a Wilson confidence interval and plot that. This has more reasonable behavior for ratios close to 0 or 1.\"\n },\n \"fillGraph\": {\n \"default\": \"false\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"boolean\",\n \"description\": \"Should the area underneath the graph be filled? This option is not compatible with error bars. This may be set on a per-series basis.\"\n },\n \"highlightCircleSize\": {\n \"default\": \"3\",\n \"labels\": [\"Interactive Elements\"],\n \"type\": \"integer\",\n \"description\": \"The size in pixels of the dot drawn over highlighted points.\"\n },\n \"gridLineColor\": {\n \"default\": \"rgb(128,128,128)\",\n \"labels\": [\"Grid\"],\n \"type\": \"red, blue\",\n \"description\": \"The color of the gridlines. This may be set on a per-axis basis to define each axis' grid separately.\"\n },\n \"gridLinePattern\": {\n \"default\": \"null\",\n \"labels\": [\"Grid\"],\n \"type\": \"array\",\n \"example\": \"[10, 2, 5, 2]\",\n \"description\": \"A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed gridlines.\"\n },\n \"visibility\": {\n \"default\": \"[true, true, ...]\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"Array of booleans\",\n \"description\": \"Which series should initially be visible? Once the Dygraph has been constructed, you can access and modify the visibility of each series using the visibility and setVisibility methods.\"\n },\n \"valueRange\": {\n \"default\": \"Full range of the input is shown\",\n \"labels\": [\"Axis display\"],\n \"type\": \"Array of two numbers\",\n \"example\": \"[10, 110]\",\n \"description\": \"Explicitly set the vertical range of the graph to [low, high]. This may be set on a per-axis basis to define each y-axis separately. If either limit is unspecified, it will be calculated automatically (e.g. [null, 30] to automatically calculate just the lower bound)\"\n },\n \"colorSaturation\": {\n \"default\": \"1.0\",\n \"labels\": [\"Data Series Colors\"],\n \"type\": \"float (0.0 - 1.0)\",\n \"description\": \"If colors is not specified, saturation of the automatically-generated data series colors.\"\n },\n \"hideOverlayOnMouseOut\": {\n \"default\": \"true\",\n \"labels\": [\"Interactive Elements\", \"Legend\"],\n \"type\": \"boolean\",\n \"description\": \"Whether to hide the legend when the mouse leaves the chart area.\"\n },\n \"legend\": {\n \"default\": \"onmouseover\",\n \"labels\": [\"Legend\"],\n \"type\": \"string\",\n \"description\": \"When to display the legend. By default, it only appears when a user mouses over the chart. Set it to \\\"always\\\" to always display a legend of some sort. When set to \\\"follow\\\", legend follows highlighted points.\"\n },\n \"legendFormatter\": {\n \"default\": \"null\",\n \"labels\": [\"Legend\"],\n \"type\": \"function(data): string\",\n \"params\": [[\"data\", \"An object containing information about the selection (or lack of a selection). This includes formatted values and series information. See here for sample values.\"]],\n \"description\": \"Set this to supply a custom formatter for the legend. See this comment and the legendFormatter demo for usage.\"\n },\n \"labelsShowZeroValues\": {\n \"default\": \"true\",\n \"labels\": [\"Legend\"],\n \"type\": \"boolean\",\n \"description\": \"Show zero value labels in the labelsDiv.\"\n },\n \"stepPlot\": {\n \"default\": \"false\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"boolean\",\n \"description\": \"When set, display the graph as a step plot instead of a line plot. This option may either be set for the whole graph or for single series.\"\n },\n \"labelsUTC\": {\n \"default\": \"false\",\n \"labels\": [\"Value display/formatting\", \"Axis display\"],\n \"type\": \"boolean\",\n \"description\": \"Show date/time labels according to UTC (instead of local time).\"\n },\n \"labelsKMB\": {\n \"default\": \"false\",\n \"labels\": [\"Value display/formatting\"],\n \"type\": \"boolean\",\n \"description\": \"Show K/M/B for thousands/millions/billions on y-axis.\"\n },\n \"rightGap\": {\n \"default\": \"5\",\n \"labels\": [\"Overall display\"],\n \"type\": \"integer\",\n \"description\": \"Number of pixels to leave blank at the right edge of the Dygraph. This makes it easier to highlight the right-most data point.\"\n },\n \"drawAxesAtZero\": {\n \"default\": \"false\",\n \"labels\": [\"Axis display\"],\n \"type\": \"boolean\",\n \"description\": \"When set, draw the X axis at the Y=0 position and the Y axis at the X=0 position if those positions are inside the graph's visible area. Otherwise, draw the axes at the bottom or left graph edge as usual.\"\n },\n \"xRangePad\": {\n \"default\": \"0\",\n \"labels\": [\"Axis display\"],\n \"type\": \"float\",\n \"description\": \"Add the specified amount of extra space (in pixels) around the X-axis value range to ensure points at the edges remain visible.\"\n },\n \"yRangePad\": {\n \"default\": \"null\",\n \"labels\": [\"Axis display\"],\n \"type\": \"float\",\n \"description\": \"If set, add the specified amount of extra space (in pixels) around the Y-axis value range to ensure points at the edges remain visible. If unset, use the traditional Y padding algorithm.\"\n },\n \"axisLabelFormatter\": {\n \"default\": \"Depends on the data type\",\n \"labels\": [\"Axis display\"],\n \"type\": \"function(number or Date, granularity, opts, dygraph)\",\n \"parameters\": [[\"number or date\", \"Either a number (for a numeric axis) or a Date object (for a date axis)\"], [\"granularity\", \"specifies how fine-grained the axis is. For date axes, this is a reference to the time granularity enumeration, defined in dygraph-tickers.js, e.g. Dygraph.WEEKLY.\"], [\"opts\", \"a function which provides access to various options on the dygraph, e.g. opts('labelsKMB').\"], [\"dygraph\", \"the referenced graph\"]],\n \"description\": \"Function to call to format the tick values that appear along an axis. This is usually set on a per-axis basis.\"\n },\n \"clickCallback\": {\n \"snippet\": \"function(e, date_millis){
  alert(new Date(date_millis));
}\",\n \"default\": \"null\",\n \"labels\": [\"Callbacks\"],\n \"type\": \"function(e, x, points)\",\n \"parameters\": [[\"e\", \"The event object for the click\"], [\"x\", \"The x value that was clicked (for dates, this is milliseconds since epoch)\"], [\"points\", \"The closest points along that date. See Point properties for details.\"]],\n \"description\": \"A function to call when the canvas is clicked.\"\n },\n \"labels\": {\n \"default\": \"[\\\"X\\\", \\\"Y1\\\", \\\"Y2\\\", ...]*\",\n \"labels\": [\"Legend\"],\n \"type\": \"array\",\n \"description\": \"A name for each data series, including the independent (X) series. For CSV files and DataTable objections, this is determined by context. For raw data, this must be specified. If it is not, default values are supplied and a warning is logged.\"\n },\n \"dateWindow\": {\n \"default\": \"Full range of the input is shown\",\n \"labels\": [\"Axis display\"],\n \"type\": \"Array of two numbers\",\n \"example\": \"[
  Date.parse('2006-01-01'),
  (new Date()).valueOf()
]\",\n \"description\": \"Initially zoom in on a section of the graph. Is of the form [earliest, latest], where earliest/latest are milliseconds since epoch. If the data for the x-axis is numeric, the values in dateWindow must also be numbers.\"\n },\n \"showRoller\": {\n \"default\": \"false\",\n \"labels\": [\"Interactive Elements\", \"Rolling Averages\"],\n \"type\": \"boolean\",\n \"description\": \"If the rolling average period text box should be shown.\"\n },\n \"sigma\": {\n \"default\": \"2.0\",\n \"labels\": [\"Error Bars\"],\n \"type\": \"float\",\n \"description\": \"When errorBars is set, shade this many standard deviations above/below each point.\"\n },\n \"customBars\": {\n \"default\": \"false\",\n \"labels\": [\"CSV parsing\", \"Error Bars\"],\n \"type\": \"boolean\",\n \"description\": \"When set, parse each CSV cell as \\\"low;middle;high\\\". Error bars will be drawn for each point between low and high, with the series itself going through middle.\"\n },\n \"colorValue\": {\n \"default\": \"1.0\",\n \"labels\": [\"Data Series Colors\"],\n \"type\": \"float (0.0 - 1.0)\",\n \"description\": \"If colors is not specified, value of the data series colors, as in hue/saturation/value. (0.0-1.0, default 0.5)\"\n },\n \"errorBars\": {\n \"default\": \"false\",\n \"labels\": [\"CSV parsing\", \"Error Bars\"],\n \"type\": \"boolean\",\n \"description\": \"Does the data contain standard deviations? Setting this to true alters the input format (see above).\"\n },\n \"displayAnnotations\": {\n \"default\": \"false\",\n \"labels\": [\"Annotations\"],\n \"type\": \"boolean\",\n \"description\": \"Only applies when Dygraphs is used as a GViz chart. Causes string columns following a data series to be interpreted as annotations on points in that series. This is the same format used by Google's AnnotatedTimeLine chart.\"\n },\n \"panEdgeFraction\": {\n \"default\": \"null\",\n \"labels\": [\"Axis display\", \"Interactive Elements\"],\n \"type\": \"float\",\n \"description\": \"A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% passed the edges of the displayed values. null means no bounds.\"\n },\n \"title\": {\n \"labels\": [\"Chart labels\"],\n \"type\": \"string\",\n \"default\": \"null\",\n \"description\": \"Text to display above the chart. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-title' classes.\"\n },\n \"titleHeight\": {\n \"default\": \"18\",\n \"labels\": [\"Chart labels\"],\n \"type\": \"integer\",\n \"description\": \"Height of the chart title, in pixels. This also controls the default font size of the title. If you style the title on your own, this controls how much space is set aside above the chart for the title's div.\"\n },\n \"xlabel\": {\n \"labels\": [\"Chart labels\"],\n \"type\": \"string\",\n \"default\": \"null\",\n \"description\": \"Text to display below the chart's x-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-xlabel' classes.\"\n },\n \"xLabelHeight\": {\n \"labels\": [\"Chart labels\"],\n \"type\": \"integer\",\n \"default\": \"18\",\n \"description\": \"Height of the x-axis label, in pixels. This also controls the default font size of the x-axis label. If you style the label on your own, this controls how much space is set aside below the chart for the x-axis label's div.\"\n },\n \"ylabel\": {\n \"labels\": [\"Chart labels\"],\n \"type\": \"string\",\n \"default\": \"null\",\n \"description\": \"Text to display to the left of the chart's y-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-ylabel' classes. The text will be rotated 90 degrees by default, so CSS rules may behave in unintuitive ways. No additional space is set aside for a y-axis label. If you need more space, increase the width of the y-axis tick labels using the yAxisLabelWidth option. If you need a wider div for the y-axis label, either style it that way with CSS (but remember that it's rotated, so width is controlled by the 'height' property) or set the yLabelWidth option.\"\n },\n \"y2label\": {\n \"labels\": [\"Chart labels\"],\n \"type\": \"string\",\n \"default\": \"null\",\n \"description\": \"Text to display to the right of the chart's secondary y-axis. This label is only displayed if a secondary y-axis is present. See this test for an example of how to do this. The comments for the 'ylabel' option generally apply here as well. This label gets a 'dygraph-y2label' instead of a 'dygraph-ylabel' class.\"\n },\n \"yLabelWidth\": {\n \"labels\": [\"Chart labels\"],\n \"type\": \"integer\",\n \"default\": \"18\",\n \"description\": \"Width of the div which contains the y-axis label. Since the y-axis label appears rotated 90 degrees, this actually affects the height of its div.\"\n },\n \"drawGrid\": {\n \"default\": \"true for x and y, false for y2\",\n \"labels\": [\"Grid\"],\n \"type\": \"boolean\",\n \"description\": \"Whether to display gridlines in the chart. This may be set on a per-axis basis to define the visibility of each axis' grid separately.\"\n },\n \"independentTicks\": {\n \"default\": \"true for y, false for y2\",\n \"labels\": [\"Axis display\", \"Grid\"],\n \"type\": \"boolean\",\n \"description\": \"Only valid for y and y2, has no effect on x: This option defines whether the y axes should align their ticks or if they should be independent. Possible combinations: 1.) y=true, y2=false (default): y is the primary axis and the y2 ticks are aligned to the the ones of y. (only 1 grid) 2.) y=false, y2=true: y2 is the primary axis and the y ticks are aligned to the the ones of y2. (only 1 grid) 3.) y=true, y2=true: Both axis are independent and have their own ticks. (2 grids) 4.) y=false, y2=false: Invalid configuration causes an error.\"\n },\n \"drawAxis\": {\n \"default\": \"true for x and y, false for y2\",\n \"labels\": [\"Axis display\"],\n \"type\": \"boolean\",\n \"description\": \"Whether to draw the specified axis. This may be set on a per-axis basis to define the visibility of each axis separately. Setting this to false also prevents axis ticks from being drawn and reclaims the space for the chart grid/lines.\"\n },\n \"gridLineWidth\": {\n \"default\": \"0.3\",\n \"labels\": [\"Grid\"],\n \"type\": \"float\",\n \"description\": \"Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawGrid option. This may be set on a per-axis basis to define each axis' grid separately.\"\n },\n \"axisLineWidth\": {\n \"default\": \"0.3\",\n \"labels\": [\"Axis display\"],\n \"type\": \"float\",\n \"description\": \"Thickness (in pixels) of the x- and y-axis lines.\"\n },\n \"axisLineColor\": {\n \"default\": \"black\",\n \"labels\": [\"Axis display\"],\n \"type\": \"string\",\n \"description\": \"Color of the x- and y-axis lines. Accepts any value which the HTML canvas strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'.\"\n },\n \"fillAlpha\": {\n \"default\": \"0.15\",\n \"labels\": [\"Error Bars\", \"Data Series Colors\"],\n \"type\": \"float (0.0 - 1.0)\",\n \"description\": \"Error bars (or custom bars) for each series are drawn in the same color as the series, but with partial transparency. This sets the transparency. A value of 0.0 means that the error bars will not be drawn, whereas a value of 1.0 means that the error bars will be as dark as the line for the series itself. This can be used to produce chart lines whose thickness varies at each point.\"\n },\n \"axisLabelWidth\": {\n \"default\": \"50 (y-axis), 60 (x-axis)\",\n \"labels\": [\"Axis display\", \"Chart labels\"],\n \"type\": \"integer\",\n \"description\": \"Width (in pixels) of the containing divs for x- and y-axis labels. For the y-axis, this also controls the width of the y-axis. Note that for the x-axis, this is independent from pixelsPerLabel, which controls the spacing between labels.\"\n },\n \"sigFigs\": {\n \"default\": \"null\",\n \"labels\": [\"Value display/formatting\"],\n \"type\": \"integer\",\n \"description\": \"By default, dygraphs displays numbers with a fixed number of digits after the decimal point. If you'd prefer to have a fixed number of significant figures, set this option to that number of sig figs. A value of 2, for instance, would cause 1 to be display as 1.0 and 1234 to be displayed as 1.23e+3.\"\n },\n \"digitsAfterDecimal\": {\n \"default\": \"2\",\n \"labels\": [\"Value display/formatting\"],\n \"type\": \"integer\",\n \"description\": \"Unless it's run in scientific mode (see the sigFigs option), dygraphs displays numbers with digitsAfterDecimal digits after the decimal point. Trailing zeros are not displayed, so with a value of 2 you'll get '0', '0.1', '0.12', '123.45' but not '123.456' (it will be rounded to '123.46'). Numbers with absolute value less than 0.1^digitsAfterDecimal (i.e. those which would show up as '0.00') will be displayed in scientific notation.\"\n },\n \"maxNumberWidth\": {\n \"default\": \"6\",\n \"labels\": [\"Value display/formatting\"],\n \"type\": \"integer\",\n \"description\": \"When displaying numbers in normal (not scientific) mode, large numbers will be displayed with many trailing zeros (e.g. 100000000 instead of 1e9). This can lead to unwieldy y-axis labels. If there are more than maxNumberWidth digits to the left of the decimal in a number, dygraphs will switch to scientific notation, even when not operating in scientific mode. If you'd like to see all those digits, set this to something large, like 20 or 30.\"\n },\n \"file\": {\n \"default\": \"(set when constructed)\",\n \"labels\": [\"Data\"],\n \"type\": \"string (URL of CSV or CSV), GViz DataTable or 2D Array\",\n \"description\": \"Sets the data being displayed in the chart. This can only be set when calling updateOptions; it cannot be set from the constructor. For a full description of valid data formats, see the Data Formats page.\"\n },\n \"timingName\": {\n \"default\": \"null\",\n \"labels\": [\"Debugging\", \"Deprecated\"],\n \"type\": \"string\",\n \"description\": \"Set this option to log timing information. The value of the option will be logged along with the timimg, so that you can distinguish multiple dygraphs on the same page.\"\n },\n \"showRangeSelector\": {\n \"default\": \"false\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"boolean\",\n \"description\": \"Show or hide the range selector widget.\"\n },\n \"rangeSelectorHeight\": {\n \"default\": \"40\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"integer\",\n \"description\": \"Height, in pixels, of the range selector widget. This option can only be specified at Dygraph creation time.\"\n },\n \"rangeSelectorPlotStrokeColor\": {\n \"default\": \"#808FAB\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"string\",\n \"description\": \"The range selector mini plot stroke color. This can be of the form \\\"#AABBCC\\\" or \\\"rgb(255,100,200)\\\" or \\\"yellow\\\". You can also specify null or \\\"\\\" to turn off stroke.\"\n },\n \"rangeSelectorPlotFillColor\": {\n \"default\": \"#A7B1C4\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"string\",\n \"description\": \"The range selector mini plot fill color. This can be of the form \\\"#AABBCC\\\" or \\\"rgb(255,100,200)\\\" or \\\"yellow\\\". You can also specify null or \\\"\\\" to turn off fill.\"\n },\n \"rangeSelectorPlotFillGradientColor\": {\n \"default\": \"white\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"string\",\n \"description\": \"The top color for the range selector mini plot fill color gradient. This can be of the form \\\"#AABBCC\\\" or \\\"rgb(255,100,200)\\\" or \\\"rgba(255,100,200,42)\\\" or \\\"yellow\\\". You can also specify null or \\\"\\\" to disable the gradient and fill with one single color.\"\n },\n \"rangeSelectorBackgroundStrokeColor\": {\n \"default\": \"gray\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"string\",\n \"description\": \"The color of the lines below and on both sides of the range selector mini plot. This can be of the form \\\"#AABBCC\\\" or \\\"rgb(255,100,200)\\\" or \\\"yellow\\\".\"\n },\n \"rangeSelectorBackgroundLineWidth\": {\n \"default\": \"1\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"float\",\n \"description\": \"The width of the lines below and on both sides of the range selector mini plot.\"\n },\n \"rangeSelectorPlotLineWidth\": {\n \"default\": \"1.5\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"float\",\n \"description\": \"The width of the range selector mini plot line.\"\n },\n \"rangeSelectorForegroundStrokeColor\": {\n \"default\": \"black\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"string\",\n \"description\": \"The color of the lines in the interactive layer of the range selector. This can be of the form \\\"#AABBCC\\\" or \\\"rgb(255,100,200)\\\" or \\\"yellow\\\".\"\n },\n \"rangeSelectorForegroundLineWidth\": {\n \"default\": \"1\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"float\",\n \"description\": \"The width the lines in the interactive layer of the range selector.\"\n },\n \"rangeSelectorAlpha\": {\n \"default\": \"0.6\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"float (0.0 - 1.0)\",\n \"description\": \"The transparency of the veil that is drawn over the unselected portions of the range selector mini plot. A value of 0 represents full transparency and the unselected portions of the mini plot will appear as normal. A value of 1 represents full opacity and the unselected portions of the mini plot will be hidden.\"\n },\n \"showInRangeSelector\": {\n \"default\": \"null\",\n \"labels\": [\"Range Selector\"],\n \"type\": \"boolean\",\n \"description\": \"Mark this series for inclusion in the range selector. The mini plot curve will be an average of all such series. If this is not specified for any series, the default behavior is to average all the visible series. Setting it for one series will result in that series being charted alone in the range selector. Once it's set for a single series, it needs to be set for all series which should be included (regardless of visibility).\"\n },\n \"animatedZooms\": {\n \"default\": \"false\",\n \"labels\": [\"Interactive Elements\"],\n \"type\": \"boolean\",\n \"description\": \"Set this option to animate the transition between zoom windows. Applies to programmatic and interactive zooms. Note that if you also set a drawCallback, it will be called several times on each zoom. If you set a zoomCallback, it will only be called after the animation is complete.\"\n },\n \"plotter\": {\n \"default\": \"[DygraphCanvasRenderer.Plotters.fillPlotter, DygraphCanvasRenderer.Plotters.errorPlotter, DygraphCanvasRenderer.Plotters.linePlotter]\",\n \"labels\": [\"Data Line display\"],\n \"type\": \"array or function\",\n \"description\": \"A function (or array of functions) which plot each data series on the chart. TODO(danvk): more details! May be set per-series.\"\n },\n \"axes\": {\n \"default\": \"null\",\n \"labels\": [\"Configuration\"],\n \"type\": \"Object\",\n \"description\": \"Defines per-axis options. Valid keys are 'x', 'y' and 'y2'. Only some options may be set on a per-axis basis. If an option may be set in this way, it will be noted on this page. See also documentation on per-series and per-axis options.\"\n },\n \"series\": {\n \"default\": \"null\",\n \"labels\": [\"Series\"],\n \"type\": \"Object\",\n \"description\": \"Defines per-series options. Its keys match the y-axis label names, and the values are dictionaries themselves that contain options specific to that series.\"\n },\n \"plugins\": {\n \"default\": \"[]\",\n \"labels\": [\"Configuration\"],\n \"type\": \"Array\",\n \"description\": \"Defines per-graph plugins. Useful for per-graph customization\"\n },\n \"dataHandler\": {\n \"default\": \"(depends on data)\",\n \"labels\": [\"Data\"],\n \"type\": \"Dygraph.DataHandler\",\n \"description\": \"Custom DataHandler. This is an advanced customization. See http://bit.ly/151E7Aq.\"\n }\n }; //
\n // NOTE: in addition to parsing as JS, this snippet is expected to be valid\n // JSON. This assumption cannot be checked in JS, but it will be checked when\n // documentation is generated by the generate-documentation.py script. For the\n // most part, this just means that you should always use double quotes.\n\n // Do a quick sanity check on the options reference.\n var warn = function warn(msg) {\n if (window.console) window.console.warn(msg);\n };\n var flds = ['type', 'default', 'description'];\n var valid_cats = ['Annotations', 'Axis display', 'Chart labels', 'CSV parsing', 'Callbacks', 'Data', 'Data Line display', 'Data Series Colors', 'Error Bars', 'Grid', 'Interactive Elements', 'Range Selector', 'Legend', 'Overall display', 'Rolling Averages', 'Series', 'Value display/formatting', 'Zooming', 'Debugging', 'Configuration', 'Deprecated'];\n var i;\n var cats = {};\n for (i = 0; i < valid_cats.length; i++) cats[valid_cats[i]] = true;\n\n for (var k in OPTIONS_REFERENCE) {\n if (!OPTIONS_REFERENCE.hasOwnProperty(k)) continue;\n var op = OPTIONS_REFERENCE[k];\n for (i = 0; i < flds.length; i++) {\n if (!op.hasOwnProperty(flds[i])) {\n warn('Option ' + k + ' missing \"' + flds[i] + '\" property');\n } else if (typeof op[flds[i]] != 'string') {\n warn(k + '.' + flds[i] + ' must be of type string');\n }\n }\n var labels = op.labels;\n if (typeof labels !== 'object') {\n warn('Option \"' + k + '\" is missing a \"labels\": [...] option');\n } else {\n for (i = 0; i < labels.length; i++) {\n if (!cats.hasOwnProperty(labels[i])) {\n warn('Option \"' + k + '\" has label \"' + labels[i] + '\", which is invalid.');\n }\n }\n }\n }\n }\n}\n\nexports['default'] = OPTIONS_REFERENCE;\nmodule.exports = exports['default'];","/**\n * To create a \"drag\" interaction, you typically register a mousedown event\n * handler on the element where the drag begins. In that handler, you register a\n * mouseup handler on the window to determine when the mouse is released,\n * wherever that release happens. This works well, except when the user releases\n * the mouse over an off-domain iframe. In that case, the mouseup event is\n * handled by the iframe and never bubbles up to the window handler.\n *\n * To deal with this issue, we cover iframes with high z-index divs to make sure\n * they don't capture mouseup.\n *\n * Usage:\n * element.addEventListener('mousedown', function() {\n * var tarper = new IFrameTarp();\n * tarper.cover();\n * var mouseUpHandler = function() {\n * ...\n * window.removeEventListener(mouseUpHandler);\n * tarper.uncover();\n * };\n * window.addEventListener('mouseup', mouseUpHandler);\n * };\n *\n * @constructor\n */\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj[\"default\"] = obj; return newObj; } }\n\nvar _dygraphUtils = require('./dygraph-utils');\n\nvar utils = _interopRequireWildcard(_dygraphUtils);\n\nfunction IFrameTarp() {\n /** @type {Array.} */\n this.tarps = [];\n};\n\n/**\n * Find all the iframes in the document and cover them with high z-index\n * transparent divs.\n */\nIFrameTarp.prototype.cover = function () {\n var iframes = document.getElementsByTagName(\"iframe\");\n for (var i = 0; i < iframes.length; i++) {\n var iframe = iframes[i];\n var pos = utils.findPos(iframe),\n x = pos.x,\n y = pos.y,\n width = iframe.offsetWidth,\n height = iframe.offsetHeight;\n\n var div = document.createElement(\"div\");\n div.style.position = \"absolute\";\n div.style.left = x + 'px';\n div.style.top = y + 'px';\n div.style.width = width + 'px';\n div.style.height = height + 'px';\n div.style.zIndex = 999;\n document.body.appendChild(div);\n this.tarps.push(div);\n }\n};\n\n/**\n * Remove all the iframe covers. You should call this in a mouseup handler.\n */\nIFrameTarp.prototype.uncover = function () {\n for (var i = 0; i < this.tarps.length; i++) {\n this.tarps[i].parentNode.removeChild(this.tarps[i]);\n }\n this.tarps = [];\n};\n\nexports[\"default\"] = IFrameTarp;\nmodule.exports = exports[\"default\"];","/**\n * @license\n * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)\n * MIT-licensed (http://opensource.org/licenses/MIT)\n */\n\n/**\n * @fileoverview DataHandler default implementation used for simple line charts.\n * @author David Eberlein (david.eberlein@ch.sauter-bc.com)\n */\n\n/*global Dygraph:false */\n\"use strict\";\n\nObject.defineProperty(exports, '__esModule', {\n value: true\n});\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nvar _datahandler = require('./datahandler');\n\nvar _datahandler2 = _interopRequireDefault(_datahandler);\n\n/**\n * @constructor\n * @extends Dygraph.DataHandler\n */\nvar DefaultHandler = function DefaultHandler() {};\n\nDefaultHandler.prototype = new _datahandler2['default']();\n\n/** @inheritDoc */\nDefaultHandler.prototype.extractSeries = function (rawData, i, options) {\n // TODO(danvk): pre-allocate series here.\n var series = [];\n var logScale = options.get('logscale');\n for (var j = 0; j < rawData.length; j++) {\n var x = rawData[j][0];\n var point = rawData[j][i];\n if (logScale) {\n // On the log scale, points less than zero do not exist.\n // This will create a gap in the chart.\n if (point <= 0) {\n point = null;\n }\n }\n series.push([x, point]);\n }\n return series;\n};\n\n/** @inheritDoc */\nDefaultHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) {\n rollPeriod = Math.min(rollPeriod, originalData.length);\n var rollingData = [];\n\n var i, j, y, sum, num_ok;\n // Calculate the rolling average for the first rollPeriod - 1 points\n // where\n // there is not enough data to roll over the full number of points\n if (rollPeriod == 1) {\n return originalData;\n }\n for (i = 0; i < originalData.length; i++) {\n sum = 0;\n num_ok = 0;\n for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {\n y = originalData[j][1];\n if (y === null || isNaN(y)) continue;\n num_ok++;\n sum += originalData[j][1];\n }\n if (num_ok) {\n rollingData[i] = [originalData[i][0], sum / num_ok];\n } else {\n rollingData[i] = [originalData[i][0], null];\n }\n }\n\n return rollingData;\n};\n\n/** @inheritDoc */\nDefaultHandler.prototype.getExtremeYValues = function (series, dateWindow, options) {\n var minY = null,\n maxY = null,\n y;\n var firstIdx = 0,\n lastIdx = series.length - 1;\n\n for (var j = firstIdx; j <= lastIdx; j++) {\n y = series[j][1];\n if (y === null || isNaN(y)) continue;\n if (maxY === null || y > maxY) {\n maxY = y;\n }\n if (minY === null || y < minY) {\n minY = y;\n }\n }\n return [minY, maxY];\n};\n\nexports['default'] = DefaultHandler;\nmodule.exports = exports['default'];","'use strict'\r\n\r\nmodule.exports = {\r\n\t\"aliceblue\": [240, 248, 255],\r\n\t\"antiquewhite\": [250, 235, 215],\r\n\t\"aqua\": [0, 255, 255],\r\n\t\"aquamarine\": [127, 255, 212],\r\n\t\"azure\": [240, 255, 255],\r\n\t\"beige\": [245, 245, 220],\r\n\t\"bisque\": [255, 228, 196],\r\n\t\"black\": [0, 0, 0],\r\n\t\"blanchedalmond\": [255, 235, 205],\r\n\t\"blue\": [0, 0, 255],\r\n\t\"blueviolet\": [138, 43, 226],\r\n\t\"brown\": [165, 42, 42],\r\n\t\"burlywood\": [222, 184, 135],\r\n\t\"cadetblue\": [95, 158, 160],\r\n\t\"chartreuse\": [127, 255, 0],\r\n\t\"chocolate\": [210, 105, 30],\r\n\t\"coral\": [255, 127, 80],\r\n\t\"cornflowerblue\": [100, 149, 237],\r\n\t\"cornsilk\": [255, 248, 220],\r\n\t\"crimson\": [220, 20, 60],\r\n\t\"cyan\": [0, 255, 255],\r\n\t\"darkblue\": [0, 0, 139],\r\n\t\"darkcyan\": [0, 139, 139],\r\n\t\"darkgoldenrod\": [184, 134, 11],\r\n\t\"darkgray\": [169, 169, 169],\r\n\t\"darkgreen\": [0, 100, 0],\r\n\t\"darkgrey\": [169, 169, 169],\r\n\t\"darkkhaki\": [189, 183, 107],\r\n\t\"darkmagenta\": [139, 0, 139],\r\n\t\"darkolivegreen\": [85, 107, 47],\r\n\t\"darkorange\": [255, 140, 0],\r\n\t\"darkorchid\": [153, 50, 204],\r\n\t\"darkred\": [139, 0, 0],\r\n\t\"darksalmon\": [233, 150, 122],\r\n\t\"darkseagreen\": [143, 188, 143],\r\n\t\"darkslateblue\": [72, 61, 139],\r\n\t\"darkslategray\": [47, 79, 79],\r\n\t\"darkslategrey\": [47, 79, 79],\r\n\t\"darkturquoise\": [0, 206, 209],\r\n\t\"darkviolet\": [148, 0, 211],\r\n\t\"deeppink\": [255, 20, 147],\r\n\t\"deepskyblue\": [0, 191, 255],\r\n\t\"dimgray\": [105, 105, 105],\r\n\t\"dimgrey\": [105, 105, 105],\r\n\t\"dodgerblue\": [30, 144, 255],\r\n\t\"firebrick\": [178, 34, 34],\r\n\t\"floralwhite\": [255, 250, 240],\r\n\t\"forestgreen\": [34, 139, 34],\r\n\t\"fuchsia\": [255, 0, 255],\r\n\t\"gainsboro\": [220, 220, 220],\r\n\t\"ghostwhite\": [248, 248, 255],\r\n\t\"gold\": [255, 215, 0],\r\n\t\"goldenrod\": [218, 165, 32],\r\n\t\"gray\": [128, 128, 128],\r\n\t\"green\": [0, 128, 0],\r\n\t\"greenyellow\": [173, 255, 47],\r\n\t\"grey\": [128, 128, 128],\r\n\t\"honeydew\": [240, 255, 240],\r\n\t\"hotpink\": [255, 105, 180],\r\n\t\"indianred\": [205, 92, 92],\r\n\t\"indigo\": [75, 0, 130],\r\n\t\"ivory\": [255, 255, 240],\r\n\t\"khaki\": [240, 230, 140],\r\n\t\"lavender\": [230, 230, 250],\r\n\t\"lavenderblush\": [255, 240, 245],\r\n\t\"lawngreen\": [124, 252, 0],\r\n\t\"lemonchiffon\": [255, 250, 205],\r\n\t\"lightblue\": [173, 216, 230],\r\n\t\"lightcoral\": [240, 128, 128],\r\n\t\"lightcyan\": [224, 255, 255],\r\n\t\"lightgoldenrodyellow\": [250, 250, 210],\r\n\t\"lightgray\": [211, 211, 211],\r\n\t\"lightgreen\": [144, 238, 144],\r\n\t\"lightgrey\": [211, 211, 211],\r\n\t\"lightpink\": [255, 182, 193],\r\n\t\"lightsalmon\": [255, 160, 122],\r\n\t\"lightseagreen\": [32, 178, 170],\r\n\t\"lightskyblue\": [135, 206, 250],\r\n\t\"lightslategray\": [119, 136, 153],\r\n\t\"lightslategrey\": [119, 136, 153],\r\n\t\"lightsteelblue\": [176, 196, 222],\r\n\t\"lightyellow\": [255, 255, 224],\r\n\t\"lime\": [0, 255, 0],\r\n\t\"limegreen\": [50, 205, 50],\r\n\t\"linen\": [250, 240, 230],\r\n\t\"magenta\": [255, 0, 255],\r\n\t\"maroon\": [128, 0, 0],\r\n\t\"mediumaquamarine\": [102, 205, 170],\r\n\t\"mediumblue\": [0, 0, 205],\r\n\t\"mediumorchid\": [186, 85, 211],\r\n\t\"mediumpurple\": [147, 112, 219],\r\n\t\"mediumseagreen\": [60, 179, 113],\r\n\t\"mediumslateblue\": [123, 104, 238],\r\n\t\"mediumspringgreen\": [0, 250, 154],\r\n\t\"mediumturquoise\": [72, 209, 204],\r\n\t\"mediumvioletred\": [199, 21, 133],\r\n\t\"midnightblue\": [25, 25, 112],\r\n\t\"mintcream\": [245, 255, 250],\r\n\t\"mistyrose\": [255, 228, 225],\r\n\t\"moccasin\": [255, 228, 181],\r\n\t\"navajowhite\": [255, 222, 173],\r\n\t\"navy\": [0, 0, 128],\r\n\t\"oldlace\": [253, 245, 230],\r\n\t\"olive\": [128, 128, 0],\r\n\t\"olivedrab\": [107, 142, 35],\r\n\t\"orange\": [255, 165, 0],\r\n\t\"orangered\": [255, 69, 0],\r\n\t\"orchid\": [218, 112, 214],\r\n\t\"palegoldenrod\": [238, 232, 170],\r\n\t\"palegreen\": [152, 251, 152],\r\n\t\"paleturquoise\": [175, 238, 238],\r\n\t\"palevioletred\": [219, 112, 147],\r\n\t\"papayawhip\": [255, 239, 213],\r\n\t\"peachpuff\": [255, 218, 185],\r\n\t\"peru\": [205, 133, 63],\r\n\t\"pink\": [255, 192, 203],\r\n\t\"plum\": [221, 160, 221],\r\n\t\"powderblue\": [176, 224, 230],\r\n\t\"purple\": [128, 0, 128],\r\n\t\"rebeccapurple\": [102, 51, 153],\r\n\t\"red\": [255, 0, 0],\r\n\t\"rosybrown\": [188, 143, 143],\r\n\t\"royalblue\": [65, 105, 225],\r\n\t\"saddlebrown\": [139, 69, 19],\r\n\t\"salmon\": [250, 128, 114],\r\n\t\"sandybrown\": [244, 164, 96],\r\n\t\"seagreen\": [46, 139, 87],\r\n\t\"seashell\": [255, 245, 238],\r\n\t\"sienna\": [160, 82, 45],\r\n\t\"silver\": [192, 192, 192],\r\n\t\"skyblue\": [135, 206, 235],\r\n\t\"slateblue\": [106, 90, 205],\r\n\t\"slategray\": [112, 128, 144],\r\n\t\"slategrey\": [112, 128, 144],\r\n\t\"snow\": [255, 250, 250],\r\n\t\"springgreen\": [0, 255, 127],\r\n\t\"steelblue\": [70, 130, 180],\r\n\t\"tan\": [210, 180, 140],\r\n\t\"teal\": [0, 128, 128],\r\n\t\"thistle\": [216, 191, 216],\r\n\t\"tomato\": [255, 99, 71],\r\n\t\"turquoise\": [64, 224, 208],\r\n\t\"violet\": [238, 130, 238],\r\n\t\"wheat\": [245, 222, 179],\r\n\t\"white\": [255, 255, 255],\r\n\t\"whitesmoke\": [245, 245, 245],\r\n\t\"yellow\": [255, 255, 0],\r\n\t\"yellowgreen\": [154, 205, 50]\r\n};\r\n","/* MIT license */\nvar cssKeywords = require('color-name');\n\n// NOTE: conversions should only return primitive values (i.e. arrays, or\n// values that give correct `typeof` results).\n// do not use box values types (i.e. Number(), String(), etc.)\n\nvar reverseKeywords = {};\nfor (var key in cssKeywords) {\n\tif (cssKeywords.hasOwnProperty(key)) {\n\t\treverseKeywords[cssKeywords[key]] = key;\n\t}\n}\n\nvar convert = module.exports = {\n\trgb: {channels: 3, labels: 'rgb'},\n\thsl: {channels: 3, labels: 'hsl'},\n\thsv: {channels: 3, labels: 'hsv'},\n\thwb: {channels: 3, labels: 'hwb'},\n\tcmyk: {channels: 4, labels: 'cmyk'},\n\txyz: {channels: 3, labels: 'xyz'},\n\tlab: {channels: 3, labels: 'lab'},\n\tlch: {channels: 3, labels: 'lch'},\n\thex: {channels: 1, labels: ['hex']},\n\tkeyword: {channels: 1, labels: ['keyword']},\n\tansi16: {channels: 1, labels: ['ansi16']},\n\tansi256: {channels: 1, labels: ['ansi256']},\n\thcg: {channels: 3, labels: ['h', 'c', 'g']},\n\tapple: {channels: 3, labels: ['r16', 'g16', 'b16']},\n\tgray: {channels: 1, labels: ['gray']}\n};\n\n// hide .channels and .labels properties\nfor (var model in convert) {\n\tif (convert.hasOwnProperty(model)) {\n\t\tif (!('channels' in convert[model])) {\n\t\t\tthrow new Error('missing channels property: ' + model);\n\t\t}\n\n\t\tif (!('labels' in convert[model])) {\n\t\t\tthrow new Error('missing channel labels property: ' + model);\n\t\t}\n\n\t\tif (convert[model].labels.length !== convert[model].channels) {\n\t\t\tthrow new Error('channel and label counts mismatch: ' + model);\n\t\t}\n\n\t\tvar channels = convert[model].channels;\n\t\tvar labels = convert[model].labels;\n\t\tdelete convert[model].channels;\n\t\tdelete convert[model].labels;\n\t\tObject.defineProperty(convert[model], 'channels', {value: channels});\n\t\tObject.defineProperty(convert[model], 'labels', {value: labels});\n\t}\n}\n\nconvert.rgb.hsl = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar min = Math.min(r, g, b);\n\tvar max = Math.max(r, g, b);\n\tvar delta = max - min;\n\tvar h;\n\tvar s;\n\tvar l;\n\n\tif (max === min) {\n\t\th = 0;\n\t} else if (r === max) {\n\t\th = (g - b) / delta;\n\t} else if (g === max) {\n\t\th = 2 + (b - r) / delta;\n\t} else if (b === max) {\n\t\th = 4 + (r - g) / delta;\n\t}\n\n\th = Math.min(h * 60, 360);\n\n\tif (h < 0) {\n\t\th += 360;\n\t}\n\n\tl = (min + max) / 2;\n\n\tif (max === min) {\n\t\ts = 0;\n\t} else if (l <= 0.5) {\n\t\ts = delta / (max + min);\n\t} else {\n\t\ts = delta / (2 - max - min);\n\t}\n\n\treturn [h, s * 100, l * 100];\n};\n\nconvert.rgb.hsv = function (rgb) {\n\tvar rdif;\n\tvar gdif;\n\tvar bdif;\n\tvar h;\n\tvar s;\n\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar v = Math.max(r, g, b);\n\tvar diff = v - Math.min(r, g, b);\n\tvar diffc = function (c) {\n\t\treturn (v - c) / 6 / diff + 1 / 2;\n\t};\n\n\tif (diff === 0) {\n\t\th = s = 0;\n\t} else {\n\t\ts = diff / v;\n\t\trdif = diffc(r);\n\t\tgdif = diffc(g);\n\t\tbdif = diffc(b);\n\n\t\tif (r === v) {\n\t\t\th = bdif - gdif;\n\t\t} else if (g === v) {\n\t\t\th = (1 / 3) + rdif - bdif;\n\t\t} else if (b === v) {\n\t\t\th = (2 / 3) + gdif - rdif;\n\t\t}\n\t\tif (h < 0) {\n\t\t\th += 1;\n\t\t} else if (h > 1) {\n\t\t\th -= 1;\n\t\t}\n\t}\n\n\treturn [\n\t\th * 360,\n\t\ts * 100,\n\t\tv * 100\n\t];\n};\n\nconvert.rgb.hwb = function (rgb) {\n\tvar r = rgb[0];\n\tvar g = rgb[1];\n\tvar b = rgb[2];\n\tvar h = convert.rgb.hsl(rgb)[0];\n\tvar w = 1 / 255 * Math.min(r, Math.min(g, b));\n\n\tb = 1 - 1 / 255 * Math.max(r, Math.max(g, b));\n\n\treturn [h, w * 100, b * 100];\n};\n\nconvert.rgb.cmyk = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar c;\n\tvar m;\n\tvar y;\n\tvar k;\n\n\tk = Math.min(1 - r, 1 - g, 1 - b);\n\tc = (1 - r - k) / (1 - k) || 0;\n\tm = (1 - g - k) / (1 - k) || 0;\n\ty = (1 - b - k) / (1 - k) || 0;\n\n\treturn [c * 100, m * 100, y * 100, k * 100];\n};\n\n/**\n * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance\n * */\nfunction comparativeDistance(x, y) {\n\treturn (\n\t\tMath.pow(x[0] - y[0], 2) +\n\t\tMath.pow(x[1] - y[1], 2) +\n\t\tMath.pow(x[2] - y[2], 2)\n\t);\n}\n\nconvert.rgb.keyword = function (rgb) {\n\tvar reversed = reverseKeywords[rgb];\n\tif (reversed) {\n\t\treturn reversed;\n\t}\n\n\tvar currentClosestDistance = Infinity;\n\tvar currentClosestKeyword;\n\n\tfor (var keyword in cssKeywords) {\n\t\tif (cssKeywords.hasOwnProperty(keyword)) {\n\t\t\tvar value = cssKeywords[keyword];\n\n\t\t\t// Compute comparative distance\n\t\t\tvar distance = comparativeDistance(rgb, value);\n\n\t\t\t// Check if its less, if so set as closest\n\t\t\tif (distance < currentClosestDistance) {\n\t\t\t\tcurrentClosestDistance = distance;\n\t\t\t\tcurrentClosestKeyword = keyword;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn currentClosestKeyword;\n};\n\nconvert.keyword.rgb = function (keyword) {\n\treturn cssKeywords[keyword];\n};\n\nconvert.rgb.xyz = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\n\t// assume sRGB\n\tr = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);\n\tg = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);\n\tb = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);\n\n\tvar x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);\n\tvar y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);\n\tvar z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);\n\n\treturn [x * 100, y * 100, z * 100];\n};\n\nconvert.rgb.lab = function (rgb) {\n\tvar xyz = convert.rgb.xyz(rgb);\n\tvar x = xyz[0];\n\tvar y = xyz[1];\n\tvar z = xyz[2];\n\tvar l;\n\tvar a;\n\tvar b;\n\n\tx /= 95.047;\n\ty /= 100;\n\tz /= 108.883;\n\n\tx = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);\n\ty = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);\n\tz = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);\n\n\tl = (116 * y) - 16;\n\ta = 500 * (x - y);\n\tb = 200 * (y - z);\n\n\treturn [l, a, b];\n};\n\nconvert.hsl.rgb = function (hsl) {\n\tvar h = hsl[0] / 360;\n\tvar s = hsl[1] / 100;\n\tvar l = hsl[2] / 100;\n\tvar t1;\n\tvar t2;\n\tvar t3;\n\tvar rgb;\n\tvar val;\n\n\tif (s === 0) {\n\t\tval = l * 255;\n\t\treturn [val, val, val];\n\t}\n\n\tif (l < 0.5) {\n\t\tt2 = l * (1 + s);\n\t} else {\n\t\tt2 = l + s - l * s;\n\t}\n\n\tt1 = 2 * l - t2;\n\n\trgb = [0, 0, 0];\n\tfor (var i = 0; i < 3; i++) {\n\t\tt3 = h + 1 / 3 * -(i - 1);\n\t\tif (t3 < 0) {\n\t\t\tt3++;\n\t\t}\n\t\tif (t3 > 1) {\n\t\t\tt3--;\n\t\t}\n\n\t\tif (6 * t3 < 1) {\n\t\t\tval = t1 + (t2 - t1) * 6 * t3;\n\t\t} else if (2 * t3 < 1) {\n\t\t\tval = t2;\n\t\t} else if (3 * t3 < 2) {\n\t\t\tval = t1 + (t2 - t1) * (2 / 3 - t3) * 6;\n\t\t} else {\n\t\t\tval = t1;\n\t\t}\n\n\t\trgb[i] = val * 255;\n\t}\n\n\treturn rgb;\n};\n\nconvert.hsl.hsv = function (hsl) {\n\tvar h = hsl[0];\n\tvar s = hsl[1] / 100;\n\tvar l = hsl[2] / 100;\n\tvar smin = s;\n\tvar lmin = Math.max(l, 0.01);\n\tvar sv;\n\tvar v;\n\n\tl *= 2;\n\ts *= (l <= 1) ? l : 2 - l;\n\tsmin *= lmin <= 1 ? lmin : 2 - lmin;\n\tv = (l + s) / 2;\n\tsv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);\n\n\treturn [h, sv * 100, v * 100];\n};\n\nconvert.hsv.rgb = function (hsv) {\n\tvar h = hsv[0] / 60;\n\tvar s = hsv[1] / 100;\n\tvar v = hsv[2] / 100;\n\tvar hi = Math.floor(h) % 6;\n\n\tvar f = h - Math.floor(h);\n\tvar p = 255 * v * (1 - s);\n\tvar q = 255 * v * (1 - (s * f));\n\tvar t = 255 * v * (1 - (s * (1 - f)));\n\tv *= 255;\n\n\tswitch (hi) {\n\t\tcase 0:\n\t\t\treturn [v, t, p];\n\t\tcase 1:\n\t\t\treturn [q, v, p];\n\t\tcase 2:\n\t\t\treturn [p, v, t];\n\t\tcase 3:\n\t\t\treturn [p, q, v];\n\t\tcase 4:\n\t\t\treturn [t, p, v];\n\t\tcase 5:\n\t\t\treturn [v, p, q];\n\t}\n};\n\nconvert.hsv.hsl = function (hsv) {\n\tvar h = hsv[0];\n\tvar s = hsv[1] / 100;\n\tvar v = hsv[2] / 100;\n\tvar vmin = Math.max(v, 0.01);\n\tvar lmin;\n\tvar sl;\n\tvar l;\n\n\tl = (2 - s) * v;\n\tlmin = (2 - s) * vmin;\n\tsl = s * vmin;\n\tsl /= (lmin <= 1) ? lmin : 2 - lmin;\n\tsl = sl || 0;\n\tl /= 2;\n\n\treturn [h, sl * 100, l * 100];\n};\n\n// http://dev.w3.org/csswg/css-color/#hwb-to-rgb\nconvert.hwb.rgb = function (hwb) {\n\tvar h = hwb[0] / 360;\n\tvar wh = hwb[1] / 100;\n\tvar bl = hwb[2] / 100;\n\tvar ratio = wh + bl;\n\tvar i;\n\tvar v;\n\tvar f;\n\tvar n;\n\n\t// wh + bl cant be > 1\n\tif (ratio > 1) {\n\t\twh /= ratio;\n\t\tbl /= ratio;\n\t}\n\n\ti = Math.floor(6 * h);\n\tv = 1 - bl;\n\tf = 6 * h - i;\n\n\tif ((i & 0x01) !== 0) {\n\t\tf = 1 - f;\n\t}\n\n\tn = wh + f * (v - wh); // linear interpolation\n\n\tvar r;\n\tvar g;\n\tvar b;\n\tswitch (i) {\n\t\tdefault:\n\t\tcase 6:\n\t\tcase 0: r = v; g = n; b = wh; break;\n\t\tcase 1: r = n; g = v; b = wh; break;\n\t\tcase 2: r = wh; g = v; b = n; break;\n\t\tcase 3: r = wh; g = n; b = v; break;\n\t\tcase 4: r = n; g = wh; b = v; break;\n\t\tcase 5: r = v; g = wh; b = n; break;\n\t}\n\n\treturn [r * 255, g * 255, b * 255];\n};\n\nconvert.cmyk.rgb = function (cmyk) {\n\tvar c = cmyk[0] / 100;\n\tvar m = cmyk[1] / 100;\n\tvar y = cmyk[2] / 100;\n\tvar k = cmyk[3] / 100;\n\tvar r;\n\tvar g;\n\tvar b;\n\n\tr = 1 - Math.min(1, c * (1 - k) + k);\n\tg = 1 - Math.min(1, m * (1 - k) + k);\n\tb = 1 - Math.min(1, y * (1 - k) + k);\n\n\treturn [r * 255, g * 255, b * 255];\n};\n\nconvert.xyz.rgb = function (xyz) {\n\tvar x = xyz[0] / 100;\n\tvar y = xyz[1] / 100;\n\tvar z = xyz[2] / 100;\n\tvar r;\n\tvar g;\n\tvar b;\n\n\tr = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);\n\tg = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);\n\tb = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);\n\n\t// assume sRGB\n\tr = r > 0.0031308\n\t\t? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)\n\t\t: r * 12.92;\n\n\tg = g > 0.0031308\n\t\t? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)\n\t\t: g * 12.92;\n\n\tb = b > 0.0031308\n\t\t? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)\n\t\t: b * 12.92;\n\n\tr = Math.min(Math.max(0, r), 1);\n\tg = Math.min(Math.max(0, g), 1);\n\tb = Math.min(Math.max(0, b), 1);\n\n\treturn [r * 255, g * 255, b * 255];\n};\n\nconvert.xyz.lab = function (xyz) {\n\tvar x = xyz[0];\n\tvar y = xyz[1];\n\tvar z = xyz[2];\n\tvar l;\n\tvar a;\n\tvar b;\n\n\tx /= 95.047;\n\ty /= 100;\n\tz /= 108.883;\n\n\tx = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);\n\ty = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);\n\tz = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);\n\n\tl = (116 * y) - 16;\n\ta = 500 * (x - y);\n\tb = 200 * (y - z);\n\n\treturn [l, a, b];\n};\n\nconvert.lab.xyz = function (lab) {\n\tvar l = lab[0];\n\tvar a = lab[1];\n\tvar b = lab[2];\n\tvar x;\n\tvar y;\n\tvar z;\n\n\ty = (l + 16) / 116;\n\tx = a / 500 + y;\n\tz = y - b / 200;\n\n\tvar y2 = Math.pow(y, 3);\n\tvar x2 = Math.pow(x, 3);\n\tvar z2 = Math.pow(z, 3);\n\ty = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;\n\tx = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;\n\tz = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;\n\n\tx *= 95.047;\n\ty *= 100;\n\tz *= 108.883;\n\n\treturn [x, y, z];\n};\n\nconvert.lab.lch = function (lab) {\n\tvar l = lab[0];\n\tvar a = lab[1];\n\tvar b = lab[2];\n\tvar hr;\n\tvar h;\n\tvar c;\n\n\thr = Math.atan2(b, a);\n\th = hr * 360 / 2 / Math.PI;\n\n\tif (h < 0) {\n\t\th += 360;\n\t}\n\n\tc = Math.sqrt(a * a + b * b);\n\n\treturn [l, c, h];\n};\n\nconvert.lch.lab = function (lch) {\n\tvar l = lch[0];\n\tvar c = lch[1];\n\tvar h = lch[2];\n\tvar a;\n\tvar b;\n\tvar hr;\n\n\thr = h / 360 * 2 * Math.PI;\n\ta = c * Math.cos(hr);\n\tb = c * Math.sin(hr);\n\n\treturn [l, a, b];\n};\n\nconvert.rgb.ansi16 = function (args) {\n\tvar r = args[0];\n\tvar g = args[1];\n\tvar b = args[2];\n\tvar value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization\n\n\tvalue = Math.round(value / 50);\n\n\tif (value === 0) {\n\t\treturn 30;\n\t}\n\n\tvar ansi = 30\n\t\t+ ((Math.round(b / 255) << 2)\n\t\t| (Math.round(g / 255) << 1)\n\t\t| Math.round(r / 255));\n\n\tif (value === 2) {\n\t\tansi += 60;\n\t}\n\n\treturn ansi;\n};\n\nconvert.hsv.ansi16 = function (args) {\n\t// optimization here; we already know the value and don't need to get\n\t// it converted for us.\n\treturn convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);\n};\n\nconvert.rgb.ansi256 = function (args) {\n\tvar r = args[0];\n\tvar g = args[1];\n\tvar b = args[2];\n\n\t// we use the extended greyscale palette here, with the exception of\n\t// black and white. normal palette only has 4 greyscale shades.\n\tif (r === g && g === b) {\n\t\tif (r < 8) {\n\t\t\treturn 16;\n\t\t}\n\n\t\tif (r > 248) {\n\t\t\treturn 231;\n\t\t}\n\n\t\treturn Math.round(((r - 8) / 247) * 24) + 232;\n\t}\n\n\tvar ansi = 16\n\t\t+ (36 * Math.round(r / 255 * 5))\n\t\t+ (6 * Math.round(g / 255 * 5))\n\t\t+ Math.round(b / 255 * 5);\n\n\treturn ansi;\n};\n\nconvert.ansi16.rgb = function (args) {\n\tvar color = args % 10;\n\n\t// handle greyscale\n\tif (color === 0 || color === 7) {\n\t\tif (args > 50) {\n\t\t\tcolor += 3.5;\n\t\t}\n\n\t\tcolor = color / 10.5 * 255;\n\n\t\treturn [color, color, color];\n\t}\n\n\tvar mult = (~~(args > 50) + 1) * 0.5;\n\tvar r = ((color & 1) * mult) * 255;\n\tvar g = (((color >> 1) & 1) * mult) * 255;\n\tvar b = (((color >> 2) & 1) * mult) * 255;\n\n\treturn [r, g, b];\n};\n\nconvert.ansi256.rgb = function (args) {\n\t// handle greyscale\n\tif (args >= 232) {\n\t\tvar c = (args - 232) * 10 + 8;\n\t\treturn [c, c, c];\n\t}\n\n\targs -= 16;\n\n\tvar rem;\n\tvar r = Math.floor(args / 36) / 5 * 255;\n\tvar g = Math.floor((rem = args % 36) / 6) / 5 * 255;\n\tvar b = (rem % 6) / 5 * 255;\n\n\treturn [r, g, b];\n};\n\nconvert.rgb.hex = function (args) {\n\tvar integer = ((Math.round(args[0]) & 0xFF) << 16)\n\t\t+ ((Math.round(args[1]) & 0xFF) << 8)\n\t\t+ (Math.round(args[2]) & 0xFF);\n\n\tvar string = integer.toString(16).toUpperCase();\n\treturn '000000'.substring(string.length) + string;\n};\n\nconvert.hex.rgb = function (args) {\n\tvar match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);\n\tif (!match) {\n\t\treturn [0, 0, 0];\n\t}\n\n\tvar colorString = match[0];\n\n\tif (match[0].length === 3) {\n\t\tcolorString = colorString.split('').map(function (char) {\n\t\t\treturn char + char;\n\t\t}).join('');\n\t}\n\n\tvar integer = parseInt(colorString, 16);\n\tvar r = (integer >> 16) & 0xFF;\n\tvar g = (integer >> 8) & 0xFF;\n\tvar b = integer & 0xFF;\n\n\treturn [r, g, b];\n};\n\nconvert.rgb.hcg = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar max = Math.max(Math.max(r, g), b);\n\tvar min = Math.min(Math.min(r, g), b);\n\tvar chroma = (max - min);\n\tvar grayscale;\n\tvar hue;\n\n\tif (chroma < 1) {\n\t\tgrayscale = min / (1 - chroma);\n\t} else {\n\t\tgrayscale = 0;\n\t}\n\n\tif (chroma <= 0) {\n\t\thue = 0;\n\t} else\n\tif (max === r) {\n\t\thue = ((g - b) / chroma) % 6;\n\t} else\n\tif (max === g) {\n\t\thue = 2 + (b - r) / chroma;\n\t} else {\n\t\thue = 4 + (r - g) / chroma + 4;\n\t}\n\n\thue /= 6;\n\thue %= 1;\n\n\treturn [hue * 360, chroma * 100, grayscale * 100];\n};\n\nconvert.hsl.hcg = function (hsl) {\n\tvar s = hsl[1] / 100;\n\tvar l = hsl[2] / 100;\n\tvar c = 1;\n\tvar f = 0;\n\n\tif (l < 0.5) {\n\t\tc = 2.0 * s * l;\n\t} else {\n\t\tc = 2.0 * s * (1.0 - l);\n\t}\n\n\tif (c < 1.0) {\n\t\tf = (l - 0.5 * c) / (1.0 - c);\n\t}\n\n\treturn [hsl[0], c * 100, f * 100];\n};\n\nconvert.hsv.hcg = function (hsv) {\n\tvar s = hsv[1] / 100;\n\tvar v = hsv[2] / 100;\n\n\tvar c = s * v;\n\tvar f = 0;\n\n\tif (c < 1.0) {\n\t\tf = (v - c) / (1 - c);\n\t}\n\n\treturn [hsv[0], c * 100, f * 100];\n};\n\nconvert.hcg.rgb = function (hcg) {\n\tvar h = hcg[0] / 360;\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\n\tif (c === 0.0) {\n\t\treturn [g * 255, g * 255, g * 255];\n\t}\n\n\tvar pure = [0, 0, 0];\n\tvar hi = (h % 1) * 6;\n\tvar v = hi % 1;\n\tvar w = 1 - v;\n\tvar mg = 0;\n\n\tswitch (Math.floor(hi)) {\n\t\tcase 0:\n\t\t\tpure[0] = 1; pure[1] = v; pure[2] = 0; break;\n\t\tcase 1:\n\t\t\tpure[0] = w; pure[1] = 1; pure[2] = 0; break;\n\t\tcase 2:\n\t\t\tpure[0] = 0; pure[1] = 1; pure[2] = v; break;\n\t\tcase 3:\n\t\t\tpure[0] = 0; pure[1] = w; pure[2] = 1; break;\n\t\tcase 4:\n\t\t\tpure[0] = v; pure[1] = 0; pure[2] = 1; break;\n\t\tdefault:\n\t\t\tpure[0] = 1; pure[1] = 0; pure[2] = w;\n\t}\n\n\tmg = (1.0 - c) * g;\n\n\treturn [\n\t\t(c * pure[0] + mg) * 255,\n\t\t(c * pure[1] + mg) * 255,\n\t\t(c * pure[2] + mg) * 255\n\t];\n};\n\nconvert.hcg.hsv = function (hcg) {\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\n\tvar v = c + g * (1.0 - c);\n\tvar f = 0;\n\n\tif (v > 0.0) {\n\t\tf = c / v;\n\t}\n\n\treturn [hcg[0], f * 100, v * 100];\n};\n\nconvert.hcg.hsl = function (hcg) {\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\n\tvar l = g * (1.0 - c) + 0.5 * c;\n\tvar s = 0;\n\n\tif (l > 0.0 && l < 0.5) {\n\t\ts = c / (2 * l);\n\t} else\n\tif (l >= 0.5 && l < 1.0) {\n\t\ts = c / (2 * (1 - l));\n\t}\n\n\treturn [hcg[0], s * 100, l * 100];\n};\n\nconvert.hcg.hwb = function (hcg) {\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\tvar v = c + g * (1.0 - c);\n\treturn [hcg[0], (v - c) * 100, (1 - v) * 100];\n};\n\nconvert.hwb.hcg = function (hwb) {\n\tvar w = hwb[1] / 100;\n\tvar b = hwb[2] / 100;\n\tvar v = 1 - b;\n\tvar c = v - w;\n\tvar g = 0;\n\n\tif (c < 1) {\n\t\tg = (v - c) / (1 - c);\n\t}\n\n\treturn [hwb[0], c * 100, g * 100];\n};\n\nconvert.apple.rgb = function (apple) {\n\treturn [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];\n};\n\nconvert.rgb.apple = function (rgb) {\n\treturn [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];\n};\n\nconvert.gray.rgb = function (args) {\n\treturn [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];\n};\n\nconvert.gray.hsl = convert.gray.hsv = function (args) {\n\treturn [0, 0, args[0]];\n};\n\nconvert.gray.hwb = function (gray) {\n\treturn [0, 100, gray[0]];\n};\n\nconvert.gray.cmyk = function (gray) {\n\treturn [0, 0, 0, gray[0]];\n};\n\nconvert.gray.lab = function (gray) {\n\treturn [gray[0], 0, 0];\n};\n\nconvert.gray.hex = function (gray) {\n\tvar val = Math.round(gray[0] / 100 * 255) & 0xFF;\n\tvar integer = (val << 16) + (val << 8) + val;\n\n\tvar string = integer.toString(16).toUpperCase();\n\treturn '000000'.substring(string.length) + string;\n};\n\nconvert.rgb.gray = function (rgb) {\n\tvar val = (rgb[0] + rgb[1] + rgb[2]) / 3;\n\treturn [val / 255 * 100];\n};\n","/**\n*\n* jquery.sparkline.js\n*\n* v2.4.1\n* (c) Splunk, Inc\n* Contact: Gareth Watts (gareth@splunk.com)\n* http://omnipotent.net/jquery.sparkline/\n*\n* Generates inline sparkline charts from data supplied either to the method\n* or inline in HTML\n*\n* Compatible with Internet Explorer 6.0+ and modern browsers equipped with the canvas tag\n* (Firefox 2.0+, Safari, Opera, etc)\n*\n* License: New BSD License\n*\n* Copyright (c) 2012, Splunk Inc.\n* All rights reserved.\n*\n* Redistribution and use in source and binary forms, with or without modification,\n* are permitted provided that the following conditions are met:\n*\n* * Redistributions of source code must retain the above copyright notice,\n* this list of conditions and the following disclaimer.\n* * Redistributions in binary form must reproduce the above copyright notice,\n* this list of conditions and the following disclaimer in the documentation\n* and/or other materials provided with the distribution.\n* * Neither the name of Splunk Inc nor the names of its contributors may\n* be used to endorse or promote products derived from this software without\n* specific prior written permission.\n*\n* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY\n* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT\n* SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\n* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*\n*\n* Usage:\n* $(selector).sparkline(values, options)\n*\n* If values is undefined or set to 'html' then the data values are read from the specified tag:\n*

Sparkline: 1,4,6,6,8,5,3,5

\n* $('.sparkline').sparkline();\n* There must be no spaces in the enclosed data set\n*\n* Otherwise values must be an array of numbers or null values\n*

Sparkline: This text replaced if the browser is compatible

\n* $('#sparkline1').sparkline([1,4,6,6,8,5,3,5])\n* $('#sparkline2').sparkline([1,4,6,null,null,5,3,5])\n*\n* Values can also be specified in an HTML comment, or as a values attribute:\n*

Sparkline:

\n*

Sparkline:

\n* $('.sparkline').sparkline();\n*\n* For line charts, x values can also be specified:\n*

Sparkline: 1:1,2.7:4,3.4:6,5:6,6:8,8.7:5,9:3,10:5

\n* $('#sparkline1').sparkline([ [1,1], [2.7,4], [3.4,6], [5,6], [6,8], [8.7,5], [9,3], [10,5] ])\n*\n* By default, options should be passed in as the second argument to the sparkline function:\n* $('.sparkline').sparkline([1,2,3,4], {type: 'bar'})\n*\n* Options can also be set by passing them on the tag itself. This feature is disabled by default though\n* as there's a slight performance overhead:\n* $('.sparkline').sparkline([1,2,3,4], {enableTagOptions: true})\n*

Sparkline: loading

\n* Prefix all options supplied as tag attribute with \"spark\" (configurable by setting tagOptionsPrefix)\n*\n* Supported options:\n* lineColor - Color of the line used for the chart\n* fillColor - Color used to fill in the chart - Set to '' or false for a transparent chart\n* width - Width of the chart - Defaults to 3 times the number of values in pixels\n* height - Height of the chart - Defaults to the height of the containing element\n* chartRangeMin - Specify the minimum value to use for the Y range of the chart - Defaults to the minimum value supplied\n* chartRangeMax - Specify the maximum value to use for the Y range of the chart - Defaults to the maximum value supplied\n* chartRangeClip - Clip out of range values to the max/min specified by chartRangeMin and chartRangeMax\n* chartRangeMinX - Specify the minimum value to use for the X range of the chart - Defaults to the minimum value supplied\n* chartRangeMaxX - Specify the maximum value to use for the X range of the chart - Defaults to the maximum value supplied\n* composite - If true then don't erase any existing chart attached to the tag, but draw\n* another chart over the top - Note that width and height are ignored if an\n* existing chart is detected.\n* tagValuesAttribute - Name of tag attribute to check for data values - Defaults to 'values'\n* enableTagOptions - Whether to check tags for sparkline options\n* tagOptionsPrefix - Prefix used for options supplied as tag attributes - Defaults to 'spark'\n* disableHiddenCheck - If set to true, then the plugin will assume that charts will never be drawn into a\n* hidden dom element, avoding a browser reflow\n* disableInteraction - If set to true then all mouseover/click interaction behaviour will be disabled,\n* making the plugin perform much like it did in 1.x\n* disableTooltips - If set to true then tooltips will be disabled - Defaults to false (tooltips enabled)\n* disableHighlight - If set to true then highlighting of selected chart elements on mouseover will be disabled\n* defaults to false (highlights enabled)\n* highlightLighten - Factor to lighten/darken highlighted chart values by - Defaults to 1.4 for a 40% increase\n* tooltipContainer - Specify which DOM element the tooltip should be rendered into - defaults to document.body\n* tooltipClassname - Optional CSS classname to apply to tooltips - If not specified then a default style will be applied\n* tooltipOffsetX - How many pixels away from the mouse pointer to render the tooltip on the X axis\n* tooltipOffsetY - How many pixels away from the mouse pointer to render the tooltip on the r axis\n* tooltipFormatter - Optional callback that allows you to override the HTML displayed in the tooltip\n* callback is given arguments of (sparkline, options, fields)\n* tooltipChartTitle - If specified then the tooltip uses the string specified by this setting as a title\n* tooltipFormat - A format string or SPFormat object (or an array thereof for multiple entries)\n* to control the format of the tooltip\n* tooltipPrefix - A string to prepend to each field displayed in a tooltip\n* tooltipSuffix - A string to append to each field displayed in a tooltip\n* tooltipSkipNull - If true then null values will not have a tooltip displayed (defaults to true)\n* tooltipValueLookups - An object or range map to map field values to tooltip strings\n* (eg. to map -1 to \"Lost\", 0 to \"Draw\", and 1 to \"Win\")\n* numberFormatter - Optional callback for formatting numbers in tooltips\n* numberDigitGroupSep - Character to use for group separator in numbers \"1,234\" - Defaults to \",\"\n* numberDecimalMark - Character to use for the decimal point when formatting numbers - Defaults to \".\"\n* numberDigitGroupCount - Number of digits between group separator - Defaults to 3\n*\n* There are 7 types of sparkline, selected by supplying a \"type\" option of 'line' (default),\n* 'bar', 'tristate', 'bullet', 'discrete', 'pie' or 'box'\n* line - Line chart. Options:\n* spotColor - Set to '' to not end each line in a circular spot\n* minSpotColor - If set, color of spot at minimum value\n* maxSpotColor - If set, color of spot at maximum value\n* spotRadius - Radius in pixels\n* lineWidth - Width of line in pixels\n* normalRangeMin\n* normalRangeMax - If set draws a filled horizontal bar between these two values marking the \"normal\"\n* or expected range of values\n* normalRangeColor - Color to use for the above bar\n* drawNormalOnTop - Draw the normal range above the chart fill color if true\n* defaultPixelsPerValue - Defaults to 3 pixels of width for each value in the chart\n* highlightSpotColor - The color to use for drawing a highlight spot on mouseover - Set to null to disable\n* highlightLineColor - The color to use for drawing a highlight line on mouseover - Set to null to disable\n* valueSpots - Specify which points to draw spots on, and in which color. Accepts a range map\n*\n* bar - Bar chart. Options:\n* barColor - Color of bars for postive values\n* negBarColor - Color of bars for negative values\n* zeroColor - Color of bars with zero values\n* nullColor - Color of bars with null values - Defaults to omitting the bar entirely\n* barWidth - Width of bars in pixels\n* colorMap - Optional mappnig of values to colors to override the *BarColor values above\n* can be an Array of values to control the color of individual bars or a range map\n* to specify colors for individual ranges of values\n* barSpacing - Gap between bars in pixels\n* zeroAxis - Centers the y-axis around zero if true\n*\n* tristate - Charts values of win (>0), lose (<0) or draw (=0)\n* posBarColor - Color of win values\n* negBarColor - Color of lose values\n* zeroBarColor - Color of draw values\n* barWidth - Width of bars in pixels\n* barSpacing - Gap between bars in pixels\n* colorMap - Optional mappnig of values to colors to override the *BarColor values above\n* can be an Array of values to control the color of individual bars or a range map\n* to specify colors for individual ranges of values\n*\n* discrete - Options:\n* lineHeight - Height of each line in pixels - Defaults to 30% of the graph height\n* thesholdValue - Values less than this value will be drawn using thresholdColor instead of lineColor\n* thresholdColor\n*\n* bullet - Values for bullet graphs msut be in the order: target, performance, range1, range2, range3, ...\n* options:\n* targetColor - The color of the vertical target marker\n* targetWidth - The width of the target marker in pixels\n* performanceColor - The color of the performance measure horizontal bar\n* rangeColors - Colors to use for each qualitative range background color\n*\n* pie - Pie chart. Options:\n* sliceColors - An array of colors to use for pie slices\n* offset - Angle in degrees to offset the first slice - Try -90 or +90\n* borderWidth - Width of border to draw around the pie chart, in pixels - Defaults to 0 (no border)\n* borderColor - Color to use for the pie chart border - Defaults to #000\n*\n* box - Box plot. Options:\n* raw - Set to true to supply pre-computed plot points as values\n* values should be: low_outlier, low_whisker, q1, median, q3, high_whisker, high_outlier\n* When set to false you can supply any number of values and the box plot will\n* be computed for you. Default is false.\n* showOutliers - Set to true (default) to display outliers as circles\n* outlierIQR - Interquartile range used to determine outliers. Default 1.5\n* boxLineColor - Outline color of the box\n* boxFillColor - Fill color for the box\n* whiskerColor - Line color used for whiskers\n* outlierLineColor - Outline color of outlier circles\n* outlierFillColor - Fill color of the outlier circles\n* spotRadius - Radius of outlier circles\n* medianColor - Line color of the median line\n* target - Draw a target cross hair at the supplied value (default undefined)\n*\n*\n*\n* Examples:\n* $('#sparkline1').sparkline(myvalues, { lineColor: '#f00', fillColor: false });\n* $('.barsparks').sparkline('html', { type:'bar', height:'40px', barWidth:5 });\n* $('#tristate').sparkline([1,1,-1,1,0,0,-1], { type:'tristate' }):\n* $('#discrete').sparkline([1,3,4,5,5,3,4,5], { type:'discrete' });\n* $('#bullet').sparkline([10,12,12,9,7], { type:'bullet' });\n* $('#pie').sparkline([1,1,2], { type:'pie' });\n*/\n\n/*jslint regexp: true, browser: true, jquery: true, white: true, nomen: false, plusplus: false, maxerr: 500, indent: 4 */\n\n(function(document, Math, undefined) { // performance/minified-size optimization\n(function(factory) {\n if(typeof define === 'function' && define.amd) {\n define(['jquery'], factory);\n } else if (jQuery && !jQuery.fn.sparkline) {\n factory(jQuery);\n }\n}\n(function($) {\n 'use strict';\n\n var UNSET_OPTION = {},\n getDefaults, createClass, SPFormat, clipval, quartile, normalizeValue, normalizeValues,\n remove, isNumber, all, sum, addCSS, ensureArray, formatNumber, RangeMap,\n MouseHandler, Tooltip, barHighlightMixin,\n line, bar, tristate, discrete, bullet, pie, box, defaultStyles, initStyles,\n VShape, VCanvas_base, VCanvas_canvas, VCanvas_vml, pending, shapeCount = 0;\n\n /**\n * Default configuration settings\n */\n getDefaults = function () {\n return {\n // Settings common to most/all chart types\n common: {\n type: 'line',\n lineColor: '#00f',\n fillColor: '#cdf',\n defaultPixelsPerValue: 3,\n width: 'auto',\n height: 'auto',\n composite: false,\n tagValuesAttribute: 'values',\n tagOptionsPrefix: 'spark',\n enableTagOptions: false,\n enableHighlight: true,\n highlightLighten: 1.4,\n tooltipSkipNull: true,\n tooltipPrefix: '',\n tooltipSuffix: '',\n disableHiddenCheck: false,\n numberFormatter: false,\n numberDigitGroupCount: 3,\n numberDigitGroupSep: ',',\n numberDecimalMark: '.',\n disableTooltips: false,\n disableInteraction: false\n },\n // Defaults for line charts\n line: {\n spotColor: '#f80',\n highlightSpotColor: '#5f5',\n highlightLineColor: '#f22',\n spotRadius: 1.5,\n minSpotColor: '#f80',\n maxSpotColor: '#f80',\n lineWidth: 1,\n normalRangeMin: undefined,\n normalRangeMax: undefined,\n normalRangeColor: '#ccc',\n drawNormalOnTop: false,\n chartRangeMin: undefined,\n chartRangeMax: undefined,\n chartRangeMinX: undefined,\n chartRangeMaxX: undefined,\n tooltipFormat: new SPFormat(' {{prefix}}{{y}}{{suffix}}')\n },\n // Defaults for bar charts\n bar: {\n barColor: '#3366cc',\n negBarColor: '#f44',\n stackedBarColor: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#66aa00',\n '#dd4477', '#0099c6', '#990099'],\n zeroColor: undefined,\n nullColor: undefined,\n zeroAxis: true,\n barWidth: 4,\n barSpacing: 1,\n chartRangeMax: undefined,\n chartRangeMin: undefined,\n chartRangeClip: false,\n colorMap: undefined,\n tooltipFormat: new SPFormat(' {{prefix}}{{value}}{{suffix}}')\n },\n // Defaults for tristate charts\n tristate: {\n barWidth: 4,\n barSpacing: 1,\n posBarColor: '#6f6',\n negBarColor: '#f44',\n zeroBarColor: '#999',\n colorMap: {},\n tooltipFormat: new SPFormat(' {{value:map}}'),\n tooltipValueLookups: { map: { '-1': 'Loss', '0': 'Draw', '1': 'Win' } }\n },\n // Defaults for discrete charts\n discrete: {\n lineHeight: 'auto',\n thresholdColor: undefined,\n thresholdValue: 0,\n chartRangeMax: undefined,\n chartRangeMin: undefined,\n chartRangeClip: false,\n tooltipFormat: new SPFormat('{{prefix}}{{value}}{{suffix}}')\n },\n // Defaults for bullet charts\n bullet: {\n targetColor: '#f33',\n targetWidth: 3, // width of the target bar in pixels\n performanceColor: '#33f',\n rangeColors: ['#d3dafe', '#a8b6ff', '#7f94ff'],\n base: undefined, // set this to a number to change the base start number\n tooltipFormat: new SPFormat('{{fieldkey:fields}} - {{value}}'),\n tooltipValueLookups: { fields: {r: 'Range', p: 'Performance', t: 'Target'} }\n },\n // Defaults for pie charts\n pie: {\n offset: 0,\n sliceColors: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#66aa00',\n '#dd4477', '#0099c6', '#990099'],\n borderWidth: 0,\n borderColor: '#000',\n tooltipFormat: new SPFormat(' {{value}} ({{percent.1}}%)')\n },\n // Defaults for box plots\n box: {\n raw: false,\n boxLineColor: '#000',\n boxFillColor: '#cdf',\n whiskerColor: '#000',\n outlierLineColor: '#333',\n outlierFillColor: '#fff',\n medianColor: '#f00',\n showOutliers: true,\n outlierIQR: 1.5,\n spotRadius: 1.5,\n target: undefined,\n targetColor: '#4a2',\n chartRangeMax: undefined,\n chartRangeMin: undefined,\n tooltipFormat: new SPFormat('{{field:fields}}: {{value}}'),\n tooltipFormatFieldlistKey: 'field',\n tooltipValueLookups: { fields: { lq: 'Lower Quartile', med: 'Median',\n uq: 'Upper Quartile', lo: 'Left Outlier', ro: 'Right Outlier',\n lw: 'Left Whisker', rw: 'Right Whisker'} }\n }\n };\n };\n\n // You can have tooltips use a css class other than jqstooltip by specifying tooltipClassname\n defaultStyles = '.jqstooltip { ' +\n 'position: absolute;' +\n 'left: 0px;' +\n 'top: 0px;' +\n 'visibility: hidden;' +\n 'background: rgb(0, 0, 0) transparent;' +\n 'background-color: rgba(0,0,0,0.6);' +\n 'filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);' +\n '-ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)\";' +\n 'color: white;' +\n 'font: 10px arial, san serif;' +\n 'text-align: left;' +\n 'white-space: nowrap;' +\n 'padding: 5px;' +\n 'border: 1px solid white;' +\n 'box-sizing: content-box;' +\n 'z-index: 10000;' +\n '}' +\n '.jqsfield { ' +\n 'color: white;' +\n 'font: 10px arial, san serif;' +\n 'text-align: left;' +\n '}';\n\n /**\n * Utilities\n */\n\n createClass = function (/* [baseclass, [mixin, ...]], definition */) {\n var Class, args;\n Class = function () {\n this.init.apply(this, arguments);\n };\n if (arguments.length > 1) {\n if (arguments[0]) {\n Class.prototype = $.extend(new arguments[0](), arguments[arguments.length - 1]);\n Class._super = arguments[0].prototype;\n } else {\n Class.prototype = arguments[arguments.length - 1];\n }\n if (arguments.length > 2) {\n args = Array.prototype.slice.call(arguments, 1, -1);\n args.unshift(Class.prototype);\n $.extend.apply($, args);\n }\n } else {\n Class.prototype = arguments[0];\n }\n Class.prototype.cls = Class;\n return Class;\n };\n\n /**\n * Wraps a format string for tooltips\n * {{x}}\n * {{x.2}\n * {{x:months}}\n */\n $.SPFormatClass = SPFormat = createClass({\n fre: /\\{\\{([\\w.]+?)(:(.+?))?\\}\\}/g,\n precre: /(\\w+)\\.(\\d+)/,\n\n init: function (format, fclass) {\n this.format = format;\n this.fclass = fclass;\n },\n\n render: function (fieldset, lookups, options) {\n var self = this,\n fields = fieldset,\n match, token, lookupkey, fieldvalue, prec;\n return this.format.replace(this.fre, function () {\n var lookup;\n token = arguments[1];\n lookupkey = arguments[3];\n match = self.precre.exec(token);\n if (match) {\n prec = match[2];\n token = match[1];\n } else {\n prec = false;\n }\n fieldvalue = fields[token];\n if (fieldvalue === undefined) {\n return '';\n }\n if (lookupkey && lookups && lookups[lookupkey]) {\n lookup = lookups[lookupkey];\n if (lookup.get) { // RangeMap\n return lookups[lookupkey].get(fieldvalue) || fieldvalue;\n } else {\n return lookups[lookupkey][fieldvalue] || fieldvalue;\n }\n }\n if (isNumber(fieldvalue)) {\n if (options.get('numberFormatter')) {\n fieldvalue = options.get('numberFormatter')(fieldvalue);\n } else {\n fieldvalue = formatNumber(fieldvalue, prec,\n options.get('numberDigitGroupCount'),\n options.get('numberDigitGroupSep'),\n options.get('numberDecimalMark'));\n }\n }\n return fieldvalue;\n });\n }\n });\n\n // convience method to avoid needing the new operator\n $.spformat = function(format, fclass) {\n return new SPFormat(format, fclass);\n };\n\n clipval = function (val, min, max) {\n if (val < min) {\n return min;\n }\n if (val > max) {\n return max;\n }\n return val;\n };\n\n quartile = function (values, q) {\n var vl;\n if (q === 2) {\n vl = Math.floor(values.length / 2);\n return values.length % 2 ? values[vl] : (values[vl-1] + values[vl]) / 2;\n } else {\n if (values.length % 2 ) { // odd\n vl = (values.length * q + q) / 4;\n return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1];\n } else { //even\n vl = (values.length * q + 2) / 4;\n return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1];\n\n }\n }\n };\n\n normalizeValue = function (val) {\n var nf;\n switch (val) {\n case 'undefined':\n val = undefined;\n break;\n case 'null':\n val = null;\n break;\n case 'true':\n val = true;\n break;\n case 'false':\n val = false;\n break;\n default:\n nf = parseFloat(val);\n if (val == nf) {\n val = nf;\n }\n }\n return val;\n };\n\n normalizeValues = function (vals) {\n var i, result = [];\n for (i = vals.length; i--;) {\n result[i] = normalizeValue(vals[i]);\n }\n return result;\n };\n\n remove = function (vals, filter) {\n var i, vl, result = [];\n for (i = 0, vl = vals.length; i < vl; i++) {\n if (vals[i] !== filter) {\n result.push(vals[i]);\n }\n }\n return result;\n };\n\n isNumber = function (num) {\n return !isNaN(parseFloat(num)) && isFinite(num);\n };\n\n formatNumber = function (num, prec, groupsize, groupsep, decsep) {\n var p, i;\n num = (prec === false ? parseFloat(num).toString() : num.toFixed(prec)).split('');\n p = (p = $.inArray('.', num)) < 0 ? num.length : p;\n if (p < num.length) {\n num[p] = decsep;\n }\n for (i = p - groupsize; i > 0; i -= groupsize) {\n num.splice(i, 0, groupsep);\n }\n return num.join('');\n };\n\n // determine if all values of an array match a value\n // returns true if the array is empty\n all = function (val, arr, ignoreNull) {\n var i;\n for (i = arr.length; i--; ) {\n if (ignoreNull && arr[i] === null) continue;\n if (arr[i] !== val) {\n return false;\n }\n }\n return true;\n };\n\n // sums the numeric values in an array, ignoring other values\n sum = function (vals) {\n var total = 0, i;\n for (i = vals.length; i--;) {\n total += typeof vals[i] === 'number' ? vals[i] : 0;\n }\n return total;\n };\n\n ensureArray = function (val) {\n return $.isArray(val) ? val : [val];\n };\n\n // http://paulirish.com/2008/bookmarklet-inject-new-css-rules/\n addCSS = function(css) {\n var tag, iefail;\n if (document.createStyleSheet) {\n try {\n document.createStyleSheet().cssText = css;\n return;\n } catch (e) {\n // IE <= 9 maxes out at 31 stylesheets; inject into page instead.\n iefail = true;\n }\n }\n tag = document.createElement('style');\n tag.type = 'text/css';\n document.getElementsByTagName('head')[0].appendChild(tag);\n if (iefail) {\n document.styleSheets[document.styleSheets.length - 1].cssText = css;\n } else {\n tag[(typeof document.body.style.WebkitAppearance == 'string') /* webkit only */ ? 'innerText' : 'innerHTML'] = css;\n }\n };\n\n // Provide a cross-browser interface to a few simple drawing primitives\n $.fn.simpledraw = function (width, height, useExisting, interact) {\n var target, mhandler;\n if (useExisting && (target = this.data('_jqs_vcanvas'))) {\n return target;\n }\n\n if ($.fn.sparkline.canvas === false) {\n // We've already determined that neither Canvas nor VML are available\n return false;\n\n } else if ($.fn.sparkline.canvas === undefined) {\n // No function defined yet -- need to see if we support Canvas or VML\n var el = document.createElement('canvas');\n if (!!(el.getContext && el.getContext('2d'))) {\n // Canvas is available\n $.fn.sparkline.canvas = function(width, height, target, interact) {\n return new VCanvas_canvas(width, height, target, interact);\n };\n } else if (document.namespaces && !document.namespaces.v) {\n // VML is available\n document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML');\n $.fn.sparkline.canvas = function(width, height, target, interact) {\n return new VCanvas_vml(width, height, target);\n };\n } else {\n // Neither Canvas nor VML are available\n $.fn.sparkline.canvas = false;\n return false;\n }\n }\n\n if (width === undefined) {\n width = $(this).innerWidth();\n }\n if (height === undefined) {\n height = $(this).innerHeight();\n }\n\n target = $.fn.sparkline.canvas(width, height, this, interact);\n\n mhandler = $(this).data('_jqs_mhandler');\n if (mhandler) {\n mhandler.registerCanvas(target);\n }\n return target;\n };\n\n $.fn.cleardraw = function () {\n var target = this.data('_jqs_vcanvas');\n if (target) {\n target.reset();\n }\n };\n\n $.RangeMapClass = RangeMap = createClass({\n init: function (map) {\n var key, range, rangelist = [];\n for (key in map) {\n if (map.hasOwnProperty(key) && typeof key === 'string' && key.indexOf(':') > -1) {\n range = key.split(':');\n range[0] = range[0].length === 0 ? -Infinity : parseFloat(range[0]);\n range[1] = range[1].length === 0 ? Infinity : parseFloat(range[1]);\n range[2] = map[key];\n rangelist.push(range);\n }\n }\n this.map = map;\n this.rangelist = rangelist || false;\n },\n\n get: function (value) {\n var rangelist = this.rangelist,\n i, range, result;\n if ((result = this.map[value]) !== undefined) {\n return result;\n }\n if (rangelist) {\n for (i = rangelist.length; i--;) {\n range = rangelist[i];\n if (range[0] <= value && range[1] >= value) {\n return range[2];\n }\n }\n }\n return undefined;\n }\n });\n\n // Convenience function\n $.range_map = function(map) {\n return new RangeMap(map);\n };\n\n MouseHandler = createClass({\n init: function (el, options) {\n var $el = $(el);\n this.$el = $el;\n this.options = options;\n this.currentPageX = 0;\n this.currentPageY = 0;\n this.el = el;\n this.splist = [];\n this.tooltip = null;\n this.over = false;\n this.displayTooltips = !options.get('disableTooltips');\n this.highlightEnabled = !options.get('disableHighlight');\n },\n\n registerSparkline: function (sp) {\n this.splist.push(sp);\n if (this.over) {\n this.updateDisplay();\n }\n },\n\n registerCanvas: function (canvas) {\n var $canvas = $(canvas.canvas);\n this.canvas = canvas;\n this.$canvas = $canvas;\n $canvas.mouseenter($.proxy(this.mouseenter, this));\n $canvas.mouseleave($.proxy(this.mouseleave, this));\n $canvas.click($.proxy(this.mouseclick, this));\n },\n\n reset: function (removeTooltip) {\n this.splist = [];\n if (this.tooltip && removeTooltip) {\n this.tooltip.remove();\n this.tooltip = undefined;\n }\n },\n\n mouseclick: function (e) {\n var clickEvent = $.Event('sparklineClick');\n clickEvent.originalEvent = e;\n clickEvent.sparklines = this.splist;\n this.$el.trigger(clickEvent);\n },\n\n mouseenter: function (e) {\n $(document.body).unbind('mousemove.jqs');\n $(document.body).bind('mousemove.jqs', $.proxy(this.mousemove, this));\n this.over = true;\n this.currentPageX = e.pageX;\n this.currentPageY = e.pageY;\n this.currentEl = e.target;\n if (!this.tooltip && this.displayTooltips) {\n this.tooltip = new Tooltip(this.options);\n this.tooltip.updatePosition(e.pageX, e.pageY);\n }\n this.updateDisplay();\n },\n\n mouseleave: function () {\n $(document.body).unbind('mousemove.jqs');\n var splist = this.splist,\n spcount = splist.length,\n needsRefresh = false,\n sp, i;\n this.over = false;\n this.currentEl = null;\n\n if (this.tooltip) {\n this.tooltip.remove();\n this.tooltip = null;\n }\n\n for (i = 0; i < spcount; i++) {\n sp = splist[i];\n if (sp.clearRegionHighlight()) {\n needsRefresh = true;\n }\n }\n\n if (needsRefresh) {\n this.canvas.render();\n }\n },\n\n mousemove: function (e) {\n this.currentPageX = e.pageX;\n this.currentPageY = e.pageY;\n this.currentEl = e.target;\n if (this.tooltip) {\n this.tooltip.updatePosition(e.pageX, e.pageY);\n }\n this.updateDisplay();\n },\n\n updateDisplay: function () {\n var splist = this.splist,\n spcount = splist.length,\n needsRefresh = false,\n offset = this.$canvas.offset(),\n localX = this.currentPageX - offset.left,\n localY = this.currentPageY - offset.top,\n tooltiphtml, sp, i, result, changeEvent;\n if (!this.over) {\n return;\n }\n for (i = 0; i < spcount; i++) {\n sp = splist[i];\n result = sp.setRegionHighlight(this.currentEl, localX, localY);\n if (result) {\n needsRefresh = true;\n }\n }\n if (needsRefresh) {\n changeEvent = $.Event('sparklineRegionChange');\n changeEvent.sparklines = this.splist;\n this.$el.trigger(changeEvent);\n if (this.tooltip) {\n tooltiphtml = '';\n for (i = 0; i < spcount; i++) {\n sp = splist[i];\n tooltiphtml += sp.getCurrentRegionTooltip();\n }\n this.tooltip.setContent(tooltiphtml);\n }\n if (!this.disableHighlight) {\n this.canvas.render();\n }\n }\n if (result === null) {\n this.mouseleave();\n }\n }\n });\n\n\n Tooltip = createClass({\n sizeStyle: 'position: static !important;' +\n 'display: block !important;' +\n 'visibility: hidden !important;' +\n 'float: left !important;',\n\n init: function (options) {\n var tooltipClassname = options.get('tooltipClassname', 'jqstooltip'),\n sizetipStyle = this.sizeStyle,\n offset;\n this.container = options.get('tooltipContainer') || document.body;\n this.tooltipOffsetX = options.get('tooltipOffsetX', 10);\n this.tooltipOffsetY = options.get('tooltipOffsetY', 12);\n // remove any previous lingering tooltip\n $('#jqssizetip').remove();\n $('#jqstooltip').remove();\n this.sizetip = $('
', {\n id: 'jqssizetip',\n style: sizetipStyle,\n 'class': tooltipClassname\n });\n this.tooltip = $('
', {\n id: 'jqstooltip',\n 'class': tooltipClassname\n }).appendTo(this.container);\n // account for the container's location\n offset = this.tooltip.offset();\n this.offsetLeft = offset.left;\n this.offsetTop = offset.top;\n this.hidden = true;\n $(window).unbind('resize.jqs scroll.jqs');\n $(window).bind('resize.jqs scroll.jqs', $.proxy(this.updateWindowDims, this));\n this.updateWindowDims();\n },\n\n updateWindowDims: function () {\n this.scrollTop = $(window).scrollTop();\n this.scrollLeft = $(window).scrollLeft();\n this.scrollRight = this.scrollLeft + $(window).width();\n this.updatePosition();\n },\n\n getSize: function (content) {\n this.sizetip.html(content).appendTo(this.container);\n this.width = this.sizetip.width() + 1;\n this.height = this.sizetip.height();\n this.sizetip.remove();\n },\n\n setContent: function (content) {\n if (!content) {\n this.tooltip.css('visibility', 'hidden');\n this.hidden = true;\n return;\n }\n this.getSize(content);\n this.tooltip.html(content)\n .css({\n 'width': this.width,\n 'height': this.height,\n 'visibility': 'visible'\n });\n if (this.hidden) {\n this.hidden = false;\n this.updatePosition();\n }\n },\n\n updatePosition: function (x, y) {\n if (x === undefined) {\n if (this.mousex === undefined) {\n return;\n }\n x = this.mousex - this.offsetLeft;\n y = this.mousey - this.offsetTop;\n\n } else {\n this.mousex = x = x - this.offsetLeft;\n this.mousey = y = y - this.offsetTop;\n }\n if (!this.height || !this.width || this.hidden) {\n return;\n }\n\n y -= this.height + this.tooltipOffsetY;\n x += this.tooltipOffsetX;\n\n if (y < this.scrollTop) {\n y = this.scrollTop;\n }\n if (x < this.scrollLeft) {\n x = this.scrollLeft;\n } else if (x + this.width > this.scrollRight) {\n x = this.scrollRight - this.width;\n }\n\n this.tooltip.css({\n 'left': x,\n 'top': y\n });\n },\n\n remove: function () {\n this.tooltip.remove();\n this.sizetip.remove();\n this.sizetip = this.tooltip = undefined;\n $(window).unbind('resize.jqs scroll.jqs');\n }\n });\n\n initStyles = function() {\n addCSS(defaultStyles);\n };\n\n $(initStyles);\n\n pending = [];\n $.fn.sparkline = function (userValues, userOptions) {\n return this.each(function () {\n var options = new $.fn.sparkline.options(this, userOptions),\n $this = $(this),\n render, i;\n render = function () {\n var values, width, height, tmp, mhandler, sp, vals;\n if (userValues === 'html' || userValues === undefined) {\n vals = this.getAttribute(options.get('tagValuesAttribute'));\n if (vals === undefined || vals === null) {\n vals = $this.html();\n }\n values = vals.replace(/(^\\s*\\s*$)|\\s+/g, '').split(',');\n } else {\n values = userValues;\n }\n\n width = options.get('width') === 'auto' ? values.length * options.get('defaultPixelsPerValue') : options.get('width');\n if (options.get('height') === 'auto') {\n if (!options.get('composite') || !$.data(this, '_jqs_vcanvas')) {\n // must be a better way to get the line height\n tmp = document.createElement('span');\n tmp.innerHTML = 'a';\n $this.html(tmp);\n height = $(tmp).innerHeight() || $(tmp).height();\n $(tmp).remove();\n tmp = null;\n }\n } else {\n height = options.get('height');\n }\n\n if (!options.get('disableInteraction')) {\n mhandler = $.data(this, '_jqs_mhandler');\n if (!mhandler) {\n mhandler = new MouseHandler(this, options);\n $.data(this, '_jqs_mhandler', mhandler);\n } else if (!options.get('composite')) {\n mhandler.reset();\n }\n } else {\n mhandler = false;\n }\n\n if (options.get('composite') && !$.data(this, '_jqs_vcanvas')) {\n if (!$.data(this, '_jqs_errnotify')) {\n alert('Attempted to attach a composite sparkline to an element with no existing sparkline');\n $.data(this, '_jqs_errnotify', true);\n }\n return;\n }\n\n sp = new $.fn.sparkline[options.get('type')](this, values, options, width, height);\n\n sp.render();\n\n if (mhandler) {\n mhandler.registerSparkline(sp);\n }\n };\n if (($(this).html() && !options.get('disableHiddenCheck') && $(this).is(':hidden')) || !$(this).parents('body').length) {\n if (!options.get('composite') && $.data(this, '_jqs_pending')) {\n // remove any existing references to the element\n for (i = pending.length; i; i--) {\n if (pending[i - 1][0] == this) {\n pending.splice(i - 1, 1);\n }\n }\n }\n pending.push([this, render]);\n $.data(this, '_jqs_pending', true);\n } else {\n render.call(this);\n }\n });\n };\n\n $.fn.sparkline.defaults = getDefaults();\n\n\n $.sparkline_display_visible = function () {\n var el, i, pl;\n var done = [];\n for (i = 0, pl = pending.length; i < pl; i++) {\n el = pending[i][0];\n if ($(el).is(':visible') && !$(el).parents().is(':hidden')) {\n pending[i][1].call(el);\n $.data(pending[i][0], '_jqs_pending', false);\n done.push(i);\n } else if (!$(el).closest('html').length && !$.data(el, '_jqs_pending')) {\n // element has been inserted and removed from the DOM\n // If it was not yet inserted into the dom then the .data request\n // will return true.\n // removing from the dom causes the data to be removed.\n $.data(pending[i][0], '_jqs_pending', false);\n done.push(i);\n }\n }\n for (i = done.length; i; i--) {\n pending.splice(done[i - 1], 1);\n }\n };\n\n\n /**\n * User option handler\n */\n $.fn.sparkline.options = createClass({\n init: function (tag, userOptions) {\n var extendedOptions, defaults, base, tagOptionType;\n this.userOptions = userOptions = userOptions || {};\n this.tag = tag;\n this.tagValCache = {};\n defaults = $.fn.sparkline.defaults;\n base = defaults.common;\n this.tagOptionsPrefix = userOptions.enableTagOptions && (userOptions.tagOptionsPrefix || base.tagOptionsPrefix);\n\n tagOptionType = this.getTagSetting('type');\n if (tagOptionType === UNSET_OPTION) {\n extendedOptions = defaults[userOptions.type || base.type];\n } else {\n extendedOptions = defaults[tagOptionType];\n }\n this.mergedOptions = $.extend({}, base, extendedOptions, userOptions);\n },\n\n\n getTagSetting: function (key) {\n var prefix = this.tagOptionsPrefix,\n val, i, pairs, keyval;\n if (prefix === false || prefix === undefined) {\n return UNSET_OPTION;\n }\n if (this.tagValCache.hasOwnProperty(key)) {\n val = this.tagValCache.key;\n } else {\n val = this.tag.getAttribute(prefix + key);\n if (val === undefined || val === null) {\n val = UNSET_OPTION;\n } else if (val.substr(0, 1) === '[') {\n val = val.substr(1, val.length - 2).split(',');\n for (i = val.length; i--;) {\n val[i] = normalizeValue(val[i].replace(/(^\\s*)|(\\s*$)/g, ''));\n }\n } else if (val.substr(0, 1) === '{') {\n pairs = val.substr(1, val.length - 2).split(',');\n val = {};\n for (i = pairs.length; i--;) {\n keyval = pairs[i].split(':', 2);\n val[keyval[0].replace(/(^\\s*)|(\\s*$)/g, '')] = normalizeValue(keyval[1].replace(/(^\\s*)|(\\s*$)/g, ''));\n }\n } else {\n val = normalizeValue(val);\n }\n this.tagValCache.key = val;\n }\n return val;\n },\n\n get: function (key, defaultval) {\n var tagOption = this.getTagSetting(key),\n result;\n if (tagOption !== UNSET_OPTION) {\n return tagOption;\n }\n return (result = this.mergedOptions[key]) === undefined ? defaultval : result;\n }\n });\n\n\n $.fn.sparkline._base = createClass({\n disabled: false,\n\n init: function (el, values, options, width, height) {\n this.el = el;\n this.$el = $(el);\n this.values = values;\n this.options = options;\n this.width = width;\n this.height = height;\n this.currentRegion = undefined;\n },\n\n /**\n * Setup the canvas\n */\n initTarget: function () {\n var interactive = !this.options.get('disableInteraction');\n if (!(this.target = this.$el.simpledraw(this.width, this.height, this.options.get('composite'), interactive))) {\n this.disabled = true;\n } else {\n this.canvasWidth = this.target.pixelWidth;\n this.canvasHeight = this.target.pixelHeight;\n }\n },\n\n /**\n * Actually render the chart to the canvas\n */\n render: function () {\n if (this.disabled) {\n this.el.innerHTML = '';\n return false;\n }\n return true;\n },\n\n /**\n * Return a region id for a given x/y co-ordinate\n */\n getRegion: function (x, y) {\n },\n\n /**\n * Highlight an item based on the moused-over x,y co-ordinate\n */\n setRegionHighlight: function (el, x, y) {\n var currentRegion = this.currentRegion,\n highlightEnabled = !this.options.get('disableHighlight'),\n newRegion;\n if (x > this.canvasWidth || y > this.canvasHeight || x < 0 || y < 0) {\n return null;\n }\n newRegion = this.getRegion(el, x, y);\n if (currentRegion !== newRegion) {\n if (currentRegion !== undefined && highlightEnabled) {\n this.removeHighlight();\n }\n this.currentRegion = newRegion;\n if (newRegion !== undefined && highlightEnabled) {\n this.renderHighlight();\n }\n return true;\n }\n return false;\n },\n\n /**\n * Reset any currently highlighted item\n */\n clearRegionHighlight: function () {\n if (this.currentRegion !== undefined) {\n this.removeHighlight();\n this.currentRegion = undefined;\n return true;\n }\n return false;\n },\n\n renderHighlight: function () {\n this.changeHighlight(true);\n },\n\n removeHighlight: function () {\n this.changeHighlight(false);\n },\n\n changeHighlight: function (highlight) {},\n\n /**\n * Fetch the HTML to display as a tooltip\n */\n getCurrentRegionTooltip: function () {\n var options = this.options,\n header = '',\n entries = [],\n fields, formats, formatlen, fclass, text, i,\n showFields, showFieldsKey, newFields, fv,\n formatter, format, fieldlen, j;\n if (this.currentRegion === undefined) {\n return '';\n }\n fields = this.getCurrentRegionFields();\n formatter = options.get('tooltipFormatter');\n if (formatter) {\n return formatter(this, options, fields);\n }\n if (options.get('tooltipChartTitle')) {\n header += '
' + options.get('tooltipChartTitle') + '
\\n';\n }\n formats = this.options.get('tooltipFormat');\n if (!formats) {\n return '';\n }\n if (!$.isArray(formats)) {\n formats = [formats];\n }\n if (!$.isArray(fields)) {\n fields = [fields];\n }\n showFields = this.options.get('tooltipFormatFieldlist');\n showFieldsKey = this.options.get('tooltipFormatFieldlistKey');\n if (showFields && showFieldsKey) {\n // user-selected ordering of fields\n newFields = [];\n for (i = fields.length; i--;) {\n fv = fields[i][showFieldsKey];\n if ((j = $.inArray(fv, showFields)) != -1) {\n newFields[j] = fields[i];\n }\n }\n fields = newFields;\n }\n formatlen = formats.length;\n fieldlen = fields.length;\n for (i = 0; i < formatlen; i++) {\n format = formats[i];\n if (typeof format === 'string') {\n format = new SPFormat(format);\n }\n fclass = format.fclass || 'jqsfield';\n for (j = 0; j < fieldlen; j++) {\n if (!fields[j].isNull || !options.get('tooltipSkipNull')) {\n $.extend(fields[j], {\n prefix: options.get('tooltipPrefix'),\n suffix: options.get('tooltipSuffix')\n });\n text = format.render(fields[j], options.get('tooltipValueLookups'), options);\n entries.push('
' + text + '
');\n }\n }\n }\n if (entries.length) {\n return header + entries.join('\\n');\n }\n return '';\n },\n\n getCurrentRegionFields: function () {},\n\n calcHighlightColor: function (color, options) {\n var highlightColor = options.get('highlightColor'),\n lighten = options.get('highlightLighten'),\n parse, mult, rgbnew, i;\n if (highlightColor) {\n return highlightColor;\n }\n if (lighten) {\n // extract RGB values\n parse = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color) || /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);\n if (parse) {\n rgbnew = [];\n mult = color.length === 4 ? 16 : 1;\n for (i = 0; i < 3; i++) {\n rgbnew[i] = clipval(Math.round(parseInt(parse[i + 1], 16) * mult * lighten), 0, 255);\n }\n return 'rgb(' + rgbnew.join(',') + ')';\n }\n\n }\n return color;\n }\n\n });\n\n barHighlightMixin = {\n changeHighlight: function (highlight) {\n var currentRegion = this.currentRegion,\n target = this.target,\n shapeids = this.regionShapes[currentRegion],\n newShapes;\n // will be null if the region value was null\n if (shapeids) {\n newShapes = this.renderRegion(currentRegion, highlight);\n if ($.isArray(newShapes) || $.isArray(shapeids)) {\n target.replaceWithShapes(shapeids, newShapes);\n this.regionShapes[currentRegion] = $.map(newShapes, function (newShape) {\n return newShape.id;\n });\n } else {\n target.replaceWithShape(shapeids, newShapes);\n this.regionShapes[currentRegion] = newShapes.id;\n }\n }\n },\n\n render: function () {\n var values = this.values,\n target = this.target,\n regionShapes = this.regionShapes,\n shapes, ids, i, j;\n\n if (!this.cls._super.render.call(this)) {\n return;\n }\n for (i = values.length; i--;) {\n shapes = this.renderRegion(i);\n if (shapes) {\n if ($.isArray(shapes)) {\n ids = [];\n for (j = shapes.length; j--;) {\n shapes[j].append();\n ids.push(shapes[j].id);\n }\n regionShapes[i] = ids;\n } else {\n shapes.append();\n regionShapes[i] = shapes.id; // store just the shapeid\n }\n } else {\n // null value\n regionShapes[i] = null;\n }\n }\n target.render();\n }\n };\n\n /**\n * Line charts\n */\n $.fn.sparkline.line = line = createClass($.fn.sparkline._base, {\n type: 'line',\n\n init: function (el, values, options, width, height) {\n line._super.init.call(this, el, values, options, width, height);\n this.vertices = [];\n this.regionMap = [];\n this.xvalues = [];\n this.yvalues = [];\n this.yminmax = [];\n this.hightlightSpotId = null;\n this.lastShapeId = null;\n this.initTarget();\n },\n\n getRegion: function (el, x, y) {\n var i,\n regionMap = this.regionMap; // maps regions to value positions\n for (i = regionMap.length; i--;) {\n if (regionMap[i] !== null && x >= regionMap[i][0] && x <= regionMap[i][1]) {\n return regionMap[i][2];\n }\n }\n return undefined;\n },\n\n getCurrentRegionFields: function () {\n var currentRegion = this.currentRegion;\n return {\n isNull: this.yvalues[currentRegion] === null,\n x: this.xvalues[currentRegion],\n y: this.yvalues[currentRegion],\n color: this.options.get('lineColor'),\n fillColor: this.options.get('fillColor'),\n offset: currentRegion\n };\n },\n\n renderHighlight: function () {\n var currentRegion = this.currentRegion,\n target = this.target,\n vertex = this.vertices[currentRegion],\n options = this.options,\n spotRadius = options.get('spotRadius'),\n highlightSpotColor = options.get('highlightSpotColor'),\n highlightLineColor = options.get('highlightLineColor'),\n highlightSpot, highlightLine;\n\n if (!vertex) {\n return;\n }\n if (spotRadius && highlightSpotColor) {\n highlightSpot = target.drawCircle(vertex[0], vertex[1],\n spotRadius, undefined, highlightSpotColor);\n this.highlightSpotId = highlightSpot.id;\n target.insertAfterShape(this.lastShapeId, highlightSpot);\n }\n if (highlightLineColor) {\n highlightLine = target.drawLine(vertex[0], this.canvasTop, vertex[0],\n this.canvasTop + this.canvasHeight, highlightLineColor);\n this.highlightLineId = highlightLine.id;\n target.insertAfterShape(this.lastShapeId, highlightLine);\n }\n },\n\n removeHighlight: function () {\n var target = this.target;\n if (this.highlightSpotId) {\n target.removeShapeId(this.highlightSpotId);\n this.highlightSpotId = null;\n }\n if (this.highlightLineId) {\n target.removeShapeId(this.highlightLineId);\n this.highlightLineId = null;\n }\n },\n\n scanValues: function () {\n var values = this.values,\n valcount = values.length,\n xvalues = this.xvalues,\n yvalues = this.yvalues,\n yminmax = this.yminmax,\n i, val, isStr, isArray, sp;\n for (i = 0; i < valcount; i++) {\n val = values[i];\n isStr = typeof(values[i]) === 'string';\n isArray = typeof(values[i]) === 'object' && values[i] instanceof Array;\n sp = isStr && values[i].split(':');\n if (isStr && sp.length === 2) { // x:y\n xvalues.push(Number(sp[0]));\n yvalues.push(Number(sp[1]));\n yminmax.push(Number(sp[1]));\n } else if (isArray) {\n xvalues.push(val[0]);\n yvalues.push(val[1]);\n yminmax.push(val[1]);\n } else {\n xvalues.push(i);\n if (values[i] === null || values[i] === 'null') {\n yvalues.push(null);\n } else {\n yvalues.push(Number(val));\n yminmax.push(Number(val));\n }\n }\n }\n if (this.options.get('xvalues')) {\n xvalues = this.options.get('xvalues');\n }\n\n this.maxy = this.maxyorg = Math.max.apply(Math, yminmax);\n this.miny = this.minyorg = Math.min.apply(Math, yminmax);\n\n this.maxx = Math.max.apply(Math, xvalues);\n this.minx = Math.min.apply(Math, xvalues);\n\n this.xvalues = xvalues;\n this.yvalues = yvalues;\n this.yminmax = yminmax;\n\n },\n\n processRangeOptions: function () {\n var options = this.options,\n normalRangeMin = options.get('normalRangeMin'),\n normalRangeMax = options.get('normalRangeMax');\n\n if (normalRangeMin !== undefined) {\n if (normalRangeMin < this.miny) {\n this.miny = normalRangeMin;\n }\n if (normalRangeMax > this.maxy) {\n this.maxy = normalRangeMax;\n }\n }\n if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < this.miny)) {\n this.miny = options.get('chartRangeMin');\n }\n if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > this.maxy)) {\n this.maxy = options.get('chartRangeMax');\n }\n if (options.get('chartRangeMinX') !== undefined && (options.get('chartRangeClipX') || options.get('chartRangeMinX') < this.minx)) {\n this.minx = options.get('chartRangeMinX');\n }\n if (options.get('chartRangeMaxX') !== undefined && (options.get('chartRangeClipX') || options.get('chartRangeMaxX') > this.maxx)) {\n this.maxx = options.get('chartRangeMaxX');\n }\n\n },\n\n drawNormalRange: function (canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey) {\n var normalRangeMin = this.options.get('normalRangeMin'),\n normalRangeMax = this.options.get('normalRangeMax'),\n ytop = canvasTop + Math.round(canvasHeight - (canvasHeight * ((normalRangeMax - this.miny) / rangey))),\n height = Math.round((canvasHeight * (normalRangeMax - normalRangeMin)) / rangey);\n this.target.drawRect(canvasLeft, ytop, canvasWidth, height, undefined, this.options.get('normalRangeColor')).append();\n },\n\n render: function () {\n var options = this.options,\n target = this.target,\n canvasWidth = this.canvasWidth,\n canvasHeight = this.canvasHeight,\n vertices = this.vertices,\n spotRadius = options.get('spotRadius'),\n regionMap = this.regionMap,\n rangex, rangey, yvallast,\n canvasTop, canvasLeft,\n vertex, path, paths, x, y, xnext, xpos, xposnext,\n last, next, yvalcount, lineShapes, fillShapes, plen,\n valueSpots, hlSpotsEnabled, color, xvalues, yvalues, i;\n\n if (!line._super.render.call(this)) {\n return;\n }\n\n this.scanValues();\n this.processRangeOptions();\n\n xvalues = this.xvalues;\n yvalues = this.yvalues;\n\n if (!this.yminmax.length || this.yvalues.length < 2) {\n // empty or all null valuess\n return;\n }\n\n canvasTop = canvasLeft = 0;\n\n rangex = this.maxx - this.minx === 0 ? 1 : this.maxx - this.minx;\n rangey = this.maxy - this.miny === 0 ? 1 : this.maxy - this.miny;\n yvallast = this.yvalues.length - 1;\n\n if (spotRadius && (canvasWidth < (spotRadius * 4) || canvasHeight < (spotRadius * 4))) {\n spotRadius = 0;\n }\n if (spotRadius) {\n // adjust the canvas size as required so that spots will fit\n hlSpotsEnabled = options.get('highlightSpotColor') && !options.get('disableInteraction');\n if (hlSpotsEnabled || options.get('minSpotColor') || (options.get('spotColor') && yvalues[yvallast] === this.miny)) {\n canvasHeight -= Math.ceil(spotRadius);\n }\n if (hlSpotsEnabled || options.get('maxSpotColor') || (options.get('spotColor') && yvalues[yvallast] === this.maxy)) {\n canvasHeight -= Math.ceil(spotRadius);\n canvasTop += Math.ceil(spotRadius);\n }\n if (hlSpotsEnabled ||\n ((options.get('minSpotColor') || options.get('maxSpotColor')) && (yvalues[0] === this.miny || yvalues[0] === this.maxy))) {\n canvasLeft += Math.ceil(spotRadius);\n canvasWidth -= Math.ceil(spotRadius);\n }\n if (hlSpotsEnabled || options.get('spotColor') ||\n (options.get('minSpotColor') || options.get('maxSpotColor') &&\n (yvalues[yvallast] === this.miny || yvalues[yvallast] === this.maxy))) {\n canvasWidth -= Math.ceil(spotRadius);\n }\n }\n\n\n canvasHeight--;\n\n if (options.get('normalRangeMin') !== undefined && !options.get('drawNormalOnTop')) {\n this.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey);\n }\n\n path = [];\n paths = [path];\n last = next = null;\n yvalcount = yvalues.length;\n for (i = 0; i < yvalcount; i++) {\n x = xvalues[i];\n xnext = xvalues[i + 1];\n y = yvalues[i];\n xpos = canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex));\n xposnext = i < yvalcount - 1 ? canvasLeft + Math.round((xnext - this.minx) * (canvasWidth / rangex)) : canvasWidth;\n next = xpos + ((xposnext - xpos) / 2);\n regionMap[i] = [last || 0, next, i];\n last = next;\n if (y === null) {\n if (i) {\n if (yvalues[i - 1] !== null) {\n path = [];\n paths.push(path);\n }\n vertices.push(null);\n }\n } else {\n if (y < this.miny) {\n y = this.miny;\n }\n if (y > this.maxy) {\n y = this.maxy;\n }\n if (!path.length) {\n // previous value was null\n path.push([xpos, canvasTop + canvasHeight]);\n }\n vertex = [xpos, canvasTop + Math.round(canvasHeight - (canvasHeight * ((y - this.miny) / rangey)))];\n path.push(vertex);\n vertices.push(vertex);\n }\n }\n\n lineShapes = [];\n fillShapes = [];\n plen = paths.length;\n for (i = 0; i < plen; i++) {\n path = paths[i];\n if (path.length) {\n if (options.get('fillColor')) {\n path.push([path[path.length - 1][0], (canvasTop + canvasHeight)]);\n fillShapes.push(path.slice(0));\n path.pop();\n }\n // if there's only a single point in this path, then we want to display it\n // as a vertical line which means we keep path[0] as is\n if (path.length > 2) {\n // else we want the first value\n path[0] = [path[0][0], path[1][1]];\n }\n lineShapes.push(path);\n }\n }\n\n // draw the fill first, then optionally the normal range, then the line on top of that\n plen = fillShapes.length;\n for (i = 0; i < plen; i++) {\n target.drawShape(fillShapes[i],\n options.get('fillColor'), options.get('fillColor')).append();\n }\n\n if (options.get('normalRangeMin') !== undefined && options.get('drawNormalOnTop')) {\n this.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey);\n }\n\n plen = lineShapes.length;\n for (i = 0; i < plen; i++) {\n target.drawShape(lineShapes[i], options.get('lineColor'), undefined,\n options.get('lineWidth')).append();\n }\n\n if (spotRadius && options.get('valueSpots')) {\n valueSpots = options.get('valueSpots');\n if (valueSpots.get === undefined) {\n valueSpots = new RangeMap(valueSpots);\n }\n for (i = 0; i < yvalcount; i++) {\n color = valueSpots.get(yvalues[i]);\n if (color) {\n target.drawCircle(canvasLeft + Math.round((xvalues[i] - this.minx) * (canvasWidth / rangex)),\n canvasTop + Math.round(canvasHeight - (canvasHeight * ((yvalues[i] - this.miny) / rangey))),\n spotRadius, undefined,\n color).append();\n }\n }\n\n }\n if (spotRadius && options.get('spotColor') && yvalues[yvallast] !== null) {\n target.drawCircle(canvasLeft + Math.round((xvalues[xvalues.length - 1] - this.minx) * (canvasWidth / rangex)),\n canvasTop + Math.round(canvasHeight - (canvasHeight * ((yvalues[yvallast] - this.miny) / rangey))),\n spotRadius, undefined,\n options.get('spotColor')).append();\n }\n if (this.maxy !== this.minyorg) {\n if (spotRadius && options.get('minSpotColor')) {\n x = xvalues[$.inArray(this.minyorg, yvalues)];\n target.drawCircle(canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex)),\n canvasTop + Math.round(canvasHeight - (canvasHeight * ((this.minyorg - this.miny) / rangey))),\n spotRadius, undefined,\n options.get('minSpotColor')).append();\n }\n if (spotRadius && options.get('maxSpotColor')) {\n x = xvalues[$.inArray(this.maxyorg, yvalues)];\n target.drawCircle(canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex)),\n canvasTop + Math.round(canvasHeight - (canvasHeight * ((this.maxyorg - this.miny) / rangey))),\n spotRadius, undefined,\n options.get('maxSpotColor')).append();\n }\n }\n\n this.lastShapeId = target.getLastShapeId();\n this.canvasTop = canvasTop;\n target.render();\n }\n });\n\n /**\n * Bar charts\n */\n $.fn.sparkline.bar = bar = createClass($.fn.sparkline._base, barHighlightMixin, {\n type: 'bar',\n\n init: function (el, values, options, width, height) {\n var barWidth = parseInt(options.get('barWidth'), 10),\n barSpacing = parseInt(options.get('barSpacing'), 10),\n chartRangeMin = options.get('chartRangeMin'),\n chartRangeMax = options.get('chartRangeMax'),\n chartRangeClip = options.get('chartRangeClip'),\n stackMin = Infinity,\n stackMax = -Infinity,\n isStackString, groupMin, groupMax, stackRanges,\n numValues, i, vlen, range, zeroAxis, xaxisOffset, min, max, clipMin, clipMax,\n stacked, vlist, j, slen, svals, val, yoffset, yMaxCalc, canvasHeightEf;\n bar._super.init.call(this, el, values, options, width, height);\n\n // scan values to determine whether to stack bars\n for (i = 0, vlen = values.length; i < vlen; i++) {\n val = values[i];\n isStackString = typeof(val) === 'string' && val.indexOf(':') > -1;\n if (isStackString || $.isArray(val)) {\n stacked = true;\n if (isStackString) {\n val = values[i] = normalizeValues(val.split(':'));\n }\n val = remove(val, null); // min/max will treat null as zero\n groupMin = Math.min.apply(Math, val);\n groupMax = Math.max.apply(Math, val);\n if (groupMin < stackMin) {\n stackMin = groupMin;\n }\n if (groupMax > stackMax) {\n stackMax = groupMax;\n }\n }\n }\n\n this.stacked = stacked;\n this.regionShapes = {};\n this.barWidth = barWidth;\n this.barSpacing = barSpacing;\n this.totalBarWidth = barWidth + barSpacing;\n this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing);\n\n this.initTarget();\n\n if (chartRangeClip) {\n clipMin = chartRangeMin === undefined ? -Infinity : chartRangeMin;\n clipMax = chartRangeMax === undefined ? Infinity : chartRangeMax;\n }\n\n numValues = [];\n stackRanges = stacked ? [] : numValues;\n var stackTotals = [];\n var stackRangesNeg = [];\n for (i = 0, vlen = values.length; i < vlen; i++) {\n if (stacked) {\n vlist = values[i];\n values[i] = svals = [];\n stackTotals[i] = 0;\n stackRanges[i] = stackRangesNeg[i] = 0;\n for (j = 0, slen = vlist.length; j < slen; j++) {\n val = svals[j] = chartRangeClip ? clipval(vlist[j], clipMin, clipMax) : vlist[j];\n if (val !== null) {\n if (val > 0) {\n stackTotals[i] += val;\n }\n if (stackMin < 0 && stackMax > 0) {\n if (val < 0) {\n stackRangesNeg[i] += Math.abs(val);\n } else {\n stackRanges[i] += val;\n }\n } else {\n stackRanges[i] += Math.abs(val - (val < 0 ? stackMax : stackMin));\n }\n numValues.push(val);\n }\n }\n } else {\n val = chartRangeClip ? clipval(values[i], clipMin, clipMax) : values[i];\n val = values[i] = normalizeValue(val);\n if (val !== null) {\n numValues.push(val);\n }\n }\n }\n this.max = max = Math.max.apply(Math, numValues);\n this.min = min = Math.min.apply(Math, numValues);\n this.stackMax = stackMax = stacked ? Math.max.apply(Math, stackTotals) : max;\n this.stackMin = stackMin = stacked ? Math.min.apply(Math, numValues) : min;\n\n if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < min)) {\n min = options.get('chartRangeMin');\n }\n if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > max)) {\n max = options.get('chartRangeMax');\n }\n\n this.zeroAxis = zeroAxis = options.get('zeroAxis', true);\n if (min <= 0 && max >= 0 && zeroAxis) {\n xaxisOffset = 0;\n } else if (zeroAxis == false) {\n xaxisOffset = min;\n } else if (min > 0) {\n xaxisOffset = min;\n } else {\n xaxisOffset = max;\n }\n this.xaxisOffset = xaxisOffset;\n\n range = stacked ? (Math.max.apply(Math, stackRanges) + Math.max.apply(Math, stackRangesNeg)) : max - min;\n\n // as we plot zero/min values a single pixel line, we add a pixel to all other\n // values - Reduce the effective canvas size to suit\n this.canvasHeightEf = (zeroAxis && min < 0) ? this.canvasHeight - 2 : this.canvasHeight - 1;\n\n if (min < xaxisOffset) {\n yMaxCalc = (stacked && max >= 0) ? stackMax : max;\n yoffset = (yMaxCalc - xaxisOffset) / range * this.canvasHeight;\n if (yoffset !== Math.ceil(yoffset)) {\n this.canvasHeightEf -= 2;\n yoffset = Math.ceil(yoffset);\n }\n } else {\n yoffset = this.canvasHeight;\n }\n this.yoffset = yoffset;\n\n if ($.isArray(options.get('colorMap'))) {\n this.colorMapByIndex = options.get('colorMap');\n this.colorMapByValue = null;\n } else {\n this.colorMapByIndex = null;\n this.colorMapByValue = options.get('colorMap');\n if (this.colorMapByValue && this.colorMapByValue.get === undefined) {\n this.colorMapByValue = new RangeMap(this.colorMapByValue);\n }\n }\n\n this.range = range;\n },\n\n getRegion: function (el, x, y) {\n var result = Math.floor(x / this.totalBarWidth);\n return (result < 0 || result >= this.values.length) ? undefined : result;\n },\n\n getCurrentRegionFields: function () {\n var currentRegion = this.currentRegion,\n values = ensureArray(this.values[currentRegion]),\n result = [],\n value, i;\n for (i = values.length; i--;) {\n value = values[i];\n result.push({\n isNull: value === null,\n value: value,\n color: this.calcColor(i, value, currentRegion),\n offset: currentRegion\n });\n }\n return result;\n },\n\n calcColor: function (stacknum, value, valuenum) {\n var colorMapByIndex = this.colorMapByIndex,\n colorMapByValue = this.colorMapByValue,\n options = this.options,\n color, newColor;\n if (this.stacked) {\n color = options.get('stackedBarColor');\n } else {\n color = (value < 0) ? options.get('negBarColor') : options.get('barColor');\n }\n if (value === 0 && options.get('zeroColor') !== undefined) {\n color = options.get('zeroColor');\n }\n if (colorMapByValue && (newColor = colorMapByValue.get(value))) {\n color = newColor;\n } else if (colorMapByIndex && colorMapByIndex.length > valuenum) {\n color = colorMapByIndex[valuenum];\n }\n return $.isArray(color) ? color[stacknum % color.length] : color;\n },\n\n /**\n * Render bar(s) for a region\n */\n renderRegion: function (valuenum, highlight) {\n var vals = this.values[valuenum],\n options = this.options,\n xaxisOffset = this.xaxisOffset,\n result = [],\n range = this.range,\n stacked = this.stacked,\n target = this.target,\n x = valuenum * this.totalBarWidth,\n canvasHeightEf = this.canvasHeightEf,\n yoffset = this.yoffset,\n y, height, color, isNull, yoffsetNeg, i, valcount, val, minPlotted, allMin;\n\n vals = $.isArray(vals) ? vals : [vals];\n valcount = vals.length;\n val = vals[0];\n isNull = all(null, vals);\n allMin = all(xaxisOffset, vals, true);\n\n if (isNull) {\n if (options.get('nullColor')) {\n color = highlight ? options.get('nullColor') : this.calcHighlightColor(options.get('nullColor'), options);\n y = (yoffset > 0) ? yoffset - 1 : yoffset;\n return target.drawRect(x, y, this.barWidth - 1, 0, color, color);\n } else {\n return undefined;\n }\n }\n yoffsetNeg = yoffset;\n for (i = 0; i < valcount; i++) {\n val = vals[i];\n\n if (stacked && val === xaxisOffset) {\n if (!allMin || minPlotted) {\n continue;\n }\n minPlotted = true;\n }\n\n if (range > 0) {\n height = Math.floor(canvasHeightEf * ((Math.abs(val - xaxisOffset) / range))) + 1;\n } else {\n height = 1;\n }\n if (val < xaxisOffset || (val === xaxisOffset && yoffset === 0)) {\n y = yoffsetNeg;\n yoffsetNeg += height;\n } else {\n y = yoffset - height;\n yoffset -= height;\n }\n color = this.calcColor(i, val, valuenum);\n if (highlight) {\n color = this.calcHighlightColor(color, options);\n }\n result.push(target.drawRect(x, y, this.barWidth - 1, height - 1, color, color));\n }\n if (result.length === 1) {\n return result[0];\n }\n return result;\n }\n });\n\n /**\n * Tristate charts\n */\n $.fn.sparkline.tristate = tristate = createClass($.fn.sparkline._base, barHighlightMixin, {\n type: 'tristate',\n\n init: function (el, values, options, width, height) {\n var barWidth = parseInt(options.get('barWidth'), 10),\n barSpacing = parseInt(options.get('barSpacing'), 10);\n tristate._super.init.call(this, el, values, options, width, height);\n\n this.regionShapes = {};\n this.barWidth = barWidth;\n this.barSpacing = barSpacing;\n this.totalBarWidth = barWidth + barSpacing;\n this.values = $.map(values, Number);\n this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing);\n\n if ($.isArray(options.get('colorMap'))) {\n this.colorMapByIndex = options.get('colorMap');\n this.colorMapByValue = null;\n } else {\n this.colorMapByIndex = null;\n this.colorMapByValue = options.get('colorMap');\n if (this.colorMapByValue && this.colorMapByValue.get === undefined) {\n this.colorMapByValue = new RangeMap(this.colorMapByValue);\n }\n }\n this.initTarget();\n },\n\n getRegion: function (el, x, y) {\n return Math.floor(x / this.totalBarWidth);\n },\n\n getCurrentRegionFields: function () {\n var currentRegion = this.currentRegion;\n return {\n isNull: this.values[currentRegion] === undefined,\n value: this.values[currentRegion],\n color: this.calcColor(this.values[currentRegion], currentRegion),\n offset: currentRegion\n };\n },\n\n calcColor: function (value, valuenum) {\n var values = this.values,\n options = this.options,\n colorMapByIndex = this.colorMapByIndex,\n colorMapByValue = this.colorMapByValue,\n color, newColor;\n\n if (colorMapByValue && (newColor = colorMapByValue.get(value))) {\n color = newColor;\n } else if (colorMapByIndex && colorMapByIndex.length > valuenum) {\n color = colorMapByIndex[valuenum];\n } else if (values[valuenum] < 0) {\n color = options.get('negBarColor');\n } else if (values[valuenum] > 0) {\n color = options.get('posBarColor');\n } else {\n color = options.get('zeroBarColor');\n }\n return color;\n },\n\n renderRegion: function (valuenum, highlight) {\n var values = this.values,\n options = this.options,\n target = this.target,\n canvasHeight, height, halfHeight,\n x, y, color;\n\n canvasHeight = target.pixelHeight;\n halfHeight = Math.round(canvasHeight / 2);\n\n x = valuenum * this.totalBarWidth;\n if (values[valuenum] < 0) {\n y = halfHeight;\n height = halfHeight - 1;\n } else if (values[valuenum] > 0) {\n y = 0;\n height = halfHeight - 1;\n } else {\n y = halfHeight - 1;\n height = 2;\n }\n color = this.calcColor(values[valuenum], valuenum);\n if (color === null) {\n return;\n }\n if (highlight) {\n color = this.calcHighlightColor(color, options);\n }\n return target.drawRect(x, y, this.barWidth - 1, height - 1, color, color);\n }\n });\n\n /**\n * Discrete charts\n */\n $.fn.sparkline.discrete = discrete = createClass($.fn.sparkline._base, barHighlightMixin, {\n type: 'discrete',\n\n init: function (el, values, options, width, height) {\n discrete._super.init.call(this, el, values, options, width, height);\n\n this.regionShapes = {};\n this.values = values = $.map(values, Number);\n this.min = Math.min.apply(Math, values);\n this.max = Math.max.apply(Math, values);\n this.range = this.max - this.min;\n this.width = width = options.get('width') === 'auto' ? values.length * 2 : this.width;\n this.interval = Math.floor(width / values.length);\n this.itemWidth = width / values.length;\n if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < this.min)) {\n this.min = options.get('chartRangeMin');\n }\n if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > this.max)) {\n this.max = options.get('chartRangeMax');\n }\n this.initTarget();\n if (this.target) {\n this.lineHeight = options.get('lineHeight') === 'auto' ? Math.round(this.canvasHeight * 0.3) : options.get('lineHeight');\n }\n },\n\n getRegion: function (el, x, y) {\n return Math.floor(x / this.itemWidth);\n },\n\n getCurrentRegionFields: function () {\n var currentRegion = this.currentRegion;\n return {\n isNull: this.values[currentRegion] === undefined,\n value: this.values[currentRegion],\n offset: currentRegion\n };\n },\n\n renderRegion: function (valuenum, highlight) {\n var values = this.values,\n options = this.options,\n min = this.min,\n max = this.max,\n range = this.range,\n interval = this.interval,\n target = this.target,\n canvasHeight = this.canvasHeight,\n lineHeight = this.lineHeight,\n pheight = canvasHeight - lineHeight,\n ytop, val, color, x;\n\n val = clipval(values[valuenum], min, max);\n x = valuenum * interval;\n ytop = Math.round(pheight - pheight * ((val - min) / range));\n color = (options.get('thresholdColor') && val < options.get('thresholdValue')) ? options.get('thresholdColor') : options.get('lineColor');\n if (highlight) {\n color = this.calcHighlightColor(color, options);\n }\n return target.drawLine(x, ytop, x, ytop + lineHeight, color);\n }\n });\n\n /**\n * Bullet charts\n */\n $.fn.sparkline.bullet = bullet = createClass($.fn.sparkline._base, {\n type: 'bullet',\n\n init: function (el, values, options, width, height) {\n var min, max, vals;\n bullet._super.init.call(this, el, values, options, width, height);\n\n // values: target, performance, range1, range2, range3\n this.values = values = normalizeValues(values);\n // target or performance could be null\n vals = values.slice();\n vals[0] = vals[0] === null ? vals[2] : vals[0];\n vals[1] = values[1] === null ? vals[2] : vals[1];\n min = Math.min.apply(Math, values);\n max = Math.max.apply(Math, values);\n if (options.get('base') === undefined) {\n min = min < 0 ? min : 0;\n } else {\n min = options.get('base');\n }\n this.min = min;\n this.max = max;\n this.range = max - min;\n this.shapes = {};\n this.valueShapes = {};\n this.regiondata = {};\n this.width = width = options.get('width') === 'auto' ? '4.0em' : width;\n this.target = this.$el.simpledraw(width, height, options.get('composite'));\n if (!values.length) {\n this.disabled = true;\n }\n this.initTarget();\n },\n\n getRegion: function (el, x, y) {\n var shapeid = this.target.getShapeAt(el, x, y);\n return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined;\n },\n\n getCurrentRegionFields: function () {\n var currentRegion = this.currentRegion;\n return {\n fieldkey: currentRegion.substr(0, 1),\n value: this.values[currentRegion.substr(1)],\n region: currentRegion\n };\n },\n\n changeHighlight: function (highlight) {\n var currentRegion = this.currentRegion,\n shapeid = this.valueShapes[currentRegion],\n shape;\n delete this.shapes[shapeid];\n switch (currentRegion.substr(0, 1)) {\n case 'r':\n shape = this.renderRange(currentRegion.substr(1), highlight);\n break;\n case 'p':\n shape = this.renderPerformance(highlight);\n break;\n case 't':\n shape = this.renderTarget(highlight);\n break;\n }\n this.valueShapes[currentRegion] = shape.id;\n this.shapes[shape.id] = currentRegion;\n this.target.replaceWithShape(shapeid, shape);\n },\n\n renderRange: function (rn, highlight) {\n var rangeval = this.values[rn],\n rangewidth = Math.round(this.canvasWidth * ((rangeval - this.min) / this.range)),\n color = this.options.get('rangeColors')[rn - 2];\n if (highlight) {\n color = this.calcHighlightColor(color, this.options);\n }\n return this.target.drawRect(0, 0, rangewidth - 1, this.canvasHeight - 1, color, color);\n },\n\n renderPerformance: function (highlight) {\n var perfval = this.values[1],\n perfwidth = Math.round(this.canvasWidth * ((perfval - this.min) / this.range)),\n color = this.options.get('performanceColor');\n if (highlight) {\n color = this.calcHighlightColor(color, this.options);\n }\n return this.target.drawRect(0, Math.round(this.canvasHeight * 0.3), perfwidth - 1,\n Math.round(this.canvasHeight * 0.4) - 1, color, color);\n },\n\n renderTarget: function (highlight) {\n var targetval = this.values[0],\n x = Math.round(this.canvasWidth * ((targetval - this.min) / this.range) - (this.options.get('targetWidth') / 2)),\n targettop = Math.round(this.canvasHeight * 0.10),\n targetheight = this.canvasHeight - (targettop * 2),\n color = this.options.get('targetColor');\n if (highlight) {\n color = this.calcHighlightColor(color, this.options);\n }\n return this.target.drawRect(x, targettop, this.options.get('targetWidth') - 1, targetheight - 1, color, color);\n },\n\n render: function () {\n var vlen = this.values.length,\n target = this.target,\n i, shape;\n if (!bullet._super.render.call(this)) {\n return;\n }\n for (i = 2; i < vlen; i++) {\n shape = this.renderRange(i).append();\n this.shapes[shape.id] = 'r' + i;\n this.valueShapes['r' + i] = shape.id;\n }\n if (this.values[1] !== null) {\n shape = this.renderPerformance().append();\n this.shapes[shape.id] = 'p1';\n this.valueShapes.p1 = shape.id;\n }\n if (this.values[0] !== null) {\n shape = this.renderTarget().append();\n this.shapes[shape.id] = 't0';\n this.valueShapes.t0 = shape.id;\n }\n target.render();\n }\n });\n\n /**\n * Pie charts\n */\n $.fn.sparkline.pie = pie = createClass($.fn.sparkline._base, {\n type: 'pie',\n\n init: function (el, values, options, width, height) {\n var total = 0, i;\n\n pie._super.init.call(this, el, values, options, width, height);\n\n this.shapes = {}; // map shape ids to value offsets\n this.valueShapes = {}; // maps value offsets to shape ids\n this.values = values = $.map(values, Number);\n\n if (options.get('width') === 'auto') {\n this.width = this.height;\n }\n\n if (values.length > 0) {\n for (i = values.length; i--;) {\n total += values[i];\n }\n }\n this.total = total;\n this.initTarget();\n this.radius = Math.floor(Math.min(this.canvasWidth, this.canvasHeight) / 2);\n },\n\n getRegion: function (el, x, y) {\n var shapeid = this.target.getShapeAt(el, x, y);\n return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined;\n },\n\n getCurrentRegionFields: function () {\n var currentRegion = this.currentRegion;\n return {\n isNull: this.values[currentRegion] === undefined,\n value: this.values[currentRegion],\n percent: this.values[currentRegion] / this.total * 100,\n color: this.options.get('sliceColors')[currentRegion % this.options.get('sliceColors').length],\n offset: currentRegion\n };\n },\n\n changeHighlight: function (highlight) {\n var currentRegion = this.currentRegion,\n newslice = this.renderSlice(currentRegion, highlight),\n shapeid = this.valueShapes[currentRegion];\n delete this.shapes[shapeid];\n this.target.replaceWithShape(shapeid, newslice);\n this.valueShapes[currentRegion] = newslice.id;\n this.shapes[newslice.id] = currentRegion;\n },\n\n renderSlice: function (valuenum, highlight) {\n var target = this.target,\n options = this.options,\n radius = this.radius,\n borderWidth = options.get('borderWidth'),\n offset = options.get('offset'),\n circle = 2 * Math.PI,\n values = this.values,\n total = this.total,\n next = offset ? (2*Math.PI)*(offset/360) : 0,\n start, end, i, vlen, color;\n\n vlen = values.length;\n for (i = 0; i < vlen; i++) {\n start = next;\n end = next;\n if (total > 0) { // avoid divide by zero\n end = next + (circle * (values[i] / total));\n }\n if (valuenum === i) {\n color = options.get('sliceColors')[i % options.get('sliceColors').length];\n if (highlight) {\n color = this.calcHighlightColor(color, options);\n }\n\n return target.drawPieSlice(radius, radius, radius - borderWidth, start, end, undefined, color);\n }\n next = end;\n }\n },\n\n render: function () {\n var target = this.target,\n values = this.values,\n options = this.options,\n radius = this.radius,\n borderWidth = options.get('borderWidth'),\n donutWidth = options.get('donutWidth'),\n shape, i;\n\n if (!pie._super.render.call(this)) {\n return;\n }\n if (borderWidth) {\n target.drawCircle(radius, radius, Math.floor(radius - (borderWidth / 2)),\n options.get('borderColor'), undefined, borderWidth).append();\n }\n for (i = values.length; i--;) {\n if (values[i]) { // don't render zero values\n shape = this.renderSlice(i).append();\n this.valueShapes[i] = shape.id; // store just the shapeid\n this.shapes[shape.id] = i;\n }\n }\n if (donutWidth) {\n target.drawCircle(radius, radius, radius - donutWidth, options.get('donutColor'), \n options.get('donutColor'), 0).append();\n }\n target.render();\n }\n });\n\n /**\n * Box plots\n */\n $.fn.sparkline.box = box = createClass($.fn.sparkline._base, {\n type: 'box',\n\n init: function (el, values, options, width, height) {\n box._super.init.call(this, el, values, options, width, height);\n this.values = $.map(values, Number);\n this.width = options.get('width') === 'auto' ? '4.0em' : width;\n this.initTarget();\n if (!this.values.length) {\n this.disabled = 1;\n }\n },\n\n /**\n * Simulate a single region\n */\n getRegion: function () {\n return 1;\n },\n\n getCurrentRegionFields: function () {\n var result = [\n { field: 'lq', value: this.quartiles[0] },\n { field: 'med', value: this.quartiles[1] },\n { field: 'uq', value: this.quartiles[2] }\n ];\n if (this.loutlier !== undefined) {\n result.push({ field: 'lo', value: this.loutlier});\n }\n if (this.routlier !== undefined) {\n result.push({ field: 'ro', value: this.routlier});\n }\n if (this.lwhisker !== undefined) {\n result.push({ field: 'lw', value: this.lwhisker});\n }\n if (this.rwhisker !== undefined) {\n result.push({ field: 'rw', value: this.rwhisker});\n }\n return result;\n },\n\n render: function () {\n var target = this.target,\n values = this.values,\n vlen = values.length,\n options = this.options,\n canvasWidth = this.canvasWidth,\n canvasHeight = this.canvasHeight,\n minValue = options.get('chartRangeMin') === undefined ? Math.min.apply(Math, values) : options.get('chartRangeMin'),\n maxValue = options.get('chartRangeMax') === undefined ? Math.max.apply(Math, values) : options.get('chartRangeMax'),\n canvasLeft = 0,\n lwhisker, loutlier, iqr, q1, q2, q3, rwhisker, routlier, i,\n size, unitSize;\n\n if (!box._super.render.call(this)) {\n return;\n }\n\n if (options.get('raw')) {\n if (options.get('showOutliers') && values.length > 5) {\n loutlier = values[0];\n lwhisker = values[1];\n q1 = values[2];\n q2 = values[3];\n q3 = values[4];\n rwhisker = values[5];\n routlier = values[6];\n } else {\n lwhisker = values[0];\n q1 = values[1];\n q2 = values[2];\n q3 = values[3];\n rwhisker = values[4];\n }\n } else {\n values.sort(function (a, b) { return a - b; });\n q1 = quartile(values, 1);\n q2 = quartile(values, 2);\n q3 = quartile(values, 3);\n iqr = q3 - q1;\n if (options.get('showOutliers')) {\n lwhisker = rwhisker = undefined;\n for (i = 0; i < vlen; i++) {\n if (lwhisker === undefined && values[i] > q1 - (iqr * options.get('outlierIQR'))) {\n lwhisker = values[i];\n }\n if (values[i] < q3 + (iqr * options.get('outlierIQR'))) {\n rwhisker = values[i];\n }\n }\n loutlier = values[0];\n routlier = values[vlen - 1];\n } else {\n lwhisker = values[0];\n rwhisker = values[vlen - 1];\n }\n }\n this.quartiles = [q1, q2, q3];\n this.lwhisker = lwhisker;\n this.rwhisker = rwhisker;\n this.loutlier = loutlier;\n this.routlier = routlier;\n\n unitSize = canvasWidth / (maxValue - minValue + 1);\n if (options.get('showOutliers')) {\n canvasLeft = Math.ceil(options.get('spotRadius'));\n canvasWidth -= 2 * Math.ceil(options.get('spotRadius'));\n unitSize = canvasWidth / (maxValue - minValue + 1);\n if (loutlier < lwhisker) {\n target.drawCircle((loutlier - minValue) * unitSize + canvasLeft,\n canvasHeight / 2,\n options.get('spotRadius'),\n options.get('outlierLineColor'),\n options.get('outlierFillColor')).append();\n }\n if (routlier > rwhisker) {\n target.drawCircle((routlier - minValue) * unitSize + canvasLeft,\n canvasHeight / 2,\n options.get('spotRadius'),\n options.get('outlierLineColor'),\n options.get('outlierFillColor')).append();\n }\n }\n\n // box\n target.drawRect(\n Math.round((q1 - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight * 0.1),\n Math.round((q3 - q1) * unitSize),\n Math.round(canvasHeight * 0.8),\n options.get('boxLineColor'),\n options.get('boxFillColor')).append();\n // left whisker\n target.drawLine(\n Math.round((lwhisker - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight / 2),\n Math.round((q1 - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight / 2),\n options.get('lineColor')).append();\n target.drawLine(\n Math.round((lwhisker - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight / 4),\n Math.round((lwhisker - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight - canvasHeight / 4),\n options.get('whiskerColor')).append();\n // right whisker\n target.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight / 2),\n Math.round((q3 - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight / 2),\n options.get('lineColor')).append();\n target.drawLine(\n Math.round((rwhisker - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight / 4),\n Math.round((rwhisker - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight - canvasHeight / 4),\n options.get('whiskerColor')).append();\n // median line\n target.drawLine(\n Math.round((q2 - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight * 0.1),\n Math.round((q2 - minValue) * unitSize + canvasLeft),\n Math.round(canvasHeight * 0.9),\n options.get('medianColor')).append();\n if (options.get('target')) {\n size = Math.ceil(options.get('spotRadius'));\n target.drawLine(\n Math.round((options.get('target') - minValue) * unitSize + canvasLeft),\n Math.round((canvasHeight / 2) - size),\n Math.round((options.get('target') - minValue) * unitSize + canvasLeft),\n Math.round((canvasHeight / 2) + size),\n options.get('targetColor')).append();\n target.drawLine(\n Math.round((options.get('target') - minValue) * unitSize + canvasLeft - size),\n Math.round(canvasHeight / 2),\n Math.round((options.get('target') - minValue) * unitSize + canvasLeft + size),\n Math.round(canvasHeight / 2),\n options.get('targetColor')).append();\n }\n target.render();\n }\n });\n\n // Setup a very simple \"virtual canvas\" to make drawing the few shapes we need easier\n // This is accessible as $(foo).simpledraw()\n\n VShape = createClass({\n init: function (target, id, type, args) {\n this.target = target;\n this.id = id;\n this.type = type;\n this.args = args;\n },\n append: function () {\n this.target.appendShape(this);\n return this;\n }\n });\n\n VCanvas_base = createClass({\n _pxregex: /(\\d+)(px)?\\s*$/i,\n\n init: function (width, height, target) {\n if (!width) {\n return;\n }\n this.width = width;\n this.height = height;\n this.target = target;\n this.lastShapeId = null;\n if (target[0]) {\n target = target[0];\n }\n $.data(target, '_jqs_vcanvas', this);\n },\n\n drawLine: function (x1, y1, x2, y2, lineColor, lineWidth) {\n return this.drawShape([[x1, y1], [x2, y2]], lineColor, lineWidth);\n },\n\n drawShape: function (path, lineColor, fillColor, lineWidth) {\n return this._genShape('Shape', [path, lineColor, fillColor, lineWidth]);\n },\n\n drawCircle: function (x, y, radius, lineColor, fillColor, lineWidth) {\n return this._genShape('Circle', [x, y, radius, lineColor, fillColor, lineWidth]);\n },\n\n drawPieSlice: function (x, y, radius, startAngle, endAngle, lineColor, fillColor) {\n return this._genShape('PieSlice', [x, y, radius, startAngle, endAngle, lineColor, fillColor]);\n },\n\n drawRect: function (x, y, width, height, lineColor, fillColor) {\n return this._genShape('Rect', [x, y, width, height, lineColor, fillColor]);\n },\n\n getElement: function () {\n return this.canvas;\n },\n\n /**\n * Return the most recently inserted shape id\n */\n getLastShapeId: function () {\n return this.lastShapeId;\n },\n\n /**\n * Clear and reset the canvas\n */\n reset: function () {\n alert('reset not implemented');\n },\n\n _insert: function (el, target) {\n $(target).html(el);\n },\n\n /**\n * Calculate the pixel dimensions of the canvas\n */\n _calculatePixelDims: function (width, height, canvas) {\n // XXX This should probably be a configurable option\n var match;\n match = this._pxregex.exec(height);\n if (match) {\n this.pixelHeight = match[1];\n } else {\n this.pixelHeight = $(canvas).height();\n }\n match = this._pxregex.exec(width);\n if (match) {\n this.pixelWidth = match[1];\n } else {\n this.pixelWidth = $(canvas).width();\n }\n },\n\n /**\n * Generate a shape object and id for later rendering\n */\n _genShape: function (shapetype, shapeargs) {\n var id = shapeCount++;\n shapeargs.unshift(id);\n return new VShape(this, id, shapetype, shapeargs);\n },\n\n /**\n * Add a shape to the end of the render queue\n */\n appendShape: function (shape) {\n alert('appendShape not implemented');\n },\n\n /**\n * Replace one shape with another\n */\n replaceWithShape: function (shapeid, shape) {\n alert('replaceWithShape not implemented');\n },\n\n /**\n * Insert one shape after another in the render queue\n */\n insertAfterShape: function (shapeid, shape) {\n alert('insertAfterShape not implemented');\n },\n\n /**\n * Remove a shape from the queue\n */\n removeShapeId: function (shapeid) {\n alert('removeShapeId not implemented');\n },\n\n /**\n * Find a shape at the specified x/y co-ordinates\n */\n getShapeAt: function (el, x, y) {\n alert('getShapeAt not implemented');\n },\n\n /**\n * Render all queued shapes onto the canvas\n */\n render: function () {\n alert('render not implemented');\n }\n });\n\n VCanvas_canvas = createClass(VCanvas_base, {\n init: function (width, height, target, interact) {\n VCanvas_canvas._super.init.call(this, width, height, target);\n this.canvas = document.createElement('canvas');\n if (target[0]) {\n target = target[0];\n }\n $.data(target, '_jqs_vcanvas', this);\n $(this.canvas).css({ display: 'inline-block', width: width, height: height, verticalAlign: 'top' });\n this._insert(this.canvas, target);\n this._calculatePixelDims(width, height, this.canvas);\n this.canvas.width = this.pixelWidth;\n this.canvas.height = this.pixelHeight;\n this.interact = interact;\n this.shapes = {};\n this.shapeseq = [];\n this.currentTargetShapeId = undefined;\n $(this.canvas).css({width: this.pixelWidth, height: this.pixelHeight});\n },\n\n _getContext: function (lineColor, fillColor, lineWidth) {\n var context = this.canvas.getContext('2d');\n if (lineColor !== undefined) {\n context.strokeStyle = lineColor;\n }\n context.lineWidth = lineWidth === undefined ? 1 : lineWidth;\n if (fillColor !== undefined) {\n context.fillStyle = fillColor;\n }\n return context;\n },\n\n reset: function () {\n var context = this._getContext();\n context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);\n this.shapes = {};\n this.shapeseq = [];\n this.currentTargetShapeId = undefined;\n },\n\n _drawShape: function (shapeid, path, lineColor, fillColor, lineWidth) {\n var context = this._getContext(lineColor, fillColor, lineWidth),\n i, plen;\n context.beginPath();\n context.moveTo(path[0][0] + 0.5, path[0][1] + 0.5);\n for (i = 1, plen = path.length; i < plen; i++) {\n context.lineTo(path[i][0] + 0.5, path[i][1] + 0.5); // the 0.5 offset gives us crisp pixel-width lines\n }\n if (lineColor !== undefined) {\n context.stroke();\n }\n if (fillColor !== undefined) {\n context.fill();\n }\n if (this.targetX !== undefined && this.targetY !== undefined &&\n context.isPointInPath(this.targetX, this.targetY)) {\n this.currentTargetShapeId = shapeid;\n }\n },\n\n _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) {\n var context = this._getContext(lineColor, fillColor, lineWidth);\n context.beginPath();\n context.arc(x, y, radius, 0, 2 * Math.PI, false);\n if (this.targetX !== undefined && this.targetY !== undefined &&\n context.isPointInPath(this.targetX, this.targetY)) {\n this.currentTargetShapeId = shapeid;\n }\n if (lineColor !== undefined) {\n context.stroke();\n }\n if (fillColor !== undefined) {\n context.fill();\n }\n },\n\n _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) {\n var context = this._getContext(lineColor, fillColor);\n context.beginPath();\n context.moveTo(x, y);\n context.arc(x, y, radius, startAngle, endAngle, false);\n context.lineTo(x, y);\n context.closePath();\n if (lineColor !== undefined) {\n context.stroke();\n }\n if (fillColor) {\n context.fill();\n }\n if (this.targetX !== undefined && this.targetY !== undefined &&\n context.isPointInPath(this.targetX, this.targetY)) {\n this.currentTargetShapeId = shapeid;\n }\n },\n\n _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) {\n return this._drawShape(shapeid, [[x, y], [x + width, y], [x + width, y + height], [x, y + height], [x, y]], lineColor, fillColor);\n },\n\n appendShape: function (shape) {\n this.shapes[shape.id] = shape;\n this.shapeseq.push(shape.id);\n this.lastShapeId = shape.id;\n return shape.id;\n },\n\n replaceWithShape: function (shapeid, shape) {\n var shapeseq = this.shapeseq,\n i;\n this.shapes[shape.id] = shape;\n for (i = shapeseq.length; i--;) {\n if (shapeseq[i] == shapeid) {\n shapeseq[i] = shape.id;\n }\n }\n delete this.shapes[shapeid];\n },\n\n replaceWithShapes: function (shapeids, shapes) {\n var shapeseq = this.shapeseq,\n shapemap = {},\n sid, i, first;\n\n for (i = shapeids.length; i--;) {\n shapemap[shapeids[i]] = true;\n }\n for (i = shapeseq.length; i--;) {\n sid = shapeseq[i];\n if (shapemap[sid]) {\n shapeseq.splice(i, 1);\n delete this.shapes[sid];\n first = i;\n }\n }\n for (i = shapes.length; i--;) {\n shapeseq.splice(first, 0, shapes[i].id);\n this.shapes[shapes[i].id] = shapes[i];\n }\n\n },\n\n insertAfterShape: function (shapeid, shape) {\n var shapeseq = this.shapeseq,\n i;\n for (i = shapeseq.length; i--;) {\n if (shapeseq[i] === shapeid) {\n shapeseq.splice(i + 1, 0, shape.id);\n this.shapes[shape.id] = shape;\n return;\n }\n }\n },\n\n removeShapeId: function (shapeid) {\n var shapeseq = this.shapeseq,\n i;\n for (i = shapeseq.length; i--;) {\n if (shapeseq[i] === shapeid) {\n shapeseq.splice(i, 1);\n break;\n }\n }\n delete this.shapes[shapeid];\n },\n\n getShapeAt: function (el, x, y) {\n this.targetX = x;\n this.targetY = y;\n this.render();\n return this.currentTargetShapeId;\n },\n\n render: function () {\n var shapeseq = this.shapeseq,\n shapes = this.shapes,\n shapeCount = shapeseq.length,\n context = this._getContext(),\n shapeid, shape, i;\n context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);\n for (i = 0; i < shapeCount; i++) {\n shapeid = shapeseq[i];\n shape = shapes[shapeid];\n this['_draw' + shape.type].apply(this, shape.args);\n }\n if (!this.interact) {\n // not interactive so no need to keep the shapes array\n this.shapes = {};\n this.shapeseq = [];\n }\n }\n\n });\n\n VCanvas_vml = createClass(VCanvas_base, {\n init: function (width, height, target) {\n var groupel;\n VCanvas_vml._super.init.call(this, width, height, target);\n if (target[0]) {\n target = target[0];\n }\n $.data(target, '_jqs_vcanvas', this);\n this.canvas = document.createElement('span');\n $(this.canvas).css({ display: 'inline-block', position: 'relative', overflow: 'hidden', width: width, height: height, margin: '0px', padding: '0px', verticalAlign: 'top'});\n this._insert(this.canvas, target);\n this._calculatePixelDims(width, height, this.canvas);\n this.canvas.width = this.pixelWidth;\n this.canvas.height = this.pixelHeight;\n groupel = '';\n this.canvas.insertAdjacentHTML('beforeEnd', groupel);\n this.group = $(this.canvas).children()[0];\n this.rendered = false;\n this.prerender = '';\n },\n\n _drawShape: function (shapeid, path, lineColor, fillColor, lineWidth) {\n var vpath = [],\n initial, stroke, fill, closed, vel, plen, i;\n for (i = 0, plen = path.length; i < plen; i++) {\n vpath[i] = '' + (path[i][0]) + ',' + (path[i][1]);\n }\n initial = vpath.splice(0, 1);\n lineWidth = lineWidth === undefined ? 1 : lineWidth;\n stroke = lineColor === undefined ? ' stroked=\"false\" ' : ' strokeWeight=\"' + lineWidth + 'px\" strokeColor=\"' + lineColor + '\" ';\n fill = fillColor === undefined ? ' filled=\"false\"' : ' fillColor=\"' + fillColor + '\" filled=\"true\" ';\n closed = vpath[0] === vpath[vpath.length - 1] ? 'x ' : '';\n vel = '' +\n ' ';\n return vel;\n },\n\n _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) {\n var stroke, fill, vel;\n x -= radius;\n y -= radius;\n stroke = lineColor === undefined ? ' stroked=\"false\" ' : ' strokeWeight=\"' + lineWidth + 'px\" strokeColor=\"' + lineColor + '\" ';\n fill = fillColor === undefined ? ' filled=\"false\"' : ' fillColor=\"' + fillColor + '\" filled=\"true\" ';\n vel = '';\n return vel;\n\n },\n\n _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) {\n var vpath, startx, starty, endx, endy, stroke, fill, vel;\n if (startAngle === endAngle) {\n return ''; // VML seems to have problem when start angle equals end angle.\n }\n if ((endAngle - startAngle) === (2 * Math.PI)) {\n startAngle = 0.0; // VML seems to have a problem when drawing a full circle that doesn't start 0\n endAngle = (2 * Math.PI);\n }\n\n startx = x + Math.round(Math.cos(startAngle) * radius);\n starty = y + Math.round(Math.sin(startAngle) * radius);\n endx = x + Math.round(Math.cos(endAngle) * radius);\n endy = y + Math.round(Math.sin(endAngle) * radius);\n\n if (startx === endx && starty === endy) {\n if ((endAngle - startAngle) < Math.PI) {\n // Prevent very small slices from being mistaken as a whole pie\n return '';\n }\n // essentially going to be the entire circle, so ignore startAngle\n startx = endx = x + radius;\n starty = endy = y;\n }\n\n if (startx === endx && starty === endy && (endAngle - startAngle) < Math.PI) {\n return '';\n }\n\n vpath = [x - radius, y - radius, x + radius, y + radius, startx, starty, endx, endy];\n stroke = lineColor === undefined ? ' stroked=\"false\" ' : ' strokeWeight=\"1px\" strokeColor=\"' + lineColor + '\" ';\n fill = fillColor === undefined ? ' filled=\"false\"' : ' fillColor=\"' + fillColor + '\" filled=\"true\" ';\n vel = '' +\n ' ';\n return vel;\n },\n\n _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) {\n return this._drawShape(shapeid, [[x, y], [x, y + height], [x + width, y + height], [x + width, y], [x, y]], lineColor, fillColor);\n },\n\n reset: function () {\n this.group.innerHTML = '';\n },\n\n appendShape: function (shape) {\n var vel = this['_draw' + shape.type].apply(this, shape.args);\n if (this.rendered) {\n this.group.insertAdjacentHTML('beforeEnd', vel);\n } else {\n this.prerender += vel;\n }\n this.lastShapeId = shape.id;\n return shape.id;\n },\n\n replaceWithShape: function (shapeid, shape) {\n var existing = $('#jqsshape' + shapeid),\n vel = this['_draw' + shape.type].apply(this, shape.args);\n existing[0].outerHTML = vel;\n },\n\n replaceWithShapes: function (shapeids, shapes) {\n // replace the first shapeid with all the new shapes then toast the remaining old shapes\n var existing = $('#jqsshape' + shapeids[0]),\n replace = '',\n slen = shapes.length,\n i;\n for (i = 0; i < slen; i++) {\n replace += this['_draw' + shapes[i].type].apply(this, shapes[i].args);\n }\n existing[0].outerHTML = replace;\n for (i = 1; i < shapeids.length; i++) {\n $('#jqsshape' + shapeids[i]).remove();\n }\n },\n\n insertAfterShape: function (shapeid, shape) {\n var existing = $('#jqsshape' + shapeid),\n vel = this['_draw' + shape.type].apply(this, shape.args);\n existing[0].insertAdjacentHTML('afterEnd', vel);\n },\n\n removeShapeId: function (shapeid) {\n var existing = $('#jqsshape' + shapeid);\n this.group.removeChild(existing[0]);\n },\n\n getShapeAt: function (el, x, y) {\n var shapeid = el.id.substr(8);\n return shapeid;\n },\n\n render: function () {\n if (!this.rendered) {\n // batch the intial render into a single repaint\n this.group.innerHTML = this.prerender;\n this.rendered = true;\n }\n }\n });\n\n}))}(document, Math));\n","var __assign = (this && this.__assign) || function () {\n __assign = Object.assign || function(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\n t[p] = s[p];\n }\n return t;\n };\n return __assign.apply(this, arguments);\n};\nvar __rest = (this && this.__rest) || function (s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n};\nvar __read = (this && this.__read) || function (o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n};\nvar __spread = (this && this.__spread) || function () {\n for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));\n return ar;\n};\nimport * as React from 'react';\nimport classNamesFunc from 'classnames';\nimport { parseThemeOptions } from './with-theme';\nimport { handleDeprecations } from './utils/deprecation';\n// ALL OF THESE FUNCTIONS MUTATE THE COPY OF PROPS\n// this is intentional and done for speed and memory\nvar handleClassNames = function (props, classNames, className, theme) {\n var finalClassNames = classNamesFunc.apply(void 0, __spread([className], (!!theme ? parseThemeOptions(theme) : []), (typeof classNames === 'function' ? classNames(props) : classNames)));\n props.className = finalClassNames;\n};\nvar handleTag = function (props, defaultTag, tag) {\n // Handle the case where we are extending a component but passing\n // a string as a tag. For instance, extending an Icon but rendering a span\n if (typeof defaultTag !== 'string') {\n props.tag = tag;\n return defaultTag;\n }\n return tag || defaultTag;\n};\nvar handleConsumeProps = function (props, consumeProps) {\n consumeProps.forEach(function (p) {\n delete props[p];\n });\n};\nexport var componentFactory = function (_a) {\n var displayName = _a.displayName, _b = _a.classNames, classNames = _b === void 0 ? [] : _b, _c = _a.tag, defaultTag = _c === void 0 ? 'div' : _c, deprecate = _a.deprecate, defaultProps = _a.defaultProps, _d = _a.consumeProps, consumeProps = _d === void 0 ? [] : _d, render = _a.render;\n var Component = React.forwardRef(function (props, ref) {\n var className = props.className, theme = props.theme, tag = props.tag, rest = __rest(props, [\"className\", \"theme\", \"tag\"]);\n var newProps = rest;\n handleClassNames(newProps, classNames, className, theme);\n var Tag = handleTag(newProps, defaultTag, tag);\n if (deprecate) {\n newProps = handleDeprecations(newProps, deprecate, displayName);\n }\n handleConsumeProps(newProps, consumeProps);\n var finalProps = newProps;\n // @ts-ignore\n return render ? (render(finalProps, ref, Tag)) : (React.createElement(Tag, __assign({}, finalProps, { ref: ref })));\n });\n Component.displayName = displayName;\n Component.defaultProps = defaultProps;\n return Component;\n};\n","/* istanbul ignore file */\nexport var eventsMap = {\n blur: 'onBlur',\n cancel: 'onCancel',\n click: 'onClick',\n close: 'onClose',\n contextmenu: 'onContextMenu',\n copy: 'onCopy',\n cut: 'onCut',\n auxclick: 'onAuxClick',\n doubleclick: 'onDoubleClick',\n dragend: 'onDragEnd',\n dragstart: 'onDragStart',\n drop: 'onDrop',\n focus: 'onFocus',\n input: 'onInput',\n invalid: 'onInvalid',\n keydown: 'onKeyDown',\n keypress: 'onKeyPress',\n keyup: 'onKeyUp',\n mousedown: 'onMouseDown',\n mouseup: 'onMouseUp',\n paste: 'onPaste',\n pause: 'onPause',\n play: 'onPlay',\n pointercancel: 'onPointerCancel',\n pointerdown: 'onPointerDown',\n pointerup: 'onPointerUp',\n ratechange: 'onRateChange',\n reset: 'onReset',\n seeked: 'onSeeked',\n submit: 'onSubmit',\n touchcancel: 'onTouchCancel',\n touchend: 'onTouchEnd',\n touchstart: 'onTouchStart',\n volumechange: 'onVolumeChange',\n abort: 'onAbort',\n animationend: 'onAnimationEnd',\n animationiteration: 'onAnimationIteration',\n animationstart: 'onAnimationStart',\n canplay: 'onCanPlay',\n canplaythrough: 'onCanPlayThrough',\n drag: 'onDrag',\n dragenter: 'onDragEnter',\n dragexit: 'onDragExit',\n dragleave: 'onDragLeave',\n dragover: 'onDragOver',\n durationchange: 'onDurationChange',\n emptied: 'onEmptied',\n encrypted: 'onEncrypted',\n ended: 'onEnded',\n error: 'onError',\n gotpointercapture: 'onGotPointerCapture',\n load: 'onLoad',\n loadeddata: 'onLoadedData',\n loadedmetadata: 'onLoadedMetadata',\n loadstart: 'onLoadStart',\n lostpointercapture: 'onLostPointerCapture',\n mousemove: 'onMouseMove',\n mouseout: 'onMouseOut',\n mouseover: 'onMouseOver',\n playing: 'onPlaying',\n pointermove: 'onPointerMove',\n pointerout: 'onPointerOut',\n pointerover: 'onPointerOver',\n progress: 'onProgress',\n scroll: 'onScroll',\n seeking: 'onSeeking',\n stalled: 'onStalled',\n suspend: 'onSuspend',\n timeupdate: 'onTimeUpdate',\n toggle: 'onToggle',\n touchmove: 'onTouchMove',\n transitionend: 'onTransitionEnd',\n waiting: 'onWaiting',\n wheel: 'onWheel',\n mouseenter: 'onMouseEnter',\n mouseleave: 'onMouseLeave',\n pointerenter: 'onPointerEnter',\n pointerleave: 'onPointerLeave',\n change: 'onChange',\n select: 'onSelect',\n beforeinput: 'onBeforeInput',\n compositionend: 'onCompositionEnd',\n compositionstart: 'onCompositionStart',\n compositionupdate: 'onCompositionUpdate'\n};\n","export var debounce = function (func, wait) {\n var timeout;\n return function () {\n // @ts-ignore\n var context = this, args = arguments;\n var later = function () {\n timeout = null;\n func.apply(context, args);\n };\n timeout !== null && clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n};\n","var __extends = (this && this.__extends) || (function () {\n var extendStatics = function (d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return extendStatics(d, b);\n };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nvar __assign = (this && this.__assign) || function () {\n __assign = Object.assign || function(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))\n t[p] = s[p];\n }\n return t;\n };\n return __assign.apply(this, arguments);\n};\nvar __read = (this && this.__read) || function (o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n};\nvar __spread = (this && this.__spread) || function () {\n for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));\n return ar;\n};\nimport * as React from 'react';\nimport classNames from 'classnames';\nimport { eventsMap } from './utils/events-map';\nimport { debounce } from './utils/debounce';\nimport { toCamel } from './utils/strings';\nvar reactPropFromEventName = function (evtName) {\n return eventsMap[evtName] || evtName;\n};\nvar FoundationElement = /** @class */ (function () {\n function FoundationElement(onChange) {\n this._classes = new Set();\n this._events = {};\n this._style = {};\n this._props = {};\n this._ref = null;\n this._onChange = null;\n this._onChange = onChange;\n this.onChange = this.onChange.bind(this);\n this.addClass = this.addClass.bind(this);\n this.removeClass = this.removeClass.bind(this);\n this.hasClass = this.hasClass.bind(this);\n this.setProp = this.setProp.bind(this);\n this.getProp = this.getProp.bind(this);\n this.removeProp = this.removeProp.bind(this);\n this.setStyle = this.setStyle.bind(this);\n this.addEventListener = this.addEventListener.bind(this);\n this.removeEventListener = this.removeEventListener.bind(this);\n this.setRef = this.setRef.bind(this);\n }\n FoundationElement.prototype.onChange = function () {\n this._onChange && this._onChange();\n };\n FoundationElement.prototype.destroy = function () {\n this._onChange = null;\n this._ref = null;\n this._events = {};\n this._style = {};\n this._props = {};\n this._classes = new Set();\n };\n /**************************************************\n * Classes\n **************************************************/\n FoundationElement.prototype.addClass = function (className) {\n if (!this._classes.has(className)) {\n this._classes.add(className);\n this.onChange();\n }\n };\n FoundationElement.prototype.removeClass = function (className) {\n if (this._classes.has(className)) {\n this._classes.delete(className);\n this.onChange();\n }\n };\n FoundationElement.prototype.hasClass = function (className) {\n return this._classes.has(className);\n };\n /**************************************************\n * Props\n **************************************************/\n FoundationElement.prototype.setProp = function (propName, value) {\n if (this._props[propName] !== value) {\n this._props[propName] = value;\n this.onChange();\n }\n };\n FoundationElement.prototype.getProp = function (propName) {\n return this._props[propName];\n };\n FoundationElement.prototype.removeProp = function (propName) {\n if (this._props[propName] !== undefined) {\n delete this._props[propName];\n this.onChange();\n }\n };\n FoundationElement.prototype.props = function (propsToMerge) {\n var _this = this;\n var _a = propsToMerge.className, className = _a === void 0 ? '' : _a, _b = propsToMerge.style, style = _b === void 0 ? {} : _b;\n // handle merging events\n // the foundation should be able to pass something onClick as well as a user\n // This wraps them in a function that calls both\n var mergedEvents = Object.entries(propsToMerge).reduce(function (acc, _a) {\n var _b = __read(_a, 2), key = _b[0], possibleCallback = _b[1];\n var existingCallback = _this._events[key];\n if (typeof possibleCallback === 'function' &&\n typeof existingCallback === 'function') {\n var wrappedCallback = function (evt) {\n existingCallback(evt);\n return possibleCallback(evt);\n };\n acc[key] = wrappedCallback;\n }\n return acc;\n }, __assign({}, this._events));\n // handle className\n var mergedClasses = classNames(className, __spread(this._classes));\n // handle styles\n var mergedStyles = __assign({}, this._style, style);\n return __assign({}, propsToMerge, this._props, mergedEvents, { style: mergedStyles, className: mergedClasses });\n };\n /**************************************************\n * Styles\n **************************************************/\n FoundationElement.prototype.setStyle = function (propertyName, value) {\n propertyName = propertyName.startsWith('--')\n ? propertyName\n : toCamel(propertyName);\n if (this._style[propertyName] !== value) {\n this._style[propertyName] = value;\n this.onChange();\n }\n };\n /**************************************************\n * Events\n **************************************************/\n FoundationElement.prototype.addEventListener = function (evtName, callback) {\n var propName = reactPropFromEventName(evtName);\n if (this._events[propName] !== callback) {\n this._events[propName] = callback;\n this.onChange();\n }\n };\n FoundationElement.prototype.removeEventListener = function (evtName, callback) {\n var propName = reactPropFromEventName(evtName);\n if (this._events[propName]) {\n delete this._events[propName];\n this.onChange();\n }\n };\n /**************************************************\n * Refs\n **************************************************/\n FoundationElement.prototype.setRef = function (el) {\n if (el) {\n this._ref = el;\n }\n };\n Object.defineProperty(FoundationElement.prototype, \"ref\", {\n get: function () {\n return this._ref;\n },\n enumerable: true,\n configurable: true\n });\n return FoundationElement;\n}());\nexport { FoundationElement };\nvar FoundationComponent = /** @class */ (function (_super) {\n __extends(FoundationComponent, _super);\n function FoundationComponent(props) {\n var _this = _super.call(this, props) || this;\n _this.elements = {};\n //@ts-ignore\n if (_this.constructor.shouldDebounce) {\n _this.update = debounce(_this.update.bind(_this), 0);\n }\n else {\n _this.update = _this.update.bind(_this);\n }\n return _this;\n }\n FoundationComponent.prototype.componentDidMount = function () {\n this.foundation = this.getDefaultFoundation();\n this.foundation.init();\n this.sync(this.props, {});\n };\n FoundationComponent.prototype.componentDidUpdate = function (prevProps) {\n this.sync(this.props, prevProps);\n };\n FoundationComponent.prototype.componentWillUnmount = function () {\n this.foundation && this.foundation.destroy();\n // @ts-ignore\n this.foundation = undefined;\n Object.values(this.elements).forEach(function (el) { return el.destroy(); });\n };\n FoundationComponent.prototype.createElement = function (elementName) {\n var el = new FoundationElement(this.update);\n this.elements[elementName] = el;\n return el;\n };\n FoundationComponent.prototype.update = function () {\n this.foundation && this.setState({});\n };\n FoundationComponent.prototype.sync = function (props, prevProps) { };\n FoundationComponent.prototype.syncProp = function (prop, prevProp, callback) {\n if ((prop !== undefined || (prevProp !== undefined && prop === undefined)) &&\n prop !== prevProp) {\n callback();\n }\n };\n FoundationComponent.prototype.getDefaultFoundation = function () {\n return {\n init: function () { },\n destroy: function () { }\n };\n };\n /**\n * Fires a cross-browser-compatible custom event from the component root of the given type,\n */\n FoundationComponent.prototype.emit = function (evtType, evtData, shouldBubble) {\n if (shouldBubble === void 0) { shouldBubble = false; }\n var evt;\n evt = new CustomEvent(evtType, {\n detail: evtData,\n bubbles: shouldBubble\n });\n // bugfix for events coming from form elements\n // and also fits with reacts form pattern better...\n // This should always otherwise be null since there is no target\n // for Custom Events\n Object.defineProperty(evt, 'target', {\n value: evtData,\n writable: false\n });\n Object.defineProperty(evt, 'currentTarget', {\n value: evtData,\n writable: false\n });\n // Custom handling for React\n var propName = evtType;\n // check to see if the foundation still exists. If not, we are\n // probably unmounted or destroyed and dont want to call any more handlers\n // This happens when MDC broadcasts certain events on timers\n if (this.foundation) {\n //@ts-ignore\n this.props[propName] && this.props[propName](evt);\n }\n return evt;\n };\n FoundationComponent.shouldDebounce = false;\n return FoundationComponent;\n}(React.Component));\nexport { FoundationComponent };\n","export default function symbolObservablePonyfill(root) {\n\tvar result;\n\tvar Symbol = root.Symbol;\n\n\tif (typeof Symbol === 'function') {\n\t\tif (Symbol.observable) {\n\t\t\tresult = Symbol.observable;\n\t\t} else {\n\t\t\tresult = Symbol('observable');\n\t\t\tSymbol.observable = result;\n\t\t}\n\t} else {\n\t\tresult = '@@observable';\n\t}\n\n\treturn result;\n};\n","import _checkForMethod from './internal/_checkForMethod.js';\nimport _curry1 from './internal/_curry1.js';\nimport slice from './slice.js';\n\n/**\n * Returns all but the first element of the given list or string (or object\n * with a `tail` method).\n *\n * Dispatches to the `slice` method of the first argument, if present.\n *\n * @func\n * @memberOf R\n * @since v0.1.0\n * @category List\n * @sig [a] -> [a]\n * @sig String -> String\n * @param {*} list\n * @return {*}\n * @see R.head, R.init, R.last\n * @example\n *\n * R.tail([1, 2, 3]); //=> [2, 3]\n * R.tail([1, 2]); //=> [2]\n * R.tail([1]); //=> []\n * R.tail([]); //=> []\n *\n * R.tail('abc'); //=> 'bc'\n * R.tail('ab'); //=> 'b'\n * R.tail('a'); //=> ''\n * R.tail(''); //=> ''\n */\nvar tail = /*#__PURE__*/_curry1( /*#__PURE__*/_checkForMethod('tail', /*#__PURE__*/slice(1, Infinity)));\nexport default tail;","/** PURE_IMPORTS_START tslib,_util_subscribeToResult,_OuterSubscriber,_InnerSubscriber,_map,_observable_from PURE_IMPORTS_END */\nimport * as tslib_1 from \"tslib\";\nimport { subscribeToResult } from '../util/subscribeToResult';\nimport { OuterSubscriber } from '../OuterSubscriber';\nimport { InnerSubscriber } from '../InnerSubscriber';\nimport { map } from './map';\nimport { from } from '../observable/from';\nexport function mergeMap(project, resultSelector, concurrent) {\n if (concurrent === void 0) {\n concurrent = Number.POSITIVE_INFINITY;\n }\n if (typeof resultSelector === 'function') {\n return function (source) { return source.pipe(mergeMap(function (a, i) { return from(project(a, i)).pipe(map(function (b, ii) { return resultSelector(a, b, i, ii); })); }, concurrent)); };\n }\n else if (typeof resultSelector === 'number') {\n concurrent = resultSelector;\n }\n return function (source) { return source.lift(new MergeMapOperator(project, concurrent)); };\n}\nvar MergeMapOperator = /*@__PURE__*/ (function () {\n function MergeMapOperator(project, concurrent) {\n if (concurrent === void 0) {\n concurrent = Number.POSITIVE_INFINITY;\n }\n this.project = project;\n this.concurrent = concurrent;\n }\n MergeMapOperator.prototype.call = function (observer, source) {\n return source.subscribe(new MergeMapSubscriber(observer, this.project, this.concurrent));\n };\n return MergeMapOperator;\n}());\nexport { MergeMapOperator };\nvar MergeMapSubscriber = /*@__PURE__*/ (function (_super) {\n tslib_1.__extends(MergeMapSubscriber, _super);\n function MergeMapSubscriber(destination, project, concurrent) {\n if (concurrent === void 0) {\n concurrent = Number.POSITIVE_INFINITY;\n }\n var _this = _super.call(this, destination) || this;\n _this.project = project;\n _this.concurrent = concurrent;\n _this.hasCompleted = false;\n _this.buffer = [];\n _this.active = 0;\n _this.index = 0;\n return _this;\n }\n MergeMapSubscriber.prototype._next = function (value) {\n if (this.active < this.concurrent) {\n this._tryNext(value);\n }\n else {\n this.buffer.push(value);\n }\n };\n MergeMapSubscriber.prototype._tryNext = function (value) {\n var result;\n var index = this.index++;\n try {\n result = this.project(value, index);\n }\n catch (err) {\n this.destination.error(err);\n return;\n }\n this.active++;\n this._innerSub(result, value, index);\n };\n MergeMapSubscriber.prototype._innerSub = function (ish, value, index) {\n var innerSubscriber = new InnerSubscriber(this, value, index);\n var destination = this.destination;\n destination.add(innerSubscriber);\n var innerSubscription = subscribeToResult(this, ish, undefined, undefined, innerSubscriber);\n if (innerSubscription !== innerSubscriber) {\n destination.add(innerSubscription);\n }\n };\n MergeMapSubscriber.prototype._complete = function () {\n this.hasCompleted = true;\n if (this.active === 0 && this.buffer.length === 0) {\n this.destination.complete();\n }\n this.unsubscribe();\n };\n MergeMapSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) {\n this.destination.next(innerValue);\n };\n MergeMapSubscriber.prototype.notifyComplete = function (innerSub) {\n var buffer = this.buffer;\n this.remove(innerSub);\n this.active--;\n if (buffer.length > 0) {\n this._next(buffer.shift());\n }\n else if (this.active === 0 && this.hasCompleted) {\n this.destination.complete();\n }\n };\n return MergeMapSubscriber;\n}(OuterSubscriber));\nexport { MergeMapSubscriber };\n//# sourceMappingURL=mergeMap.js.map\n","\"use strict\";\n\nexports.__esModule = true;\nexports[\"default\"] = math;\n\nvar _defaultSymbols = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require(\"./presets/defaultSymbols\"));\n\nvar _errors = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require(\"../internalHelpers/_errors\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nvar unitRegExp = /((?!\\w)a|na|hc|mc|dg|me[r]?|xe|ni(?![a-zA-Z])|mm|cp|tp|xp|q(?!s)|hv|xamv|nimv|wv|sm|s(?!\\D|$)|ged|darg?|nrut)/g; // Merges additional math functionality into the defaults.\n\nfunction mergeSymbolMaps(additionalSymbols) {\n var symbolMap = {};\n symbolMap.symbols = additionalSymbols ? _extends({}, _defaultSymbols[\"default\"].symbols, additionalSymbols.symbols) : _extends({}, _defaultSymbols[\"default\"].symbols);\n return symbolMap;\n}\n\nfunction exec(operators, values) {\n var _ref;\n\n var op = operators.pop();\n values.push(op.f.apply(op, (_ref = []).concat.apply(_ref, values.splice(-op.argCount))));\n return op.precedence;\n}\n\nfunction calculate(expression, additionalSymbols) {\n var symbolMap = mergeSymbolMaps(additionalSymbols);\n var match;\n var operators = [symbolMap.symbols['('].prefix];\n var values = [];\n var pattern = new RegExp( // Pattern for numbers\n \"\\\\d+(?:\\\\.\\\\d+)?|\" + // ...and patterns for individual operators/function names\n Object.keys(symbolMap.symbols).map(function (key) {\n return symbolMap.symbols[key];\n }) // longer symbols should be listed first\n // $FlowFixMe\n .sort(function (a, b) {\n return b.symbol.length - a.symbol.length;\n }) // $FlowFixMe\n .map(function (val) {\n return val.regSymbol;\n }).join('|') + \"|(\\\\S)\", 'g');\n pattern.lastIndex = 0; // Reset regular expression object\n\n var afterValue = false;\n\n do {\n match = pattern.exec(expression);\n\n var _ref2 = match || [')', undefined],\n token = _ref2[0],\n bad = _ref2[1];\n\n var notNumber = symbolMap.symbols[token];\n var notNewValue = notNumber && !notNumber.prefix && !notNumber.func;\n var notAfterValue = !notNumber || !notNumber.postfix && !notNumber.infix; // Check for syntax errors:\n\n if (bad || (afterValue ? notAfterValue : notNewValue)) {\n throw new _errors[\"default\"](37, match ? match.index : expression.length, expression);\n }\n\n if (afterValue) {\n // We either have an infix or postfix operator (they should be mutually exclusive)\n var curr = notNumber.postfix || notNumber.infix;\n\n do {\n var prev = operators[operators.length - 1];\n if ((curr.precedence - prev.precedence || prev.rightToLeft) > 0) break; // Apply previous operator, since it has precedence over current one\n } while (exec(operators, values)); // Exit loop after executing an opening parenthesis or function\n\n\n afterValue = curr.notation === 'postfix';\n\n if (curr.symbol !== ')') {\n operators.push(curr); // Postfix always has precedence over any operator that follows after it\n\n if (afterValue) exec(operators, values);\n }\n } else if (notNumber) {\n // prefix operator or function\n operators.push(notNumber.prefix || notNumber.func);\n\n if (notNumber.func) {\n // Require an opening parenthesis\n match = pattern.exec(expression);\n\n if (!match || match[0] !== '(') {\n throw new _errors[\"default\"](38, match ? match.index : expression.length, expression);\n }\n }\n } else {\n // number\n values.push(+token);\n afterValue = true;\n }\n } while (match && operators.length);\n\n if (operators.length) {\n throw new _errors[\"default\"](39, match ? match.index : expression.length, expression);\n } else if (match) {\n throw new _errors[\"default\"](40, match ? match.index : expression.length, expression);\n } else {\n return values.pop();\n }\n}\n\nfunction reverseString(str) {\n return str.split('').reverse().join('');\n}\n/**\n * Helper for doing math with CSS Units. Accepts a formula as a string. All values in the formula must have the same unit (or be unitless). Supports complex formulas utliziing addition, subtraction, multiplication, division, square root, powers, factorial, min, max, as well as parentheses for order of operation.\n *\n *In cases where you need to do calculations with mixed units where one unit is a [relative length unit](https://developer.mozilla.org/en-US/docs/Web/CSS/length#Relative_length_units), you will want to use [CSS Calc](https://developer.mozilla.org/en-US/docs/Web/CSS/calc).\n *\n * *warning* While we've done everything possible to ensure math safely evalutes formulas expressed as strings, you should always use extreme caution when passing `math` user provided values.\n * @example\n * // Styles as object usage\n * const styles = {\n * fontSize: math('12rem + 8rem'),\n * fontSize: math('(12px + 2px) * 3'),\n * fontSize: math('3px^2 + sqrt(4)'),\n * }\n *\n * // styled-components usage\n * const div = styled.div`\n * fontSize: ${math('12rem + 8rem')};\n * fontSize: ${math('(12px + 2px) * 3')};\n * fontSize: ${math('3px^2 + sqrt(4)')};\n * `\n *\n * // CSS as JS Output\n *\n * div: {\n * fontSize: '20rem',\n * fontSize: '42px',\n * fontSize: '11px',\n * }\n */\n\n\nfunction math(formula, additionalSymbols) {\n var reversedFormula = reverseString(formula);\n var formulaMatch = reversedFormula.match(unitRegExp); // Check that all units are the same\n\n if (formulaMatch && !formulaMatch.every(function (unit) {\n return unit === formulaMatch[0];\n })) {\n throw new _errors[\"default\"](41);\n }\n\n var cleanFormula = reverseString(reversedFormula.replace(unitRegExp, ''));\n return \"\" + calculate(cleanFormula, additionalSymbols) + (formulaMatch ? reverseString(formulaMatch[0]) : '');\n}\n\nmodule.exports = exports.default;","//\n\nmodule.exports = function shallowEqual(objA, objB, compare, compareContext) {\n var ret = compare ? compare.call(compareContext, objA, objB) : void 0;\n\n if (ret !== void 0) {\n return !!ret;\n }\n\n if (objA === objB) {\n return true;\n }\n\n if (typeof objA !== \"object\" || !objA || typeof objB !== \"object\" || !objB) {\n return false;\n }\n\n var keysA = Object.keys(objA);\n var keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);\n\n // Test for A's keys different from B.\n for (var idx = 0; idx < keysA.length; idx++) {\n var key = keysA[idx];\n\n if (!bHasOwnProperty(key)) {\n return false;\n }\n\n var valueA = objA[key];\n var valueB = objB[key];\n\n ret = compare ? compare.call(compareContext, valueA, valueB, key) : void 0;\n\n if (ret === false || (ret === void 0 && valueA !== valueB)) {\n return false;\n }\n }\n\n return true;\n};\n","function stylis_min (W) {\n function M(d, c, e, h, a) {\n for (var m = 0, b = 0, v = 0, n = 0, q, g, x = 0, K = 0, k, u = k = q = 0, l = 0, r = 0, I = 0, t = 0, B = e.length, J = B - 1, y, f = '', p = '', F = '', G = '', C; l < B;) {\n g = e.charCodeAt(l);\n l === J && 0 !== b + n + v + m && (0 !== b && (g = 47 === b ? 10 : 47), n = v = m = 0, B++, J++);\n\n if (0 === b + n + v + m) {\n if (l === J && (0 < r && (f = f.replace(N, '')), 0 < f.trim().length)) {\n switch (g) {\n case 32:\n case 9:\n case 59:\n case 13:\n case 10:\n break;\n\n default:\n f += e.charAt(l);\n }\n\n g = 59;\n }\n\n switch (g) {\n case 123:\n f = f.trim();\n q = f.charCodeAt(0);\n k = 1;\n\n for (t = ++l; l < B;) {\n switch (g = e.charCodeAt(l)) {\n case 123:\n k++;\n break;\n\n case 125:\n k--;\n break;\n\n case 47:\n switch (g = e.charCodeAt(l + 1)) {\n case 42:\n case 47:\n a: {\n for (u = l + 1; u < J; ++u) {\n switch (e.charCodeAt(u)) {\n case 47:\n if (42 === g && 42 === e.charCodeAt(u - 1) && l + 2 !== u) {\n l = u + 1;\n break a;\n }\n\n break;\n\n case 10:\n if (47 === g) {\n l = u + 1;\n break a;\n }\n\n }\n }\n\n l = u;\n }\n\n }\n\n break;\n\n case 91:\n g++;\n\n case 40:\n g++;\n\n case 34:\n case 39:\n for (; l++ < J && e.charCodeAt(l) !== g;) {\n }\n\n }\n\n if (0 === k) break;\n l++;\n }\n\n k = e.substring(t, l);\n 0 === q && (q = (f = f.replace(ca, '').trim()).charCodeAt(0));\n\n switch (q) {\n case 64:\n 0 < r && (f = f.replace(N, ''));\n g = f.charCodeAt(1);\n\n switch (g) {\n case 100:\n case 109:\n case 115:\n case 45:\n r = c;\n break;\n\n default:\n r = O;\n }\n\n k = M(c, r, k, g, a + 1);\n t = k.length;\n 0 < A && (r = X(O, f, I), C = H(3, k, r, c, D, z, t, g, a, h), f = r.join(''), void 0 !== C && 0 === (t = (k = C.trim()).length) && (g = 0, k = ''));\n if (0 < t) switch (g) {\n case 115:\n f = f.replace(da, ea);\n\n case 100:\n case 109:\n case 45:\n k = f + '{' + k + '}';\n break;\n\n case 107:\n f = f.replace(fa, '$1 $2');\n k = f + '{' + k + '}';\n k = 1 === w || 2 === w && L('@' + k, 3) ? '@-webkit-' + k + '@' + k : '@' + k;\n break;\n\n default:\n k = f + k, 112 === h && (k = (p += k, ''));\n } else k = '';\n break;\n\n default:\n k = M(c, X(c, f, I), k, h, a + 1);\n }\n\n F += k;\n k = I = r = u = q = 0;\n f = '';\n g = e.charCodeAt(++l);\n break;\n\n case 125:\n case 59:\n f = (0 < r ? f.replace(N, '') : f).trim();\n if (1 < (t = f.length)) switch (0 === u && (q = f.charCodeAt(0), 45 === q || 96 < q && 123 > q) && (t = (f = f.replace(' ', ':')).length), 0 < A && void 0 !== (C = H(1, f, c, d, D, z, p.length, h, a, h)) && 0 === (t = (f = C.trim()).length) && (f = '\\x00\\x00'), q = f.charCodeAt(0), g = f.charCodeAt(1), q) {\n case 0:\n break;\n\n case 64:\n if (105 === g || 99 === g) {\n G += f + e.charAt(l);\n break;\n }\n\n default:\n 58 !== f.charCodeAt(t - 1) && (p += P(f, q, g, f.charCodeAt(2)));\n }\n I = r = u = q = 0;\n f = '';\n g = e.charCodeAt(++l);\n }\n }\n\n switch (g) {\n case 13:\n case 10:\n 47 === b ? b = 0 : 0 === 1 + q && 107 !== h && 0 < f.length && (r = 1, f += '\\x00');\n 0 < A * Y && H(0, f, c, d, D, z, p.length, h, a, h);\n z = 1;\n D++;\n break;\n\n case 59:\n case 125:\n if (0 === b + n + v + m) {\n z++;\n break;\n }\n\n default:\n z++;\n y = e.charAt(l);\n\n switch (g) {\n case 9:\n case 32:\n if (0 === n + m + b) switch (x) {\n case 44:\n case 58:\n case 9:\n case 32:\n y = '';\n break;\n\n default:\n 32 !== g && (y = ' ');\n }\n break;\n\n case 0:\n y = '\\\\0';\n break;\n\n case 12:\n y = '\\\\f';\n break;\n\n case 11:\n y = '\\\\v';\n break;\n\n case 38:\n 0 === n + b + m && (r = I = 1, y = '\\f' + y);\n break;\n\n case 108:\n if (0 === n + b + m + E && 0 < u) switch (l - u) {\n case 2:\n 112 === x && 58 === e.charCodeAt(l - 3) && (E = x);\n\n case 8:\n 111 === K && (E = K);\n }\n break;\n\n case 58:\n 0 === n + b + m && (u = l);\n break;\n\n case 44:\n 0 === b + v + n + m && (r = 1, y += '\\r');\n break;\n\n case 34:\n case 39:\n 0 === b && (n = n === g ? 0 : 0 === n ? g : n);\n break;\n\n case 91:\n 0 === n + b + v && m++;\n break;\n\n case 93:\n 0 === n + b + v && m--;\n break;\n\n case 41:\n 0 === n + b + m && v--;\n break;\n\n case 40:\n if (0 === n + b + m) {\n if (0 === q) switch (2 * x + 3 * K) {\n case 533:\n break;\n\n default:\n q = 1;\n }\n v++;\n }\n\n break;\n\n case 64:\n 0 === b + v + n + m + u + k && (k = 1);\n break;\n\n case 42:\n case 47:\n if (!(0 < n + m + v)) switch (b) {\n case 0:\n switch (2 * g + 3 * e.charCodeAt(l + 1)) {\n case 235:\n b = 47;\n break;\n\n case 220:\n t = l, b = 42;\n }\n\n break;\n\n case 42:\n 47 === g && 42 === x && t + 2 !== l && (33 === e.charCodeAt(t + 2) && (p += e.substring(t, l + 1)), y = '', b = 0);\n }\n }\n\n 0 === b && (f += y);\n }\n\n K = x;\n x = g;\n l++;\n }\n\n t = p.length;\n\n if (0 < t) {\n r = c;\n if (0 < A && (C = H(2, p, r, d, D, z, t, h, a, h), void 0 !== C && 0 === (p = C).length)) return G + p + F;\n p = r.join(',') + '{' + p + '}';\n\n if (0 !== w * E) {\n 2 !== w || L(p, 2) || (E = 0);\n\n switch (E) {\n case 111:\n p = p.replace(ha, ':-moz-$1') + p;\n break;\n\n case 112:\n p = p.replace(Q, '::-webkit-input-$1') + p.replace(Q, '::-moz-$1') + p.replace(Q, ':-ms-input-$1') + p;\n }\n\n E = 0;\n }\n }\n\n return G + p + F;\n }\n\n function X(d, c, e) {\n var h = c.trim().split(ia);\n c = h;\n var a = h.length,\n m = d.length;\n\n switch (m) {\n case 0:\n case 1:\n var b = 0;\n\n for (d = 0 === m ? '' : d[0] + ' '; b < a; ++b) {\n c[b] = Z(d, c[b], e).trim();\n }\n\n break;\n\n default:\n var v = b = 0;\n\n for (c = []; b < a; ++b) {\n for (var n = 0; n < m; ++n) {\n c[v++] = Z(d[n] + ' ', h[b], e).trim();\n }\n }\n\n }\n\n return c;\n }\n\n function Z(d, c, e) {\n var h = c.charCodeAt(0);\n 33 > h && (h = (c = c.trim()).charCodeAt(0));\n\n switch (h) {\n case 38:\n return c.replace(F, '$1' + d.trim());\n\n case 58:\n return d.trim() + c.replace(F, '$1' + d.trim());\n\n default:\n if (0 < 1 * e && 0 < c.indexOf('\\f')) return c.replace(F, (58 === d.charCodeAt(0) ? '' : '$1') + d.trim());\n }\n\n return d + c;\n }\n\n function P(d, c, e, h) {\n var a = d + ';',\n m = 2 * c + 3 * e + 4 * h;\n\n if (944 === m) {\n d = a.indexOf(':', 9) + 1;\n var b = a.substring(d, a.length - 1).trim();\n b = a.substring(0, d).trim() + b + ';';\n return 1 === w || 2 === w && L(b, 1) ? '-webkit-' + b + b : b;\n }\n\n if (0 === w || 2 === w && !L(a, 1)) return a;\n\n switch (m) {\n case 1015:\n return 97 === a.charCodeAt(10) ? '-webkit-' + a + a : a;\n\n case 951:\n return 116 === a.charCodeAt(3) ? '-webkit-' + a + a : a;\n\n case 963:\n return 110 === a.charCodeAt(5) ? '-webkit-' + a + a : a;\n\n case 1009:\n if (100 !== a.charCodeAt(4)) break;\n\n case 969:\n case 942:\n return '-webkit-' + a + a;\n\n case 978:\n return '-webkit-' + a + '-moz-' + a + a;\n\n case 1019:\n case 983:\n return '-webkit-' + a + '-moz-' + a + '-ms-' + a + a;\n\n case 883:\n if (45 === a.charCodeAt(8)) return '-webkit-' + a + a;\n if (0 < a.indexOf('image-set(', 11)) return a.replace(ja, '$1-webkit-$2') + a;\n break;\n\n case 932:\n if (45 === a.charCodeAt(4)) switch (a.charCodeAt(5)) {\n case 103:\n return '-webkit-box-' + a.replace('-grow', '') + '-webkit-' + a + '-ms-' + a.replace('grow', 'positive') + a;\n\n case 115:\n return '-webkit-' + a + '-ms-' + a.replace('shrink', 'negative') + a;\n\n case 98:\n return '-webkit-' + a + '-ms-' + a.replace('basis', 'preferred-size') + a;\n }\n return '-webkit-' + a + '-ms-' + a + a;\n\n case 964:\n return '-webkit-' + a + '-ms-flex-' + a + a;\n\n case 1023:\n if (99 !== a.charCodeAt(8)) break;\n b = a.substring(a.indexOf(':', 15)).replace('flex-', '').replace('space-between', 'justify');\n return '-webkit-box-pack' + b + '-webkit-' + a + '-ms-flex-pack' + b + a;\n\n case 1005:\n return ka.test(a) ? a.replace(aa, ':-webkit-') + a.replace(aa, ':-moz-') + a : a;\n\n case 1e3:\n b = a.substring(13).trim();\n c = b.indexOf('-') + 1;\n\n switch (b.charCodeAt(0) + b.charCodeAt(c)) {\n case 226:\n b = a.replace(G, 'tb');\n break;\n\n case 232:\n b = a.replace(G, 'tb-rl');\n break;\n\n case 220:\n b = a.replace(G, 'lr');\n break;\n\n default:\n return a;\n }\n\n return '-webkit-' + a + '-ms-' + b + a;\n\n case 1017:\n if (-1 === a.indexOf('sticky', 9)) break;\n\n case 975:\n c = (a = d).length - 10;\n b = (33 === a.charCodeAt(c) ? a.substring(0, c) : a).substring(d.indexOf(':', 7) + 1).trim();\n\n switch (m = b.charCodeAt(0) + (b.charCodeAt(7) | 0)) {\n case 203:\n if (111 > b.charCodeAt(8)) break;\n\n case 115:\n a = a.replace(b, '-webkit-' + b) + ';' + a;\n break;\n\n case 207:\n case 102:\n a = a.replace(b, '-webkit-' + (102 < m ? 'inline-' : '') + 'box') + ';' + a.replace(b, '-webkit-' + b) + ';' + a.replace(b, '-ms-' + b + 'box') + ';' + a;\n }\n\n return a + ';';\n\n case 938:\n if (45 === a.charCodeAt(5)) switch (a.charCodeAt(6)) {\n case 105:\n return b = a.replace('-items', ''), '-webkit-' + a + '-webkit-box-' + b + '-ms-flex-' + b + a;\n\n case 115:\n return '-webkit-' + a + '-ms-flex-item-' + a.replace(ba, '') + a;\n\n default:\n return '-webkit-' + a + '-ms-flex-line-pack' + a.replace('align-content', '').replace(ba, '') + a;\n }\n break;\n\n case 973:\n case 989:\n if (45 !== a.charCodeAt(3) || 122 === a.charCodeAt(4)) break;\n\n case 931:\n case 953:\n if (!0 === la.test(d)) return 115 === (b = d.substring(d.indexOf(':') + 1)).charCodeAt(0) ? P(d.replace('stretch', 'fill-available'), c, e, h).replace(':fill-available', ':stretch') : a.replace(b, '-webkit-' + b) + a.replace(b, '-moz-' + b.replace('fill-', '')) + a;\n break;\n\n case 962:\n if (a = '-webkit-' + a + (102 === a.charCodeAt(5) ? '-ms-' + a : '') + a, 211 === e + h && 105 === a.charCodeAt(13) && 0 < a.indexOf('transform', 10)) return a.substring(0, a.indexOf(';', 27) + 1).replace(ma, '$1-webkit-$2') + a;\n }\n\n return a;\n }\n\n function L(d, c) {\n var e = d.indexOf(1 === c ? ':' : '{'),\n h = d.substring(0, 3 !== c ? e : 10);\n e = d.substring(e + 1, d.length - 1);\n return R(2 !== c ? h : h.replace(na, '$1'), e, c);\n }\n\n function ea(d, c) {\n var e = P(c, c.charCodeAt(0), c.charCodeAt(1), c.charCodeAt(2));\n return e !== c + ';' ? e.replace(oa, ' or ($1)').substring(4) : '(' + c + ')';\n }\n\n function H(d, c, e, h, a, m, b, v, n, q) {\n for (var g = 0, x = c, w; g < A; ++g) {\n switch (w = S[g].call(B, d, x, e, h, a, m, b, v, n, q)) {\n case void 0:\n case !1:\n case !0:\n case null:\n break;\n\n default:\n x = w;\n }\n }\n\n if (x !== c) return x;\n }\n\n function T(d) {\n switch (d) {\n case void 0:\n case null:\n A = S.length = 0;\n break;\n\n default:\n if ('function' === typeof d) S[A++] = d;else if ('object' === typeof d) for (var c = 0, e = d.length; c < e; ++c) {\n T(d[c]);\n } else Y = !!d | 0;\n }\n\n return T;\n }\n\n function U(d) {\n d = d.prefix;\n void 0 !== d && (R = null, d ? 'function' !== typeof d ? w = 1 : (w = 2, R = d) : w = 0);\n return U;\n }\n\n function B(d, c) {\n var e = d;\n 33 > e.charCodeAt(0) && (e = e.trim());\n V = e;\n e = [V];\n\n if (0 < A) {\n var h = H(-1, c, e, e, D, z, 0, 0, 0, 0);\n void 0 !== h && 'string' === typeof h && (c = h);\n }\n\n var a = M(O, e, c, 0, 0);\n 0 < A && (h = H(-2, a, e, e, D, z, a.length, 0, 0, 0), void 0 !== h && (a = h));\n V = '';\n E = 0;\n z = D = 1;\n return a;\n }\n\n var ca = /^\\0+/g,\n N = /[\\0\\r\\f]/g,\n aa = /: */g,\n ka = /zoo|gra/,\n ma = /([,: ])(transform)/g,\n ia = /,\\r+?/g,\n F = /([\\t\\r\\n ])*\\f?&/g,\n fa = /@(k\\w+)\\s*(\\S*)\\s*/,\n Q = /::(place)/g,\n ha = /:(read-only)/g,\n G = /[svh]\\w+-[tblr]{2}/,\n da = /\\(\\s*(.*)\\s*\\)/g,\n oa = /([\\s\\S]*?);/g,\n ba = /-self|flex-/g,\n na = /[^]*?(:[rp][el]a[\\w-]+)[^]*/,\n la = /stretch|:\\s*\\w+\\-(?:conte|avail)/,\n ja = /([^-])(image-set\\()/,\n z = 1,\n D = 1,\n E = 0,\n w = 1,\n O = [],\n S = [],\n A = 0,\n R = null,\n Y = 0,\n V = '';\n B.use = T;\n B.set = U;\n void 0 !== W && U(W);\n return B;\n}\n\nexport default stylis_min;\n","var unitlessKeys = {\n animationIterationCount: 1,\n borderImageOutset: 1,\n borderImageSlice: 1,\n borderImageWidth: 1,\n boxFlex: 1,\n boxFlexGroup: 1,\n boxOrdinalGroup: 1,\n columnCount: 1,\n columns: 1,\n flex: 1,\n flexGrow: 1,\n flexPositive: 1,\n flexShrink: 1,\n flexNegative: 1,\n flexOrder: 1,\n gridRow: 1,\n gridRowEnd: 1,\n gridRowSpan: 1,\n gridRowStart: 1,\n gridColumn: 1,\n gridColumnEnd: 1,\n gridColumnSpan: 1,\n gridColumnStart: 1,\n msGridRow: 1,\n msGridRowSpan: 1,\n msGridColumn: 1,\n msGridColumnSpan: 1,\n fontWeight: 1,\n lineHeight: 1,\n opacity: 1,\n order: 1,\n orphans: 1,\n tabSize: 1,\n widows: 1,\n zIndex: 1,\n zoom: 1,\n WebkitLineClamp: 1,\n // SVG-related properties\n fillOpacity: 1,\n floodOpacity: 1,\n stopOpacity: 1,\n strokeDasharray: 1,\n strokeDashoffset: 1,\n strokeMiterlimit: 1,\n strokeOpacity: 1,\n strokeWidth: 1\n};\n\nexport default unitlessKeys;\n","var objectKeys = require('object-keys');\nvar isArguments = require('is-arguments');\nvar is = require('object-is');\nvar isRegex = require('is-regex');\nvar flags = require('regexp.prototype.flags');\nvar isDate = require('is-date-object');\n\nvar getTime = Date.prototype.getTime;\n\nfunction deepEqual(actual, expected, options) {\n var opts = options || {};\n\n // 7.1. All identical values are equivalent, as determined by ===.\n if (opts.strict ? is(actual, expected) : actual === expected) {\n return true;\n }\n\n // 7.3. Other pairs that do not both pass typeof value == 'object', equivalence is determined by ==.\n if (!actual || !expected || (typeof actual !== 'object' && typeof expected !== 'object')) {\n return opts.strict ? is(actual, expected) : actual == expected;\n }\n\n /*\n * 7.4. For all other Object pairs, including Array objects, equivalence is\n * determined by having the same number of owned properties (as verified\n * with Object.prototype.hasOwnProperty.call), the same set of keys\n * (although not necessarily the same order), equivalent values for every\n * corresponding key, and an identical 'prototype' property. Note: this\n * accounts for both named and indexed properties on Arrays.\n */\n // eslint-disable-next-line no-use-before-define\n return objEquiv(actual, expected, opts);\n}\n\nfunction isUndefinedOrNull(value) {\n return value === null || value === undefined;\n}\n\nfunction isBuffer(x) {\n if (!x || typeof x !== 'object' || typeof x.length !== 'number') {\n return false;\n }\n if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {\n return false;\n }\n if (x.length > 0 && typeof x[0] !== 'number') {\n return false;\n }\n return true;\n}\n\nfunction objEquiv(a, b, opts) {\n /* eslint max-statements: [2, 50] */\n var i, key;\n if (typeof a !== typeof b) { return false; }\n if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) { return false; }\n\n // an identical 'prototype' property.\n if (a.prototype !== b.prototype) { return false; }\n\n if (isArguments(a) !== isArguments(b)) { return false; }\n\n var aIsRegex = isRegex(a);\n var bIsRegex = isRegex(b);\n if (aIsRegex !== bIsRegex) { return false; }\n if (aIsRegex || bIsRegex) {\n return a.source === b.source && flags(a) === flags(b);\n }\n\n if (isDate(a) && isDate(b)) {\n return getTime.call(a) === getTime.call(b);\n }\n\n var aIsBuffer = isBuffer(a);\n var bIsBuffer = isBuffer(b);\n if (aIsBuffer !== bIsBuffer) { return false; }\n if (aIsBuffer || bIsBuffer) { // && would work too, because both are true or both false here\n if (a.length !== b.length) { return false; }\n for (i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) { return false; }\n }\n return true;\n }\n\n if (typeof a !== typeof b) { return false; }\n\n try {\n var ka = objectKeys(a);\n var kb = objectKeys(b);\n } catch (e) { // happens when one is a string literal and the other isn't\n return false;\n }\n // having the same number of owned properties (keys incorporates hasOwnProperty)\n if (ka.length !== kb.length) { return false; }\n\n // the same set of keys (although not necessarily the same order),\n ka.sort();\n kb.sort();\n // ~~~cheap key test\n for (i = ka.length - 1; i >= 0; i--) {\n if (ka[i] != kb[i]) { return false; }\n }\n // equivalent values for every corresponding key, and ~~~possibly expensive deep test\n for (i = ka.length - 1; i >= 0; i--) {\n key = ka[i];\n if (!deepEqual(a[key], b[key], opts)) { return false; }\n }\n\n return true;\n}\n\nmodule.exports = deepEqual;\n","var global = typeof self !== 'undefined' ? self : this;\nvar __self__ = (function () {\nfunction F() {\nthis.fetch = false;\nthis.DOMException = global.DOMException\n}\nF.prototype = global;\nreturn new F();\n})();\n(function(self) {\n\nvar irrelevant = (function (exports) {\n\n var support = {\n searchParams: 'URLSearchParams' in self,\n iterable: 'Symbol' in self && 'iterator' in Symbol,\n blob:\n 'FileReader' in self &&\n 'Blob' in self &&\n (function() {\n try {\n new Blob();\n return true\n } catch (e) {\n return false\n }\n })(),\n formData: 'FormData' in self,\n arrayBuffer: 'ArrayBuffer' in self\n };\n\n function isDataView(obj) {\n return obj && DataView.prototype.isPrototypeOf(obj)\n }\n\n if (support.arrayBuffer) {\n var viewClasses = [\n '[object Int8Array]',\n '[object Uint8Array]',\n '[object Uint8ClampedArray]',\n '[object Int16Array]',\n '[object Uint16Array]',\n '[object Int32Array]',\n '[object Uint32Array]',\n '[object Float32Array]',\n '[object Float64Array]'\n ];\n\n var isArrayBufferView =\n ArrayBuffer.isView ||\n function(obj) {\n return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1\n };\n }\n\n function normalizeName(name) {\n if (typeof name !== 'string') {\n name = String(name);\n }\n if (/[^a-z0-9\\-#$%&'*+.^_`|~]/i.test(name)) {\n throw new TypeError('Invalid character in header field name')\n }\n return name.toLowerCase()\n }\n\n function normalizeValue(value) {\n if (typeof value !== 'string') {\n value = String(value);\n }\n return value\n }\n\n // Build a destructive iterator for the value list\n function iteratorFor(items) {\n var iterator = {\n next: function() {\n var value = items.shift();\n return {done: value === undefined, value: value}\n }\n };\n\n if (support.iterable) {\n iterator[Symbol.iterator] = function() {\n return iterator\n };\n }\n\n return iterator\n }\n\n function Headers(headers) {\n this.map = {};\n\n if (headers instanceof Headers) {\n headers.forEach(function(value, name) {\n this.append(name, value);\n }, this);\n } else if (Array.isArray(headers)) {\n headers.forEach(function(header) {\n this.append(header[0], header[1]);\n }, this);\n } else if (headers) {\n Object.getOwnPropertyNames(headers).forEach(function(name) {\n this.append(name, headers[name]);\n }, this);\n }\n }\n\n Headers.prototype.append = function(name, value) {\n name = normalizeName(name);\n value = normalizeValue(value);\n var oldValue = this.map[name];\n this.map[name] = oldValue ? oldValue + ', ' + value : value;\n };\n\n Headers.prototype['delete'] = function(name) {\n delete this.map[normalizeName(name)];\n };\n\n Headers.prototype.get = function(name) {\n name = normalizeName(name);\n return this.has(name) ? this.map[name] : null\n };\n\n Headers.prototype.has = function(name) {\n return this.map.hasOwnProperty(normalizeName(name))\n };\n\n Headers.prototype.set = function(name, value) {\n this.map[normalizeName(name)] = normalizeValue(value);\n };\n\n Headers.prototype.forEach = function(callback, thisArg) {\n for (var name in this.map) {\n if (this.map.hasOwnProperty(name)) {\n callback.call(thisArg, this.map[name], name, this);\n }\n }\n };\n\n Headers.prototype.keys = function() {\n var items = [];\n this.forEach(function(value, name) {\n items.push(name);\n });\n return iteratorFor(items)\n };\n\n Headers.prototype.values = function() {\n var items = [];\n this.forEach(function(value) {\n items.push(value);\n });\n return iteratorFor(items)\n };\n\n Headers.prototype.entries = function() {\n var items = [];\n this.forEach(function(value, name) {\n items.push([name, value]);\n });\n return iteratorFor(items)\n };\n\n if (support.iterable) {\n Headers.prototype[Symbol.iterator] = Headers.prototype.entries;\n }\n\n function consumed(body) {\n if (body.bodyUsed) {\n return Promise.reject(new TypeError('Already read'))\n }\n body.bodyUsed = true;\n }\n\n function fileReaderReady(reader) {\n return new Promise(function(resolve, reject) {\n reader.onload = function() {\n resolve(reader.result);\n };\n reader.onerror = function() {\n reject(reader.error);\n };\n })\n }\n\n function readBlobAsArrayBuffer(blob) {\n var reader = new FileReader();\n var promise = fileReaderReady(reader);\n reader.readAsArrayBuffer(blob);\n return promise\n }\n\n function readBlobAsText(blob) {\n var reader = new FileReader();\n var promise = fileReaderReady(reader);\n reader.readAsText(blob);\n return promise\n }\n\n function readArrayBufferAsText(buf) {\n var view = new Uint8Array(buf);\n var chars = new Array(view.length);\n\n for (var i = 0; i < view.length; i++) {\n chars[i] = String.fromCharCode(view[i]);\n }\n return chars.join('')\n }\n\n function bufferClone(buf) {\n if (buf.slice) {\n return buf.slice(0)\n } else {\n var view = new Uint8Array(buf.byteLength);\n view.set(new Uint8Array(buf));\n return view.buffer\n }\n }\n\n function Body() {\n this.bodyUsed = false;\n\n this._initBody = function(body) {\n this._bodyInit = body;\n if (!body) {\n this._bodyText = '';\n } else if (typeof body === 'string') {\n this._bodyText = body;\n } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {\n this._bodyBlob = body;\n } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {\n this._bodyFormData = body;\n } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {\n this._bodyText = body.toString();\n } else if (support.arrayBuffer && support.blob && isDataView(body)) {\n this._bodyArrayBuffer = bufferClone(body.buffer);\n // IE 10-11 can't handle a DataView body.\n this._bodyInit = new Blob([this._bodyArrayBuffer]);\n } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {\n this._bodyArrayBuffer = bufferClone(body);\n } else {\n this._bodyText = body = Object.prototype.toString.call(body);\n }\n\n if (!this.headers.get('content-type')) {\n if (typeof body === 'string') {\n this.headers.set('content-type', 'text/plain;charset=UTF-8');\n } else if (this._bodyBlob && this._bodyBlob.type) {\n this.headers.set('content-type', this._bodyBlob.type);\n } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {\n this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');\n }\n }\n };\n\n if (support.blob) {\n this.blob = function() {\n var rejected = consumed(this);\n if (rejected) {\n return rejected\n }\n\n if (this._bodyBlob) {\n return Promise.resolve(this._bodyBlob)\n } else if (this._bodyArrayBuffer) {\n return Promise.resolve(new Blob([this._bodyArrayBuffer]))\n } else if (this._bodyFormData) {\n throw new Error('could not read FormData body as blob')\n } else {\n return Promise.resolve(new Blob([this._bodyText]))\n }\n };\n\n this.arrayBuffer = function() {\n if (this._bodyArrayBuffer) {\n return consumed(this) || Promise.resolve(this._bodyArrayBuffer)\n } else {\n return this.blob().then(readBlobAsArrayBuffer)\n }\n };\n }\n\n this.text = function() {\n var rejected = consumed(this);\n if (rejected) {\n return rejected\n }\n\n if (this._bodyBlob) {\n return readBlobAsText(this._bodyBlob)\n } else if (this._bodyArrayBuffer) {\n return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))\n } else if (this._bodyFormData) {\n throw new Error('could not read FormData body as text')\n } else {\n return Promise.resolve(this._bodyText)\n }\n };\n\n if (support.formData) {\n this.formData = function() {\n return this.text().then(decode)\n };\n }\n\n this.json = function() {\n return this.text().then(JSON.parse)\n };\n\n return this\n }\n\n // HTTP methods whose capitalization should be normalized\n var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];\n\n function normalizeMethod(method) {\n var upcased = method.toUpperCase();\n return methods.indexOf(upcased) > -1 ? upcased : method\n }\n\n function Request(input, options) {\n options = options || {};\n var body = options.body;\n\n if (input instanceof Request) {\n if (input.bodyUsed) {\n throw new TypeError('Already read')\n }\n this.url = input.url;\n this.credentials = input.credentials;\n if (!options.headers) {\n this.headers = new Headers(input.headers);\n }\n this.method = input.method;\n this.mode = input.mode;\n this.signal = input.signal;\n if (!body && input._bodyInit != null) {\n body = input._bodyInit;\n input.bodyUsed = true;\n }\n } else {\n this.url = String(input);\n }\n\n this.credentials = options.credentials || this.credentials || 'same-origin';\n if (options.headers || !this.headers) {\n this.headers = new Headers(options.headers);\n }\n this.method = normalizeMethod(options.method || this.method || 'GET');\n this.mode = options.mode || this.mode || null;\n this.signal = options.signal || this.signal;\n this.referrer = null;\n\n if ((this.method === 'GET' || this.method === 'HEAD') && body) {\n throw new TypeError('Body not allowed for GET or HEAD requests')\n }\n this._initBody(body);\n }\n\n Request.prototype.clone = function() {\n return new Request(this, {body: this._bodyInit})\n };\n\n function decode(body) {\n var form = new FormData();\n body\n .trim()\n .split('&')\n .forEach(function(bytes) {\n if (bytes) {\n var split = bytes.split('=');\n var name = split.shift().replace(/\\+/g, ' ');\n var value = split.join('=').replace(/\\+/g, ' ');\n form.append(decodeURIComponent(name), decodeURIComponent(value));\n }\n });\n return form\n }\n\n function parseHeaders(rawHeaders) {\n var headers = new Headers();\n // Replace instances of \\r\\n and \\n followed by at least one space or horizontal tab with a space\n // https://tools.ietf.org/html/rfc7230#section-3.2\n var preProcessedHeaders = rawHeaders.replace(/\\r?\\n[\\t ]+/g, ' ');\n preProcessedHeaders.split(/\\r?\\n/).forEach(function(line) {\n var parts = line.split(':');\n var key = parts.shift().trim();\n if (key) {\n var value = parts.join(':').trim();\n headers.append(key, value);\n }\n });\n return headers\n }\n\n Body.call(Request.prototype);\n\n function Response(bodyInit, options) {\n if (!options) {\n options = {};\n }\n\n this.type = 'default';\n this.status = options.status === undefined ? 200 : options.status;\n this.ok = this.status >= 200 && this.status < 300;\n this.statusText = 'statusText' in options ? options.statusText : 'OK';\n this.headers = new Headers(options.headers);\n this.url = options.url || '';\n this._initBody(bodyInit);\n }\n\n Body.call(Response.prototype);\n\n Response.prototype.clone = function() {\n return new Response(this._bodyInit, {\n status: this.status,\n statusText: this.statusText,\n headers: new Headers(this.headers),\n url: this.url\n })\n };\n\n Response.error = function() {\n var response = new Response(null, {status: 0, statusText: ''});\n response.type = 'error';\n return response\n };\n\n var redirectStatuses = [301, 302, 303, 307, 308];\n\n Response.redirect = function(url, status) {\n if (redirectStatuses.indexOf(status) === -1) {\n throw new RangeError('Invalid status code')\n }\n\n return new Response(null, {status: status, headers: {location: url}})\n };\n\n exports.DOMException = self.DOMException;\n try {\n new exports.DOMException();\n } catch (err) {\n exports.DOMException = function(message, name) {\n this.message = message;\n this.name = name;\n var error = Error(message);\n this.stack = error.stack;\n };\n exports.DOMException.prototype = Object.create(Error.prototype);\n exports.DOMException.prototype.constructor = exports.DOMException;\n }\n\n function fetch(input, init) {\n return new Promise(function(resolve, reject) {\n var request = new Request(input, init);\n\n if (request.signal && request.signal.aborted) {\n return reject(new exports.DOMException('Aborted', 'AbortError'))\n }\n\n var xhr = new XMLHttpRequest();\n\n function abortXhr() {\n xhr.abort();\n }\n\n xhr.onload = function() {\n var options = {\n status: xhr.status,\n statusText: xhr.statusText,\n headers: parseHeaders(xhr.getAllResponseHeaders() || '')\n };\n options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');\n var body = 'response' in xhr ? xhr.response : xhr.responseText;\n resolve(new Response(body, options));\n };\n\n xhr.onerror = function() {\n reject(new TypeError('Network request failed'));\n };\n\n xhr.ontimeout = function() {\n reject(new TypeError('Network request failed'));\n };\n\n xhr.onabort = function() {\n reject(new exports.DOMException('Aborted', 'AbortError'));\n };\n\n xhr.open(request.method, request.url, true);\n\n if (request.credentials === 'include') {\n xhr.withCredentials = true;\n } else if (request.credentials === 'omit') {\n xhr.withCredentials = false;\n }\n\n if ('responseType' in xhr && support.blob) {\n xhr.responseType = 'blob';\n }\n\n request.headers.forEach(function(value, name) {\n xhr.setRequestHeader(name, value);\n });\n\n if (request.signal) {\n request.signal.addEventListener('abort', abortXhr);\n\n xhr.onreadystatechange = function() {\n // DONE (success or failure)\n if (xhr.readyState === 4) {\n request.signal.removeEventListener('abort', abortXhr);\n }\n };\n }\n\n xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);\n })\n }\n\n fetch.polyfill = true;\n\n if (!self.fetch) {\n self.fetch = fetch;\n self.Headers = Headers;\n self.Request = Request;\n self.Response = Response;\n }\n\n exports.Headers = Headers;\n exports.Request = Request;\n exports.Response = Response;\n exports.fetch = fetch;\n\n Object.defineProperty(exports, '__esModule', { value: true });\n\n return exports;\n\n})({});\n})(__self__);\n__self__.fetch.ponyfill = true;\n// Remove \"polyfill\" property added by whatwg-fetch\ndelete __self__.fetch.polyfill;\n// Choose between native implementation (global) or custom implementation (__self__)\n// var ctx = global.fetch ? global : __self__;\nvar ctx = __self__; // this line disable service worker support temporarily\nexports = ctx.fetch // To enable: import fetch from 'cross-fetch'\nexports.default = ctx.fetch // For TypeScript consumers without esModuleInterop.\nexports.fetch = ctx.fetch // To enable: import {fetch} from 'cross-fetch'\nexports.Headers = ctx.Headers\nexports.Request = ctx.Request\nexports.Response = ctx.Response\nmodule.exports = exports\n","'use strict';\n\n// do not edit .js files directly - edit src/index.jst\n\n\n\nmodule.exports = function equal(a, b) {\n if (a === b) return true;\n\n if (a && b && typeof a == 'object' && typeof b == 'object') {\n if (a.constructor !== b.constructor) return false;\n\n var length, i, keys;\n if (Array.isArray(a)) {\n length = a.length;\n if (length != b.length) return false;\n for (i = length; i-- !== 0;)\n if (!equal(a[i], b[i])) return false;\n return true;\n }\n\n\n\n if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;\n if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();\n if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();\n\n keys = Object.keys(a);\n length = keys.length;\n if (length !== Object.keys(b).length) return false;\n\n for (i = length; i-- !== 0;)\n if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;\n\n for (i = length; i-- !== 0;) {\n var key = keys[i];\n\n if (key === '_owner' && a.$$typeof) {\n // React-specific: avoid traversing React elements' _owner.\n // _owner contains circular references\n // and is not needed when comparing the actual elements (and not their owners)\n continue;\n }\n\n if (!equal(a[key], b[key])) return false;\n }\n\n return true;\n }\n\n // true if both NaN, false otherwise\n return a!==a && b!==b;\n};\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-is.production.min.js');\n} else {\n module.exports = require('./cjs/react-is.development.js');\n}\n","'use strict';\n\nmodule.exports = require('./src/js/main');\n","/**\r\n * A collection of shims that provide minimal functionality of the ES6 collections.\r\n *\r\n * These implementations are not meant to be used outside of the ResizeObserver\r\n * modules as they cover only a limited range of use cases.\r\n */\r\n/* eslint-disable require-jsdoc, valid-jsdoc */\r\nvar MapShim = (function () {\r\n if (typeof Map !== 'undefined') {\r\n return Map;\r\n }\r\n /**\r\n * Returns index in provided array that matches the specified key.\r\n *\r\n * @param {Array} arr\r\n * @param {*} key\r\n * @returns {number}\r\n */\r\n function getIndex(arr, key) {\r\n var result = -1;\r\n arr.some(function (entry, index) {\r\n if (entry[0] === key) {\r\n result = index;\r\n return true;\r\n }\r\n return false;\r\n });\r\n return result;\r\n }\r\n return /** @class */ (function () {\r\n function class_1() {\r\n this.__entries__ = [];\r\n }\r\n Object.defineProperty(class_1.prototype, \"size\", {\r\n /**\r\n * @returns {boolean}\r\n */\r\n get: function () {\r\n return this.__entries__.length;\r\n },\r\n enumerable: true,\r\n configurable: true\r\n });\r\n /**\r\n * @param {*} key\r\n * @returns {*}\r\n */\r\n class_1.prototype.get = function (key) {\r\n var index = getIndex(this.__entries__, key);\r\n var entry = this.__entries__[index];\r\n return entry && entry[1];\r\n };\r\n /**\r\n * @param {*} key\r\n * @param {*} value\r\n * @returns {void}\r\n */\r\n class_1.prototype.set = function (key, value) {\r\n var index = getIndex(this.__entries__, key);\r\n if (~index) {\r\n this.__entries__[index][1] = value;\r\n }\r\n else {\r\n this.__entries__.push([key, value]);\r\n }\r\n };\r\n /**\r\n * @param {*} key\r\n * @returns {void}\r\n */\r\n class_1.prototype.delete = function (key) {\r\n var entries = this.__entries__;\r\n var index = getIndex(entries, key);\r\n if (~index) {\r\n entries.splice(index, 1);\r\n }\r\n };\r\n /**\r\n * @param {*} key\r\n * @returns {void}\r\n */\r\n class_1.prototype.has = function (key) {\r\n return !!~getIndex(this.__entries__, key);\r\n };\r\n /**\r\n * @returns {void}\r\n */\r\n class_1.prototype.clear = function () {\r\n this.__entries__.splice(0);\r\n };\r\n /**\r\n * @param {Function} callback\r\n * @param {*} [ctx=null]\r\n * @returns {void}\r\n */\r\n class_1.prototype.forEach = function (callback, ctx) {\r\n if (ctx === void 0) { ctx = null; }\r\n for (var _i = 0, _a = this.__entries__; _i < _a.length; _i++) {\r\n var entry = _a[_i];\r\n callback.call(ctx, entry[1], entry[0]);\r\n }\r\n };\r\n return class_1;\r\n }());\r\n})();\n\n/**\r\n * Detects whether window and document objects are available in current environment.\r\n */\r\nvar isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && window.document === document;\n\n// Returns global object of a current environment.\r\nvar global$1 = (function () {\r\n if (typeof global !== 'undefined' && global.Math === Math) {\r\n return global;\r\n }\r\n if (typeof self !== 'undefined' && self.Math === Math) {\r\n return self;\r\n }\r\n if (typeof window !== 'undefined' && window.Math === Math) {\r\n return window;\r\n }\r\n // eslint-disable-next-line no-new-func\r\n return Function('return this')();\r\n})();\n\n/**\r\n * A shim for the requestAnimationFrame which falls back to the setTimeout if\r\n * first one is not supported.\r\n *\r\n * @returns {number} Requests' identifier.\r\n */\r\nvar requestAnimationFrame$1 = (function () {\r\n if (typeof requestAnimationFrame === 'function') {\r\n // It's required to use a bounded function because IE sometimes throws\r\n // an \"Invalid calling object\" error if rAF is invoked without the global\r\n // object on the left hand side.\r\n return requestAnimationFrame.bind(global$1);\r\n }\r\n return function (callback) { return setTimeout(function () { return callback(Date.now()); }, 1000 / 60); };\r\n})();\n\n// Defines minimum timeout before adding a trailing call.\r\nvar trailingTimeout = 2;\r\n/**\r\n * Creates a wrapper function which ensures that provided callback will be\r\n * invoked only once during the specified delay period.\r\n *\r\n * @param {Function} callback - Function to be invoked after the delay period.\r\n * @param {number} delay - Delay after which to invoke callback.\r\n * @returns {Function}\r\n */\r\nfunction throttle (callback, delay) {\r\n var leadingCall = false, trailingCall = false, lastCallTime = 0;\r\n /**\r\n * Invokes the original callback function and schedules new invocation if\r\n * the \"proxy\" was called during current request.\r\n *\r\n * @returns {void}\r\n */\r\n function resolvePending() {\r\n if (leadingCall) {\r\n leadingCall = false;\r\n callback();\r\n }\r\n if (trailingCall) {\r\n proxy();\r\n }\r\n }\r\n /**\r\n * Callback invoked after the specified delay. It will further postpone\r\n * invocation of the original function delegating it to the\r\n * requestAnimationFrame.\r\n *\r\n * @returns {void}\r\n */\r\n function timeoutCallback() {\r\n requestAnimationFrame$1(resolvePending);\r\n }\r\n /**\r\n * Schedules invocation of the original function.\r\n *\r\n * @returns {void}\r\n */\r\n function proxy() {\r\n var timeStamp = Date.now();\r\n if (leadingCall) {\r\n // Reject immediately following calls.\r\n if (timeStamp - lastCallTime < trailingTimeout) {\r\n return;\r\n }\r\n // Schedule new call to be in invoked when the pending one is resolved.\r\n // This is important for \"transitions\" which never actually start\r\n // immediately so there is a chance that we might miss one if change\r\n // happens amids the pending invocation.\r\n trailingCall = true;\r\n }\r\n else {\r\n leadingCall = true;\r\n trailingCall = false;\r\n setTimeout(timeoutCallback, delay);\r\n }\r\n lastCallTime = timeStamp;\r\n }\r\n return proxy;\r\n}\n\n// Minimum delay before invoking the update of observers.\r\nvar REFRESH_DELAY = 20;\r\n// A list of substrings of CSS properties used to find transition events that\r\n// might affect dimensions of observed elements.\r\nvar transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight'];\r\n// Check if MutationObserver is available.\r\nvar mutationObserverSupported = typeof MutationObserver !== 'undefined';\r\n/**\r\n * Singleton controller class which handles updates of ResizeObserver instances.\r\n */\r\nvar ResizeObserverController = /** @class */ (function () {\r\n /**\r\n * Creates a new instance of ResizeObserverController.\r\n *\r\n * @private\r\n */\r\n function ResizeObserverController() {\r\n /**\r\n * Indicates whether DOM listeners have been added.\r\n *\r\n * @private {boolean}\r\n */\r\n this.connected_ = false;\r\n /**\r\n * Tells that controller has subscribed for Mutation Events.\r\n *\r\n * @private {boolean}\r\n */\r\n this.mutationEventsAdded_ = false;\r\n /**\r\n * Keeps reference to the instance of MutationObserver.\r\n *\r\n * @private {MutationObserver}\r\n */\r\n this.mutationsObserver_ = null;\r\n /**\r\n * A list of connected observers.\r\n *\r\n * @private {Array}\r\n */\r\n this.observers_ = [];\r\n this.onTransitionEnd_ = this.onTransitionEnd_.bind(this);\r\n this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY);\r\n }\r\n /**\r\n * Adds observer to observers list.\r\n *\r\n * @param {ResizeObserverSPI} observer - Observer to be added.\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.addObserver = function (observer) {\r\n if (!~this.observers_.indexOf(observer)) {\r\n this.observers_.push(observer);\r\n }\r\n // Add listeners if they haven't been added yet.\r\n if (!this.connected_) {\r\n this.connect_();\r\n }\r\n };\r\n /**\r\n * Removes observer from observers list.\r\n *\r\n * @param {ResizeObserverSPI} observer - Observer to be removed.\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.removeObserver = function (observer) {\r\n var observers = this.observers_;\r\n var index = observers.indexOf(observer);\r\n // Remove observer if it's present in registry.\r\n if (~index) {\r\n observers.splice(index, 1);\r\n }\r\n // Remove listeners if controller has no connected observers.\r\n if (!observers.length && this.connected_) {\r\n this.disconnect_();\r\n }\r\n };\r\n /**\r\n * Invokes the update of observers. It will continue running updates insofar\r\n * it detects changes.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.refresh = function () {\r\n var changesDetected = this.updateObservers_();\r\n // Continue running updates if changes have been detected as there might\r\n // be future ones caused by CSS transitions.\r\n if (changesDetected) {\r\n this.refresh();\r\n }\r\n };\r\n /**\r\n * Updates every observer from observers list and notifies them of queued\r\n * entries.\r\n *\r\n * @private\r\n * @returns {boolean} Returns \"true\" if any observer has detected changes in\r\n * dimensions of it's elements.\r\n */\r\n ResizeObserverController.prototype.updateObservers_ = function () {\r\n // Collect observers that have active observations.\r\n var activeObservers = this.observers_.filter(function (observer) {\r\n return observer.gatherActive(), observer.hasActive();\r\n });\r\n // Deliver notifications in a separate cycle in order to avoid any\r\n // collisions between observers, e.g. when multiple instances of\r\n // ResizeObserver are tracking the same element and the callback of one\r\n // of them changes content dimensions of the observed target. Sometimes\r\n // this may result in notifications being blocked for the rest of observers.\r\n activeObservers.forEach(function (observer) { return observer.broadcastActive(); });\r\n return activeObservers.length > 0;\r\n };\r\n /**\r\n * Initializes DOM listeners.\r\n *\r\n * @private\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.connect_ = function () {\r\n // Do nothing if running in a non-browser environment or if listeners\r\n // have been already added.\r\n if (!isBrowser || this.connected_) {\r\n return;\r\n }\r\n // Subscription to the \"Transitionend\" event is used as a workaround for\r\n // delayed transitions. This way it's possible to capture at least the\r\n // final state of an element.\r\n document.addEventListener('transitionend', this.onTransitionEnd_);\r\n window.addEventListener('resize', this.refresh);\r\n if (mutationObserverSupported) {\r\n this.mutationsObserver_ = new MutationObserver(this.refresh);\r\n this.mutationsObserver_.observe(document, {\r\n attributes: true,\r\n childList: true,\r\n characterData: true,\r\n subtree: true\r\n });\r\n }\r\n else {\r\n document.addEventListener('DOMSubtreeModified', this.refresh);\r\n this.mutationEventsAdded_ = true;\r\n }\r\n this.connected_ = true;\r\n };\r\n /**\r\n * Removes DOM listeners.\r\n *\r\n * @private\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.disconnect_ = function () {\r\n // Do nothing if running in a non-browser environment or if listeners\r\n // have been already removed.\r\n if (!isBrowser || !this.connected_) {\r\n return;\r\n }\r\n document.removeEventListener('transitionend', this.onTransitionEnd_);\r\n window.removeEventListener('resize', this.refresh);\r\n if (this.mutationsObserver_) {\r\n this.mutationsObserver_.disconnect();\r\n }\r\n if (this.mutationEventsAdded_) {\r\n document.removeEventListener('DOMSubtreeModified', this.refresh);\r\n }\r\n this.mutationsObserver_ = null;\r\n this.mutationEventsAdded_ = false;\r\n this.connected_ = false;\r\n };\r\n /**\r\n * \"Transitionend\" event handler.\r\n *\r\n * @private\r\n * @param {TransitionEvent} event\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.onTransitionEnd_ = function (_a) {\r\n var _b = _a.propertyName, propertyName = _b === void 0 ? '' : _b;\r\n // Detect whether transition may affect dimensions of an element.\r\n var isReflowProperty = transitionKeys.some(function (key) {\r\n return !!~propertyName.indexOf(key);\r\n });\r\n if (isReflowProperty) {\r\n this.refresh();\r\n }\r\n };\r\n /**\r\n * Returns instance of the ResizeObserverController.\r\n *\r\n * @returns {ResizeObserverController}\r\n */\r\n ResizeObserverController.getInstance = function () {\r\n if (!this.instance_) {\r\n this.instance_ = new ResizeObserverController();\r\n }\r\n return this.instance_;\r\n };\r\n /**\r\n * Holds reference to the controller's instance.\r\n *\r\n * @private {ResizeObserverController}\r\n */\r\n ResizeObserverController.instance_ = null;\r\n return ResizeObserverController;\r\n}());\n\n/**\r\n * Defines non-writable/enumerable properties of the provided target object.\r\n *\r\n * @param {Object} target - Object for which to define properties.\r\n * @param {Object} props - Properties to be defined.\r\n * @returns {Object} Target object.\r\n */\r\nvar defineConfigurable = (function (target, props) {\r\n for (var _i = 0, _a = Object.keys(props); _i < _a.length; _i++) {\r\n var key = _a[_i];\r\n Object.defineProperty(target, key, {\r\n value: props[key],\r\n enumerable: false,\r\n writable: false,\r\n configurable: true\r\n });\r\n }\r\n return target;\r\n});\n\n/**\r\n * Returns the global object associated with provided element.\r\n *\r\n * @param {Object} target\r\n * @returns {Object}\r\n */\r\nvar getWindowOf = (function (target) {\r\n // Assume that the element is an instance of Node, which means that it\r\n // has the \"ownerDocument\" property from which we can retrieve a\r\n // corresponding global object.\r\n var ownerGlobal = target && target.ownerDocument && target.ownerDocument.defaultView;\r\n // Return the local global object if it's not possible extract one from\r\n // provided element.\r\n return ownerGlobal || global$1;\r\n});\n\n// Placeholder of an empty content rectangle.\r\nvar emptyRect = createRectInit(0, 0, 0, 0);\r\n/**\r\n * Converts provided string to a number.\r\n *\r\n * @param {number|string} value\r\n * @returns {number}\r\n */\r\nfunction toFloat(value) {\r\n return parseFloat(value) || 0;\r\n}\r\n/**\r\n * Extracts borders size from provided styles.\r\n *\r\n * @param {CSSStyleDeclaration} styles\r\n * @param {...string} positions - Borders positions (top, right, ...)\r\n * @returns {number}\r\n */\r\nfunction getBordersSize(styles) {\r\n var positions = [];\r\n for (var _i = 1; _i < arguments.length; _i++) {\r\n positions[_i - 1] = arguments[_i];\r\n }\r\n return positions.reduce(function (size, position) {\r\n var value = styles['border-' + position + '-width'];\r\n return size + toFloat(value);\r\n }, 0);\r\n}\r\n/**\r\n * Extracts paddings sizes from provided styles.\r\n *\r\n * @param {CSSStyleDeclaration} styles\r\n * @returns {Object} Paddings box.\r\n */\r\nfunction getPaddings(styles) {\r\n var positions = ['top', 'right', 'bottom', 'left'];\r\n var paddings = {};\r\n for (var _i = 0, positions_1 = positions; _i < positions_1.length; _i++) {\r\n var position = positions_1[_i];\r\n var value = styles['padding-' + position];\r\n paddings[position] = toFloat(value);\r\n }\r\n return paddings;\r\n}\r\n/**\r\n * Calculates content rectangle of provided SVG element.\r\n *\r\n * @param {SVGGraphicsElement} target - Element content rectangle of which needs\r\n * to be calculated.\r\n * @returns {DOMRectInit}\r\n */\r\nfunction getSVGContentRect(target) {\r\n var bbox = target.getBBox();\r\n return createRectInit(0, 0, bbox.width, bbox.height);\r\n}\r\n/**\r\n * Calculates content rectangle of provided HTMLElement.\r\n *\r\n * @param {HTMLElement} target - Element for which to calculate the content rectangle.\r\n * @returns {DOMRectInit}\r\n */\r\nfunction getHTMLElementContentRect(target) {\r\n // Client width & height properties can't be\r\n // used exclusively as they provide rounded values.\r\n var clientWidth = target.clientWidth, clientHeight = target.clientHeight;\r\n // By this condition we can catch all non-replaced inline, hidden and\r\n // detached elements. Though elements with width & height properties less\r\n // than 0.5 will be discarded as well.\r\n //\r\n // Without it we would need to implement separate methods for each of\r\n // those cases and it's not possible to perform a precise and performance\r\n // effective test for hidden elements. E.g. even jQuery's ':visible' filter\r\n // gives wrong results for elements with width & height less than 0.5.\r\n if (!clientWidth && !clientHeight) {\r\n return emptyRect;\r\n }\r\n var styles = getWindowOf(target).getComputedStyle(target);\r\n var paddings = getPaddings(styles);\r\n var horizPad = paddings.left + paddings.right;\r\n var vertPad = paddings.top + paddings.bottom;\r\n // Computed styles of width & height are being used because they are the\r\n // only dimensions available to JS that contain non-rounded values. It could\r\n // be possible to utilize the getBoundingClientRect if only it's data wasn't\r\n // affected by CSS transformations let alone paddings, borders and scroll bars.\r\n var width = toFloat(styles.width), height = toFloat(styles.height);\r\n // Width & height include paddings and borders when the 'border-box' box\r\n // model is applied (except for IE).\r\n if (styles.boxSizing === 'border-box') {\r\n // Following conditions are required to handle Internet Explorer which\r\n // doesn't include paddings and borders to computed CSS dimensions.\r\n //\r\n // We can say that if CSS dimensions + paddings are equal to the \"client\"\r\n // properties then it's either IE, and thus we don't need to subtract\r\n // anything, or an element merely doesn't have paddings/borders styles.\r\n if (Math.round(width + horizPad) !== clientWidth) {\r\n width -= getBordersSize(styles, 'left', 'right') + horizPad;\r\n }\r\n if (Math.round(height + vertPad) !== clientHeight) {\r\n height -= getBordersSize(styles, 'top', 'bottom') + vertPad;\r\n }\r\n }\r\n // Following steps can't be applied to the document's root element as its\r\n // client[Width/Height] properties represent viewport area of the window.\r\n // Besides, it's as well not necessary as the itself neither has\r\n // rendered scroll bars nor it can be clipped.\r\n if (!isDocumentElement(target)) {\r\n // In some browsers (only in Firefox, actually) CSS width & height\r\n // include scroll bars size which can be removed at this step as scroll\r\n // bars are the only difference between rounded dimensions + paddings\r\n // and \"client\" properties, though that is not always true in Chrome.\r\n var vertScrollbar = Math.round(width + horizPad) - clientWidth;\r\n var horizScrollbar = Math.round(height + vertPad) - clientHeight;\r\n // Chrome has a rather weird rounding of \"client\" properties.\r\n // E.g. for an element with content width of 314.2px it sometimes gives\r\n // the client width of 315px and for the width of 314.7px it may give\r\n // 314px. And it doesn't happen all the time. So just ignore this delta\r\n // as a non-relevant.\r\n if (Math.abs(vertScrollbar) !== 1) {\r\n width -= vertScrollbar;\r\n }\r\n if (Math.abs(horizScrollbar) !== 1) {\r\n height -= horizScrollbar;\r\n }\r\n }\r\n return createRectInit(paddings.left, paddings.top, width, height);\r\n}\r\n/**\r\n * Checks whether provided element is an instance of the SVGGraphicsElement.\r\n *\r\n * @param {Element} target - Element to be checked.\r\n * @returns {boolean}\r\n */\r\nvar isSVGGraphicsElement = (function () {\r\n // Some browsers, namely IE and Edge, don't have the SVGGraphicsElement\r\n // interface.\r\n if (typeof SVGGraphicsElement !== 'undefined') {\r\n return function (target) { return target instanceof getWindowOf(target).SVGGraphicsElement; };\r\n }\r\n // If it's so, then check that element is at least an instance of the\r\n // SVGElement and that it has the \"getBBox\" method.\r\n // eslint-disable-next-line no-extra-parens\r\n return function (target) { return (target instanceof getWindowOf(target).SVGElement &&\r\n typeof target.getBBox === 'function'); };\r\n})();\r\n/**\r\n * Checks whether provided element is a document element ().\r\n *\r\n * @param {Element} target - Element to be checked.\r\n * @returns {boolean}\r\n */\r\nfunction isDocumentElement(target) {\r\n return target === getWindowOf(target).document.documentElement;\r\n}\r\n/**\r\n * Calculates an appropriate content rectangle for provided html or svg element.\r\n *\r\n * @param {Element} target - Element content rectangle of which needs to be calculated.\r\n * @returns {DOMRectInit}\r\n */\r\nfunction getContentRect(target) {\r\n if (!isBrowser) {\r\n return emptyRect;\r\n }\r\n if (isSVGGraphicsElement(target)) {\r\n return getSVGContentRect(target);\r\n }\r\n return getHTMLElementContentRect(target);\r\n}\r\n/**\r\n * Creates rectangle with an interface of the DOMRectReadOnly.\r\n * Spec: https://drafts.fxtf.org/geometry/#domrectreadonly\r\n *\r\n * @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions.\r\n * @returns {DOMRectReadOnly}\r\n */\r\nfunction createReadOnlyRect(_a) {\r\n var x = _a.x, y = _a.y, width = _a.width, height = _a.height;\r\n // If DOMRectReadOnly is available use it as a prototype for the rectangle.\r\n var Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object;\r\n var rect = Object.create(Constr.prototype);\r\n // Rectangle's properties are not writable and non-enumerable.\r\n defineConfigurable(rect, {\r\n x: x, y: y, width: width, height: height,\r\n top: y,\r\n right: x + width,\r\n bottom: height + y,\r\n left: x\r\n });\r\n return rect;\r\n}\r\n/**\r\n * Creates DOMRectInit object based on the provided dimensions and the x/y coordinates.\r\n * Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit\r\n *\r\n * @param {number} x - X coordinate.\r\n * @param {number} y - Y coordinate.\r\n * @param {number} width - Rectangle's width.\r\n * @param {number} height - Rectangle's height.\r\n * @returns {DOMRectInit}\r\n */\r\nfunction createRectInit(x, y, width, height) {\r\n return { x: x, y: y, width: width, height: height };\r\n}\n\n/**\r\n * Class that is responsible for computations of the content rectangle of\r\n * provided DOM element and for keeping track of it's changes.\r\n */\r\nvar ResizeObservation = /** @class */ (function () {\r\n /**\r\n * Creates an instance of ResizeObservation.\r\n *\r\n * @param {Element} target - Element to be observed.\r\n */\r\n function ResizeObservation(target) {\r\n /**\r\n * Broadcasted width of content rectangle.\r\n *\r\n * @type {number}\r\n */\r\n this.broadcastWidth = 0;\r\n /**\r\n * Broadcasted height of content rectangle.\r\n *\r\n * @type {number}\r\n */\r\n this.broadcastHeight = 0;\r\n /**\r\n * Reference to the last observed content rectangle.\r\n *\r\n * @private {DOMRectInit}\r\n */\r\n this.contentRect_ = createRectInit(0, 0, 0, 0);\r\n this.target = target;\r\n }\r\n /**\r\n * Updates content rectangle and tells whether it's width or height properties\r\n * have changed since the last broadcast.\r\n *\r\n * @returns {boolean}\r\n */\r\n ResizeObservation.prototype.isActive = function () {\r\n var rect = getContentRect(this.target);\r\n this.contentRect_ = rect;\r\n return (rect.width !== this.broadcastWidth ||\r\n rect.height !== this.broadcastHeight);\r\n };\r\n /**\r\n * Updates 'broadcastWidth' and 'broadcastHeight' properties with a data\r\n * from the corresponding properties of the last observed content rectangle.\r\n *\r\n * @returns {DOMRectInit} Last observed content rectangle.\r\n */\r\n ResizeObservation.prototype.broadcastRect = function () {\r\n var rect = this.contentRect_;\r\n this.broadcastWidth = rect.width;\r\n this.broadcastHeight = rect.height;\r\n return rect;\r\n };\r\n return ResizeObservation;\r\n}());\n\nvar ResizeObserverEntry = /** @class */ (function () {\r\n /**\r\n * Creates an instance of ResizeObserverEntry.\r\n *\r\n * @param {Element} target - Element that is being observed.\r\n * @param {DOMRectInit} rectInit - Data of the element's content rectangle.\r\n */\r\n function ResizeObserverEntry(target, rectInit) {\r\n var contentRect = createReadOnlyRect(rectInit);\r\n // According to the specification following properties are not writable\r\n // and are also not enumerable in the native implementation.\r\n //\r\n // Property accessors are not being used as they'd require to define a\r\n // private WeakMap storage which may cause memory leaks in browsers that\r\n // don't support this type of collections.\r\n defineConfigurable(this, { target: target, contentRect: contentRect });\r\n }\r\n return ResizeObserverEntry;\r\n}());\n\nvar ResizeObserverSPI = /** @class */ (function () {\r\n /**\r\n * Creates a new instance of ResizeObserver.\r\n *\r\n * @param {ResizeObserverCallback} callback - Callback function that is invoked\r\n * when one of the observed elements changes it's content dimensions.\r\n * @param {ResizeObserverController} controller - Controller instance which\r\n * is responsible for the updates of observer.\r\n * @param {ResizeObserver} callbackCtx - Reference to the public\r\n * ResizeObserver instance which will be passed to callback function.\r\n */\r\n function ResizeObserverSPI(callback, controller, callbackCtx) {\r\n /**\r\n * Collection of resize observations that have detected changes in dimensions\r\n * of elements.\r\n *\r\n * @private {Array}\r\n */\r\n this.activeObservations_ = [];\r\n /**\r\n * Registry of the ResizeObservation instances.\r\n *\r\n * @private {Map}\r\n */\r\n this.observations_ = new MapShim();\r\n if (typeof callback !== 'function') {\r\n throw new TypeError('The callback provided as parameter 1 is not a function.');\r\n }\r\n this.callback_ = callback;\r\n this.controller_ = controller;\r\n this.callbackCtx_ = callbackCtx;\r\n }\r\n /**\r\n * Starts observing provided element.\r\n *\r\n * @param {Element} target - Element to be observed.\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.observe = function (target) {\r\n if (!arguments.length) {\r\n throw new TypeError('1 argument required, but only 0 present.');\r\n }\r\n // Do nothing if current environment doesn't have the Element interface.\r\n if (typeof Element === 'undefined' || !(Element instanceof Object)) {\r\n return;\r\n }\r\n if (!(target instanceof getWindowOf(target).Element)) {\r\n throw new TypeError('parameter 1 is not of type \"Element\".');\r\n }\r\n var observations = this.observations_;\r\n // Do nothing if element is already being observed.\r\n if (observations.has(target)) {\r\n return;\r\n }\r\n observations.set(target, new ResizeObservation(target));\r\n this.controller_.addObserver(this);\r\n // Force the update of observations.\r\n this.controller_.refresh();\r\n };\r\n /**\r\n * Stops observing provided element.\r\n *\r\n * @param {Element} target - Element to stop observing.\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.unobserve = function (target) {\r\n if (!arguments.length) {\r\n throw new TypeError('1 argument required, but only 0 present.');\r\n }\r\n // Do nothing if current environment doesn't have the Element interface.\r\n if (typeof Element === 'undefined' || !(Element instanceof Object)) {\r\n return;\r\n }\r\n if (!(target instanceof getWindowOf(target).Element)) {\r\n throw new TypeError('parameter 1 is not of type \"Element\".');\r\n }\r\n var observations = this.observations_;\r\n // Do nothing if element is not being observed.\r\n if (!observations.has(target)) {\r\n return;\r\n }\r\n observations.delete(target);\r\n if (!observations.size) {\r\n this.controller_.removeObserver(this);\r\n }\r\n };\r\n /**\r\n * Stops observing all elements.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.disconnect = function () {\r\n this.clearActive();\r\n this.observations_.clear();\r\n this.controller_.removeObserver(this);\r\n };\r\n /**\r\n * Collects observation instances the associated element of which has changed\r\n * it's content rectangle.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.gatherActive = function () {\r\n var _this = this;\r\n this.clearActive();\r\n this.observations_.forEach(function (observation) {\r\n if (observation.isActive()) {\r\n _this.activeObservations_.push(observation);\r\n }\r\n });\r\n };\r\n /**\r\n * Invokes initial callback function with a list of ResizeObserverEntry\r\n * instances collected from active resize observations.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.broadcastActive = function () {\r\n // Do nothing if observer doesn't have active observations.\r\n if (!this.hasActive()) {\r\n return;\r\n }\r\n var ctx = this.callbackCtx_;\r\n // Create ResizeObserverEntry instance for every active observation.\r\n var entries = this.activeObservations_.map(function (observation) {\r\n return new ResizeObserverEntry(observation.target, observation.broadcastRect());\r\n });\r\n this.callback_.call(ctx, entries, ctx);\r\n this.clearActive();\r\n };\r\n /**\r\n * Clears the collection of active observations.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.clearActive = function () {\r\n this.activeObservations_.splice(0);\r\n };\r\n /**\r\n * Tells whether observer has active observations.\r\n *\r\n * @returns {boolean}\r\n */\r\n ResizeObserverSPI.prototype.hasActive = function () {\r\n return this.activeObservations_.length > 0;\r\n };\r\n return ResizeObserverSPI;\r\n}());\n\n// Registry of internal observers. If WeakMap is not available use current shim\r\n// for the Map collection as it has all required methods and because WeakMap\r\n// can't be fully polyfilled anyway.\r\nvar observers = typeof WeakMap !== 'undefined' ? new WeakMap() : new MapShim();\r\n/**\r\n * ResizeObserver API. Encapsulates the ResizeObserver SPI implementation\r\n * exposing only those methods and properties that are defined in the spec.\r\n */\r\nvar ResizeObserver = /** @class */ (function () {\r\n /**\r\n * Creates a new instance of ResizeObserver.\r\n *\r\n * @param {ResizeObserverCallback} callback - Callback that is invoked when\r\n * dimensions of the observed elements change.\r\n */\r\n function ResizeObserver(callback) {\r\n if (!(this instanceof ResizeObserver)) {\r\n throw new TypeError('Cannot call a class as a function.');\r\n }\r\n if (!arguments.length) {\r\n throw new TypeError('1 argument required, but only 0 present.');\r\n }\r\n var controller = ResizeObserverController.getInstance();\r\n var observer = new ResizeObserverSPI(callback, controller, this);\r\n observers.set(this, observer);\r\n }\r\n return ResizeObserver;\r\n}());\r\n// Expose public methods of ResizeObserver.\r\n[\r\n 'observe',\r\n 'unobserve',\r\n 'disconnect'\r\n].forEach(function (method) {\r\n ResizeObserver.prototype[method] = function () {\r\n var _a;\r\n return (_a = observers.get(this))[method].apply(_a, arguments);\r\n };\r\n});\n\nvar index = (function () {\r\n // Export existing implementation if available.\r\n if (typeof global$1.ResizeObserver !== 'undefined') {\r\n return global$1.ResizeObserver;\r\n }\r\n return ResizeObserver;\r\n})();\n\nexport default index;\n","import arrayWithHoles from \"./arrayWithHoles\";\nimport iterableToArray from \"./iterableToArray\";\nimport nonIterableRest from \"./nonIterableRest\";\nexport default function _toArray(arr) {\n return arrayWithHoles(arr) || iterableToArray(arr) || nonIterableRest();\n}","import { useRef } from 'react';\nimport useEffectOnce from './useEffectOnce';\nvar useUnmount = function (fn) {\n var fnRef = useRef(fn);\n // update the ref each render so if it change the newest callback will be invoked\n fnRef.current = fn;\n useEffectOnce(function () { return function () { return fnRef.current(); }; });\n};\nexport default useUnmount;\n","/**!\n * easy-pie-chart\n * Lightweight plugin to render simple, animated and retina optimized pie charts\n *\n * @license \n * @author Robert Fleischmann (http://robert-fleischmann.de)\n * @version 2.1.7\n **/\n\n(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module unless amdModuleId is set\n define([], function () {\n return (root['EasyPieChart'] = factory());\n });\n } else if (typeof exports === 'object') {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n } else {\n root['EasyPieChart'] = factory();\n }\n}(this, function () {\n\n/**\n * Renderer to render the chart on a canvas object\n * @param {DOMElement} el DOM element to host the canvas (root of the plugin)\n * @param {object} options options object of the plugin\n */\nvar CanvasRenderer = function(el, options) {\n\tvar cachedBackground;\n\tvar canvas = document.createElement('canvas');\n\n\tel.appendChild(canvas);\n\n\tif (typeof(G_vmlCanvasManager) === 'object') {\n\t\tG_vmlCanvasManager.initElement(canvas);\n\t}\n\n\tvar ctx = canvas.getContext('2d');\n\n\tcanvas.width = canvas.height = options.size;\n\n\t// canvas on retina devices\n\tvar scaleBy = 1;\n\tif (window.devicePixelRatio > 1) {\n\t\tscaleBy = window.devicePixelRatio;\n\t\tcanvas.style.width = canvas.style.height = [options.size, 'px'].join('');\n\t\tcanvas.width = canvas.height = options.size * scaleBy;\n\t\tctx.scale(scaleBy, scaleBy);\n\t}\n\n\t// move 0,0 coordinates to the center\n\tctx.translate(options.size / 2, options.size / 2);\n\n\t// rotate canvas -90deg\n\tctx.rotate((-1 / 2 + options.rotate / 180) * Math.PI);\n\n\tvar radius = (options.size - options.lineWidth) / 2;\n\tif (options.scaleColor && options.scaleLength) {\n\t\tradius -= options.scaleLength + 2; // 2 is the distance between scale and bar\n\t}\n\n\t// IE polyfill for Date\n\tDate.now = Date.now || function() {\n\t\treturn +(new Date());\n\t};\n\n\t/**\n\t * Draw a circle around the center of the canvas\n\t * @param {strong} color Valid CSS color string\n\t * @param {number} lineWidth Width of the line in px\n\t * @param {number} percent Percentage to draw (float between -1 and 1)\n\t */\n\tvar drawCircle = function(color, lineWidth, percent) {\n\t\tpercent = Math.min(Math.max(-1, percent || 0), 1);\n\t\tvar isNegative = percent <= 0 ? true : false;\n\n\t\tctx.beginPath();\n\t\tctx.arc(0, 0, radius, 0, Math.PI * 2 * percent, isNegative);\n\n\t\tctx.strokeStyle = color;\n\t\tctx.lineWidth = lineWidth;\n\n\t\tctx.stroke();\n\t};\n\n\t/**\n\t * Draw the scale of the chart\n\t */\n\tvar drawScale = function() {\n\t\tvar offset;\n\t\tvar length;\n\n\t\tctx.lineWidth = 1;\n\t\tctx.fillStyle = options.scaleColor;\n\n\t\tctx.save();\n\t\tfor (var i = 24; i > 0; --i) {\n\t\t\tif (i % 6 === 0) {\n\t\t\t\tlength = options.scaleLength;\n\t\t\t\toffset = 0;\n\t\t\t} else {\n\t\t\t\tlength = options.scaleLength * 0.6;\n\t\t\t\toffset = options.scaleLength - length;\n\t\t\t}\n\t\t\tctx.fillRect(-options.size/2 + offset, 0, length, 1);\n\t\t\tctx.rotate(Math.PI / 12);\n\t\t}\n\t\tctx.restore();\n\t};\n\n\t/**\n\t * Request animation frame wrapper with polyfill\n\t * @return {function} Request animation frame method or timeout fallback\n\t */\n\tvar reqAnimationFrame = (function() {\n\t\treturn window.requestAnimationFrame ||\n\t\t\t\twindow.webkitRequestAnimationFrame ||\n\t\t\t\twindow.mozRequestAnimationFrame ||\n\t\t\t\tfunction(callback) {\n\t\t\t\t\twindow.setTimeout(callback, 1000 / 60);\n\t\t\t\t};\n\t}());\n\n\t/**\n\t * Draw the background of the plugin including the scale and the track\n\t */\n\tvar drawBackground = function() {\n\t\tif(options.scaleColor) drawScale();\n\t\tif(options.trackColor) drawCircle(options.trackColor, options.trackWidth || options.lineWidth, 1);\n\t};\n\n /**\n * Canvas accessor\n */\n this.getCanvas = function() {\n return canvas;\n };\n\n /**\n * Canvas 2D context 'ctx' accessor\n */\n this.getCtx = function() {\n return ctx;\n };\n\n\t/**\n\t * Clear the complete canvas\n\t */\n\tthis.clear = function() {\n\t\tctx.clearRect(options.size / -2, options.size / -2, options.size, options.size);\n\t};\n\n\t/**\n\t * Draw the complete chart\n\t * @param {number} percent Percent shown by the chart between -100 and 100\n\t */\n\tthis.draw = function(percent) {\n\t\t// do we need to render a background\n\t\tif (!!options.scaleColor || !!options.trackColor) {\n\t\t\t// getImageData and putImageData are supported\n\t\t\tif (ctx.getImageData && ctx.putImageData) {\n\t\t\t\tif (!cachedBackground) {\n\t\t\t\t\tdrawBackground();\n\t\t\t\t\tcachedBackground = ctx.getImageData(0, 0, options.size * scaleBy, options.size * scaleBy);\n\t\t\t\t} else {\n\t\t\t\t\tctx.putImageData(cachedBackground, 0, 0);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.clear();\n\t\t\t\tdrawBackground();\n\t\t\t}\n\t\t} else {\n\t\t\tthis.clear();\n\t\t}\n\n\t\tctx.lineCap = options.lineCap;\n\n\t\t// if barcolor is a function execute it and pass the percent as a value\n\t\tvar color;\n\t\tif (typeof(options.barColor) === 'function') {\n\t\t\tcolor = options.barColor(percent);\n\t\t} else {\n\t\t\tcolor = options.barColor;\n\t\t}\n\n\t\t// draw bar\n\t\tdrawCircle(color, options.lineWidth, percent / 100);\n\t}.bind(this);\n\n\t/**\n\t * Animate from some percent to some other percentage\n\t * @param {number} from Starting percentage\n\t * @param {number} to Final percentage\n\t */\n\tthis.animate = function(from, to) {\n\t\tvar startTime = Date.now();\n\t\toptions.onStart(from, to);\n\t\tvar animation = function() {\n\t\t\tvar process = Math.min(Date.now() - startTime, options.animate.duration);\n\t\t\tvar currentValue = options.easing(this, process, from, to - from, options.animate.duration);\n\t\t\tthis.draw(currentValue);\n\t\t\toptions.onStep(from, to, currentValue);\n\t\t\tif (process >= options.animate.duration) {\n\t\t\t\toptions.onStop(from, to);\n\t\t\t} else {\n\t\t\t\treqAnimationFrame(animation);\n\t\t\t}\n\t\t}.bind(this);\n\n\t\treqAnimationFrame(animation);\n\t}.bind(this);\n};\n\nvar EasyPieChart = function(el, opts) {\n\tvar defaultOptions = {\n\t\tbarColor: '#ef1e25',\n\t\ttrackColor: '#f9f9f9',\n\t\tscaleColor: '#dfe0e0',\n\t\tscaleLength: 5,\n\t\tlineCap: 'round',\n\t\tlineWidth: 3,\n\t\ttrackWidth: undefined,\n\t\tsize: 110,\n\t\trotate: 0,\n\t\tanimate: {\n\t\t\tduration: 1000,\n\t\t\tenabled: true\n\t\t},\n\t\teasing: function (x, t, b, c, d) { // more can be found here: http://gsgd.co.uk/sandbox/jquery/easing/\n\t\t\tt = t / (d/2);\n\t\t\tif (t < 1) {\n\t\t\t\treturn c / 2 * t * t + b;\n\t\t\t}\n\t\t\treturn -c/2 * ((--t)*(t-2) - 1) + b;\n\t\t},\n\t\tonStart: function(from, to) {\n\t\t\treturn;\n\t\t},\n\t\tonStep: function(from, to, currentValue) {\n\t\t\treturn;\n\t\t},\n\t\tonStop: function(from, to) {\n\t\t\treturn;\n\t\t}\n\t};\n\n\t// detect present renderer\n\tif (typeof(CanvasRenderer) !== 'undefined') {\n\t\tdefaultOptions.renderer = CanvasRenderer;\n\t} else if (typeof(SVGRenderer) !== 'undefined') {\n\t\tdefaultOptions.renderer = SVGRenderer;\n\t} else {\n\t\tthrow new Error('Please load either the SVG- or the CanvasRenderer');\n\t}\n\n\tvar options = {};\n\tvar currentValue = 0;\n\n\t/**\n\t * Initialize the plugin by creating the options object and initialize rendering\n\t */\n\tvar init = function() {\n\t\tthis.el = el;\n\t\tthis.options = options;\n\n\t\t// merge user options into default options\n\t\tfor (var i in defaultOptions) {\n\t\t\tif (defaultOptions.hasOwnProperty(i)) {\n\t\t\t\toptions[i] = opts && typeof(opts[i]) !== 'undefined' ? opts[i] : defaultOptions[i];\n\t\t\t\tif (typeof(options[i]) === 'function') {\n\t\t\t\t\toptions[i] = options[i].bind(this);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// check for jQuery easing\n\t\tif (typeof(options.easing) === 'string' && typeof(jQuery) !== 'undefined' && jQuery.isFunction(jQuery.easing[options.easing])) {\n\t\t\toptions.easing = jQuery.easing[options.easing];\n\t\t} else {\n\t\t\toptions.easing = defaultOptions.easing;\n\t\t}\n\n\t\t// process earlier animate option to avoid bc breaks\n\t\tif (typeof(options.animate) === 'number') {\n\t\t\toptions.animate = {\n\t\t\t\tduration: options.animate,\n\t\t\t\tenabled: true\n\t\t\t};\n\t\t}\n\n\t\tif (typeof(options.animate) === 'boolean' && !options.animate) {\n\t\t\toptions.animate = {\n\t\t\t\tduration: 1000,\n\t\t\t\tenabled: options.animate\n\t\t\t};\n\t\t}\n\n\t\t// create renderer\n\t\tthis.renderer = new options.renderer(el, options);\n\n\t\t// initial draw\n\t\tthis.renderer.draw(currentValue);\n\n\t\t// initial update\n\t\tif (el.dataset && el.dataset.percent) {\n\t\t\tthis.update(parseFloat(el.dataset.percent));\n\t\t} else if (el.getAttribute && el.getAttribute('data-percent')) {\n\t\t\tthis.update(parseFloat(el.getAttribute('data-percent')));\n\t\t}\n\t}.bind(this);\n\n\t/**\n\t * Update the value of the chart\n\t * @param {number} newValue Number between 0 and 100\n\t * @return {object} Instance of the plugin for method chaining\n\t */\n\tthis.update = function(newValue) {\n\t\tnewValue = parseFloat(newValue);\n\t\tif (options.animate.enabled) {\n\t\t\tthis.renderer.animate(currentValue, newValue);\n\t\t} else {\n\t\t\tthis.renderer.draw(newValue);\n\t\t}\n\t\tcurrentValue = newValue;\n\t\treturn this;\n\t}.bind(this);\n\n\t/**\n\t * Disable animation\n\t * @return {object} Instance of the plugin for method chaining\n\t */\n\tthis.disableAnimation = function() {\n\t\toptions.animate.enabled = false;\n\t\treturn this;\n\t};\n\n\t/**\n\t * Enable animation\n\t * @return {object} Instance of the plugin for method chaining\n\t */\n\tthis.enableAnimation = function() {\n\t\toptions.animate.enabled = true;\n\t\treturn this;\n\t};\n\n\tinit();\n};\n\nreturn EasyPieChart;\n\n}));\n","// Generated by CoffeeScript 1.10.0\n(function() {\n var AnimatedText, AnimatedTextFactory, Bar, BaseDonut, BaseGauge, Donut, Gauge, GaugePointer, TextRenderer, ValueUpdater, addCommas, cutHex, formatNumber, mergeObjects, secondsToString,\n slice = [].slice,\n hasProp = {}.hasOwnProperty,\n extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };\n\n (function() {\n var browserRequestAnimationFrame, isCancelled, j, lastId, len, vendor, vendors;\n vendors = ['ms', 'moz', 'webkit', 'o'];\n for (j = 0, len = vendors.length; j < len; j++) {\n vendor = vendors[j];\n if (window.requestAnimationFrame) {\n break;\n }\n window.requestAnimationFrame = window[vendor + 'RequestAnimationFrame'];\n window.cancelAnimationFrame = window[vendor + 'CancelAnimationFrame'] || window[vendor + 'CancelRequestAnimationFrame'];\n }\n browserRequestAnimationFrame = null;\n lastId = 0;\n isCancelled = {};\n if (!requestAnimationFrame) {\n window.requestAnimationFrame = function(callback, element) {\n var currTime, id, lastTime, timeToCall;\n currTime = new Date().getTime();\n timeToCall = Math.max(0, 16 - (currTime - lastTime));\n id = window.setTimeout(function() {\n return callback(currTime + timeToCall);\n }, timeToCall);\n lastTime = currTime + timeToCall;\n return id;\n };\n return window.cancelAnimationFrame = function(id) {\n return clearTimeout(id);\n };\n } else if (!window.cancelAnimationFrame) {\n browserRequestAnimationFrame = window.requestAnimationFrame;\n window.requestAnimationFrame = function(callback, element) {\n var myId;\n myId = ++lastId;\n browserRequestAnimationFrame(function() {\n if (!isCancelled[myId]) {\n return callback();\n }\n }, element);\n return myId;\n };\n return window.cancelAnimationFrame = function(id) {\n return isCancelled[id] = true;\n };\n }\n })();\n\n secondsToString = function(sec) {\n var hr, min;\n hr = Math.floor(sec / 3600);\n min = Math.floor((sec - (hr * 3600)) / 60);\n sec -= (hr * 3600) + (min * 60);\n sec += '';\n min += '';\n while (min.length < 2) {\n min = '0' + min;\n }\n while (sec.length < 2) {\n sec = '0' + sec;\n }\n hr = hr ? hr + ':' : '';\n return hr + min + ':' + sec;\n };\n\n formatNumber = function() {\n var digits, num, value;\n num = 1 <= arguments.length ? slice.call(arguments, 0) : [];\n value = num[0];\n digits = 0 || num[1];\n return addCommas(value.toFixed(digits));\n };\n\n mergeObjects = function(obj1, obj2) {\n var key, out, val;\n out = {};\n for (key in obj1) {\n if (!hasProp.call(obj1, key)) continue;\n val = obj1[key];\n out[key] = val;\n }\n for (key in obj2) {\n if (!hasProp.call(obj2, key)) continue;\n val = obj2[key];\n out[key] = val;\n }\n return out;\n };\n\n addCommas = function(nStr) {\n var rgx, x, x1, x2;\n nStr += '';\n x = nStr.split('.');\n x1 = x[0];\n x2 = '';\n if (x.length > 1) {\n x2 = '.' + x[1];\n }\n rgx = /(\\d+)(\\d{3})/;\n while (rgx.test(x1)) {\n x1 = x1.replace(rgx, '$1' + ',' + '$2');\n }\n return x1 + x2;\n };\n\n cutHex = function(nStr) {\n if (nStr.charAt(0) === \"#\") {\n return nStr.substring(1, 7);\n }\n return nStr;\n };\n\n ValueUpdater = (function() {\n ValueUpdater.prototype.animationSpeed = 32;\n\n function ValueUpdater(addToAnimationQueue, clear) {\n if (addToAnimationQueue == null) {\n addToAnimationQueue = true;\n }\n this.clear = clear != null ? clear : true;\n if (addToAnimationQueue) {\n AnimationUpdater.add(this);\n }\n }\n\n ValueUpdater.prototype.update = function(force) {\n var diff;\n if (force == null) {\n force = false;\n }\n if (force || this.displayedValue !== this.value) {\n if (this.ctx && this.clear) {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n }\n diff = this.value - this.displayedValue;\n if (Math.abs(diff / this.animationSpeed) <= 0.001) {\n this.displayedValue = this.value;\n } else {\n this.displayedValue = this.displayedValue + diff / this.animationSpeed;\n }\n this.render();\n return true;\n }\n return false;\n };\n\n return ValueUpdater;\n\n })();\n\n BaseGauge = (function(superClass) {\n extend(BaseGauge, superClass);\n\n function BaseGauge() {\n return BaseGauge.__super__.constructor.apply(this, arguments);\n }\n\n BaseGauge.prototype.displayScale = 1;\n\n BaseGauge.prototype.forceUpdate = true;\n\n BaseGauge.prototype.setTextField = function(textField, fractionDigits) {\n return this.textField = textField instanceof TextRenderer ? textField : new TextRenderer(textField, fractionDigits);\n };\n\n BaseGauge.prototype.setMinValue = function(minValue, updateStartValue) {\n var gauge, j, len, ref, results;\n this.minValue = minValue;\n if (updateStartValue == null) {\n updateStartValue = true;\n }\n if (updateStartValue) {\n this.displayedValue = this.minValue;\n ref = this.gp || [];\n results = [];\n for (j = 0, len = ref.length; j < len; j++) {\n gauge = ref[j];\n results.push(gauge.displayedValue = this.minValue);\n }\n return results;\n }\n };\n\n BaseGauge.prototype.setOptions = function(options) {\n if (options == null) {\n options = null;\n }\n this.options = mergeObjects(this.options, options);\n if (this.textField) {\n this.textField.el.style.fontSize = options.fontSize + 'px';\n }\n if (this.options.angle > .5) {\n this.options.angle = .5;\n }\n this.configDisplayScale();\n return this;\n };\n\n BaseGauge.prototype.configDisplayScale = function() {\n var backingStorePixelRatio, devicePixelRatio, height, prevDisplayScale, width;\n prevDisplayScale = this.displayScale;\n if (this.options.highDpiSupport === false) {\n delete this.displayScale;\n } else {\n devicePixelRatio = window.devicePixelRatio || 1;\n backingStorePixelRatio = this.ctx.webkitBackingStorePixelRatio || this.ctx.mozBackingStorePixelRatio || this.ctx.msBackingStorePixelRatio || this.ctx.oBackingStorePixelRatio || this.ctx.backingStorePixelRatio || 1;\n this.displayScale = devicePixelRatio / backingStorePixelRatio;\n }\n if (this.displayScale !== prevDisplayScale) {\n width = this.canvas.G__width || this.canvas.width;\n height = this.canvas.G__height || this.canvas.height;\n this.canvas.width = width * this.displayScale;\n this.canvas.height = height * this.displayScale;\n this.canvas.style.width = width + \"px\";\n this.canvas.style.height = height + \"px\";\n this.canvas.G__width = width;\n this.canvas.G__height = height;\n }\n return this;\n };\n\n BaseGauge.prototype.parseValue = function(value) {\n value = parseFloat(value) || Number(value);\n if (isFinite(value)) {\n return value;\n } else {\n return 0;\n }\n };\n\n return BaseGauge;\n\n })(ValueUpdater);\n\n TextRenderer = (function() {\n function TextRenderer(el, fractionDigits1) {\n this.el = el;\n this.fractionDigits = fractionDigits1;\n }\n\n TextRenderer.prototype.render = function(gauge) {\n return this.el.innerHTML = formatNumber(gauge.displayedValue, this.fractionDigits);\n };\n\n return TextRenderer;\n\n })();\n\n AnimatedText = (function(superClass) {\n extend(AnimatedText, superClass);\n\n AnimatedText.prototype.displayedValue = 0;\n\n AnimatedText.prototype.value = 0;\n\n AnimatedText.prototype.setVal = function(value) {\n return this.value = 1 * value;\n };\n\n function AnimatedText(elem1, text) {\n this.elem = elem1;\n this.text = text != null ? text : false;\n AnimatedText.__super__.constructor.call(this);\n if (this.elem === void 0) {\n throw new Error('The element isn\\'t defined.');\n }\n this.value = 1 * this.elem.innerHTML;\n if (this.text) {\n this.value = 0;\n }\n }\n\n AnimatedText.prototype.render = function() {\n var textVal;\n if (this.text) {\n textVal = secondsToString(this.displayedValue.toFixed(0));\n } else {\n textVal = addCommas(formatNumber(this.displayedValue));\n }\n return this.elem.innerHTML = textVal;\n };\n\n return AnimatedText;\n\n })(ValueUpdater);\n\n AnimatedTextFactory = {\n create: function(objList) {\n var elem, j, len, out;\n out = [];\n for (j = 0, len = objList.length; j < len; j++) {\n elem = objList[j];\n out.push(new AnimatedText(elem));\n }\n return out;\n }\n };\n\n GaugePointer = (function(superClass) {\n extend(GaugePointer, superClass);\n\n GaugePointer.prototype.displayedValue = 0;\n\n GaugePointer.prototype.value = 0;\n\n GaugePointer.prototype.options = {\n strokeWidth: 0.035,\n length: 0.1,\n color: \"#000000\",\n iconPath: null,\n iconScale: 1.0,\n iconAngle: 0\n };\n\n GaugePointer.prototype.img = null;\n\n function GaugePointer(gauge1) {\n this.gauge = gauge1;\n if (this.gauge === void 0) {\n throw new Error('The element isn\\'t defined.');\n }\n this.ctx = this.gauge.ctx;\n this.canvas = this.gauge.canvas;\n GaugePointer.__super__.constructor.call(this, false, false);\n this.setOptions();\n }\n\n GaugePointer.prototype.setOptions = function(options) {\n if (options == null) {\n options = null;\n }\n this.options = mergeObjects(this.options, options);\n this.length = 2 * this.gauge.radius * this.gauge.options.radiusScale * this.options.length;\n this.strokeWidth = this.canvas.height * this.options.strokeWidth;\n this.maxValue = this.gauge.maxValue;\n this.minValue = this.gauge.minValue;\n this.animationSpeed = this.gauge.animationSpeed;\n this.options.angle = this.gauge.options.angle;\n if (this.options.iconPath) {\n this.img = new Image();\n return this.img.src = this.options.iconPath;\n }\n };\n\n GaugePointer.prototype.render = function() {\n var angle, endX, endY, imgX, imgY, startX, startY, x, y;\n angle = this.gauge.getAngle.call(this, this.displayedValue);\n x = Math.round(this.length * Math.cos(angle));\n y = Math.round(this.length * Math.sin(angle));\n startX = Math.round(this.strokeWidth * Math.cos(angle - Math.PI / 2));\n startY = Math.round(this.strokeWidth * Math.sin(angle - Math.PI / 2));\n endX = Math.round(this.strokeWidth * Math.cos(angle + Math.PI / 2));\n endY = Math.round(this.strokeWidth * Math.sin(angle + Math.PI / 2));\n this.ctx.beginPath();\n this.ctx.fillStyle = this.options.color;\n this.ctx.arc(0, 0, this.strokeWidth, 0, Math.PI * 2, false);\n this.ctx.fill();\n this.ctx.beginPath();\n this.ctx.moveTo(startX, startY);\n this.ctx.lineTo(x, y);\n this.ctx.lineTo(endX, endY);\n this.ctx.fill();\n if (this.img) {\n imgX = Math.round(this.img.width * this.options.iconScale);\n imgY = Math.round(this.img.height * this.options.iconScale);\n this.ctx.save();\n this.ctx.translate(x, y);\n this.ctx.rotate(angle + Math.PI / 180.0 * (90 + this.options.iconAngle));\n this.ctx.drawImage(this.img, -imgX / 2, -imgY / 2, imgX, imgY);\n return this.ctx.restore();\n }\n };\n\n return GaugePointer;\n\n })(ValueUpdater);\n\n Bar = (function() {\n function Bar(elem1) {\n this.elem = elem1;\n }\n\n Bar.prototype.updateValues = function(arrValues) {\n this.value = arrValues[0];\n this.maxValue = arrValues[1];\n this.avgValue = arrValues[2];\n return this.render();\n };\n\n Bar.prototype.render = function() {\n var avgPercent, valPercent;\n if (this.textField) {\n this.textField.text(formatNumber(this.value));\n }\n if (this.maxValue === 0) {\n this.maxValue = this.avgValue * 2;\n }\n valPercent = (this.value / this.maxValue) * 100;\n avgPercent = (this.avgValue / this.maxValue) * 100;\n $(\".bar-value\", this.elem).css({\n \"width\": valPercent + \"%\"\n });\n return $(\".typical-value\", this.elem).css({\n \"width\": avgPercent + \"%\"\n });\n };\n\n return Bar;\n\n })();\n\n Gauge = (function(superClass) {\n extend(Gauge, superClass);\n\n Gauge.prototype.elem = null;\n\n Gauge.prototype.value = [20];\n\n Gauge.prototype.maxValue = 80;\n\n Gauge.prototype.minValue = 0;\n\n Gauge.prototype.displayedAngle = 0;\n\n Gauge.prototype.displayedValue = 0;\n\n Gauge.prototype.lineWidth = 40;\n\n Gauge.prototype.paddingTop = 0.1;\n\n Gauge.prototype.paddingBottom = 0.1;\n\n Gauge.prototype.percentColors = null;\n\n Gauge.prototype.options = {\n colorStart: \"#6fadcf\",\n colorStop: void 0,\n gradientType: 0,\n strokeColor: \"#e0e0e0\",\n pointer: {\n length: 0.8,\n strokeWidth: 0.035,\n iconScale: 1.0\n },\n angle: 0.15,\n lineWidth: 0.44,\n radiusScale: 1.0,\n fontSize: 40,\n limitMax: false,\n limitMin: false\n };\n\n function Gauge(canvas) {\n var h, w;\n this.canvas = canvas;\n Gauge.__super__.constructor.call(this);\n this.percentColors = null;\n if (typeof G_vmlCanvasManager !== 'undefined') {\n this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);\n }\n this.ctx = this.canvas.getContext('2d');\n h = this.canvas.clientHeight;\n w = this.canvas.clientWidth;\n this.canvas.height = h;\n this.canvas.width = w;\n this.gp = [new GaugePointer(this)];\n this.setOptions();\n }\n\n Gauge.prototype.setOptions = function(options) {\n var gauge, j, len, phi, ref;\n if (options == null) {\n options = null;\n }\n Gauge.__super__.setOptions.call(this, options);\n this.configPercentColors();\n this.extraPadding = 0;\n if (this.options.angle < 0) {\n phi = Math.PI * (1 + this.options.angle);\n this.extraPadding = Math.sin(phi);\n }\n this.availableHeight = this.canvas.height * (1 - this.paddingTop - this.paddingBottom);\n this.lineWidth = this.availableHeight * this.options.lineWidth;\n this.radius = (this.availableHeight - this.lineWidth / 2) / (1.0 + this.extraPadding);\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n ref = this.gp;\n for (j = 0, len = ref.length; j < len; j++) {\n gauge = ref[j];\n gauge.setOptions(this.options.pointer);\n gauge.render();\n }\n this.render();\n return this;\n };\n\n Gauge.prototype.configPercentColors = function() {\n var bval, gval, i, j, ref, results, rval;\n this.percentColors = null;\n if (this.options.percentColors !== void 0) {\n this.percentColors = new Array();\n results = [];\n for (i = j = 0, ref = this.options.percentColors.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {\n rval = parseInt((cutHex(this.options.percentColors[i][1])).substring(0, 2), 16);\n gval = parseInt((cutHex(this.options.percentColors[i][1])).substring(2, 4), 16);\n bval = parseInt((cutHex(this.options.percentColors[i][1])).substring(4, 6), 16);\n results.push(this.percentColors[i] = {\n pct: this.options.percentColors[i][0],\n color: {\n r: rval,\n g: gval,\n b: bval\n }\n });\n }\n return results;\n }\n };\n\n Gauge.prototype.set = function(value) {\n var gp, i, j, k, l, len, ref, ref1, val;\n if (!(value instanceof Array)) {\n value = [value];\n }\n for (i = j = 0, ref = value.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {\n value[i] = this.parseValue(value[i]);\n }\n if (value.length > this.gp.length) {\n for (i = k = 0, ref1 = value.length - this.gp.length; 0 <= ref1 ? k < ref1 : k > ref1; i = 0 <= ref1 ? ++k : --k) {\n gp = new GaugePointer(this);\n gp.setOptions(this.options.pointer);\n this.gp.push(gp);\n }\n } else if (value.length < this.gp.length) {\n this.gp = this.gp.slice(this.gp.length - value.length);\n }\n i = 0;\n for (l = 0, len = value.length; l < len; l++) {\n val = value[l];\n if (val > this.maxValue) {\n if (this.options.limitMax) {\n val = this.maxValue;\n } else {\n this.maxValue = val + 1;\n }\n } else if (val < this.minValue) {\n if (this.options.limitMin) {\n val = this.minValue;\n } else {\n this.minValue = val - 1;\n }\n }\n this.gp[i].value = val;\n this.gp[i++].setOptions({\n minValue: this.minValue,\n maxValue: this.maxValue,\n angle: this.options.angle\n });\n }\n this.value = Math.max(Math.min(value[value.length - 1], this.maxValue), this.minValue);\n AnimationUpdater.run(this.forceUpdate);\n return this.forceUpdate = false;\n };\n\n Gauge.prototype.getAngle = function(value) {\n return (1 + this.options.angle) * Math.PI + ((value - this.minValue) / (this.maxValue - this.minValue)) * (1 - this.options.angle * 2) * Math.PI;\n };\n\n Gauge.prototype.getColorForPercentage = function(pct, grad) {\n var color, endColor, i, j, rangePct, ref, startColor;\n if (pct === 0) {\n color = this.percentColors[0].color;\n } else {\n color = this.percentColors[this.percentColors.length - 1].color;\n for (i = j = 0, ref = this.percentColors.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {\n if (pct <= this.percentColors[i].pct) {\n if (grad === true) {\n startColor = this.percentColors[i - 1] || this.percentColors[0];\n endColor = this.percentColors[i];\n rangePct = (pct - startColor.pct) / (endColor.pct - startColor.pct);\n color = {\n r: Math.floor(startColor.color.r * (1 - rangePct) + endColor.color.r * rangePct),\n g: Math.floor(startColor.color.g * (1 - rangePct) + endColor.color.g * rangePct),\n b: Math.floor(startColor.color.b * (1 - rangePct) + endColor.color.b * rangePct)\n };\n } else {\n color = this.percentColors[i].color;\n }\n break;\n }\n }\n }\n return 'rgb(' + [color.r, color.g, color.b].join(',') + ')';\n };\n\n Gauge.prototype.getColorForValue = function(val, grad) {\n var pct;\n pct = (val - this.minValue) / (this.maxValue - this.minValue);\n return this.getColorForPercentage(pct, grad);\n };\n\n Gauge.prototype.renderStaticLabels = function(staticLabels, w, h, radius) {\n var font, fontsize, j, len, match, re, ref, rest, rotationAngle, value;\n this.ctx.save();\n this.ctx.translate(w, h);\n font = staticLabels.font || \"10px Times\";\n re = /\\d+\\.?\\d?/;\n match = font.match(re)[0];\n rest = font.slice(match.length);\n fontsize = parseFloat(match) * this.displayScale;\n this.ctx.font = fontsize + rest;\n this.ctx.fillStyle = staticLabels.color || \"#000000\";\n this.ctx.textBaseline = \"bottom\";\n this.ctx.textAlign = \"center\";\n ref = staticLabels.labels;\n for (j = 0, len = ref.length; j < len; j++) {\n value = ref[j];\n if (value.label !== void 0) {\n if ((!this.options.limitMin || value >= this.minValue) && (!this.options.limitMax || value <= this.maxValue)) {\n font = value.font || staticLabels.font;\n match = font.match(re)[0];\n rest = font.slice(match.length);\n fontsize = parseFloat(match) * this.displayScale;\n this.ctx.font = fontsize + rest;\n rotationAngle = this.getAngle(value.label) - 3 * Math.PI / 2;\n this.ctx.rotate(rotationAngle);\n this.ctx.fillText(formatNumber(value.label, staticLabels.fractionDigits), 0, -radius - this.lineWidth / 2);\n this.ctx.rotate(-rotationAngle);\n }\n } else {\n if ((!this.options.limitMin || value >= this.minValue) && (!this.options.limitMax || value <= this.maxValue)) {\n rotationAngle = this.getAngle(value) - 3 * Math.PI / 2;\n this.ctx.rotate(rotationAngle);\n this.ctx.fillText(formatNumber(value, staticLabels.fractionDigits), 0, -radius - this.lineWidth / 2);\n this.ctx.rotate(-rotationAngle);\n }\n }\n }\n return this.ctx.restore();\n };\n\n Gauge.prototype.renderTicks = function(ticksOptions, w, h, radius) {\n var currentDivision, currentSubDivision, divColor, divLength, divWidth, divisionCount, j, lineWidth, range, rangeDivisions, ref, results, scaleMutate, st, subColor, subDivisions, subLength, subWidth, subdivisionCount, t, tmpRadius;\n if (ticksOptions !== {}) {\n divisionCount = ticksOptions.divisions || 0;\n subdivisionCount = ticksOptions.subDivisions || 0;\n divColor = ticksOptions.divColor || '#fff';\n subColor = ticksOptions.subColor || '#fff';\n divLength = ticksOptions.divLength || 0.7;\n subLength = ticksOptions.subLength || 0.2;\n range = parseFloat(this.maxValue) - parseFloat(this.minValue);\n rangeDivisions = parseFloat(range) / parseFloat(ticksOptions.divisions);\n subDivisions = parseFloat(rangeDivisions) / parseFloat(ticksOptions.subDivisions);\n currentDivision = parseFloat(this.minValue);\n currentSubDivision = 0.0 + subDivisions;\n lineWidth = range / 400;\n divWidth = lineWidth * (ticksOptions.divWidth || 1);\n subWidth = lineWidth * (ticksOptions.subWidth || 1);\n results = [];\n for (t = j = 0, ref = divisionCount + 1; j < ref; t = j += 1) {\n this.ctx.lineWidth = this.lineWidth * divLength;\n scaleMutate = (this.lineWidth / 2) * (1 - divLength);\n tmpRadius = (this.radius * this.options.radiusScale) + scaleMutate;\n this.ctx.strokeStyle = divColor;\n this.ctx.beginPath();\n this.ctx.arc(0, 0, tmpRadius, this.getAngle(currentDivision - divWidth), this.getAngle(currentDivision + divWidth), false);\n this.ctx.stroke();\n currentSubDivision = currentDivision + subDivisions;\n currentDivision += rangeDivisions;\n if (t !== ticksOptions.divisions && subdivisionCount > 0) {\n results.push((function() {\n var k, ref1, results1;\n results1 = [];\n for (st = k = 0, ref1 = subdivisionCount - 1; k < ref1; st = k += 1) {\n this.ctx.lineWidth = this.lineWidth * subLength;\n scaleMutate = (this.lineWidth / 2) * (1 - subLength);\n tmpRadius = (this.radius * this.options.radiusScale) + scaleMutate;\n this.ctx.strokeStyle = subColor;\n this.ctx.beginPath();\n this.ctx.arc(0, 0, tmpRadius, this.getAngle(currentSubDivision - subWidth), this.getAngle(currentSubDivision + subWidth), false);\n this.ctx.stroke();\n results1.push(currentSubDivision += subDivisions);\n }\n return results1;\n }).call(this));\n } else {\n results.push(void 0);\n }\n }\n return results;\n }\n };\n\n Gauge.prototype.render = function() {\n var displayedAngle, fillStyle, gauge, h, j, k, len, len1, max, min, radius, ref, ref1, scaleMutate, tmpRadius, w, zone;\n w = this.canvas.width / 2;\n h = (this.canvas.height * this.paddingTop + this.availableHeight) - ((this.radius + this.lineWidth / 2) * this.extraPadding);\n displayedAngle = this.getAngle(this.displayedValue);\n if (this.textField) {\n this.textField.render(this);\n }\n this.ctx.lineCap = \"butt\";\n radius = this.radius * this.options.radiusScale;\n if (this.options.staticLabels) {\n this.renderStaticLabels(this.options.staticLabels, w, h, radius);\n }\n if (this.options.staticZones) {\n this.ctx.save();\n this.ctx.translate(w, h);\n this.ctx.lineWidth = this.lineWidth;\n ref = this.options.staticZones;\n for (j = 0, len = ref.length; j < len; j++) {\n zone = ref[j];\n min = zone.min;\n if (this.options.limitMin && min < this.minValue) {\n min = this.minValue;\n }\n max = zone.max;\n if (this.options.limitMax && max > this.maxValue) {\n max = this.maxValue;\n }\n tmpRadius = this.radius * this.options.radiusScale;\n if (zone.height) {\n this.ctx.lineWidth = this.lineWidth * zone.height;\n scaleMutate = (this.lineWidth / 2) * (zone.offset || 1 - zone.height);\n tmpRadius = (this.radius * this.options.radiusScale) + scaleMutate;\n }\n this.ctx.strokeStyle = zone.strokeStyle;\n this.ctx.beginPath();\n this.ctx.arc(0, 0, tmpRadius, this.getAngle(min), this.getAngle(max), false);\n this.ctx.stroke();\n }\n } else {\n if (this.options.customFillStyle !== void 0) {\n fillStyle = this.options.customFillStyle(this);\n } else if (this.percentColors !== null) {\n fillStyle = this.getColorForValue(this.displayedValue, this.options.generateGradient);\n } else if (this.options.colorStop !== void 0) {\n if (this.options.gradientType === 0) {\n fillStyle = this.ctx.createRadialGradient(w, h, 9, w, h, 70);\n } else {\n fillStyle = this.ctx.createLinearGradient(0, 0, w, 0);\n }\n fillStyle.addColorStop(0, this.options.colorStart);\n fillStyle.addColorStop(1, this.options.colorStop);\n } else {\n fillStyle = this.options.colorStart;\n }\n this.ctx.strokeStyle = fillStyle;\n this.ctx.beginPath();\n this.ctx.arc(w, h, radius, (1 + this.options.angle) * Math.PI, displayedAngle, false);\n this.ctx.lineWidth = this.lineWidth;\n this.ctx.stroke();\n this.ctx.strokeStyle = this.options.strokeColor;\n this.ctx.beginPath();\n this.ctx.arc(w, h, radius, displayedAngle, (2 - this.options.angle) * Math.PI, false);\n this.ctx.stroke();\n this.ctx.save();\n this.ctx.translate(w, h);\n }\n if (this.options.renderTicks) {\n this.renderTicks(this.options.renderTicks, w, h, radius);\n }\n this.ctx.restore();\n this.ctx.translate(w, h);\n ref1 = this.gp;\n for (k = 0, len1 = ref1.length; k < len1; k++) {\n gauge = ref1[k];\n gauge.update(true);\n }\n return this.ctx.translate(-w, -h);\n };\n\n return Gauge;\n\n })(BaseGauge);\n\n BaseDonut = (function(superClass) {\n extend(BaseDonut, superClass);\n\n BaseDonut.prototype.lineWidth = 15;\n\n BaseDonut.prototype.displayedValue = 0;\n\n BaseDonut.prototype.value = 33;\n\n BaseDonut.prototype.maxValue = 80;\n\n BaseDonut.prototype.minValue = 0;\n\n BaseDonut.prototype.options = {\n lineWidth: 0.10,\n colorStart: \"#6f6ea0\",\n colorStop: \"#c0c0db\",\n strokeColor: \"#eeeeee\",\n shadowColor: \"#d5d5d5\",\n angle: 0.35,\n radiusScale: 1.0\n };\n\n function BaseDonut(canvas) {\n this.canvas = canvas;\n BaseDonut.__super__.constructor.call(this);\n if (typeof G_vmlCanvasManager !== 'undefined') {\n this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);\n }\n this.ctx = this.canvas.getContext('2d');\n this.setOptions();\n this.render();\n }\n\n BaseDonut.prototype.getAngle = function(value) {\n return (1 - this.options.angle) * Math.PI + ((value - this.minValue) / (this.maxValue - this.minValue)) * ((2 + this.options.angle) - (1 - this.options.angle)) * Math.PI;\n };\n\n BaseDonut.prototype.setOptions = function(options) {\n if (options == null) {\n options = null;\n }\n BaseDonut.__super__.setOptions.call(this, options);\n this.lineWidth = this.canvas.height * this.options.lineWidth;\n this.radius = this.options.radiusScale * (this.canvas.height / 2 - this.lineWidth / 2);\n return this;\n };\n\n BaseDonut.prototype.set = function(value) {\n this.value = this.parseValue(value);\n if (this.value > this.maxValue) {\n if (this.options.limitMax) {\n this.value = this.maxValue;\n } else {\n this.maxValue = this.value;\n }\n } else if (this.value < this.minValue) {\n if (this.options.limitMin) {\n this.value = this.minValue;\n } else {\n this.minValue = this.value;\n }\n }\n AnimationUpdater.run(this.forceUpdate);\n return this.forceUpdate = false;\n };\n\n BaseDonut.prototype.render = function() {\n var displayedAngle, grdFill, h, start, stop, w;\n displayedAngle = this.getAngle(this.displayedValue);\n w = this.canvas.width / 2;\n h = this.canvas.height / 2;\n if (this.textField) {\n this.textField.render(this);\n }\n grdFill = this.ctx.createRadialGradient(w, h, 39, w, h, 70);\n grdFill.addColorStop(0, this.options.colorStart);\n grdFill.addColorStop(1, this.options.colorStop);\n start = this.radius - this.lineWidth / 2;\n stop = this.radius + this.lineWidth / 2;\n this.ctx.strokeStyle = this.options.strokeColor;\n this.ctx.beginPath();\n this.ctx.arc(w, h, this.radius, (1 - this.options.angle) * Math.PI, (2 + this.options.angle) * Math.PI, false);\n this.ctx.lineWidth = this.lineWidth;\n this.ctx.lineCap = \"round\";\n this.ctx.stroke();\n this.ctx.strokeStyle = grdFill;\n this.ctx.beginPath();\n this.ctx.arc(w, h, this.radius, (1 - this.options.angle) * Math.PI, displayedAngle, false);\n return this.ctx.stroke();\n };\n\n return BaseDonut;\n\n })(BaseGauge);\n\n Donut = (function(superClass) {\n extend(Donut, superClass);\n\n function Donut() {\n return Donut.__super__.constructor.apply(this, arguments);\n }\n\n Donut.prototype.strokeGradient = function(w, h, start, stop) {\n var grd;\n grd = this.ctx.createRadialGradient(w, h, start, w, h, stop);\n grd.addColorStop(0, this.options.shadowColor);\n grd.addColorStop(0.12, this.options._orgStrokeColor);\n grd.addColorStop(0.88, this.options._orgStrokeColor);\n grd.addColorStop(1, this.options.shadowColor);\n return grd;\n };\n\n Donut.prototype.setOptions = function(options) {\n var h, start, stop, w;\n if (options == null) {\n options = null;\n }\n Donut.__super__.setOptions.call(this, options);\n w = this.canvas.width / 2;\n h = this.canvas.height / 2;\n start = this.radius - this.lineWidth / 2;\n stop = this.radius + this.lineWidth / 2;\n this.options._orgStrokeColor = this.options.strokeColor;\n this.options.strokeColor = this.strokeGradient(w, h, start, stop);\n return this;\n };\n\n return Donut;\n\n })(BaseDonut);\n\n window.AnimationUpdater = {\n elements: [],\n animId: null,\n addAll: function(list) {\n var elem, j, len, results;\n results = [];\n for (j = 0, len = list.length; j < len; j++) {\n elem = list[j];\n results.push(AnimationUpdater.elements.push(elem));\n }\n return results;\n },\n add: function(object) {\n return AnimationUpdater.elements.push(object);\n },\n run: function(force) {\n var elem, finished, isCallback, j, len, ref;\n if (force == null) {\n force = false;\n }\n isCallback = isFinite(parseFloat(force));\n if (isCallback || force === true) {\n finished = true;\n ref = AnimationUpdater.elements;\n for (j = 0, len = ref.length; j < len; j++) {\n elem = ref[j];\n if (elem.update(force === true)) {\n finished = false;\n }\n }\n return AnimationUpdater.animId = finished ? null : requestAnimationFrame(AnimationUpdater.run);\n } else if (force === false) {\n if (AnimationUpdater.animId === !null) {\n cancelAnimationFrame(AnimationUpdater.animId);\n }\n return AnimationUpdater.animId = requestAnimationFrame(AnimationUpdater.run);\n }\n }\n };\n\n if (typeof window.define === 'function' && (window.define.amd != null)) {\n define(function() {\n return {\n Gauge: Gauge,\n Donut: Donut,\n BaseDonut: BaseDonut,\n TextRenderer: TextRenderer\n };\n });\n } else if (typeof module !== 'undefined' && (module.exports != null)) {\n module.exports = {\n Gauge: Gauge,\n Donut: Donut,\n BaseDonut: BaseDonut,\n TextRenderer: TextRenderer\n };\n } else {\n window.Gauge = Gauge;\n window.Donut = Donut;\n window.BaseDonut = BaseDonut;\n window.TextRenderer = TextRenderer;\n }\n\n}).call(this);\n","var tabbable = require('tabbable');\nvar xtend = require('xtend');\n\nvar activeFocusDelay;\n\nvar activeFocusTraps = (function() {\n var trapQueue = [];\n return {\n activateTrap: function(trap) {\n if (trapQueue.length > 0) {\n var activeTrap = trapQueue[trapQueue.length - 1];\n if (activeTrap !== trap) {\n activeTrap.pause();\n }\n }\n\n var trapIndex = trapQueue.indexOf(trap);\n if (trapIndex === -1) {\n trapQueue.push(trap);\n } else {\n // move this existing trap to the front of the queue\n trapQueue.splice(trapIndex, 1);\n trapQueue.push(trap);\n }\n },\n\n deactivateTrap: function(trap) {\n var trapIndex = trapQueue.indexOf(trap);\n if (trapIndex !== -1) {\n trapQueue.splice(trapIndex, 1);\n }\n\n if (trapQueue.length > 0) {\n trapQueue[trapQueue.length - 1].unpause();\n }\n }\n };\n})();\n\nfunction focusTrap(element, userOptions) {\n var doc = document;\n var container =\n typeof element === 'string' ? doc.querySelector(element) : element;\n\n var config = xtend(\n {\n returnFocusOnDeactivate: true,\n escapeDeactivates: true\n },\n userOptions\n );\n\n var state = {\n firstTabbableNode: null,\n lastTabbableNode: null,\n nodeFocusedBeforeActivation: null,\n mostRecentlyFocusedNode: null,\n active: false,\n paused: false\n };\n\n var trap = {\n activate: activate,\n deactivate: deactivate,\n pause: pause,\n unpause: unpause\n };\n\n return trap;\n\n function activate(activateOptions) {\n if (state.active) return;\n\n updateTabbableNodes();\n\n state.active = true;\n state.paused = false;\n state.nodeFocusedBeforeActivation = doc.activeElement;\n\n var onActivate =\n activateOptions && activateOptions.onActivate\n ? activateOptions.onActivate\n : config.onActivate;\n if (onActivate) {\n onActivate();\n }\n\n addListeners();\n return trap;\n }\n\n function deactivate(deactivateOptions) {\n if (!state.active) return;\n\n clearTimeout(activeFocusDelay);\n\n removeListeners();\n state.active = false;\n state.paused = false;\n\n activeFocusTraps.deactivateTrap(trap);\n\n var onDeactivate =\n deactivateOptions && deactivateOptions.onDeactivate !== undefined\n ? deactivateOptions.onDeactivate\n : config.onDeactivate;\n if (onDeactivate) {\n onDeactivate();\n }\n\n var returnFocus =\n deactivateOptions && deactivateOptions.returnFocus !== undefined\n ? deactivateOptions.returnFocus\n : config.returnFocusOnDeactivate;\n if (returnFocus) {\n delay(function() {\n tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));\n });\n }\n\n return trap;\n }\n\n function pause() {\n if (state.paused || !state.active) return;\n state.paused = true;\n removeListeners();\n }\n\n function unpause() {\n if (!state.paused || !state.active) return;\n state.paused = false;\n updateTabbableNodes();\n addListeners();\n }\n\n function addListeners() {\n if (!state.active) return;\n\n // There can be only one listening focus trap at a time\n activeFocusTraps.activateTrap(trap);\n\n // Delay ensures that the focused element doesn't capture the event\n // that caused the focus trap activation.\n activeFocusDelay = delay(function() {\n tryFocus(getInitialFocusNode());\n });\n\n doc.addEventListener('focusin', checkFocusIn, true);\n doc.addEventListener('mousedown', checkPointerDown, {\n capture: true,\n passive: false\n });\n doc.addEventListener('touchstart', checkPointerDown, {\n capture: true,\n passive: false\n });\n doc.addEventListener('click', checkClick, {\n capture: true,\n passive: false\n });\n doc.addEventListener('keydown', checkKey, {\n capture: true,\n passive: false\n });\n\n return trap;\n }\n\n function removeListeners() {\n if (!state.active) return;\n\n doc.removeEventListener('focusin', checkFocusIn, true);\n doc.removeEventListener('mousedown', checkPointerDown, true);\n doc.removeEventListener('touchstart', checkPointerDown, true);\n doc.removeEventListener('click', checkClick, true);\n doc.removeEventListener('keydown', checkKey, true);\n\n return trap;\n }\n\n function getNodeForOption(optionName) {\n var optionValue = config[optionName];\n var node = optionValue;\n if (!optionValue) {\n return null;\n }\n if (typeof optionValue === 'string') {\n node = doc.querySelector(optionValue);\n if (!node) {\n throw new Error('`' + optionName + '` refers to no known node');\n }\n }\n if (typeof optionValue === 'function') {\n node = optionValue();\n if (!node) {\n throw new Error('`' + optionName + '` did not return a node');\n }\n }\n return node;\n }\n\n function getInitialFocusNode() {\n var node;\n if (getNodeForOption('initialFocus') !== null) {\n node = getNodeForOption('initialFocus');\n } else if (container.contains(doc.activeElement)) {\n node = doc.activeElement;\n } else {\n node = state.firstTabbableNode || getNodeForOption('fallbackFocus');\n }\n\n if (!node) {\n throw new Error(\n 'Your focus-trap needs to have at least one focusable element'\n );\n }\n\n return node;\n }\n\n function getReturnFocusNode(previousActiveElement) {\n var node = getNodeForOption('setReturnFocus');\n return node ? node : previousActiveElement;\n }\n\n // This needs to be done on mousedown and touchstart instead of click\n // so that it precedes the focus event.\n function checkPointerDown(e) {\n if (container.contains(e.target)) return;\n if (config.clickOutsideDeactivates) {\n deactivate({\n returnFocus: !tabbable.isFocusable(e.target)\n });\n return;\n }\n // This is needed for mobile devices.\n // (If we'll only let `click` events through,\n // then on mobile they will be blocked anyways if `touchstart` is blocked.)\n if (config.allowOutsideClick && config.allowOutsideClick(e)) {\n return;\n }\n e.preventDefault();\n }\n\n // In case focus escapes the trap for some strange reason, pull it back in.\n function checkFocusIn(e) {\n // In Firefox when you Tab out of an iframe the Document is briefly focused.\n if (container.contains(e.target) || e.target instanceof Document) {\n return;\n }\n e.stopImmediatePropagation();\n tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());\n }\n\n function checkKey(e) {\n if (config.escapeDeactivates !== false && isEscapeEvent(e)) {\n e.preventDefault();\n deactivate();\n return;\n }\n if (isTabEvent(e)) {\n checkTab(e);\n return;\n }\n }\n\n // Hijack Tab events on the first and last focusable nodes of the trap,\n // in order to prevent focus from escaping. If it escapes for even a\n // moment it can end up scrolling the page and causing confusion so we\n // kind of need to capture the action at the keydown phase.\n function checkTab(e) {\n updateTabbableNodes();\n if (e.shiftKey && e.target === state.firstTabbableNode) {\n e.preventDefault();\n tryFocus(state.lastTabbableNode);\n return;\n }\n if (!e.shiftKey && e.target === state.lastTabbableNode) {\n e.preventDefault();\n tryFocus(state.firstTabbableNode);\n return;\n }\n }\n\n function checkClick(e) {\n if (config.clickOutsideDeactivates) return;\n if (container.contains(e.target)) return;\n if (config.allowOutsideClick && config.allowOutsideClick(e)) {\n return;\n }\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n\n function updateTabbableNodes() {\n var tabbableNodes = tabbable(container);\n state.firstTabbableNode = tabbableNodes[0] || getInitialFocusNode();\n state.lastTabbableNode =\n tabbableNodes[tabbableNodes.length - 1] || getInitialFocusNode();\n }\n\n function tryFocus(node) {\n if (node === doc.activeElement) return;\n if (!node || !node.focus) {\n tryFocus(getInitialFocusNode());\n return;\n }\n node.focus();\n state.mostRecentlyFocusedNode = node;\n if (isSelectableInput(node)) {\n node.select();\n }\n }\n}\n\nfunction isSelectableInput(node) {\n return (\n node.tagName &&\n node.tagName.toLowerCase() === 'input' &&\n typeof node.select === 'function'\n );\n}\n\nfunction isEscapeEvent(e) {\n return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27;\n}\n\nfunction isTabEvent(e) {\n return e.key === 'Tab' || e.keyCode === 9;\n}\n\nfunction delay(fn) {\n return setTimeout(fn, 0);\n}\n\nmodule.exports = focusTrap;\n","import toDate from \"../toDate/index.js\";\nimport requiredArgs from \"../_lib/requiredArgs/index.js\";\n/**\n * @name getTime\n * @category Timestamp Helpers\n * @summary Get the milliseconds timestamp of the given date.\n *\n * @description\n * Get the milliseconds timestamp of the given date.\n *\n * ### v2.0.0 breaking changes:\n *\n * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).\n *\n * @param {Date|Number} date - the given date\n * @returns {Number} the timestamp\n * @throws {TypeError} 1 argument required\n *\n * @example\n * // Get the timestamp of 29 February 2012 11:45:05.123:\n * const result = getTime(new Date(2012, 1, 29, 11, 45, 5, 123))\n * //=> 1330515905123\n */\n\nexport default function getTime(dirtyDate) {\n requiredArgs(1, arguments);\n var date = toDate(dirtyDate);\n var timestamp = date.getTime();\n return timestamp;\n}","!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?t(exports,require(\"react\"),require(\"prop-types\"),require(\"classnames\"),require(\"date-fns/isDate\"),require(\"date-fns/isValid\"),require(\"date-fns/format\"),require(\"date-fns/addMinutes\"),require(\"date-fns/addHours\"),require(\"date-fns/addDays\"),require(\"date-fns/addWeeks\"),require(\"date-fns/addMonths\"),require(\"date-fns/addYears\"),require(\"date-fns/subMinutes\"),require(\"date-fns/subHours\"),require(\"date-fns/subDays\"),require(\"date-fns/subWeeks\"),require(\"date-fns/subMonths\"),require(\"date-fns/subYears\"),require(\"date-fns/getSeconds\"),require(\"date-fns/getMinutes\"),require(\"date-fns/getHours\"),require(\"date-fns/getDay\"),require(\"date-fns/getDate\"),require(\"date-fns/getISOWeek\"),require(\"date-fns/getMonth\"),require(\"date-fns/getQuarter\"),require(\"date-fns/getYear\"),require(\"date-fns/getTime\"),require(\"date-fns/setSeconds\"),require(\"date-fns/setMinutes\"),require(\"date-fns/setHours\"),require(\"date-fns/setMonth\"),require(\"date-fns/setQuarter\"),require(\"date-fns/setYear\"),require(\"date-fns/min\"),require(\"date-fns/max\"),require(\"date-fns/differenceInCalendarDays\"),require(\"date-fns/differenceInCalendarMonths\"),require(\"date-fns/differenceInCalendarWeeks\"),require(\"date-fns/differenceInCalendarYears\"),require(\"date-fns/startOfDay\"),require(\"date-fns/startOfWeek\"),require(\"date-fns/startOfMonth\"),require(\"date-fns/startOfQuarter\"),require(\"date-fns/startOfYear\"),require(\"date-fns/endOfDay\"),require(\"date-fns/endOfWeek\"),require(\"date-fns/endOfMonth\"),require(\"date-fns/isEqual\"),require(\"date-fns/isSameDay\"),require(\"date-fns/isSameMonth\"),require(\"date-fns/isSameYear\"),require(\"date-fns/isSameQuarter\"),require(\"date-fns/isAfter\"),require(\"date-fns/isBefore\"),require(\"date-fns/isWithinInterval\"),require(\"date-fns/toDate\"),require(\"date-fns/parse\"),require(\"date-fns/parseISO\"),require(\"react-onclickoutside\"),require(\"react-dom\"),require(\"react-popper\")):\"function\"==typeof define&&define.amd?define([\"exports\",\"react\",\"prop-types\",\"classnames\",\"date-fns/isDate\",\"date-fns/isValid\",\"date-fns/format\",\"date-fns/addMinutes\",\"date-fns/addHours\",\"date-fns/addDays\",\"date-fns/addWeeks\",\"date-fns/addMonths\",\"date-fns/addYears\",\"date-fns/subMinutes\",\"date-fns/subHours\",\"date-fns/subDays\",\"date-fns/subWeeks\",\"date-fns/subMonths\",\"date-fns/subYears\",\"date-fns/getSeconds\",\"date-fns/getMinutes\",\"date-fns/getHours\",\"date-fns/getDay\",\"date-fns/getDate\",\"date-fns/getISOWeek\",\"date-fns/getMonth\",\"date-fns/getQuarter\",\"date-fns/getYear\",\"date-fns/getTime\",\"date-fns/setSeconds\",\"date-fns/setMinutes\",\"date-fns/setHours\",\"date-fns/setMonth\",\"date-fns/setQuarter\",\"date-fns/setYear\",\"date-fns/min\",\"date-fns/max\",\"date-fns/differenceInCalendarDays\",\"date-fns/differenceInCalendarMonths\",\"date-fns/differenceInCalendarWeeks\",\"date-fns/differenceInCalendarYears\",\"date-fns/startOfDay\",\"date-fns/startOfWeek\",\"date-fns/startOfMonth\",\"date-fns/startOfQuarter\",\"date-fns/startOfYear\",\"date-fns/endOfDay\",\"date-fns/endOfWeek\",\"date-fns/endOfMonth\",\"date-fns/isEqual\",\"date-fns/isSameDay\",\"date-fns/isSameMonth\",\"date-fns/isSameYear\",\"date-fns/isSameQuarter\",\"date-fns/isAfter\",\"date-fns/isBefore\",\"date-fns/isWithinInterval\",\"date-fns/toDate\",\"date-fns/parse\",\"date-fns/parseISO\",\"react-onclickoutside\",\"react-dom\",\"react-popper\"],t):t((e=\"undefined\"!=typeof globalThis?globalThis:e||self).DatePicker={},e.React,e.PropTypes,e.classNames,e.isDate,e.isValidDate,e.format,e.addMinutes,e.addHours,e.addDays,e.addWeeks,e.addMonths,e.addYears,null,null,e.subDays,e.subWeeks,e.subMonths,e.subYears,e.getSeconds,e.getMinutes,e.getHours,e.getDay,e.getDate,e.getISOWeek,e.getMonth,e.getQuarter,e.getYear,e.getTime,e.setSeconds,e.setMinutes,e.setHours,e.setMonth,e.setQuarter,e.setYear,e.min,e.max,e.differenceInCalendarDays,e.differenceInCalendarMonths,null,e.differenceInCalendarYears,e.startOfDay,e.startOfWeek,e.startOfMonth,e.startOfQuarter,e.startOfYear,e.endOfDay,null,null,e.dfIsEqual,e.dfIsSameDay,e.dfIsSameMonth,e.dfIsSameYear,e.dfIsSameQuarter,e.isAfter,e.isBefore,e.isWithinInterval,e.toDate,e.parse,e.parseISO,e.onClickOutside,e.ReactDOM,e.ReactPopper)}(this,(function(e,t,r,a,n,o,s,i,p,l,d,c,u,f,h,m,y,D,v,w,g,k,b,C,S,_,M,P,E,N,O,Y,x,T,I,L,F,R,q,A,W,K,B,j,H,Q,V,U,$,z,G,J,X,Z,ee,te,re,ae,ne,oe,se,ie,pe){\"use strict\";function le(e){return e&&\"object\"==typeof e&&\"default\"in e?e:{default:e}}var de=le(t),ce=le(a),ue=le(n),fe=le(o),he=le(s),me=le(i),ye=le(p),De=le(l),ve=le(d),we=le(c),ge=le(u),ke=le(m),be=le(y),Ce=le(D),Se=le(v),_e=le(w),Me=le(g),Pe=le(k),Ee=le(b),Ne=le(C),Oe=le(S),Ye=le(_),xe=le(M),Te=le(P),Ie=le(E),Le=le(N),Fe=le(O),Re=le(Y),qe=le(x),Ae=le(T),We=le(I),Ke=le(L),Be=le(F),je=le(R),He=le(q),Qe=le(W),Ve=le(K),Ue=le(B),$e=le(j),ze=le(H),Ge=le(Q),Je=le(V),Xe=le(z),Ze=le(G),et=le(J),tt=le(X),rt=le(Z),at=le(ee),nt=le(te),ot=le(re),st=le(ae),it=le(ne),pt=le(oe),lt=le(se),dt=le(ie);function ct(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function ut(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,a=new Array(t);r0&&(o=it.default(e,t.slice(0,e.length),new Date)),It(o)||(o=new Date(e))),It(o)&&i?o:null)}function It(e,t){return t=t||new Date(\"1/1/1000\"),fe.default(e)&&at.default(e,t)}function Lt(e,t,r){if(\"en\"===r)return he.default(e,t,{awareOfUnicodeTokens:!0});var a=Xt(r);return r&&!a&&console.warn('A locale object was not found for the provided string [\"'.concat(r,'\"].')),!a&&Jt()&&Xt(Jt())&&(a=Xt(Jt())),he.default(e,t,{locale:a||null,awareOfUnicodeTokens:!0})}function Ft(e,t){var r=t.dateFormat,a=t.locale;return e&&Lt(e,Array.isArray(r)?r[0]:r,a)||\"\"}function Rt(e,t){var r=t.hour,a=void 0===r?0:r,n=t.minute,o=void 0===n?0:n,s=t.second,i=void 0===s?0:s;return Re.default(Fe.default(Le.default(e,i),o),a)}function qt(e,t){var r=t&&Xt(t)||Jt()&&Xt(Jt());return Oe.default(e,r?{locale:r}:null)}function At(e,t){return Lt(e,\"ddd\",t)}function Wt(e){return Ve.default(e)}function Kt(e,t,r){var a=Xt(t||Jt());return Ue.default(e,{locale:a,weekStartsOn:r})}function Bt(e){return $e.default(e)}function jt(e){return Ge.default(e)}function Ht(e){return ze.default(e)}function Qt(e,t){return e&&t?tt.default(e,t):!e&&!t}function Vt(e,t){return e&&t?et.default(e,t):!e&&!t}function Ut(e,t){return e&&t?rt.default(e,t):!e&&!t}function $t(e,t){return e&&t?Ze.default(e,t):!e&&!t}function zt(e,t){return e&&t?Xe.default(e,t):!e&&!t}function Gt(e,t,r){var a,n=Ve.default(t),o=Je.default(r);try{a=ot.default(e,{start:n,end:o})}catch(e){a=!1}return a}function Jt(){return(\"undefined\"!=typeof window?window:global).__localeId__}function Xt(e){if(\"string\"==typeof e){var t=\"undefined\"!=typeof window?window:global;return t.__localeData__?t.__localeData__[e]:null}return e}function Zt(e,t){return Lt(qe.default(xt(),e),\"LLLL\",t)}function er(e,t){return Lt(qe.default(xt(),e),\"LLL\",t)}function tr(e,t){return Lt(Ae.default(xt(),e),\"QQQ\",t)}function rr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.minDate,a=t.maxDate,n=t.excludeDates,o=t.includeDates,s=t.filterDate;return lr(e,{minDate:r,maxDate:a})||n&&n.some((function(t){return $t(e,t)}))||o&&!o.some((function(t){return $t(e,t)}))||s&&!s(xt(e))||!1}function ar(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.excludeDates;return r&&r.some((function(t){return $t(e,t)}))||!1}function nr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.minDate,a=t.maxDate,n=t.excludeDates,o=t.includeDates,s=t.filterDate;return lr(e,{minDate:r,maxDate:a})||n&&n.some((function(t){return Vt(e,t)}))||o&&!o.some((function(t){return Vt(e,t)}))||s&&!s(xt(e))||!1}function or(e,t,r,a){var n=Te.default(e),o=Ye.default(e),s=Te.default(t),i=Ye.default(t),p=Te.default(a);return n===s&&n===p?o<=r&&r<=i:n=r||pn:void 0}function sr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.minDate,a=t.maxDate,n=t.excludeDates,o=t.includeDates,s=t.filterDate;return lr(e,{minDate:r,maxDate:a})||n&&n.some((function(t){return Ut(e,t)}))||o&&!o.some((function(t){return Ut(e,t)}))||s&&!s(xt(e))||!1}function ir(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.minDate,a=t.maxDate,n=new Date(e,0,1);return lr(n,{minDate:r,maxDate:a})||!1}function pr(e,t,r,a){var n=Te.default(e),o=xe.default(e),s=Te.default(t),i=xe.default(t),p=Te.default(a);return n===s&&n===p?o<=r&&r<=i:n=r||pn:void 0}function lr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.minDate,a=t.maxDate;return r&&je.default(e,r)<0||a&&je.default(e,a)>0}function dr(e,t){return t.some((function(t){return Pe.default(t)===Pe.default(e)&&Me.default(t)===Me.default(e)}))}function cr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.excludeTimes,a=t.includeTimes,n=t.filterTime;return r&&dr(e,r)||a&&!dr(e,a)||n&&!n(e)||!1}function ur(e,t){var r=t.minTime,a=t.maxTime;if(!r||!a)throw new Error(\"Both minTime and maxTime props required\");var n,o=xt(),s=Re.default(Fe.default(o,Me.default(e)),Pe.default(e)),i=Re.default(Fe.default(o,Me.default(r)),Pe.default(r)),p=Re.default(Fe.default(o,Me.default(a)),Pe.default(a));try{n=!ot.default(s,{start:i,end:p})}catch(e){n=!1}return n}function fr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.minDate,a=t.includeDates,n=Ce.default(e,1);return r&&He.default(r,n)>0||a&&a.every((function(e){return He.default(e,n)>0}))||!1}function hr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.maxDate,a=t.includeDates,n=we.default(e,1);return r&&He.default(n,r)>0||a&&a.every((function(e){return He.default(n,e)>0}))||!1}function mr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.minDate,a=t.includeDates,n=Se.default(e,1);return r&&Qe.default(r,n)>0||a&&a.every((function(e){return Qe.default(e,n)>0}))||!1}function yr(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.maxDate,a=t.includeDates,n=ge.default(e,1);return r&&Qe.default(n,r)>0||a&&a.every((function(e){return Qe.default(n,e)>0}))||!1}function Dr(e){var t=e.minDate,r=e.includeDates;if(r&&t){var a=r.filter((function(e){return je.default(e,t)>=0}));return Ke.default(a)}return r?Ke.default(r):t}function vr(e){var t=e.maxDate,r=e.includeDates;if(r&&t){var a=r.filter((function(e){return je.default(e,t)<=0}));return Be.default(a)}return r?Be.default(r):t}function wr(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:\"react-datepicker__day--highlighted\",r=new Map,a=0,n=e.length;a1&&void 0!==arguments[1]?arguments[1]:Ot,r=Math.ceil(Te.default(e)/t)*t,a=r-(t-1);return{startPeriod:a,endPeriod:r}}function Cr(e,t,r,a){for(var n=[],o=0;o<2*t+1;o++){var s=e+t-o,i=!0;r&&(i=Te.default(r)<=s),a&&i&&(i=Te.default(a)>=s),i&&n.push(s)}return n}var Sr=function(e){wt(r,e);var t=St(r);function r(e){var a;ht(this,r),Dt(bt(a=t.call(this,e)),\"renderOptions\",(function(){var e=a.props.year,t=a.state.yearsList.map((function(t){return de.default.createElement(\"div\",{className:e===t?\"react-datepicker__year-option react-datepicker__year-option--selected_year\":\"react-datepicker__year-option\",key:t,onClick:a.onChange.bind(bt(a),t)},e===t?de.default.createElement(\"span\",{className:\"react-datepicker__year-option--selected\"},\"✓\"):\"\",t)})),r=a.props.minDate?Te.default(a.props.minDate):null,n=a.props.maxDate?Te.default(a.props.maxDate):null;return n&&a.state.yearsList.find((function(e){return e===n}))||t.unshift(de.default.createElement(\"div\",{className:\"react-datepicker__year-option\",key:\"upcoming\",onClick:a.incrementYears},de.default.createElement(\"a\",{className:\"react-datepicker__navigation react-datepicker__navigation--years react-datepicker__navigation--years-upcoming\"}))),r&&a.state.yearsList.find((function(e){return e===r}))||t.push(de.default.createElement(\"div\",{className:\"react-datepicker__year-option\",key:\"previous\",onClick:a.decrementYears},de.default.createElement(\"a\",{className:\"react-datepicker__navigation react-datepicker__navigation--years react-datepicker__navigation--years-previous\"}))),t})),Dt(bt(a),\"onChange\",(function(e){a.props.onChange(e)})),Dt(bt(a),\"handleClickOutside\",(function(){a.props.onCancel()})),Dt(bt(a),\"shiftYears\",(function(e){var t=a.state.yearsList.map((function(t){return t+e}));a.setState({yearsList:t})})),Dt(bt(a),\"incrementYears\",(function(){return a.shiftYears(1)})),Dt(bt(a),\"decrementYears\",(function(){return a.shiftYears(-1)}));var n=e.yearDropdownItemNumber,o=e.scrollableYearDropdown,s=n||(o?10:5);return a.state={yearsList:Cr(a.props.year,s,a.props.minDate,a.props.maxDate)},a}return yt(r,[{key:\"render\",value:function(){var e=ce.default({\"react-datepicker__year-dropdown\":!0,\"react-datepicker__year-dropdown--scrollable\":this.props.scrollableYearDropdown});return de.default.createElement(\"div\",{className:e},this.renderOptions())}}]),r}(de.default.Component),_r=lt.default(Sr),Mr=function(e){wt(r,e);var t=St(r);function r(){var e;ht(this,r);for(var a=arguments.length,n=new Array(a),o=0;o0&&void 0!==arguments[0]?arguments[0]:{},r=!1;0===e.getTabIndex()&&!t.isInputFocused&&e.isSameDay(e.props.preSelection)&&(document.activeElement&&document.activeElement!==document.body||(r=!0),e.props.inline&&!e.props.shouldFocusDayInline&&(r=!1),e.props.containerRef&&e.props.containerRef.current&&e.props.containerRef.current.contains(document.activeElement)&&document.activeElement.classList.contains(\"react-datepicker__day\")&&(r=!0)),r&&e.dayEl.current.focus({preventScroll:!0})})),Dt(bt(e),\"renderDayContents\",(function(){if(e.isOutsideMonth()){if(e.props.monthShowsDuplicateDaysEnd&&Ne.default(e.props.day)<10)return null;if(e.props.monthShowsDuplicateDaysStart&&Ne.default(e.props.day)>20)return null}return e.props.renderDayContents?e.props.renderDayContents(Ne.default(e.props.day),e.props.day):Ne.default(e.props.day)})),Dt(bt(e),\"render\",(function(){return de.default.createElement(\"div\",{ref:e.dayEl,className:e.getClassNames(e.props.day),onKeyDown:e.handleOnKeyDown,onClick:e.handleClick,onMouseEnter:e.handleMouseEnter,tabIndex:e.getTabIndex(),\"aria-label\":e.getAriaLabel(),role:\"button\",\"aria-disabled\":e.isDisabled()},e.renderDayContents())})),e}return yt(r,[{key:\"componentDidMount\",value:function(){this.handleFocusDay()}},{key:\"componentDidUpdate\",value:function(e){this.handleFocusDay(e)}}]),r}(de.default.Component),Lr=function(e){wt(r,e);var t=St(r);function r(){var e;ht(this,r);for(var a=arguments.length,n=new Array(a),o=0;o=6,i=!r&&!e.isWeekInMonth(o);if(s||i){if(!e.props.peekNextMonth)break;n=!0}}return t})),Dt(bt(e),\"onMonthClick\",(function(t,r){e.handleDayClick(Bt(qe.default(e.props.day,r)),t)})),Dt(bt(e),\"handleMonthNavigation\",(function(t,r){e.isDisabled(r)||e.isExcluded(r)||(e.props.setPreSelection(r),e.MONTH_REFS[t].current&&e.MONTH_REFS[t].current.focus())})),Dt(bt(e),\"onMonthKeyDown\",(function(t,r){var a=t.key;if(!e.props.disabledKeyboardNavigation)switch(a){case\"Enter\":e.onMonthClick(t,r),e.props.setPreSelection(e.props.selected);break;case\"ArrowRight\":e.handleMonthNavigation(11===r?0:r+1,we.default(e.props.preSelection,1));break;case\"ArrowLeft\":e.handleMonthNavigation(0===r?11:r-1,Ce.default(e.props.preSelection,1))}})),Dt(bt(e),\"onQuarterClick\",(function(t,r){e.handleDayClick(Ht(Ae.default(e.props.day,r)),t)})),Dt(bt(e),\"getMonthClassNames\",(function(t){var r=e.props,a=r.day,n=r.startDate,o=r.endDate,s=r.selected,i=r.minDate,p=r.maxDate,l=r.preSelection,d=r.monthClassName,c=d?d(a):void 0;return ce.default(\"react-datepicker__month-text\",\"react-datepicker__month-\".concat(t),c,{\"react-datepicker__month--disabled\":(i||p)&&nr(qe.default(a,t),e.props),\"react-datepicker__month--selected\":Ye.default(a)===t&&Te.default(a)===Te.default(s),\"react-datepicker__month-text--keyboard-selected\":Ye.default(l)===t,\"react-datepicker__month--in-range\":or(n,o,t,a),\"react-datepicker__month--range-start\":e.isRangeStartMonth(t),\"react-datepicker__month--range-end\":e.isRangeEndMonth(t)})})),Dt(bt(e),\"getTabIndex\",(function(t){var r=Ye.default(e.props.preSelection);return e.props.disabledKeyboardNavigation||t!==r?\"-1\":\"0\"})),Dt(bt(e),\"getAriaLabel\",(function(t){var r=e.props,a=r.ariaLabelPrefix,n=void 0===a?\"Choose\":a,o=r.disabledDayAriaLabelPrefix,s=void 0===o?\"Not available\":o,i=r.day,p=qe.default(i,t),l=e.isDisabled(p)||e.isExcluded(p)?s:n;return\"\".concat(l,\" \").concat(Lt(p,\"MMMM yyyy\"))})),Dt(bt(e),\"getQuarterClassNames\",(function(t){var r=e.props,a=r.day,n=r.startDate,o=r.endDate,s=r.selected,i=r.minDate,p=r.maxDate;return ce.default(\"react-datepicker__quarter-text\",\"react-datepicker__quarter-\".concat(t),{\"react-datepicker__quarter--disabled\":(i||p)&&sr(Ae.default(a,t),e.props),\"react-datepicker__quarter--selected\":xe.default(a)===t&&Te.default(a)===Te.default(s),\"react-datepicker__quarter--in-range\":pr(n,o,t,a),\"react-datepicker__quarter--range-start\":e.isRangeStartQuarter(t),\"react-datepicker__quarter--range-end\":e.isRangeEndQuarter(t)})})),Dt(bt(e),\"renderMonths\",(function(){var t=e.props,r=t.showFullMonthYearPicker,a=t.showTwoColumnMonthYearPicker,n=t.showFourColumnMonthYearPicker,o=t.locale;return(n?[[0,1,2,3],[4,5,6,7],[8,9,10,11]]:a?[[0,1],[2,3],[4,5],[6,7],[8,9],[10,11]]:[[0,1,2],[3,4,5],[6,7,8],[9,10,11]]).map((function(t,a){return de.default.createElement(\"div\",{className:\"react-datepicker__month-wrapper\",key:a},t.map((function(t,a){return de.default.createElement(\"div\",{ref:e.MONTH_REFS[t],key:a,onClick:function(r){e.onMonthClick(r,t)},onKeyDown:function(r){e.onMonthKeyDown(r,t)},tabIndex:e.getTabIndex(t),className:e.getMonthClassNames(t),role:\"button\",\"aria-label\":e.getAriaLabel(t)},r?Zt(t,o):er(t,o))})))}))})),Dt(bt(e),\"renderQuarters\",(function(){return de.default.createElement(\"div\",{className:\"react-datepicker__quarter-wrapper\"},[1,2,3,4].map((function(t,r){return de.default.createElement(\"div\",{key:r,onClick:function(r){e.onQuarterClick(r,t)},className:e.getQuarterClassNames(t)},tr(t,e.props.locale))})))})),Dt(bt(e),\"getClassNames\",(function(){var t=e.props;t.day;var r=t.selectingDate,a=t.selectsStart,n=t.selectsEnd,o=t.showMonthYearPicker,s=t.showQuarterYearPicker;return ce.default(\"react-datepicker__month\",{\"react-datepicker__month--selecting-range\":r&&(a||n)},{\"react-datepicker__monthPicker\":o},{\"react-datepicker__quarterPicker\":s})})),e}return yt(r,[{key:\"render\",value:function(){var e=this.props,t=e.showMonthYearPicker,r=e.showQuarterYearPicker,a=e.day,n=e.ariaLabelPrefix,o=void 0===n?\"month \":n;return de.default.createElement(\"div\",{className:this.getClassNames(),onMouseLeave:this.handleMouseLeave,\"aria-label\":\"\".concat(o,\" \").concat(Lt(a,\"yyyy-MM\"))},t?this.renderMonths():r?this.renderQuarters():this.renderWeeks())}}]),r}(de.default.Component),qr=function(e){wt(r,e);var t=St(r);function r(){var e;ht(this,r);for(var a=arguments.length,n=new Array(a),o=0;o0&&void 0!==arguments[0]?arguments[0]:{}).className||\"\").split(/\\s+/);return Br.some((function(t){return e.indexOf(t)>=0}))})(e.target)&&a.props.onDropdownFocus()})),Dt(bt(a),\"getDateInView\",(function(){var e=a.props,t=e.preSelection,r=e.selected,n=e.openToDate,o=Dr(a.props),s=vr(a.props),i=xt(),p=n||r||t;return p||(o&&nt.default(i,o)?o:s&&at.default(i,s)?s:i)})),Dt(bt(a),\"increaseMonth\",(function(){a.setState((function(e){var t=e.date;return{date:we.default(t,1)}}),(function(){return a.handleMonthChange(a.state.date)}))})),Dt(bt(a),\"decreaseMonth\",(function(){a.setState((function(e){var t=e.date;return{date:Ce.default(t,1)}}),(function(){return a.handleMonthChange(a.state.date)}))})),Dt(bt(a),\"handleDayClick\",(function(e,t,r){a.props.onSelect(e,t,r),a.props.setPreSelection&&a.props.setPreSelection(e)})),Dt(bt(a),\"handleDayMouseEnter\",(function(e){a.setState({selectingDate:e}),a.props.onDayMouseEnter&&a.props.onDayMouseEnter(e)})),Dt(bt(a),\"handleMonthMouseLeave\",(function(){a.setState({selectingDate:null}),a.props.onMonthMouseLeave&&a.props.onMonthMouseLeave()})),Dt(bt(a),\"handleYearChange\",(function(e){a.props.onYearChange&&a.props.onYearChange(e),a.props.adjustDateOnChange&&(a.props.onSelect&&a.props.onSelect(e),a.props.setOpen&&a.props.setOpen(!0)),a.props.setPreSelection&&a.props.setPreSelection(e)})),Dt(bt(a),\"handleMonthChange\",(function(e){a.props.onMonthChange&&a.props.onMonthChange(e),a.props.adjustDateOnChange&&(a.props.onSelect&&a.props.onSelect(e),a.props.setOpen&&a.props.setOpen(!0)),a.props.setPreSelection&&a.props.setPreSelection(e)})),Dt(bt(a),\"handleMonthYearChange\",(function(e){a.handleYearChange(e),a.handleMonthChange(e)})),Dt(bt(a),\"changeYear\",(function(e){a.setState((function(t){var r=t.date;return{date:We.default(r,e)}}),(function(){return a.handleYearChange(a.state.date)}))})),Dt(bt(a),\"changeMonth\",(function(e){a.setState((function(t){var r=t.date;return{date:qe.default(r,e)}}),(function(){return a.handleMonthChange(a.state.date)}))})),Dt(bt(a),\"changeMonthYear\",(function(e){a.setState((function(t){var r=t.date;return{date:We.default(qe.default(r,Ye.default(e)),Te.default(e))}}),(function(){return a.handleMonthYearChange(a.state.date)}))})),Dt(bt(a),\"header\",(function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:a.state.date,t=Kt(e,a.props.locale,a.props.calendarStartDay),r=[];return a.props.showWeekNumbers&&r.push(de.default.createElement(\"div\",{key:\"W\",className:\"react-datepicker__day-name\"},a.props.weekLabel||\"#\")),r.concat([0,1,2,3,4,5,6].map((function(e){var r=De.default(t,e),n=a.formatWeekday(r,a.props.locale),o=a.props.weekDayClassName?a.props.weekDayClassName(r):void 0;return de.default.createElement(\"div\",{key:e,className:ce.default(\"react-datepicker__day-name\",o)},n)})))})),Dt(bt(a),\"formatWeekday\",(function(e,t){return a.props.formatWeekDay?function(e,t,r){return t(Lt(e,\"EEEE\",r))}(e,a.props.formatWeekDay,t):a.props.useWeekdaysShort?function(e,t){return Lt(e,\"EEE\",t)}(e,t):function(e,t){return Lt(e,\"EEEEEE\",t)}(e,t)})),Dt(bt(a),\"decreaseYear\",(function(){a.setState((function(e){var t=e.date;return{date:Se.default(t,a.props.showYearPicker?a.props.yearItemNumber:1)}}),(function(){return a.handleYearChange(a.state.date)}))})),Dt(bt(a),\"renderPreviousButton\",(function(){if(!a.props.renderCustomHeader){var e;switch(!0){case a.props.showMonthYearPicker:e=mr(a.state.date,a.props);break;case a.props.showYearPicker:e=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.minDate,a=t.yearItemNumber,n=void 0===a?Ot:a,o=br(jt(Se.default(e,n)),n).endPeriod,s=r&&Te.default(r);return s&&s>o||!1}(a.state.date,a.props);break;default:e=fr(a.state.date,a.props)}if((a.props.forceShowMonthNavigation||a.props.showDisabledMonthNavigation||!e)&&!a.props.showTimeSelectOnly){var t=[\"react-datepicker__navigation\",\"react-datepicker__navigation--previous\"],r=a.decreaseMonth;(a.props.showMonthYearPicker||a.props.showQuarterYearPicker||a.props.showYearPicker)&&(r=a.decreaseYear),e&&a.props.showDisabledMonthNavigation&&(t.push(\"react-datepicker__navigation--previous--disabled\"),r=null);var n=a.props.showMonthYearPicker||a.props.showQuarterYearPicker||a.props.showYearPicker,o=a.props,s=o.previousMonthAriaLabel,i=void 0===s?\"Previous Month\":s,p=o.previousYearAriaLabel,l=void 0===p?\"Previous Year\":p;return de.default.createElement(\"button\",{type:\"button\",className:t.join(\" \"),onClick:r,onKeyDown:a.props.handleOnKeyDown,\"aria-label\":n?l:i},de.default.createElement(\"span\",{className:[\"react-datepicker__navigation-icon\",\"react-datepicker__navigation-icon--previous\"].join(\" \")},n?a.props.previousYearButtonLabel:a.props.previousMonthButtonLabel))}}})),Dt(bt(a),\"increaseYear\",(function(){a.setState((function(e){var t=e.date;return{date:ge.default(t,a.props.showYearPicker?a.props.yearItemNumber:1)}}),(function(){return a.handleYearChange(a.state.date)}))})),Dt(bt(a),\"renderNextButton\",(function(){if(!a.props.renderCustomHeader){var e;switch(!0){case a.props.showMonthYearPicker:e=yr(a.state.date,a.props);break;case a.props.showYearPicker:e=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=t.maxDate,a=t.yearItemNumber,n=void 0===a?Ot:a,o=br(ge.default(e,n),n).startPeriod,s=r&&Te.default(r);return s&&s0&&void 0!==arguments[0]?arguments[0]:a.state.date,t=[\"react-datepicker__current-month\"];return a.props.showYearDropdown&&t.push(\"react-datepicker__current-month--hasYearDropdown\"),a.props.showMonthDropdown&&t.push(\"react-datepicker__current-month--hasMonthDropdown\"),a.props.showMonthYearDropdown&&t.push(\"react-datepicker__current-month--hasMonthYearDropdown\"),de.default.createElement(\"div\",{className:t.join(\" \")},Lt(e,a.props.dateFormat,a.props.locale))})),Dt(bt(a),\"renderYearDropdown\",(function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(a.props.showYearDropdown&&!e)return de.default.createElement(Mr,{adjustDateOnChange:a.props.adjustDateOnChange,date:a.state.date,onSelect:a.props.onSelect,setOpen:a.props.setOpen,dropdownMode:a.props.dropdownMode,onChange:a.changeYear,minDate:a.props.minDate,maxDate:a.props.maxDate,year:Te.default(a.state.date),scrollableYearDropdown:a.props.scrollableYearDropdown,yearDropdownItemNumber:a.props.yearDropdownItemNumber})})),Dt(bt(a),\"renderMonthDropdown\",(function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(a.props.showMonthDropdown&&!e)return de.default.createElement(Nr,{dropdownMode:a.props.dropdownMode,locale:a.props.locale,onChange:a.changeMonth,month:Ye.default(a.state.date),useShortMonthInDropdown:a.props.useShortMonthInDropdown})})),Dt(bt(a),\"renderMonthYearDropdown\",(function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(a.props.showMonthYearDropdown&&!e)return de.default.createElement(Tr,{dropdownMode:a.props.dropdownMode,locale:a.props.locale,dateFormat:a.props.dateFormat,onChange:a.changeMonthYear,minDate:a.props.minDate,maxDate:a.props.maxDate,date:a.state.date,scrollableMonthYearDropdown:a.props.scrollableMonthYearDropdown})})),Dt(bt(a),\"renderTodayButton\",(function(){if(a.props.todayButton&&!a.props.showTimeSelectOnly)return de.default.createElement(\"div\",{className:\"react-datepicker__today-button\",onClick:function(e){return a.props.onSelect(Ve.default(xt()),e)}},a.props.todayButton)})),Dt(bt(a),\"renderDefaultHeader\",(function(e){var t=e.monthDate,r=e.i;return de.default.createElement(\"div\",{className:\"react-datepicker__header \".concat(a.props.showTimeSelect?\"react-datepicker__header--has-time-select\":\"\")},a.renderCurrentMonth(t),de.default.createElement(\"div\",{className:\"react-datepicker__header__dropdown react-datepicker__header__dropdown--\".concat(a.props.dropdownMode),onFocus:a.handleDropdownFocus},a.renderMonthDropdown(0!==r),a.renderMonthYearDropdown(0!==r),a.renderYearDropdown(0!==r)),de.default.createElement(\"div\",{className:\"react-datepicker__day-names\"},a.header(t)))})),Dt(bt(a),\"renderCustomHeader\",(function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.monthDate,r=e.i;if(a.props.showTimeSelect&&!a.state.monthContainer||a.props.showTimeSelectOnly)return null;var n=fr(a.state.date,a.props),o=hr(a.state.date,a.props),s=mr(a.state.date,a.props),i=yr(a.state.date,a.props),p=!a.props.showMonthYearPicker&&!a.props.showQuarterYearPicker&&!a.props.showYearPicker;return de.default.createElement(\"div\",{className:\"react-datepicker__header react-datepicker__header--custom\",onFocus:a.props.onDropdownFocus},a.props.renderCustomHeader(ut(ut({},a.state),{},{customHeaderCount:r,monthDate:t,changeMonth:a.changeMonth,changeYear:a.changeYear,decreaseMonth:a.decreaseMonth,increaseMonth:a.increaseMonth,decreaseYear:a.decreaseYear,increaseYear:a.increaseYear,prevMonthButtonDisabled:n,nextMonthButtonDisabled:o,prevYearButtonDisabled:s,nextYearButtonDisabled:i})),p&&de.default.createElement(\"div\",{className:\"react-datepicker__day-names\"},a.header(t)))})),Dt(bt(a),\"renderYearHeader\",(function(){var e=a.state.date,t=a.props,r=t.showYearPicker,n=br(e,t.yearItemNumber),o=n.startPeriod,s=n.endPeriod;return de.default.createElement(\"div\",{className:\"react-datepicker__header react-datepicker-year-header\"},r?\"\".concat(o,\" - \").concat(s):Te.default(e))})),Dt(bt(a),\"renderHeader\",(function(e){switch(!0){case void 0!==a.props.renderCustomHeader:return a.renderCustomHeader(e);case a.props.showMonthYearPicker||a.props.showQuarterYearPicker||a.props.showYearPicker:return a.renderYearHeader(e);default:return a.renderDefaultHeader(e)}})),Dt(bt(a),\"renderMonths\",(function(){if(!a.props.showTimeSelectOnly&&!a.props.showYearPicker){for(var e=[],t=a.props.showPreviousMonths?a.props.monthsShown-1:0,r=Ce.default(a.state.date,t),n=0;n0;e.push(de.default.createElement(\"div\",{key:i,ref:function(e){a.monthContainer=e},className:\"react-datepicker__month-container\"},a.renderHeader({monthDate:s,i:n}),de.default.createElement(Rr,{chooseDayAriaLabelPrefix:a.props.chooseDayAriaLabelPrefix,disabledDayAriaLabelPrefix:a.props.disabledDayAriaLabelPrefix,weekAriaLabelPrefix:a.props.weekAriaLabelPrefix,onChange:a.changeMonthYear,day:s,dayClassName:a.props.dayClassName,calendarStartDay:a.props.calendarStartDay,monthClassName:a.props.monthClassName,onDayClick:a.handleDayClick,handleOnKeyDown:a.props.handleOnDayKeyDown,onDayMouseEnter:a.handleDayMouseEnter,onMouseLeave:a.handleMonthMouseLeave,onWeekSelect:a.props.onWeekSelect,orderInDisplay:n,formatWeekNumber:a.props.formatWeekNumber,locale:a.props.locale,minDate:a.props.minDate,maxDate:a.props.maxDate,excludeDates:a.props.excludeDates,highlightDates:a.props.highlightDates,selectingDate:a.state.selectingDate,includeDates:a.props.includeDates,inline:a.props.inline,shouldFocusDayInline:a.props.shouldFocusDayInline,fixedHeight:a.props.fixedHeight,filterDate:a.props.filterDate,preSelection:a.props.preSelection,setPreSelection:a.props.setPreSelection,selected:a.props.selected,selectsStart:a.props.selectsStart,selectsEnd:a.props.selectsEnd,selectsRange:a.props.selectsRange,showWeekNumbers:a.props.showWeekNumbers,startDate:a.props.startDate,endDate:a.props.endDate,peekNextMonth:a.props.peekNextMonth,setOpen:a.props.setOpen,shouldCloseOnSelect:a.props.shouldCloseOnSelect,renderDayContents:a.props.renderDayContents,disabledKeyboardNavigation:a.props.disabledKeyboardNavigation,showMonthYearPicker:a.props.showMonthYearPicker,showFullMonthYearPicker:a.props.showFullMonthYearPicker,showTwoColumnMonthYearPicker:a.props.showTwoColumnMonthYearPicker,showFourColumnMonthYearPicker:a.props.showFourColumnMonthYearPicker,showYearPicker:a.props.showYearPicker,showQuarterYearPicker:a.props.showQuarterYearPicker,isInputFocused:a.props.isInputFocused,containerRef:a.containerRef,monthShowsDuplicateDaysEnd:p,monthShowsDuplicateDaysStart:l})))}return e}})),Dt(bt(a),\"renderYears\",(function(){if(!a.props.showTimeSelectOnly)return a.props.showYearPicker?de.default.createElement(\"div\",{className:\"react-datepicker__year--container\"},a.renderHeader(),de.default.createElement(Ar,vt({onDayClick:a.handleDayClick,date:a.state.date},a.props))):void 0})),Dt(bt(a),\"renderTimeSection\",(function(){if(a.props.showTimeSelect&&(a.state.monthContainer||a.props.showTimeSelectOnly))return de.default.createElement(qr,{selected:a.props.selected,openToDate:a.props.openToDate,onChange:a.props.onTimeChange,timeClassName:a.props.timeClassName,format:a.props.timeFormat,includeTimes:a.props.includeTimes,intervals:a.props.timeIntervals,minTime:a.props.minTime,maxTime:a.props.maxTime,excludeTimes:a.props.excludeTimes,filterTime:a.props.filterTime,timeCaption:a.props.timeCaption,todayButton:a.props.todayButton,showMonthDropdown:a.props.showMonthDropdown,showMonthYearDropdown:a.props.showMonthYearDropdown,showYearDropdown:a.props.showYearDropdown,withPortal:a.props.withPortal,monthRef:a.state.monthContainer,injectTimes:a.props.injectTimes,locale:a.props.locale,handleOnKeyDown:a.props.handleOnKeyDown,showTimeSelectOnly:a.props.showTimeSelectOnly})})),Dt(bt(a),\"renderInputTimeSection\",(function(){var e=new Date(a.props.selected),t=It(e)&&Boolean(a.props.selected)?\"\".concat(kr(e.getHours()),\":\").concat(kr(e.getMinutes())):\"\";if(a.props.showTimeInput)return de.default.createElement(Wr,{date:e,timeString:t,timeInputLabel:a.props.timeInputLabel,onChange:a.props.onTimeChange,customTimeInput:a.props.customTimeInput})})),a.containerRef=de.default.createRef(),a.state={date:a.getDateInView(),selectingDate:null,monthContainer:null},a}return yt(r,[{key:\"componentDidMount\",value:function(){var e=this;this.props.showTimeSelect&&(this.assignMonthContainer=void e.setState({monthContainer:e.monthContainer}))}},{key:\"componentDidUpdate\",value:function(e){this.props.preSelection&&!$t(this.props.preSelection,e.preSelection)?this.setState({date:this.props.preSelection}):this.props.openToDate&&!$t(this.props.openToDate,e.openToDate)&&this.setState({date:this.props.openToDate})}},{key:\"render\",value:function(){var e=this.props.container||Kr;return de.default.createElement(\"div\",{ref:this.containerRef},de.default.createElement(e,{className:ce.default(\"react-datepicker\",this.props.className,{\"react-datepicker--time-only\":this.props.showTimeSelectOnly}),showPopperArrow:this.props.showPopperArrow,arrowProps:this.props.arrowProps},this.renderPreviousButton(),this.renderNextButton(),this.renderMonths(),this.renderYears(),this.renderTodayButton(),this.renderTimeSection(),this.renderInputTimeSection(),this.props.children))}}],[{key:\"defaultProps\",get:function(){return{onDropdownFocus:function(){},monthsShown:1,monthSelectedIn:0,forceShowMonthNavigation:!1,timeCaption:\"Time\",previousYearButtonLabel:\"Previous Year\",nextYearButtonLabel:\"Next Year\",previousMonthButtonLabel:\"Previous Month\",nextMonthButtonLabel:\"Next Month\",customTimeInput:null,yearItemNumber:Ot}}}]),r}(de.default.Component),Hr=function(e){wt(r,e);var t=St(r);function r(e){var a;return ht(this,r),(a=t.call(this,e)).el=document.createElement(\"div\"),a}return yt(r,[{key:\"componentDidMount\",value:function(){this.portalRoot=document.getElementById(this.props.portalId),this.portalRoot||(this.portalRoot=document.createElement(\"div\"),this.portalRoot.setAttribute(\"id\",this.props.portalId),document.body.appendChild(this.portalRoot)),this.portalRoot.appendChild(this.el)}},{key:\"componentWillUnmount\",value:function(){this.portalRoot.removeChild(this.el)}},{key:\"render\",value:function(){return dt.default.createPortal(this.props.children,this.el)}}]),r}(de.default.Component),Qr=function(e){return!e.disabled&&-1!==e.tabIndex},Vr=function(e){wt(r,e);var t=St(r);function r(e){var a;return ht(this,r),Dt(bt(a=t.call(this,e)),\"getTabChildren\",(function(){return Array.prototype.slice.call(a.tabLoopRef.current.querySelectorAll(\"[tabindex], a, button, input, select, textarea\"),1,-1).filter(Qr)})),Dt(bt(a),\"handleFocusStart\",(function(e){var t=a.getTabChildren();t&&t.length>1&&t[t.length-1].focus()})),Dt(bt(a),\"handleFocusEnd\",(function(e){var t=a.getTabChildren();t&&t.length>1&&t[0].focus()})),a.tabLoopRef=de.default.createRef(),a}return yt(r,[{key:\"render\",value:function(){return this.props.enableTabLoop?de.default.createElement(\"div\",{className:\"react-datepicker__tab-loop\",ref:this.tabLoopRef},de.default.createElement(\"div\",{className:\"react-datepicker__tab-loop__start\",tabIndex:\"0\",onFocus:this.handleFocusStart}),this.props.children,de.default.createElement(\"div\",{className:\"react-datepicker__tab-loop__end\",tabIndex:\"0\",onFocus:this.handleFocusEnd})):this.props.children}}],[{key:\"defaultProps\",get:function(){return{enableTabLoop:!0}}}]),r}(de.default.Component),Ur=function(e){wt(r,e);var t=St(r);function r(){return ht(this,r),t.apply(this,arguments)}return yt(r,[{key:\"render\",value:function(){var e,t=this.props,r=t.className,a=t.wrapperClassName,n=t.hidePopper,o=t.popperComponent,s=t.popperModifiers,i=t.popperPlacement,p=t.popperProps,l=t.targetComponent,d=t.enableTabLoop,c=t.popperOnKeyDown,u=t.portalId;if(!n){var f=ce.default(\"react-datepicker-popper\",r);e=de.default.createElement(pe.Popper,vt({modifiers:s,placement:i},p),(function(e){var t=e.ref,r=e.style,a=e.placement,n=e.arrowProps;return de.default.createElement(Vr,{enableTabLoop:d},de.default.createElement(\"div\",{ref:t,style:r,className:f,\"data-placement\":a,onKeyDown:c},de.default.cloneElement(o,{arrowProps:n})))}))}this.props.popperContainer&&(e=de.default.createElement(this.props.popperContainer,{},e)),u&&!n&&(e=de.default.createElement(Hr,{portalId:u},e));var h=ce.default(\"react-datepicker-wrapper\",a);return de.default.createElement(pe.Manager,{className:\"react-datepicker-manager\"},de.default.createElement(pe.Reference,null,(function(e){var t=e.ref;return de.default.createElement(\"div\",{ref:t,className:h},l)})),e)}}],[{key:\"defaultProps\",get:function(){return{hidePopper:!0,popperModifiers:[],popperProps:{},popperPlacement:\"bottom-start\"}}}]),r}(de.default.Component),$r=\"react-datepicker-ignore-onclickoutside\",zr=lt.default(jr);var Gr=\"Date input not valid.\",Jr=function(e){wt(r,e);var t=St(r);function r(e){var a;return ht(this,r),Dt(bt(a=t.call(this,e)),\"getPreSelection\",(function(){return a.props.openToDate?a.props.openToDate:a.props.selectsEnd&&a.props.startDate?a.props.startDate:a.props.selectsStart&&a.props.endDate?a.props.endDate:xt()})),Dt(bt(a),\"calcInitialState\",(function(){var e,t=a.getPreSelection(),r=Dr(a.props),n=vr(a.props),o=r&&nt.default(t,Ve.default(r))?r:n&&at.default(t,Je.default(n))?n:t;return{open:a.props.startOpen||!1,preventFocus:!1,preSelection:null!==(e=a.props.selectsRange?a.props.startDate:a.props.selected)&&void 0!==e?e:o,highlightDates:wr(a.props.highlightDates),focused:!1,shouldFocusDayInline:!1}})),Dt(bt(a),\"clearPreventFocusTimeout\",(function(){a.preventFocusTimeout&&clearTimeout(a.preventFocusTimeout)})),Dt(bt(a),\"setFocus\",(function(){a.input&&a.input.focus&&a.input.focus({preventScroll:!0})})),Dt(bt(a),\"setBlur\",(function(){a.input&&a.input.blur&&a.input.blur(),a.cancelFocusInput()})),Dt(bt(a),\"setOpen\",(function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];a.setState({open:e,preSelection:e&&a.state.open?a.state.preSelection:a.calcInitialState().preSelection,lastPreSelectChange:Zr},(function(){e||a.setState((function(e){return{focused:!!t&&e.focused}}),(function(){!t&&a.setBlur(),a.setState({inputValue:null})}))}))})),Dt(bt(a),\"inputOk\",(function(){return ue.default(a.state.preSelection)})),Dt(bt(a),\"isCalendarOpen\",(function(){return void 0===a.props.open?a.state.open&&!a.props.disabled&&!a.props.readOnly:a.props.open})),Dt(bt(a),\"handleFocus\",(function(e){a.state.preventFocus||(a.props.onFocus(e),a.props.preventOpenOnFocus||a.props.readOnly||a.setOpen(!0)),a.setState({focused:!0})})),Dt(bt(a),\"cancelFocusInput\",(function(){clearTimeout(a.inputFocusTimeout),a.inputFocusTimeout=null})),Dt(bt(a),\"deferFocusInput\",(function(){a.cancelFocusInput(),a.inputFocusTimeout=setTimeout((function(){return a.setFocus()}),1)})),Dt(bt(a),\"handleDropdownFocus\",(function(){a.cancelFocusInput()})),Dt(bt(a),\"handleBlur\",(function(e){(!a.state.open||a.props.withPortal||a.props.showTimeInput)&&a.props.onBlur(e),a.setState({focused:!1})})),Dt(bt(a),\"handleCalendarClickOutside\",(function(e){a.props.inline||a.setOpen(!1),a.props.onClickOutside(e),a.props.withPortal&&e.preventDefault()})),Dt(bt(a),\"handleChange\",(function(){for(var e=arguments.length,t=new Array(e),r=0;r &&` helpers in initial condition allow es6 code\n // to co-exist with es5.\n // 2. Replace `for of` with es5 compliant iteration using `for`.\n // Basically, take:\n //\n // ```js\n // for (i of a.entries())\n // if (!b.has(i[0])) return false;\n // ```\n //\n // ... and convert to:\n //\n // ```js\n // it = a.entries();\n // while (!(i = it.next()).done)\n // if (!b.has(i.value[0])) return false;\n // ```\n //\n // **Note**: `i` access switches to `i.value`.\n var it;\n if (hasMap && (a instanceof Map) && (b instanceof Map)) {\n if (a.size !== b.size) return false;\n it = a.entries();\n while (!(i = it.next()).done)\n if (!b.has(i.value[0])) return false;\n it = a.entries();\n while (!(i = it.next()).done)\n if (!equal(i.value[1], b.get(i.value[0]))) return false;\n return true;\n }\n\n if (hasSet && (a instanceof Set) && (b instanceof Set)) {\n if (a.size !== b.size) return false;\n it = a.entries();\n while (!(i = it.next()).done)\n if (!b.has(i.value[0])) return false;\n return true;\n }\n // END: Modifications\n\n if (hasArrayBuffer && ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {\n length = a.length;\n if (length != b.length) return false;\n for (i = length; i-- !== 0;)\n if (a[i] !== b[i]) return false;\n return true;\n }\n\n if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;\n if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();\n if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();\n\n keys = Object.keys(a);\n length = keys.length;\n if (length !== Object.keys(b).length) return false;\n\n for (i = length; i-- !== 0;)\n if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;\n // END: fast-deep-equal\n\n // START: react-fast-compare\n // custom handling for DOM elements\n if (hasElementType && a instanceof Element) return false;\n\n // custom handling for React/Preact\n for (i = length; i-- !== 0;) {\n if ((keys[i] === '_owner' || keys[i] === '__v' || keys[i] === '__o') && a.$$typeof) {\n // React-specific: avoid traversing React elements' _owner\n // Preact-specific: avoid traversing Preact elements' __v and __o\n // __v = $_original / $_vnode\n // __o = $_owner\n // These properties contain circular references and are not needed when\n // comparing the actual elements (and not their owners)\n // .$$typeof and ._store on just reasonable markers of elements\n\n continue;\n }\n\n // all other properties should be traversed as usual\n if (!equal(a[keys[i]], b[keys[i]])) return false;\n }\n // END: react-fast-compare\n\n // START: fast-deep-equal\n return true;\n }\n\n return a !== a && b !== b;\n}\n// end fast-deep-equal\n\nmodule.exports = function isEqual(a, b) {\n try {\n return equal(a, b);\n } catch (error) {\n if (((error.message || '').match(/stack|recursion/i))) {\n // warn on circular references, don't crash\n // browsers give this different errors name and messages:\n // chrome/safari: \"RangeError\", \"Maximum call stack size exceeded\"\n // firefox: \"InternalError\", too much recursion\"\n // edge: \"Error\", \"Out of stack space\"\n console.warn('react-fast-compare cannot handle circular refs');\n return false;\n }\n // some other error. we should definitely know about these\n throw error;\n }\n};\n","/**\n * Copyright (c) 2014-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\n/**\n * Similar to invariant but only logs a warning if the condition is not met.\n * This can be used to log issues in development environments in critical\n * paths. Removing the logging code for production environments will keep the\n * same logic and follow the same code paths.\n */\n\nvar __DEV__ = process.env.NODE_ENV !== 'production';\n\nvar warning = function() {};\n\nif (__DEV__) {\n var printWarning = function printWarning(format, args) {\n var len = arguments.length;\n args = new Array(len > 1 ? len - 1 : 0);\n for (var key = 1; key < len; key++) {\n args[key - 1] = arguments[key];\n }\n var argIndex = 0;\n var message = 'Warning: ' +\n format.replace(/%s/g, function() {\n return args[argIndex++];\n });\n if (typeof console !== 'undefined') {\n console.error(message);\n }\n try {\n // --- Welcome to debugging React ---\n // This error was thrown as a convenience so that you can use this stack\n // to find the callsite that caused this warning to fire.\n throw new Error(message);\n } catch (x) {}\n }\n\n warning = function(condition, format, args) {\n var len = arguments.length;\n args = new Array(len > 2 ? len - 2 : 0);\n for (var key = 2; key < len; key++) {\n args[key - 2] = arguments[key];\n }\n if (format === undefined) {\n throw new Error(\n '`warning(condition, format, ...args)` requires a warning ' +\n 'message argument'\n );\n }\n if (!condition) {\n printWarning.apply(null, [format].concat(args));\n }\n };\n}\n\nmodule.exports = warning;\n","import toInteger from \"../toInteger/index.js\";\nimport toDate from \"../../toDate/index.js\";\nimport requiredArgs from \"../requiredArgs/index.js\"; // This function will be a part of public API when UTC function will be implemented.\n// See issue: https://github.com/date-fns/date-fns/issues/376\n\nexport default function setUTCDay(dirtyDate, dirtyDay, dirtyOptions) {\n requiredArgs(2, arguments);\n var options = dirtyOptions || {};\n var locale = options.locale;\n var localeWeekStartsOn = locale && locale.options && locale.options.weekStartsOn;\n var defaultWeekStartsOn = localeWeekStartsOn == null ? 0 : toInteger(localeWeekStartsOn);\n var weekStartsOn = options.weekStartsOn == null ? defaultWeekStartsOn : toInteger(options.weekStartsOn); // Test if weekStartsOn is between 0 and 6 _and_ is not NaN\n\n if (!(weekStartsOn >= 0 && weekStartsOn <= 6)) {\n throw new RangeError('weekStartsOn must be between 0 and 6 inclusively');\n }\n\n var date = toDate(dirtyDate);\n var day = toInteger(dirtyDay);\n var currentDay = date.getUTCDay();\n var remainder = day % 7;\n var dayIndex = (remainder + 7) % 7;\n var diff = (dayIndex < weekStartsOn ? 7 : 0) + day - currentDay;\n date.setUTCDate(date.getUTCDate() + diff);\n return date;\n}","import getUTCWeekYear from \"../../../_lib/getUTCWeekYear/index.js\";\nimport setUTCDay from \"../../../_lib/setUTCDay/index.js\";\nimport setUTCISODay from \"../../../_lib/setUTCISODay/index.js\";\nimport setUTCISOWeek from \"../../../_lib/setUTCISOWeek/index.js\";\nimport setUTCWeek from \"../../../_lib/setUTCWeek/index.js\";\nimport startOfUTCISOWeek from \"../../../_lib/startOfUTCISOWeek/index.js\";\nimport startOfUTCWeek from \"../../../_lib/startOfUTCWeek/index.js\";\nvar MILLISECONDS_IN_HOUR = 3600000;\nvar MILLISECONDS_IN_MINUTE = 60000;\nvar MILLISECONDS_IN_SECOND = 1000;\nvar numericPatterns = {\n month: /^(1[0-2]|0?\\d)/,\n // 0 to 12\n date: /^(3[0-1]|[0-2]?\\d)/,\n // 0 to 31\n dayOfYear: /^(36[0-6]|3[0-5]\\d|[0-2]?\\d?\\d)/,\n // 0 to 366\n week: /^(5[0-3]|[0-4]?\\d)/,\n // 0 to 53\n hour23h: /^(2[0-3]|[0-1]?\\d)/,\n // 0 to 23\n hour24h: /^(2[0-4]|[0-1]?\\d)/,\n // 0 to 24\n hour11h: /^(1[0-1]|0?\\d)/,\n // 0 to 11\n hour12h: /^(1[0-2]|0?\\d)/,\n // 0 to 12\n minute: /^[0-5]?\\d/,\n // 0 to 59\n second: /^[0-5]?\\d/,\n // 0 to 59\n singleDigit: /^\\d/,\n // 0 to 9\n twoDigits: /^\\d{1,2}/,\n // 0 to 99\n threeDigits: /^\\d{1,3}/,\n // 0 to 999\n fourDigits: /^\\d{1,4}/,\n // 0 to 9999\n anyDigitsSigned: /^-?\\d+/,\n singleDigitSigned: /^-?\\d/,\n // 0 to 9, -0 to -9\n twoDigitsSigned: /^-?\\d{1,2}/,\n // 0 to 99, -0 to -99\n threeDigitsSigned: /^-?\\d{1,3}/,\n // 0 to 999, -0 to -999\n fourDigitsSigned: /^-?\\d{1,4}/ // 0 to 9999, -0 to -9999\n\n};\nvar timezonePatterns = {\n basicOptionalMinutes: /^([+-])(\\d{2})(\\d{2})?|Z/,\n basic: /^([+-])(\\d{2})(\\d{2})|Z/,\n basicOptionalSeconds: /^([+-])(\\d{2})(\\d{2})((\\d{2}))?|Z/,\n extended: /^([+-])(\\d{2}):(\\d{2})|Z/,\n extendedOptionalSeconds: /^([+-])(\\d{2}):(\\d{2})(:(\\d{2}))?|Z/\n};\n\nfunction parseNumericPattern(pattern, string, valueCallback) {\n var matchResult = string.match(pattern);\n\n if (!matchResult) {\n return null;\n }\n\n var value = parseInt(matchResult[0], 10);\n return {\n value: valueCallback ? valueCallback(value) : value,\n rest: string.slice(matchResult[0].length)\n };\n}\n\nfunction parseTimezonePattern(pattern, string) {\n var matchResult = string.match(pattern);\n\n if (!matchResult) {\n return null;\n } // Input is 'Z'\n\n\n if (matchResult[0] === 'Z') {\n return {\n value: 0,\n rest: string.slice(1)\n };\n }\n\n var sign = matchResult[1] === '+' ? 1 : -1;\n var hours = matchResult[2] ? parseInt(matchResult[2], 10) : 0;\n var minutes = matchResult[3] ? parseInt(matchResult[3], 10) : 0;\n var seconds = matchResult[5] ? parseInt(matchResult[5], 10) : 0;\n return {\n value: sign * (hours * MILLISECONDS_IN_HOUR + minutes * MILLISECONDS_IN_MINUTE + seconds * MILLISECONDS_IN_SECOND),\n rest: string.slice(matchResult[0].length)\n };\n}\n\nfunction parseAnyDigitsSigned(string, valueCallback) {\n return parseNumericPattern(numericPatterns.anyDigitsSigned, string, valueCallback);\n}\n\nfunction parseNDigits(n, string, valueCallback) {\n switch (n) {\n case 1:\n return parseNumericPattern(numericPatterns.singleDigit, string, valueCallback);\n\n case 2:\n return parseNumericPattern(numericPatterns.twoDigits, string, valueCallback);\n\n case 3:\n return parseNumericPattern(numericPatterns.threeDigits, string, valueCallback);\n\n case 4:\n return parseNumericPattern(numericPatterns.fourDigits, string, valueCallback);\n\n default:\n return parseNumericPattern(new RegExp('^\\\\d{1,' + n + '}'), string, valueCallback);\n }\n}\n\nfunction parseNDigitsSigned(n, string, valueCallback) {\n switch (n) {\n case 1:\n return parseNumericPattern(numericPatterns.singleDigitSigned, string, valueCallback);\n\n case 2:\n return parseNumericPattern(numericPatterns.twoDigitsSigned, string, valueCallback);\n\n case 3:\n return parseNumericPattern(numericPatterns.threeDigitsSigned, string, valueCallback);\n\n case 4:\n return parseNumericPattern(numericPatterns.fourDigitsSigned, string, valueCallback);\n\n default:\n return parseNumericPattern(new RegExp('^-?\\\\d{1,' + n + '}'), string, valueCallback);\n }\n}\n\nfunction dayPeriodEnumToHours(enumValue) {\n switch (enumValue) {\n case 'morning':\n return 4;\n\n case 'evening':\n return 17;\n\n case 'pm':\n case 'noon':\n case 'afternoon':\n return 12;\n\n case 'am':\n case 'midnight':\n case 'night':\n default:\n return 0;\n }\n}\n\nfunction normalizeTwoDigitYear(twoDigitYear, currentYear) {\n var isCommonEra = currentYear > 0; // Absolute number of the current year:\n // 1 -> 1 AC\n // 0 -> 1 BC\n // -1 -> 2 BC\n\n var absCurrentYear = isCommonEra ? currentYear : 1 - currentYear;\n var result;\n\n if (absCurrentYear <= 50) {\n result = twoDigitYear || 100;\n } else {\n var rangeEnd = absCurrentYear + 50;\n var rangeEndCentury = Math.floor(rangeEnd / 100) * 100;\n var isPreviousCentury = twoDigitYear >= rangeEnd % 100;\n result = twoDigitYear + rangeEndCentury - (isPreviousCentury ? 100 : 0);\n }\n\n return isCommonEra ? result : 1 - result;\n}\n\nvar DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\nvar DAYS_IN_MONTH_LEAP_YEAR = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; // User for validation\n\nfunction isLeapYearIndex(year) {\n return year % 400 === 0 || year % 4 === 0 && year % 100 !== 0;\n}\n/*\n * | | Unit | | Unit |\n * |-----|--------------------------------|-----|--------------------------------|\n * | a | AM, PM | A* | Milliseconds in day |\n * | b | AM, PM, noon, midnight | B | Flexible day period |\n * | c | Stand-alone local day of week | C* | Localized hour w/ day period |\n * | d | Day of month | D | Day of year |\n * | e | Local day of week | E | Day of week |\n * | f | | F* | Day of week in month |\n * | g* | Modified Julian day | G | Era |\n * | h | Hour [1-12] | H | Hour [0-23] |\n * | i! | ISO day of week | I! | ISO week of year |\n * | j* | Localized hour w/ day period | J* | Localized hour w/o day period |\n * | k | Hour [1-24] | K | Hour [0-11] |\n * | l* | (deprecated) | L | Stand-alone month |\n * | m | Minute | M | Month |\n * | n | | N | |\n * | o! | Ordinal number modifier | O* | Timezone (GMT) |\n * | p | | P | |\n * | q | Stand-alone quarter | Q | Quarter |\n * | r* | Related Gregorian year | R! | ISO week-numbering year |\n * | s | Second | S | Fraction of second |\n * | t! | Seconds timestamp | T! | Milliseconds timestamp |\n * | u | Extended year | U* | Cyclic year |\n * | v* | Timezone (generic non-locat.) | V* | Timezone (location) |\n * | w | Local week of year | W* | Week of month |\n * | x | Timezone (ISO-8601 w/o Z) | X | Timezone (ISO-8601) |\n * | y | Year (abs) | Y | Local week-numbering year |\n * | z* | Timezone (specific non-locat.) | Z* | Timezone (aliases) |\n *\n * Letters marked by * are not implemented but reserved by Unicode standard.\n *\n * Letters marked by ! are non-standard, but implemented by date-fns:\n * - `o` modifies the previous token to turn it into an ordinal (see `parse` docs)\n * - `i` is ISO day of week. For `i` and `ii` is returns numeric ISO week days,\n * i.e. 7 for Sunday, 1 for Monday, etc.\n * - `I` is ISO week of year, as opposed to `w` which is local week of year.\n * - `R` is ISO week-numbering year, as opposed to `Y` which is local week-numbering year.\n * `R` is supposed to be used in conjunction with `I` and `i`\n * for universal ISO week-numbering date, whereas\n * `Y` is supposed to be used in conjunction with `w` and `e`\n * for week-numbering date specific to the locale.\n */\n\n\nvar parsers = {\n // Era\n G: {\n priority: 140,\n parse: function (string, token, match, _options) {\n switch (token) {\n // AD, BC\n case 'G':\n case 'GG':\n case 'GGG':\n return match.era(string, {\n width: 'abbreviated'\n }) || match.era(string, {\n width: 'narrow'\n });\n // A, B\n\n case 'GGGGG':\n return match.era(string, {\n width: 'narrow'\n });\n // Anno Domini, Before Christ\n\n case 'GGGG':\n default:\n return match.era(string, {\n width: 'wide'\n }) || match.era(string, {\n width: 'abbreviated'\n }) || match.era(string, {\n width: 'narrow'\n });\n }\n },\n set: function (date, flags, value, _options) {\n flags.era = value;\n date.setUTCFullYear(value, 0, 1);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['R', 'u', 't', 'T']\n },\n // Year\n y: {\n // From http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns\n // | Year | y | yy | yyy | yyyy | yyyyy |\n // |----------|-------|----|-------|-------|-------|\n // | AD 1 | 1 | 01 | 001 | 0001 | 00001 |\n // | AD 12 | 12 | 12 | 012 | 0012 | 00012 |\n // | AD 123 | 123 | 23 | 123 | 0123 | 00123 |\n // | AD 1234 | 1234 | 34 | 1234 | 1234 | 01234 |\n // | AD 12345 | 12345 | 45 | 12345 | 12345 | 12345 |\n priority: 130,\n parse: function (string, token, match, _options) {\n var valueCallback = function (year) {\n return {\n year: year,\n isTwoDigitYear: token === 'yy'\n };\n };\n\n switch (token) {\n case 'y':\n return parseNDigits(4, string, valueCallback);\n\n case 'yo':\n return match.ordinalNumber(string, {\n unit: 'year',\n valueCallback: valueCallback\n });\n\n default:\n return parseNDigits(token.length, string, valueCallback);\n }\n },\n validate: function (_date, value, _options) {\n return value.isTwoDigitYear || value.year > 0;\n },\n set: function (date, flags, value, _options) {\n var currentYear = date.getUTCFullYear();\n\n if (value.isTwoDigitYear) {\n var normalizedTwoDigitYear = normalizeTwoDigitYear(value.year, currentYear);\n date.setUTCFullYear(normalizedTwoDigitYear, 0, 1);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n }\n\n var year = !('era' in flags) || flags.era === 1 ? value.year : 1 - value.year;\n date.setUTCFullYear(year, 0, 1);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['Y', 'R', 'u', 'w', 'I', 'i', 'e', 'c', 't', 'T']\n },\n // Local week-numbering year\n Y: {\n priority: 130,\n parse: function (string, token, match, _options) {\n var valueCallback = function (year) {\n return {\n year: year,\n isTwoDigitYear: token === 'YY'\n };\n };\n\n switch (token) {\n case 'Y':\n return parseNDigits(4, string, valueCallback);\n\n case 'Yo':\n return match.ordinalNumber(string, {\n unit: 'year',\n valueCallback: valueCallback\n });\n\n default:\n return parseNDigits(token.length, string, valueCallback);\n }\n },\n validate: function (_date, value, _options) {\n return value.isTwoDigitYear || value.year > 0;\n },\n set: function (date, flags, value, options) {\n var currentYear = getUTCWeekYear(date, options);\n\n if (value.isTwoDigitYear) {\n var normalizedTwoDigitYear = normalizeTwoDigitYear(value.year, currentYear);\n date.setUTCFullYear(normalizedTwoDigitYear, 0, options.firstWeekContainsDate);\n date.setUTCHours(0, 0, 0, 0);\n return startOfUTCWeek(date, options);\n }\n\n var year = !('era' in flags) || flags.era === 1 ? value.year : 1 - value.year;\n date.setUTCFullYear(year, 0, options.firstWeekContainsDate);\n date.setUTCHours(0, 0, 0, 0);\n return startOfUTCWeek(date, options);\n },\n incompatibleTokens: ['y', 'R', 'u', 'Q', 'q', 'M', 'L', 'I', 'd', 'D', 'i', 't', 'T']\n },\n // ISO week-numbering year\n R: {\n priority: 130,\n parse: function (string, token, _match, _options) {\n if (token === 'R') {\n return parseNDigitsSigned(4, string);\n }\n\n return parseNDigitsSigned(token.length, string);\n },\n set: function (_date, _flags, value, _options) {\n var firstWeekOfYear = new Date(0);\n firstWeekOfYear.setUTCFullYear(value, 0, 4);\n firstWeekOfYear.setUTCHours(0, 0, 0, 0);\n return startOfUTCISOWeek(firstWeekOfYear);\n },\n incompatibleTokens: ['G', 'y', 'Y', 'u', 'Q', 'q', 'M', 'L', 'w', 'd', 'D', 'e', 'c', 't', 'T']\n },\n // Extended year\n u: {\n priority: 130,\n parse: function (string, token, _match, _options) {\n if (token === 'u') {\n return parseNDigitsSigned(4, string);\n }\n\n return parseNDigitsSigned(token.length, string);\n },\n set: function (date, _flags, value, _options) {\n date.setUTCFullYear(value, 0, 1);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['G', 'y', 'Y', 'R', 'w', 'I', 'i', 'e', 'c', 't', 'T']\n },\n // Quarter\n Q: {\n priority: 120,\n parse: function (string, token, match, _options) {\n switch (token) {\n // 1, 2, 3, 4\n case 'Q':\n case 'QQ':\n // 01, 02, 03, 04\n return parseNDigits(token.length, string);\n // 1st, 2nd, 3rd, 4th\n\n case 'Qo':\n return match.ordinalNumber(string, {\n unit: 'quarter'\n });\n // Q1, Q2, Q3, Q4\n\n case 'QQQ':\n return match.quarter(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.quarter(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // 1, 2, 3, 4 (narrow quarter; could be not numerical)\n\n case 'QQQQQ':\n return match.quarter(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // 1st quarter, 2nd quarter, ...\n\n case 'QQQQ':\n default:\n return match.quarter(string, {\n width: 'wide',\n context: 'formatting'\n }) || match.quarter(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.quarter(string, {\n width: 'narrow',\n context: 'formatting'\n });\n }\n },\n validate: function (_date, value, _options) {\n return value >= 1 && value <= 4;\n },\n set: function (date, _flags, value, _options) {\n date.setUTCMonth((value - 1) * 3, 1);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['Y', 'R', 'q', 'M', 'L', 'w', 'I', 'd', 'D', 'i', 'e', 'c', 't', 'T']\n },\n // Stand-alone quarter\n q: {\n priority: 120,\n parse: function (string, token, match, _options) {\n switch (token) {\n // 1, 2, 3, 4\n case 'q':\n case 'qq':\n // 01, 02, 03, 04\n return parseNDigits(token.length, string);\n // 1st, 2nd, 3rd, 4th\n\n case 'qo':\n return match.ordinalNumber(string, {\n unit: 'quarter'\n });\n // Q1, Q2, Q3, Q4\n\n case 'qqq':\n return match.quarter(string, {\n width: 'abbreviated',\n context: 'standalone'\n }) || match.quarter(string, {\n width: 'narrow',\n context: 'standalone'\n });\n // 1, 2, 3, 4 (narrow quarter; could be not numerical)\n\n case 'qqqqq':\n return match.quarter(string, {\n width: 'narrow',\n context: 'standalone'\n });\n // 1st quarter, 2nd quarter, ...\n\n case 'qqqq':\n default:\n return match.quarter(string, {\n width: 'wide',\n context: 'standalone'\n }) || match.quarter(string, {\n width: 'abbreviated',\n context: 'standalone'\n }) || match.quarter(string, {\n width: 'narrow',\n context: 'standalone'\n });\n }\n },\n validate: function (_date, value, _options) {\n return value >= 1 && value <= 4;\n },\n set: function (date, _flags, value, _options) {\n date.setUTCMonth((value - 1) * 3, 1);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['Y', 'R', 'Q', 'M', 'L', 'w', 'I', 'd', 'D', 'i', 'e', 'c', 't', 'T']\n },\n // Month\n M: {\n priority: 110,\n parse: function (string, token, match, _options) {\n var valueCallback = function (value) {\n return value - 1;\n };\n\n switch (token) {\n // 1, 2, ..., 12\n case 'M':\n return parseNumericPattern(numericPatterns.month, string, valueCallback);\n // 01, 02, ..., 12\n\n case 'MM':\n return parseNDigits(2, string, valueCallback);\n // 1st, 2nd, ..., 12th\n\n case 'Mo':\n return match.ordinalNumber(string, {\n unit: 'month',\n valueCallback: valueCallback\n });\n // Jan, Feb, ..., Dec\n\n case 'MMM':\n return match.month(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.month(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // J, F, ..., D\n\n case 'MMMMM':\n return match.month(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // January, February, ..., December\n\n case 'MMMM':\n default:\n return match.month(string, {\n width: 'wide',\n context: 'formatting'\n }) || match.month(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.month(string, {\n width: 'narrow',\n context: 'formatting'\n });\n }\n },\n validate: function (_date, value, _options) {\n return value >= 0 && value <= 11;\n },\n set: function (date, _flags, value, _options) {\n date.setUTCMonth(value, 1);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['Y', 'R', 'q', 'Q', 'L', 'w', 'I', 'D', 'i', 'e', 'c', 't', 'T']\n },\n // Stand-alone month\n L: {\n priority: 110,\n parse: function (string, token, match, _options) {\n var valueCallback = function (value) {\n return value - 1;\n };\n\n switch (token) {\n // 1, 2, ..., 12\n case 'L':\n return parseNumericPattern(numericPatterns.month, string, valueCallback);\n // 01, 02, ..., 12\n\n case 'LL':\n return parseNDigits(2, string, valueCallback);\n // 1st, 2nd, ..., 12th\n\n case 'Lo':\n return match.ordinalNumber(string, {\n unit: 'month',\n valueCallback: valueCallback\n });\n // Jan, Feb, ..., Dec\n\n case 'LLL':\n return match.month(string, {\n width: 'abbreviated',\n context: 'standalone'\n }) || match.month(string, {\n width: 'narrow',\n context: 'standalone'\n });\n // J, F, ..., D\n\n case 'LLLLL':\n return match.month(string, {\n width: 'narrow',\n context: 'standalone'\n });\n // January, February, ..., December\n\n case 'LLLL':\n default:\n return match.month(string, {\n width: 'wide',\n context: 'standalone'\n }) || match.month(string, {\n width: 'abbreviated',\n context: 'standalone'\n }) || match.month(string, {\n width: 'narrow',\n context: 'standalone'\n });\n }\n },\n validate: function (_date, value, _options) {\n return value >= 0 && value <= 11;\n },\n set: function (date, _flags, value, _options) {\n date.setUTCMonth(value, 1);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['Y', 'R', 'q', 'Q', 'M', 'w', 'I', 'D', 'i', 'e', 'c', 't', 'T']\n },\n // Local week of year\n w: {\n priority: 100,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'w':\n return parseNumericPattern(numericPatterns.week, string);\n\n case 'wo':\n return match.ordinalNumber(string, {\n unit: 'week'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (_date, value, _options) {\n return value >= 1 && value <= 53;\n },\n set: function (date, _flags, value, options) {\n return startOfUTCWeek(setUTCWeek(date, value, options), options);\n },\n incompatibleTokens: ['y', 'R', 'u', 'q', 'Q', 'M', 'L', 'I', 'd', 'D', 'i', 't', 'T']\n },\n // ISO week of year\n I: {\n priority: 100,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'I':\n return parseNumericPattern(numericPatterns.week, string);\n\n case 'Io':\n return match.ordinalNumber(string, {\n unit: 'week'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (_date, value, _options) {\n return value >= 1 && value <= 53;\n },\n set: function (date, _flags, value, options) {\n return startOfUTCISOWeek(setUTCISOWeek(date, value, options), options);\n },\n incompatibleTokens: ['y', 'Y', 'u', 'q', 'Q', 'M', 'L', 'w', 'd', 'D', 'e', 'c', 't', 'T']\n },\n // Day of the month\n d: {\n priority: 90,\n subPriority: 1,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'd':\n return parseNumericPattern(numericPatterns.date, string);\n\n case 'do':\n return match.ordinalNumber(string, {\n unit: 'date'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (date, value, _options) {\n var year = date.getUTCFullYear();\n var isLeapYear = isLeapYearIndex(year);\n var month = date.getUTCMonth();\n\n if (isLeapYear) {\n return value >= 1 && value <= DAYS_IN_MONTH_LEAP_YEAR[month];\n } else {\n return value >= 1 && value <= DAYS_IN_MONTH[month];\n }\n },\n set: function (date, _flags, value, _options) {\n date.setUTCDate(value);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['Y', 'R', 'q', 'Q', 'w', 'I', 'D', 'i', 'e', 'c', 't', 'T']\n },\n // Day of year\n D: {\n priority: 90,\n subPriority: 1,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'D':\n case 'DD':\n return parseNumericPattern(numericPatterns.dayOfYear, string);\n\n case 'Do':\n return match.ordinalNumber(string, {\n unit: 'date'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (date, value, _options) {\n var year = date.getUTCFullYear();\n var isLeapYear = isLeapYearIndex(year);\n\n if (isLeapYear) {\n return value >= 1 && value <= 366;\n } else {\n return value >= 1 && value <= 365;\n }\n },\n set: function (date, _flags, value, _options) {\n date.setUTCMonth(0, value);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['Y', 'R', 'q', 'Q', 'M', 'L', 'w', 'I', 'd', 'E', 'i', 'e', 'c', 't', 'T']\n },\n // Day of week\n E: {\n priority: 90,\n parse: function (string, token, match, _options) {\n switch (token) {\n // Tue\n case 'E':\n case 'EE':\n case 'EEE':\n return match.day(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.day(string, {\n width: 'short',\n context: 'formatting'\n }) || match.day(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // T\n\n case 'EEEEE':\n return match.day(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // Tu\n\n case 'EEEEEE':\n return match.day(string, {\n width: 'short',\n context: 'formatting'\n }) || match.day(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // Tuesday\n\n case 'EEEE':\n default:\n return match.day(string, {\n width: 'wide',\n context: 'formatting'\n }) || match.day(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.day(string, {\n width: 'short',\n context: 'formatting'\n }) || match.day(string, {\n width: 'narrow',\n context: 'formatting'\n });\n }\n },\n validate: function (_date, value, _options) {\n return value >= 0 && value <= 6;\n },\n set: function (date, _flags, value, options) {\n date = setUTCDay(date, value, options);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['D', 'i', 'e', 'c', 't', 'T']\n },\n // Local day of week\n e: {\n priority: 90,\n parse: function (string, token, match, options) {\n var valueCallback = function (value) {\n var wholeWeekDays = Math.floor((value - 1) / 7) * 7;\n return (value + options.weekStartsOn + 6) % 7 + wholeWeekDays;\n };\n\n switch (token) {\n // 3\n case 'e':\n case 'ee':\n // 03\n return parseNDigits(token.length, string, valueCallback);\n // 3rd\n\n case 'eo':\n return match.ordinalNumber(string, {\n unit: 'day',\n valueCallback: valueCallback\n });\n // Tue\n\n case 'eee':\n return match.day(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.day(string, {\n width: 'short',\n context: 'formatting'\n }) || match.day(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // T\n\n case 'eeeee':\n return match.day(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // Tu\n\n case 'eeeeee':\n return match.day(string, {\n width: 'short',\n context: 'formatting'\n }) || match.day(string, {\n width: 'narrow',\n context: 'formatting'\n });\n // Tuesday\n\n case 'eeee':\n default:\n return match.day(string, {\n width: 'wide',\n context: 'formatting'\n }) || match.day(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.day(string, {\n width: 'short',\n context: 'formatting'\n }) || match.day(string, {\n width: 'narrow',\n context: 'formatting'\n });\n }\n },\n validate: function (_date, value, _options) {\n return value >= 0 && value <= 6;\n },\n set: function (date, _flags, value, options) {\n date = setUTCDay(date, value, options);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['y', 'R', 'u', 'q', 'Q', 'M', 'L', 'I', 'd', 'D', 'E', 'i', 'c', 't', 'T']\n },\n // Stand-alone local day of week\n c: {\n priority: 90,\n parse: function (string, token, match, options) {\n var valueCallback = function (value) {\n var wholeWeekDays = Math.floor((value - 1) / 7) * 7;\n return (value + options.weekStartsOn + 6) % 7 + wholeWeekDays;\n };\n\n switch (token) {\n // 3\n case 'c':\n case 'cc':\n // 03\n return parseNDigits(token.length, string, valueCallback);\n // 3rd\n\n case 'co':\n return match.ordinalNumber(string, {\n unit: 'day',\n valueCallback: valueCallback\n });\n // Tue\n\n case 'ccc':\n return match.day(string, {\n width: 'abbreviated',\n context: 'standalone'\n }) || match.day(string, {\n width: 'short',\n context: 'standalone'\n }) || match.day(string, {\n width: 'narrow',\n context: 'standalone'\n });\n // T\n\n case 'ccccc':\n return match.day(string, {\n width: 'narrow',\n context: 'standalone'\n });\n // Tu\n\n case 'cccccc':\n return match.day(string, {\n width: 'short',\n context: 'standalone'\n }) || match.day(string, {\n width: 'narrow',\n context: 'standalone'\n });\n // Tuesday\n\n case 'cccc':\n default:\n return match.day(string, {\n width: 'wide',\n context: 'standalone'\n }) || match.day(string, {\n width: 'abbreviated',\n context: 'standalone'\n }) || match.day(string, {\n width: 'short',\n context: 'standalone'\n }) || match.day(string, {\n width: 'narrow',\n context: 'standalone'\n });\n }\n },\n validate: function (_date, value, _options) {\n return value >= 0 && value <= 6;\n },\n set: function (date, _flags, value, options) {\n date = setUTCDay(date, value, options);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['y', 'R', 'u', 'q', 'Q', 'M', 'L', 'I', 'd', 'D', 'E', 'i', 'e', 't', 'T']\n },\n // ISO day of week\n i: {\n priority: 90,\n parse: function (string, token, match, _options) {\n var valueCallback = function (value) {\n if (value === 0) {\n return 7;\n }\n\n return value;\n };\n\n switch (token) {\n // 2\n case 'i':\n case 'ii':\n // 02\n return parseNDigits(token.length, string);\n // 2nd\n\n case 'io':\n return match.ordinalNumber(string, {\n unit: 'day'\n });\n // Tue\n\n case 'iii':\n return match.day(string, {\n width: 'abbreviated',\n context: 'formatting',\n valueCallback: valueCallback\n }) || match.day(string, {\n width: 'short',\n context: 'formatting',\n valueCallback: valueCallback\n }) || match.day(string, {\n width: 'narrow',\n context: 'formatting',\n valueCallback: valueCallback\n });\n // T\n\n case 'iiiii':\n return match.day(string, {\n width: 'narrow',\n context: 'formatting',\n valueCallback: valueCallback\n });\n // Tu\n\n case 'iiiiii':\n return match.day(string, {\n width: 'short',\n context: 'formatting',\n valueCallback: valueCallback\n }) || match.day(string, {\n width: 'narrow',\n context: 'formatting',\n valueCallback: valueCallback\n });\n // Tuesday\n\n case 'iiii':\n default:\n return match.day(string, {\n width: 'wide',\n context: 'formatting',\n valueCallback: valueCallback\n }) || match.day(string, {\n width: 'abbreviated',\n context: 'formatting',\n valueCallback: valueCallback\n }) || match.day(string, {\n width: 'short',\n context: 'formatting',\n valueCallback: valueCallback\n }) || match.day(string, {\n width: 'narrow',\n context: 'formatting',\n valueCallback: valueCallback\n });\n }\n },\n validate: function (_date, value, _options) {\n return value >= 1 && value <= 7;\n },\n set: function (date, _flags, value, options) {\n date = setUTCISODay(date, value, options);\n date.setUTCHours(0, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['y', 'Y', 'u', 'q', 'Q', 'M', 'L', 'w', 'd', 'D', 'E', 'e', 'c', 't', 'T']\n },\n // AM or PM\n a: {\n priority: 80,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'a':\n case 'aa':\n case 'aaa':\n return match.dayPeriod(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.dayPeriod(string, {\n width: 'narrow',\n context: 'formatting'\n });\n\n case 'aaaaa':\n return match.dayPeriod(string, {\n width: 'narrow',\n context: 'formatting'\n });\n\n case 'aaaa':\n default:\n return match.dayPeriod(string, {\n width: 'wide',\n context: 'formatting'\n }) || match.dayPeriod(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.dayPeriod(string, {\n width: 'narrow',\n context: 'formatting'\n });\n }\n },\n set: function (date, _flags, value, _options) {\n date.setUTCHours(dayPeriodEnumToHours(value), 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['b', 'B', 'H', 'K', 'k', 't', 'T']\n },\n // AM, PM, midnight\n b: {\n priority: 80,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'b':\n case 'bb':\n case 'bbb':\n return match.dayPeriod(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.dayPeriod(string, {\n width: 'narrow',\n context: 'formatting'\n });\n\n case 'bbbbb':\n return match.dayPeriod(string, {\n width: 'narrow',\n context: 'formatting'\n });\n\n case 'bbbb':\n default:\n return match.dayPeriod(string, {\n width: 'wide',\n context: 'formatting'\n }) || match.dayPeriod(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.dayPeriod(string, {\n width: 'narrow',\n context: 'formatting'\n });\n }\n },\n set: function (date, _flags, value, _options) {\n date.setUTCHours(dayPeriodEnumToHours(value), 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['a', 'B', 'H', 'K', 'k', 't', 'T']\n },\n // in the morning, in the afternoon, in the evening, at night\n B: {\n priority: 80,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'B':\n case 'BB':\n case 'BBB':\n return match.dayPeriod(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.dayPeriod(string, {\n width: 'narrow',\n context: 'formatting'\n });\n\n case 'BBBBB':\n return match.dayPeriod(string, {\n width: 'narrow',\n context: 'formatting'\n });\n\n case 'BBBB':\n default:\n return match.dayPeriod(string, {\n width: 'wide',\n context: 'formatting'\n }) || match.dayPeriod(string, {\n width: 'abbreviated',\n context: 'formatting'\n }) || match.dayPeriod(string, {\n width: 'narrow',\n context: 'formatting'\n });\n }\n },\n set: function (date, _flags, value, _options) {\n date.setUTCHours(dayPeriodEnumToHours(value), 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['a', 'b', 't', 'T']\n },\n // Hour [1-12]\n h: {\n priority: 70,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'h':\n return parseNumericPattern(numericPatterns.hour12h, string);\n\n case 'ho':\n return match.ordinalNumber(string, {\n unit: 'hour'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (_date, value, _options) {\n return value >= 1 && value <= 12;\n },\n set: function (date, _flags, value, _options) {\n var isPM = date.getUTCHours() >= 12;\n\n if (isPM && value < 12) {\n date.setUTCHours(value + 12, 0, 0, 0);\n } else if (!isPM && value === 12) {\n date.setUTCHours(0, 0, 0, 0);\n } else {\n date.setUTCHours(value, 0, 0, 0);\n }\n\n return date;\n },\n incompatibleTokens: ['H', 'K', 'k', 't', 'T']\n },\n // Hour [0-23]\n H: {\n priority: 70,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'H':\n return parseNumericPattern(numericPatterns.hour23h, string);\n\n case 'Ho':\n return match.ordinalNumber(string, {\n unit: 'hour'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (_date, value, _options) {\n return value >= 0 && value <= 23;\n },\n set: function (date, _flags, value, _options) {\n date.setUTCHours(value, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['a', 'b', 'h', 'K', 'k', 't', 'T']\n },\n // Hour [0-11]\n K: {\n priority: 70,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'K':\n return parseNumericPattern(numericPatterns.hour11h, string);\n\n case 'Ko':\n return match.ordinalNumber(string, {\n unit: 'hour'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (_date, value, _options) {\n return value >= 0 && value <= 11;\n },\n set: function (date, _flags, value, _options) {\n var isPM = date.getUTCHours() >= 12;\n\n if (isPM && value < 12) {\n date.setUTCHours(value + 12, 0, 0, 0);\n } else {\n date.setUTCHours(value, 0, 0, 0);\n }\n\n return date;\n },\n incompatibleTokens: ['a', 'b', 'h', 'H', 'k', 't', 'T']\n },\n // Hour [1-24]\n k: {\n priority: 70,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'k':\n return parseNumericPattern(numericPatterns.hour24h, string);\n\n case 'ko':\n return match.ordinalNumber(string, {\n unit: 'hour'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (_date, value, _options) {\n return value >= 1 && value <= 24;\n },\n set: function (date, _flags, value, _options) {\n var hours = value <= 24 ? value % 24 : value;\n date.setUTCHours(hours, 0, 0, 0);\n return date;\n },\n incompatibleTokens: ['a', 'b', 'h', 'H', 'K', 't', 'T']\n },\n // Minute\n m: {\n priority: 60,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 'm':\n return parseNumericPattern(numericPatterns.minute, string);\n\n case 'mo':\n return match.ordinalNumber(string, {\n unit: 'minute'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (_date, value, _options) {\n return value >= 0 && value <= 59;\n },\n set: function (date, _flags, value, _options) {\n date.setUTCMinutes(value, 0, 0);\n return date;\n },\n incompatibleTokens: ['t', 'T']\n },\n // Second\n s: {\n priority: 50,\n parse: function (string, token, match, _options) {\n switch (token) {\n case 's':\n return parseNumericPattern(numericPatterns.second, string);\n\n case 'so':\n return match.ordinalNumber(string, {\n unit: 'second'\n });\n\n default:\n return parseNDigits(token.length, string);\n }\n },\n validate: function (_date, value, _options) {\n return value >= 0 && value <= 59;\n },\n set: function (date, _flags, value, _options) {\n date.setUTCSeconds(value, 0);\n return date;\n },\n incompatibleTokens: ['t', 'T']\n },\n // Fraction of second\n S: {\n priority: 30,\n parse: function (string, token, _match, _options) {\n var valueCallback = function (value) {\n return Math.floor(value * Math.pow(10, -token.length + 3));\n };\n\n return parseNDigits(token.length, string, valueCallback);\n },\n set: function (date, _flags, value, _options) {\n date.setUTCMilliseconds(value);\n return date;\n },\n incompatibleTokens: ['t', 'T']\n },\n // Timezone (ISO-8601. +00:00 is `'Z'`)\n X: {\n priority: 10,\n parse: function (string, token, _match, _options) {\n switch (token) {\n case 'X':\n return parseTimezonePattern(timezonePatterns.basicOptionalMinutes, string);\n\n case 'XX':\n return parseTimezonePattern(timezonePatterns.basic, string);\n\n case 'XXXX':\n return parseTimezonePattern(timezonePatterns.basicOptionalSeconds, string);\n\n case 'XXXXX':\n return parseTimezonePattern(timezonePatterns.extendedOptionalSeconds, string);\n\n case 'XXX':\n default:\n return parseTimezonePattern(timezonePatterns.extended, string);\n }\n },\n set: function (date, flags, value, _options) {\n if (flags.timestampIsSet) {\n return date;\n }\n\n return new Date(date.getTime() - value);\n },\n incompatibleTokens: ['t', 'T', 'x']\n },\n // Timezone (ISO-8601)\n x: {\n priority: 10,\n parse: function (string, token, _match, _options) {\n switch (token) {\n case 'x':\n return parseTimezonePattern(timezonePatterns.basicOptionalMinutes, string);\n\n case 'xx':\n return parseTimezonePattern(timezonePatterns.basic, string);\n\n case 'xxxx':\n return parseTimezonePattern(timezonePatterns.basicOptionalSeconds, string);\n\n case 'xxxxx':\n return parseTimezonePattern(timezonePatterns.extendedOptionalSeconds, string);\n\n case 'xxx':\n default:\n return parseTimezonePattern(timezonePatterns.extended, string);\n }\n },\n set: function (date, flags, value, _options) {\n if (flags.timestampIsSet) {\n return date;\n }\n\n return new Date(date.getTime() - value);\n },\n incompatibleTokens: ['t', 'T', 'X']\n },\n // Seconds timestamp\n t: {\n priority: 40,\n parse: function (string, _token, _match, _options) {\n return parseAnyDigitsSigned(string);\n },\n set: function (_date, _flags, value, _options) {\n return [new Date(value * 1000), {\n timestampIsSet: true\n }];\n },\n incompatibleTokens: '*'\n },\n // Milliseconds timestamp\n T: {\n priority: 20,\n parse: function (string, _token, _match, _options) {\n return parseAnyDigitsSigned(string);\n },\n set: function (_date, _flags, value, _options) {\n return [new Date(value), {\n timestampIsSet: true\n }];\n },\n incompatibleTokens: '*'\n }\n};\nexport default parsers;","import toInteger from \"../toInteger/index.js\";\nimport toDate from \"../../toDate/index.js\";\nimport getUTCWeek from \"../getUTCWeek/index.js\";\nimport requiredArgs from \"../requiredArgs/index.js\"; // This function will be a part of public API when UTC function will be implemented.\n// See issue: https://github.com/date-fns/date-fns/issues/376\n\nexport default function setUTCWeek(dirtyDate, dirtyWeek, options) {\n requiredArgs(2, arguments);\n var date = toDate(dirtyDate);\n var week = toInteger(dirtyWeek);\n var diff = getUTCWeek(date, options) - week;\n date.setUTCDate(date.getUTCDate() - diff * 7);\n return date;\n}","import toInteger from \"../toInteger/index.js\";\nimport toDate from \"../../toDate/index.js\";\nimport getUTCISOWeek from \"../getUTCISOWeek/index.js\";\nimport requiredArgs from \"../requiredArgs/index.js\"; // This function will be a part of public API when UTC function will be implemented.\n// See issue: https://github.com/date-fns/date-fns/issues/376\n\nexport default function setUTCISOWeek(dirtyDate, dirtyISOWeek) {\n requiredArgs(2, arguments);\n var date = toDate(dirtyDate);\n var isoWeek = toInteger(dirtyISOWeek);\n var diff = getUTCISOWeek(date) - isoWeek;\n date.setUTCDate(date.getUTCDate() - diff * 7);\n return date;\n}","import toInteger from \"../toInteger/index.js\";\nimport toDate from \"../../toDate/index.js\";\nimport requiredArgs from \"../requiredArgs/index.js\"; // This function will be a part of public API when UTC function will be implemented.\n// See issue: https://github.com/date-fns/date-fns/issues/376\n\nexport default function setUTCISODay(dirtyDate, dirtyDay) {\n requiredArgs(2, arguments);\n var day = toInteger(dirtyDay);\n\n if (day % 7 === 0) {\n day = day - 7;\n }\n\n var weekStartsOn = 1;\n var date = toDate(dirtyDate);\n var currentDay = date.getUTCDay();\n var remainder = day % 7;\n var dayIndex = (remainder + 7) % 7;\n var diff = (dayIndex < weekStartsOn ? 7 : 0) + day - currentDay;\n date.setUTCDate(date.getUTCDate() + diff);\n return date;\n}","import defaultLocale from \"../locale/en-US/index.js\";\nimport subMilliseconds from \"../subMilliseconds/index.js\";\nimport toDate from \"../toDate/index.js\";\nimport assign from \"../_lib/assign/index.js\";\nimport longFormatters from \"../_lib/format/longFormatters/index.js\";\nimport getTimezoneOffsetInMilliseconds from \"../_lib/getTimezoneOffsetInMilliseconds/index.js\";\nimport { isProtectedDayOfYearToken, isProtectedWeekYearToken, throwProtectedError } from \"../_lib/protectedTokens/index.js\";\nimport toInteger from \"../_lib/toInteger/index.js\";\nimport parsers from \"./_lib/parsers/index.js\";\nimport requiredArgs from \"../_lib/requiredArgs/index.js\";\nvar TIMEZONE_UNIT_PRIORITY = 10; // This RegExp consists of three parts separated by `|`:\n// - [yYQqMLwIdDecihHKkms]o matches any available ordinal number token\n// (one of the certain letters followed by `o`)\n// - (\\w)\\1* matches any sequences of the same letter\n// - '' matches two quote characters in a row\n// - '(''|[^'])+('|$) matches anything surrounded by two quote characters ('),\n// except a single quote symbol, which ends the sequence.\n// Two quote characters do not end the sequence.\n// If there is no matching single quote\n// then the sequence will continue until the end of the string.\n// - . matches any single character unmatched by previous parts of the RegExps\n\nvar formattingTokensRegExp = /[yYQqMLwIdDecihHKkms]o|(\\w)\\1*|''|'(''|[^'])+('|$)|./g; // This RegExp catches symbols escaped by quotes, and also\n// sequences of symbols P, p, and the combinations like `PPPPPPPppppp`\n\nvar longFormattingTokensRegExp = /P+p+|P+|p+|''|'(''|[^'])+('|$)|./g;\nvar escapedStringRegExp = /^'([^]*?)'?$/;\nvar doubleQuoteRegExp = /''/g;\nvar notWhitespaceRegExp = /\\S/;\nvar unescapedLatinCharacterRegExp = /[a-zA-Z]/;\n/**\n * @name parse\n * @category Common Helpers\n * @summary Parse the date.\n *\n * @description\n * Return the date parsed from string using the given format string.\n *\n * > ⚠️ Please note that the `format` tokens differ from Moment.js and other libraries.\n * > See: https://git.io/fxCyr\n *\n * The characters in the format string wrapped between two single quotes characters (') are escaped.\n * Two single quotes in a row, whether inside or outside a quoted sequence, represent a 'real' single quote.\n *\n * Format of the format string is based on Unicode Technical Standard #35:\n * https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table\n * with a few additions (see note 5 below the table).\n *\n * Not all tokens are compatible. Combinations that don't make sense or could lead to bugs are prohibited\n * and will throw `RangeError`. For example usage of 24-hour format token with AM/PM token will throw an exception:\n *\n * ```javascript\n * parse('23 AM', 'HH a', new Date())\n * //=> RangeError: The format string mustn't contain `HH` and `a` at the same time\n * ```\n *\n * See the compatibility table: https://docs.google.com/spreadsheets/d/e/2PACX-1vQOPU3xUhplll6dyoMmVUXHKl_8CRDs6_ueLmex3SoqwhuolkuN3O05l4rqx5h1dKX8eb46Ul-CCSrq/pubhtml?gid=0&single=true\n *\n * Accepted format string patterns:\n * | Unit |Prior| Pattern | Result examples | Notes |\n * |---------------------------------|-----|---------|-----------------------------------|-------|\n * | Era | 140 | G..GGG | AD, BC | |\n * | | | GGGG | Anno Domini, Before Christ | 2 |\n * | | | GGGGG | A, B | |\n * | Calendar year | 130 | y | 44, 1, 1900, 2017, 9999 | 4 |\n * | | | yo | 44th, 1st, 1900th, 9999999th | 4,5 |\n * | | | yy | 44, 01, 00, 17 | 4 |\n * | | | yyy | 044, 001, 123, 999 | 4 |\n * | | | yyyy | 0044, 0001, 1900, 2017 | 4 |\n * | | | yyyyy | ... | 2,4 |\n * | Local week-numbering year | 130 | Y | 44, 1, 1900, 2017, 9000 | 4 |\n * | | | Yo | 44th, 1st, 1900th, 9999999th | 4,5 |\n * | | | YY | 44, 01, 00, 17 | 4,6 |\n * | | | YYY | 044, 001, 123, 999 | 4 |\n * | | | YYYY | 0044, 0001, 1900, 2017 | 4,6 |\n * | | | YYYYY | ... | 2,4 |\n * | ISO week-numbering year | 130 | R | -43, 1, 1900, 2017, 9999, -9999 | 4,5 |\n * | | | RR | -43, 01, 00, 17 | 4,5 |\n * | | | RRR | -043, 001, 123, 999, -999 | 4,5 |\n * | | | RRRR | -0043, 0001, 2017, 9999, -9999 | 4,5 |\n * | | | RRRRR | ... | 2,4,5 |\n * | Extended year | 130 | u | -43, 1, 1900, 2017, 9999, -999 | 4 |\n * | | | uu | -43, 01, 99, -99 | 4 |\n * | | | uuu | -043, 001, 123, 999, -999 | 4 |\n * | | | uuuu | -0043, 0001, 2017, 9999, -9999 | 4 |\n * | | | uuuuu | ... | 2,4 |\n * | Quarter (formatting) | 120 | Q | 1, 2, 3, 4 | |\n * | | | Qo | 1st, 2nd, 3rd, 4th | 5 |\n * | | | QQ | 01, 02, 03, 04 | |\n * | | | QQQ | Q1, Q2, Q3, Q4 | |\n * | | | QQQQ | 1st quarter, 2nd quarter, ... | 2 |\n * | | | QQQQQ | 1, 2, 3, 4 | 4 |\n * | Quarter (stand-alone) | 120 | q | 1, 2, 3, 4 | |\n * | | | qo | 1st, 2nd, 3rd, 4th | 5 |\n * | | | qq | 01, 02, 03, 04 | |\n * | | | qqq | Q1, Q2, Q3, Q4 | |\n * | | | qqqq | 1st quarter, 2nd quarter, ... | 2 |\n * | | | qqqqq | 1, 2, 3, 4 | 3 |\n * | Month (formatting) | 110 | M | 1, 2, ..., 12 | |\n * | | | Mo | 1st, 2nd, ..., 12th | 5 |\n * | | | MM | 01, 02, ..., 12 | |\n * | | | MMM | Jan, Feb, ..., Dec | |\n * | | | MMMM | January, February, ..., December | 2 |\n * | | | MMMMM | J, F, ..., D | |\n * | Month (stand-alone) | 110 | L | 1, 2, ..., 12 | |\n * | | | Lo | 1st, 2nd, ..., 12th | 5 |\n * | | | LL | 01, 02, ..., 12 | |\n * | | | LLL | Jan, Feb, ..., Dec | |\n * | | | LLLL | January, February, ..., December | 2 |\n * | | | LLLLL | J, F, ..., D | |\n * | Local week of year | 100 | w | 1, 2, ..., 53 | |\n * | | | wo | 1st, 2nd, ..., 53th | 5 |\n * | | | ww | 01, 02, ..., 53 | |\n * | ISO week of year | 100 | I | 1, 2, ..., 53 | 5 |\n * | | | Io | 1st, 2nd, ..., 53th | 5 |\n * | | | II | 01, 02, ..., 53 | 5 |\n * | Day of month | 90 | d | 1, 2, ..., 31 | |\n * | | | do | 1st, 2nd, ..., 31st | 5 |\n * | | | dd | 01, 02, ..., 31 | |\n * | Day of year | 90 | D | 1, 2, ..., 365, 366 | 7 |\n * | | | Do | 1st, 2nd, ..., 365th, 366th | 5 |\n * | | | DD | 01, 02, ..., 365, 366 | 7 |\n * | | | DDD | 001, 002, ..., 365, 366 | |\n * | | | DDDD | ... | 2 |\n * | Day of week (formatting) | 90 | E..EEE | Mon, Tue, Wed, ..., Sun | |\n * | | | EEEE | Monday, Tuesday, ..., Sunday | 2 |\n * | | | EEEEE | M, T, W, T, F, S, S | |\n * | | | EEEEEE | Mo, Tu, We, Th, Fr, Su, Sa | |\n * | ISO day of week (formatting) | 90 | i | 1, 2, 3, ..., 7 | 5 |\n * | | | io | 1st, 2nd, ..., 7th | 5 |\n * | | | ii | 01, 02, ..., 07 | 5 |\n * | | | iii | Mon, Tue, Wed, ..., Sun | 5 |\n * | | | iiii | Monday, Tuesday, ..., Sunday | 2,5 |\n * | | | iiiii | M, T, W, T, F, S, S | 5 |\n * | | | iiiiii | Mo, Tu, We, Th, Fr, Su, Sa | 5 |\n * | Local day of week (formatting) | 90 | e | 2, 3, 4, ..., 1 | |\n * | | | eo | 2nd, 3rd, ..., 1st | 5 |\n * | | | ee | 02, 03, ..., 01 | |\n * | | | eee | Mon, Tue, Wed, ..., Sun | |\n * | | | eeee | Monday, Tuesday, ..., Sunday | 2 |\n * | | | eeeee | M, T, W, T, F, S, S | |\n * | | | eeeeee | Mo, Tu, We, Th, Fr, Su, Sa | |\n * | Local day of week (stand-alone) | 90 | c | 2, 3, 4, ..., 1 | |\n * | | | co | 2nd, 3rd, ..., 1st | 5 |\n * | | | cc | 02, 03, ..., 01 | |\n * | | | ccc | Mon, Tue, Wed, ..., Sun | |\n * | | | cccc | Monday, Tuesday, ..., Sunday | 2 |\n * | | | ccccc | M, T, W, T, F, S, S | |\n * | | | cccccc | Mo, Tu, We, Th, Fr, Su, Sa | |\n * | AM, PM | 80 | a..aaa | AM, PM | |\n * | | | aaaa | a.m., p.m. | 2 |\n * | | | aaaaa | a, p | |\n * | AM, PM, noon, midnight | 80 | b..bbb | AM, PM, noon, midnight | |\n * | | | bbbb | a.m., p.m., noon, midnight | 2 |\n * | | | bbbbb | a, p, n, mi | |\n * | Flexible day period | 80 | B..BBB | at night, in the morning, ... | |\n * | | | BBBB | at night, in the morning, ... | 2 |\n * | | | BBBBB | at night, in the morning, ... | |\n * | Hour [1-12] | 70 | h | 1, 2, ..., 11, 12 | |\n * | | | ho | 1st, 2nd, ..., 11th, 12th | 5 |\n * | | | hh | 01, 02, ..., 11, 12 | |\n * | Hour [0-23] | 70 | H | 0, 1, 2, ..., 23 | |\n * | | | Ho | 0th, 1st, 2nd, ..., 23rd | 5 |\n * | | | HH | 00, 01, 02, ..., 23 | |\n * | Hour [0-11] | 70 | K | 1, 2, ..., 11, 0 | |\n * | | | Ko | 1st, 2nd, ..., 11th, 0th | 5 |\n * | | | KK | 01, 02, ..., 11, 00 | |\n * | Hour [1-24] | 70 | k | 24, 1, 2, ..., 23 | |\n * | | | ko | 24th, 1st, 2nd, ..., 23rd | 5 |\n * | | | kk | 24, 01, 02, ..., 23 | |\n * | Minute | 60 | m | 0, 1, ..., 59 | |\n * | | | mo | 0th, 1st, ..., 59th | 5 |\n * | | | mm | 00, 01, ..., 59 | |\n * | Second | 50 | s | 0, 1, ..., 59 | |\n * | | | so | 0th, 1st, ..., 59th | 5 |\n * | | | ss | 00, 01, ..., 59 | |\n * | Seconds timestamp | 40 | t | 512969520 | |\n * | | | tt | ... | 2 |\n * | Fraction of second | 30 | S | 0, 1, ..., 9 | |\n * | | | SS | 00, 01, ..., 99 | |\n * | | | SSS | 000, 0001, ..., 999 | |\n * | | | SSSS | ... | 2 |\n * | Milliseconds timestamp | 20 | T | 512969520900 | |\n * | | | TT | ... | 2 |\n * | Timezone (ISO-8601 w/ Z) | 10 | X | -08, +0530, Z | |\n * | | | XX | -0800, +0530, Z | |\n * | | | XXX | -08:00, +05:30, Z | |\n * | | | XXXX | -0800, +0530, Z, +123456 | 2 |\n * | | | XXXXX | -08:00, +05:30, Z, +12:34:56 | |\n * | Timezone (ISO-8601 w/o Z) | 10 | x | -08, +0530, +00 | |\n * | | | xx | -0800, +0530, +0000 | |\n * | | | xxx | -08:00, +05:30, +00:00 | 2 |\n * | | | xxxx | -0800, +0530, +0000, +123456 | |\n * | | | xxxxx | -08:00, +05:30, +00:00, +12:34:56 | |\n * | Long localized date | NA | P | 05/29/1453 | 5,8 |\n * | | | PP | May 29, 1453 | |\n * | | | PPP | May 29th, 1453 | |\n * | | | PPPP | Sunday, May 29th, 1453 | 2,5,8 |\n * | Long localized time | NA | p | 12:00 AM | 5,8 |\n * | | | pp | 12:00:00 AM | |\n * | Combination of date and time | NA | Pp | 05/29/1453, 12:00 AM | |\n * | | | PPpp | May 29, 1453, 12:00:00 AM | |\n * | | | PPPpp | May 29th, 1453 at ... | |\n * | | | PPPPpp | Sunday, May 29th, 1453 at ... | 2,5,8 |\n * Notes:\n * 1. \"Formatting\" units (e.g. formatting quarter) in the default en-US locale\n * are the same as \"stand-alone\" units, but are different in some languages.\n * \"Formatting\" units are declined according to the rules of the language\n * in the context of a date. \"Stand-alone\" units are always nominative singular.\n * In `format` function, they will produce different result:\n *\n * `format(new Date(2017, 10, 6), 'do LLLL', {locale: cs}) //=> '6. listopad'`\n *\n * `format(new Date(2017, 10, 6), 'do MMMM', {locale: cs}) //=> '6. listopadu'`\n *\n * `parse` will try to match both formatting and stand-alone units interchangably.\n *\n * 2. Any sequence of the identical letters is a pattern, unless it is escaped by\n * the single quote characters (see below).\n * If the sequence is longer than listed in table:\n * - for numerical units (`yyyyyyyy`) `parse` will try to match a number\n * as wide as the sequence\n * - for text units (`MMMMMMMM`) `parse` will try to match the widest variation of the unit.\n * These variations are marked with \"2\" in the last column of the table.\n *\n * 3. `QQQQQ` and `qqqqq` could be not strictly numerical in some locales.\n * These tokens represent the shortest form of the quarter.\n *\n * 4. The main difference between `y` and `u` patterns are B.C. years:\n *\n * | Year | `y` | `u` |\n * |------|-----|-----|\n * | AC 1 | 1 | 1 |\n * | BC 1 | 1 | 0 |\n * | BC 2 | 2 | -1 |\n *\n * Also `yy` will try to guess the century of two digit year by proximity with `referenceDate`:\n *\n * `parse('50', 'yy', new Date(2018, 0, 1)) //=> Sat Jan 01 2050 00:00:00`\n *\n * `parse('75', 'yy', new Date(2018, 0, 1)) //=> Wed Jan 01 1975 00:00:00`\n *\n * while `uu` will just assign the year as is:\n *\n * `parse('50', 'uu', new Date(2018, 0, 1)) //=> Sat Jan 01 0050 00:00:00`\n *\n * `parse('75', 'uu', new Date(2018, 0, 1)) //=> Tue Jan 01 0075 00:00:00`\n *\n * The same difference is true for local and ISO week-numbering years (`Y` and `R`),\n * except local week-numbering years are dependent on `options.weekStartsOn`\n * and `options.firstWeekContainsDate` (compare [setISOWeekYear]{@link https://date-fns.org/docs/setISOWeekYear}\n * and [setWeekYear]{@link https://date-fns.org/docs/setWeekYear}).\n *\n * 5. These patterns are not in the Unicode Technical Standard #35:\n * - `i`: ISO day of week\n * - `I`: ISO week of year\n * - `R`: ISO week-numbering year\n * - `o`: ordinal number modifier\n * - `P`: long localized date\n * - `p`: long localized time\n *\n * 6. `YY` and `YYYY` tokens represent week-numbering years but they are often confused with years.\n * You should enable `options.useAdditionalWeekYearTokens` to use them. See: https://git.io/fxCyr\n *\n * 7. `D` and `DD` tokens represent days of the year but they are ofthen confused with days of the month.\n * You should enable `options.useAdditionalDayOfYearTokens` to use them. See: https://git.io/fxCyr\n *\n * 8. `P+` tokens do not have a defined priority since they are merely aliases to other tokens based\n * on the given locale.\n *\n * using `en-US` locale: `P` => `MM/dd/yyyy`\n * using `en-US` locale: `p` => `hh:mm a`\n * using `pt-BR` locale: `P` => `dd/MM/yyyy`\n * using `pt-BR` locale: `p` => `HH:mm`\n *\n * Values will be assigned to the date in the descending order of its unit's priority.\n * Units of an equal priority overwrite each other in the order of appearance.\n *\n * If no values of higher priority are parsed (e.g. when parsing string 'January 1st' without a year),\n * the values will be taken from 3rd argument `referenceDate` which works as a context of parsing.\n *\n * `referenceDate` must be passed for correct work of the function.\n * If you're not sure which `referenceDate` to supply, create a new instance of Date:\n * `parse('02/11/2014', 'MM/dd/yyyy', new Date())`\n * In this case parsing will be done in the context of the current date.\n * If `referenceDate` is `Invalid Date` or a value not convertible to valid `Date`,\n * then `Invalid Date` will be returned.\n *\n * The result may vary by locale.\n *\n * If `formatString` matches with `dateString` but does not provides tokens, `referenceDate` will be returned.\n *\n * If parsing failed, `Invalid Date` will be returned.\n * Invalid Date is a Date, whose time value is NaN.\n * Time value of Date: http://es5.github.io/#x15.9.1.1\n *\n * ### v2.0.0 breaking changes:\n *\n * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes).\n *\n * - Old `parse` was renamed to `toDate`.\n * Now `parse` is a new function which parses a string using a provided format.\n *\n * ```javascript\n * // Before v2.0.0\n * parse('2016-01-01')\n *\n * // v2.0.0 onward (toDate no longer accepts a string)\n * toDate(1392098430000) // Unix to timestamp\n * toDate(new Date(2014, 1, 11, 11, 30, 30)) // Cloning the date\n * parse('2016-01-01', 'yyyy-MM-dd', new Date())\n * ```\n *\n * @param {String} dateString - the string to parse\n * @param {String} formatString - the string of tokens\n * @param {Date|Number} referenceDate - defines values missing from the parsed dateString\n * @param {Object} [options] - an object with options.\n * @param {Locale} [options.locale=defaultLocale] - the locale object. See [Locale]{@link https://date-fns.org/docs/Locale}\n * @param {0|1|2|3|4|5|6} [options.weekStartsOn=0] - the index of the first day of the week (0 - Sunday)\n * @param {1|2|3|4|5|6|7} [options.firstWeekContainsDate=1] - the day of January, which is always in the first week of the year\n * @param {Boolean} [options.useAdditionalWeekYearTokens=false] - if true, allows usage of the week-numbering year tokens `YY` and `YYYY`;\n * see: https://git.io/fxCyr\n * @param {Boolean} [options.useAdditionalDayOfYearTokens=false] - if true, allows usage of the day of year tokens `D` and `DD`;\n * see: https://git.io/fxCyr\n * @returns {Date} the parsed date\n * @throws {TypeError} 3 arguments required\n * @throws {RangeError} `options.weekStartsOn` must be between 0 and 6\n * @throws {RangeError} `options.firstWeekContainsDate` must be between 1 and 7\n * @throws {RangeError} `options.locale` must contain `match` property\n * @throws {RangeError} use `yyyy` instead of `YYYY` for formatting years using [format provided] to the input [input provided]; see: https://git.io/fxCyr\n * @throws {RangeError} use `yy` instead of `YY` for formatting years using [format provided] to the input [input provided]; see: https://git.io/fxCyr\n * @throws {RangeError} use `d` instead of `D` for formatting days of the month using [format provided] to the input [input provided]; see: https://git.io/fxCyr\n * @throws {RangeError} use `dd` instead of `DD` for formatting days of the month using [format provided] to the input [input provided]; see: https://git.io/fxCyr\n * @throws {RangeError} format string contains an unescaped latin alphabet character\n *\n * @example\n * // Parse 11 February 2014 from middle-endian format:\n * var result = parse('02/11/2014', 'MM/dd/yyyy', new Date())\n * //=> Tue Feb 11 2014 00:00:00\n *\n * @example\n * // Parse 28th of February in Esperanto locale in the context of 2010 year:\n * import eo from 'date-fns/locale/eo'\n * var result = parse('28-a de februaro', \"do 'de' MMMM\", new Date(2010, 0, 1), {\n * locale: eo\n * })\n * //=> Sun Feb 28 2010 00:00:00\n */\n\nexport default function parse(dirtyDateString, dirtyFormatString, dirtyReferenceDate, dirtyOptions) {\n requiredArgs(3, arguments);\n var dateString = String(dirtyDateString);\n var formatString = String(dirtyFormatString);\n var options = dirtyOptions || {};\n var locale = options.locale || defaultLocale;\n\n if (!locale.match) {\n throw new RangeError('locale must contain match property');\n }\n\n var localeFirstWeekContainsDate = locale.options && locale.options.firstWeekContainsDate;\n var defaultFirstWeekContainsDate = localeFirstWeekContainsDate == null ? 1 : toInteger(localeFirstWeekContainsDate);\n var firstWeekContainsDate = options.firstWeekContainsDate == null ? defaultFirstWeekContainsDate : toInteger(options.firstWeekContainsDate); // Test if weekStartsOn is between 1 and 7 _and_ is not NaN\n\n if (!(firstWeekContainsDate >= 1 && firstWeekContainsDate <= 7)) {\n throw new RangeError('firstWeekContainsDate must be between 1 and 7 inclusively');\n }\n\n var localeWeekStartsOn = locale.options && locale.options.weekStartsOn;\n var defaultWeekStartsOn = localeWeekStartsOn == null ? 0 : toInteger(localeWeekStartsOn);\n var weekStartsOn = options.weekStartsOn == null ? defaultWeekStartsOn : toInteger(options.weekStartsOn); // Test if weekStartsOn is between 0 and 6 _and_ is not NaN\n\n if (!(weekStartsOn >= 0 && weekStartsOn <= 6)) {\n throw new RangeError('weekStartsOn must be between 0 and 6 inclusively');\n }\n\n if (formatString === '') {\n if (dateString === '') {\n return toDate(dirtyReferenceDate);\n } else {\n return new Date(NaN);\n }\n }\n\n var subFnOptions = {\n firstWeekContainsDate: firstWeekContainsDate,\n weekStartsOn: weekStartsOn,\n locale: locale\n }; // If timezone isn't specified, it will be set to the system timezone\n\n var setters = [{\n priority: TIMEZONE_UNIT_PRIORITY,\n subPriority: -1,\n set: dateToSystemTimezone,\n index: 0\n }];\n var i;\n var tokens = formatString.match(longFormattingTokensRegExp).map(function (substring) {\n var firstCharacter = substring[0];\n\n if (firstCharacter === 'p' || firstCharacter === 'P') {\n var longFormatter = longFormatters[firstCharacter];\n return longFormatter(substring, locale.formatLong, subFnOptions);\n }\n\n return substring;\n }).join('').match(formattingTokensRegExp);\n var usedTokens = [];\n\n for (i = 0; i < tokens.length; i++) {\n var token = tokens[i];\n\n if (!options.useAdditionalWeekYearTokens && isProtectedWeekYearToken(token)) {\n throwProtectedError(token, formatString, dirtyDateString);\n }\n\n if (!options.useAdditionalDayOfYearTokens && isProtectedDayOfYearToken(token)) {\n throwProtectedError(token, formatString, dirtyDateString);\n }\n\n var firstCharacter = token[0];\n var parser = parsers[firstCharacter];\n\n if (parser) {\n var incompatibleTokens = parser.incompatibleTokens;\n\n if (Array.isArray(incompatibleTokens)) {\n var incompatibleToken = void 0;\n\n for (var _i = 0; _i < usedTokens.length; _i++) {\n var usedToken = usedTokens[_i].token;\n\n if (incompatibleTokens.indexOf(usedToken) !== -1 || usedToken === firstCharacter) {\n incompatibleToken = usedTokens[_i];\n break;\n }\n }\n\n if (incompatibleToken) {\n throw new RangeError(\"The format string mustn't contain `\".concat(incompatibleToken.fullToken, \"` and `\").concat(token, \"` at the same time\"));\n }\n } else if (parser.incompatibleTokens === '*' && usedTokens.length) {\n throw new RangeError(\"The format string mustn't contain `\".concat(token, \"` and any other token at the same time\"));\n }\n\n usedTokens.push({\n token: firstCharacter,\n fullToken: token\n });\n var parseResult = parser.parse(dateString, token, locale.match, subFnOptions);\n\n if (!parseResult) {\n return new Date(NaN);\n }\n\n setters.push({\n priority: parser.priority,\n subPriority: parser.subPriority || 0,\n set: parser.set,\n validate: parser.validate,\n value: parseResult.value,\n index: setters.length\n });\n dateString = parseResult.rest;\n } else {\n if (firstCharacter.match(unescapedLatinCharacterRegExp)) {\n throw new RangeError('Format string contains an unescaped latin alphabet character `' + firstCharacter + '`');\n } // Replace two single quote characters with one single quote character\n\n\n if (token === \"''\") {\n token = \"'\";\n } else if (firstCharacter === \"'\") {\n token = cleanEscapedString(token);\n } // Cut token from string, or, if string doesn't match the token, return Invalid Date\n\n\n if (dateString.indexOf(token) === 0) {\n dateString = dateString.slice(token.length);\n } else {\n return new Date(NaN);\n }\n }\n } // Check if the remaining input contains something other than whitespace\n\n\n if (dateString.length > 0 && notWhitespaceRegExp.test(dateString)) {\n return new Date(NaN);\n }\n\n var uniquePrioritySetters = setters.map(function (setter) {\n return setter.priority;\n }).sort(function (a, b) {\n return b - a;\n }).filter(function (priority, index, array) {\n return array.indexOf(priority) === index;\n }).map(function (priority) {\n return setters.filter(function (setter) {\n return setter.priority === priority;\n }).sort(function (a, b) {\n return b.subPriority - a.subPriority;\n });\n }).map(function (setterArray) {\n return setterArray[0];\n });\n var date = toDate(dirtyReferenceDate);\n\n if (isNaN(date)) {\n return new Date(NaN);\n } // Convert the date in system timezone to the same date in UTC+00:00 timezone.\n // This ensures that when UTC functions will be implemented, locales will be compatible with them.\n // See an issue about UTC functions: https://github.com/date-fns/date-fns/issues/37\n\n\n var utcDate = subMilliseconds(date, getTimezoneOffsetInMilliseconds(date));\n var flags = {};\n\n for (i = 0; i < uniquePrioritySetters.length; i++) {\n var setter = uniquePrioritySetters[i];\n\n if (setter.validate && !setter.validate(utcDate, setter.value, subFnOptions)) {\n return new Date(NaN);\n }\n\n var result = setter.set(utcDate, flags, setter.value, subFnOptions); // Result is tuple (date, flags)\n\n if (result[0]) {\n utcDate = result[0];\n assign(flags, result[1]); // Result is date\n } else {\n utcDate = result;\n }\n }\n\n return utcDate;\n}\n\nfunction dateToSystemTimezone(date, flags) {\n if (flags.timestampIsSet) {\n return date;\n }\n\n var convertedDate = new Date(0);\n convertedDate.setFullYear(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());\n convertedDate.setHours(date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds());\n return convertedDate;\n}\n\nfunction cleanEscapedString(input) {\n return input.match(escapedStringRegExp)[1].replace(doubleQuoteRegExp, \"'\");\n}","import _curry2 from './_curry2.js';\nimport _xfBase from './_xfBase.js';\n\nvar XFilter = /*#__PURE__*/function () {\n function XFilter(f, xf) {\n this.xf = xf;\n this.f = f;\n }\n XFilter.prototype['@@transducer/init'] = _xfBase.init;\n XFilter.prototype['@@transducer/result'] = _xfBase.result;\n XFilter.prototype['@@transducer/step'] = function (result, input) {\n return this.f(input) ? this.xf['@@transducer/step'](result, input) : result;\n };\n\n return XFilter;\n}();\n\nvar _xfilter = /*#__PURE__*/_curry2(function _xfilter(f, xf) {\n return new XFilter(f, xf);\n});\nexport default _xfilter;","import _curry2 from './internal/_curry2.js';\nimport _dispatchable from './internal/_dispatchable.js';\nimport _filter from './internal/_filter.js';\nimport _isObject from './internal/_isObject.js';\nimport _reduce from './internal/_reduce.js';\nimport _xfilter from './internal/_xfilter.js';\nimport keys from './keys.js';\n\n/**\n * Takes a predicate and a `Filterable`, and returns a new filterable of the\n * same type containing the members of the given filterable which satisfy the\n * given predicate. Filterable objects include plain objects or any object\n * that has a filter method such as `Array`.\n *\n * Dispatches to the `filter` method of the second argument, if present.\n *\n * Acts as a transducer if a transformer is given in list position.\n *\n * @func\n * @memberOf R\n * @since v0.1.0\n * @category List\n * @sig Filterable f => (a -> Boolean) -> f a -> f a\n * @param {Function} pred\n * @param {Array} filterable\n * @return {Array} Filterable\n * @see R.reject, R.transduce, R.addIndex\n * @example\n *\n * const isEven = n => n % 2 === 0;\n *\n * R.filter(isEven, [1, 2, 3, 4]); //=> [2, 4]\n *\n * R.filter(isEven, {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, d: 4}\n */\nvar filter = /*#__PURE__*/_curry2( /*#__PURE__*/_dispatchable(['filter'], _xfilter, function (pred, filterable) {\n return _isObject(filterable) ? _reduce(function (acc, key) {\n if (pred(filterable[key])) {\n acc[key] = filterable[key];\n }\n return acc;\n }, {}, keys(filterable)) :\n // else\n _filter(pred, filterable);\n}));\nexport default filter;","export default function _filter(fn, list) {\n var idx = 0;\n var len = list.length;\n var result = [];\n\n while (idx < len) {\n if (fn(list[idx])) {\n result[result.length] = list[idx];\n }\n idx += 1;\n }\n return result;\n}","\n\n/**\n * A function that always returns `true`. Any passed in parameters are ignored.\n *\n * @func\n * @memberOf R\n * @since v0.9.0\n * @category Function\n * @sig * -> Boolean\n * @param {*}\n * @return {Boolean}\n * @see R.F\n * @example\n *\n * R.T(); //=> true\n */\nvar T = function () {\n return true;\n};\nexport default T;","import _curry2 from './internal/_curry2.js';\n\n/**\n * Returns a partial copy of an object omitting the keys specified.\n *\n * @func\n * @memberOf R\n * @since v0.1.0\n * @category Object\n * @sig [String] -> {String: *} -> {String: *}\n * @param {Array} names an array of String property names to omit from the new object\n * @param {Object} obj The object to copy from\n * @return {Object} A new object with properties from `names` not on it.\n * @see R.pick\n * @example\n *\n * R.omit(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, c: 3}\n */\nvar omit = /*#__PURE__*/_curry2(function omit(names, obj) {\n var result = {};\n var index = {};\n var idx = 0;\n var len = names.length;\n\n while (idx < len) {\n index[names[idx]] = 1;\n idx += 1;\n }\n\n for (var prop in obj) {\n if (!index.hasOwnProperty(prop)) {\n result[prop] = obj[prop];\n }\n }\n return result;\n});\nexport default omit;","import _curry2 from './internal/_curry2.js';\n\n/**\n * Returns the larger of its two arguments.\n *\n * @func\n * @memberOf R\n * @since v0.1.0\n * @category Relation\n * @sig Ord a => a -> a -> a\n * @param {*} a\n * @param {*} b\n * @return {*}\n * @see R.maxBy, R.min\n * @example\n *\n * R.max(789, 123); //=> 789\n * R.max('a', 'b'); //=> 'b'\n */\nvar max = /*#__PURE__*/_curry2(function max(a, b) {\n return b > a ? b : a;\n});\nexport default max;","import _arity from './internal/_arity.js';\nimport _curry1 from './internal/_curry1.js';\nimport map from './map.js';\nimport max from './max.js';\nimport reduce from './reduce.js';\n\n/**\n * Returns a function, `fn`, which encapsulates `if/else, if/else, ...` logic.\n * `R.cond` takes a list of [predicate, transformer] pairs. All of the arguments\n * to `fn` are applied to each of the predicates in turn until one returns a\n * \"truthy\" value, at which point `fn` returns the result of applying its\n * arguments to the corresponding transformer. If none of the predicates\n * matches, `fn` returns undefined.\n *\n * @func\n * @memberOf R\n * @since v0.6.0\n * @category Logic\n * @sig [[(*... -> Boolean),(*... -> *)]] -> (*... -> *)\n * @param {Array} pairs A list of [predicate, transformer]\n * @return {Function}\n * @see R.ifElse, R.unless, R.when\n * @example\n *\n * const fn = R.cond([\n * [R.equals(0), R.always('water freezes at 0°C')],\n * [R.equals(100), R.always('water boils at 100°C')],\n * [R.T, temp => 'nothing special happens at ' + temp + '°C']\n * ]);\n * fn(0); //=> 'water freezes at 0°C'\n * fn(50); //=> 'nothing special happens at 50°C'\n * fn(100); //=> 'water boils at 100°C'\n */\nvar cond = /*#__PURE__*/_curry1(function cond(pairs) {\n var arity = reduce(max, 0, map(function (pair) {\n return pair[0].length;\n }, pairs));\n return _arity(arity, function () {\n var idx = 0;\n while (idx < pairs.length) {\n if (pairs[idx][0].apply(this, arguments)) {\n return pairs[idx][1].apply(this, arguments);\n }\n idx += 1;\n }\n });\n});\nexport default cond;","export default function _pipe(f, g) {\n return function () {\n return g.call(this, f.apply(this, arguments));\n };\n}","import _arity from './internal/_arity.js';\nimport _pipe from './internal/_pipe.js';\nimport reduce from './reduce.js';\nimport tail from './tail.js';\n\n/**\n * Performs left-to-right function composition. The leftmost function may have\n * any arity; the remaining functions must be unary.\n *\n * In some libraries this function is named `sequence`.\n *\n * **Note:** The result of pipe is not automatically curried.\n *\n * @func\n * @memberOf R\n * @since v0.1.0\n * @category Function\n * @sig (((a, b, ..., n) -> o), (o -> p), ..., (x -> y), (y -> z)) -> ((a, b, ..., n) -> z)\n * @param {...Function} functions\n * @return {Function}\n * @see R.compose\n * @example\n *\n * const f = R.pipe(Math.pow, R.negate, R.inc);\n *\n * f(3, 4); // -(3^4) + 1\n * @symb R.pipe(f, g, h)(a, b) = h(g(f(a, b)))\n */\nexport default function pipe() {\n if (arguments.length === 0) {\n throw new Error('pipe requires at least one argument');\n }\n return _arity(arguments[0].length, reduce(_pipe, arguments[0], tail(arguments)));\n}","/** @license React v16.12.0\n * react.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';var h=require(\"object-assign\"),n=\"function\"===typeof Symbol&&Symbol.for,p=n?Symbol.for(\"react.element\"):60103,q=n?Symbol.for(\"react.portal\"):60106,r=n?Symbol.for(\"react.fragment\"):60107,t=n?Symbol.for(\"react.strict_mode\"):60108,u=n?Symbol.for(\"react.profiler\"):60114,v=n?Symbol.for(\"react.provider\"):60109,w=n?Symbol.for(\"react.context\"):60110,x=n?Symbol.for(\"react.forward_ref\"):60112,y=n?Symbol.for(\"react.suspense\"):60113;n&&Symbol.for(\"react.suspense_list\");\nvar z=n?Symbol.for(\"react.memo\"):60115,aa=n?Symbol.for(\"react.lazy\"):60116;n&&Symbol.for(\"react.fundamental\");n&&Symbol.for(\"react.responder\");n&&Symbol.for(\"react.scope\");var A=\"function\"===typeof Symbol&&Symbol.iterator;\nfunction B(a){for(var b=\"https://reactjs.org/docs/error-decoder.html?invariant=\"+a,c=1;cP.length&&P.push(a)}\nfunction S(a,b,c,e){var d=typeof a;if(\"undefined\"===d||\"boolean\"===d)a=null;var g=!1;if(null===a)g=!0;else switch(d){case \"string\":case \"number\":g=!0;break;case \"object\":switch(a.$$typeof){case p:case q:g=!0}}if(g)return c(e,a,\"\"===b?\".\"+T(a,0):b),1;g=0;b=\"\"===b?\".\":b+\":\";if(Array.isArray(a))for(var l=0;lb}return!1}function B(a,b,c,d,e,f){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f}var D={};\n\"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style\".split(\" \").forEach(function(a){D[a]=new B(a,0,!1,a,null,!1)});[[\"acceptCharset\",\"accept-charset\"],[\"className\",\"class\"],[\"htmlFor\",\"for\"],[\"httpEquiv\",\"http-equiv\"]].forEach(function(a){var b=a[0];D[b]=new B(b,1,!1,a[1],null,!1)});[\"contentEditable\",\"draggable\",\"spellCheck\",\"value\"].forEach(function(a){D[a]=new B(a,2,!1,a.toLowerCase(),null,!1)});\n[\"autoReverse\",\"externalResourcesRequired\",\"focusable\",\"preserveAlpha\"].forEach(function(a){D[a]=new B(a,2,!1,a,null,!1)});\"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope\".split(\" \").forEach(function(a){D[a]=new B(a,3,!1,a.toLowerCase(),null,!1)});\n[\"checked\",\"multiple\",\"muted\",\"selected\"].forEach(function(a){D[a]=new B(a,3,!0,a,null,!1)});[\"capture\",\"download\"].forEach(function(a){D[a]=new B(a,4,!1,a,null,!1)});[\"cols\",\"rows\",\"size\",\"span\"].forEach(function(a){D[a]=new B(a,6,!1,a,null,!1)});[\"rowSpan\",\"start\"].forEach(function(a){D[a]=new B(a,5,!1,a.toLowerCase(),null,!1)});var sb=/[\\-:]([a-z])/g;function tb(a){return a[1].toUpperCase()}\n\"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height\".split(\" \").forEach(function(a){var b=a.replace(sb,\ntb);D[b]=new B(b,1,!1,a,null,!1)});\"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type\".split(\" \").forEach(function(a){var b=a.replace(sb,tb);D[b]=new B(b,1,!1,a,\"http://www.w3.org/1999/xlink\",!1)});[\"xml:base\",\"xml:lang\",\"xml:space\"].forEach(function(a){var b=a.replace(sb,tb);D[b]=new B(b,1,!1,a,\"http://www.w3.org/XML/1998/namespace\",!1)});[\"tabIndex\",\"crossOrigin\"].forEach(function(a){D[a]=new B(a,1,!1,a.toLowerCase(),null,!1)});\nD.xlinkHref=new B(\"xlinkHref\",1,!1,\"xlink:href\",\"http://www.w3.org/1999/xlink\",!0);[\"src\",\"href\",\"action\",\"formAction\"].forEach(function(a){D[a]=new B(a,1,!1,a.toLowerCase(),null,!0)});function ub(a){switch(typeof a){case \"boolean\":case \"number\":case \"object\":case \"string\":case \"undefined\":return a;default:return\"\"}}\nfunction vb(a,b,c,d){var e=D.hasOwnProperty(b)?D[b]:null;var f=null!==e?0===e.type:d?!1:!(2=b.length))throw Error(u(93));b=b[0]}c=b}null==c&&(c=\"\")}a._wrapperState={initialValue:ub(c)}}\nfunction Mb(a,b){var c=ub(b.value),d=ub(b.defaultValue);null!=c&&(c=\"\"+c,c!==a.value&&(a.value=c),null==b.defaultValue&&a.defaultValue!==c&&(a.defaultValue=c));null!=d&&(a.defaultValue=\"\"+d)}function Nb(a){var b=a.textContent;b===a._wrapperState.initialValue&&\"\"!==b&&null!==b&&(a.value=b)}var Ob={html:\"http://www.w3.org/1999/xhtml\",mathml:\"http://www.w3.org/1998/Math/MathML\",svg:\"http://www.w3.org/2000/svg\"};\nfunction Pb(a){switch(a){case \"svg\":return\"http://www.w3.org/2000/svg\";case \"math\":return\"http://www.w3.org/1998/Math/MathML\";default:return\"http://www.w3.org/1999/xhtml\"}}function Qb(a,b){return null==a||\"http://www.w3.org/1999/xhtml\"===a?Pb(b):\"http://www.w3.org/2000/svg\"===a&&\"foreignObject\"===b?\"http://www.w3.org/1999/xhtml\":a}\nvar Rb,Sb=function(a){return\"undefined\"!==typeof MSApp&&MSApp.execUnsafeLocalFunction?function(b,c,d,e){MSApp.execUnsafeLocalFunction(function(){return a(b,c,d,e)})}:a}(function(a,b){if(a.namespaceURI!==Ob.svg||\"innerHTML\"in a)a.innerHTML=b;else{Rb=Rb||document.createElement(\"div\");Rb.innerHTML=\"\"+b.valueOf().toString()+\"\";for(b=Rb.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;b.firstChild;)a.appendChild(b.firstChild)}});\nfunction Tb(a,b){if(b){var c=a.firstChild;if(c&&c===a.lastChild&&3===c.nodeType){c.nodeValue=b;return}}a.textContent=b}function Ub(a,b){var c={};c[a.toLowerCase()]=b.toLowerCase();c[\"Webkit\"+a]=\"webkit\"+b;c[\"Moz\"+a]=\"moz\"+b;return c}var Vb={animationend:Ub(\"Animation\",\"AnimationEnd\"),animationiteration:Ub(\"Animation\",\"AnimationIteration\"),animationstart:Ub(\"Animation\",\"AnimationStart\"),transitionend:Ub(\"Transition\",\"TransitionEnd\")},Wb={},Xb={};\nYa&&(Xb=document.createElement(\"div\").style,\"AnimationEvent\"in window||(delete Vb.animationend.animation,delete Vb.animationiteration.animation,delete Vb.animationstart.animation),\"TransitionEvent\"in window||delete Vb.transitionend.transition);function Yb(a){if(Wb[a])return Wb[a];if(!Vb[a])return a;var b=Vb[a],c;for(c in b)if(b.hasOwnProperty(c)&&c in Xb)return Wb[a]=b[c];return a}var Zb=Yb(\"animationend\"),$b=Yb(\"animationiteration\"),ac=Yb(\"animationstart\"),bc=Yb(\"transitionend\"),cc=\"abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange waiting\".split(\" \");\nfunction ec(a){var b=a,c=a;if(a.alternate)for(;b.return;)b=b.return;else{a=b;do b=a,0!==(b.effectTag&1026)&&(c=b.return),a=b.return;while(a)}return 3===b.tag?c:null}function fc(a){if(13===a.tag){var b=a.memoizedState;null===b&&(a=a.alternate,null!==a&&(b=a.memoizedState));if(null!==b)return b.dehydrated}return null}function gc(a){if(ec(a)!==a)throw Error(u(188));}\nfunction hc(a){var b=a.alternate;if(!b){b=ec(a);if(null===b)throw Error(u(188));return b!==a?null:a}for(var c=a,d=b;;){var e=c.return;if(null===e)break;var f=e.alternate;if(null===f){d=e.return;if(null!==d){c=d;continue}break}if(e.child===f.child){for(f=e.child;f;){if(f===c)return gc(e),a;if(f===d)return gc(e),b;f=f.sibling}throw Error(u(188));}if(c.return!==d.return)c=e,d=f;else{for(var g=!1,h=e.child;h;){if(h===c){g=!0;c=e;d=f;break}if(h===d){g=!0;d=e;c=f;break}h=h.sibling}if(!g){for(h=f.child;h;){if(h===\nc){g=!0;c=f;d=e;break}if(h===d){g=!0;d=f;c=e;break}h=h.sibling}if(!g)throw Error(u(189));}}if(c.alternate!==d)throw Error(u(190));}if(3!==c.tag)throw Error(u(188));return c.stateNode.current===c?a:b}function ic(a){a=hc(a);if(!a)return null;for(var b=a;;){if(5===b.tag||6===b.tag)return b;if(b.child)b.child.return=b,b=b.child;else{if(b===a)break;for(;!b.sibling;){if(!b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}}return null}\nvar jc,kc,lc,mc=!1,nc=[],oc=null,pc=null,qc=null,rc=new Map,sc=new Map,tc=[],uc=\"mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput close cancel copy cut paste click change contextmenu reset submit\".split(\" \"),vc=\"focus blur dragenter dragleave mouseover mouseout pointerover pointerout gotpointercapture lostpointercapture\".split(\" \");\nfunction wc(a){var b=xc(a);uc.forEach(function(c){yc(c,a,b)});vc.forEach(function(c){yc(c,a,b)})}function zc(a,b,c,d){return{blockedOn:a,topLevelType:b,eventSystemFlags:c|32,nativeEvent:d}}function Ac(a,b){switch(a){case \"focus\":case \"blur\":oc=null;break;case \"dragenter\":case \"dragleave\":pc=null;break;case \"mouseover\":case \"mouseout\":qc=null;break;case \"pointerover\":case \"pointerout\":rc.delete(b.pointerId);break;case \"gotpointercapture\":case \"lostpointercapture\":sc.delete(b.pointerId)}}\nfunction Bc(a,b,c,d,e){if(null===a||a.nativeEvent!==e)return a=zc(b,c,d,e),null!==b&&(b=Cc(b),null!==b&&kc(b)),a;a.eventSystemFlags|=d;return a}function Dc(a,b,c,d){switch(b){case \"focus\":return oc=Bc(oc,a,b,c,d),!0;case \"dragenter\":return pc=Bc(pc,a,b,c,d),!0;case \"mouseover\":return qc=Bc(qc,a,b,c,d),!0;case \"pointerover\":var e=d.pointerId;rc.set(e,Bc(rc.get(e)||null,a,b,c,d));return!0;case \"gotpointercapture\":return e=d.pointerId,sc.set(e,Bc(sc.get(e)||null,a,b,c,d)),!0}return!1}\nfunction Ec(a){var b=Fc(a.target);if(null!==b){var c=ec(b);if(null!==c)if(b=c.tag,13===b){if(b=fc(c),null!==b){a.blockedOn=b;q.unstable_runWithPriority(a.priority,function(){lc(c)});return}}else if(3===b&&c.stateNode.hydrate){a.blockedOn=3===c.tag?c.stateNode.containerInfo:null;return}}a.blockedOn=null}function Gc(a){if(null!==a.blockedOn)return!1;var b=Hc(a.topLevelType,a.eventSystemFlags,a.nativeEvent);if(null!==b){var c=Cc(b);null!==c&&kc(c);a.blockedOn=b;return!1}return!0}\nfunction Ic(a,b,c){Gc(a)&&c.delete(b)}function Jc(){for(mc=!1;0this.eventPool.length&&this.eventPool.push(a)}function Vc(a){a.eventPool=[];a.getPooled=Wc;a.release=Xc}var Yc=E.extend({animationName:null,elapsedTime:null,pseudoElement:null}),Zc=E.extend({clipboardData:function(a){return\"clipboardData\"in a?a.clipboardData:window.clipboardData}}),$c=E.extend({view:null,detail:null}),ad=$c.extend({relatedTarget:null});\nfunction bd(a){var b=a.keyCode;\"charCode\"in a?(a=a.charCode,0===a&&13===b&&(a=13)):a=b;10===a&&(a=13);return 32<=a||13===a?a:0}\nvar cd={Esc:\"Escape\",Spacebar:\" \",Left:\"ArrowLeft\",Up:\"ArrowUp\",Right:\"ArrowRight\",Down:\"ArrowDown\",Del:\"Delete\",Win:\"OS\",Menu:\"ContextMenu\",Apps:\"ContextMenu\",Scroll:\"ScrollLock\",MozPrintableKey:\"Unidentified\"},dd={8:\"Backspace\",9:\"Tab\",12:\"Clear\",13:\"Enter\",16:\"Shift\",17:\"Control\",18:\"Alt\",19:\"Pause\",20:\"CapsLock\",27:\"Escape\",32:\" \",33:\"PageUp\",34:\"PageDown\",35:\"End\",36:\"Home\",37:\"ArrowLeft\",38:\"ArrowUp\",39:\"ArrowRight\",40:\"ArrowDown\",45:\"Insert\",46:\"Delete\",112:\"F1\",113:\"F2\",114:\"F3\",115:\"F4\",\n116:\"F5\",117:\"F6\",118:\"F7\",119:\"F8\",120:\"F9\",121:\"F10\",122:\"F11\",123:\"F12\",144:\"NumLock\",145:\"ScrollLock\",224:\"Meta\"},ed={Alt:\"altKey\",Control:\"ctrlKey\",Meta:\"metaKey\",Shift:\"shiftKey\"};function gd(a){var b=this.nativeEvent;return b.getModifierState?b.getModifierState(a):(a=ed[a])?!!b[a]:!1}function hd(){return gd}\nvar id=$c.extend({key:function(a){if(a.key){var b=cd[a.key]||a.key;if(\"Unidentified\"!==b)return b}return\"keypress\"===a.type?(a=bd(a),13===a?\"Enter\":String.fromCharCode(a)):\"keydown\"===a.type||\"keyup\"===a.type?dd[a.keyCode]||\"Unidentified\":\"\"},location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:hd,charCode:function(a){return\"keypress\"===a.type?bd(a):0},keyCode:function(a){return\"keydown\"===a.type||\"keyup\"===a.type?a.keyCode:0},which:function(a){return\"keypress\"===\na.type?bd(a):\"keydown\"===a.type||\"keyup\"===a.type?a.keyCode:0}}),jd=0,kd=0,ld=!1,md=!1,nd=$c.extend({screenX:null,screenY:null,clientX:null,clientY:null,pageX:null,pageY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:hd,button:null,buttons:null,relatedTarget:function(a){return a.relatedTarget||(a.fromElement===a.srcElement?a.toElement:a.fromElement)},movementX:function(a){if(\"movementX\"in a)return a.movementX;var b=jd;jd=a.screenX;return ld?\"mousemove\"===a.type?a.screenX-\nb:0:(ld=!0,0)},movementY:function(a){if(\"movementY\"in a)return a.movementY;var b=kd;kd=a.screenY;return md?\"mousemove\"===a.type?a.screenY-b:0:(md=!0,0)}}),od=nd.extend({pointerId:null,width:null,height:null,pressure:null,tangentialPressure:null,tiltX:null,tiltY:null,twist:null,pointerType:null,isPrimary:null}),pd=nd.extend({dataTransfer:null}),qd=$c.extend({touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:hd}),rd=E.extend({propertyName:null,\nelapsedTime:null,pseudoElement:null}),sd=nd.extend({deltaX:function(a){return\"deltaX\"in a?a.deltaX:\"wheelDeltaX\"in a?-a.wheelDeltaX:0},deltaY:function(a){return\"deltaY\"in a?a.deltaY:\"wheelDeltaY\"in a?-a.wheelDeltaY:\"wheelDelta\"in a?-a.wheelDelta:0},deltaZ:null,deltaMode:null}),td=[[\"blur\",\"blur\",0],[\"cancel\",\"cancel\",0],[\"click\",\"click\",0],[\"close\",\"close\",0],[\"contextmenu\",\"contextMenu\",0],[\"copy\",\"copy\",0],[\"cut\",\"cut\",0],[\"auxclick\",\"auxClick\",0],[\"dblclick\",\"doubleClick\",0],[\"dragend\",\"dragEnd\",\n0],[\"dragstart\",\"dragStart\",0],[\"drop\",\"drop\",0],[\"focus\",\"focus\",0],[\"input\",\"input\",0],[\"invalid\",\"invalid\",0],[\"keydown\",\"keyDown\",0],[\"keypress\",\"keyPress\",0],[\"keyup\",\"keyUp\",0],[\"mousedown\",\"mouseDown\",0],[\"mouseup\",\"mouseUp\",0],[\"paste\",\"paste\",0],[\"pause\",\"pause\",0],[\"play\",\"play\",0],[\"pointercancel\",\"pointerCancel\",0],[\"pointerdown\",\"pointerDown\",0],[\"pointerup\",\"pointerUp\",0],[\"ratechange\",\"rateChange\",0],[\"reset\",\"reset\",0],[\"seeked\",\"seeked\",0],[\"submit\",\"submit\",0],[\"touchcancel\",\"touchCancel\",\n0],[\"touchend\",\"touchEnd\",0],[\"touchstart\",\"touchStart\",0],[\"volumechange\",\"volumeChange\",0],[\"drag\",\"drag\",1],[\"dragenter\",\"dragEnter\",1],[\"dragexit\",\"dragExit\",1],[\"dragleave\",\"dragLeave\",1],[\"dragover\",\"dragOver\",1],[\"mousemove\",\"mouseMove\",1],[\"mouseout\",\"mouseOut\",1],[\"mouseover\",\"mouseOver\",1],[\"pointermove\",\"pointerMove\",1],[\"pointerout\",\"pointerOut\",1],[\"pointerover\",\"pointerOver\",1],[\"scroll\",\"scroll\",1],[\"toggle\",\"toggle\",1],[\"touchmove\",\"touchMove\",1],[\"wheel\",\"wheel\",1],[\"abort\",\"abort\",\n2],[Zb,\"animationEnd\",2],[$b,\"animationIteration\",2],[ac,\"animationStart\",2],[\"canplay\",\"canPlay\",2],[\"canplaythrough\",\"canPlayThrough\",2],[\"durationchange\",\"durationChange\",2],[\"emptied\",\"emptied\",2],[\"encrypted\",\"encrypted\",2],[\"ended\",\"ended\",2],[\"error\",\"error\",2],[\"gotpointercapture\",\"gotPointerCapture\",2],[\"load\",\"load\",2],[\"loadeddata\",\"loadedData\",2],[\"loadedmetadata\",\"loadedMetadata\",2],[\"loadstart\",\"loadStart\",2],[\"lostpointercapture\",\"lostPointerCapture\",2],[\"playing\",\"playing\",2],[\"progress\",\n\"progress\",2],[\"seeking\",\"seeking\",2],[\"stalled\",\"stalled\",2],[\"suspend\",\"suspend\",2],[\"timeupdate\",\"timeUpdate\",2],[bc,\"transitionEnd\",2],[\"waiting\",\"waiting\",2]],ud={},vd={},wd=0;for(;wd=b)return{node:c,offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=ce(c)}}\nfunction ee(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?ee(a,b.parentNode):\"contains\"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}function fe(){for(var a=window,b=be();b instanceof a.HTMLIFrameElement;){try{var c=\"string\"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break;b=be(a.document)}return b}\nfunction ge(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&(\"input\"===b&&(\"text\"===a.type||\"search\"===a.type||\"tel\"===a.type||\"url\"===a.type||\"password\"===a.type)||\"textarea\"===b||\"true\"===a.contentEditable)}var he=\"$\",ie=\"/$\",je=\"$?\",ke=\"$!\",le=null,me=null;function ne(a,b){switch(a){case \"button\":case \"input\":case \"select\":case \"textarea\":return!!b.autoFocus}return!1}\nfunction oe(a,b){return\"textarea\"===a||\"option\"===a||\"noscript\"===a||\"string\"===typeof b.children||\"number\"===typeof b.children||\"object\"===typeof b.dangerouslySetInnerHTML&&null!==b.dangerouslySetInnerHTML&&null!=b.dangerouslySetInnerHTML.__html}var pe=\"function\"===typeof setTimeout?setTimeout:void 0,qe=\"function\"===typeof clearTimeout?clearTimeout:void 0;function re(a){for(;null!=a;a=a.nextSibling){var b=a.nodeType;if(1===b||3===b)break}return a}\nfunction se(a){a=a.previousSibling;for(var b=0;a;){if(8===a.nodeType){var c=a.data;if(c===he||c===ke||c===je){if(0===b)return a;b--}else c===ie&&b++}a=a.previousSibling}return null}var te=Math.random().toString(36).slice(2),ue=\"__reactInternalInstance$\"+te,ve=\"__reactEventHandlers$\"+te,we=\"__reactContainere$\"+te;\nfunction Fc(a){var b=a[ue];if(b)return b;for(var c=a.parentNode;c;){if(b=c[we]||c[ue]){c=b.alternate;if(null!==b.child||null!==c&&null!==c.child)for(a=se(a);null!==a;){if(c=a[ue])return c;a=se(a)}return b}a=c;c=a.parentNode}return null}function Cc(a){a=a[ue]||a[we];return!a||5!==a.tag&&6!==a.tag&&13!==a.tag&&3!==a.tag?null:a}function xe(a){if(5===a.tag||6===a.tag)return a.stateNode;throw Error(u(33));}function ye(a){return a[ve]||null}var ze=null,Ae=null,Be=null;\nfunction Ce(){if(Be)return Be;var a,b=Ae,c=b.length,d,e=\"value\"in ze?ze.value:ze.textContent,f=e.length;for(a=0;a=He),Ke=String.fromCharCode(32),Le={beforeInput:{phasedRegistrationNames:{bubbled:\"onBeforeInput\",captured:\"onBeforeInputCapture\"},dependencies:[\"compositionend\",\"keypress\",\"textInput\",\"paste\"]},compositionEnd:{phasedRegistrationNames:{bubbled:\"onCompositionEnd\",captured:\"onCompositionEndCapture\"},dependencies:\"blur compositionend keydown keypress keyup mousedown\".split(\" \")},compositionStart:{phasedRegistrationNames:{bubbled:\"onCompositionStart\",\ncaptured:\"onCompositionStartCapture\"},dependencies:\"blur compositionstart keydown keypress keyup mousedown\".split(\" \")},compositionUpdate:{phasedRegistrationNames:{bubbled:\"onCompositionUpdate\",captured:\"onCompositionUpdateCapture\"},dependencies:\"blur compositionupdate keydown keypress keyup mousedown\".split(\" \")}},Me=!1;\nfunction Ne(a,b){switch(a){case \"keyup\":return-1!==Fe.indexOf(b.keyCode);case \"keydown\":return 229!==b.keyCode;case \"keypress\":case \"mousedown\":case \"blur\":return!0;default:return!1}}function Oe(a){a=a.detail;return\"object\"===typeof a&&\"data\"in a?a.data:null}var Pe=!1;function Qe(a,b){switch(a){case \"compositionend\":return Oe(b);case \"keypress\":if(32!==b.which)return null;Me=!0;return Ke;case \"textInput\":return a=b.data,a===Ke&&Me?null:a;default:return null}}\nfunction Re(a,b){if(Pe)return\"compositionend\"===a||!Ge&&Ne(a,b)?(a=Ce(),Be=Ae=ze=null,Pe=!1,a):null;switch(a){case \"paste\":return null;case \"keypress\":if(!(b.ctrlKey||b.altKey||b.metaKey)||b.ctrlKey&&b.altKey){if(b.char&&1=document.documentMode,sf={select:{phasedRegistrationNames:{bubbled:\"onSelect\",captured:\"onSelectCapture\"},dependencies:\"blur contextmenu dragend focus keydown keyup mousedown mouseup selectionchange\".split(\" \")}},tf=null,uf=null,vf=null,wf=!1;\nfunction xf(a,b){var c=b.window===b?b.document:9===b.nodeType?b:b.ownerDocument;if(wf||null==tf||tf!==be(c))return null;c=tf;\"selectionStart\"in c&&ge(c)?c={start:c.selectionStart,end:c.selectionEnd}:(c=(c.ownerDocument&&c.ownerDocument.defaultView||window).getSelection(),c={anchorNode:c.anchorNode,anchorOffset:c.anchorOffset,focusNode:c.focusNode,focusOffset:c.focusOffset});return vf&&qf(vf,c)?null:(vf=c,a=E.getPooled(sf.select,uf,a,b),a.type=\"select\",a.target=tf,Sc(a),a)}\nvar yf={eventTypes:sf,extractEvents:function(a,b,c,d){var e=d.window===d?d.document:9===d.nodeType?d:d.ownerDocument,f;if(!(f=!e)){a:{e=xc(e);f=ja.onSelect;for(var g=0;gBf||(a.current=Af[Bf],Af[Bf]=null,Bf--)}\nfunction I(a,b){Bf++;Af[Bf]=a.current;a.current=b}var Cf={},J={current:Cf},K={current:!1},Df=Cf;function Ef(a,b){var c=a.type.contextTypes;if(!c)return Cf;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function L(a){a=a.childContextTypes;return null!==a&&void 0!==a}\nfunction Ff(a){G(K,a);G(J,a)}function Gf(a){G(K,a);G(J,a)}function Hf(a,b,c){if(J.current!==Cf)throw Error(u(168));I(J,b,a);I(K,c,a)}function If(a,b,c){var d=a.stateNode;a=b.childContextTypes;if(\"function\"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in a))throw Error(u(108,Wa(b)||\"Unknown\",e));return n({},c,{},d)}function Jf(a){var b=a.stateNode;b=b&&b.__reactInternalMemoizedMergedChildContext||Cf;Df=J.current;I(J,b,a);I(K,K.current,a);return!0}\nfunction Kf(a,b,c){var d=a.stateNode;if(!d)throw Error(u(169));c?(b=If(a,b,Df),d.__reactInternalMemoizedMergedChildContext=b,G(K,a),G(J,a),I(J,b,a)):G(K,a);I(K,c,a)}\nvar Lf=q.unstable_runWithPriority,Mf=q.unstable_scheduleCallback,Nf=q.unstable_cancelCallback,Of=q.unstable_shouldYield,Pf=q.unstable_requestPaint,Qf=q.unstable_now,Rf=q.unstable_getCurrentPriorityLevel,Sf=q.unstable_ImmediatePriority,Tf=q.unstable_UserBlockingPriority,Uf=q.unstable_NormalPriority,Vf=q.unstable_LowPriority,Wf=q.unstable_IdlePriority,Xf={},Yf=void 0!==Pf?Pf:function(){},Zf=null,$f=null,ag=!1,bg=Qf(),cg=1E4>bg?Qf:function(){return Qf()-bg};\nfunction dg(){switch(Rf()){case Sf:return 99;case Tf:return 98;case Uf:return 97;case Vf:return 96;case Wf:return 95;default:throw Error(u(332));}}function eg(a){switch(a){case 99:return Sf;case 98:return Tf;case 97:return Uf;case 96:return Vf;case 95:return Wf;default:throw Error(u(332));}}function fg(a,b){a=eg(a);return Lf(a,b)}function gg(a,b,c){a=eg(a);return Mf(a,b,c)}function hg(a){null===Zf?(Zf=[a],$f=Mf(Sf,ig)):Zf.push(a);return Xf}function jg(){if(null!==$f){var a=$f;$f=null;Nf(a)}ig()}\nfunction ig(){if(!ag&&null!==Zf){ag=!0;var a=0;try{var b=Zf;fg(99,function(){for(;a=b&&(wg=!0),a.firstContext=null)}function xg(a,b){if(qg!==a&&!1!==b&&0!==b){if(\"number\"!==typeof b||1073741823===b)qg=a,b=1073741823;b={context:a,observedBits:b,next:null};if(null===pg){if(null===og)throw Error(u(308));pg=b;og.dependencies={expirationTime:0,firstContext:b,responders:null}}else pg=pg.next=b}return a._currentValue}var yg=!1;\nfunction zg(a){return{baseState:a,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function Ag(a){return{baseState:a.baseState,firstUpdate:a.firstUpdate,lastUpdate:a.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}\nfunction Bg(a,b){return{expirationTime:a,suspenseConfig:b,tag:0,payload:null,callback:null,next:null,nextEffect:null}}function Cg(a,b){null===a.lastUpdate?a.firstUpdate=a.lastUpdate=b:(a.lastUpdate.next=b,a.lastUpdate=b)}\nfunction Dg(a,b){var c=a.alternate;if(null===c){var d=a.updateQueue;var e=null;null===d&&(d=a.updateQueue=zg(a.memoizedState))}else d=a.updateQueue,e=c.updateQueue,null===d?null===e?(d=a.updateQueue=zg(a.memoizedState),e=c.updateQueue=zg(c.memoizedState)):d=a.updateQueue=Ag(e):null===e&&(e=c.updateQueue=Ag(d));null===e||d===e?Cg(d,b):null===d.lastUpdate||null===e.lastUpdate?(Cg(d,b),Cg(e,b)):(Cg(d,b),e.lastUpdate=b)}\nfunction Eg(a,b){var c=a.updateQueue;c=null===c?a.updateQueue=zg(a.memoizedState):Fg(a,c);null===c.lastCapturedUpdate?c.firstCapturedUpdate=c.lastCapturedUpdate=b:(c.lastCapturedUpdate.next=b,c.lastCapturedUpdate=b)}function Fg(a,b){var c=a.alternate;null!==c&&b===c.updateQueue&&(b=a.updateQueue=Ag(b));return b}\nfunction Gg(a,b,c,d,e,f){switch(c.tag){case 1:return a=c.payload,\"function\"===typeof a?a.call(f,d,e):a;case 3:a.effectTag=a.effectTag&-4097|64;case 0:a=c.payload;e=\"function\"===typeof a?a.call(f,d,e):a;if(null===e||void 0===e)break;return n({},d,e);case 2:yg=!0}return d}\nfunction Hg(a,b,c,d,e){yg=!1;b=Fg(a,b);for(var f=b.baseState,g=null,h=0,k=b.firstUpdate,l=f;null!==k;){var m=k.expirationTime;mx?(A=r,r=null):A=r.sibling;var p=y(e,r,h[x],k);if(null===p){null===r&&(r=A);break}a&&\nr&&null===p.alternate&&b(e,r);g=f(p,g,x);null===m?l=p:m.sibling=p;m=p;r=A}if(x===h.length)return c(e,r),l;if(null===r){for(;xx?(A=r,r=null):A=r.sibling;var z=y(e,r,p.value,k);if(null===z){null===r&&(r=A);break}a&&r&&null===z.alternate&&b(e,r);g=f(z,g,x);null===m?l=z:m.sibling=z;m=z;r=A}if(p.done)return c(e,r),l;if(null===r){for(;!p.done;x++,p=h.next())p=C(e,p.value,k),null!==p&&(g=f(p,g,x),null===m?l=p:m.sibling=p,m=p);return l}for(r=d(e,r);!p.done;x++,p=h.next())p=H(r,e,x,p.value,k),null!==p&&(a&&null!==\np.alternate&&r.delete(null===p.key?x:p.key),g=f(p,g,x),null===m?l=p:m.sibling=p,m=p);a&&r.forEach(function(a){return b(e,a)});return l}return function(a,d,f,h){var k=\"object\"===typeof f&&null!==f&&f.type===Ia&&null===f.key;k&&(f=f.props.children);var l=\"object\"===typeof f&&null!==f;if(l)switch(f.$$typeof){case Ga:a:{l=f.key;for(k=d;null!==k;){if(k.key===l)if(7===k.tag?f.type===Ia:k.elementType===f.type){c(a,k.sibling);d=e(k,f.type===Ia?f.props.children:f.props,h);d.ref=Yg(a,k,f);d.return=a;a=d;break a}else{c(a,\nk);break}else b(a,k);k=k.sibling}f.type===Ia?(d=eh(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=ch(f.type,f.key,f.props,null,a.mode,h),h.ref=Yg(a,d,f),h.return=a,a=h)}return g(a);case Ha:a:{for(k=f.key;null!==d;){if(d.key===k)if(4===d.tag&&d.stateNode.containerInfo===f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[],h);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=dh(f,a.mode,h);d.return=a;a=d}return g(a)}if(\"string\"===\ntypeof f||\"number\"===typeof f)return f=\"\"+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f,h),d.return=a,a=d):(c(a,d),d=bh(f,a.mode,h),d.return=a,a=d),g(a);if(Xg(f))return z(a,d,f,h);if(Ua(f))return ta(a,d,f,h);l&&Zg(a,f);if(\"undefined\"===typeof f&&!k)switch(a.tag){case 1:case 0:throw a=a.type,Error(u(152,a.displayName||a.name||\"Component\"));}return c(a,d)}}var fh=$g(!0),gh=$g(!1),hh={},ih={current:hh},jh={current:hh},kh={current:hh};function lh(a){if(a===hh)throw Error(u(174));return a}\nfunction mh(a,b){I(kh,b,a);I(jh,a,a);I(ih,hh,a);var c=b.nodeType;switch(c){case 9:case 11:b=(b=b.documentElement)?b.namespaceURI:Qb(null,\"\");break;default:c=8===c?b.parentNode:b,b=c.namespaceURI||null,c=c.tagName,b=Qb(b,c)}G(ih,a);I(ih,b,a)}function nh(a){G(ih,a);G(jh,a);G(kh,a)}function oh(a){lh(kh.current);var b=lh(ih.current);var c=Qb(b,a.type);b!==c&&(I(jh,a,a),I(ih,c,a))}function ph(a){jh.current===a&&(G(ih,a),G(jh,a))}var M={current:0};\nfunction qh(a){for(var b=a;null!==b;){if(13===b.tag){var c=b.memoizedState;if(null!==c&&(c=c.dehydrated,null===c||c.data===je||c.data===ke))return b}else if(19===b.tag&&void 0!==b.memoizedProps.revealOrder){if(0!==(b.effectTag&64))return b}else if(null!==b.child){b.child.return=b;b=b.child;continue}if(b===a)break;for(;null===b.sibling;){if(null===b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}return null}function rh(a,b){return{responder:a,props:b}}\nvar sh=Ea.ReactCurrentDispatcher,N=Ea.ReactCurrentBatchConfig,th=0,uh=null,O=null,vh=null,wh=null,P=null,xh=null,yh=0,zh=null,Ah=0,Bh=!1,Ch=null,Gh=0;function Q(){throw Error(u(321));}function Hh(a,b){if(null===b)return!1;for(var c=0;cyh&&(yh=m,Jg(yh))):(Ig(m,k.suspenseConfig),f=k.eagerReducer===a?k.eagerState:a(f,k.action));g=k;k=k.next}while(null!==k&&k!==d);l||(h=g,e=f);of(f,b.memoizedState)||(wg=!0);b.memoizedState=f;b.baseUpdate=h;b.baseState=e;c.lastRenderedState=f}return[b.memoizedState,c.dispatch]}\nfunction Rh(a){var b=Nh();\"function\"===typeof a&&(a=a());b.memoizedState=b.baseState=a;a=b.queue={last:null,dispatch:null,lastRenderedReducer:Ph,lastRenderedState:a};a=a.dispatch=Sh.bind(null,uh,a);return[b.memoizedState,a]}function Th(a){return Qh(Ph,a)}function Uh(a,b,c,d){a={tag:a,create:b,destroy:c,deps:d,next:null};null===zh?(zh={lastEffect:null},zh.lastEffect=a.next=a):(b=zh.lastEffect,null===b?zh.lastEffect=a.next=a:(c=b.next,b.next=a,a.next=c,zh.lastEffect=a));return a}\nfunction Vh(a,b,c,d){var e=Nh();Ah|=a;e.memoizedState=Uh(b,c,void 0,void 0===d?null:d)}function Wh(a,b,c,d){var e=Oh();d=void 0===d?null:d;var f=void 0;if(null!==O){var g=O.memoizedState;f=g.destroy;if(null!==d&&Hh(d,g.deps)){Uh(0,c,f,d);return}}Ah|=a;e.memoizedState=Uh(b,c,f,d)}function Xh(a,b){return Vh(516,192,a,b)}function Yh(a,b){return Wh(516,192,a,b)}\nfunction Zh(a,b){if(\"function\"===typeof b)return a=a(),b(a),function(){b(null)};if(null!==b&&void 0!==b)return a=a(),b.current=a,function(){b.current=null}}function $h(){}function ai(a,b){Nh().memoizedState=[a,void 0===b?null:b];return a}function bi(a,b){var c=Oh();b=void 0===b?null:b;var d=c.memoizedState;if(null!==d&&null!==b&&Hh(b,d[1]))return d[0];c.memoizedState=[a,b];return a}\nfunction Sh(a,b,c){if(!(25>Gh))throw Error(u(301));var d=a.alternate;if(a===uh||null!==d&&d===uh)if(Bh=!0,a={expirationTime:th,suspenseConfig:null,action:c,eagerReducer:null,eagerState:null,next:null},null===Ch&&(Ch=new Map),c=Ch.get(b),void 0===c)Ch.set(b,a);else{for(b=c;null!==b.next;)b=b.next;b.next=a}else{var e=Pg(),f=Mg.suspense;e=Qg(e,a,f);f={expirationTime:e,suspenseConfig:f,action:c,eagerReducer:null,eagerState:null,next:null};var g=b.last;if(null===g)f.next=f;else{var h=g.next;null!==h&&\n(f.next=h);g.next=f}b.last=f;if(0===a.expirationTime&&(null===d||0===d.expirationTime)&&(d=b.lastRenderedReducer,null!==d))try{var k=b.lastRenderedState,l=d(k,c);f.eagerReducer=d;f.eagerState=l;if(of(l,k))return}catch(m){}finally{}Rg(a,e)}}\nvar Lh={readContext:xg,useCallback:Q,useContext:Q,useEffect:Q,useImperativeHandle:Q,useLayoutEffect:Q,useMemo:Q,useReducer:Q,useRef:Q,useState:Q,useDebugValue:Q,useResponder:Q,useDeferredValue:Q,useTransition:Q},Jh={readContext:xg,useCallback:ai,useContext:xg,useEffect:Xh,useImperativeHandle:function(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;return Vh(4,36,Zh.bind(null,b,a),c)},useLayoutEffect:function(a,b){return Vh(4,36,a,b)},useMemo:function(a,b){var c=Nh();b=void 0===b?null:b;a=a();c.memoizedState=\n[a,b];return a},useReducer:function(a,b,c){var d=Nh();b=void 0!==c?c(b):b;d.memoizedState=d.baseState=b;a=d.queue={last:null,dispatch:null,lastRenderedReducer:a,lastRenderedState:b};a=a.dispatch=Sh.bind(null,uh,a);return[d.memoizedState,a]},useRef:function(a){var b=Nh();a={current:a};return b.memoizedState=a},useState:Rh,useDebugValue:$h,useResponder:rh,useDeferredValue:function(a,b){var c=Rh(a),d=c[0],e=c[1];Xh(function(){q.unstable_next(function(){var c=N.suspense;N.suspense=void 0===b?null:b;try{e(a)}finally{N.suspense=\nc}})},[a,b]);return d},useTransition:function(a){var b=Rh(!1),c=b[0],d=b[1];return[ai(function(b){d(!0);q.unstable_next(function(){var c=N.suspense;N.suspense=void 0===a?null:a;try{d(!1),b()}finally{N.suspense=c}})},[a,c]),c]}},Kh={readContext:xg,useCallback:bi,useContext:xg,useEffect:Yh,useImperativeHandle:function(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;return Wh(4,36,Zh.bind(null,b,a),c)},useLayoutEffect:function(a,b){return Wh(4,36,a,b)},useMemo:function(a,b){var c=Oh();b=void 0===b?\nnull:b;var d=c.memoizedState;if(null!==d&&null!==b&&Hh(b,d[1]))return d[0];a=a();c.memoizedState=[a,b];return a},useReducer:Qh,useRef:function(){return Oh().memoizedState},useState:Th,useDebugValue:$h,useResponder:rh,useDeferredValue:function(a,b){var c=Th(a),d=c[0],e=c[1];Yh(function(){q.unstable_next(function(){var c=N.suspense;N.suspense=void 0===b?null:b;try{e(a)}finally{N.suspense=c}})},[a,b]);return d},useTransition:function(a){var b=Th(!1),c=b[0],d=b[1];return[bi(function(b){d(!0);q.unstable_next(function(){var c=\nN.suspense;N.suspense=void 0===a?null:a;try{d(!1),b()}finally{N.suspense=c}})},[a,c]),c]}},ci=null,di=null,ei=!1;function fi(a,b){var c=gi(5,null,null,0);c.elementType=\"DELETED\";c.type=\"DELETED\";c.stateNode=b;c.return=a;c.effectTag=8;null!==a.lastEffect?(a.lastEffect.nextEffect=c,a.lastEffect=c):a.firstEffect=a.lastEffect=c}\nfunction hi(a,b){switch(a.tag){case 5:var c=a.type;b=1!==b.nodeType||c.toLowerCase()!==b.nodeName.toLowerCase()?null:b;return null!==b?(a.stateNode=b,!0):!1;case 6:return b=\"\"===a.pendingProps||3!==b.nodeType?null:b,null!==b?(a.stateNode=b,!0):!1;case 13:return!1;default:return!1}}\nfunction ii(a){if(ei){var b=di;if(b){var c=b;if(!hi(a,b)){b=re(c.nextSibling);if(!b||!hi(a,b)){a.effectTag=a.effectTag&-1025|2;ei=!1;ci=a;return}fi(ci,c)}ci=a;di=re(b.firstChild)}else a.effectTag=a.effectTag&-1025|2,ei=!1,ci=a}}function ji(a){for(a=a.return;null!==a&&5!==a.tag&&3!==a.tag&&13!==a.tag;)a=a.return;ci=a}\nfunction ki(a){if(a!==ci)return!1;if(!ei)return ji(a),ei=!0,!1;var b=a.type;if(5!==a.tag||\"head\"!==b&&\"body\"!==b&&!oe(b,a.memoizedProps))for(b=di;b;)fi(a,b),b=re(b.nextSibling);ji(a);if(13===a.tag){a=a.memoizedState;a=null!==a?a.dehydrated:null;if(!a)throw Error(u(317));a:{a=a.nextSibling;for(b=0;a;){if(8===a.nodeType){var c=a.data;if(c===ie){if(0===b){di=re(a.nextSibling);break a}b--}else c!==he&&c!==ke&&c!==je||b++}a=a.nextSibling}di=null}}else di=ci?re(a.stateNode.nextSibling):null;return!0}\nfunction li(){di=ci=null;ei=!1}var mi=Ea.ReactCurrentOwner,wg=!1;function R(a,b,c,d){b.child=null===a?gh(b,null,c,d):fh(b,a.child,c,d)}function ni(a,b,c,d,e){c=c.render;var f=b.ref;vg(b,e);d=Ih(a,b,c,d,f,e);if(null!==a&&!wg)return b.updateQueue=a.updateQueue,b.effectTag&=-517,a.expirationTime<=e&&(a.expirationTime=0),oi(a,b,e);b.effectTag|=1;R(a,b,d,e);return b.child}\nfunction pi(a,b,c,d,e,f){if(null===a){var g=c.type;if(\"function\"===typeof g&&!qi(g)&&void 0===g.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=g,ri(a,b,g,d,e,f);a=ch(c.type,null,d,null,b.mode,f);a.ref=b.ref;a.return=b;return b.child=a}g=a.child;if(eb)&&Gj.set(a,b)))}}\nfunction Kj(a,b){a.expirationTimea?b:a}\nfunction Z(a){if(0!==a.lastExpiredTime)a.callbackExpirationTime=1073741823,a.callbackPriority=99,a.callbackNode=hg(Lj.bind(null,a));else{var b=Oj(a),c=a.callbackNode;if(0===b)null!==c&&(a.callbackNode=null,a.callbackExpirationTime=0,a.callbackPriority=90);else{var d=Pg();1073741823===b?d=99:1===b||2===b?d=95:(d=10*(1073741821-b)-10*(1073741821-d),d=0>=d?99:250>=d?98:5250>=d?97:95);if(null!==c){var e=a.callbackPriority;if(a.callbackExpirationTime===b&&e>=d)return;c!==Xf&&Nf(c)}a.callbackExpirationTime=\nb;a.callbackPriority=d;b=1073741823===b?hg(Lj.bind(null,a)):gg(d,Qj.bind(null,a),{timeout:10*(1073741821-b)-cg()});a.callbackNode=b}}}\nfunction Qj(a,b){Jj=0;if(b)return b=Pg(),Rj(a,b),Z(a),null;var c=Oj(a);if(0!==c){b=a.callbackNode;if((T&(oj|pj))!==S)throw Error(u(327));Sj();a===U&&c===W||Tj(a,c);if(null!==V){var d=T;T|=oj;var e=Uj(a);do try{Vj();break}catch(h){Wj(a,h)}while(1);rg();T=d;lj.current=e;if(X===rj)throw b=wj,Tj(a,c),Mj(a,c),Z(a),b;if(null===V)switch(e=a.finishedWork=a.current.alternate,a.finishedExpirationTime=c,d=X,U=null,d){case qj:case rj:throw Error(u(345));case sj:Rj(a,2=c){a.lastPingedTime=c;Tj(a,c);break}}f=Oj(a);if(0!==f&&f!==c)break;if(0!==d&&d!==c){a.lastPingedTime=d;break}a.timeoutHandle=pe(Yj.bind(null,a),e);break}Yj(a);break;case uj:Mj(a,c);d=a.lastSuspendedTime;c===d&&(a.nextKnownPendingLevel=Xj(e));if(Bj&&(e=a.lastPingedTime,0===e||e>=c)){a.lastPingedTime=c;Tj(a,c);break}e=Oj(a);if(0!==e&&e!==c)break;if(0!==d&&d!==c){a.lastPingedTime=\nd;break}1073741823!==yj?d=10*(1073741821-yj)-cg():1073741823===xj?d=0:(d=10*(1073741821-xj)-5E3,e=cg(),c=10*(1073741821-c)-e,d=e-d,0>d&&(d=0),d=(120>d?120:480>d?480:1080>d?1080:1920>d?1920:3E3>d?3E3:4320>d?4320:1960*kj(d/1960))-d,c=d?d=0:(e=g.busyDelayMs|0,f=cg()-(10*(1073741821-f)-(g.timeoutMs|0||5E3)),d=f<=e?0:e+d-f);if(10 component higher in the tree to provide a loading indicator or placeholder to display.\"+Xa(e))}X!==vj&&(X=sj);f=Ni(f,e);k=d;do{switch(k.tag){case 3:g=f;k.effectTag|=4096;k.expirationTime=b;var x=fj(k,g,b);Eg(k,x);break a;case 1:g=f;var A=k.type,p=k.stateNode;if(0===(k.effectTag&64)&&(\"function\"===typeof A.getDerivedStateFromError||null!==p&&\"function\"===typeof p.componentDidCatch&&\n(null===jj||!jj.has(p)))){k.effectTag|=4096;k.expirationTime=b;var t=ij(k,g,b);Eg(k,t);break a}}k=k.return}while(null!==k)}V=ek(V)}catch(v){b=v;continue}break}while(1)}function Uj(){var a=lj.current;lj.current=Lh;return null===a?Lh:a}function Ig(a,b){aAj&&(Aj=a)}function Zj(){for(;null!==V;)V=fk(V)}function Vj(){for(;null!==V&&!Of();)V=fk(V)}\nfunction fk(a){var b=gk(a.alternate,a,W);a.memoizedProps=a.pendingProps;null===b&&(b=ek(a));mj.current=null;return b}\nfunction ek(a){V=a;do{var b=V.alternate;a=V.return;if(0===(V.effectTag&2048)){a:{var c=b;b=V;var d=W;var e=b.pendingProps;switch(b.tag){case 2:break;case 16:break;case 15:case 0:break;case 1:L(b.type)&&Ff(b);break;case 3:nh(b);Gf(b);e=b.stateNode;e.pendingContext&&(e.context=e.pendingContext,e.pendingContext=null);(null===c||null===c.child)&&ki(b)&&Ci(b);Ii(b);break;case 5:ph(b);d=lh(kh.current);var f=b.type;if(null!==c&&null!=b.stateNode)Ji(c,b,f,e,d),c.ref!==b.ref&&(b.effectTag|=128);else if(e){var g=\nlh(ih.current);if(ki(b)){e=b;var h=e.stateNode;c=e.type;var k=e.memoizedProps,l=d;h[ue]=e;h[ve]=k;f=void 0;d=h;switch(c){case \"iframe\":case \"object\":case \"embed\":F(\"load\",d);break;case \"video\":case \"audio\":for(h=0;h\\x3c/script>\",h=k.removeChild(k.firstChild)):\"string\"===typeof k.is?h=h.createElement(l,{is:k.is}):(h=h.createElement(l),\"select\"===l&&(l=h,k.multiple?l.multiple=!0:k.size&&(l.size=k.size))):h=h.createElementNS(g,l);k=h;k[ue]=c;k[ve]=e;Hi(k,b,!1,!1);b.stateNode=k;l=f;c=e;var m=d,C=Zd(l,c);switch(l){case \"iframe\":case \"object\":case \"embed\":F(\"load\",\nk);d=c;break;case \"video\":case \"audio\":for(d=0;de.tailExpiration&&1f&&(f=c),k>f&&(f=k),d=d.sibling;e.childExpirationTime=f}if(null!==b)return b;null!==a&&0===(a.effectTag&2048)&&(null===a.firstEffect&&(a.firstEffect=V.firstEffect),null!==V.lastEffect&&(null!==a.lastEffect&&(a.lastEffect.nextEffect=\nV.firstEffect),a.lastEffect=V.lastEffect),1a?b:a}function Yj(a){var b=dg();fg(99,ik.bind(null,a,b));return null}\nfunction ik(a,b){do Sj();while(null!==Ej);if((T&(oj|pj))!==S)throw Error(u(327));var c=a.finishedWork,d=a.finishedExpirationTime;if(null===c)return null;a.finishedWork=null;a.finishedExpirationTime=0;if(c===a.current)throw Error(u(177));a.callbackNode=null;a.callbackExpirationTime=0;a.callbackPriority=90;a.nextKnownPendingLevel=0;var e=Xj(c);a.firstPendingTime=e;d<=a.lastSuspendedTime?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=0:d<=a.firstSuspendedTime&&(a.firstSuspendedTime=\nd-1);d<=a.lastPingedTime&&(a.lastPingedTime=0);d<=a.lastExpiredTime&&(a.lastExpiredTime=0);a===U&&(V=U=null,W=0);1h&&(l=h,h=g,g=l),l=de(p,g),m=de(p,h),l&&m&&(1!==v.rangeCount||v.anchorNode!==l.node||v.anchorOffset!==l.offset||v.focusNode!==m.node||v.focusOffset!==m.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),v.removeAllRanges(),g>h?(v.addRange(t),v.extend(m.node,m.offset)):(t.setEnd(m.node,m.offset),v.addRange(t))))));t=[];for(v=p;v=v.parentNode;)1===v.nodeType&&t.push({element:v,left:v.scrollLeft,top:v.scrollTop});\n\"function\"===typeof p.focus&&p.focus();for(p=0;p=c)return yi(a,b,c);I(M,M.current&\n1,b);b=oi(a,b,c);return null!==b?b.sibling:null}I(M,M.current&1,b);break;case 19:d=b.childExpirationTime>=c;if(0!==(a.effectTag&64)){if(d)return Bi(a,b,c);b.effectTag|=64}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null);I(M,M.current,b);if(!d)return null}return oi(a,b,c)}wg=!1}}else wg=!1;b.expirationTime=0;switch(b.tag){case 2:d=b.type;null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2);a=b.pendingProps;e=Ef(b,J.current);vg(b,c);e=Ih(null,b,d,a,e,c);b.effectTag|=1;if(\"object\"===\ntypeof e&&null!==e&&\"function\"===typeof e.render&&void 0===e.$$typeof){b.tag=1;Mh();if(L(d)){var f=!0;Jf(b)}else f=!1;b.memoizedState=null!==e.state&&void 0!==e.state?e.state:null;var g=d.getDerivedStateFromProps;\"function\"===typeof g&&Og(b,d,g,a);e.updater=Sg;b.stateNode=e;e._reactInternalFiber=b;Wg(b,d,a,c);b=vi(null,b,d,!0,f,c)}else b.tag=0,R(null,b,e,c),b=b.child;return b;case 16:e=b.elementType;null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2);a=b.pendingProps;Va(e);if(1!==e._status)throw e._result;\ne=e._result;b.type=e;f=b.tag=nk(e);a=mg(e,a);switch(f){case 0:b=si(null,b,e,a,c);break;case 1:b=ui(null,b,e,a,c);break;case 11:b=ni(null,b,e,a,c);break;case 14:b=pi(null,b,e,mg(e.type,a),d,c);break;default:throw Error(u(306,e,\"\"));}return b;case 0:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:mg(d,e),si(a,b,d,e,c);case 1:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:mg(d,e),ui(a,b,d,e,c);case 3:wi(b);d=b.updateQueue;if(null===d)throw Error(u(282));e=b.memoizedState;e=null!==e?e.element:\nnull;Hg(b,d,b.pendingProps,null,c);d=b.memoizedState.element;if(d===e)li(),b=oi(a,b,c);else{if(e=b.stateNode.hydrate)di=re(b.stateNode.containerInfo.firstChild),ci=b,e=ei=!0;if(e)for(c=gh(b,null,d,c),b.child=c;c;)c.effectTag=c.effectTag&-3|1024,c=c.sibling;else R(a,b,d,c),li();b=b.child}return b;case 5:return oh(b),null===a&&ii(b),d=b.type,e=b.pendingProps,f=null!==a?a.memoizedProps:null,g=e.children,oe(d,e)?g=null:null!==f&&oe(d,f)&&(b.effectTag|=16),ti(a,b),b.mode&4&&1!==c&&e.hidden?(b.expirationTime=\nb.childExpirationTime=1,b=null):(R(a,b,g,c),b=b.child),b;case 6:return null===a&&ii(b),null;case 13:return yi(a,b,c);case 4:return mh(b,b.stateNode.containerInfo),d=b.pendingProps,null===a?b.child=fh(b,null,d,c):R(a,b,d,c),b.child;case 11:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:mg(d,e),ni(a,b,d,e,c);case 7:return R(a,b,b.pendingProps,c),b.child;case 8:return R(a,b,b.pendingProps.children,c),b.child;case 12:return R(a,b,b.pendingProps.children,c),b.child;case 10:a:{d=b.type._context;\ne=b.pendingProps;g=b.memoizedProps;f=e.value;sg(b,f);if(null!==g){var h=g.value;f=of(h,f)?0:(\"function\"===typeof d._calculateChangedBits?d._calculateChangedBits(h,f):1073741823)|0;if(0===f){if(g.children===e.children&&!K.current){b=oi(a,b,c);break a}}else for(h=b.child,null!==h&&(h.return=b);null!==h;){var k=h.dependencies;if(null!==k){g=h.child;for(var l=k.firstContext;null!==l;){if(l.context===d&&0!==(l.observedBits&f)){1===h.tag&&(l=Bg(c,null),l.tag=2,Dg(h,l));h.expirationTime=b&&a<=b}function Mj(a,b){var c=a.firstSuspendedTime,d=a.lastSuspendedTime;cb||0===c)a.lastSuspendedTime=b;b<=a.lastPingedTime&&(a.lastPingedTime=0);b<=a.lastExpiredTime&&(a.lastExpiredTime=0)}\nfunction Nj(a,b){b>a.firstPendingTime&&(a.firstPendingTime=b);var c=a.firstSuspendedTime;0!==c&&(b>=c?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=0:b>=a.lastSuspendedTime&&(a.lastSuspendedTime=b+1),b>a.nextKnownPendingLevel&&(a.nextKnownPendingLevel=b))}function Rj(a,b){var c=a.lastExpiredTime;if(0===c||c>b)a.lastExpiredTime=b}\nfunction rk(a,b,c,d){var e=b.current,f=Pg(),g=Mg.suspense;f=Qg(f,e,g);a:if(c){c=c._reactInternalFiber;b:{if(ec(c)!==c||1!==c.tag)throw Error(u(170));var h=c;do{switch(h.tag){case 3:h=h.stateNode.context;break b;case 1:if(L(h.type)){h=h.stateNode.__reactInternalMemoizedMergedChildContext;break b}}h=h.return}while(null!==h);throw Error(u(171));}if(1===c.tag){var k=c.type;if(L(k)){c=If(c,k,h);break a}}c=h}else c=Cf;null===b.context?b.context=c:b.pendingContext=c;b=Bg(f,g);b.payload={element:a};d=void 0===\nd?null:d;null!==d&&(b.callback=d);Dg(e,b);Rg(e,f);return f}function sk(a){a=a.current;if(!a.child)return null;switch(a.child.tag){case 5:return a.child.stateNode;default:return a.child.stateNode}}function tk(a,b){a=a.memoizedState;null!==a&&null!==a.dehydrated&&a.retryTime=G};l=function(){};exports.unstable_forceFrameRate=function(a){0>a||125K(n,c))void 0!==r&&0>K(r,n)?(a[d]=r,a[v]=c,d=v):(a[d]=n,a[m]=c,d=m);else if(void 0!==r&&0>K(r,c))a[d]=r,a[v]=c,d=v;else break a}}return b}return null}function K(a,b){var c=a.sortIndex-b.sortIndex;return 0!==c?c:a.id-b.id}var N=[],O=[],P=1,Q=null,R=3,S=!1,T=!1,U=!1;\nfunction V(a){for(var b=L(O);null!==b;){if(null===b.callback)M(O);else if(b.startTime<=a)M(O),b.sortIndex=b.expirationTime,J(N,b);else break;b=L(O)}}function W(a){U=!1;V(a);if(!T)if(null!==L(N))T=!0,f(X);else{var b=L(O);null!==b&&g(W,b.startTime-a)}}\nfunction X(a,b){T=!1;U&&(U=!1,h());S=!0;var c=R;try{V(b);for(Q=L(N);null!==Q&&(!(Q.expirationTime>b)||a&&!k());){var d=Q.callback;if(null!==d){Q.callback=null;R=Q.priorityLevel;var e=d(Q.expirationTime<=b);b=exports.unstable_now();\"function\"===typeof e?Q.callback=e:Q===L(N)&&M(N);V(b)}else M(N);Q=L(N)}if(null!==Q)var m=!0;else{var n=L(O);null!==n&&g(W,n.startTime-b);m=!1}return m}finally{Q=null,R=c,S=!1}}\nfunction Y(a){switch(a){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1E4;default:return 5E3}}var Z=l;exports.unstable_ImmediatePriority=1;exports.unstable_UserBlockingPriority=2;exports.unstable_NormalPriority=3;exports.unstable_IdlePriority=5;exports.unstable_LowPriority=4;exports.unstable_runWithPriority=function(a,b){switch(a){case 1:case 2:case 3:case 4:case 5:break;default:a=3}var c=R;R=a;try{return b()}finally{R=c}};\nexports.unstable_next=function(a){switch(R){case 1:case 2:case 3:var b=3;break;default:b=R}var c=R;R=b;try{return a()}finally{R=c}};\nexports.unstable_scheduleCallback=function(a,b,c){var d=exports.unstable_now();if(\"object\"===typeof c&&null!==c){var e=c.delay;e=\"number\"===typeof e&&0d?(a.sortIndex=e,J(O,a),null===L(N)&&a===L(O)&&(U?h():U=!0,g(W,e-d))):(a.sortIndex=c,J(N,a),T||S||(T=!0,f(X)));return a};exports.unstable_cancelCallback=function(a){a.callback=null};\nexports.unstable_wrapCallback=function(a){var b=R;return function(){var c=R;R=b;try{return a.apply(this,arguments)}finally{R=c}}};exports.unstable_getCurrentPriorityLevel=function(){return R};exports.unstable_shouldYield=function(){var a=exports.unstable_now();V(a);var b=L(N);return b!==Q&&null!==Q&&null!==b&&null!==b.callback&&b.startTime<=a&&b.expirationTime 0 && arguments[0] !== undefined ? arguments[0] : {};\n var defaultState = arguments.length > 1 ? arguments[1] : undefined;\n var opts = {\n payload: true,\n fallback: null\n };\n\n var reducer = _extends(reduce, {\n has: has,\n on: on,\n off: off,\n options: options\n });\n\n function has(typeOrActionCreator) {\n return !!handlers[normalizeType(typeOrActionCreator)];\n }\n\n function on(typeOrActionCreator, handler) {\n if (Array.isArray(typeOrActionCreator)) {\n typeOrActionCreator.forEach(function (action) {\n on(action, handler);\n });\n } else {\n handlers[normalizeType(typeOrActionCreator)] = handler;\n }\n\n return reducer;\n }\n\n function off(typeOrActionCreator) {\n if (Array.isArray(typeOrActionCreator)) {\n typeOrActionCreator.forEach(off);\n } else {\n delete handlers[normalizeType(typeOrActionCreator)];\n }\n\n return reducer;\n }\n\n function options(newOpts) {\n Object.keys(newOpts).forEach(function (name) {\n return opts[name] = newOpts[name];\n });\n return reducer;\n }\n\n if (typeof handlers === 'function') {\n var factory = handlers;\n handlers = {};\n factory(on, off);\n }\n\n if (!has(_batch[\"default\"])) {\n on(_batch[\"default\"], function (state, payload) {\n if (opts.payload) {\n return payload.reduce(reduce, state);\n } else {\n return payload.payload.reduce(reduce, state);\n }\n });\n }\n\n function reduce() {\n var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultState;\n var action = arguments.length > 1 ? arguments[1] : undefined;\n\n if (!action || typeof action.type !== 'string') {\n return state;\n }\n\n if (action.type.startsWith('@@redux/')) {\n return state;\n }\n\n var handler = handlers[action.type] || opts.fallback;\n\n if (handler) {\n if (opts.payload) {\n return handler(state, action.payload, action.meta);\n } else {\n return handler(state, action);\n }\n }\n\n return state;\n }\n\n ;\n return reducer;\n}\n\n;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = assignAll;\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction assignAll(actions, stores) {\n if (Array.isArray(actions)) {\n return actions.map(function (action) {\n return action.assignTo(stores);\n });\n }\n\n return Object.keys(actions).reduce(function (assigns, action) {\n return _extends(assigns, _defineProperty({}, action, actions[action].assignTo(stores)));\n }, {});\n}\n\n;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = bindAll;\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction bindAll(actions, stores) {\n if (Array.isArray(actions)) {\n return actions.map(function (action) {\n return action.bindTo(stores);\n });\n }\n\n return Object.keys(actions).reduce(function (binds, action) {\n return _extends(binds, _defineProperty({}, action, actions[action].bindTo(stores)));\n }, {});\n}\n\n;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = disbatch;\n\nvar _batch = _interopRequireDefault(require(\"./batch\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction disbatch(store) {\n for (var _len = arguments.length, actions = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n actions[_key - 1] = arguments[_key];\n }\n\n if (actions && actions.length > 0) {\n if (!store || typeof store !== 'function' && typeof store.dispatch !== 'function') {\n throw new TypeError('disbatch must take either a valid Redux store or a dispatch function as first parameter');\n }\n\n if (typeof store.dispatch === 'function') {\n store = store.dispatch;\n } // store is actually the dispatch function here\n\n\n return store(_batch[\"default\"].apply(void 0, actions));\n } else {\n if (!store || typeof store.dispatch !== 'function') {\n throw new TypeError('disbatch must take a valid Redux store with a dispatch function as first parameter');\n }\n\n return _extends(store, {\n disbatch: disbatch.bind(undefined, store)\n });\n }\n}","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = void 0;\n\nvar reduxLogger = _interopRequireWildcard(require(\"./reduxLogger\"));\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj[\"default\"] = obj; return newObj; } }\n\nvar _default = {\n reduxLogger: reduxLogger\n};\nexports[\"default\"] = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.actionTransformer = actionTransformer;\nexports.logger = void 0;\n\nvar _batch = _interopRequireDefault(require(\"../batch\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\nvar batchType = _batch[\"default\"].getType();\n\nfunction actionTransformer(action) {\n if (action && action.type === batchType) {\n action.payload.type = batchType;\n return action.payload;\n }\n\n return action;\n}\n\nvar logger = {};\nexports.logger = logger;\n\nvar _loop = function _loop(level) {\n if (typeof console[level] === 'function') {\n logger[level] = function levelFn() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n var lastArg = args.pop();\n\n if (Array.isArray(lastArg) && lastArg.type === batchType) {\n lastArg.forEach(function (action) {\n console[level].apply(console, [].concat(args, [action]));\n });\n } else {\n args.push(lastArg);\n console[level].apply(console, args);\n }\n };\n }\n};\n\nfor (var level in console) {\n _loop(level);\n}","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports[\"default\"] = asError;\n\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction asError(action) {\n if (_typeof(action) === 'object' && action !== null) {\n action.error = true;\n }\n\n return action;\n}\n\n;","/**\n * Copyright (c) 2014-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nvar runtime = (function (exports) {\n \"use strict\";\n\n var Op = Object.prototype;\n var hasOwn = Op.hasOwnProperty;\n var undefined; // More compressible than void 0.\n var $Symbol = typeof Symbol === \"function\" ? Symbol : {};\n var iteratorSymbol = $Symbol.iterator || \"@@iterator\";\n var asyncIteratorSymbol = $Symbol.asyncIterator || \"@@asyncIterator\";\n var toStringTagSymbol = $Symbol.toStringTag || \"@@toStringTag\";\n\n function wrap(innerFn, outerFn, self, tryLocsList) {\n // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.\n var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;\n var generator = Object.create(protoGenerator.prototype);\n var context = new Context(tryLocsList || []);\n\n // The ._invoke method unifies the implementations of the .next,\n // .throw, and .return methods.\n generator._invoke = makeInvokeMethod(innerFn, self, context);\n\n return generator;\n }\n exports.wrap = wrap;\n\n // Try/catch helper to minimize deoptimizations. Returns a completion\n // record like context.tryEntries[i].completion. This interface could\n // have been (and was previously) designed to take a closure to be\n // invoked without arguments, but in all the cases we care about we\n // already have an existing method we want to call, so there's no need\n // to create a new function object. We can even get away with assuming\n // the method takes exactly one argument, since that happens to be true\n // in every case, so we don't have to touch the arguments object. The\n // only additional allocation required is the completion record, which\n // has a stable shape and so hopefully should be cheap to allocate.\n function tryCatch(fn, obj, arg) {\n try {\n return { type: \"normal\", arg: fn.call(obj, arg) };\n } catch (err) {\n return { type: \"throw\", arg: err };\n }\n }\n\n var GenStateSuspendedStart = \"suspendedStart\";\n var GenStateSuspendedYield = \"suspendedYield\";\n var GenStateExecuting = \"executing\";\n var GenStateCompleted = \"completed\";\n\n // Returning this object from the innerFn has the same effect as\n // breaking out of the dispatch switch statement.\n var ContinueSentinel = {};\n\n // Dummy constructor functions that we use as the .constructor and\n // .constructor.prototype properties for functions that return Generator\n // objects. For full spec compliance, you may wish to configure your\n // minifier not to mangle the names of these two functions.\n function Generator() {}\n function GeneratorFunction() {}\n function GeneratorFunctionPrototype() {}\n\n // This is a polyfill for %IteratorPrototype% for environments that\n // don't natively support it.\n var IteratorPrototype = {};\n IteratorPrototype[iteratorSymbol] = function () {\n return this;\n };\n\n var getProto = Object.getPrototypeOf;\n var NativeIteratorPrototype = getProto && getProto(getProto(values([])));\n if (NativeIteratorPrototype &&\n NativeIteratorPrototype !== Op &&\n hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {\n // This environment has a native %IteratorPrototype%; use it instead\n // of the polyfill.\n IteratorPrototype = NativeIteratorPrototype;\n }\n\n var Gp = GeneratorFunctionPrototype.prototype =\n Generator.prototype = Object.create(IteratorPrototype);\n GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;\n GeneratorFunctionPrototype.constructor = GeneratorFunction;\n GeneratorFunctionPrototype[toStringTagSymbol] =\n GeneratorFunction.displayName = \"GeneratorFunction\";\n\n // Helper for defining the .next, .throw, and .return methods of the\n // Iterator interface in terms of a single ._invoke method.\n function defineIteratorMethods(prototype) {\n [\"next\", \"throw\", \"return\"].forEach(function(method) {\n prototype[method] = function(arg) {\n return this._invoke(method, arg);\n };\n });\n }\n\n exports.isGeneratorFunction = function(genFun) {\n var ctor = typeof genFun === \"function\" && genFun.constructor;\n return ctor\n ? ctor === GeneratorFunction ||\n // For the native GeneratorFunction constructor, the best we can\n // do is to check its .name property.\n (ctor.displayName || ctor.name) === \"GeneratorFunction\"\n : false;\n };\n\n exports.mark = function(genFun) {\n if (Object.setPrototypeOf) {\n Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);\n } else {\n genFun.__proto__ = GeneratorFunctionPrototype;\n if (!(toStringTagSymbol in genFun)) {\n genFun[toStringTagSymbol] = \"GeneratorFunction\";\n }\n }\n genFun.prototype = Object.create(Gp);\n return genFun;\n };\n\n // Within the body of any async function, `await x` is transformed to\n // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test\n // `hasOwn.call(value, \"__await\")` to determine if the yielded value is\n // meant to be awaited.\n exports.awrap = function(arg) {\n return { __await: arg };\n };\n\n function AsyncIterator(generator) {\n function invoke(method, arg, resolve, reject) {\n var record = tryCatch(generator[method], generator, arg);\n if (record.type === \"throw\") {\n reject(record.arg);\n } else {\n var result = record.arg;\n var value = result.value;\n if (value &&\n typeof value === \"object\" &&\n hasOwn.call(value, \"__await\")) {\n return Promise.resolve(value.__await).then(function(value) {\n invoke(\"next\", value, resolve, reject);\n }, function(err) {\n invoke(\"throw\", err, resolve, reject);\n });\n }\n\n return Promise.resolve(value).then(function(unwrapped) {\n // When a yielded Promise is resolved, its final value becomes\n // the .value of the Promise<{value,done}> result for the\n // current iteration.\n result.value = unwrapped;\n resolve(result);\n }, function(error) {\n // If a rejected Promise was yielded, throw the rejection back\n // into the async generator function so it can be handled there.\n return invoke(\"throw\", error, resolve, reject);\n });\n }\n }\n\n var previousPromise;\n\n function enqueue(method, arg) {\n function callInvokeWithMethodAndArg() {\n return new Promise(function(resolve, reject) {\n invoke(method, arg, resolve, reject);\n });\n }\n\n return previousPromise =\n // If enqueue has been called before, then we want to wait until\n // all previous Promises have been resolved before calling invoke,\n // so that results are always delivered in the correct order. If\n // enqueue has not been called before, then it is important to\n // call invoke immediately, without waiting on a callback to fire,\n // so that the async generator function has the opportunity to do\n // any necessary setup in a predictable way. This predictability\n // is why the Promise constructor synchronously invokes its\n // executor callback, and why async functions synchronously\n // execute code before the first await. Since we implement simple\n // async functions in terms of async generators, it is especially\n // important to get this right, even though it requires care.\n previousPromise ? previousPromise.then(\n callInvokeWithMethodAndArg,\n // Avoid propagating failures to Promises returned by later\n // invocations of the iterator.\n callInvokeWithMethodAndArg\n ) : callInvokeWithMethodAndArg();\n }\n\n // Define the unified helper method that is used to implement .next,\n // .throw, and .return (see defineIteratorMethods).\n this._invoke = enqueue;\n }\n\n defineIteratorMethods(AsyncIterator.prototype);\n AsyncIterator.prototype[asyncIteratorSymbol] = function () {\n return this;\n };\n exports.AsyncIterator = AsyncIterator;\n\n // Note that simple async functions are implemented on top of\n // AsyncIterator objects; they just return a Promise for the value of\n // the final result produced by the iterator.\n exports.async = function(innerFn, outerFn, self, tryLocsList) {\n var iter = new AsyncIterator(\n wrap(innerFn, outerFn, self, tryLocsList)\n );\n\n return exports.isGeneratorFunction(outerFn)\n ? iter // If outerFn is a generator, return the full iterator.\n : iter.next().then(function(result) {\n return result.done ? result.value : iter.next();\n });\n };\n\n function makeInvokeMethod(innerFn, self, context) {\n var state = GenStateSuspendedStart;\n\n return function invoke(method, arg) {\n if (state === GenStateExecuting) {\n throw new Error(\"Generator is already running\");\n }\n\n if (state === GenStateCompleted) {\n if (method === \"throw\") {\n throw arg;\n }\n\n // Be forgiving, per 25.3.3.3.3 of the spec:\n // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume\n return doneResult();\n }\n\n context.method = method;\n context.arg = arg;\n\n while (true) {\n var delegate = context.delegate;\n if (delegate) {\n var delegateResult = maybeInvokeDelegate(delegate, context);\n if (delegateResult) {\n if (delegateResult === ContinueSentinel) continue;\n return delegateResult;\n }\n }\n\n if (context.method === \"next\") {\n // Setting context._sent for legacy support of Babel's\n // function.sent implementation.\n context.sent = context._sent = context.arg;\n\n } else if (context.method === \"throw\") {\n if (state === GenStateSuspendedStart) {\n state = GenStateCompleted;\n throw context.arg;\n }\n\n context.dispatchException(context.arg);\n\n } else if (context.method === \"return\") {\n context.abrupt(\"return\", context.arg);\n }\n\n state = GenStateExecuting;\n\n var record = tryCatch(innerFn, self, context);\n if (record.type === \"normal\") {\n // If an exception is thrown from innerFn, we leave state ===\n // GenStateExecuting and loop back for another invocation.\n state = context.done\n ? GenStateCompleted\n : GenStateSuspendedYield;\n\n if (record.arg === ContinueSentinel) {\n continue;\n }\n\n return {\n value: record.arg,\n done: context.done\n };\n\n } else if (record.type === \"throw\") {\n state = GenStateCompleted;\n // Dispatch the exception by looping back around to the\n // context.dispatchException(context.arg) call above.\n context.method = \"throw\";\n context.arg = record.arg;\n }\n }\n };\n }\n\n // Call delegate.iterator[context.method](context.arg) and handle the\n // result, either by returning a { value, done } result from the\n // delegate iterator, or by modifying context.method and context.arg,\n // setting context.delegate to null, and returning the ContinueSentinel.\n function maybeInvokeDelegate(delegate, context) {\n var method = delegate.iterator[context.method];\n if (method === undefined) {\n // A .throw or .return when the delegate iterator has no .throw\n // method always terminates the yield* loop.\n context.delegate = null;\n\n if (context.method === \"throw\") {\n // Note: [\"return\"] must be used for ES3 parsing compatibility.\n if (delegate.iterator[\"return\"]) {\n // If the delegate iterator has a return method, give it a\n // chance to clean up.\n context.method = \"return\";\n context.arg = undefined;\n maybeInvokeDelegate(delegate, context);\n\n if (context.method === \"throw\") {\n // If maybeInvokeDelegate(context) changed context.method from\n // \"return\" to \"throw\", let that override the TypeError below.\n return ContinueSentinel;\n }\n }\n\n context.method = \"throw\";\n context.arg = new TypeError(\n \"The iterator does not provide a 'throw' method\");\n }\n\n return ContinueSentinel;\n }\n\n var record = tryCatch(method, delegate.iterator, context.arg);\n\n if (record.type === \"throw\") {\n context.method = \"throw\";\n context.arg = record.arg;\n context.delegate = null;\n return ContinueSentinel;\n }\n\n var info = record.arg;\n\n if (! info) {\n context.method = \"throw\";\n context.arg = new TypeError(\"iterator result is not an object\");\n context.delegate = null;\n return ContinueSentinel;\n }\n\n if (info.done) {\n // Assign the result of the finished delegate to the temporary\n // variable specified by delegate.resultName (see delegateYield).\n context[delegate.resultName] = info.value;\n\n // Resume execution at the desired location (see delegateYield).\n context.next = delegate.nextLoc;\n\n // If context.method was \"throw\" but the delegate handled the\n // exception, let the outer generator proceed normally. If\n // context.method was \"next\", forget context.arg since it has been\n // \"consumed\" by the delegate iterator. If context.method was\n // \"return\", allow the original .return call to continue in the\n // outer generator.\n if (context.method !== \"return\") {\n context.method = \"next\";\n context.arg = undefined;\n }\n\n } else {\n // Re-yield the result returned by the delegate method.\n return info;\n }\n\n // The delegate iterator is finished, so forget it and continue with\n // the outer generator.\n context.delegate = null;\n return ContinueSentinel;\n }\n\n // Define Generator.prototype.{next,throw,return} in terms of the\n // unified ._invoke helper method.\n defineIteratorMethods(Gp);\n\n Gp[toStringTagSymbol] = \"Generator\";\n\n // A Generator should always return itself as the iterator object when the\n // @@iterator function is called on it. Some browsers' implementations of the\n // iterator prototype chain incorrectly implement this, causing the Generator\n // object to not be returned from this call. This ensures that doesn't happen.\n // See https://github.com/facebook/regenerator/issues/274 for more details.\n Gp[iteratorSymbol] = function() {\n return this;\n };\n\n Gp.toString = function() {\n return \"[object Generator]\";\n };\n\n function pushTryEntry(locs) {\n var entry = { tryLoc: locs[0] };\n\n if (1 in locs) {\n entry.catchLoc = locs[1];\n }\n\n if (2 in locs) {\n entry.finallyLoc = locs[2];\n entry.afterLoc = locs[3];\n }\n\n this.tryEntries.push(entry);\n }\n\n function resetTryEntry(entry) {\n var record = entry.completion || {};\n record.type = \"normal\";\n delete record.arg;\n entry.completion = record;\n }\n\n function Context(tryLocsList) {\n // The root entry object (effectively a try statement without a catch\n // or a finally block) gives us a place to store values thrown from\n // locations where there is no enclosing try statement.\n this.tryEntries = [{ tryLoc: \"root\" }];\n tryLocsList.forEach(pushTryEntry, this);\n this.reset(true);\n }\n\n exports.keys = function(object) {\n var keys = [];\n for (var key in object) {\n keys.push(key);\n }\n keys.reverse();\n\n // Rather than returning an object with a next method, we keep\n // things simple and return the next function itself.\n return function next() {\n while (keys.length) {\n var key = keys.pop();\n if (key in object) {\n next.value = key;\n next.done = false;\n return next;\n }\n }\n\n // To avoid creating an additional object, we just hang the .value\n // and .done properties off the next function object itself. This\n // also ensures that the minifier will not anonymize the function.\n next.done = true;\n return next;\n };\n };\n\n function values(iterable) {\n if (iterable) {\n var iteratorMethod = iterable[iteratorSymbol];\n if (iteratorMethod) {\n return iteratorMethod.call(iterable);\n }\n\n if (typeof iterable.next === \"function\") {\n return iterable;\n }\n\n if (!isNaN(iterable.length)) {\n var i = -1, next = function next() {\n while (++i < iterable.length) {\n if (hasOwn.call(iterable, i)) {\n next.value = iterable[i];\n next.done = false;\n return next;\n }\n }\n\n next.value = undefined;\n next.done = true;\n\n return next;\n };\n\n return next.next = next;\n }\n }\n\n // Return an iterator with no values.\n return { next: doneResult };\n }\n exports.values = values;\n\n function doneResult() {\n return { value: undefined, done: true };\n }\n\n Context.prototype = {\n constructor: Context,\n\n reset: function(skipTempReset) {\n this.prev = 0;\n this.next = 0;\n // Resetting context._sent for legacy support of Babel's\n // function.sent implementation.\n this.sent = this._sent = undefined;\n this.done = false;\n this.delegate = null;\n\n this.method = \"next\";\n this.arg = undefined;\n\n this.tryEntries.forEach(resetTryEntry);\n\n if (!skipTempReset) {\n for (var name in this) {\n // Not sure about the optimal order of these conditions:\n if (name.charAt(0) === \"t\" &&\n hasOwn.call(this, name) &&\n !isNaN(+name.slice(1))) {\n this[name] = undefined;\n }\n }\n }\n },\n\n stop: function() {\n this.done = true;\n\n var rootEntry = this.tryEntries[0];\n var rootRecord = rootEntry.completion;\n if (rootRecord.type === \"throw\") {\n throw rootRecord.arg;\n }\n\n return this.rval;\n },\n\n dispatchException: function(exception) {\n if (this.done) {\n throw exception;\n }\n\n var context = this;\n function handle(loc, caught) {\n record.type = \"throw\";\n record.arg = exception;\n context.next = loc;\n\n if (caught) {\n // If the dispatched exception was caught by a catch block,\n // then let that catch block handle the exception normally.\n context.method = \"next\";\n context.arg = undefined;\n }\n\n return !! caught;\n }\n\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n var record = entry.completion;\n\n if (entry.tryLoc === \"root\") {\n // Exception thrown outside of any try block that could handle\n // it, so set the completion value of the entire function to\n // throw the exception.\n return handle(\"end\");\n }\n\n if (entry.tryLoc <= this.prev) {\n var hasCatch = hasOwn.call(entry, \"catchLoc\");\n var hasFinally = hasOwn.call(entry, \"finallyLoc\");\n\n if (hasCatch && hasFinally) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n } else if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else if (hasCatch) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n }\n\n } else if (hasFinally) {\n if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else {\n throw new Error(\"try statement without catch or finally\");\n }\n }\n }\n },\n\n abrupt: function(type, arg) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc <= this.prev &&\n hasOwn.call(entry, \"finallyLoc\") &&\n this.prev < entry.finallyLoc) {\n var finallyEntry = entry;\n break;\n }\n }\n\n if (finallyEntry &&\n (type === \"break\" ||\n type === \"continue\") &&\n finallyEntry.tryLoc <= arg &&\n arg <= finallyEntry.finallyLoc) {\n // Ignore the finally entry if control is not jumping to a\n // location outside the try/catch block.\n finallyEntry = null;\n }\n\n var record = finallyEntry ? finallyEntry.completion : {};\n record.type = type;\n record.arg = arg;\n\n if (finallyEntry) {\n this.method = \"next\";\n this.next = finallyEntry.finallyLoc;\n return ContinueSentinel;\n }\n\n return this.complete(record);\n },\n\n complete: function(record, afterLoc) {\n if (record.type === \"throw\") {\n throw record.arg;\n }\n\n if (record.type === \"break\" ||\n record.type === \"continue\") {\n this.next = record.arg;\n } else if (record.type === \"return\") {\n this.rval = this.arg = record.arg;\n this.method = \"return\";\n this.next = \"end\";\n } else if (record.type === \"normal\" && afterLoc) {\n this.next = afterLoc;\n }\n\n return ContinueSentinel;\n },\n\n finish: function(finallyLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.finallyLoc === finallyLoc) {\n this.complete(entry.completion, entry.afterLoc);\n resetTryEntry(entry);\n return ContinueSentinel;\n }\n }\n },\n\n \"catch\": function(tryLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc === tryLoc) {\n var record = entry.completion;\n if (record.type === \"throw\") {\n var thrown = record.arg;\n resetTryEntry(entry);\n }\n return thrown;\n }\n }\n\n // The context.catch method must only be called with a location\n // argument that corresponds to a known catch block.\n throw new Error(\"illegal catch attempt\");\n },\n\n delegateYield: function(iterable, resultName, nextLoc) {\n this.delegate = {\n iterator: values(iterable),\n resultName: resultName,\n nextLoc: nextLoc\n };\n\n if (this.method === \"next\") {\n // Deliberately forget the last sent value so that we don't\n // accidentally pass it on to the delegate.\n this.arg = undefined;\n }\n\n return ContinueSentinel;\n }\n };\n\n // Regardless of whether this script is executing as a CommonJS module\n // or not, return the runtime object so that we can declare the variable\n // regeneratorRuntime in the outer scope, which allows this module to be\n // injected easily by `bin/regenerator --include-runtime script.js`.\n return exports;\n\n}(\n // If this script is executing as a CommonJS module, use module.exports\n // as the regeneratorRuntime namespace. Otherwise create a new empty\n // object. Either way, the resulting object will be used to initialize\n // the regeneratorRuntime variable at the top of this file.\n typeof module === \"object\" ? module.exports : {}\n));\n\ntry {\n regeneratorRuntime = runtime;\n} catch (accidentalStrictMode) {\n // This module should not be running in strict mode, so the above\n // assignment should always work unless something is misconfigured. Just\n // in case runtime.js accidentally runs in strict mode, we can escape\n // strict mode using a global Function call. This could conceivably fail\n // if a Content Security Policy forbids using Function, but in that case\n // the proper solution is to fix the accidental strict mode problem. If\n // you've misconfigured your bundler to force strict mode and applied a\n // CSP to forbid Function, and you're not willing to fix either of those\n // problems, please detail your unique predicament in a GitHub issue.\n Function(\"r\", \"regeneratorRuntime = r\")(runtime);\n}\n","'use strict';\n\nvar utils = require('./utils');\nvar bind = require('./helpers/bind');\nvar Axios = require('./core/Axios');\nvar mergeConfig = require('./core/mergeConfig');\nvar defaults = require('./defaults');\n\n/**\n * Create an instance of Axios\n *\n * @param {Object} defaultConfig The default config for the instance\n * @return {Axios} A new instance of Axios\n */\nfunction createInstance(defaultConfig) {\n var context = new Axios(defaultConfig);\n var instance = bind(Axios.prototype.request, context);\n\n // Copy axios.prototype to instance\n utils.extend(instance, Axios.prototype, context);\n\n // Copy context to instance\n utils.extend(instance, context);\n\n return instance;\n}\n\n// Create the default instance to be exported\nvar axios = createInstance(defaults);\n\n// Expose Axios class to allow class inheritance\naxios.Axios = Axios;\n\n// Factory for creating new instances\naxios.create = function create(instanceConfig) {\n return createInstance(mergeConfig(axios.defaults, instanceConfig));\n};\n\n// Expose Cancel & CancelToken\naxios.Cancel = require('./cancel/Cancel');\naxios.CancelToken = require('./cancel/CancelToken');\naxios.isCancel = require('./cancel/isCancel');\n\n// Expose all/spread\naxios.all = function all(promises) {\n return Promise.all(promises);\n};\naxios.spread = require('./helpers/spread');\n\n// Expose isAxiosError\naxios.isAxiosError = require('./helpers/isAxiosError');\n\nmodule.exports = axios;\n\n// Allow use of default import syntax in TypeScript\nmodule.exports.default = axios;\n","'use strict';\n\nvar utils = require('./../utils');\nvar buildURL = require('../helpers/buildURL');\nvar InterceptorManager = require('./InterceptorManager');\nvar dispatchRequest = require('./dispatchRequest');\nvar mergeConfig = require('./mergeConfig');\nvar validator = require('../helpers/validator');\n\nvar validators = validator.validators;\n/**\n * Create a new instance of Axios\n *\n * @param {Object} instanceConfig The default config for the instance\n */\nfunction Axios(instanceConfig) {\n this.defaults = instanceConfig;\n this.interceptors = {\n request: new InterceptorManager(),\n response: new InterceptorManager()\n };\n}\n\n/**\n * Dispatch a request\n *\n * @param {Object} config The config specific for this request (merged with this.defaults)\n */\nAxios.prototype.request = function request(config) {\n /*eslint no-param-reassign:0*/\n // Allow for axios('example/url'[, config]) a la fetch API\n if (typeof config === 'string') {\n config = arguments[1] || {};\n config.url = arguments[0];\n } else {\n config = config || {};\n }\n\n config = mergeConfig(this.defaults, config);\n\n // Set config.method\n if (config.method) {\n config.method = config.method.toLowerCase();\n } else if (this.defaults.method) {\n config.method = this.defaults.method.toLowerCase();\n } else {\n config.method = 'get';\n }\n\n var transitional = config.transitional;\n\n if (transitional !== undefined) {\n validator.assertOptions(transitional, {\n silentJSONParsing: validators.transitional(validators.boolean, '1.0.0'),\n forcedJSONParsing: validators.transitional(validators.boolean, '1.0.0'),\n clarifyTimeoutError: validators.transitional(validators.boolean, '1.0.0')\n }, false);\n }\n\n // filter out skipped interceptors\n var requestInterceptorChain = [];\n var synchronousRequestInterceptors = true;\n this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {\n if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {\n return;\n }\n\n synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;\n\n requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);\n });\n\n var responseInterceptorChain = [];\n this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {\n responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);\n });\n\n var promise;\n\n if (!synchronousRequestInterceptors) {\n var chain = [dispatchRequest, undefined];\n\n Array.prototype.unshift.apply(chain, requestInterceptorChain);\n chain.concat(responseInterceptorChain);\n\n promise = Promise.resolve(config);\n while (chain.length) {\n promise = promise.then(chain.shift(), chain.shift());\n }\n\n return promise;\n }\n\n\n var newConfig = config;\n while (requestInterceptorChain.length) {\n var onFulfilled = requestInterceptorChain.shift();\n var onRejected = requestInterceptorChain.shift();\n try {\n newConfig = onFulfilled(newConfig);\n } catch (error) {\n onRejected(error);\n break;\n }\n }\n\n try {\n promise = dispatchRequest(newConfig);\n } catch (error) {\n return Promise.reject(error);\n }\n\n while (responseInterceptorChain.length) {\n promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());\n }\n\n return promise;\n};\n\nAxios.prototype.getUri = function getUri(config) {\n config = mergeConfig(this.defaults, config);\n return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\\?/, '');\n};\n\n// Provide aliases for supported request methods\nutils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, config) {\n return this.request(mergeConfig(config || {}, {\n method: method,\n url: url,\n data: (config || {}).data\n }));\n };\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, data, config) {\n return this.request(mergeConfig(config || {}, {\n method: method,\n url: url,\n data: data\n }));\n };\n});\n\nmodule.exports = Axios;\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction InterceptorManager() {\n this.handlers = [];\n}\n\n/**\n * Add a new interceptor to the stack\n *\n * @param {Function} fulfilled The function to handle `then` for a `Promise`\n * @param {Function} rejected The function to handle `reject` for a `Promise`\n *\n * @return {Number} An ID used to remove interceptor later\n */\nInterceptorManager.prototype.use = function use(fulfilled, rejected, options) {\n this.handlers.push({\n fulfilled: fulfilled,\n rejected: rejected,\n synchronous: options ? options.synchronous : false,\n runWhen: options ? options.runWhen : null\n });\n return this.handlers.length - 1;\n};\n\n/**\n * Remove an interceptor from the stack\n *\n * @param {Number} id The ID that was returned by `use`\n */\nInterceptorManager.prototype.eject = function eject(id) {\n if (this.handlers[id]) {\n this.handlers[id] = null;\n }\n};\n\n/**\n * Iterate over all the registered interceptors\n *\n * This method is particularly useful for skipping over any\n * interceptors that may have become `null` calling `eject`.\n *\n * @param {Function} fn The function to call for each interceptor\n */\nInterceptorManager.prototype.forEach = function forEach(fn) {\n utils.forEach(this.handlers, function forEachHandler(h) {\n if (h !== null) {\n fn(h);\n }\n });\n};\n\nmodule.exports = InterceptorManager;\n","'use strict';\n\nvar utils = require('./../utils');\nvar transformData = require('./transformData');\nvar isCancel = require('../cancel/isCancel');\nvar defaults = require('../defaults');\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nfunction throwIfCancellationRequested(config) {\n if (config.cancelToken) {\n config.cancelToken.throwIfRequested();\n }\n}\n\n/**\n * Dispatch a request to the server using the configured adapter.\n *\n * @param {object} config The config that is to be used for the request\n * @returns {Promise} The Promise to be fulfilled\n */\nmodule.exports = function dispatchRequest(config) {\n throwIfCancellationRequested(config);\n\n // Ensure headers exist\n config.headers = config.headers || {};\n\n // Transform request data\n config.data = transformData.call(\n config,\n config.data,\n config.headers,\n config.transformRequest\n );\n\n // Flatten headers\n config.headers = utils.merge(\n config.headers.common || {},\n config.headers[config.method] || {},\n config.headers\n );\n\n utils.forEach(\n ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],\n function cleanHeaderConfig(method) {\n delete config.headers[method];\n }\n );\n\n var adapter = config.adapter || defaults.adapter;\n\n return adapter(config).then(function onAdapterResolution(response) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n response.data = transformData.call(\n config,\n response.data,\n response.headers,\n config.transformResponse\n );\n\n return response;\n }, function onAdapterRejection(reason) {\n if (!isCancel(reason)) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n if (reason && reason.response) {\n reason.response.data = transformData.call(\n config,\n reason.response.data,\n reason.response.headers,\n config.transformResponse\n );\n }\n }\n\n return Promise.reject(reason);\n });\n};\n","'use strict';\n\nvar utils = require('./../utils');\nvar defaults = require('./../defaults');\n\n/**\n * Transform the data for a request or a response\n *\n * @param {Object|String} data The data to be transformed\n * @param {Array} headers The headers for the request or response\n * @param {Array|Function} fns A single function or Array of functions\n * @returns {*} The resulting transformed data\n */\nmodule.exports = function transformData(data, headers, fns) {\n var context = this || defaults;\n /*eslint no-param-reassign:0*/\n utils.forEach(fns, function transform(fn) {\n data = fn.call(context, data, headers);\n });\n\n return data;\n};\n","'use strict';\n\nvar utils = require('../utils');\n\nmodule.exports = function normalizeHeaderName(headers, normalizedName) {\n utils.forEach(headers, function processHeader(value, name) {\n if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {\n headers[normalizedName] = value;\n delete headers[name];\n }\n });\n};\n","'use strict';\n\nvar createError = require('./createError');\n\n/**\n * Resolve or reject a Promise based on response status.\n *\n * @param {Function} resolve A function that resolves the promise.\n * @param {Function} reject A function that rejects the promise.\n * @param {object} response The response.\n */\nmodule.exports = function settle(resolve, reject, response) {\n var validateStatus = response.config.validateStatus;\n if (!response.status || !validateStatus || validateStatus(response.status)) {\n resolve(response);\n } else {\n reject(createError(\n 'Request failed with status code ' + response.status,\n response.config,\n null,\n response.request,\n response\n ));\n }\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs support document.cookie\n (function standardBrowserEnv() {\n return {\n write: function write(name, value, expires, path, domain, secure) {\n var cookie = [];\n cookie.push(name + '=' + encodeURIComponent(value));\n\n if (utils.isNumber(expires)) {\n cookie.push('expires=' + new Date(expires).toGMTString());\n }\n\n if (utils.isString(path)) {\n cookie.push('path=' + path);\n }\n\n if (utils.isString(domain)) {\n cookie.push('domain=' + domain);\n }\n\n if (secure === true) {\n cookie.push('secure');\n }\n\n document.cookie = cookie.join('; ');\n },\n\n read: function read(name) {\n var match = document.cookie.match(new RegExp('(^|;\\\\s*)(' + name + ')=([^;]*)'));\n return (match ? decodeURIComponent(match[3]) : null);\n },\n\n remove: function remove(name) {\n this.write(name, '', Date.now() - 86400000);\n }\n };\n })() :\n\n // Non standard browser env (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return {\n write: function write() {},\n read: function read() { return null; },\n remove: function remove() {}\n };\n })()\n);\n","'use strict';\n\nvar isAbsoluteURL = require('../helpers/isAbsoluteURL');\nvar combineURLs = require('../helpers/combineURLs');\n\n/**\n * Creates a new URL by combining the baseURL with the requestedURL,\n * only when the requestedURL is not already an absolute URL.\n * If the requestURL is absolute, this function returns the requestedURL untouched.\n *\n * @param {string} baseURL The base URL\n * @param {string} requestedURL Absolute or relative URL to combine\n * @returns {string} The combined full path\n */\nmodule.exports = function buildFullPath(baseURL, requestedURL) {\n if (baseURL && !isAbsoluteURL(requestedURL)) {\n return combineURLs(baseURL, requestedURL);\n }\n return requestedURL;\n};\n","'use strict';\n\n/**\n * Determines whether the specified URL is absolute\n *\n * @param {string} url The URL to test\n * @returns {boolean} True if the specified URL is absolute, otherwise false\n */\nmodule.exports = function isAbsoluteURL(url) {\n // A URL is considered absolute if it begins with \"://\" or \"//\" (protocol-relative URL).\n // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed\n // by any combination of letters, digits, plus, period, or hyphen.\n return /^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(url);\n};\n","'use strict';\n\n/**\n * Creates a new URL by combining the specified URLs\n *\n * @param {string} baseURL The base URL\n * @param {string} relativeURL The relative URL\n * @returns {string} The combined URL\n */\nmodule.exports = function combineURLs(baseURL, relativeURL) {\n return relativeURL\n ? baseURL.replace(/\\/+$/, '') + '/' + relativeURL.replace(/^\\/+/, '')\n : baseURL;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\n// Headers whose duplicates are ignored by node\n// c.f. https://nodejs.org/api/http.html#http_message_headers\nvar ignoreDuplicateOf = [\n 'age', 'authorization', 'content-length', 'content-type', 'etag',\n 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',\n 'last-modified', 'location', 'max-forwards', 'proxy-authorization',\n 'referer', 'retry-after', 'user-agent'\n];\n\n/**\n * Parse headers into an object\n *\n * ```\n * Date: Wed, 27 Aug 2014 08:58:49 GMT\n * Content-Type: application/json\n * Connection: keep-alive\n * Transfer-Encoding: chunked\n * ```\n *\n * @param {String} headers Headers needing to be parsed\n * @returns {Object} Headers parsed into an object\n */\nmodule.exports = function parseHeaders(headers) {\n var parsed = {};\n var key;\n var val;\n var i;\n\n if (!headers) { return parsed; }\n\n utils.forEach(headers.split('\\n'), function parser(line) {\n i = line.indexOf(':');\n key = utils.trim(line.substr(0, i)).toLowerCase();\n val = utils.trim(line.substr(i + 1));\n\n if (key) {\n if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {\n return;\n }\n if (key === 'set-cookie') {\n parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);\n } else {\n parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;\n }\n }\n });\n\n return parsed;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs have full support of the APIs needed to test\n // whether the request URL is of the same origin as current location.\n (function standardBrowserEnv() {\n var msie = /(msie|trident)/i.test(navigator.userAgent);\n var urlParsingNode = document.createElement('a');\n var originURL;\n\n /**\n * Parse a URL to discover it's components\n *\n * @param {String} url The URL to be parsed\n * @returns {Object}\n */\n function resolveURL(url) {\n var href = url;\n\n if (msie) {\n // IE needs attribute set twice to normalize properties\n urlParsingNode.setAttribute('href', href);\n href = urlParsingNode.href;\n }\n\n urlParsingNode.setAttribute('href', href);\n\n // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils\n return {\n href: urlParsingNode.href,\n protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',\n host: urlParsingNode.host,\n search: urlParsingNode.search ? urlParsingNode.search.replace(/^\\?/, '') : '',\n hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',\n hostname: urlParsingNode.hostname,\n port: urlParsingNode.port,\n pathname: (urlParsingNode.pathname.charAt(0) === '/') ?\n urlParsingNode.pathname :\n '/' + urlParsingNode.pathname\n };\n }\n\n originURL = resolveURL(window.location.href);\n\n /**\n * Determine if a URL shares the same origin as the current location\n *\n * @param {String} requestURL The URL to test\n * @returns {boolean} True if URL shares the same origin, otherwise false\n */\n return function isURLSameOrigin(requestURL) {\n var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;\n return (parsed.protocol === originURL.protocol &&\n parsed.host === originURL.host);\n };\n })() :\n\n // Non standard browser envs (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return function isURLSameOrigin() {\n return true;\n };\n })()\n);\n","'use strict';\n\nvar pkg = require('./../../package.json');\n\nvar validators = {};\n\n// eslint-disable-next-line func-names\n['object', 'boolean', 'number', 'function', 'string', 'symbol'].forEach(function(type, i) {\n validators[type] = function validator(thing) {\n return typeof thing === type || 'a' + (i < 1 ? 'n ' : ' ') + type;\n };\n});\n\nvar deprecatedWarnings = {};\nvar currentVerArr = pkg.version.split('.');\n\n/**\n * Compare package versions\n * @param {string} version\n * @param {string?} thanVersion\n * @returns {boolean}\n */\nfunction isOlderVersion(version, thanVersion) {\n var pkgVersionArr = thanVersion ? thanVersion.split('.') : currentVerArr;\n var destVer = version.split('.');\n for (var i = 0; i < 3; i++) {\n if (pkgVersionArr[i] > destVer[i]) {\n return true;\n } else if (pkgVersionArr[i] < destVer[i]) {\n return false;\n }\n }\n return false;\n}\n\n/**\n * Transitional option validator\n * @param {function|boolean?} validator\n * @param {string?} version\n * @param {string} message\n * @returns {function}\n */\nvalidators.transitional = function transitional(validator, version, message) {\n var isDeprecated = version && isOlderVersion(version);\n\n function formatMessage(opt, desc) {\n return '[Axios v' + pkg.version + '] Transitional option \\'' + opt + '\\'' + desc + (message ? '. ' + message : '');\n }\n\n // eslint-disable-next-line func-names\n return function(value, opt, opts) {\n if (validator === false) {\n throw new Error(formatMessage(opt, ' has been removed in ' + version));\n }\n\n if (isDeprecated && !deprecatedWarnings[opt]) {\n deprecatedWarnings[opt] = true;\n // eslint-disable-next-line no-console\n console.warn(\n formatMessage(\n opt,\n ' has been deprecated since v' + version + ' and will be removed in the near future'\n )\n );\n }\n\n return validator ? validator(value, opt, opts) : true;\n };\n};\n\n/**\n * Assert object's properties type\n * @param {object} options\n * @param {object} schema\n * @param {boolean?} allowUnknown\n */\n\nfunction assertOptions(options, schema, allowUnknown) {\n if (typeof options !== 'object') {\n throw new TypeError('options must be an object');\n }\n var keys = Object.keys(options);\n var i = keys.length;\n while (i-- > 0) {\n var opt = keys[i];\n var validator = schema[opt];\n if (validator) {\n var value = options[opt];\n var result = value === undefined || validator(value, opt, options);\n if (result !== true) {\n throw new TypeError('option ' + opt + ' must be ' + result);\n }\n continue;\n }\n if (allowUnknown !== true) {\n throw Error('Unknown option ' + opt);\n }\n }\n}\n\nmodule.exports = {\n isOlderVersion: isOlderVersion,\n assertOptions: assertOptions,\n validators: validators\n};\n","'use strict';\n\nvar Cancel = require('./Cancel');\n\n/**\n * A `CancelToken` is an object that can be used to request cancellation of an operation.\n *\n * @class\n * @param {Function} executor The executor function.\n */\nfunction CancelToken(executor) {\n if (typeof executor !== 'function') {\n throw new TypeError('executor must be a function.');\n }\n\n var resolvePromise;\n this.promise = new Promise(function promiseExecutor(resolve) {\n resolvePromise = resolve;\n });\n\n var token = this;\n executor(function cancel(message) {\n if (token.reason) {\n // Cancellation has already been requested\n return;\n }\n\n token.reason = new Cancel(message);\n resolvePromise(token.reason);\n });\n}\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nCancelToken.prototype.throwIfRequested = function throwIfRequested() {\n if (this.reason) {\n throw this.reason;\n }\n};\n\n/**\n * Returns an object that contains a new `CancelToken` and a function that, when called,\n * cancels the `CancelToken`.\n */\nCancelToken.source = function source() {\n var cancel;\n var token = new CancelToken(function executor(c) {\n cancel = c;\n });\n return {\n token: token,\n cancel: cancel\n };\n};\n\nmodule.exports = CancelToken;\n","'use strict';\n\n/**\n * Syntactic sugar for invoking a function and expanding an array for arguments.\n *\n * Common use case would be to use `Function.prototype.apply`.\n *\n * ```js\n * function f(x, y, z) {}\n * var args = [1, 2, 3];\n * f.apply(null, args);\n * ```\n *\n * With `spread` this example can be re-written.\n *\n * ```js\n * spread(function(x, y, z) {})([1, 2, 3]);\n * ```\n *\n * @param {Function} callback\n * @returns {Function}\n */\nmodule.exports = function spread(callback) {\n return function wrap(arr) {\n return callback.apply(null, arr);\n };\n};\n","'use strict';\n\n/**\n * Determines whether the payload is an error thrown by Axios\n *\n * @param {*} payload The value to test\n * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false\n */\nmodule.exports = function isAxiosError(payload) {\n return (typeof payload === 'object') && (payload.isAxiosError === true);\n};\n","\"use strict\";\n\nexports.__esModule = true;\nexports[\"default\"] = void 0;\n\nfunction last() {\n var _ref;\n\n return _ref = arguments.length - 1, _ref < 0 || arguments.length <= _ref ? undefined : arguments[_ref];\n}\n\nfunction negation(a) {\n return -a;\n}\n\nfunction addition(a, b) {\n return a + b;\n}\n\nfunction subtraction(a, b) {\n return a - b;\n}\n\nfunction multiplication(a, b) {\n return a * b;\n}\n\nfunction division(a, b) {\n return a / b;\n}\n\nfunction max() {\n return Math.max.apply(Math, arguments);\n}\n\nfunction min() {\n return Math.min.apply(Math, arguments);\n}\n\nfunction comma() {\n return Array.of.apply(Array, arguments);\n}\n\nvar defaultSymbols = {\n symbols: {\n '*': {\n infix: {\n symbol: '*',\n f: multiplication,\n notation: 'infix',\n precedence: 4,\n rightToLeft: 0,\n argCount: 2\n },\n symbol: '*',\n regSymbol: '\\\\*'\n },\n '/': {\n infix: {\n symbol: '/',\n f: division,\n notation: 'infix',\n precedence: 4,\n rightToLeft: 0,\n argCount: 2\n },\n symbol: '/',\n regSymbol: '/'\n },\n '+': {\n infix: {\n symbol: '+',\n f: addition,\n notation: 'infix',\n precedence: 2,\n rightToLeft: 0,\n argCount: 2\n },\n prefix: {\n symbol: '+',\n f: last,\n notation: 'prefix',\n precedence: 3,\n rightToLeft: 0,\n argCount: 1\n },\n symbol: '+',\n regSymbol: '\\\\+'\n },\n '-': {\n infix: {\n symbol: '-',\n f: subtraction,\n notation: 'infix',\n precedence: 2,\n rightToLeft: 0,\n argCount: 2\n },\n prefix: {\n symbol: '-',\n f: negation,\n notation: 'prefix',\n precedence: 3,\n rightToLeft: 0,\n argCount: 1\n },\n symbol: '-',\n regSymbol: '-'\n },\n ',': {\n infix: {\n symbol: ',',\n f: comma,\n notation: 'infix',\n precedence: 1,\n rightToLeft: 0,\n argCount: 2\n },\n symbol: ',',\n regSymbol: ','\n },\n '(': {\n prefix: {\n symbol: '(',\n f: last,\n notation: 'prefix',\n precedence: 0,\n rightToLeft: 0,\n argCount: 1\n },\n symbol: '(',\n regSymbol: '\\\\('\n },\n ')': {\n postfix: {\n symbol: ')',\n f: undefined,\n notation: 'postfix',\n precedence: 0,\n rightToLeft: 0,\n argCount: 1\n },\n symbol: ')',\n regSymbol: '\\\\)'\n },\n min: {\n func: {\n symbol: 'min',\n f: min,\n notation: 'func',\n precedence: 0,\n rightToLeft: 0,\n argCount: 1\n },\n symbol: 'min',\n regSymbol: 'min\\\\b'\n },\n max: {\n func: {\n symbol: 'max',\n f: max,\n notation: 'func',\n precedence: 0,\n rightToLeft: 0,\n argCount: 1\n },\n symbol: 'max',\n regSymbol: 'max\\\\b'\n }\n }\n};\nvar _default = defaultSymbols;\nexports[\"default\"] = _default;\nmodule.exports = exports.default;","\"use strict\";\n\nexports.__esModule = true;\nexports[\"default\"] = void 0;\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); }\n\nfunction _wrapNativeSuper(Class) { var _cache = typeof Map === \"function\" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== \"function\") { throw new TypeError(\"Super expression must either be null or a function\"); } if (typeof _cache !== \"undefined\") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); }\n\nfunction _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _isNativeFunction(fn) { return Function.toString.call(fn).indexOf(\"[native code]\") !== -1; }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n// based on https://github.com/styled-components/styled-components/blob/fcf6f3804c57a14dd7984dfab7bc06ee2edca044/src/utils/error.js\n\n/**\n * Parse errors.md and turn it into a simple hash of code: message\n * @private\n */\nvar ERRORS = {\n \"1\": \"Passed invalid arguments to hsl, please pass multiple numbers e.g. hsl(360, 0.75, 0.4) or an object e.g. rgb({ hue: 255, saturation: 0.4, lightness: 0.75 }).\\n\\n\",\n \"2\": \"Passed invalid arguments to hsla, please pass multiple numbers e.g. hsla(360, 0.75, 0.4, 0.7) or an object e.g. rgb({ hue: 255, saturation: 0.4, lightness: 0.75, alpha: 0.7 }).\\n\\n\",\n \"3\": \"Passed an incorrect argument to a color function, please pass a string representation of a color.\\n\\n\",\n \"4\": \"Couldn't generate valid rgb string from %s, it returned %s.\\n\\n\",\n \"5\": \"Couldn't parse the color string. Please provide the color as a string in hex, rgb, rgba, hsl or hsla notation.\\n\\n\",\n \"6\": \"Passed invalid arguments to rgb, please pass multiple numbers e.g. rgb(255, 205, 100) or an object e.g. rgb({ red: 255, green: 205, blue: 100 }).\\n\\n\",\n \"7\": \"Passed invalid arguments to rgba, please pass multiple numbers e.g. rgb(255, 205, 100, 0.75) or an object e.g. rgb({ red: 255, green: 205, blue: 100, alpha: 0.75 }).\\n\\n\",\n \"8\": \"Passed invalid argument to toColorString, please pass a RgbColor, RgbaColor, HslColor or HslaColor object.\\n\\n\",\n \"9\": \"Please provide a number of steps to the modularScale helper.\\n\\n\",\n \"10\": \"Please pass a number or one of the predefined scales to the modularScale helper as the ratio.\\n\\n\",\n \"11\": \"Invalid value passed as base to modularScale, expected number or em string but got \\\"%s\\\"\\n\\n\",\n \"12\": \"Expected a string ending in \\\"px\\\" or a number passed as the first argument to %s(), got \\\"%s\\\" instead.\\n\\n\",\n \"13\": \"Expected a string ending in \\\"px\\\" or a number passed as the second argument to %s(), got \\\"%s\\\" instead.\\n\\n\",\n \"14\": \"Passed invalid pixel value (\\\"%s\\\") to %s(), please pass a value like \\\"12px\\\" or 12.\\n\\n\",\n \"15\": \"Passed invalid base value (\\\"%s\\\") to %s(), please pass a value like \\\"12px\\\" or 12.\\n\\n\",\n \"16\": \"You must provide a template to this method.\\n\\n\",\n \"17\": \"You passed an unsupported selector state to this method.\\n\\n\",\n \"18\": \"minScreen and maxScreen must be provided as stringified numbers with the same units.\\n\\n\",\n \"19\": \"fromSize and toSize must be provided as stringified numbers with the same units.\\n\\n\",\n \"20\": \"expects either an array of objects or a single object with the properties prop, fromSize, and toSize.\\n\\n\",\n \"21\": \"expects the objects in the first argument array to have the properties `prop`, `fromSize`, and `toSize`.\\n\\n\",\n \"22\": \"expects the first argument object to have the properties `prop`, `fromSize`, and `toSize`.\\n\\n\",\n \"23\": \"fontFace expects a name of a font-family.\\n\\n\",\n \"24\": \"fontFace expects either the path to the font file(s) or a name of a local copy.\\n\\n\",\n \"25\": \"fontFace expects localFonts to be an array.\\n\\n\",\n \"26\": \"fontFace expects fileFormats to be an array.\\n\\n\",\n \"27\": \"radialGradient requries at least 2 color-stops to properly render.\\n\\n\",\n \"28\": \"Please supply a filename to retinaImage() as the first argument.\\n\\n\",\n \"29\": \"Passed invalid argument to triangle, please pass correct pointingDirection e.g. 'right'.\\n\\n\",\n \"30\": \"Passed an invalid value to `height` or `width`. Please provide a pixel based unit.\\n\\n\",\n \"31\": \"The animation shorthand only takes 8 arguments. See the specification for more information: http://mdn.io/animation\\n\\n\",\n \"32\": \"To pass multiple animations please supply them in arrays, e.g. animation(['rotate', '2s'], ['move', '1s'])\\nTo pass a single animation please supply them in simple values, e.g. animation('rotate', '2s')\\n\\n\",\n \"33\": \"The animation shorthand arrays can only have 8 elements. See the specification for more information: http://mdn.io/animation\\n\\n\",\n \"34\": \"borderRadius expects a radius value as a string or number as the second argument.\\n\\n\",\n \"35\": \"borderRadius expects one of \\\"top\\\", \\\"bottom\\\", \\\"left\\\" or \\\"right\\\" as the first argument.\\n\\n\",\n \"36\": \"Property must be a string value.\\n\\n\",\n \"37\": \"Syntax Error at %s.\\n\\n\",\n \"38\": \"Formula contains a function that needs parentheses at %s.\\n\\n\",\n \"39\": \"Formula is missing closing parenthesis at %s.\\n\\n\",\n \"40\": \"Formula has too many closing parentheses at %s.\\n\\n\",\n \"41\": \"All values in a formula must have the same unit or be unitless.\\n\\n\",\n \"42\": \"Please provide a number of steps to the modularScale helper.\\n\\n\",\n \"43\": \"Please pass a number or one of the predefined scales to the modularScale helper as the ratio.\\n\\n\",\n \"44\": \"Invalid value passed as base to modularScale, expected number or em/rem string but got %s.\\n\\n\",\n \"45\": \"Passed invalid argument to hslToColorString, please pass a HslColor or HslaColor object.\\n\\n\",\n \"46\": \"Passed invalid argument to rgbToColorString, please pass a RgbColor or RgbaColor object.\\n\\n\",\n \"47\": \"minScreen and maxScreen must be provided as stringified numbers with the same units.\\n\\n\",\n \"48\": \"fromSize and toSize must be provided as stringified numbers with the same units.\\n\\n\",\n \"49\": \"Expects either an array of objects or a single object with the properties prop, fromSize, and toSize.\\n\\n\",\n \"50\": \"Expects the objects in the first argument array to have the properties prop, fromSize, and toSize.\\n\\n\",\n \"51\": \"Expects the first argument object to have the properties prop, fromSize, and toSize.\\n\\n\",\n \"52\": \"fontFace expects either the path to the font file(s) or a name of a local copy.\\n\\n\",\n \"53\": \"fontFace expects localFonts to be an array.\\n\\n\",\n \"54\": \"fontFace expects fileFormats to be an array.\\n\\n\",\n \"55\": \"fontFace expects a name of a font-family.\\n\\n\",\n \"56\": \"linearGradient requries at least 2 color-stops to properly render.\\n\\n\",\n \"57\": \"radialGradient requries at least 2 color-stops to properly render.\\n\\n\",\n \"58\": \"Please supply a filename to retinaImage() as the first argument.\\n\\n\",\n \"59\": \"Passed invalid argument to triangle, please pass correct pointingDirection e.g. 'right'.\\n\\n\",\n \"60\": \"Passed an invalid value to `height` or `width`. Please provide a pixel based unit.\\n\\n\",\n \"61\": \"Property must be a string value.\\n\\n\",\n \"62\": \"borderRadius expects a radius value as a string or number as the second argument.\\n\\n\",\n \"63\": \"borderRadius expects one of \\\"top\\\", \\\"bottom\\\", \\\"left\\\" or \\\"right\\\" as the first argument.\\n\\n\",\n \"64\": \"The animation shorthand only takes 8 arguments. See the specification for more information: http://mdn.io/animation.\\n\\n\",\n \"65\": \"To pass multiple animations please supply them in arrays, e.g. animation(['rotate', '2s'], ['move', '1s'])\\\\nTo pass a single animation please supply them in simple values, e.g. animation('rotate', '2s').\\n\\n\",\n \"66\": \"The animation shorthand arrays can only have 8 elements. See the specification for more information: http://mdn.io/animation.\\n\\n\",\n \"67\": \"You must provide a template to this method.\\n\\n\",\n \"68\": \"You passed an unsupported selector state to this method.\\n\\n\",\n \"69\": \"Expected a string ending in \\\"px\\\" or a number passed as the first argument to %s(), got %s instead.\\n\\n\",\n \"70\": \"Expected a string ending in \\\"px\\\" or a number passed as the second argument to %s(), got %s instead.\\n\\n\",\n \"71\": \"Passed invalid pixel value %s to %s(), please pass a value like \\\"12px\\\" or 12.\\n\\n\",\n \"72\": \"Passed invalid base value %s to %s(), please pass a value like \\\"12px\\\" or 12.\\n\\n\",\n \"73\": \"Please provide a valid CSS variable.\\n\\n\",\n \"74\": \"CSS variable not found and no default was provided.\\n\\n\",\n \"75\": \"important requires a valid style object, got a %s instead.\\n\\n\",\n \"76\": \"fromSize and toSize must be provided as stringified numbers with the same units as minScreen and maxScreen.\\n\\n\",\n \"77\": \"remToPx expects a value in \\\"rem\\\" but you provided it in \\\"%s\\\".\\n\\n\",\n \"78\": \"base must be set in \\\"px\\\" or \\\"%\\\" but you set it in \\\"%s\\\".\\n\"\n};\n/**\n * super basic version of sprintf\n * @private\n */\n\nfunction format() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n var a = args[0];\n var b = [];\n var c;\n\n for (c = 1; c < args.length; c += 1) {\n b.push(args[c]);\n }\n\n b.forEach(function (d) {\n a = a.replace(/%[a-z]/, d);\n });\n return a;\n}\n/**\n * Create an error file out of errors.md for development and a simple web link to the full errors\n * in production mode.\n * @private\n */\n\n\nvar PolishedError = /*#__PURE__*/function (_Error) {\n _inheritsLoose(PolishedError, _Error);\n\n function PolishedError(code) {\n var _this;\n\n if (process.env.NODE_ENV === 'production') {\n _this = _Error.call(this, \"An error occurred. See https://github.com/styled-components/polished/blob/main/src/internalHelpers/errors.md#\" + code + \" for more information.\") || this;\n } else {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n\n _this = _Error.call(this, format.apply(void 0, [ERRORS[code]].concat(args))) || this;\n }\n\n return _assertThisInitialized(_this);\n }\n\n return PolishedError;\n}( /*#__PURE__*/_wrapNativeSuper(Error));\n\nexports[\"default\"] = PolishedError;\nmodule.exports = exports.default;","import React from 'react'\n\nlet renderErr = 'Renderer Error ☝️'\n\nexport const actions = {\n init: 'init',\n}\n\nexport const defaultRenderer = ({ value = '' }) => value;\nexport const emptyRenderer = () => <> ;\n\nexport const defaultColumn = {\n Cell: defaultRenderer,\n width: 150,\n minWidth: 0,\n maxWidth: Number.MAX_SAFE_INTEGER,\n}\n\nfunction mergeProps(...propList) {\n return propList.reduce((props, next) => {\n const { style, className, ...rest } = next\n\n props = {\n ...props,\n ...rest,\n }\n\n if (style) {\n props.style = props.style\n ? { ...(props.style || {}), ...(style || {}) }\n : style\n }\n\n if (className) {\n props.className = props.className\n ? props.className + ' ' + className\n : className\n }\n\n if (props.className === '') {\n delete props.className\n }\n\n return props\n }, {})\n}\n\nfunction handlePropGetter(prevProps, userProps, meta) {\n // Handle a lambda, pass it the previous props\n if (typeof userProps === 'function') {\n return handlePropGetter({}, userProps(prevProps, meta))\n }\n\n // Handle an array, merge each item as separate props\n if (Array.isArray(userProps)) {\n return mergeProps(prevProps, ...userProps)\n }\n\n // Handle an object by default, merge the two objects\n return mergeProps(prevProps, userProps)\n}\n\nexport const makePropGetter = (hooks, meta = {}) => {\n return (userProps = {}) =>\n [...hooks, userProps].reduce(\n (prev, next) =>\n handlePropGetter(prev, next, {\n ...meta,\n userProps,\n }),\n {}\n )\n}\n\nexport const reduceHooks = (hooks, initial, meta = {}, allowUndefined) =>\n hooks.reduce((prev, next) => {\n const nextValue = next(prev, meta)\n if (process.env.NODE_ENV !== 'production') {\n if (!allowUndefined && typeof nextValue === 'undefined') {\n console.info(next)\n throw new Error(\n 'React Table: A reducer hook ☝️ just returned undefined! This is not allowed.'\n )\n }\n }\n return nextValue\n }, initial)\n\nexport const loopHooks = (hooks, context, meta = {}) =>\n hooks.forEach(hook => {\n const nextValue = hook(context, meta)\n if (process.env.NODE_ENV !== 'production') {\n if (typeof nextValue !== 'undefined') {\n console.info(hook, nextValue)\n throw new Error(\n 'React Table: A loop-type hook ☝️ just returned a value! This is not allowed.'\n )\n }\n }\n })\n\nexport function ensurePluginOrder(plugins, befores, pluginName, afters) {\n if (process.env.NODE_ENV !== 'production' && afters) {\n throw new Error(\n `Defining plugins in the \"after\" section of ensurePluginOrder is no longer supported (see plugin ${pluginName})`\n )\n }\n const pluginIndex = plugins.findIndex(\n plugin => plugin.pluginName === pluginName\n )\n\n if (pluginIndex === -1) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(`The plugin \"${pluginName}\" was not found in the plugin list!\nThis usually means you need to need to name your plugin hook by setting the 'pluginName' property of the hook function, eg:\n\n ${pluginName}.pluginName = '${pluginName}'\n`)\n }\n }\n\n befores.forEach(before => {\n const beforeIndex = plugins.findIndex(\n plugin => plugin.pluginName === before\n )\n if (beforeIndex > -1 && beforeIndex > pluginIndex) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `React Table: The ${pluginName} plugin hook must be placed after the ${before} plugin hook!`\n )\n }\n }\n })\n}\n\nexport function functionalUpdate(updater, old) {\n return typeof updater === 'function' ? updater(old) : updater\n}\n\nexport function useGetLatest(obj) {\n const ref = React.useRef()\n ref.current = obj\n\n return React.useCallback(() => ref.current, [])\n}\n\n// SSR has issues with useLayoutEffect still, so use useEffect during SSR\nexport const safeUseLayoutEffect =\n typeof document !== 'undefined' ? React.useLayoutEffect : React.useEffect\n\nexport function useMountedLayoutEffect(fn, deps) {\n const mountedRef = React.useRef(false)\n\n safeUseLayoutEffect(() => {\n if (mountedRef.current) {\n fn()\n }\n mountedRef.current = true\n // eslint-disable-next-line\n }, deps)\n}\n\nexport function useAsyncDebounce(defaultFn, defaultWait = 0) {\n const debounceRef = React.useRef({})\n\n const getDefaultFn = useGetLatest(defaultFn)\n const getDefaultWait = useGetLatest(defaultWait)\n\n return React.useCallback(\n async (...args) => {\n if (!debounceRef.current.promise) {\n debounceRef.current.promise = new Promise((resolve, reject) => {\n debounceRef.current.resolve = resolve\n debounceRef.current.reject = reject\n })\n }\n\n if (debounceRef.current.timeout) {\n clearTimeout(debounceRef.current.timeout)\n }\n\n debounceRef.current.timeout = setTimeout(async () => {\n delete debounceRef.current.timeout\n try {\n debounceRef.current.resolve(await getDefaultFn()(...args))\n } catch (err) {\n debounceRef.current.reject(err)\n } finally {\n delete debounceRef.current.promise\n }\n }, getDefaultWait())\n\n return debounceRef.current.promise\n },\n [getDefaultFn, getDefaultWait]\n )\n}\n\nexport function makeRenderer(instance, column, meta = {}) {\n return (type, userProps = {}) => {\n const Comp = typeof type === 'string' ? column[type] : type\n\n if (typeof Comp === 'undefined') {\n console.info(column)\n throw new Error(renderErr)\n }\n\n return flexRender(Comp, { ...instance, column, ...meta, ...userProps })\n }\n}\n\nexport function flexRender(Comp, props) {\n return isReactComponent(Comp) ? : Comp\n}\n\nfunction isReactComponent(component) {\n return (\n isClassComponent(component) ||\n typeof component === 'function' ||\n isExoticComponent(component)\n )\n}\n\nfunction isClassComponent(component) {\n return (\n typeof component === 'function' &&\n (() => {\n const proto = Object.getPrototypeOf(component)\n return proto.prototype && proto.prototype.isReactComponent\n })()\n )\n}\n\nfunction isExoticComponent(component) {\n return (\n typeof component === 'object' &&\n typeof component.$$typeof === 'symbol' &&\n ['react.memo', 'react.forward_ref'].includes(component.$$typeof.description)\n )\n}\n","import { defaultColumn, emptyRenderer } from './publicUtils'\n\n// Find the depth of the columns\nexport function findMaxDepth(columns, depth = 0) {\n return columns.reduce((prev, curr) => {\n if (curr.columns) {\n return Math.max(prev, findMaxDepth(curr.columns, depth + 1))\n }\n return depth\n }, 0)\n}\n\n// Build the visible columns, headers and flat column list\nexport function linkColumnStructure(columns, parent, depth = 0) {\n return columns.map(column => {\n column = {\n ...column,\n parent,\n depth,\n }\n\n assignColumnAccessor(column)\n\n if (column.columns) {\n column.columns = linkColumnStructure(column.columns, column, depth + 1)\n }\n return column\n })\n}\n\nexport function flattenColumns(columns) {\n return flattenBy(columns, 'columns')\n}\n\nexport function assignColumnAccessor(column) {\n // First check for string accessor\n let { id, accessor, Header } = column\n\n if (typeof accessor === 'string') {\n id = id || accessor\n const accessorPath = accessor.split('.')\n accessor = row => getBy(row, accessorPath)\n }\n\n if (!id && typeof Header === 'string' && Header) {\n id = Header\n }\n\n if (!id && column.columns) {\n console.error(column)\n throw new Error('A column ID (or unique \"Header\" value) is required!')\n }\n\n if (!id) {\n console.error(column)\n throw new Error('A column ID (or string accessor) is required!')\n }\n\n Object.assign(column, {\n id,\n accessor,\n })\n\n return column\n}\n\nexport function decorateColumn(column, userDefaultColumn) {\n if (!userDefaultColumn) {\n throw new Error()\n }\n Object.assign(column, {\n // Make sure there is a fallback header, just in case\n Header: emptyRenderer,\n Footer: emptyRenderer,\n ...defaultColumn,\n ...userDefaultColumn,\n ...column,\n })\n\n Object.assign(column, {\n originalWidth: column.width,\n })\n\n return column\n}\n\n// Build the header groups from the bottom up\nexport function makeHeaderGroups(\n allColumns,\n defaultColumn,\n additionalHeaderProperties = () => ({})\n) {\n const headerGroups = []\n\n let scanColumns = allColumns\n\n let uid = 0\n const getUID = () => uid++\n\n while (scanColumns.length) {\n // The header group we are creating\n const headerGroup = {\n headers: [],\n }\n\n // The parent columns we're going to scan next\n const parentColumns = []\n\n const hasParents = scanColumns.some(d => d.parent)\n\n // Scan each column for parents\n scanColumns.forEach(column => {\n // What is the latest (last) parent column?\n let latestParentColumn = [...parentColumns].reverse()[0]\n\n let newParent\n\n if (hasParents) {\n // If the column has a parent, add it if necessary\n if (column.parent) {\n newParent = {\n ...column.parent,\n originalId: column.parent.id,\n id: `${column.parent.id}_${getUID()}`,\n headers: [column],\n ...additionalHeaderProperties(column),\n }\n } else {\n // If other columns have parents, we'll need to add a place holder if necessary\n const originalId = `${column.id}_placeholder`\n newParent = decorateColumn(\n {\n originalId,\n id: `${column.id}_placeholder_${getUID()}`,\n placeholderOf: column,\n headers: [column],\n ...additionalHeaderProperties(column),\n },\n defaultColumn\n )\n }\n\n // If the resulting parent columns are the same, just add\n // the column and increment the header span\n if (\n latestParentColumn &&\n latestParentColumn.originalId === newParent.originalId\n ) {\n latestParentColumn.headers.push(column)\n } else {\n parentColumns.push(newParent)\n }\n }\n\n headerGroup.headers.push(column)\n })\n\n headerGroups.push(headerGroup)\n\n // Start scanning the parent columns\n scanColumns = parentColumns\n }\n\n return headerGroups.reverse()\n}\n\nconst pathObjCache = new Map()\n\nexport function getBy(obj, path, def) {\n if (!path) {\n return obj\n }\n const cacheKey = typeof path === 'function' ? path : JSON.stringify(path)\n\n const pathObj =\n pathObjCache.get(cacheKey) ||\n (() => {\n const pathObj = makePathArray(path)\n pathObjCache.set(cacheKey, pathObj)\n return pathObj\n })()\n\n let val\n\n try {\n val = pathObj.reduce((cursor, pathPart) => cursor[pathPart], obj)\n } catch (e) {\n // continue regardless of error\n }\n return typeof val !== 'undefined' ? val : def\n}\n\nexport function getFirstDefined(...args) {\n for (let i = 0; i < args.length; i += 1) {\n if (typeof args[i] !== 'undefined') {\n return args[i]\n }\n }\n}\n\nexport function getElementDimensions(element) {\n const rect = element.getBoundingClientRect()\n const style = window.getComputedStyle(element)\n const margins = {\n left: parseInt(style.marginLeft),\n right: parseInt(style.marginRight),\n }\n const padding = {\n left: parseInt(style.paddingLeft),\n right: parseInt(style.paddingRight),\n }\n return {\n left: Math.ceil(rect.left),\n width: Math.ceil(rect.width),\n outerWidth: Math.ceil(\n rect.width + margins.left + margins.right + padding.left + padding.right\n ),\n marginLeft: margins.left,\n marginRight: margins.right,\n paddingLeft: padding.left,\n paddingRight: padding.right,\n scrollWidth: element.scrollWidth,\n }\n}\n\nexport function isFunction(a) {\n if (typeof a === 'function') {\n return a\n }\n}\n\nexport function flattenBy(arr, key) {\n const flat = []\n\n const recurse = arr => {\n arr.forEach(d => {\n if (!d[key]) {\n flat.push(d)\n } else {\n recurse(d[key])\n }\n })\n }\n\n recurse(arr)\n\n return flat\n}\n\nexport function expandRows(\n rows,\n { manualExpandedKey, expanded, expandSubRows = true }\n) {\n const expandedRows = []\n\n const handleRow = (row, addToExpandedRows = true) => {\n row.isExpanded =\n (row.original && row.original[manualExpandedKey]) || expanded[row.id]\n\n row.canExpand = row.subRows && !!row.subRows.length\n\n if (addToExpandedRows) {\n expandedRows.push(row)\n }\n\n if (row.subRows && row.subRows.length && row.isExpanded) {\n row.subRows.forEach(row => handleRow(row, expandSubRows))\n }\n }\n\n rows.forEach(row => handleRow(row))\n\n return expandedRows\n}\n\nexport function getFilterMethod(filter, userFilterTypes, filterTypes) {\n return (\n isFunction(filter) ||\n userFilterTypes[filter] ||\n filterTypes[filter] ||\n filterTypes.text\n )\n}\n\nexport function shouldAutoRemoveFilter(autoRemove, value, column) {\n return autoRemove ? autoRemove(value, column) : typeof value === 'undefined'\n}\n\nexport function unpreparedAccessWarning() {\n throw new Error(\n 'React-Table: You have not called prepareRow(row) one or more rows you are attempting to render.'\n )\n}\n\nlet passiveSupported = null\nexport function passiveEventSupported() {\n // memoize support to avoid adding multiple test events\n if (typeof passiveSupported === 'boolean') return passiveSupported\n\n let supported = false\n try {\n const options = {\n get passive() {\n supported = true\n return false\n },\n }\n\n window.addEventListener('test', null, options)\n window.removeEventListener('test', null, options)\n } catch (err) {\n supported = false\n }\n passiveSupported = supported\n return passiveSupported\n}\n\n//\n\nconst reOpenBracket = /\\[/g\nconst reCloseBracket = /\\]/g\n\nfunction makePathArray(obj) {\n return (\n flattenDeep(obj)\n // remove all periods in parts\n .map(d => String(d).replace('.', '_'))\n // join parts using period\n .join('.')\n // replace brackets with periods\n .replace(reOpenBracket, '.')\n .replace(reCloseBracket, '')\n // split it back out on periods\n .split('.')\n )\n}\n\nfunction flattenDeep(arr, newArr = []) {\n if (!Array.isArray(arr)) {\n newArr.push(arr)\n } else {\n for (let i = 0; i < arr.length; i += 1) {\n flattenDeep(arr[i], newArr)\n }\n }\n return newArr\n}\n","const defaultGetTableProps = props => ({\n role: 'table',\n ...props,\n})\n\nconst defaultGetTableBodyProps = props => ({\n role: 'rowgroup',\n ...props,\n})\n\nconst defaultGetHeaderProps = (props, { column }) => ({\n key: `header_${column.id}`,\n colSpan: column.totalVisibleHeaderCount,\n role: 'columnheader',\n ...props,\n})\n\nconst defaultGetFooterProps = (props, { column }) => ({\n key: `footer_${column.id}`,\n colSpan: column.totalVisibleHeaderCount,\n ...props,\n})\n\nconst defaultGetHeaderGroupProps = (props, { index }) => ({\n key: `headerGroup_${index}`,\n role: 'row',\n ...props,\n})\n\nconst defaultGetFooterGroupProps = (props, { index }) => ({\n key: `footerGroup_${index}`,\n ...props,\n})\n\nconst defaultGetRowProps = (props, { row }) => ({\n key: `row_${row.id}`,\n role: 'row',\n ...props,\n})\n\nconst defaultGetCellProps = (props, { cell }) => ({\n key: `cell_${cell.row.id}_${cell.column.id}`,\n role: 'cell',\n ...props,\n})\n\nexport default function makeDefaultPluginHooks() {\n return {\n useOptions: [],\n stateReducers: [],\n useControlledState: [],\n columns: [],\n columnsDeps: [],\n allColumns: [],\n allColumnsDeps: [],\n accessValue: [],\n materializedColumns: [],\n materializedColumnsDeps: [],\n useInstanceAfterData: [],\n visibleColumns: [],\n visibleColumnsDeps: [],\n headerGroups: [],\n headerGroupsDeps: [],\n useInstanceBeforeDimensions: [],\n useInstance: [],\n prepareRow: [],\n getTableProps: [defaultGetTableProps],\n getTableBodyProps: [defaultGetTableBodyProps],\n getHeaderGroupProps: [defaultGetHeaderGroupProps],\n getFooterGroupProps: [defaultGetFooterGroupProps],\n getHeaderProps: [defaultGetHeaderProps],\n getFooterProps: [defaultGetFooterProps],\n getRowProps: [defaultGetRowProps],\n getCellProps: [defaultGetCellProps],\n useFinalInstance: [],\n }\n}\n","import React from 'react'\n\nimport {\n actions,\n functionalUpdate,\n useGetLatest,\n makePropGetter,\n useMountedLayoutEffect,\n} from '../publicUtils'\n\nactions.resetHiddenColumns = 'resetHiddenColumns'\nactions.toggleHideColumn = 'toggleHideColumn'\nactions.setHiddenColumns = 'setHiddenColumns'\nactions.toggleHideAllColumns = 'toggleHideAllColumns'\n\nexport const useColumnVisibility = hooks => {\n hooks.getToggleHiddenProps = [defaultGetToggleHiddenProps]\n hooks.getToggleHideAllColumnsProps = [defaultGetToggleHideAllColumnsProps]\n\n hooks.stateReducers.push(reducer)\n hooks.useInstanceBeforeDimensions.push(useInstanceBeforeDimensions)\n hooks.headerGroupsDeps.push((deps, { instance }) => [\n ...deps,\n instance.state.hiddenColumns,\n ])\n hooks.useInstance.push(useInstance)\n}\n\nuseColumnVisibility.pluginName = 'useColumnVisibility'\n\nconst defaultGetToggleHiddenProps = (props, { column }) => [\n props,\n {\n onChange: e => {\n column.toggleHidden(!e.target.checked)\n },\n style: {\n cursor: 'pointer',\n },\n checked: column.isVisible,\n title: 'Toggle Column Visible',\n },\n]\n\nconst defaultGetToggleHideAllColumnsProps = (props, { instance }) => [\n props,\n {\n onChange: e => {\n instance.toggleHideAllColumns(!e.target.checked)\n },\n style: {\n cursor: 'pointer',\n },\n checked: !instance.allColumnsHidden && !instance.state.hiddenColumns.length,\n title: 'Toggle All Columns Hidden',\n indeterminate:\n !instance.allColumnsHidden && instance.state.hiddenColumns.length,\n },\n]\n\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n hiddenColumns: [],\n ...state,\n }\n }\n\n if (action.type === actions.resetHiddenColumns) {\n return {\n ...state,\n hiddenColumns: instance.initialState.hiddenColumns || [],\n }\n }\n\n if (action.type === actions.toggleHideColumn) {\n const should =\n typeof action.value !== 'undefined'\n ? action.value\n : !state.hiddenColumns.includes(action.columnId)\n\n const hiddenColumns = should\n ? [...state.hiddenColumns, action.columnId]\n : state.hiddenColumns.filter(d => d !== action.columnId)\n\n return {\n ...state,\n hiddenColumns,\n }\n }\n\n if (action.type === actions.setHiddenColumns) {\n return {\n ...state,\n hiddenColumns: functionalUpdate(action.value, state.hiddenColumns),\n }\n }\n\n if (action.type === actions.toggleHideAllColumns) {\n const shouldAll =\n typeof action.value !== 'undefined'\n ? action.value\n : !state.hiddenColumns.length\n\n return {\n ...state,\n hiddenColumns: shouldAll ? instance.allColumns.map(d => d.id) : [],\n }\n }\n}\n\nfunction useInstanceBeforeDimensions(instance) {\n const {\n headers,\n state: { hiddenColumns },\n } = instance\n\n const isMountedRef = React.useRef(false)\n\n if (!isMountedRef.current) {\n }\n\n const handleColumn = (column, parentVisible) => {\n column.isVisible = parentVisible && !hiddenColumns.includes(column.id)\n\n let totalVisibleHeaderCount = 0\n\n if (column.headers && column.headers.length) {\n column.headers.forEach(\n subColumn =>\n (totalVisibleHeaderCount += handleColumn(subColumn, column.isVisible))\n )\n } else {\n totalVisibleHeaderCount = column.isVisible ? 1 : 0\n }\n\n column.totalVisibleHeaderCount = totalVisibleHeaderCount\n\n return totalVisibleHeaderCount\n }\n\n let totalVisibleHeaderCount = 0\n\n headers.forEach(\n subHeader => (totalVisibleHeaderCount += handleColumn(subHeader, true))\n )\n}\n\nfunction useInstance(instance) {\n const {\n columns,\n flatHeaders,\n dispatch,\n allColumns,\n getHooks,\n state: { hiddenColumns },\n autoResetHiddenColumns = true,\n } = instance\n\n const getInstance = useGetLatest(instance)\n\n const allColumnsHidden = allColumns.length === hiddenColumns.length\n\n const toggleHideColumn = React.useCallback(\n (columnId, value) =>\n dispatch({ type: actions.toggleHideColumn, columnId, value }),\n [dispatch]\n )\n\n const setHiddenColumns = React.useCallback(\n value => dispatch({ type: actions.setHiddenColumns, value }),\n [dispatch]\n )\n\n const toggleHideAllColumns = React.useCallback(\n value => dispatch({ type: actions.toggleHideAllColumns, value }),\n [dispatch]\n )\n\n const getToggleHideAllColumnsProps = makePropGetter(\n getHooks().getToggleHideAllColumnsProps,\n { instance: getInstance() }\n )\n\n flatHeaders.forEach(column => {\n column.toggleHidden = value => {\n dispatch({\n type: actions.toggleHideColumn,\n columnId: column.id,\n value,\n })\n }\n\n column.getToggleHiddenProps = makePropGetter(\n getHooks().getToggleHiddenProps,\n {\n instance: getInstance(),\n column,\n }\n )\n })\n\n const getAutoResetHiddenColumns = useGetLatest(autoResetHiddenColumns)\n\n useMountedLayoutEffect(() => {\n if (getAutoResetHiddenColumns()) {\n dispatch({ type: actions.resetHiddenColumns })\n }\n }, [dispatch, columns])\n\n Object.assign(instance, {\n allColumnsHidden,\n toggleHideColumn,\n setHiddenColumns,\n toggleHideAllColumns,\n getToggleHideAllColumnsProps,\n })\n}\n","import React from 'react'\n\n//\n\nimport {\n linkColumnStructure,\n flattenColumns,\n assignColumnAccessor,\n unpreparedAccessWarning,\n makeHeaderGroups,\n decorateColumn,\n} from '../utils'\n\nimport {\n useGetLatest,\n reduceHooks,\n actions,\n loopHooks,\n makePropGetter,\n makeRenderer,\n} from '../publicUtils'\n\nimport makeDefaultPluginHooks from '../makeDefaultPluginHooks'\n\nimport { useColumnVisibility } from './useColumnVisibility'\n\nconst defaultInitialState = {}\nconst defaultColumnInstance = {}\nconst defaultReducer = (state, action, prevState) => state\nconst defaultGetSubRows = (row, index) => row.subRows || []\nconst defaultGetRowId = (row, index, parent) =>\n `${parent ? [parent.id, index].join('.') : index}`\nconst defaultUseControlledState = d => d\n\nfunction applyDefaults(props) {\n const {\n initialState = defaultInitialState,\n defaultColumn = defaultColumnInstance,\n getSubRows = defaultGetSubRows,\n getRowId = defaultGetRowId,\n stateReducer = defaultReducer,\n useControlledState = defaultUseControlledState,\n ...rest\n } = props\n\n return {\n ...rest,\n initialState,\n defaultColumn,\n getSubRows,\n getRowId,\n stateReducer,\n useControlledState,\n }\n}\n\nexport const useTable = (props, ...plugins) => {\n // Apply default props\n props = applyDefaults(props)\n\n // Add core plugins\n plugins = [useColumnVisibility, ...plugins]\n\n // Create the table instance\n let instanceRef = React.useRef({})\n\n // Create a getter for the instance (helps avoid a lot of potential memory leaks)\n const getInstance = useGetLatest(instanceRef.current)\n\n // Assign the props, plugins and hooks to the instance\n Object.assign(getInstance(), {\n ...props,\n plugins,\n hooks: makeDefaultPluginHooks(),\n })\n\n // Allow plugins to register hooks as early as possible\n plugins.filter(Boolean).forEach(plugin => {\n plugin(getInstance().hooks)\n })\n\n // Consume all hooks and make a getter for them\n const getHooks = useGetLatest(getInstance().hooks)\n getInstance().getHooks = getHooks\n delete getInstance().hooks\n\n // Allow useOptions hooks to modify the options coming into the table\n Object.assign(\n getInstance(),\n reduceHooks(getHooks().useOptions, applyDefaults(props))\n )\n\n const {\n data,\n columns: userColumns,\n initialState,\n defaultColumn,\n getSubRows,\n getRowId,\n stateReducer,\n useControlledState,\n } = getInstance()\n\n // Setup user reducer ref\n const getStateReducer = useGetLatest(stateReducer)\n\n // Build the reducer\n const reducer = React.useCallback(\n (state, action) => {\n // Detect invalid actions\n if (!action.type) {\n console.info({ action })\n throw new Error('Unknown Action 👆')\n }\n\n // Reduce the state from all plugin reducers\n return [\n ...getHooks().stateReducers,\n // Allow the user to add their own state reducer(s)\n ...(Array.isArray(getStateReducer())\n ? getStateReducer()\n : [getStateReducer()]),\n ].reduce(\n (s, handler) => handler(s, action, state, getInstance()) || s,\n state\n )\n },\n [getHooks, getStateReducer, getInstance]\n )\n\n // Start the reducer\n const [reducerState, dispatch] = React.useReducer(reducer, undefined, () =>\n reducer(initialState, { type: actions.init })\n )\n\n // Allow the user to control the final state with hooks\n const state = reduceHooks(\n [...getHooks().useControlledState, useControlledState],\n reducerState,\n { instance: getInstance() }\n )\n\n Object.assign(getInstance(), {\n state,\n dispatch,\n })\n\n // Decorate All the columns\n const columns = React.useMemo(\n () =>\n linkColumnStructure(\n reduceHooks(getHooks().columns, userColumns, {\n instance: getInstance(),\n })\n ),\n [\n getHooks,\n getInstance,\n userColumns,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n ...reduceHooks(getHooks().columnsDeps, [], { instance: getInstance() }),\n ]\n )\n getInstance().columns = columns\n\n // Get the flat list of all columns and allow hooks to decorate\n // those columns (and trigger this memoization via deps)\n let allColumns = React.useMemo(\n () =>\n reduceHooks(getHooks().allColumns, flattenColumns(columns), {\n instance: getInstance(),\n }).map(assignColumnAccessor),\n [\n columns,\n getHooks,\n getInstance,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n ...reduceHooks(getHooks().allColumnsDeps, [], {\n instance: getInstance(),\n }),\n ]\n )\n getInstance().allColumns = allColumns\n\n // Access the row model using initial columns\n const [rows, flatRows, rowsById] = React.useMemo(() => {\n let rows = []\n let flatRows = []\n const rowsById = {}\n\n const allColumnsQueue = [...allColumns]\n\n while (allColumnsQueue.length) {\n const column = allColumnsQueue.shift()\n accessRowsForColumn({\n data,\n rows,\n flatRows,\n rowsById,\n column,\n getRowId,\n getSubRows,\n accessValueHooks: getHooks().accessValue,\n getInstance,\n })\n }\n\n return [rows, flatRows, rowsById]\n }, [allColumns, data, getRowId, getSubRows, getHooks, getInstance])\n\n Object.assign(getInstance(), {\n rows,\n initialRows: [...rows],\n flatRows,\n rowsById,\n // materializedColumns,\n })\n\n loopHooks(getHooks().useInstanceAfterData, getInstance())\n\n // Get the flat list of all columns AFTER the rows\n // have been access, and allow hooks to decorate\n // those columns (and trigger this memoization via deps)\n let visibleColumns = React.useMemo(\n () =>\n reduceHooks(getHooks().visibleColumns, allColumns, {\n instance: getInstance(),\n }).map(d => decorateColumn(d, defaultColumn)),\n [\n getHooks,\n allColumns,\n getInstance,\n defaultColumn,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n ...reduceHooks(getHooks().visibleColumnsDeps, [], {\n instance: getInstance(),\n }),\n ]\n )\n\n // Combine new visible columns with all columns\n allColumns = React.useMemo(() => {\n const columns = [...visibleColumns]\n\n allColumns.forEach(column => {\n if (!columns.find(d => d.id === column.id)) {\n columns.push(column)\n }\n })\n\n return columns\n }, [allColumns, visibleColumns])\n getInstance().allColumns = allColumns\n\n if (process.env.NODE_ENV !== 'production') {\n const duplicateColumns = allColumns.filter((column, i) => {\n return allColumns.findIndex(d => d.id === column.id) !== i\n })\n\n if (duplicateColumns.length) {\n console.info(allColumns)\n throw new Error(\n `Duplicate columns were found with ids: \"${duplicateColumns\n .map(d => d.id)\n .join(', ')}\" in the columns array above`\n )\n }\n }\n\n // Make the headerGroups\n const headerGroups = React.useMemo(\n () =>\n reduceHooks(\n getHooks().headerGroups,\n makeHeaderGroups(visibleColumns, defaultColumn),\n getInstance()\n ),\n [\n getHooks,\n visibleColumns,\n defaultColumn,\n getInstance,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n ...reduceHooks(getHooks().headerGroupsDeps, [], {\n instance: getInstance(),\n }),\n ]\n )\n getInstance().headerGroups = headerGroups\n\n // Get the first level of headers\n const headers = React.useMemo(\n () => (headerGroups.length ? headerGroups[0].headers : []),\n [headerGroups]\n )\n getInstance().headers = headers\n\n // Provide a flat header list for utilities\n getInstance().flatHeaders = headerGroups.reduce(\n (all, headerGroup) => [...all, ...headerGroup.headers],\n []\n )\n\n loopHooks(getHooks().useInstanceBeforeDimensions, getInstance())\n\n // Filter columns down to visible ones\n const visibleColumnsDep = visibleColumns\n .filter(d => d.isVisible)\n .map(d => d.id)\n .sort()\n .join('_')\n\n visibleColumns = React.useMemo(\n () => visibleColumns.filter(d => d.isVisible),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [visibleColumns, visibleColumnsDep]\n )\n getInstance().visibleColumns = visibleColumns\n\n // Header Visibility is needed by this point\n const [\n totalColumnsMinWidth,\n totalColumnsWidth,\n totalColumnsMaxWidth,\n ] = calculateHeaderWidths(headers)\n\n getInstance().totalColumnsMinWidth = totalColumnsMinWidth\n getInstance().totalColumnsWidth = totalColumnsWidth\n getInstance().totalColumnsMaxWidth = totalColumnsMaxWidth\n\n loopHooks(getHooks().useInstance, getInstance())\n\n // Each materialized header needs to be assigned a render function and other\n // prop getter properties here.\n ;[...getInstance().flatHeaders, ...getInstance().allColumns].forEach(\n column => {\n // Give columns/headers rendering power\n column.render = makeRenderer(getInstance(), column)\n\n // Give columns/headers a default getHeaderProps\n column.getHeaderProps = makePropGetter(getHooks().getHeaderProps, {\n instance: getInstance(),\n column,\n })\n\n // Give columns/headers a default getFooterProps\n column.getFooterProps = makePropGetter(getHooks().getFooterProps, {\n instance: getInstance(),\n column,\n })\n }\n )\n\n getInstance().headerGroups = React.useMemo(\n () =>\n headerGroups.filter((headerGroup, i) => {\n // Filter out any headers and headerGroups that don't have visible columns\n headerGroup.headers = headerGroup.headers.filter(column => {\n const recurse = headers =>\n headers.filter(column => {\n if (column.headers) {\n return recurse(column.headers)\n }\n return column.isVisible\n }).length\n if (column.headers) {\n return recurse(column.headers)\n }\n return column.isVisible\n })\n\n // Give headerGroups getRowProps\n if (headerGroup.headers.length) {\n headerGroup.getHeaderGroupProps = makePropGetter(\n getHooks().getHeaderGroupProps,\n { instance: getInstance(), headerGroup, index: i }\n )\n\n headerGroup.getFooterGroupProps = makePropGetter(\n getHooks().getFooterGroupProps,\n { instance: getInstance(), headerGroup, index: i }\n )\n\n return true\n }\n\n return false\n }),\n [headerGroups, getInstance, getHooks]\n )\n\n getInstance().footerGroups = [...getInstance().headerGroups].reverse()\n\n // The prepareRow function is absolutely necessary and MUST be called on\n // any rows the user wishes to be displayed.\n\n getInstance().prepareRow = React.useCallback(\n row => {\n row.getRowProps = makePropGetter(getHooks().getRowProps, {\n instance: getInstance(),\n row,\n })\n\n // Build the visible cells for each row\n row.allCells = allColumns.map(column => {\n const value = row.values[column.id]\n\n const cell = {\n column,\n row,\n value,\n }\n\n // Give each cell a getCellProps base\n cell.getCellProps = makePropGetter(getHooks().getCellProps, {\n instance: getInstance(),\n cell,\n })\n\n // Give each cell a renderer function (supports multiple renderers)\n cell.render = makeRenderer(getInstance(), column, {\n row,\n cell,\n value,\n })\n\n return cell\n })\n\n row.cells = visibleColumns.map(column =>\n row.allCells.find(cell => cell.column.id === column.id)\n )\n\n // need to apply any row specific hooks (useExpanded requires this)\n loopHooks(getHooks().prepareRow, row, { instance: getInstance() })\n },\n [getHooks, getInstance, allColumns, visibleColumns]\n )\n\n getInstance().getTableProps = makePropGetter(getHooks().getTableProps, {\n instance: getInstance(),\n })\n\n getInstance().getTableBodyProps = makePropGetter(\n getHooks().getTableBodyProps,\n {\n instance: getInstance(),\n }\n )\n\n loopHooks(getHooks().useFinalInstance, getInstance())\n\n return getInstance()\n}\n\nfunction calculateHeaderWidths(headers, left = 0) {\n let sumTotalMinWidth = 0\n let sumTotalWidth = 0\n let sumTotalMaxWidth = 0\n let sumTotalFlexWidth = 0\n\n headers.forEach(header => {\n let { headers: subHeaders } = header\n\n header.totalLeft = left\n\n if (subHeaders && subHeaders.length) {\n const [\n totalMinWidth,\n totalWidth,\n totalMaxWidth,\n totalFlexWidth,\n ] = calculateHeaderWidths(subHeaders, left)\n header.totalMinWidth = totalMinWidth\n header.totalWidth = totalWidth\n header.totalMaxWidth = totalMaxWidth\n header.totalFlexWidth = totalFlexWidth\n } else {\n header.totalMinWidth = header.minWidth\n header.totalWidth = Math.min(\n Math.max(header.minWidth, header.width),\n header.maxWidth\n )\n header.totalMaxWidth = header.maxWidth\n header.totalFlexWidth = header.canResize ? header.totalWidth : 0\n }\n if (header.isVisible) {\n left += header.totalWidth\n sumTotalMinWidth += header.totalMinWidth\n sumTotalWidth += header.totalWidth\n sumTotalMaxWidth += header.totalMaxWidth\n sumTotalFlexWidth += header.totalFlexWidth\n }\n })\n\n return [sumTotalMinWidth, sumTotalWidth, sumTotalMaxWidth, sumTotalFlexWidth]\n}\n\nfunction accessRowsForColumn({\n data,\n rows,\n flatRows,\n rowsById,\n column,\n getRowId,\n getSubRows,\n accessValueHooks,\n getInstance,\n}) {\n // Access the row's data column-by-column\n // We do it this way so we can incrementally add materialized\n // columns after the first pass and avoid excessive looping\n const accessRow = (originalRow, rowIndex, depth = 0, parent, parentRows) => {\n // Keep the original reference around\n const original = originalRow\n\n const id = getRowId(originalRow, rowIndex, parent)\n\n let row = rowsById[id]\n\n // If the row hasn't been created, let's make it\n if (!row) {\n row = {\n id,\n original,\n index: rowIndex,\n depth,\n cells: [{}], // This is a dummy cell\n }\n\n // Override common array functions (and the dummy cell's getCellProps function)\n // to show an error if it is accessed without calling prepareRow\n row.cells.map = unpreparedAccessWarning\n row.cells.filter = unpreparedAccessWarning\n row.cells.forEach = unpreparedAccessWarning\n row.cells[0].getCellProps = unpreparedAccessWarning\n\n // Create the cells and values\n row.values = {}\n\n // Push this row into the parentRows array\n parentRows.push(row)\n // Keep track of every row in a flat array\n flatRows.push(row)\n // Also keep track of every row by its ID\n rowsById[id] = row\n\n // Get the original subrows\n row.originalSubRows = getSubRows(originalRow, rowIndex)\n\n // Then recursively access them\n if (row.originalSubRows) {\n const subRows = []\n row.originalSubRows.forEach((d, i) =>\n accessRow(d, i, depth + 1, row, subRows)\n )\n // Keep the new subRows array on the row\n row.subRows = subRows\n }\n } else if (row.subRows) {\n // If the row exists, then it's already been accessed\n // Keep recursing, but don't worry about passing the\n // accumlator array (those rows already exist)\n row.originalSubRows.forEach((d, i) => accessRow(d, i, depth + 1, row))\n }\n\n // If the column has an accessor, use it to get a value\n if (column.accessor) {\n row.values[column.id] = column.accessor(\n originalRow,\n rowIndex,\n row,\n parentRows,\n data\n )\n }\n\n // Allow plugins to manipulate the column value\n row.values[column.id] = reduceHooks(\n accessValueHooks,\n row.values[column.id],\n {\n row,\n column,\n instance: getInstance(),\n },\n true\n )\n }\n\n data.forEach((originalRow, rowIndex) =>\n accessRow(originalRow, rowIndex, 0, undefined, rows)\n )\n}\n","import React from 'react'\n\nimport { expandRows } from '../utils'\n\nimport {\n useGetLatest,\n actions,\n useMountedLayoutEffect,\n makePropGetter,\n ensurePluginOrder,\n} from '../publicUtils'\n\n// Actions\nactions.resetExpanded = 'resetExpanded'\nactions.toggleRowExpanded = 'toggleRowExpanded'\nactions.toggleAllRowsExpanded = 'toggleAllRowsExpanded'\n\nexport const useExpanded = hooks => {\n hooks.getToggleAllRowsExpandedProps = [defaultGetToggleAllRowsExpandedProps]\n hooks.getToggleRowExpandedProps = [defaultGetToggleRowExpandedProps]\n hooks.stateReducers.push(reducer)\n hooks.useInstance.push(useInstance)\n hooks.prepareRow.push(prepareRow)\n}\n\nuseExpanded.pluginName = 'useExpanded'\n\nconst defaultGetToggleAllRowsExpandedProps = (props, { instance }) => [\n props,\n {\n onClick: e => {\n instance.toggleAllRowsExpanded()\n },\n style: {\n cursor: 'pointer',\n },\n title: 'Toggle All Rows Expanded',\n },\n]\n\nconst defaultGetToggleRowExpandedProps = (props, { row }) => [\n props,\n {\n onClick: () => {\n row.toggleRowExpanded()\n },\n style: {\n cursor: 'pointer',\n },\n title: 'Toggle Row Expanded',\n },\n]\n\n// Reducer\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n expanded: {},\n ...state,\n }\n }\n\n if (action.type === actions.resetExpanded) {\n return {\n ...state,\n expanded: instance.initialState.expanded || {},\n }\n }\n\n if (action.type === actions.toggleAllRowsExpanded) {\n const { value } = action\n const { rowsById } = instance\n\n const isAllRowsExpanded =\n Object.keys(rowsById).length === Object.keys(state.expanded).length\n\n const expandAll = typeof value !== 'undefined' ? value : !isAllRowsExpanded\n\n if (expandAll) {\n const expanded = {}\n\n Object.keys(rowsById).forEach(rowId => {\n expanded[rowId] = true\n })\n\n return {\n ...state,\n expanded,\n }\n }\n\n return {\n ...state,\n expanded: {},\n }\n }\n\n if (action.type === actions.toggleRowExpanded) {\n const { id, value: setExpanded } = action\n const exists = state.expanded[id]\n\n const shouldExist =\n typeof setExpanded !== 'undefined' ? setExpanded : !exists\n\n if (!exists && shouldExist) {\n return {\n ...state,\n expanded: {\n ...state.expanded,\n [id]: true,\n },\n }\n } else if (exists && !shouldExist) {\n const { [id]: _, ...rest } = state.expanded\n return {\n ...state,\n expanded: rest,\n }\n } else {\n return state\n }\n }\n}\n\nfunction useInstance(instance) {\n const {\n data,\n rows,\n rowsById,\n manualExpandedKey = 'expanded',\n paginateExpandedRows = true,\n expandSubRows = true,\n autoResetExpanded = true,\n getHooks,\n plugins,\n state: { expanded },\n dispatch,\n } = instance\n\n ensurePluginOrder(\n plugins,\n ['useSortBy', 'useGroupBy', 'usePivotColumns', 'useGlobalFilter'],\n 'useExpanded'\n )\n\n const getAutoResetExpanded = useGetLatest(autoResetExpanded)\n\n let isAllRowsExpanded = Boolean(\n Object.keys(rowsById).length && Object.keys(expanded).length\n )\n\n if (isAllRowsExpanded) {\n if (Object.keys(rowsById).some(id => !expanded[id])) {\n isAllRowsExpanded = false\n }\n }\n\n // Bypass any effects from firing when this changes\n useMountedLayoutEffect(() => {\n if (getAutoResetExpanded()) {\n dispatch({ type: actions.resetExpanded })\n }\n }, [dispatch, data])\n\n const toggleRowExpanded = React.useCallback(\n (id, value) => {\n dispatch({ type: actions.toggleRowExpanded, id, value })\n },\n [dispatch]\n )\n\n const toggleAllRowsExpanded = React.useCallback(\n value => dispatch({ type: actions.toggleAllRowsExpanded, value }),\n [dispatch]\n )\n\n const expandedRows = React.useMemo(() => {\n if (paginateExpandedRows) {\n return expandRows(rows, { manualExpandedKey, expanded, expandSubRows })\n }\n\n return rows\n }, [paginateExpandedRows, rows, manualExpandedKey, expanded, expandSubRows])\n\n const expandedDepth = React.useMemo(() => findExpandedDepth(expanded), [\n expanded,\n ])\n\n const getInstance = useGetLatest(instance)\n\n const getToggleAllRowsExpandedProps = makePropGetter(\n getHooks().getToggleAllRowsExpandedProps,\n { instance: getInstance() }\n )\n\n Object.assign(instance, {\n preExpandedRows: rows,\n expandedRows,\n rows: expandedRows,\n expandedDepth,\n isAllRowsExpanded,\n toggleRowExpanded,\n toggleAllRowsExpanded,\n getToggleAllRowsExpandedProps,\n })\n}\n\nfunction prepareRow(row, { instance: { getHooks }, instance }) {\n row.toggleRowExpanded = set => instance.toggleRowExpanded(row.id, set)\n\n row.getToggleRowExpandedProps = makePropGetter(\n getHooks().getToggleRowExpandedProps,\n {\n instance,\n row,\n }\n )\n}\n\nfunction findExpandedDepth(expanded) {\n let maxDepth = 0\n\n Object.keys(expanded).forEach(id => {\n const splitId = id.split('.')\n maxDepth = Math.max(maxDepth, splitId.length)\n })\n\n return maxDepth\n}\n","export const text = (rows, ids, filterValue) => {\n rows = rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n return String(rowValue)\n .toLowerCase()\n .includes(String(filterValue).toLowerCase())\n })\n })\n return rows\n}\n\ntext.autoRemove = val => !val\n\nexport const exactText = (rows, ids, filterValue) => {\n return rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n return rowValue !== undefined\n ? String(rowValue).toLowerCase() === String(filterValue).toLowerCase()\n : true\n })\n })\n}\n\nexactText.autoRemove = val => !val\n\nexport const exactTextCase = (rows, ids, filterValue) => {\n return rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n return rowValue !== undefined\n ? String(rowValue) === String(filterValue)\n : true\n })\n })\n}\n\nexactTextCase.autoRemove = val => !val\n\nexport const includes = (rows, ids, filterValue) => {\n return rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n return rowValue.includes(filterValue)\n })\n })\n}\n\nincludes.autoRemove = val => !val || !val.length\n\nexport const includesAll = (rows, ids, filterValue) => {\n return rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n return (\n rowValue &&\n rowValue.length &&\n filterValue.every(val => rowValue.includes(val))\n )\n })\n })\n}\n\nincludesAll.autoRemove = val => !val || !val.length\n\nexport const includesSome = (rows, ids, filterValue) => {\n return rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n return (\n rowValue &&\n rowValue.length &&\n filterValue.some(val => rowValue.includes(val))\n )\n })\n })\n}\n\nincludesSome.autoRemove = val => !val || !val.length\n\nexport const includesValue = (rows, ids, filterValue) => {\n return rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n return filterValue.includes(rowValue)\n })\n })\n}\n\nincludesValue.autoRemove = val => !val || !val.length\n\nexport const exact = (rows, ids, filterValue) => {\n return rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n return rowValue === filterValue\n })\n })\n}\n\nexact.autoRemove = val => typeof val === 'undefined'\n\nexport const equals = (rows, ids, filterValue) => {\n return rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n // eslint-disable-next-line eqeqeq\n return rowValue == filterValue\n })\n })\n}\n\nequals.autoRemove = val => val == null\n\nexport const between = (rows, ids, filterValue) => {\n let [min, max] = filterValue || []\n\n min = typeof min === 'number' ? min : -Infinity\n max = typeof max === 'number' ? max : Infinity\n\n if (min > max) {\n const temp = min\n min = max\n max = temp\n }\n\n return rows.filter(row => {\n return ids.some(id => {\n const rowValue = row.values[id]\n return rowValue >= min && rowValue <= max\n })\n })\n}\n\nbetween.autoRemove = val =>\n !val || (typeof val[0] !== 'number' && typeof val[1] !== 'number')\n","import React from 'react'\n\nimport {\n getFirstDefined,\n getFilterMethod,\n shouldAutoRemoveFilter,\n} from '../utils'\n\nimport {\n actions,\n useGetLatest,\n functionalUpdate,\n useMountedLayoutEffect,\n} from '../publicUtils'\n\nimport * as filterTypes from '../filterTypes'\n\n// Actions\nactions.resetFilters = 'resetFilters'\nactions.setFilter = 'setFilter'\nactions.setAllFilters = 'setAllFilters'\n\nexport const useFilters = hooks => {\n hooks.stateReducers.push(reducer)\n hooks.useInstance.push(useInstance)\n}\n\nuseFilters.pluginName = 'useFilters'\n\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n filters: [],\n ...state,\n }\n }\n\n if (action.type === actions.resetFilters) {\n return {\n ...state,\n filters: instance.initialState.filters || [],\n }\n }\n\n if (action.type === actions.setFilter) {\n const { columnId, filterValue } = action\n const { allColumns, filterTypes: userFilterTypes } = instance\n\n const column = allColumns.find(d => d.id === columnId)\n\n if (!column) {\n throw new Error(\n `React-Table: Could not find a column with id: ${columnId}`\n )\n }\n\n const filterMethod = getFilterMethod(\n column.filter,\n userFilterTypes || {},\n filterTypes\n )\n\n const previousfilter = state.filters.find(d => d.id === columnId)\n\n const newFilter = functionalUpdate(\n filterValue,\n previousfilter && previousfilter.value\n )\n\n //\n if (shouldAutoRemoveFilter(filterMethod.autoRemove, newFilter, column)) {\n return {\n ...state,\n filters: state.filters.filter(d => d.id !== columnId),\n }\n }\n\n if (previousfilter) {\n return {\n ...state,\n filters: state.filters.map(d => {\n if (d.id === columnId) {\n return { id: columnId, value: newFilter }\n }\n return d\n }),\n }\n }\n\n return {\n ...state,\n filters: [...state.filters, { id: columnId, value: newFilter }],\n }\n }\n\n if (action.type === actions.setAllFilters) {\n const { filters } = action\n const { allColumns, filterTypes: userFilterTypes } = instance\n\n return {\n ...state,\n // Filter out undefined values\n filters: functionalUpdate(filters, state.filters).filter(filter => {\n const column = allColumns.find(d => d.id === filter.id)\n const filterMethod = getFilterMethod(\n column.filter,\n userFilterTypes || {},\n filterTypes\n )\n\n if (\n shouldAutoRemoveFilter(filterMethod.autoRemove, filter.value, column)\n ) {\n return false\n }\n return true\n }),\n }\n }\n}\n\nfunction useInstance(instance) {\n const {\n data,\n rows,\n flatRows,\n rowsById,\n allColumns,\n filterTypes: userFilterTypes,\n manualFilters,\n defaultCanFilter = false,\n disableFilters,\n state: { filters },\n dispatch,\n autoResetFilters = true,\n } = instance\n\n const setFilter = React.useCallback(\n (columnId, filterValue) => {\n dispatch({ type: actions.setFilter, columnId, filterValue })\n },\n [dispatch]\n )\n\n const setAllFilters = React.useCallback(\n filters => {\n dispatch({\n type: actions.setAllFilters,\n filters,\n })\n },\n [dispatch]\n )\n\n allColumns.forEach(column => {\n const {\n id,\n accessor,\n defaultCanFilter: columnDefaultCanFilter,\n disableFilters: columnDisableFilters,\n } = column\n\n // Determine if a column is filterable\n column.canFilter = accessor\n ? getFirstDefined(\n columnDisableFilters === true ? false : undefined,\n disableFilters === true ? false : undefined,\n true\n )\n : getFirstDefined(columnDefaultCanFilter, defaultCanFilter, false)\n\n // Provide the column a way of updating the filter value\n column.setFilter = val => setFilter(column.id, val)\n\n // Provide the current filter value to the column for\n // convenience\n const found = filters.find(d => d.id === id)\n column.filterValue = found && found.value\n })\n\n const [\n filteredRows,\n filteredFlatRows,\n filteredRowsById,\n ] = React.useMemo(() => {\n if (manualFilters || !filters.length) {\n return [rows, flatRows, rowsById]\n }\n\n const filteredFlatRows = []\n const filteredRowsById = {}\n\n // Filters top level and nested rows\n const filterRows = (rows, depth = 0) => {\n let filteredRows = rows\n\n filteredRows = filters.reduce(\n (filteredSoFar, { id: columnId, value: filterValue }) => {\n // Find the filters column\n const column = allColumns.find(d => d.id === columnId)\n\n if (!column) {\n return filteredSoFar\n }\n\n if (depth === 0) {\n column.preFilteredRows = filteredSoFar\n }\n\n const filterMethod = getFilterMethod(\n column.filter,\n userFilterTypes || {},\n filterTypes\n )\n\n if (!filterMethod) {\n console.warn(\n `Could not find a valid 'column.filter' for column with the ID: ${column.id}.`\n )\n return filteredSoFar\n }\n\n // Pass the rows, id, filterValue and column to the filterMethod\n // to get the filtered rows back\n column.filteredRows = filterMethod(\n filteredSoFar,\n [columnId],\n filterValue\n )\n\n return column.filteredRows\n },\n rows\n )\n\n // Apply the filter to any subRows\n // We technically could do this recursively in the above loop,\n // but that would severely hinder the API for the user, since they\n // would be required to do that recursion in some scenarios\n filteredRows.forEach(row => {\n filteredFlatRows.push(row)\n filteredRowsById[row.id] = row\n if (!row.subRows) {\n return\n }\n\n row.subRows =\n row.subRows && row.subRows.length > 0\n ? filterRows(row.subRows, depth + 1)\n : row.subRows\n })\n\n return filteredRows\n }\n\n return [filterRows(rows), filteredFlatRows, filteredRowsById]\n }, [\n manualFilters,\n filters,\n rows,\n flatRows,\n rowsById,\n allColumns,\n userFilterTypes,\n ])\n\n React.useMemo(() => {\n // Now that each filtered column has it's partially filtered rows,\n // lets assign the final filtered rows to all of the other columns\n const nonFilteredColumns = allColumns.filter(\n column => !filters.find(d => d.id === column.id)\n )\n\n // This essentially enables faceted filter options to be built easily\n // using every column's preFilteredRows value\n nonFilteredColumns.forEach(column => {\n column.preFilteredRows = filteredRows\n column.filteredRows = filteredRows\n })\n }, [filteredRows, filters, allColumns])\n\n const getAutoResetFilters = useGetLatest(autoResetFilters)\n\n useMountedLayoutEffect(() => {\n if (getAutoResetFilters()) {\n dispatch({ type: actions.resetFilters })\n }\n }, [dispatch, manualFilters ? null : data])\n\n Object.assign(instance, {\n preFilteredRows: rows,\n preFilteredFlatRows: flatRows,\n preFilteredRowsById: rowsById,\n filteredRows,\n filteredFlatRows,\n filteredRowsById,\n rows: filteredRows,\n flatRows: filteredFlatRows,\n rowsById: filteredRowsById,\n setFilter,\n setAllFilters,\n })\n}\n","import React from 'react'\n\nimport {\n getFilterMethod,\n shouldAutoRemoveFilter,\n getFirstDefined,\n} from '../utils'\n\nimport {\n actions,\n useMountedLayoutEffect,\n functionalUpdate,\n useGetLatest,\n} from '../publicUtils'\n\nimport * as filterTypes from '../filterTypes'\n\n// Actions\nactions.resetGlobalFilter = 'resetGlobalFilter'\nactions.setGlobalFilter = 'setGlobalFilter'\n\nexport const useGlobalFilter = hooks => {\n hooks.stateReducers.push(reducer)\n hooks.useInstance.push(useInstance)\n}\n\nuseGlobalFilter.pluginName = 'useGlobalFilter'\n\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.resetGlobalFilter) {\n return {\n ...state,\n globalFilter: instance.initialState.globalFilter || undefined,\n }\n }\n\n if (action.type === actions.setGlobalFilter) {\n const { filterValue } = action\n const { userFilterTypes } = instance\n\n const filterMethod = getFilterMethod(\n instance.globalFilter,\n userFilterTypes || {},\n filterTypes\n )\n\n const newFilter = functionalUpdate(filterValue, state.globalFilter)\n\n //\n if (shouldAutoRemoveFilter(filterMethod.autoRemove, newFilter)) {\n const { globalFilter, ...stateWithoutGlobalFilter } = state\n return stateWithoutGlobalFilter\n }\n\n return {\n ...state,\n globalFilter: newFilter,\n }\n }\n}\n\nfunction useInstance(instance) {\n const {\n data,\n rows,\n flatRows,\n rowsById,\n allColumns,\n filterTypes: userFilterTypes,\n globalFilter,\n manualGlobalFilter,\n state: { globalFilter: globalFilterValue },\n dispatch,\n autoResetGlobalFilter = true,\n disableGlobalFilter,\n } = instance\n\n const setGlobalFilter = React.useCallback(\n filterValue => {\n dispatch({ type: actions.setGlobalFilter, filterValue })\n },\n [dispatch]\n )\n\n // TODO: Create a filter cache for incremental high speed multi-filtering\n // This gets pretty complicated pretty fast, since you have to maintain a\n // cache for each row group (top-level rows, and each row's recursive subrows)\n // This would make multi-filtering a lot faster though. Too far?\n\n const [\n globalFilteredRows,\n globalFilteredFlatRows,\n globalFilteredRowsById,\n ] = React.useMemo(() => {\n if (manualGlobalFilter || typeof globalFilterValue === 'undefined') {\n return [rows, flatRows, rowsById]\n }\n\n const filteredFlatRows = []\n const filteredRowsById = {}\n\n const filterMethod = getFilterMethod(\n globalFilter,\n userFilterTypes || {},\n filterTypes\n )\n\n if (!filterMethod) {\n console.warn(`Could not find a valid 'globalFilter' option.`)\n return rows\n }\n\n allColumns.forEach(column => {\n const { disableGlobalFilter: columnDisableGlobalFilter } = column\n\n column.canFilter = getFirstDefined(\n columnDisableGlobalFilter === true ? false : undefined,\n disableGlobalFilter === true ? false : undefined,\n true\n )\n })\n\n const filterableColumns = allColumns.filter(c => c.canFilter === true)\n\n // Filters top level and nested rows\n const filterRows = filteredRows => {\n filteredRows = filterMethod(\n filteredRows,\n filterableColumns.map(d => d.id),\n globalFilterValue\n )\n\n filteredRows.forEach(row => {\n filteredFlatRows.push(row)\n filteredRowsById[row.id] = row\n\n row.subRows =\n row.subRows && row.subRows.length\n ? filterRows(row.subRows)\n : row.subRows\n })\n\n return filteredRows\n }\n\n return [filterRows(rows), filteredFlatRows, filteredRowsById]\n }, [\n manualGlobalFilter,\n globalFilterValue,\n globalFilter,\n userFilterTypes,\n allColumns,\n rows,\n flatRows,\n rowsById,\n disableGlobalFilter,\n ])\n\n const getAutoResetGlobalFilter = useGetLatest(autoResetGlobalFilter)\n\n useMountedLayoutEffect(() => {\n if (getAutoResetGlobalFilter()) {\n dispatch({ type: actions.resetGlobalFilter })\n }\n }, [dispatch, manualGlobalFilter ? null : data])\n\n Object.assign(instance, {\n preGlobalFilteredRows: rows,\n preGlobalFilteredFlatRows: flatRows,\n preGlobalFilteredRowsById: rowsById,\n globalFilteredRows,\n globalFilteredFlatRows,\n globalFilteredRowsById,\n rows: globalFilteredRows,\n flatRows: globalFilteredFlatRows,\n rowsById: globalFilteredRowsById,\n setGlobalFilter,\n disableGlobalFilter,\n })\n}\n","export function sum(values, aggregatedValues) {\n // It's faster to just add the aggregations together instead of\n // process leaf nodes individually\n return aggregatedValues.reduce(\n (sum, next) => sum + (typeof next === 'number' ? next : 0),\n 0\n )\n}\n\nexport function min(values) {\n let min = values[0] || 0\n\n values.forEach(value => {\n if (typeof value === 'number') {\n min = Math.min(min, value)\n }\n })\n\n return min\n}\n\nexport function max(values) {\n let max = values[0] || 0\n\n values.forEach(value => {\n if (typeof value === 'number') {\n max = Math.max(max, value)\n }\n })\n\n return max\n}\n\nexport function minMax(values) {\n let min = values[0] || 0\n let max = values[0] || 0\n\n values.forEach(value => {\n if (typeof value === 'number') {\n min = Math.min(min, value)\n max = Math.max(max, value)\n }\n })\n\n return `${min}..${max}`\n}\n\nexport function average(values) {\n return sum(null, values) / values.length\n}\n\nexport function median(values) {\n if (!values.length) {\n return null\n }\n\n const mid = Math.floor(values.length / 2)\n const nums = [...values].sort((a, b) => a - b)\n return values.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2\n}\n\nexport function unique(values) {\n return Array.from(new Set(values).values())\n}\n\nexport function uniqueCount(values) {\n return new Set(values).size\n}\n\nexport function count(values) {\n return values.length\n}\n","import React from 'react'\n\nimport * as aggregations from '../aggregations'\n\nimport { getFirstDefined, flattenBy } from '../utils'\n\nimport {\n actions,\n makePropGetter,\n ensurePluginOrder,\n useMountedLayoutEffect,\n useGetLatest,\n} from '../publicUtils'\n\nconst emptyArray = []\nconst emptyObject = {}\n\n// Actions\nactions.resetGroupBy = 'resetGroupBy'\nactions.setGroupBy = 'setGroupBy'\nactions.toggleGroupBy = 'toggleGroupBy'\n\nexport const useGroupBy = hooks => {\n hooks.getGroupByToggleProps = [defaultGetGroupByToggleProps]\n hooks.stateReducers.push(reducer)\n hooks.visibleColumnsDeps.push((deps, { instance }) => [\n ...deps,\n instance.state.groupBy,\n ])\n hooks.visibleColumns.push(visibleColumns)\n hooks.useInstance.push(useInstance)\n hooks.prepareRow.push(prepareRow)\n}\n\nuseGroupBy.pluginName = 'useGroupBy'\n\nconst defaultGetGroupByToggleProps = (props, { header }) => [\n props,\n {\n onClick: header.canGroupBy\n ? e => {\n e.persist()\n header.toggleGroupBy()\n }\n : undefined,\n style: {\n cursor: header.canGroupBy ? 'pointer' : undefined,\n },\n title: 'Toggle GroupBy',\n },\n]\n\n// Reducer\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n groupBy: [],\n ...state,\n }\n }\n\n if (action.type === actions.resetGroupBy) {\n return {\n ...state,\n groupBy: instance.initialState.groupBy || [],\n }\n }\n\n if (action.type === actions.setGroupBy) {\n const { value } = action\n return {\n ...state,\n groupBy: value,\n }\n }\n\n if (action.type === actions.toggleGroupBy) {\n const { columnId, value: setGroupBy } = action\n\n const resolvedGroupBy =\n typeof setGroupBy !== 'undefined'\n ? setGroupBy\n : !state.groupBy.includes(columnId)\n\n if (resolvedGroupBy) {\n return {\n ...state,\n groupBy: [...state.groupBy, columnId],\n }\n }\n\n return {\n ...state,\n groupBy: state.groupBy.filter(d => d !== columnId),\n }\n }\n}\n\nfunction visibleColumns(\n columns,\n {\n instance: {\n state: { groupBy },\n },\n }\n) {\n // Sort grouped columns to the start of the column list\n // before the headers are built\n\n const groupByColumns = groupBy\n .map(g => columns.find(col => col.id === g))\n .filter(Boolean)\n\n const nonGroupByColumns = columns.filter(col => !groupBy.includes(col.id))\n\n columns = [...groupByColumns, ...nonGroupByColumns]\n\n columns.forEach(column => {\n column.isGrouped = groupBy.includes(column.id)\n column.groupedIndex = groupBy.indexOf(column.id)\n })\n\n return columns\n}\n\nconst defaultUserAggregations = {}\n\nfunction useInstance(instance) {\n const {\n data,\n rows,\n flatRows,\n rowsById,\n allColumns,\n flatHeaders,\n groupByFn = defaultGroupByFn,\n manualGroupBy,\n aggregations: userAggregations = defaultUserAggregations,\n plugins,\n state: { groupBy },\n dispatch,\n autoResetGroupBy = true,\n disableGroupBy,\n defaultCanGroupBy,\n getHooks,\n } = instance\n\n ensurePluginOrder(plugins, ['useColumnOrder', 'useFilters'], 'useGroupBy')\n\n const getInstance = useGetLatest(instance)\n\n allColumns.forEach(column => {\n const {\n accessor,\n defaultGroupBy: defaultColumnGroupBy,\n disableGroupBy: columnDisableGroupBy,\n } = column\n\n column.canGroupBy = accessor\n ? getFirstDefined(\n column.canGroupBy,\n columnDisableGroupBy === true ? false : undefined,\n disableGroupBy === true ? false : undefined,\n true\n )\n : getFirstDefined(\n column.canGroupBy,\n defaultColumnGroupBy,\n defaultCanGroupBy,\n false\n )\n\n if (column.canGroupBy) {\n column.toggleGroupBy = () => instance.toggleGroupBy(column.id)\n }\n\n column.Aggregated = column.Aggregated || column.Cell\n })\n\n const toggleGroupBy = React.useCallback(\n (columnId, value) => {\n dispatch({ type: actions.toggleGroupBy, columnId, value })\n },\n [dispatch]\n )\n\n const setGroupBy = React.useCallback(\n value => {\n dispatch({ type: actions.setGroupBy, value })\n },\n [dispatch]\n )\n\n flatHeaders.forEach(header => {\n header.getGroupByToggleProps = makePropGetter(\n getHooks().getGroupByToggleProps,\n { instance: getInstance(), header }\n )\n })\n\n const [\n groupedRows,\n groupedFlatRows,\n groupedRowsById,\n onlyGroupedFlatRows,\n onlyGroupedRowsById,\n nonGroupedFlatRows,\n nonGroupedRowsById,\n ] = React.useMemo(() => {\n if (manualGroupBy || !groupBy.length) {\n return [\n rows,\n flatRows,\n rowsById,\n emptyArray,\n emptyObject,\n flatRows,\n rowsById,\n ]\n }\n\n // Ensure that the list of filtered columns exist\n const existingGroupBy = groupBy.filter(g =>\n allColumns.find(col => col.id === g)\n )\n\n // Find the columns that can or are aggregating\n // Uses each column to aggregate rows into a single value\n const aggregateRowsToValues = (leafRows, groupedRows, depth) => {\n const values = {}\n\n allColumns.forEach(column => {\n // Don't aggregate columns that are in the groupBy\n if (existingGroupBy.includes(column.id)) {\n values[column.id] = groupedRows[0]\n ? groupedRows[0].values[column.id]\n : null\n return\n }\n\n // Aggregate the values\n let aggregateFn =\n typeof column.aggregate === 'function'\n ? column.aggregate\n : userAggregations[column.aggregate] ||\n aggregations[column.aggregate]\n\n if (aggregateFn) {\n // Get the columnValues to aggregate\n const groupedValues = groupedRows.map(row => row.values[column.id])\n\n // Get the columnValues to aggregate\n const leafValues = leafRows.map(row => {\n let columnValue = row.values[column.id]\n\n if (!depth && column.aggregateValue) {\n const aggregateValueFn =\n typeof column.aggregateValue === 'function'\n ? column.aggregateValue\n : userAggregations[column.aggregateValue] ||\n aggregations[column.aggregateValue]\n\n if (!aggregateValueFn) {\n console.info({ column })\n throw new Error(\n `React Table: Invalid column.aggregateValue option for column listed above`\n )\n }\n\n columnValue = aggregateValueFn(columnValue, row, column)\n }\n return columnValue\n })\n\n values[column.id] = aggregateFn(leafValues, groupedValues)\n } else if (column.aggregate) {\n console.info({ column })\n throw new Error(\n `React Table: Invalid column.aggregate option for column listed above`\n )\n } else {\n values[column.id] = null\n }\n })\n\n return values\n }\n\n let groupedFlatRows = []\n const groupedRowsById = {}\n const onlyGroupedFlatRows = []\n const onlyGroupedRowsById = {}\n const nonGroupedFlatRows = []\n const nonGroupedRowsById = {}\n\n // Recursively group the data\n const groupUpRecursively = (rows, depth = 0, parentId) => {\n // This is the last level, just return the rows\n if (depth === existingGroupBy.length) {\n return rows.map((row) => ({ ...row, depth }))\n }\n\n const columnId = existingGroupBy[depth]\n\n // Group the rows together for this level\n let rowGroupsMap = groupByFn(rows, columnId)\n\n // Peform aggregations for each group\n const aggregatedGroupedRows = Object.entries(rowGroupsMap).map(\n ([groupByVal, groupedRows], index) => {\n let id = `${columnId}:${groupByVal}`\n id = parentId ? `${parentId}>${id}` : id\n\n // First, Recurse to group sub rows before aggregation\n const subRows = groupUpRecursively(groupedRows, depth + 1, id)\n\n // Flatten the leaf rows of the rows in this group\n const leafRows = depth\n ? flattenBy(groupedRows, 'leafRows')\n : groupedRows\n\n const values = aggregateRowsToValues(leafRows, groupedRows, depth)\n\n const row = {\n id,\n isGrouped: true,\n groupByID: columnId,\n groupByVal,\n values,\n subRows,\n leafRows,\n depth,\n index,\n }\n\n subRows.forEach(subRow => {\n groupedFlatRows.push(subRow)\n groupedRowsById[subRow.id] = subRow\n if (subRow.isGrouped) {\n onlyGroupedFlatRows.push(subRow)\n onlyGroupedRowsById[subRow.id] = subRow\n } else {\n nonGroupedFlatRows.push(subRow)\n nonGroupedRowsById[subRow.id] = subRow\n }\n })\n\n return row\n }\n )\n\n return aggregatedGroupedRows\n }\n\n const groupedRows = groupUpRecursively(rows)\n\n groupedRows.forEach(subRow => {\n groupedFlatRows.push(subRow)\n groupedRowsById[subRow.id] = subRow\n if (subRow.isGrouped) {\n onlyGroupedFlatRows.push(subRow)\n onlyGroupedRowsById[subRow.id] = subRow\n } else {\n nonGroupedFlatRows.push(subRow)\n nonGroupedRowsById[subRow.id] = subRow\n }\n })\n\n // Assign the new data\n return [\n groupedRows,\n groupedFlatRows,\n groupedRowsById,\n onlyGroupedFlatRows,\n onlyGroupedRowsById,\n nonGroupedFlatRows,\n nonGroupedRowsById,\n ]\n }, [\n manualGroupBy,\n groupBy,\n rows,\n flatRows,\n rowsById,\n allColumns,\n userAggregations,\n groupByFn,\n ])\n\n const getAutoResetGroupBy = useGetLatest(autoResetGroupBy)\n\n useMountedLayoutEffect(() => {\n if (getAutoResetGroupBy()) {\n dispatch({ type: actions.resetGroupBy })\n }\n }, [dispatch, manualGroupBy ? null : data])\n\n Object.assign(instance, {\n preGroupedRows: rows,\n preGroupedFlatRow: flatRows,\n preGroupedRowsById: rowsById,\n groupedRows,\n groupedFlatRows,\n groupedRowsById,\n onlyGroupedFlatRows,\n onlyGroupedRowsById,\n nonGroupedFlatRows,\n nonGroupedRowsById,\n rows: groupedRows,\n flatRows: groupedFlatRows,\n rowsById: groupedRowsById,\n toggleGroupBy,\n setGroupBy,\n })\n}\n\nfunction prepareRow(row) {\n row.allCells.forEach(cell => {\n // Grouped cells are in the groupBy and the pivot cell for the row\n cell.isGrouped = cell.column.isGrouped && cell.column.id === row.groupByID\n // Placeholder cells are any columns in the groupBy that are not grouped\n cell.isPlaceholder = !cell.isGrouped && cell.column.isGrouped\n // Aggregated cells are not grouped, not repeated, but still have subRows\n cell.isAggregated =\n !cell.isGrouped && !cell.isPlaceholder && row.subRows?.length\n })\n}\n\nexport function defaultGroupByFn(rows, columnId) {\n return rows.reduce((prev, row, i) => {\n // TODO: Might want to implement a key serializer here so\n // irregular column values can still be grouped if needed?\n const resKey = `${row.values[columnId]}`\n prev[resKey] = Array.isArray(prev[resKey]) ? prev[resKey] : []\n prev[resKey].push(row)\n return prev\n }, {})\n}\n","const reSplitAlphaNumeric = /([0-9]+)/gm\n\n// Mixed sorting is slow, but very inclusive of many edge cases.\n// It handles numbers, mixed alphanumeric combinations, and even\n// null, undefined, and Infinity\nexport const alphanumeric = (rowA, rowB, columnId) => {\n let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId)\n\n // Force to strings (or \"\" for unsupported types)\n a = toString(a)\n b = toString(b)\n\n // Split on number groups, but keep the delimiter\n // Then remove falsey split values\n a = a.split(reSplitAlphaNumeric).filter(Boolean)\n b = b.split(reSplitAlphaNumeric).filter(Boolean)\n\n // While\n while (a.length && b.length) {\n let aa = a.shift()\n let bb = b.shift()\n\n const an = parseInt(aa, 10)\n const bn = parseInt(bb, 10)\n\n const combo = [an, bn].sort()\n\n // Both are string\n if (isNaN(combo[0])) {\n if (aa > bb) {\n return 1\n }\n if (bb > aa) {\n return -1\n }\n continue\n }\n\n // One is a string, one is a number\n if (isNaN(combo[1])) {\n return isNaN(an) ? -1 : 1\n }\n\n // Both are numbers\n if (an > bn) {\n return 1\n }\n if (bn > an) {\n return -1\n }\n }\n\n return a.length - b.length\n}\nexport function datetime(rowA, rowB, columnId) {\n let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId)\n\n a = a.getTime()\n b = b.getTime()\n\n return compareBasic(a, b)\n}\n\nexport function basic(rowA, rowB, columnId) {\n let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId)\n\n return compareBasic(a, b)\n}\n\nexport function string(rowA, rowB, columnId) {\n let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId)\n\n a = a.split('').filter(Boolean)\n b = b.split('').filter(Boolean)\n\n while (a.length && b.length) {\n let aa = a.shift()\n let bb = b.shift()\n\n let alower = aa.toLowerCase()\n let blower = bb.toLowerCase()\n\n // Case insensitive comparison until characters match\n if (alower > blower) {\n return 1\n }\n if (blower > alower) {\n return -1\n }\n // If lowercase characters are identical\n if (aa > bb) {\n return 1\n }\n if (bb > aa) {\n return -1\n }\n continue\n }\n\n return a.length - b.length\n}\n\nexport function number(rowA, rowB, columnId) {\n let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId)\n\n const replaceNonNumeric = /[^0-9.]/gi\n\n a = Number(String(a).replace(replaceNonNumeric, ''))\n b = Number(String(b).replace(replaceNonNumeric, ''))\n\n return compareBasic(a, b)\n}\n\n// Utils\n\nfunction compareBasic(a, b) {\n return a === b ? 0 : a > b ? 1 : -1\n}\n\nfunction getRowValuesByColumnID(row1, row2, columnId) {\n return [row1.values[columnId], row2.values[columnId]]\n}\n\nfunction toString(a) {\n if (typeof a === 'number') {\n if (isNaN(a) || a === Infinity || a === -Infinity) {\n return ''\n }\n return String(a)\n }\n if (typeof a === 'string') {\n return a\n }\n return ''\n}\n","import React from 'react'\n\nimport {\n actions,\n ensurePluginOrder,\n defaultColumn,\n makePropGetter,\n useGetLatest,\n useMountedLayoutEffect,\n} from '../publicUtils'\n\nimport { getFirstDefined, isFunction } from '../utils'\n\nimport * as sortTypes from '../sortTypes'\n\n// Actions\nactions.resetSortBy = 'resetSortBy'\nactions.setSortBy = 'setSortBy'\nactions.toggleSortBy = 'toggleSortBy'\nactions.clearSortBy = 'clearSortBy'\n\ndefaultColumn.sortType = 'alphanumeric'\ndefaultColumn.sortDescFirst = false\n\nexport const useSortBy = hooks => {\n hooks.getSortByToggleProps = [defaultGetSortByToggleProps]\n hooks.stateReducers.push(reducer)\n hooks.useInstance.push(useInstance)\n}\n\nuseSortBy.pluginName = 'useSortBy'\n\nconst defaultGetSortByToggleProps = (props, { instance, column }) => {\n const { isMultiSortEvent = e => e.shiftKey } = instance\n\n return [\n props,\n {\n onClick: column.canSort\n ? e => {\n e.persist()\n column.toggleSortBy(\n undefined,\n !instance.disableMultiSort && isMultiSortEvent(e)\n )\n }\n : undefined,\n style: {\n cursor: column.canSort ? 'pointer' : undefined,\n },\n title: column.canSort ? 'Toggle SortBy' : undefined,\n },\n ]\n}\n\n// Reducer\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n sortBy: [],\n ...state,\n }\n }\n\n if (action.type === actions.resetSortBy) {\n return {\n ...state,\n sortBy: instance.initialState.sortBy || [],\n }\n }\n\n if (action.type === actions.clearSortBy) {\n const { sortBy } = state\n const newSortBy = sortBy.filter(d => d.id !== action.columnId)\n\n return {\n ...state,\n sortBy: newSortBy,\n }\n }\n\n if (action.type === actions.setSortBy) {\n const { sortBy } = action\n return {\n ...state,\n sortBy,\n }\n }\n\n if (action.type === actions.toggleSortBy) {\n const { columnId, desc, multi } = action\n\n const {\n allColumns,\n disableMultiSort,\n disableSortRemove,\n disableMultiRemove,\n maxMultiSortColCount = Number.MAX_SAFE_INTEGER,\n } = instance\n\n const { sortBy } = state\n\n // Find the column for this columnId\n const column = allColumns.find(d => d.id === columnId)\n const { sortDescFirst } = column\n\n // Find any existing sortBy for this column\n const existingSortBy = sortBy.find(d => d.id === columnId)\n const existingIndex = sortBy.findIndex(d => d.id === columnId)\n const hasDescDefined = typeof desc !== 'undefined' && desc !== null\n\n let newSortBy = []\n\n // What should we do with this sort action?\n let sortAction\n\n if (!disableMultiSort && multi) {\n if (existingSortBy) {\n sortAction = 'toggle'\n } else {\n sortAction = 'add'\n }\n } else {\n // Normal mode\n if (existingIndex !== sortBy.length - 1 || sortBy.length !== 1) {\n sortAction = 'replace'\n } else if (existingSortBy) {\n sortAction = 'toggle'\n } else {\n sortAction = 'replace'\n }\n }\n\n // Handle toggle states that will remove the sortBy\n if (\n sortAction === 'toggle' && // Must be toggling\n !disableSortRemove && // If disableSortRemove, disable in general\n !hasDescDefined && // Must not be setting desc\n (multi ? !disableMultiRemove : true) && // If multi, don't allow if disableMultiRemove\n ((existingSortBy && // Finally, detect if it should indeed be removed\n existingSortBy.desc &&\n !sortDescFirst) ||\n (!existingSortBy.desc && sortDescFirst))\n ) {\n sortAction = 'remove'\n }\n\n if (sortAction === 'replace') {\n newSortBy = [\n {\n id: columnId,\n desc: hasDescDefined ? desc : sortDescFirst,\n },\n ]\n } else if (sortAction === 'add') {\n newSortBy = [\n ...sortBy,\n {\n id: columnId,\n desc: hasDescDefined ? desc : sortDescFirst,\n },\n ]\n // Take latest n columns\n newSortBy.splice(0, newSortBy.length - maxMultiSortColCount)\n } else if (sortAction === 'toggle') {\n // This flips (or sets) the\n newSortBy = sortBy.map(d => {\n if (d.id === columnId) {\n return {\n ...d,\n desc: hasDescDefined ? desc : !existingSortBy.desc,\n }\n }\n return d\n })\n } else if (sortAction === 'remove') {\n newSortBy = sortBy.filter(d => d.id !== columnId)\n }\n\n return {\n ...state,\n sortBy: newSortBy,\n }\n }\n}\n\nfunction useInstance(instance) {\n const {\n data,\n rows,\n flatRows,\n allColumns,\n orderByFn = defaultOrderByFn,\n sortTypes: userSortTypes,\n manualSortBy,\n defaultCanSort,\n disableSortBy,\n flatHeaders,\n state: { sortBy },\n dispatch,\n plugins,\n getHooks,\n autoResetSortBy = true,\n } = instance\n\n ensurePluginOrder(\n plugins,\n ['useFilters', 'useGlobalFilter', 'useGroupBy', 'usePivotColumns'],\n 'useSortBy'\n )\n\n const setSortBy = React.useCallback(\n sortBy => {\n dispatch({ type: actions.setSortBy, sortBy })\n },\n [dispatch]\n )\n\n // Updates sorting based on a columnId, desc flag and multi flag\n const toggleSortBy = React.useCallback(\n (columnId, desc, multi) => {\n dispatch({ type: actions.toggleSortBy, columnId, desc, multi })\n },\n [dispatch]\n )\n\n // use reference to avoid memory leak in #1608\n const getInstance = useGetLatest(instance)\n\n // Add the getSortByToggleProps method to columns and headers\n flatHeaders.forEach(column => {\n const {\n accessor,\n canSort: defaultColumnCanSort,\n disableSortBy: columnDisableSortBy,\n id,\n } = column\n\n const canSort = accessor\n ? getFirstDefined(\n columnDisableSortBy === true ? false : undefined,\n disableSortBy === true ? false : undefined,\n true\n )\n : getFirstDefined(defaultCanSort, defaultColumnCanSort, false)\n\n column.canSort = canSort\n\n if (column.canSort) {\n column.toggleSortBy = (desc, multi) =>\n toggleSortBy(column.id, desc, multi)\n\n column.clearSortBy = () => {\n dispatch({ type: actions.clearSortBy, columnId: column.id })\n }\n }\n\n column.getSortByToggleProps = makePropGetter(\n getHooks().getSortByToggleProps,\n {\n instance: getInstance(),\n column,\n }\n )\n\n const columnSort = sortBy.find(d => d.id === id)\n column.isSorted = !!columnSort\n column.sortedIndex = sortBy.findIndex(d => d.id === id)\n column.isSortedDesc = column.isSorted ? columnSort.desc : undefined\n })\n\n const [sortedRows, sortedFlatRows] = React.useMemo(() => {\n if (manualSortBy || !sortBy.length) {\n return [rows, flatRows]\n }\n\n const sortedFlatRows = []\n\n // Filter out sortBys that correspond to non existing columns\n const availableSortBy = sortBy.filter(sort =>\n allColumns.find(col => col.id === sort.id)\n )\n\n const sortData = rows => {\n // Use the orderByFn to compose multiple sortBy's together.\n // This will also perform a stable sorting using the row index\n // if needed.\n const sortedData = orderByFn(\n rows,\n availableSortBy.map(sort => {\n // Support custom sorting methods for each column\n const column = allColumns.find(d => d.id === sort.id)\n\n if (!column) {\n throw new Error(\n `React-Table: Could not find a column with id: ${sort.id} while sorting`\n )\n }\n\n const { sortType } = column\n\n // Look up sortBy functions in this order:\n // column function\n // column string lookup on user sortType\n // column string lookup on built-in sortType\n // default function\n // default string lookup on user sortType\n // default string lookup on built-in sortType\n const sortMethod =\n isFunction(sortType) ||\n (userSortTypes || {})[sortType] ||\n sortTypes[sortType]\n\n if (!sortMethod) {\n throw new Error(\n `React-Table: Could not find a valid sortType of '${sortType}' for column '${sort.id}'.`\n )\n }\n\n // Return the correct sortFn.\n // This function should always return in ascending order\n return (a, b) => sortMethod(a, b, sort.id, sort.desc)\n }),\n // Map the directions\n availableSortBy.map(sort => {\n // Detect and use the sortInverted option\n const column = allColumns.find(d => d.id === sort.id)\n\n if (column && column.sortInverted) {\n return sort.desc\n }\n\n return !sort.desc\n })\n )\n\n // If there are sub-rows, sort them\n sortedData.forEach(row => {\n sortedFlatRows.push(row)\n if (!row.subRows || row.subRows.length === 0) {\n return\n }\n row.subRows = sortData(row.subRows)\n })\n\n return sortedData\n }\n\n return [sortData(rows), sortedFlatRows]\n }, [\n manualSortBy,\n sortBy,\n rows,\n flatRows,\n allColumns,\n orderByFn,\n userSortTypes,\n ])\n\n const getAutoResetSortBy = useGetLatest(autoResetSortBy)\n\n useMountedLayoutEffect(() => {\n if (getAutoResetSortBy()) {\n dispatch({ type: actions.resetSortBy })\n }\n }, [manualSortBy ? null : data])\n\n Object.assign(instance, {\n preSortedRows: rows,\n preSortedFlatRows: flatRows,\n sortedRows,\n sortedFlatRows,\n rows: sortedRows,\n flatRows: sortedFlatRows,\n setSortBy,\n toggleSortBy,\n })\n}\n\nexport function defaultOrderByFn(arr, funcs, dirs) {\n return [...arr].sort((rowA, rowB) => {\n for (let i = 0; i < funcs.length; i += 1) {\n const sortFn = funcs[i]\n const desc = dirs[i] === false || dirs[i] === 'desc'\n const sortInt = sortFn(rowA, rowB)\n if (sortInt !== 0) {\n return desc ? -sortInt : sortInt\n }\n }\n return dirs[0] ? rowA.index - rowB.index : rowB.index - rowA.index\n })\n}\n","import React from 'react'\n\n//\n\nimport {\n actions,\n ensurePluginOrder,\n functionalUpdate,\n useMountedLayoutEffect,\n useGetLatest,\n} from '../publicUtils'\n\nimport { expandRows } from '../utils'\n\nconst pluginName = 'usePagination'\n\n// Actions\nactions.resetPage = 'resetPage'\nactions.gotoPage = 'gotoPage'\nactions.setPageSize = 'setPageSize'\n\nexport const usePagination = hooks => {\n hooks.stateReducers.push(reducer)\n hooks.useInstance.push(useInstance)\n}\n\nusePagination.pluginName = pluginName\n\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n pageSize: 10,\n pageIndex: 0,\n ...state,\n }\n }\n\n if (action.type === actions.resetPage) {\n return {\n ...state,\n pageIndex: instance.initialState.pageIndex || 0,\n }\n }\n\n if (action.type === actions.gotoPage) {\n const { pageCount, page } = instance\n const newPageIndex = functionalUpdate(action.pageIndex, state.pageIndex)\n let canNavigate = false\n\n if (newPageIndex > state.pageIndex) {\n // next page\n canNavigate =\n pageCount === -1\n ? page.length >= state.pageSize\n : newPageIndex < pageCount\n } else if (newPageIndex < state.pageIndex) {\n // prev page\n canNavigate = newPageIndex > -1\n }\n\n if (!canNavigate) {\n return state\n }\n\n return {\n ...state,\n pageIndex: newPageIndex,\n }\n }\n\n if (action.type === actions.setPageSize) {\n const { pageSize } = action\n const topRowIndex = state.pageSize * state.pageIndex\n const pageIndex = Math.floor(topRowIndex / pageSize)\n\n return {\n ...state,\n pageIndex,\n pageSize,\n }\n }\n}\n\nfunction useInstance(instance) {\n const {\n rows,\n autoResetPage = true,\n manualExpandedKey = 'expanded',\n plugins,\n pageCount: userPageCount,\n paginateExpandedRows = true,\n expandSubRows = true,\n state: {\n pageSize,\n pageIndex,\n expanded,\n globalFilter,\n filters,\n groupBy,\n sortBy,\n },\n dispatch,\n data,\n manualPagination,\n } = instance\n\n ensurePluginOrder(\n plugins,\n ['useGlobalFilter', 'useFilters', 'useGroupBy', 'useSortBy', 'useExpanded'],\n 'usePagination'\n )\n\n const getAutoResetPage = useGetLatest(autoResetPage)\n\n useMountedLayoutEffect(() => {\n if (getAutoResetPage()) {\n dispatch({ type: actions.resetPage })\n }\n }, [\n dispatch,\n manualPagination ? null : data,\n globalFilter,\n filters,\n groupBy,\n sortBy,\n ])\n\n const pageCount = manualPagination\n ? userPageCount\n : Math.ceil(rows.length / pageSize)\n\n const pageOptions = React.useMemo(\n () =>\n pageCount > 0\n ? [...new Array(pageCount)].fill(null).map((d, i) => i)\n : [],\n [pageCount]\n )\n\n const page = React.useMemo(() => {\n let page\n\n if (manualPagination) {\n page = rows\n } else {\n const pageStart = pageSize * pageIndex\n const pageEnd = pageStart + pageSize\n\n page = rows.slice(pageStart, pageEnd)\n }\n\n if (paginateExpandedRows) {\n return page\n }\n\n return expandRows(page, { manualExpandedKey, expanded, expandSubRows })\n }, [\n expandSubRows,\n expanded,\n manualExpandedKey,\n manualPagination,\n pageIndex,\n pageSize,\n paginateExpandedRows,\n rows,\n ])\n\n const canPreviousPage = pageIndex > 0\n const canNextPage =\n pageCount === -1 ? page.length >= pageSize : pageIndex < pageCount - 1\n\n const gotoPage = React.useCallback(\n pageIndex => {\n dispatch({ type: actions.gotoPage, pageIndex })\n },\n [dispatch]\n )\n\n const previousPage = React.useCallback(() => {\n return gotoPage(old => old - 1)\n }, [gotoPage])\n\n const nextPage = React.useCallback(() => {\n return gotoPage(old => old + 1)\n }, [gotoPage])\n\n const setPageSize = React.useCallback(\n pageSize => {\n dispatch({ type: actions.setPageSize, pageSize })\n },\n [dispatch]\n )\n\n Object.assign(instance, {\n pageOptions,\n pageCount,\n page,\n canPreviousPage,\n canNextPage,\n gotoPage,\n previousPage,\n nextPage,\n setPageSize,\n })\n}\n","/* istanbul ignore file */\n\nimport {\n actions,\n makePropGetter,\n ensurePluginOrder,\n useMountedLayoutEffect,\n useGetLatest,\n} from '../publicUtils'\n\nimport { flattenColumns, getFirstDefined } from '../utils'\n\n// Actions\nactions.resetPivot = 'resetPivot'\nactions.togglePivot = 'togglePivot'\n\nexport const _UNSTABLE_usePivotColumns = hooks => {\n hooks.getPivotToggleProps = [defaultGetPivotToggleProps]\n hooks.stateReducers.push(reducer)\n hooks.useInstanceAfterData.push(useInstanceAfterData)\n hooks.allColumns.push(allColumns)\n hooks.accessValue.push(accessValue)\n hooks.materializedColumns.push(materializedColumns)\n hooks.materializedColumnsDeps.push(materializedColumnsDeps)\n hooks.visibleColumns.push(visibleColumns)\n hooks.visibleColumnsDeps.push(visibleColumnsDeps)\n hooks.useInstance.push(useInstance)\n hooks.prepareRow.push(prepareRow)\n}\n\n_UNSTABLE_usePivotColumns.pluginName = 'usePivotColumns'\n\nconst defaultPivotColumns = []\n\nconst defaultGetPivotToggleProps = (props, { header }) => [\n props,\n {\n onClick: header.canPivot\n ? e => {\n e.persist()\n header.togglePivot()\n }\n : undefined,\n style: {\n cursor: header.canPivot ? 'pointer' : undefined,\n },\n title: 'Toggle Pivot',\n },\n]\n\n// Reducer\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n pivotColumns: defaultPivotColumns,\n ...state,\n }\n }\n\n if (action.type === actions.resetPivot) {\n return {\n ...state,\n pivotColumns: instance.initialState.pivotColumns || defaultPivotColumns,\n }\n }\n\n if (action.type === actions.togglePivot) {\n const { columnId, value: setPivot } = action\n\n const resolvedPivot =\n typeof setPivot !== 'undefined'\n ? setPivot\n : !state.pivotColumns.includes(columnId)\n\n if (resolvedPivot) {\n return {\n ...state,\n pivotColumns: [...state.pivotColumns, columnId],\n }\n }\n\n return {\n ...state,\n pivotColumns: state.pivotColumns.filter(d => d !== columnId),\n }\n }\n}\n\nfunction useInstanceAfterData(instance) {\n instance.allColumns.forEach(column => {\n column.isPivotSource = instance.state.pivotColumns.includes(column.id)\n })\n}\n\nfunction allColumns(columns, { instance }) {\n columns.forEach(column => {\n column.isPivotSource = instance.state.pivotColumns.includes(column.id)\n column.uniqueValues = new Set()\n })\n return columns\n}\n\nfunction accessValue(value, { column }) {\n if (column.uniqueValues && typeof value !== 'undefined') {\n column.uniqueValues.add(value)\n }\n return value\n}\n\nfunction materializedColumns(materialized, { instance }) {\n const { allColumns, state } = instance\n\n if (!state.pivotColumns.length || !state.groupBy || !state.groupBy.length) {\n return materialized\n }\n\n const pivotColumns = state.pivotColumns\n .map(id => allColumns.find(d => d.id === id))\n .filter(Boolean)\n\n const sourceColumns = allColumns.filter(\n d =>\n !d.isPivotSource &&\n !state.groupBy.includes(d.id) &&\n !state.pivotColumns.includes(d.id)\n )\n\n const buildPivotColumns = (depth = 0, parent, pivotFilters = []) => {\n const pivotColumn = pivotColumns[depth]\n\n if (!pivotColumn) {\n return sourceColumns.map(sourceColumn => {\n // TODO: We could offer support here for renesting pivoted\n // columns inside copies of their header groups. For now,\n // that seems like it would be (1) overkill on nesting, considering\n // you already get nesting for every pivot level and (2)\n // really hard. :)\n\n return {\n ...sourceColumn,\n canPivot: false,\n isPivoted: true,\n parent,\n depth: depth,\n id: `${parent ? `${parent.id}.${sourceColumn.id}` : sourceColumn.id}`,\n accessor: (originalRow, i, row) => {\n if (pivotFilters.every(filter => filter(row))) {\n return row.values[sourceColumn.id]\n }\n },\n }\n })\n }\n\n const uniqueValues = Array.from(pivotColumn.uniqueValues).sort()\n\n return uniqueValues.map(uniqueValue => {\n const columnGroup = {\n ...pivotColumn,\n Header:\n pivotColumn.PivotHeader || typeof pivotColumn.header === 'string'\n ? `${pivotColumn.Header}: ${uniqueValue}`\n : uniqueValue,\n isPivotGroup: true,\n parent,\n depth,\n id: parent\n ? `${parent.id}.${pivotColumn.id}.${uniqueValue}`\n : `${pivotColumn.id}.${uniqueValue}`,\n pivotValue: uniqueValue,\n }\n\n columnGroup.columns = buildPivotColumns(depth + 1, columnGroup, [\n ...pivotFilters,\n row => row.values[pivotColumn.id] === uniqueValue,\n ])\n\n return columnGroup\n })\n }\n\n const newMaterialized = flattenColumns(buildPivotColumns())\n\n return [...materialized, ...newMaterialized]\n}\n\nfunction materializedColumnsDeps(\n deps,\n {\n instance: {\n state: { pivotColumns, groupBy },\n },\n }\n) {\n return [...deps, pivotColumns, groupBy]\n}\n\nfunction visibleColumns(visibleColumns, { instance: { state } }) {\n visibleColumns = visibleColumns.filter(d => !d.isPivotSource)\n\n if (state.pivotColumns.length && state.groupBy && state.groupBy.length) {\n visibleColumns = visibleColumns.filter(\n column => column.isGrouped || column.isPivoted\n )\n }\n\n return visibleColumns\n}\n\nfunction visibleColumnsDeps(deps, { instance }) {\n return [...deps, instance.state.pivotColumns, instance.state.groupBy]\n}\n\nfunction useInstance(instance) {\n const {\n columns,\n allColumns,\n flatHeaders,\n // pivotFn = defaultPivotFn,\n // manualPivot,\n getHooks,\n plugins,\n dispatch,\n autoResetPivot = true,\n manaulPivot,\n disablePivot,\n defaultCanPivot,\n } = instance\n\n ensurePluginOrder(plugins, ['useGroupBy'], 'usePivotColumns')\n\n const getInstance = useGetLatest(instance)\n\n allColumns.forEach(column => {\n const {\n accessor,\n defaultPivot: defaultColumnPivot,\n disablePivot: columnDisablePivot,\n } = column\n\n column.canPivot = accessor\n ? getFirstDefined(\n column.canPivot,\n columnDisablePivot === true ? false : undefined,\n disablePivot === true ? false : undefined,\n true\n )\n : getFirstDefined(\n column.canPivot,\n defaultColumnPivot,\n defaultCanPivot,\n false\n )\n\n if (column.canPivot) {\n column.togglePivot = () => instance.togglePivot(column.id)\n }\n\n column.Aggregated = column.Aggregated || column.Cell\n })\n\n const togglePivot = (columnId, value) => {\n dispatch({ type: actions.togglePivot, columnId, value })\n }\n\n flatHeaders.forEach(header => {\n header.getPivotToggleProps = makePropGetter(\n getHooks().getPivotToggleProps,\n {\n instance: getInstance(),\n header,\n }\n )\n })\n\n const getAutoResetPivot = useGetLatest(autoResetPivot)\n\n useMountedLayoutEffect(() => {\n if (getAutoResetPivot()) {\n dispatch({ type: actions.resetPivot })\n }\n }, [dispatch, manaulPivot ? null : columns])\n\n Object.assign(instance, {\n togglePivot,\n })\n}\n\nfunction prepareRow(row) {\n row.allCells.forEach(cell => {\n // Grouped cells are in the pivotColumns and the pivot cell for the row\n cell.isPivoted = cell.column.isPivoted\n })\n}\n","import React from 'react'\n\nimport {\n actions,\n makePropGetter,\n ensurePluginOrder,\n useGetLatest,\n useMountedLayoutEffect,\n} from '../publicUtils'\n\nconst pluginName = 'useRowSelect'\n\n// Actions\nactions.resetSelectedRows = 'resetSelectedRows'\nactions.toggleAllRowsSelected = 'toggleAllRowsSelected'\nactions.toggleRowSelected = 'toggleRowSelected'\nactions.toggleAllPageRowsSelected = 'toggleAllPageRowsSelected'\n\nexport const useRowSelect = hooks => {\n hooks.getToggleRowSelectedProps = [defaultGetToggleRowSelectedProps]\n hooks.getToggleAllRowsSelectedProps = [defaultGetToggleAllRowsSelectedProps]\n hooks.getToggleAllPageRowsSelectedProps = [\n defaultGetToggleAllPageRowsSelectedProps,\n ]\n hooks.stateReducers.push(reducer)\n hooks.useInstance.push(useInstance)\n hooks.prepareRow.push(prepareRow)\n}\n\nuseRowSelect.pluginName = pluginName\n\nconst defaultGetToggleRowSelectedProps = (props, { instance, row }) => {\n const { manualRowSelectedKey = 'isSelected' } = instance\n let checked = false\n\n if (row.original && row.original[manualRowSelectedKey]) {\n checked = true\n } else {\n checked = row.isSelected\n }\n\n return [\n props,\n {\n onChange: e => {\n row.toggleRowSelected(e.target.checked)\n },\n style: {\n cursor: 'pointer',\n },\n checked,\n title: 'Toggle Row Selected',\n indeterminate: row.isSomeSelected,\n },\n ]\n}\n\nconst defaultGetToggleAllRowsSelectedProps = (props, { instance }) => [\n props,\n {\n onChange: e => {\n instance.toggleAllRowsSelected(e.target.checked)\n },\n style: {\n cursor: 'pointer',\n },\n checked: instance.isAllRowsSelected,\n title: 'Toggle All Rows Selected',\n indeterminate: Boolean(\n !instance.isAllRowsSelected &&\n Object.keys(instance.state.selectedRowIds).length\n ),\n },\n]\n\nconst defaultGetToggleAllPageRowsSelectedProps = (props, { instance }) => [\n props,\n {\n onChange(e) {\n instance.toggleAllPageRowsSelected(e.target.checked)\n },\n style: {\n cursor: 'pointer',\n },\n checked: instance.isAllPageRowsSelected,\n title: 'Toggle All Current Page Rows Selected',\n indeterminate: Boolean(\n !instance.isAllPageRowsSelected &&\n instance.page.some(({ id }) => instance.state.selectedRowIds[id])\n ),\n },\n]\n\n// eslint-disable-next-line max-params\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n selectedRowIds: {},\n ...state,\n }\n }\n\n if (action.type === actions.resetSelectedRows) {\n return {\n ...state,\n selectedRowIds: instance.initialState.selectedRowIds || {},\n }\n }\n\n if (action.type === actions.toggleAllRowsSelected) {\n const { value: setSelected } = action\n const {\n isAllRowsSelected,\n rowsById,\n nonGroupedRowsById = rowsById,\n } = instance\n\n const selectAll =\n typeof setSelected !== 'undefined' ? setSelected : !isAllRowsSelected\n\n // Only remove/add the rows that are visible on the screen\n // Leave all the other rows that are selected alone.\n const selectedRowIds = Object.assign({}, state.selectedRowIds)\n\n if (selectAll) {\n Object.keys(nonGroupedRowsById).forEach(rowId => {\n selectedRowIds[rowId] = true\n })\n } else {\n Object.keys(nonGroupedRowsById).forEach(rowId => {\n delete selectedRowIds[rowId]\n })\n }\n\n return {\n ...state,\n selectedRowIds,\n }\n }\n\n if (action.type === actions.toggleRowSelected) {\n const { id, value: setSelected } = action\n const { rowsById, selectSubRows = true, getSubRows } = instance\n const isSelected = state.selectedRowIds[id]\n const shouldExist =\n typeof setSelected !== 'undefined' ? setSelected : !isSelected\n\n if (isSelected === shouldExist) {\n return state\n }\n\n const newSelectedRowIds = { ...state.selectedRowIds }\n\n const handleRowById = id => {\n const row = rowsById[id]\n\n if (row) {\n if (!row.isGrouped) {\n if (shouldExist) {\n newSelectedRowIds[id] = true\n } else {\n delete newSelectedRowIds[id]\n }\n }\n\n if (selectSubRows && getSubRows(row)) {\n return getSubRows(row).forEach(row => handleRowById(row.id))\n }\n }\n }\n\n handleRowById(id)\n\n return {\n ...state,\n selectedRowIds: newSelectedRowIds,\n }\n }\n\n if (action.type === actions.toggleAllPageRowsSelected) {\n const { value: setSelected } = action\n const {\n page,\n rowsById,\n selectSubRows = true,\n isAllPageRowsSelected,\n getSubRows,\n } = instance\n\n const selectAll =\n typeof setSelected !== 'undefined' ? setSelected : !isAllPageRowsSelected\n\n const newSelectedRowIds = { ...state.selectedRowIds }\n\n const handleRowById = id => {\n const row = rowsById[id]\n\n if (!row.isGrouped) {\n if (selectAll) {\n newSelectedRowIds[id] = true\n } else {\n delete newSelectedRowIds[id]\n }\n }\n\n if (selectSubRows && getSubRows(row)) {\n return getSubRows(row).forEach(row => handleRowById(row.id))\n }\n }\n\n page.forEach(row => handleRowById(row.id))\n\n return {\n ...state,\n selectedRowIds: newSelectedRowIds,\n }\n }\n return state\n}\n\nfunction useInstance(instance) {\n const {\n data,\n rows,\n getHooks,\n plugins,\n rowsById,\n nonGroupedRowsById = rowsById,\n autoResetSelectedRows = true,\n state: { selectedRowIds },\n selectSubRows = true,\n dispatch,\n page,\n getSubRows,\n } = instance\n\n ensurePluginOrder(\n plugins,\n ['useFilters', 'useGroupBy', 'useSortBy', 'useExpanded', 'usePagination'],\n 'useRowSelect'\n )\n\n const selectedFlatRows = React.useMemo(() => {\n const selectedFlatRows = []\n\n rows.forEach(row => {\n const isSelected = selectSubRows\n ? getRowIsSelected(row, selectedRowIds, getSubRows)\n : !!selectedRowIds[row.id]\n row.isSelected = !!isSelected\n row.isSomeSelected = isSelected === null\n\n if (isSelected) {\n selectedFlatRows.push(row)\n }\n })\n\n return selectedFlatRows\n }, [rows, selectSubRows, selectedRowIds, getSubRows])\n\n let isAllRowsSelected = Boolean(\n Object.keys(nonGroupedRowsById).length && Object.keys(selectedRowIds).length\n )\n\n let isAllPageRowsSelected = isAllRowsSelected\n\n if (isAllRowsSelected) {\n if (Object.keys(nonGroupedRowsById).some(id => !selectedRowIds[id])) {\n isAllRowsSelected = false\n }\n }\n\n if (!isAllRowsSelected) {\n if (page && page.length && page.some(({ id }) => !selectedRowIds[id])) {\n isAllPageRowsSelected = false\n }\n }\n\n const getAutoResetSelectedRows = useGetLatest(autoResetSelectedRows)\n\n useMountedLayoutEffect(() => {\n if (getAutoResetSelectedRows()) {\n dispatch({ type: actions.resetSelectedRows })\n }\n }, [dispatch, data])\n\n const toggleAllRowsSelected = React.useCallback(\n value => dispatch({ type: actions.toggleAllRowsSelected, value }),\n [dispatch]\n )\n\n const toggleAllPageRowsSelected = React.useCallback(\n value => dispatch({ type: actions.toggleAllPageRowsSelected, value }),\n [dispatch]\n )\n\n const toggleRowSelected = React.useCallback(\n (id, value) => dispatch({ type: actions.toggleRowSelected, id, value }),\n [dispatch]\n )\n\n const getInstance = useGetLatest(instance)\n\n const getToggleAllRowsSelectedProps = makePropGetter(\n getHooks().getToggleAllRowsSelectedProps,\n { instance: getInstance() }\n )\n\n const getToggleAllPageRowsSelectedProps = makePropGetter(\n getHooks().getToggleAllPageRowsSelectedProps,\n { instance: getInstance() }\n )\n\n Object.assign(instance, {\n selectedFlatRows,\n isAllRowsSelected,\n isAllPageRowsSelected,\n toggleRowSelected,\n toggleAllRowsSelected,\n getToggleAllRowsSelectedProps,\n getToggleAllPageRowsSelectedProps,\n toggleAllPageRowsSelected,\n })\n}\n\nfunction prepareRow(row, { instance }) {\n row.toggleRowSelected = set => instance.toggleRowSelected(row.id, set)\n\n row.getToggleRowSelectedProps = makePropGetter(\n instance.getHooks().getToggleRowSelectedProps,\n { instance: instance, row }\n )\n}\n\nfunction getRowIsSelected(row, selectedRowIds, getSubRows) {\n if (selectedRowIds[row.id]) {\n return true\n }\n\n const subRows = getSubRows(row)\n\n if (subRows && subRows.length) {\n let allChildrenSelected = true\n let someSelected = false\n\n subRows.forEach(subRow => {\n // Bail out early if we know both of these\n if (someSelected && !allChildrenSelected) {\n return\n }\n\n if (getRowIsSelected(subRow, selectedRowIds, getSubRows)) {\n someSelected = true\n } else {\n allChildrenSelected = false\n }\n })\n return allChildrenSelected ? true : someSelected ? null : false\n }\n\n return false\n}\n","import React from 'react'\n\nimport {\n actions,\n functionalUpdate,\n useMountedLayoutEffect,\n useGetLatest,\n} from '../publicUtils'\n\nconst defaultInitialRowStateAccessor = row => ({})\nconst defaultInitialCellStateAccessor = cell => ({})\n\n// Actions\nactions.setRowState = 'setRowState'\nactions.setCellState = 'setCellState'\nactions.resetRowState = 'resetRowState'\n\nexport const useRowState = hooks => {\n hooks.stateReducers.push(reducer)\n hooks.useInstance.push(useInstance)\n hooks.prepareRow.push(prepareRow)\n}\n\nuseRowState.pluginName = 'useRowState'\n\nfunction reducer(state, action, previousState, instance) {\n const {\n initialRowStateAccessor = defaultInitialRowStateAccessor,\n initialCellStateAccessor = defaultInitialCellStateAccessor,\n rowsById,\n } = instance\n\n if (action.type === actions.init) {\n return {\n rowState: {},\n ...state,\n }\n }\n\n if (action.type === actions.resetRowState) {\n return {\n ...state,\n rowState: instance.initialState.rowState || {},\n }\n }\n\n if (action.type === actions.setRowState) {\n const { rowId, value } = action\n\n const oldRowState =\n typeof state.rowState[rowId] !== 'undefined'\n ? state.rowState[rowId]\n : initialRowStateAccessor(rowsById[rowId])\n\n return {\n ...state,\n rowState: {\n ...state.rowState,\n [rowId]: functionalUpdate(value, oldRowState),\n },\n }\n }\n\n if (action.type === actions.setCellState) {\n const { rowId, columnId, value } = action\n\n const oldRowState =\n typeof state.rowState[rowId] !== 'undefined'\n ? state.rowState[rowId]\n : initialRowStateAccessor(rowsById[rowId])\n\n const oldCellState =\n typeof oldRowState?.cellState?.[columnId] !== 'undefined'\n ? oldRowState.cellState[columnId]\n : initialCellStateAccessor(\n rowsById[rowId]?.cells?.find(cell => cell.column.id === columnId)\n )\n\n return {\n ...state,\n rowState: {\n ...state.rowState,\n [rowId]: {\n ...oldRowState,\n cellState: {\n ...(oldRowState.cellState || {}),\n [columnId]: functionalUpdate(value, oldCellState),\n },\n },\n },\n }\n }\n}\n\nfunction useInstance(instance) {\n const { autoResetRowState = true, data, dispatch } = instance\n\n const setRowState = React.useCallback(\n (rowId, value) =>\n dispatch({\n type: actions.setRowState,\n rowId,\n value,\n }),\n [dispatch]\n )\n\n const setCellState = React.useCallback(\n (rowId, columnId, value) =>\n dispatch({\n type: actions.setCellState,\n rowId,\n columnId,\n value,\n }),\n [dispatch]\n )\n\n const getAutoResetRowState = useGetLatest(autoResetRowState)\n\n useMountedLayoutEffect(() => {\n if (getAutoResetRowState()) {\n dispatch({ type: actions.resetRowState })\n }\n }, [data])\n\n Object.assign(instance, {\n setRowState,\n setCellState,\n })\n}\n\nfunction prepareRow(row, { instance }) {\n const {\n initialRowStateAccessor = defaultInitialRowStateAccessor,\n initialCellStateAccessor = defaultInitialCellStateAccessor,\n state: { rowState },\n } = instance\n\n if (row) {\n row.state =\n typeof rowState[row.id] !== 'undefined'\n ? rowState[row.id]\n : initialRowStateAccessor(row)\n\n row.setState = updater => {\n return instance.setRowState(row.id, updater)\n }\n\n row.cells.forEach(cell => {\n if (!row.state.cellState) {\n row.state.cellState = {}\n }\n\n cell.state =\n typeof row.state.cellState[cell.column.id] !== 'undefined'\n ? row.state.cellState[cell.column.id]\n : initialCellStateAccessor(cell)\n\n cell.setState = updater => {\n return instance.setCellState(row.id, cell.column.id, updater)\n }\n })\n }\n}\n","import React from 'react'\n\nimport { functionalUpdate, actions } from '../publicUtils'\n\n// Actions\nactions.resetColumnOrder = 'resetColumnOrder'\nactions.setColumnOrder = 'setColumnOrder'\n\nexport const useColumnOrder = hooks => {\n hooks.stateReducers.push(reducer)\n hooks.visibleColumnsDeps.push((deps, { instance }) => {\n return [...deps, instance.state.columnOrder]\n })\n hooks.visibleColumns.push(visibleColumns)\n hooks.useInstance.push(useInstance)\n}\n\nuseColumnOrder.pluginName = 'useColumnOrder'\n\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n columnOrder: [],\n ...state,\n }\n }\n\n if (action.type === actions.resetColumnOrder) {\n return {\n ...state,\n columnOrder: instance.initialState.columnOrder || [],\n }\n }\n\n if (action.type === actions.setColumnOrder) {\n return {\n ...state,\n columnOrder: functionalUpdate(action.columnOrder, state.columnOrder),\n }\n }\n}\n\nfunction visibleColumns(\n columns,\n {\n instance: {\n state: { columnOrder },\n },\n }\n) {\n // If there is no order, return the normal columns\n if (!columnOrder || !columnOrder.length) {\n return columns\n }\n\n const columnOrderCopy = [...columnOrder]\n\n // If there is an order, make a copy of the columns\n const columnsCopy = [...columns]\n\n // And make a new ordered array of the columns\n const columnsInOrder = []\n\n // Loop over the columns and place them in order into the new array\n while (columnsCopy.length && columnOrderCopy.length) {\n const targetColumnId = columnOrderCopy.shift()\n const foundIndex = columnsCopy.findIndex(d => d.id === targetColumnId)\n if (foundIndex > -1) {\n columnsInOrder.push(columnsCopy.splice(foundIndex, 1)[0])\n }\n }\n\n // If there are any columns left, add them to the end\n return [...columnsInOrder, ...columnsCopy]\n}\n\nfunction useInstance(instance) {\n const { dispatch } = instance\n\n instance.setColumnOrder = React.useCallback(\n columnOrder => {\n return dispatch({ type: actions.setColumnOrder, columnOrder })\n },\n [dispatch]\n )\n}\n","import React from 'react'\n\nimport {\n actions,\n defaultColumn,\n makePropGetter,\n useGetLatest,\n ensurePluginOrder,\n useMountedLayoutEffect,\n} from '../publicUtils'\n\nimport { getFirstDefined, passiveEventSupported } from '../utils'\n\n// Default Column\ndefaultColumn.canResize = true\n\n// Actions\nactions.columnStartResizing = 'columnStartResizing'\nactions.columnResizing = 'columnResizing'\nactions.columnDoneResizing = 'columnDoneResizing'\nactions.resetResize = 'resetResize'\n\nexport const useResizeColumns = hooks => {\n hooks.getResizerProps = [defaultGetResizerProps]\n hooks.getHeaderProps.push({\n style: {\n position: 'relative',\n },\n })\n hooks.stateReducers.push(reducer)\n hooks.useInstance.push(useInstance)\n hooks.useInstanceBeforeDimensions.push(useInstanceBeforeDimensions)\n}\n\nconst defaultGetResizerProps = (props, { instance, header }) => {\n const { dispatch } = instance\n\n const onResizeStart = (e, header) => {\n let isTouchEvent = false\n if (e.type === 'touchstart') {\n // lets not respond to multiple touches (e.g. 2 or 3 fingers)\n if (e.touches && e.touches.length > 1) {\n return\n }\n isTouchEvent = true\n }\n const headersToResize = getLeafHeaders(header)\n const headerIdWidths = headersToResize.map(d => [d.id, d.totalWidth])\n\n const clientX = isTouchEvent ? Math.round(e.touches[0].clientX) : e.clientX\n\n let raf\n let mostRecentClientX\n\n const dispatchEnd = () => {\n window.cancelAnimationFrame(raf)\n raf = null\n dispatch({ type: actions.columnDoneResizing })\n }\n const dispatchMove = () => {\n window.cancelAnimationFrame(raf)\n raf = null\n dispatch({ type: actions.columnResizing, clientX: mostRecentClientX })\n }\n\n const scheduleDispatchMoveOnNextAnimationFrame = clientXPos => {\n mostRecentClientX = clientXPos\n if (!raf) {\n raf = window.requestAnimationFrame(dispatchMove)\n }\n }\n\n const handlersAndEvents = {\n mouse: {\n moveEvent: 'mousemove',\n moveHandler: e => scheduleDispatchMoveOnNextAnimationFrame(e.clientX),\n upEvent: 'mouseup',\n upHandler: e => {\n document.removeEventListener(\n 'mousemove',\n handlersAndEvents.mouse.moveHandler\n )\n document.removeEventListener(\n 'mouseup',\n handlersAndEvents.mouse.upHandler\n )\n dispatchEnd()\n },\n },\n touch: {\n moveEvent: 'touchmove',\n moveHandler: e => {\n if (e.cancelable) {\n e.preventDefault()\n e.stopPropagation()\n }\n scheduleDispatchMoveOnNextAnimationFrame(e.touches[0].clientX)\n return false\n },\n upEvent: 'touchend',\n upHandler: e => {\n document.removeEventListener(\n handlersAndEvents.touch.moveEvent,\n handlersAndEvents.touch.moveHandler\n )\n document.removeEventListener(\n handlersAndEvents.touch.upEvent,\n handlersAndEvents.touch.moveHandler\n )\n dispatchEnd()\n },\n },\n }\n\n const events = isTouchEvent\n ? handlersAndEvents.touch\n : handlersAndEvents.mouse\n const passiveIfSupported = passiveEventSupported()\n ? { passive: false }\n : false\n document.addEventListener(\n events.moveEvent,\n events.moveHandler,\n passiveIfSupported\n )\n document.addEventListener(\n events.upEvent,\n events.upHandler,\n passiveIfSupported\n )\n\n dispatch({\n type: actions.columnStartResizing,\n columnId: header.id,\n columnWidth: header.totalWidth,\n headerIdWidths,\n clientX,\n })\n }\n\n return [\n props,\n {\n onMouseDown: e => e.persist() || onResizeStart(e, header),\n onTouchStart: e => e.persist() || onResizeStart(e, header),\n style: {\n cursor: 'col-resize',\n },\n draggable: false,\n role: 'separator',\n },\n ]\n}\n\nuseResizeColumns.pluginName = 'useResizeColumns'\n\nfunction reducer(state, action) {\n if (action.type === actions.init) {\n return {\n columnResizing: {\n columnWidths: {},\n },\n ...state,\n }\n }\n\n if (action.type === actions.resetResize) {\n return {\n ...state,\n columnResizing: {\n columnWidths: {},\n },\n }\n }\n\n if (action.type === actions.columnStartResizing) {\n const { clientX, columnId, columnWidth, headerIdWidths } = action\n\n return {\n ...state,\n columnResizing: {\n ...state.columnResizing,\n startX: clientX,\n headerIdWidths,\n columnWidth,\n isResizingColumn: columnId,\n },\n }\n }\n\n if (action.type === actions.columnResizing) {\n const { clientX } = action\n const { startX, columnWidth, headerIdWidths = [] } = state.columnResizing\n\n const deltaX = clientX - startX\n const percentageDeltaX = deltaX / columnWidth\n\n const newColumnWidths = {}\n\n headerIdWidths.forEach(([headerId, headerWidth]) => {\n newColumnWidths[headerId] = Math.max(\n headerWidth + headerWidth * percentageDeltaX,\n 0\n )\n })\n\n return {\n ...state,\n columnResizing: {\n ...state.columnResizing,\n columnWidths: {\n ...state.columnResizing.columnWidths,\n ...newColumnWidths,\n },\n },\n }\n }\n\n if (action.type === actions.columnDoneResizing) {\n return {\n ...state,\n columnResizing: {\n ...state.columnResizing,\n startX: null,\n isResizingColumn: null,\n },\n }\n }\n}\n\nconst useInstanceBeforeDimensions = instance => {\n const {\n flatHeaders,\n disableResizing,\n getHooks,\n state: { columnResizing },\n } = instance\n\n const getInstance = useGetLatest(instance)\n\n flatHeaders.forEach(header => {\n const canResize = getFirstDefined(\n header.disableResizing === true ? false : undefined,\n disableResizing === true ? false : undefined,\n true\n )\n\n header.canResize = canResize\n header.width =\n columnResizing.columnWidths[header.id] ||\n header.originalWidth ||\n header.width\n header.isResizing = columnResizing.isResizingColumn === header.id\n\n if (canResize) {\n header.getResizerProps = makePropGetter(getHooks().getResizerProps, {\n instance: getInstance(),\n header,\n })\n }\n })\n}\n\nfunction useInstance(instance) {\n const { plugins, dispatch, autoResetResize = true, columns } = instance\n\n ensurePluginOrder(plugins, ['useAbsoluteLayout'], 'useResizeColumns')\n\n const getAutoResetResize = useGetLatest(autoResetResize)\n useMountedLayoutEffect(() => {\n if (getAutoResetResize()) {\n dispatch({ type: actions.resetResize })\n }\n }, [columns])\n\n const resetResizing = React.useCallback(\n () => dispatch({ type: actions.resetResize }),\n [dispatch]\n )\n\n Object.assign(instance, {\n resetResizing,\n })\n}\n\nfunction getLeafHeaders(header) {\n const leafHeaders = []\n const recurseHeader = header => {\n if (header.columns && header.columns.length) {\n header.columns.map(recurseHeader)\n }\n leafHeaders.push(header)\n }\n recurseHeader(header)\n return leafHeaders\n}\n","const cellStyles = {\n position: 'absolute',\n top: 0,\n}\n\nexport const useAbsoluteLayout = hooks => {\n hooks.getTableBodyProps.push(getRowStyles)\n hooks.getRowProps.push(getRowStyles)\n hooks.getHeaderGroupProps.push(getRowStyles)\n hooks.getFooterGroupProps.push(getRowStyles)\n\n hooks.getHeaderProps.push((props, { column }) => [\n props,\n {\n style: {\n ...cellStyles,\n left: `${column.totalLeft}px`,\n width: `${column.totalWidth}px`,\n },\n },\n ])\n\n hooks.getCellProps.push((props, { cell }) => [\n props,\n {\n style: {\n ...cellStyles,\n left: `${cell.column.totalLeft}px`,\n width: `${cell.column.totalWidth}px`,\n },\n },\n ])\n\n hooks.getFooterProps.push((props, { column }) => [\n props,\n {\n style: {\n ...cellStyles,\n left: `${column.totalLeft}px`,\n width: `${column.totalWidth}px`,\n },\n },\n ])\n}\n\nuseAbsoluteLayout.pluginName = 'useAbsoluteLayout'\n\nconst getRowStyles = (props, { instance }) => [\n props,\n {\n style: {\n position: 'relative',\n width: `${instance.totalColumnsWidth}px`,\n },\n },\n]\n","const cellStyles = {\n display: 'inline-block',\n boxSizing: 'border-box',\n}\n\nconst getRowStyles = (props, { instance }) => [\n props,\n {\n style: {\n display: 'flex',\n width: `${instance.totalColumnsWidth}px`,\n },\n },\n]\n\nexport const useBlockLayout = hooks => {\n hooks.getRowProps.push(getRowStyles)\n hooks.getHeaderGroupProps.push(getRowStyles)\n hooks.getFooterGroupProps.push(getRowStyles)\n\n hooks.getHeaderProps.push((props, { column }) => [\n props,\n {\n style: {\n ...cellStyles,\n width: `${column.totalWidth}px`,\n },\n },\n ])\n\n hooks.getCellProps.push((props, { cell }) => [\n props,\n {\n style: {\n ...cellStyles,\n width: `${cell.column.totalWidth}px`,\n },\n },\n ])\n\n hooks.getFooterProps.push((props, { column }) => [\n props,\n {\n style: {\n ...cellStyles,\n width: `${column.totalWidth}px`,\n },\n },\n ])\n}\n\nuseBlockLayout.pluginName = 'useBlockLayout'\n","export function useFlexLayout(hooks) {\n hooks.getTableProps.push(getTableProps)\n hooks.getRowProps.push(getRowStyles)\n hooks.getHeaderGroupProps.push(getRowStyles)\n hooks.getFooterGroupProps.push(getRowStyles)\n hooks.getHeaderProps.push(getHeaderProps)\n hooks.getCellProps.push(getCellProps)\n hooks.getFooterProps.push(getFooterProps)\n}\n\nuseFlexLayout.pluginName = 'useFlexLayout'\n\nconst getTableProps = (props, { instance }) => [\n props,\n {\n style: {\n minWidth: `${instance.totalColumnsMinWidth}px`,\n },\n },\n]\n\nconst getRowStyles = (props, { instance }) => [\n props,\n {\n style: {\n display: 'flex',\n flex: '1 0 auto',\n minWidth: `${instance.totalColumnsMinWidth}px`,\n },\n },\n]\n\nconst getHeaderProps = (props, { column }) => [\n props,\n {\n style: {\n boxSizing: 'border-box',\n flex: column.totalFlexWidth\n ? `${column.totalFlexWidth} 0 auto`\n : undefined,\n minWidth: `${column.totalMinWidth}px`,\n width: `${column.totalWidth}px`,\n },\n },\n]\n\nconst getCellProps = (props, { cell }) => [\n props,\n {\n style: {\n boxSizing: 'border-box',\n flex: `${cell.column.totalFlexWidth} 0 auto`,\n minWidth: `${cell.column.totalMinWidth}px`,\n width: `${cell.column.totalWidth}px`,\n },\n },\n]\n\nconst getFooterProps = (props, { column }) => [\n props,\n {\n style: {\n boxSizing: 'border-box',\n flex: column.totalFlexWidth\n ? `${column.totalFlexWidth} 0 auto`\n : undefined,\n minWidth: `${column.totalMinWidth}px`,\n width: `${column.totalWidth}px`,\n },\n },\n]\n","import { actions } from '../publicUtils'\n\n// Actions\nactions.columnStartResizing = 'columnStartResizing'\nactions.columnResizing = 'columnResizing'\nactions.columnDoneResizing = 'columnDoneResizing'\nactions.resetResize = 'resetResize'\n\nexport function useGridLayout(hooks) {\n hooks.stateReducers.push(reducer)\n hooks.getTableProps.push(getTableProps)\n hooks.getHeaderProps.push(getHeaderProps)\n hooks.getRowProps.push(getRowProps)\n}\n\nuseGridLayout.pluginName = 'useGridLayout'\n\nconst getTableProps = (props, { instance }) => {\n const gridTemplateColumns = instance.visibleColumns.map(column => {\n if (instance.state.gridLayout.columnWidths[column.id])\n return `${instance.state.gridLayout.columnWidths[column.id]}px`\n // When resizing, lock the width of all unset columns\n // instead of using user-provided width or defaultColumn width,\n // which could potentially be 'auto' or 'fr' units that don't scale linearly\n if (instance.state.columnResizing?.isResizingColumn)\n return `${instance.state.gridLayout.startWidths[column.id]}px`\n if (typeof column.width === 'number') return `${column.width}px`\n return column.width\n })\n return [\n props,\n {\n style: {\n display: `grid`,\n gridTemplateColumns: gridTemplateColumns.join(` `),\n },\n },\n ]\n}\n\nconst getHeaderProps = (props, { column }) => [\n props,\n {\n id: `header-cell-${column.id}`,\n style: {\n position: `sticky`, //enables a scroll wrapper to be placed around the table and have sticky headers\n gridColumn: `span ${column.totalVisibleHeaderCount}`,\n },\n },\n]\n\nconst getRowProps = (props, { row }) => {\n if (row.isExpanded) {\n return [\n props,\n {\n style: {\n gridColumn: `1 / ${row.cells.length + 1}`,\n },\n },\n ]\n }\n return [props, {}]\n}\n\nfunction reducer(state, action, previousState, instance) {\n if (action.type === actions.init) {\n return {\n gridLayout: {\n columnWidths: {},\n },\n ...state,\n }\n }\n\n if (action.type === actions.resetResize) {\n return {\n ...state,\n gridLayout: {\n columnWidths: {},\n },\n }\n }\n\n if (action.type === actions.columnStartResizing) {\n const { columnId, headerIdWidths } = action\n const columnWidth = getElementWidth(columnId)\n\n if (columnWidth !== undefined) {\n const startWidths = instance.visibleColumns.reduce(\n (acc, column) => ({\n ...acc,\n [column.id]: getElementWidth(column.id),\n }),\n {}\n )\n const minWidths = instance.visibleColumns.reduce(\n (acc, column) => ({\n ...acc,\n [column.id]: column.minWidth,\n }),\n {}\n )\n const maxWidths = instance.visibleColumns.reduce(\n (acc, column) => ({\n ...acc,\n [column.id]: column.maxWidth,\n }),\n {}\n )\n\n const headerIdGridWidths = headerIdWidths.map(([headerId]) => [\n headerId,\n getElementWidth(headerId),\n ])\n\n return {\n ...state,\n gridLayout: {\n ...state.gridLayout,\n startWidths,\n minWidths,\n maxWidths,\n headerIdGridWidths,\n columnWidth,\n },\n }\n } else {\n return state\n }\n }\n\n if (action.type === actions.columnResizing) {\n const { clientX } = action\n const { startX } = state.columnResizing\n const {\n columnWidth,\n minWidths,\n maxWidths,\n headerIdGridWidths = [],\n } = state.gridLayout\n\n const deltaX = clientX - startX\n const percentageDeltaX = deltaX / columnWidth\n\n const newColumnWidths = {}\n\n headerIdGridWidths.forEach(([headerId, headerWidth]) => {\n newColumnWidths[headerId] = Math.min(\n Math.max(\n minWidths[headerId],\n headerWidth + headerWidth * percentageDeltaX\n ),\n maxWidths[headerId]\n )\n })\n\n return {\n ...state,\n gridLayout: {\n ...state.gridLayout,\n columnWidths: {\n ...state.gridLayout.columnWidths,\n ...newColumnWidths,\n },\n },\n }\n }\n\n if (action.type === actions.columnDoneResizing) {\n return {\n ...state,\n gridLayout: {\n ...state.gridLayout,\n startWidths: {},\n minWidths: {},\n maxWidths: {},\n },\n }\n }\n}\n\nfunction getElementWidth(columnId) {\n const width = document.getElementById(`header-cell-${columnId}`)?.offsetWidth\n\n if (width !== undefined) {\n return width\n }\n}\n","'use strict';\n\nvar keysShim;\nif (!Object.keys) {\n\t// modified from https://github.com/es-shims/es5-shim\n\tvar has = Object.prototype.hasOwnProperty;\n\tvar toStr = Object.prototype.toString;\n\tvar isArgs = require('./isArguments'); // eslint-disable-line global-require\n\tvar isEnumerable = Object.prototype.propertyIsEnumerable;\n\tvar hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString');\n\tvar hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype');\n\tvar dontEnums = [\n\t\t'toString',\n\t\t'toLocaleString',\n\t\t'valueOf',\n\t\t'hasOwnProperty',\n\t\t'isPrototypeOf',\n\t\t'propertyIsEnumerable',\n\t\t'constructor'\n\t];\n\tvar equalsConstructorPrototype = function (o) {\n\t\tvar ctor = o.constructor;\n\t\treturn ctor && ctor.prototype === o;\n\t};\n\tvar excludedKeys = {\n\t\t$applicationCache: true,\n\t\t$console: true,\n\t\t$external: true,\n\t\t$frame: true,\n\t\t$frameElement: true,\n\t\t$frames: true,\n\t\t$innerHeight: true,\n\t\t$innerWidth: true,\n\t\t$onmozfullscreenchange: true,\n\t\t$onmozfullscreenerror: true,\n\t\t$outerHeight: true,\n\t\t$outerWidth: true,\n\t\t$pageXOffset: true,\n\t\t$pageYOffset: true,\n\t\t$parent: true,\n\t\t$scrollLeft: true,\n\t\t$scrollTop: true,\n\t\t$scrollX: true,\n\t\t$scrollY: true,\n\t\t$self: true,\n\t\t$webkitIndexedDB: true,\n\t\t$webkitStorageInfo: true,\n\t\t$window: true\n\t};\n\tvar hasAutomationEqualityBug = (function () {\n\t\t/* global window */\n\t\tif (typeof window === 'undefined') { return false; }\n\t\tfor (var k in window) {\n\t\t\ttry {\n\t\t\t\tif (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tequalsConstructorPrototype(window[k]);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}());\n\tvar equalsConstructorPrototypeIfNotBuggy = function (o) {\n\t\t/* global window */\n\t\tif (typeof window === 'undefined' || !hasAutomationEqualityBug) {\n\t\t\treturn equalsConstructorPrototype(o);\n\t\t}\n\t\ttry {\n\t\t\treturn equalsConstructorPrototype(o);\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tkeysShim = function keys(object) {\n\t\tvar isObject = object !== null && typeof object === 'object';\n\t\tvar isFunction = toStr.call(object) === '[object Function]';\n\t\tvar isArguments = isArgs(object);\n\t\tvar isString = isObject && toStr.call(object) === '[object String]';\n\t\tvar theKeys = [];\n\n\t\tif (!isObject && !isFunction && !isArguments) {\n\t\t\tthrow new TypeError('Object.keys called on a non-object');\n\t\t}\n\n\t\tvar skipProto = hasProtoEnumBug && isFunction;\n\t\tif (isString && object.length > 0 && !has.call(object, 0)) {\n\t\t\tfor (var i = 0; i < object.length; ++i) {\n\t\t\t\ttheKeys.push(String(i));\n\t\t\t}\n\t\t}\n\n\t\tif (isArguments && object.length > 0) {\n\t\t\tfor (var j = 0; j < object.length; ++j) {\n\t\t\t\ttheKeys.push(String(j));\n\t\t\t}\n\t\t} else {\n\t\t\tfor (var name in object) {\n\t\t\t\tif (!(skipProto && name === 'prototype') && has.call(object, name)) {\n\t\t\t\t\ttheKeys.push(String(name));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (hasDontEnumBug) {\n\t\t\tvar skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);\n\n\t\t\tfor (var k = 0; k < dontEnums.length; ++k) {\n\t\t\t\tif (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) {\n\t\t\t\t\ttheKeys.push(dontEnums[k]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn theKeys;\n\t};\n}\nmodule.exports = keysShim;\n","'use strict';\n\nvar hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';\nvar toStr = Object.prototype.toString;\n\nvar isStandardArguments = function isArguments(value) {\n\tif (hasToStringTag && value && typeof value === 'object' && Symbol.toStringTag in value) {\n\t\treturn false;\n\t}\n\treturn toStr.call(value) === '[object Arguments]';\n};\n\nvar isLegacyArguments = function isArguments(value) {\n\tif (isStandardArguments(value)) {\n\t\treturn true;\n\t}\n\treturn value !== null &&\n\t\ttypeof value === 'object' &&\n\t\ttypeof value.length === 'number' &&\n\t\tvalue.length >= 0 &&\n\t\ttoStr.call(value) !== '[object Array]' &&\n\t\ttoStr.call(value.callee) === '[object Function]';\n};\n\nvar supportsStandardArguments = (function () {\n\treturn isStandardArguments(arguments);\n}());\n\nisStandardArguments.isLegacyArguments = isLegacyArguments; // for tests\n\nmodule.exports = supportsStandardArguments ? isStandardArguments : isLegacyArguments;\n","\"use strict\";\n\n/* https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.is */\n\nvar NumberIsNaN = function (value) {\n\treturn value !== value;\n};\n\nmodule.exports = function is(a, b) {\n\tif (a === 0 && b === 0) {\n\t\treturn 1 / a === 1 / b;\n\t} else if (a === b) {\n\t\treturn true;\n\t} else if (NumberIsNaN(a) && NumberIsNaN(b)) {\n\t\treturn true;\n\t}\n\treturn false;\n};\n\n","'use strict';\n\nvar has = require('has');\nvar regexExec = RegExp.prototype.exec;\nvar gOPD = Object.getOwnPropertyDescriptor;\n\nvar tryRegexExecCall = function tryRegexExec(value) {\n\ttry {\n\t\tvar lastIndex = value.lastIndex;\n\t\tvalue.lastIndex = 0;\n\n\t\tregexExec.call(value);\n\t\treturn true;\n\t} catch (e) {\n\t\treturn false;\n\t} finally {\n\t\tvalue.lastIndex = lastIndex;\n\t}\n};\nvar toStr = Object.prototype.toString;\nvar regexClass = '[object RegExp]';\nvar hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';\n\nmodule.exports = function isRegex(value) {\n\tif (!value || typeof value !== 'object') {\n\t\treturn false;\n\t}\n\tif (!hasToStringTag) {\n\t\treturn toStr.call(value) === regexClass;\n\t}\n\n\tvar descriptor = gOPD(value, 'lastIndex');\n\tvar hasLastIndexDataProperty = descriptor && has(descriptor, 'value');\n\tif (!hasLastIndexDataProperty) {\n\t\treturn false;\n\t}\n\n\treturn tryRegexExecCall(value);\n};\n","'use strict';\n\n/* eslint no-invalid-this: 1 */\n\nvar ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';\nvar slice = Array.prototype.slice;\nvar toStr = Object.prototype.toString;\nvar funcType = '[object Function]';\n\nmodule.exports = function bind(that) {\n var target = this;\n if (typeof target !== 'function' || toStr.call(target) !== funcType) {\n throw new TypeError(ERROR_MESSAGE + target);\n }\n var args = slice.call(arguments, 1);\n\n var bound;\n var binder = function () {\n if (this instanceof bound) {\n var result = target.apply(\n this,\n args.concat(slice.call(arguments))\n );\n if (Object(result) === result) {\n return result;\n }\n return this;\n } else {\n return target.apply(\n that,\n args.concat(slice.call(arguments))\n );\n }\n };\n\n var boundLength = Math.max(0, target.length - args.length);\n var boundArgs = [];\n for (var i = 0; i < boundLength; i++) {\n boundArgs.push('$' + i);\n }\n\n bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);\n\n if (target.prototype) {\n var Empty = function Empty() {};\n Empty.prototype = target.prototype;\n bound.prototype = new Empty();\n Empty.prototype = null;\n }\n\n return bound;\n};\n","'use strict';\n\nvar define = require('define-properties');\n\nvar implementation = require('./implementation');\nvar getPolyfill = require('./polyfill');\nvar shim = require('./shim');\n\nvar flagsBound = Function.call.bind(implementation);\n\ndefine(flagsBound, {\n\tgetPolyfill: getPolyfill,\n\timplementation: implementation,\n\tshim: shim\n});\n\nmodule.exports = flagsBound;\n","'use strict';\n\nvar supportsDescriptors = require('define-properties').supportsDescriptors;\nvar getPolyfill = require('./polyfill');\nvar gOPD = Object.getOwnPropertyDescriptor;\nvar defineProperty = Object.defineProperty;\nvar TypeErr = TypeError;\nvar getProto = Object.getPrototypeOf;\nvar regex = /a/;\n\nmodule.exports = function shimFlags() {\n\tif (!supportsDescriptors || !getProto) {\n\t\tthrow new TypeErr('RegExp.prototype.flags requires a true ES5 environment that supports property descriptors');\n\t}\n\tvar polyfill = getPolyfill();\n\tvar proto = getProto(regex);\n\tvar descriptor = gOPD(proto, 'flags');\n\tif (!descriptor || descriptor.get !== polyfill) {\n\t\tdefineProperty(proto, 'flags', {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: false,\n\t\t\tget: polyfill\n\t\t});\n\t}\n\treturn polyfill;\n};\n","'use strict';\n\nvar getDay = Date.prototype.getDay;\nvar tryDateObject = function tryDateObject(value) {\n\ttry {\n\t\tgetDay.call(value);\n\t\treturn true;\n\t} catch (e) {\n\t\treturn false;\n\t}\n};\n\nvar toStr = Object.prototype.toString;\nvar dateClass = '[object Date]';\nvar hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';\n\nmodule.exports = function isDateObject(value) {\n\tif (typeof value !== 'object' || value === null) { return false; }\n\treturn hasToStringTag ? tryDateObject(value) : toStr.call(value) === dateClass;\n};\n","'use strict';\n\nvar getSideChannel = require('side-channel');\nvar utils = require('./utils');\nvar formats = require('./formats');\nvar has = Object.prototype.hasOwnProperty;\n\nvar arrayPrefixGenerators = {\n brackets: function brackets(prefix) {\n return prefix + '[]';\n },\n comma: 'comma',\n indices: function indices(prefix, key) {\n return prefix + '[' + key + ']';\n },\n repeat: function repeat(prefix) {\n return prefix;\n }\n};\n\nvar isArray = Array.isArray;\nvar split = String.prototype.split;\nvar push = Array.prototype.push;\nvar pushToArray = function (arr, valueOrArray) {\n push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);\n};\n\nvar toISO = Date.prototype.toISOString;\n\nvar defaultFormat = formats['default'];\nvar defaults = {\n addQueryPrefix: false,\n allowDots: false,\n charset: 'utf-8',\n charsetSentinel: false,\n delimiter: '&',\n encode: true,\n encoder: utils.encode,\n encodeValuesOnly: false,\n format: defaultFormat,\n formatter: formats.formatters[defaultFormat],\n // deprecated\n indices: false,\n serializeDate: function serializeDate(date) {\n return toISO.call(date);\n },\n skipNulls: false,\n strictNullHandling: false\n};\n\nvar isNonNullishPrimitive = function isNonNullishPrimitive(v) {\n return typeof v === 'string'\n || typeof v === 'number'\n || typeof v === 'boolean'\n || typeof v === 'symbol'\n || typeof v === 'bigint';\n};\n\nvar sentinel = {};\n\nvar stringify = function stringify(\n object,\n prefix,\n generateArrayPrefix,\n strictNullHandling,\n skipNulls,\n encoder,\n filter,\n sort,\n allowDots,\n serializeDate,\n format,\n formatter,\n encodeValuesOnly,\n charset,\n sideChannel\n) {\n var obj = object;\n\n var tmpSc = sideChannel;\n var step = 0;\n var findFlag = false;\n while ((tmpSc = tmpSc.get(sentinel)) !== void undefined && !findFlag) {\n // Where object last appeared in the ref tree\n var pos = tmpSc.get(object);\n step += 1;\n if (typeof pos !== 'undefined') {\n if (pos === step) {\n throw new RangeError('Cyclic object value');\n } else {\n findFlag = true; // Break while\n }\n }\n if (typeof tmpSc.get(sentinel) === 'undefined') {\n step = 0;\n }\n }\n\n if (typeof filter === 'function') {\n obj = filter(prefix, obj);\n } else if (obj instanceof Date) {\n obj = serializeDate(obj);\n } else if (generateArrayPrefix === 'comma' && isArray(obj)) {\n obj = utils.maybeMap(obj, function (value) {\n if (value instanceof Date) {\n return serializeDate(value);\n }\n return value;\n });\n }\n\n if (obj === null) {\n if (strictNullHandling) {\n return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix;\n }\n\n obj = '';\n }\n\n if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {\n if (encoder) {\n var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key', format);\n if (generateArrayPrefix === 'comma' && encodeValuesOnly) {\n var valuesArray = split.call(String(obj), ',');\n var valuesJoined = '';\n for (var i = 0; i < valuesArray.length; ++i) {\n valuesJoined += (i === 0 ? '' : ',') + formatter(encoder(valuesArray[i], defaults.encoder, charset, 'value', format));\n }\n return [formatter(keyValue) + (isArray(obj) && valuesArray.length === 1 ? '[]' : '') + '=' + valuesJoined];\n }\n return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value', format))];\n }\n return [formatter(prefix) + '=' + formatter(String(obj))];\n }\n\n var values = [];\n\n if (typeof obj === 'undefined') {\n return values;\n }\n\n var objKeys;\n if (generateArrayPrefix === 'comma' && isArray(obj)) {\n // we need to join elements in\n objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];\n } else if (isArray(filter)) {\n objKeys = filter;\n } else {\n var keys = Object.keys(obj);\n objKeys = sort ? keys.sort(sort) : keys;\n }\n\n var adjustedPrefix = generateArrayPrefix === 'comma' && isArray(obj) && obj.length === 1 ? prefix + '[]' : prefix;\n\n for (var j = 0; j < objKeys.length; ++j) {\n var key = objKeys[j];\n var value = typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key];\n\n if (skipNulls && value === null) {\n continue;\n }\n\n var keyPrefix = isArray(obj)\n ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(adjustedPrefix, key) : adjustedPrefix\n : adjustedPrefix + (allowDots ? '.' + key : '[' + key + ']');\n\n sideChannel.set(object, step);\n var valueSideChannel = getSideChannel();\n valueSideChannel.set(sentinel, sideChannel);\n pushToArray(values, stringify(\n value,\n keyPrefix,\n generateArrayPrefix,\n strictNullHandling,\n skipNulls,\n encoder,\n filter,\n sort,\n allowDots,\n serializeDate,\n format,\n formatter,\n encodeValuesOnly,\n charset,\n valueSideChannel\n ));\n }\n\n return values;\n};\n\nvar normalizeStringifyOptions = function normalizeStringifyOptions(opts) {\n if (!opts) {\n return defaults;\n }\n\n if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {\n throw new TypeError('Encoder has to be a function.');\n }\n\n var charset = opts.charset || defaults.charset;\n if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {\n throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');\n }\n\n var format = formats['default'];\n if (typeof opts.format !== 'undefined') {\n if (!has.call(formats.formatters, opts.format)) {\n throw new TypeError('Unknown format option provided.');\n }\n format = opts.format;\n }\n var formatter = formats.formatters[format];\n\n var filter = defaults.filter;\n if (typeof opts.filter === 'function' || isArray(opts.filter)) {\n filter = opts.filter;\n }\n\n return {\n addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix,\n allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,\n charset: charset,\n charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,\n delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter,\n encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode,\n encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,\n encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,\n filter: filter,\n format: format,\n formatter: formatter,\n serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,\n skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,\n sort: typeof opts.sort === 'function' ? opts.sort : null,\n strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling\n };\n};\n\nmodule.exports = function (object, opts) {\n var obj = object;\n var options = normalizeStringifyOptions(opts);\n\n var objKeys;\n var filter;\n\n if (typeof options.filter === 'function') {\n filter = options.filter;\n obj = filter('', obj);\n } else if (isArray(options.filter)) {\n filter = options.filter;\n objKeys = filter;\n }\n\n var keys = [];\n\n if (typeof obj !== 'object' || obj === null) {\n return '';\n }\n\n var arrayFormat;\n if (opts && opts.arrayFormat in arrayPrefixGenerators) {\n arrayFormat = opts.arrayFormat;\n } else if (opts && 'indices' in opts) {\n arrayFormat = opts.indices ? 'indices' : 'repeat';\n } else {\n arrayFormat = 'indices';\n }\n\n var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];\n\n if (!objKeys) {\n objKeys = Object.keys(obj);\n }\n\n if (options.sort) {\n objKeys.sort(options.sort);\n }\n\n var sideChannel = getSideChannel();\n for (var i = 0; i < objKeys.length; ++i) {\n var key = objKeys[i];\n\n if (options.skipNulls && obj[key] === null) {\n continue;\n }\n pushToArray(keys, stringify(\n obj[key],\n key,\n generateArrayPrefix,\n options.strictNullHandling,\n options.skipNulls,\n options.encode ? options.encoder : null,\n options.filter,\n options.sort,\n options.allowDots,\n options.serializeDate,\n options.format,\n options.formatter,\n options.encodeValuesOnly,\n options.charset,\n sideChannel\n ));\n }\n\n var joined = keys.join(options.delimiter);\n var prefix = options.addQueryPrefix === true ? '?' : '';\n\n if (options.charsetSentinel) {\n if (options.charset === 'iso-8859-1') {\n // encodeURIComponent('✓'), the \"numeric entity\" representation of a checkmark\n prefix += 'utf8=%26%2310003%3B&';\n } else {\n // encodeURIComponent('✓')\n prefix += 'utf8=%E2%9C%93&';\n }\n }\n\n return joined.length > 0 ? prefix + joined : '';\n};\n","'use strict';\n\nvar GetIntrinsic = require('get-intrinsic');\nvar callBound = require('call-bind/callBound');\nvar inspect = require('object-inspect');\n\nvar $TypeError = GetIntrinsic('%TypeError%');\nvar $WeakMap = GetIntrinsic('%WeakMap%', true);\nvar $Map = GetIntrinsic('%Map%', true);\n\nvar $weakMapGet = callBound('WeakMap.prototype.get', true);\nvar $weakMapSet = callBound('WeakMap.prototype.set', true);\nvar $weakMapHas = callBound('WeakMap.prototype.has', true);\nvar $mapGet = callBound('Map.prototype.get', true);\nvar $mapSet = callBound('Map.prototype.set', true);\nvar $mapHas = callBound('Map.prototype.has', true);\n\n/*\n * This function traverses the list returning the node corresponding to the\n * given key.\n *\n * That node is also moved to the head of the list, so that if it's accessed\n * again we don't need to traverse the whole list. By doing so, all the recently\n * used nodes can be accessed relatively quickly.\n */\nvar listGetNode = function (list, key) { // eslint-disable-line consistent-return\n\tfor (var prev = list, curr; (curr = prev.next) !== null; prev = curr) {\n\t\tif (curr.key === key) {\n\t\t\tprev.next = curr.next;\n\t\t\tcurr.next = list.next;\n\t\t\tlist.next = curr; // eslint-disable-line no-param-reassign\n\t\t\treturn curr;\n\t\t}\n\t}\n};\n\nvar listGet = function (objects, key) {\n\tvar node = listGetNode(objects, key);\n\treturn node && node.value;\n};\nvar listSet = function (objects, key, value) {\n\tvar node = listGetNode(objects, key);\n\tif (node) {\n\t\tnode.value = value;\n\t} else {\n\t\t// Prepend the new node to the beginning of the list\n\t\tobjects.next = { // eslint-disable-line no-param-reassign\n\t\t\tkey: key,\n\t\t\tnext: objects.next,\n\t\t\tvalue: value\n\t\t};\n\t}\n};\nvar listHas = function (objects, key) {\n\treturn !!listGetNode(objects, key);\n};\n\nmodule.exports = function getSideChannel() {\n\tvar $wm;\n\tvar $m;\n\tvar $o;\n\tvar channel = {\n\t\tassert: function (key) {\n\t\t\tif (!channel.has(key)) {\n\t\t\t\tthrow new $TypeError('Side channel does not contain ' + inspect(key));\n\t\t\t}\n\t\t},\n\t\tget: function (key) { // eslint-disable-line consistent-return\n\t\t\tif ($WeakMap && key && (typeof key === 'object' || typeof key === 'function')) {\n\t\t\t\tif ($wm) {\n\t\t\t\t\treturn $weakMapGet($wm, key);\n\t\t\t\t}\n\t\t\t} else if ($Map) {\n\t\t\t\tif ($m) {\n\t\t\t\t\treturn $mapGet($m, key);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif ($o) { // eslint-disable-line no-lonely-if\n\t\t\t\t\treturn listGet($o, key);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\thas: function (key) {\n\t\t\tif ($WeakMap && key && (typeof key === 'object' || typeof key === 'function')) {\n\t\t\t\tif ($wm) {\n\t\t\t\t\treturn $weakMapHas($wm, key);\n\t\t\t\t}\n\t\t\t} else if ($Map) {\n\t\t\t\tif ($m) {\n\t\t\t\t\treturn $mapHas($m, key);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif ($o) { // eslint-disable-line no-lonely-if\n\t\t\t\t\treturn listHas($o, key);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\tset: function (key, value) {\n\t\t\tif ($WeakMap && key && (typeof key === 'object' || typeof key === 'function')) {\n\t\t\t\tif (!$wm) {\n\t\t\t\t\t$wm = new $WeakMap();\n\t\t\t\t}\n\t\t\t\t$weakMapSet($wm, key, value);\n\t\t\t} else if ($Map) {\n\t\t\t\tif (!$m) {\n\t\t\t\t\t$m = new $Map();\n\t\t\t\t}\n\t\t\t\t$mapSet($m, key, value);\n\t\t\t} else {\n\t\t\t\tif (!$o) {\n\t\t\t\t\t/*\n\t\t\t\t\t * Initialize the linked list as an empty node, so that we don't have\n\t\t\t\t\t * to special-case handling of the first node: we can always refer to\n\t\t\t\t\t * it as (previous node).next, instead of something like (list).head\n\t\t\t\t\t */\n\t\t\t\t\t$o = { key: {}, next: null };\n\t\t\t\t}\n\t\t\t\tlistSet($o, key, value);\n\t\t\t}\n\t\t}\n\t};\n\treturn channel;\n};\n","'use strict';\n\nvar origSymbol = typeof Symbol !== 'undefined' && Symbol;\nvar hasSymbolSham = require('./shams');\n\nmodule.exports = function hasNativeSymbols() {\n\tif (typeof origSymbol !== 'function') { return false; }\n\tif (typeof Symbol !== 'function') { return false; }\n\tif (typeof origSymbol('foo') !== 'symbol') { return false; }\n\tif (typeof Symbol('bar') !== 'symbol') { return false; }\n\n\treturn hasSymbolSham();\n};\n","'use strict';\n\n/* eslint complexity: [2, 18], max-statements: [2, 33] */\nmodule.exports = function hasSymbols() {\n\tif (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }\n\tif (typeof Symbol.iterator === 'symbol') { return true; }\n\n\tvar obj = {};\n\tvar sym = Symbol('test');\n\tvar symObj = Object(sym);\n\tif (typeof sym === 'string') { return false; }\n\n\tif (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; }\n\tif (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; }\n\n\t// temp disabled per https://github.com/ljharb/object.assign/issues/17\n\t// if (sym instanceof Symbol) { return false; }\n\t// temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4\n\t// if (!(symObj instanceof Symbol)) { return false; }\n\n\t// if (typeof Symbol.prototype.toString !== 'function') { return false; }\n\t// if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; }\n\n\tvar symVal = 42;\n\tobj[sym] = symVal;\n\tfor (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax, no-unreachable-loop\n\tif (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }\n\n\tif (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }\n\n\tvar syms = Object.getOwnPropertySymbols(obj);\n\tif (syms.length !== 1 || syms[0] !== sym) { return false; }\n\n\tif (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }\n\n\tif (typeof Object.getOwnPropertyDescriptor === 'function') {\n\t\tvar descriptor = Object.getOwnPropertyDescriptor(obj, sym);\n\t\tif (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }\n\t}\n\n\treturn true;\n};\n","'use strict';\n\nvar GetIntrinsic = require('get-intrinsic');\n\nvar callBind = require('./');\n\nvar $indexOf = callBind(GetIntrinsic('String.prototype.indexOf'));\n\nmodule.exports = function callBoundIntrinsic(name, allowMissing) {\n\tvar intrinsic = GetIntrinsic(name, !!allowMissing);\n\tif (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) {\n\t\treturn callBind(intrinsic);\n\t}\n\treturn intrinsic;\n};\n","'use strict';\n\nvar bind = require('function-bind');\nvar GetIntrinsic = require('get-intrinsic');\n\nvar $apply = GetIntrinsic('%Function.prototype.apply%');\nvar $call = GetIntrinsic('%Function.prototype.call%');\nvar $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply);\n\nvar $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true);\nvar $defineProperty = GetIntrinsic('%Object.defineProperty%', true);\nvar $max = GetIntrinsic('%Math.max%');\n\nif ($defineProperty) {\n\ttry {\n\t\t$defineProperty({}, 'a', { value: 1 });\n\t} catch (e) {\n\t\t// IE 8 has a broken defineProperty\n\t\t$defineProperty = null;\n\t}\n}\n\nmodule.exports = function callBind(originalFunction) {\n\tvar func = $reflectApply(bind, $call, arguments);\n\tif ($gOPD && $defineProperty) {\n\t\tvar desc = $gOPD(func, 'length');\n\t\tif (desc.configurable) {\n\t\t\t// original length, plus the receiver, minus any additional arguments (after the receiver)\n\t\t\t$defineProperty(\n\t\t\t\tfunc,\n\t\t\t\t'length',\n\t\t\t\t{ value: 1 + $max(0, originalFunction.length - (arguments.length - 1)) }\n\t\t\t);\n\t\t}\n\t}\n\treturn func;\n};\n\nvar applyBind = function applyBind() {\n\treturn $reflectApply(bind, $apply, arguments);\n};\n\nif ($defineProperty) {\n\t$defineProperty(module.exports, 'apply', { value: applyBind });\n} else {\n\tmodule.exports.apply = applyBind;\n}\n","var hasMap = typeof Map === 'function' && Map.prototype;\nvar mapSizeDescriptor = Object.getOwnPropertyDescriptor && hasMap ? Object.getOwnPropertyDescriptor(Map.prototype, 'size') : null;\nvar mapSize = hasMap && mapSizeDescriptor && typeof mapSizeDescriptor.get === 'function' ? mapSizeDescriptor.get : null;\nvar mapForEach = hasMap && Map.prototype.forEach;\nvar hasSet = typeof Set === 'function' && Set.prototype;\nvar setSizeDescriptor = Object.getOwnPropertyDescriptor && hasSet ? Object.getOwnPropertyDescriptor(Set.prototype, 'size') : null;\nvar setSize = hasSet && setSizeDescriptor && typeof setSizeDescriptor.get === 'function' ? setSizeDescriptor.get : null;\nvar setForEach = hasSet && Set.prototype.forEach;\nvar hasWeakMap = typeof WeakMap === 'function' && WeakMap.prototype;\nvar weakMapHas = hasWeakMap ? WeakMap.prototype.has : null;\nvar hasWeakSet = typeof WeakSet === 'function' && WeakSet.prototype;\nvar weakSetHas = hasWeakSet ? WeakSet.prototype.has : null;\nvar hasWeakRef = typeof WeakRef === 'function' && WeakRef.prototype;\nvar weakRefDeref = hasWeakRef ? WeakRef.prototype.deref : null;\nvar booleanValueOf = Boolean.prototype.valueOf;\nvar objectToString = Object.prototype.toString;\nvar functionToString = Function.prototype.toString;\nvar $match = String.prototype.match;\nvar $slice = String.prototype.slice;\nvar $replace = String.prototype.replace;\nvar $toUpperCase = String.prototype.toUpperCase;\nvar $toLowerCase = String.prototype.toLowerCase;\nvar $test = RegExp.prototype.test;\nvar $concat = Array.prototype.concat;\nvar $join = Array.prototype.join;\nvar $arrSlice = Array.prototype.slice;\nvar $floor = Math.floor;\nvar bigIntValueOf = typeof BigInt === 'function' ? BigInt.prototype.valueOf : null;\nvar gOPS = Object.getOwnPropertySymbols;\nvar symToString = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? Symbol.prototype.toString : null;\nvar hasShammedSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'object';\n// ie, `has-tostringtag/shams\nvar toStringTag = typeof Symbol === 'function' && Symbol.toStringTag && (typeof Symbol.toStringTag === hasShammedSymbols ? 'object' : 'symbol')\n ? Symbol.toStringTag\n : null;\nvar isEnumerable = Object.prototype.propertyIsEnumerable;\n\nvar gPO = (typeof Reflect === 'function' ? Reflect.getPrototypeOf : Object.getPrototypeOf) || (\n [].__proto__ === Array.prototype // eslint-disable-line no-proto\n ? function (O) {\n return O.__proto__; // eslint-disable-line no-proto\n }\n : null\n);\n\nfunction addNumericSeparator(num, str) {\n if (\n num === Infinity\n || num === -Infinity\n || num !== num\n || (num && num > -1000 && num < 1000)\n || $test.call(/e/, str)\n ) {\n return str;\n }\n var sepRegex = /[0-9](?=(?:[0-9]{3})+(?![0-9]))/g;\n if (typeof num === 'number') {\n var int = num < 0 ? -$floor(-num) : $floor(num); // trunc(num)\n if (int !== num) {\n var intStr = String(int);\n var dec = $slice.call(str, intStr.length + 1);\n return $replace.call(intStr, sepRegex, '$&_') + '.' + $replace.call($replace.call(dec, /([0-9]{3})/g, '$&_'), /_$/, '');\n }\n }\n return $replace.call(str, sepRegex, '$&_');\n}\n\nvar utilInspect = require('./util.inspect');\nvar inspectCustom = utilInspect.custom;\nvar inspectSymbol = isSymbol(inspectCustom) ? inspectCustom : null;\n\nmodule.exports = function inspect_(obj, options, depth, seen) {\n var opts = options || {};\n\n if (has(opts, 'quoteStyle') && (opts.quoteStyle !== 'single' && opts.quoteStyle !== 'double')) {\n throw new TypeError('option \"quoteStyle\" must be \"single\" or \"double\"');\n }\n if (\n has(opts, 'maxStringLength') && (typeof opts.maxStringLength === 'number'\n ? opts.maxStringLength < 0 && opts.maxStringLength !== Infinity\n : opts.maxStringLength !== null\n )\n ) {\n throw new TypeError('option \"maxStringLength\", if provided, must be a positive integer, Infinity, or `null`');\n }\n var customInspect = has(opts, 'customInspect') ? opts.customInspect : true;\n if (typeof customInspect !== 'boolean' && customInspect !== 'symbol') {\n throw new TypeError('option \"customInspect\", if provided, must be `true`, `false`, or `\\'symbol\\'`');\n }\n\n if (\n has(opts, 'indent')\n && opts.indent !== null\n && opts.indent !== '\\t'\n && !(parseInt(opts.indent, 10) === opts.indent && opts.indent > 0)\n ) {\n throw new TypeError('option \"indent\" must be \"\\\\t\", an integer > 0, or `null`');\n }\n if (has(opts, 'numericSeparator') && typeof opts.numericSeparator !== 'boolean') {\n throw new TypeError('option \"numericSeparator\", if provided, must be `true` or `false`');\n }\n var numericSeparator = opts.numericSeparator;\n\n if (typeof obj === 'undefined') {\n return 'undefined';\n }\n if (obj === null) {\n return 'null';\n }\n if (typeof obj === 'boolean') {\n return obj ? 'true' : 'false';\n }\n\n if (typeof obj === 'string') {\n return inspectString(obj, opts);\n }\n if (typeof obj === 'number') {\n if (obj === 0) {\n return Infinity / obj > 0 ? '0' : '-0';\n }\n var str = String(obj);\n return numericSeparator ? addNumericSeparator(obj, str) : str;\n }\n if (typeof obj === 'bigint') {\n var bigIntStr = String(obj) + 'n';\n return numericSeparator ? addNumericSeparator(obj, bigIntStr) : bigIntStr;\n }\n\n var maxDepth = typeof opts.depth === 'undefined' ? 5 : opts.depth;\n if (typeof depth === 'undefined') { depth = 0; }\n if (depth >= maxDepth && maxDepth > 0 && typeof obj === 'object') {\n return isArray(obj) ? '[Array]' : '[Object]';\n }\n\n var indent = getIndent(opts, depth);\n\n if (typeof seen === 'undefined') {\n seen = [];\n } else if (indexOf(seen, obj) >= 0) {\n return '[Circular]';\n }\n\n function inspect(value, from, noIndent) {\n if (from) {\n seen = $arrSlice.call(seen);\n seen.push(from);\n }\n if (noIndent) {\n var newOpts = {\n depth: opts.depth\n };\n if (has(opts, 'quoteStyle')) {\n newOpts.quoteStyle = opts.quoteStyle;\n }\n return inspect_(value, newOpts, depth + 1, seen);\n }\n return inspect_(value, opts, depth + 1, seen);\n }\n\n if (typeof obj === 'function' && !isRegExp(obj)) { // in older engines, regexes are callable\n var name = nameOf(obj);\n var keys = arrObjKeys(obj, inspect);\n return '[Function' + (name ? ': ' + name : ' (anonymous)') + ']' + (keys.length > 0 ? ' { ' + $join.call(keys, ', ') + ' }' : '');\n }\n if (isSymbol(obj)) {\n var symString = hasShammedSymbols ? $replace.call(String(obj), /^(Symbol\\(.*\\))_[^)]*$/, '$1') : symToString.call(obj);\n return typeof obj === 'object' && !hasShammedSymbols ? markBoxed(symString) : symString;\n }\n if (isElement(obj)) {\n var s = '<' + $toLowerCase.call(String(obj.nodeName));\n var attrs = obj.attributes || [];\n for (var i = 0; i < attrs.length; i++) {\n s += ' ' + attrs[i].name + '=' + wrapQuotes(quote(attrs[i].value), 'double', opts);\n }\n s += '>';\n if (obj.childNodes && obj.childNodes.length) { s += '...'; }\n s += '';\n return s;\n }\n if (isArray(obj)) {\n if (obj.length === 0) { return '[]'; }\n var xs = arrObjKeys(obj, inspect);\n if (indent && !singleLineValues(xs)) {\n return '[' + indentedJoin(xs, indent) + ']';\n }\n return '[ ' + $join.call(xs, ', ') + ' ]';\n }\n if (isError(obj)) {\n var parts = arrObjKeys(obj, inspect);\n if (!('cause' in Error.prototype) && 'cause' in obj && !isEnumerable.call(obj, 'cause')) {\n return '{ [' + String(obj) + '] ' + $join.call($concat.call('[cause]: ' + inspect(obj.cause), parts), ', ') + ' }';\n }\n if (parts.length === 0) { return '[' + String(obj) + ']'; }\n return '{ [' + String(obj) + '] ' + $join.call(parts, ', ') + ' }';\n }\n if (typeof obj === 'object' && customInspect) {\n if (inspectSymbol && typeof obj[inspectSymbol] === 'function' && utilInspect) {\n return utilInspect(obj, { depth: maxDepth - depth });\n } else if (customInspect !== 'symbol' && typeof obj.inspect === 'function') {\n return obj.inspect();\n }\n }\n if (isMap(obj)) {\n var mapParts = [];\n mapForEach.call(obj, function (value, key) {\n mapParts.push(inspect(key, obj, true) + ' => ' + inspect(value, obj));\n });\n return collectionOf('Map', mapSize.call(obj), mapParts, indent);\n }\n if (isSet(obj)) {\n var setParts = [];\n setForEach.call(obj, function (value) {\n setParts.push(inspect(value, obj));\n });\n return collectionOf('Set', setSize.call(obj), setParts, indent);\n }\n if (isWeakMap(obj)) {\n return weakCollectionOf('WeakMap');\n }\n if (isWeakSet(obj)) {\n return weakCollectionOf('WeakSet');\n }\n if (isWeakRef(obj)) {\n return weakCollectionOf('WeakRef');\n }\n if (isNumber(obj)) {\n return markBoxed(inspect(Number(obj)));\n }\n if (isBigInt(obj)) {\n return markBoxed(inspect(bigIntValueOf.call(obj)));\n }\n if (isBoolean(obj)) {\n return markBoxed(booleanValueOf.call(obj));\n }\n if (isString(obj)) {\n return markBoxed(inspect(String(obj)));\n }\n if (!isDate(obj) && !isRegExp(obj)) {\n var ys = arrObjKeys(obj, inspect);\n var isPlainObject = gPO ? gPO(obj) === Object.prototype : obj instanceof Object || obj.constructor === Object;\n var protoTag = obj instanceof Object ? '' : 'null prototype';\n var stringTag = !isPlainObject && toStringTag && Object(obj) === obj && toStringTag in obj ? $slice.call(toStr(obj), 8, -1) : protoTag ? 'Object' : '';\n var constructorTag = isPlainObject || typeof obj.constructor !== 'function' ? '' : obj.constructor.name ? obj.constructor.name + ' ' : '';\n var tag = constructorTag + (stringTag || protoTag ? '[' + $join.call($concat.call([], stringTag || [], protoTag || []), ': ') + '] ' : '');\n if (ys.length === 0) { return tag + '{}'; }\n if (indent) {\n return tag + '{' + indentedJoin(ys, indent) + '}';\n }\n return tag + '{ ' + $join.call(ys, ', ') + ' }';\n }\n return String(obj);\n};\n\nfunction wrapQuotes(s, defaultStyle, opts) {\n var quoteChar = (opts.quoteStyle || defaultStyle) === 'double' ? '\"' : \"'\";\n return quoteChar + s + quoteChar;\n}\n\nfunction quote(s) {\n return $replace.call(String(s), /\"/g, '"');\n}\n\nfunction isArray(obj) { return toStr(obj) === '[object Array]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }\nfunction isDate(obj) { return toStr(obj) === '[object Date]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }\nfunction isRegExp(obj) { return toStr(obj) === '[object RegExp]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }\nfunction isError(obj) { return toStr(obj) === '[object Error]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }\nfunction isString(obj) { return toStr(obj) === '[object String]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }\nfunction isNumber(obj) { return toStr(obj) === '[object Number]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }\nfunction isBoolean(obj) { return toStr(obj) === '[object Boolean]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }\n\n// Symbol and BigInt do have Symbol.toStringTag by spec, so that can't be used to eliminate false positives\nfunction isSymbol(obj) {\n if (hasShammedSymbols) {\n return obj && typeof obj === 'object' && obj instanceof Symbol;\n }\n if (typeof obj === 'symbol') {\n return true;\n }\n if (!obj || typeof obj !== 'object' || !symToString) {\n return false;\n }\n try {\n symToString.call(obj);\n return true;\n } catch (e) {}\n return false;\n}\n\nfunction isBigInt(obj) {\n if (!obj || typeof obj !== 'object' || !bigIntValueOf) {\n return false;\n }\n try {\n bigIntValueOf.call(obj);\n return true;\n } catch (e) {}\n return false;\n}\n\nvar hasOwn = Object.prototype.hasOwnProperty || function (key) { return key in this; };\nfunction has(obj, key) {\n return hasOwn.call(obj, key);\n}\n\nfunction toStr(obj) {\n return objectToString.call(obj);\n}\n\nfunction nameOf(f) {\n if (f.name) { return f.name; }\n var m = $match.call(functionToString.call(f), /^function\\s*([\\w$]+)/);\n if (m) { return m[1]; }\n return null;\n}\n\nfunction indexOf(xs, x) {\n if (xs.indexOf) { return xs.indexOf(x); }\n for (var i = 0, l = xs.length; i < l; i++) {\n if (xs[i] === x) { return i; }\n }\n return -1;\n}\n\nfunction isMap(x) {\n if (!mapSize || !x || typeof x !== 'object') {\n return false;\n }\n try {\n mapSize.call(x);\n try {\n setSize.call(x);\n } catch (s) {\n return true;\n }\n return x instanceof Map; // core-js workaround, pre-v2.5.0\n } catch (e) {}\n return false;\n}\n\nfunction isWeakMap(x) {\n if (!weakMapHas || !x || typeof x !== 'object') {\n return false;\n }\n try {\n weakMapHas.call(x, weakMapHas);\n try {\n weakSetHas.call(x, weakSetHas);\n } catch (s) {\n return true;\n }\n return x instanceof WeakMap; // core-js workaround, pre-v2.5.0\n } catch (e) {}\n return false;\n}\n\nfunction isWeakRef(x) {\n if (!weakRefDeref || !x || typeof x !== 'object') {\n return false;\n }\n try {\n weakRefDeref.call(x);\n return true;\n } catch (e) {}\n return false;\n}\n\nfunction isSet(x) {\n if (!setSize || !x || typeof x !== 'object') {\n return false;\n }\n try {\n setSize.call(x);\n try {\n mapSize.call(x);\n } catch (m) {\n return true;\n }\n return x instanceof Set; // core-js workaround, pre-v2.5.0\n } catch (e) {}\n return false;\n}\n\nfunction isWeakSet(x) {\n if (!weakSetHas || !x || typeof x !== 'object') {\n return false;\n }\n try {\n weakSetHas.call(x, weakSetHas);\n try {\n weakMapHas.call(x, weakMapHas);\n } catch (s) {\n return true;\n }\n return x instanceof WeakSet; // core-js workaround, pre-v2.5.0\n } catch (e) {}\n return false;\n}\n\nfunction isElement(x) {\n if (!x || typeof x !== 'object') { return false; }\n if (typeof HTMLElement !== 'undefined' && x instanceof HTMLElement) {\n return true;\n }\n return typeof x.nodeName === 'string' && typeof x.getAttribute === 'function';\n}\n\nfunction inspectString(str, opts) {\n if (str.length > opts.maxStringLength) {\n var remaining = str.length - opts.maxStringLength;\n var trailer = '... ' + remaining + ' more character' + (remaining > 1 ? 's' : '');\n return inspectString($slice.call(str, 0, opts.maxStringLength), opts) + trailer;\n }\n // eslint-disable-next-line no-control-regex\n var s = $replace.call($replace.call(str, /(['\\\\])/g, '\\\\$1'), /[\\x00-\\x1f]/g, lowbyte);\n return wrapQuotes(s, 'single', opts);\n}\n\nfunction lowbyte(c) {\n var n = c.charCodeAt(0);\n var x = {\n 8: 'b',\n 9: 't',\n 10: 'n',\n 12: 'f',\n 13: 'r'\n }[n];\n if (x) { return '\\\\' + x; }\n return '\\\\x' + (n < 0x10 ? '0' : '') + $toUpperCase.call(n.toString(16));\n}\n\nfunction markBoxed(str) {\n return 'Object(' + str + ')';\n}\n\nfunction weakCollectionOf(type) {\n return type + ' { ? }';\n}\n\nfunction collectionOf(type, size, entries, indent) {\n var joinedEntries = indent ? indentedJoin(entries, indent) : $join.call(entries, ', ');\n return type + ' (' + size + ') {' + joinedEntries + '}';\n}\n\nfunction singleLineValues(xs) {\n for (var i = 0; i < xs.length; i++) {\n if (indexOf(xs[i], '\\n') >= 0) {\n return false;\n }\n }\n return true;\n}\n\nfunction getIndent(opts, depth) {\n var baseIndent;\n if (opts.indent === '\\t') {\n baseIndent = '\\t';\n } else if (typeof opts.indent === 'number' && opts.indent > 0) {\n baseIndent = $join.call(Array(opts.indent + 1), ' ');\n } else {\n return null;\n }\n return {\n base: baseIndent,\n prev: $join.call(Array(depth + 1), baseIndent)\n };\n}\n\nfunction indentedJoin(xs, indent) {\n if (xs.length === 0) { return ''; }\n var lineJoiner = '\\n' + indent.prev + indent.base;\n return lineJoiner + $join.call(xs, ',' + lineJoiner) + '\\n' + indent.prev;\n}\n\nfunction arrObjKeys(obj, inspect) {\n var isArr = isArray(obj);\n var xs = [];\n if (isArr) {\n xs.length = obj.length;\n for (var i = 0; i < obj.length; i++) {\n xs[i] = has(obj, i) ? inspect(obj[i], obj) : '';\n }\n }\n var syms = typeof gOPS === 'function' ? gOPS(obj) : [];\n var symMap;\n if (hasShammedSymbols) {\n symMap = {};\n for (var k = 0; k < syms.length; k++) {\n symMap['$' + syms[k]] = syms[k];\n }\n }\n\n for (var key in obj) { // eslint-disable-line no-restricted-syntax\n if (!has(obj, key)) { continue; } // eslint-disable-line no-restricted-syntax, no-continue\n if (isArr && String(Number(key)) === key && key < obj.length) { continue; } // eslint-disable-line no-restricted-syntax, no-continue\n if (hasShammedSymbols && symMap['$' + key] instanceof Symbol) {\n // this is to prevent shammed Symbols, which are stored as strings, from being included in the string key section\n continue; // eslint-disable-line no-restricted-syntax, no-continue\n } else if ($test.call(/[^\\w$]/, key)) {\n xs.push(inspect(key, obj) + ': ' + inspect(obj[key], obj));\n } else {\n xs.push(key + ': ' + inspect(obj[key], obj));\n }\n }\n if (typeof gOPS === 'function') {\n for (var j = 0; j < syms.length; j++) {\n if (isEnumerable.call(obj, syms[j])) {\n xs.push('[' + inspect(syms[j]) + ']: ' + inspect(obj[syms[j]], obj));\n }\n }\n }\n return xs;\n}\n","'use strict';\n\nvar utils = require('./utils');\n\nvar has = Object.prototype.hasOwnProperty;\nvar isArray = Array.isArray;\n\nvar defaults = {\n allowDots: false,\n allowPrototypes: false,\n allowSparse: false,\n arrayLimit: 20,\n charset: 'utf-8',\n charsetSentinel: false,\n comma: false,\n decoder: utils.decode,\n delimiter: '&',\n depth: 5,\n ignoreQueryPrefix: false,\n interpretNumericEntities: false,\n parameterLimit: 1000,\n parseArrays: true,\n plainObjects: false,\n strictNullHandling: false\n};\n\nvar interpretNumericEntities = function (str) {\n return str.replace(/&#(\\d+);/g, function ($0, numberStr) {\n return String.fromCharCode(parseInt(numberStr, 10));\n });\n};\n\nvar parseArrayValue = function (val, options) {\n if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {\n return val.split(',');\n }\n\n return val;\n};\n\n// This is what browsers will submit when the ✓ character occurs in an\n// application/x-www-form-urlencoded body and the encoding of the page containing\n// the form is iso-8859-1, or when the submitted form has an accept-charset\n// attribute of iso-8859-1. Presumably also with other charsets that do not contain\n// the ✓ character, such as us-ascii.\nvar isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('✓')\n\n// These are the percent-encoded utf-8 octets representing a checkmark, indicating that the request actually is utf-8 encoded.\nvar charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')\n\nvar parseValues = function parseQueryStringValues(str, options) {\n var obj = {};\n var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\\?/, '') : str;\n var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit;\n var parts = cleanStr.split(options.delimiter, limit);\n var skipIndex = -1; // Keep track of where the utf8 sentinel was found\n var i;\n\n var charset = options.charset;\n if (options.charsetSentinel) {\n for (i = 0; i < parts.length; ++i) {\n if (parts[i].indexOf('utf8=') === 0) {\n if (parts[i] === charsetSentinel) {\n charset = 'utf-8';\n } else if (parts[i] === isoSentinel) {\n charset = 'iso-8859-1';\n }\n skipIndex = i;\n i = parts.length; // The eslint settings do not allow break;\n }\n }\n }\n\n for (i = 0; i < parts.length; ++i) {\n if (i === skipIndex) {\n continue;\n }\n var part = parts[i];\n\n var bracketEqualsPos = part.indexOf(']=');\n var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1;\n\n var key, val;\n if (pos === -1) {\n key = options.decoder(part, defaults.decoder, charset, 'key');\n val = options.strictNullHandling ? null : '';\n } else {\n key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');\n val = utils.maybeMap(\n parseArrayValue(part.slice(pos + 1), options),\n function (encodedVal) {\n return options.decoder(encodedVal, defaults.decoder, charset, 'value');\n }\n );\n }\n\n if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {\n val = interpretNumericEntities(val);\n }\n\n if (part.indexOf('[]=') > -1) {\n val = isArray(val) ? [val] : val;\n }\n\n if (has.call(obj, key)) {\n obj[key] = utils.combine(obj[key], val);\n } else {\n obj[key] = val;\n }\n }\n\n return obj;\n};\n\nvar parseObject = function (chain, val, options, valuesParsed) {\n var leaf = valuesParsed ? val : parseArrayValue(val, options);\n\n for (var i = chain.length - 1; i >= 0; --i) {\n var obj;\n var root = chain[i];\n\n if (root === '[]' && options.parseArrays) {\n obj = [].concat(leaf);\n } else {\n obj = options.plainObjects ? Object.create(null) : {};\n var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;\n var index = parseInt(cleanRoot, 10);\n if (!options.parseArrays && cleanRoot === '') {\n obj = { 0: leaf };\n } else if (\n !isNaN(index)\n && root !== cleanRoot\n && String(index) === cleanRoot\n && index >= 0\n && (options.parseArrays && index <= options.arrayLimit)\n ) {\n obj = [];\n obj[index] = leaf;\n } else if (cleanRoot !== '__proto__') {\n obj[cleanRoot] = leaf;\n }\n }\n\n leaf = obj;\n }\n\n return leaf;\n};\n\nvar parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {\n if (!givenKey) {\n return;\n }\n\n // Transform dot notation to bracket notation\n var key = options.allowDots ? givenKey.replace(/\\.([^.[]+)/g, '[$1]') : givenKey;\n\n // The regex chunks\n\n var brackets = /(\\[[^[\\]]*])/;\n var child = /(\\[[^[\\]]*])/g;\n\n // Get the parent\n\n var segment = options.depth > 0 && brackets.exec(key);\n var parent = segment ? key.slice(0, segment.index) : key;\n\n // Stash the parent if it exists\n\n var keys = [];\n if (parent) {\n // If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties\n if (!options.plainObjects && has.call(Object.prototype, parent)) {\n if (!options.allowPrototypes) {\n return;\n }\n }\n\n keys.push(parent);\n }\n\n // Loop through children appending to the array until we hit depth\n\n var i = 0;\n while (options.depth > 0 && (segment = child.exec(key)) !== null && i < options.depth) {\n i += 1;\n if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) {\n if (!options.allowPrototypes) {\n return;\n }\n }\n keys.push(segment[1]);\n }\n\n // If there's a remainder, just add whatever is left\n\n if (segment) {\n keys.push('[' + key.slice(segment.index) + ']');\n }\n\n return parseObject(keys, val, options, valuesParsed);\n};\n\nvar normalizeParseOptions = function normalizeParseOptions(opts) {\n if (!opts) {\n return defaults;\n }\n\n if (opts.decoder !== null && opts.decoder !== undefined && typeof opts.decoder !== 'function') {\n throw new TypeError('Decoder has to be a function.');\n }\n\n if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {\n throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');\n }\n var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset;\n\n return {\n allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,\n allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes,\n allowSparse: typeof opts.allowSparse === 'boolean' ? opts.allowSparse : defaults.allowSparse,\n arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,\n charset: charset,\n charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,\n comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma,\n decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder,\n delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter,\n // eslint-disable-next-line no-implicit-coercion, no-extra-parens\n depth: (typeof opts.depth === 'number' || opts.depth === false) ? +opts.depth : defaults.depth,\n ignoreQueryPrefix: opts.ignoreQueryPrefix === true,\n interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities,\n parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit,\n parseArrays: opts.parseArrays !== false,\n plainObjects: typeof opts.plainObjects === 'boolean' ? opts.plainObjects : defaults.plainObjects,\n strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling\n };\n};\n\nmodule.exports = function (str, opts) {\n var options = normalizeParseOptions(opts);\n\n if (str === '' || str === null || typeof str === 'undefined') {\n return options.plainObjects ? Object.create(null) : {};\n }\n\n var tempObj = typeof str === 'string' ? parseValues(str, options) : str;\n var obj = options.plainObjects ? Object.create(null) : {};\n\n // Iterate over the keys and setup the new object\n\n var keys = Object.keys(tempObj);\n for (var i = 0; i < keys.length; ++i) {\n var key = keys[i];\n var newObj = parseKeys(key, tempObj[key], options, typeof str === 'string');\n obj = utils.merge(obj, newObj, options);\n }\n\n if (options.allowSparse === true) {\n return obj;\n }\n\n return utils.compact(obj);\n};\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-is.production.min.js');\n} else {\n module.exports = require('./cjs/react-is.development.js');\n}\n","/** @license React v16.13.1\n * react-is.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';var b=\"function\"===typeof Symbol&&Symbol.for,c=b?Symbol.for(\"react.element\"):60103,d=b?Symbol.for(\"react.portal\"):60106,e=b?Symbol.for(\"react.fragment\"):60107,f=b?Symbol.for(\"react.strict_mode\"):60108,g=b?Symbol.for(\"react.profiler\"):60114,h=b?Symbol.for(\"react.provider\"):60109,k=b?Symbol.for(\"react.context\"):60110,l=b?Symbol.for(\"react.async_mode\"):60111,m=b?Symbol.for(\"react.concurrent_mode\"):60111,n=b?Symbol.for(\"react.forward_ref\"):60112,p=b?Symbol.for(\"react.suspense\"):60113,q=b?\nSymbol.for(\"react.suspense_list\"):60120,r=b?Symbol.for(\"react.memo\"):60115,t=b?Symbol.for(\"react.lazy\"):60116,v=b?Symbol.for(\"react.block\"):60121,w=b?Symbol.for(\"react.fundamental\"):60117,x=b?Symbol.for(\"react.responder\"):60118,y=b?Symbol.for(\"react.scope\"):60119;\nfunction z(a){if(\"object\"===typeof a&&null!==a){var u=a.$$typeof;switch(u){case c:switch(a=a.type,a){case l:case m:case e:case g:case f:case p:return a;default:switch(a=a&&a.$$typeof,a){case k:case n:case t:case r:case h:return a;default:return u}}case d:return u}}}function A(a){return z(a)===m}exports.AsyncMode=l;exports.ConcurrentMode=m;exports.ContextConsumer=k;exports.ContextProvider=h;exports.Element=c;exports.ForwardRef=n;exports.Fragment=e;exports.Lazy=t;exports.Memo=r;exports.Portal=d;\nexports.Profiler=g;exports.StrictMode=f;exports.Suspense=p;exports.isAsyncMode=function(a){return A(a)||z(a)===l};exports.isConcurrentMode=A;exports.isContextConsumer=function(a){return z(a)===k};exports.isContextProvider=function(a){return z(a)===h};exports.isElement=function(a){return\"object\"===typeof a&&null!==a&&a.$$typeof===c};exports.isForwardRef=function(a){return z(a)===n};exports.isFragment=function(a){return z(a)===e};exports.isLazy=function(a){return z(a)===t};\nexports.isMemo=function(a){return z(a)===r};exports.isPortal=function(a){return z(a)===d};exports.isProfiler=function(a){return z(a)===g};exports.isStrictMode=function(a){return z(a)===f};exports.isSuspense=function(a){return z(a)===p};\nexports.isValidElementType=function(a){return\"string\"===typeof a||\"function\"===typeof a||a===e||a===m||a===g||a===f||a===p||a===q||\"object\"===typeof a&&null!==a&&(a.$$typeof===t||a.$$typeof===r||a.$$typeof===h||a.$$typeof===k||a.$$typeof===n||a.$$typeof===w||a.$$typeof===x||a.$$typeof===y||a.$$typeof===v)};exports.typeOf=z;\n","/** @license React v17.0.2\n * react-is.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var b=60103,c=60106,d=60107,e=60108,f=60114,g=60109,h=60110,k=60112,l=60113,m=60120,n=60115,p=60116,q=60121,r=60122,u=60117,v=60129,w=60131;\nif(\"function\"===typeof Symbol&&Symbol.for){var x=Symbol.for;b=x(\"react.element\");c=x(\"react.portal\");d=x(\"react.fragment\");e=x(\"react.strict_mode\");f=x(\"react.profiler\");g=x(\"react.provider\");h=x(\"react.context\");k=x(\"react.forward_ref\");l=x(\"react.suspense\");m=x(\"react.suspense_list\");n=x(\"react.memo\");p=x(\"react.lazy\");q=x(\"react.block\");r=x(\"react.server.block\");u=x(\"react.fundamental\");v=x(\"react.debug_trace_mode\");w=x(\"react.legacy_hidden\")}\nfunction y(a){if(\"object\"===typeof a&&null!==a){var t=a.$$typeof;switch(t){case b:switch(a=a.type,a){case d:case f:case e:case l:case m:return a;default:switch(a=a&&a.$$typeof,a){case h:case k:case p:case n:case g:return a;default:return t}}case c:return t}}}var z=g,A=b,B=k,C=d,D=p,E=n,F=c,G=f,H=e,I=l;exports.ContextConsumer=h;exports.ContextProvider=z;exports.Element=A;exports.ForwardRef=B;exports.Fragment=C;exports.Lazy=D;exports.Memo=E;exports.Portal=F;exports.Profiler=G;exports.StrictMode=H;\nexports.Suspense=I;exports.isAsyncMode=function(){return!1};exports.isConcurrentMode=function(){return!1};exports.isContextConsumer=function(a){return y(a)===h};exports.isContextProvider=function(a){return y(a)===g};exports.isElement=function(a){return\"object\"===typeof a&&null!==a&&a.$$typeof===b};exports.isForwardRef=function(a){return y(a)===k};exports.isFragment=function(a){return y(a)===d};exports.isLazy=function(a){return y(a)===p};exports.isMemo=function(a){return y(a)===n};\nexports.isPortal=function(a){return y(a)===c};exports.isProfiler=function(a){return y(a)===f};exports.isStrictMode=function(a){return y(a)===e};exports.isSuspense=function(a){return y(a)===l};exports.isValidElementType=function(a){return\"string\"===typeof a||\"function\"===typeof a||a===d||a===f||a===v||a===e||a===l||a===m||a===w||\"object\"===typeof a&&null!==a&&(a.$$typeof===p||a.$$typeof===n||a.$$typeof===g||a.$$typeof===h||a.$$typeof===k||a.$$typeof===u||a.$$typeof===q||a[0]===r)?!0:!1};\nexports.typeOf=y;\n","'use strict';\n\nvar destroy = require('./plugin/destroy');\nvar initialize = require('./plugin/initialize');\nvar update = require('./plugin/update');\n\nmodule.exports = {\n initialize: initialize,\n update: update,\n destroy: destroy\n};\n","'use strict';\n\nvar _ = require('../lib/helper');\nvar dom = require('../lib/dom');\nvar instances = require('./instances');\n\nmodule.exports = function (element) {\n var i = instances.get(element);\n\n if (!i) {\n return;\n }\n\n i.event.unbindAll();\n dom.remove(i.scrollbarX);\n dom.remove(i.scrollbarY);\n dom.remove(i.scrollbarXRail);\n dom.remove(i.scrollbarYRail);\n _.removePsClasses(element);\n\n instances.remove(element);\n};\n","'use strict';\n\nmodule.exports = {\n handlers: ['click-rail', 'drag-scrollbar', 'keyboard', 'wheel', 'touch'],\n maxScrollbarLength: null,\n minScrollbarLength: null,\n scrollXMarginOffset: 0,\n scrollYMarginOffset: 0,\n suppressScrollX: false,\n suppressScrollY: false,\n swipePropagation: true,\n useBothWheelAxes: false,\n wheelPropagation: false,\n wheelSpeed: 1,\n theme: 'default'\n};\n","'use strict';\n\nvar EventElement = function (element) {\n this.element = element;\n this.events = {};\n};\n\nEventElement.prototype.bind = function (eventName, handler) {\n if (typeof this.events[eventName] === 'undefined') {\n this.events[eventName] = [];\n }\n this.events[eventName].push(handler);\n this.element.addEventListener(eventName, handler, false);\n};\n\nEventElement.prototype.unbind = function (eventName, handler) {\n var isHandlerProvided = (typeof handler !== 'undefined');\n this.events[eventName] = this.events[eventName].filter(function (hdlr) {\n if (isHandlerProvided && hdlr !== handler) {\n return true;\n }\n this.element.removeEventListener(eventName, hdlr, false);\n return false;\n }, this);\n};\n\nEventElement.prototype.unbindAll = function () {\n for (var name in this.events) {\n this.unbind(name);\n }\n};\n\nvar EventManager = function () {\n this.eventElements = [];\n};\n\nEventManager.prototype.eventElement = function (element) {\n var ee = this.eventElements.filter(function (eventElement) {\n return eventElement.element === element;\n })[0];\n if (typeof ee === 'undefined') {\n ee = new EventElement(element);\n this.eventElements.push(ee);\n }\n return ee;\n};\n\nEventManager.prototype.bind = function (element, eventName, handler) {\n this.eventElement(element).bind(eventName, handler);\n};\n\nEventManager.prototype.unbind = function (element, eventName, handler) {\n this.eventElement(element).unbind(eventName, handler);\n};\n\nEventManager.prototype.unbindAll = function () {\n for (var i = 0; i < this.eventElements.length; i++) {\n this.eventElements[i].unbindAll();\n }\n};\n\nEventManager.prototype.once = function (element, eventName, handler) {\n var ee = this.eventElement(element);\n var onceHandler = function (e) {\n ee.unbind(eventName, onceHandler);\n handler(e);\n };\n ee.bind(eventName, onceHandler);\n};\n\nmodule.exports = EventManager;\n","'use strict';\n\nmodule.exports = (function () {\n function s4() {\n return Math.floor((1 + Math.random()) * 0x10000)\n .toString(16)\n .substring(1);\n }\n return function () {\n return s4() + s4() + '-' + s4() + '-' + s4() + '-' +\n s4() + '-' + s4() + s4() + s4();\n };\n})();\n","'use strict';\n\nvar _ = require('../lib/helper');\nvar cls = require('../lib/class');\nvar instances = require('./instances');\nvar updateGeometry = require('./update-geometry');\n\n// Handlers\nvar handlers = {\n 'click-rail': require('./handler/click-rail'),\n 'drag-scrollbar': require('./handler/drag-scrollbar'),\n 'keyboard': require('./handler/keyboard'),\n 'wheel': require('./handler/mouse-wheel'),\n 'touch': require('./handler/touch'),\n 'selection': require('./handler/selection')\n};\nvar nativeScrollHandler = require('./handler/native-scroll');\n\nmodule.exports = function (element, userSettings) {\n userSettings = typeof userSettings === 'object' ? userSettings : {};\n\n cls.add(element, 'ps-container');\n\n // Create a plugin instance.\n var i = instances.add(element);\n\n i.settings = _.extend(i.settings, userSettings);\n cls.add(element, 'ps-theme-' + i.settings.theme);\n\n i.settings.handlers.forEach(function (handlerName) {\n handlers[handlerName](element);\n });\n\n nativeScrollHandler(element);\n\n updateGeometry(element);\n};\n","'use strict';\n\nvar instances = require('../instances');\nvar updateGeometry = require('../update-geometry');\nvar updateScroll = require('../update-scroll');\n\nfunction bindClickRailHandler(element, i) {\n function pageOffset(el) {\n return el.getBoundingClientRect();\n }\n var stopPropagation = function (e) { e.stopPropagation(); };\n\n i.event.bind(i.scrollbarY, 'click', stopPropagation);\n i.event.bind(i.scrollbarYRail, 'click', function (e) {\n var positionTop = e.pageY - window.pageYOffset - pageOffset(i.scrollbarYRail).top;\n var direction = positionTop > i.scrollbarYTop ? 1 : -1;\n\n updateScroll(element, 'top', element.scrollTop + direction * i.containerHeight);\n updateGeometry(element);\n\n e.stopPropagation();\n });\n\n i.event.bind(i.scrollbarX, 'click', stopPropagation);\n i.event.bind(i.scrollbarXRail, 'click', function (e) {\n var positionLeft = e.pageX - window.pageXOffset - pageOffset(i.scrollbarXRail).left;\n var direction = positionLeft > i.scrollbarXLeft ? 1 : -1;\n\n updateScroll(element, 'left', element.scrollLeft + direction * i.containerWidth);\n updateGeometry(element);\n\n e.stopPropagation();\n });\n}\n\nmodule.exports = function (element) {\n var i = instances.get(element);\n bindClickRailHandler(element, i);\n};\n","'use strict';\n\nvar _ = require('../../lib/helper');\nvar dom = require('../../lib/dom');\nvar instances = require('../instances');\nvar updateGeometry = require('../update-geometry');\nvar updateScroll = require('../update-scroll');\n\nfunction bindMouseScrollXHandler(element, i) {\n var currentLeft = null;\n var currentPageX = null;\n\n function updateScrollLeft(deltaX) {\n var newLeft = currentLeft + (deltaX * i.railXRatio);\n var maxLeft = Math.max(0, i.scrollbarXRail.getBoundingClientRect().left) + (i.railXRatio * (i.railXWidth - i.scrollbarXWidth));\n\n if (newLeft < 0) {\n i.scrollbarXLeft = 0;\n } else if (newLeft > maxLeft) {\n i.scrollbarXLeft = maxLeft;\n } else {\n i.scrollbarXLeft = newLeft;\n }\n\n var scrollLeft = _.toInt(i.scrollbarXLeft * (i.contentWidth - i.containerWidth) / (i.containerWidth - (i.railXRatio * i.scrollbarXWidth))) - i.negativeScrollAdjustment;\n updateScroll(element, 'left', scrollLeft);\n }\n\n var mouseMoveHandler = function (e) {\n updateScrollLeft(e.pageX - currentPageX);\n updateGeometry(element);\n e.stopPropagation();\n e.preventDefault();\n };\n\n var mouseUpHandler = function () {\n _.stopScrolling(element, 'x');\n i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler);\n };\n\n i.event.bind(i.scrollbarX, 'mousedown', function (e) {\n currentPageX = e.pageX;\n currentLeft = _.toInt(dom.css(i.scrollbarX, 'left')) * i.railXRatio;\n _.startScrolling(element, 'x');\n\n i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler);\n i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler);\n\n e.stopPropagation();\n e.preventDefault();\n });\n}\n\nfunction bindMouseScrollYHandler(element, i) {\n var currentTop = null;\n var currentPageY = null;\n\n function updateScrollTop(deltaY) {\n var newTop = currentTop + (deltaY * i.railYRatio);\n var maxTop = Math.max(0, i.scrollbarYRail.getBoundingClientRect().top) + (i.railYRatio * (i.railYHeight - i.scrollbarYHeight));\n\n if (newTop < 0) {\n i.scrollbarYTop = 0;\n } else if (newTop > maxTop) {\n i.scrollbarYTop = maxTop;\n } else {\n i.scrollbarYTop = newTop;\n }\n\n var scrollTop = _.toInt(i.scrollbarYTop * (i.contentHeight - i.containerHeight) / (i.containerHeight - (i.railYRatio * i.scrollbarYHeight)));\n updateScroll(element, 'top', scrollTop);\n }\n\n var mouseMoveHandler = function (e) {\n updateScrollTop(e.pageY - currentPageY);\n updateGeometry(element);\n e.stopPropagation();\n e.preventDefault();\n };\n\n var mouseUpHandler = function () {\n _.stopScrolling(element, 'y');\n i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler);\n };\n\n i.event.bind(i.scrollbarY, 'mousedown', function (e) {\n currentPageY = e.pageY;\n currentTop = _.toInt(dom.css(i.scrollbarY, 'top')) * i.railYRatio;\n _.startScrolling(element, 'y');\n\n i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler);\n i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler);\n\n e.stopPropagation();\n e.preventDefault();\n });\n}\n\nmodule.exports = function (element) {\n var i = instances.get(element);\n bindMouseScrollXHandler(element, i);\n bindMouseScrollYHandler(element, i);\n};\n","'use strict';\n\nvar _ = require('../../lib/helper');\nvar dom = require('../../lib/dom');\nvar instances = require('../instances');\nvar updateGeometry = require('../update-geometry');\nvar updateScroll = require('../update-scroll');\n\nfunction bindKeyboardHandler(element, i) {\n var hovered = false;\n i.event.bind(element, 'mouseenter', function () {\n hovered = true;\n });\n i.event.bind(element, 'mouseleave', function () {\n hovered = false;\n });\n\n var shouldPrevent = false;\n function shouldPreventDefault(deltaX, deltaY) {\n var scrollTop = element.scrollTop;\n if (deltaX === 0) {\n if (!i.scrollbarYActive) {\n return false;\n }\n if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) {\n return !i.settings.wheelPropagation;\n }\n }\n\n var scrollLeft = element.scrollLeft;\n if (deltaY === 0) {\n if (!i.scrollbarXActive) {\n return false;\n }\n if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) {\n return !i.settings.wheelPropagation;\n }\n }\n return true;\n }\n\n i.event.bind(i.ownerDocument, 'keydown', function (e) {\n if ((e.isDefaultPrevented && e.isDefaultPrevented()) || e.defaultPrevented) {\n return;\n }\n\n var focused = dom.matches(i.scrollbarX, ':focus') ||\n dom.matches(i.scrollbarY, ':focus');\n\n if (!hovered && !focused) {\n return;\n }\n\n var activeElement = document.activeElement ? document.activeElement : i.ownerDocument.activeElement;\n if (activeElement) {\n if (activeElement.tagName === 'IFRAME') {\n activeElement = activeElement.contentDocument.activeElement;\n } else {\n // go deeper if element is a webcomponent\n while (activeElement.shadowRoot) {\n activeElement = activeElement.shadowRoot.activeElement;\n }\n }\n if (_.isEditable(activeElement)) {\n return;\n }\n }\n\n var deltaX = 0;\n var deltaY = 0;\n\n switch (e.which) {\n case 37: // left\n if (e.metaKey) {\n deltaX = -i.contentWidth;\n } else if (e.altKey) {\n deltaX = -i.containerWidth;\n } else {\n deltaX = -30;\n }\n break;\n case 38: // up\n if (e.metaKey) {\n deltaY = i.contentHeight;\n } else if (e.altKey) {\n deltaY = i.containerHeight;\n } else {\n deltaY = 30;\n }\n break;\n case 39: // right\n if (e.metaKey) {\n deltaX = i.contentWidth;\n } else if (e.altKey) {\n deltaX = i.containerWidth;\n } else {\n deltaX = 30;\n }\n break;\n case 40: // down\n if (e.metaKey) {\n deltaY = -i.contentHeight;\n } else if (e.altKey) {\n deltaY = -i.containerHeight;\n } else {\n deltaY = -30;\n }\n break;\n case 33: // page up\n deltaY = 90;\n break;\n case 32: // space bar\n if (e.shiftKey) {\n deltaY = 90;\n } else {\n deltaY = -90;\n }\n break;\n case 34: // page down\n deltaY = -90;\n break;\n case 35: // end\n if (e.ctrlKey) {\n deltaY = -i.contentHeight;\n } else {\n deltaY = -i.containerHeight;\n }\n break;\n case 36: // home\n if (e.ctrlKey) {\n deltaY = element.scrollTop;\n } else {\n deltaY = i.containerHeight;\n }\n break;\n default:\n return;\n }\n\n updateScroll(element, 'top', element.scrollTop - deltaY);\n updateScroll(element, 'left', element.scrollLeft + deltaX);\n updateGeometry(element);\n\n shouldPrevent = shouldPreventDefault(deltaX, deltaY);\n if (shouldPrevent) {\n e.preventDefault();\n }\n });\n}\n\nmodule.exports = function (element) {\n var i = instances.get(element);\n bindKeyboardHandler(element, i);\n};\n","'use strict';\n\nvar instances = require('../instances');\nvar updateGeometry = require('../update-geometry');\nvar updateScroll = require('../update-scroll');\n\nfunction bindMouseWheelHandler(element, i) {\n var shouldPrevent = false;\n\n function shouldPreventDefault(deltaX, deltaY) {\n var scrollTop = element.scrollTop;\n if (deltaX === 0) {\n if (!i.scrollbarYActive) {\n return false;\n }\n if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) {\n return !i.settings.wheelPropagation;\n }\n }\n\n var scrollLeft = element.scrollLeft;\n if (deltaY === 0) {\n if (!i.scrollbarXActive) {\n return false;\n }\n if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) {\n return !i.settings.wheelPropagation;\n }\n }\n return true;\n }\n\n function getDeltaFromEvent(e) {\n var deltaX = e.deltaX;\n var deltaY = -1 * e.deltaY;\n\n if (typeof deltaX === \"undefined\" || typeof deltaY === \"undefined\") {\n // OS X Safari\n deltaX = -1 * e.wheelDeltaX / 6;\n deltaY = e.wheelDeltaY / 6;\n }\n\n if (e.deltaMode && e.deltaMode === 1) {\n // Firefox in deltaMode 1: Line scrolling\n deltaX *= 10;\n deltaY *= 10;\n }\n\n if (deltaX !== deltaX && deltaY !== deltaY/* NaN checks */) {\n // IE in some mouse drivers\n deltaX = 0;\n deltaY = e.wheelDelta;\n }\n\n if (e.shiftKey) {\n // reverse axis with shift key\n return [-deltaY, -deltaX];\n }\n return [deltaX, deltaY];\n }\n\n function shouldBeConsumedByChild(deltaX, deltaY) {\n var child = element.querySelector('textarea:hover, select[multiple]:hover, .ps-child:hover');\n if (child) {\n if (!window.getComputedStyle(child).overflow.match(/(scroll|auto)/)) {\n // if not scrollable\n return false;\n }\n\n var maxScrollTop = child.scrollHeight - child.clientHeight;\n if (maxScrollTop > 0) {\n if (!(child.scrollTop === 0 && deltaY > 0) && !(child.scrollTop === maxScrollTop && deltaY < 0)) {\n return true;\n }\n }\n var maxScrollLeft = child.scrollLeft - child.clientWidth;\n if (maxScrollLeft > 0) {\n if (!(child.scrollLeft === 0 && deltaX < 0) && !(child.scrollLeft === maxScrollLeft && deltaX > 0)) {\n return true;\n }\n }\n }\n return false;\n }\n\n function mousewheelHandler(e) {\n var delta = getDeltaFromEvent(e);\n\n var deltaX = delta[0];\n var deltaY = delta[1];\n\n if (shouldBeConsumedByChild(deltaX, deltaY)) {\n return;\n }\n\n shouldPrevent = false;\n if (!i.settings.useBothWheelAxes) {\n // deltaX will only be used for horizontal scrolling and deltaY will\n // only be used for vertical scrolling - this is the default\n updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed));\n updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed));\n } else if (i.scrollbarYActive && !i.scrollbarXActive) {\n // only vertical scrollbar is active and useBothWheelAxes option is\n // active, so let's scroll vertical bar using both mouse wheel axes\n if (deltaY) {\n updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed));\n } else {\n updateScroll(element, 'top', element.scrollTop + (deltaX * i.settings.wheelSpeed));\n }\n shouldPrevent = true;\n } else if (i.scrollbarXActive && !i.scrollbarYActive) {\n // useBothWheelAxes and only horizontal bar is active, so use both\n // wheel axes for horizontal bar\n if (deltaX) {\n updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed));\n } else {\n updateScroll(element, 'left', element.scrollLeft - (deltaY * i.settings.wheelSpeed));\n }\n shouldPrevent = true;\n }\n\n updateGeometry(element);\n\n shouldPrevent = (shouldPrevent || shouldPreventDefault(deltaX, deltaY));\n if (shouldPrevent) {\n e.stopPropagation();\n e.preventDefault();\n }\n }\n\n if (typeof window.onwheel !== \"undefined\") {\n i.event.bind(element, 'wheel', mousewheelHandler);\n } else if (typeof window.onmousewheel !== \"undefined\") {\n i.event.bind(element, 'mousewheel', mousewheelHandler);\n }\n}\n\nmodule.exports = function (element) {\n var i = instances.get(element);\n bindMouseWheelHandler(element, i);\n};\n","'use strict';\n\nvar _ = require('../../lib/helper');\nvar instances = require('../instances');\nvar updateGeometry = require('../update-geometry');\nvar updateScroll = require('../update-scroll');\n\nfunction bindTouchHandler(element, i, supportsTouch, supportsIePointer) {\n function shouldPreventDefault(deltaX, deltaY) {\n var scrollTop = element.scrollTop;\n var scrollLeft = element.scrollLeft;\n var magnitudeX = Math.abs(deltaX);\n var magnitudeY = Math.abs(deltaY);\n\n if (magnitudeY > magnitudeX) {\n // user is perhaps trying to swipe up/down the page\n\n if (((deltaY < 0) && (scrollTop === i.contentHeight - i.containerHeight)) ||\n ((deltaY > 0) && (scrollTop === 0))) {\n return !i.settings.swipePropagation;\n }\n } else if (magnitudeX > magnitudeY) {\n // user is perhaps trying to swipe left/right across the page\n\n if (((deltaX < 0) && (scrollLeft === i.contentWidth - i.containerWidth)) ||\n ((deltaX > 0) && (scrollLeft === 0))) {\n return !i.settings.swipePropagation;\n }\n }\n\n return true;\n }\n\n function applyTouchMove(differenceX, differenceY) {\n updateScroll(element, 'top', element.scrollTop - differenceY);\n updateScroll(element, 'left', element.scrollLeft - differenceX);\n\n updateGeometry(element);\n }\n\n var startOffset = {};\n var startTime = 0;\n var speed = {};\n var easingLoop = null;\n var inGlobalTouch = false;\n var inLocalTouch = false;\n\n function globalTouchStart() {\n inGlobalTouch = true;\n }\n function globalTouchEnd() {\n inGlobalTouch = false;\n }\n\n function getTouch(e) {\n if (e.targetTouches) {\n return e.targetTouches[0];\n } else {\n // Maybe IE pointer\n return e;\n }\n }\n function shouldHandle(e) {\n if (e.targetTouches && e.targetTouches.length === 1) {\n return true;\n }\n if (e.pointerType && e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {\n return true;\n }\n return false;\n }\n function touchStart(e) {\n if (shouldHandle(e)) {\n inLocalTouch = true;\n\n var touch = getTouch(e);\n\n startOffset.pageX = touch.pageX;\n startOffset.pageY = touch.pageY;\n\n startTime = (new Date()).getTime();\n\n if (easingLoop !== null) {\n clearInterval(easingLoop);\n }\n\n e.stopPropagation();\n }\n }\n function touchMove(e) {\n if (!inLocalTouch && i.settings.swipePropagation) {\n touchStart(e);\n }\n if (!inGlobalTouch && inLocalTouch && shouldHandle(e)) {\n var touch = getTouch(e);\n\n var currentOffset = {pageX: touch.pageX, pageY: touch.pageY};\n\n var differenceX = currentOffset.pageX - startOffset.pageX;\n var differenceY = currentOffset.pageY - startOffset.pageY;\n\n applyTouchMove(differenceX, differenceY);\n startOffset = currentOffset;\n\n var currentTime = (new Date()).getTime();\n\n var timeGap = currentTime - startTime;\n if (timeGap > 0) {\n speed.x = differenceX / timeGap;\n speed.y = differenceY / timeGap;\n startTime = currentTime;\n }\n\n if (shouldPreventDefault(differenceX, differenceY)) {\n e.stopPropagation();\n e.preventDefault();\n }\n }\n }\n function touchEnd() {\n if (!inGlobalTouch && inLocalTouch) {\n inLocalTouch = false;\n\n clearInterval(easingLoop);\n easingLoop = setInterval(function () {\n if (!instances.get(element)) {\n clearInterval(easingLoop);\n return;\n }\n\n if (!speed.x && !speed.y) {\n clearInterval(easingLoop);\n return;\n }\n\n if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) {\n clearInterval(easingLoop);\n return;\n }\n\n applyTouchMove(speed.x * 30, speed.y * 30);\n\n speed.x *= 0.8;\n speed.y *= 0.8;\n }, 10);\n }\n }\n\n if (supportsTouch) {\n i.event.bind(window, 'touchstart', globalTouchStart);\n i.event.bind(window, 'touchend', globalTouchEnd);\n i.event.bind(element, 'touchstart', touchStart);\n i.event.bind(element, 'touchmove', touchMove);\n i.event.bind(element, 'touchend', touchEnd);\n }\n\n if (supportsIePointer) {\n if (window.PointerEvent) {\n i.event.bind(window, 'pointerdown', globalTouchStart);\n i.event.bind(window, 'pointerup', globalTouchEnd);\n i.event.bind(element, 'pointerdown', touchStart);\n i.event.bind(element, 'pointermove', touchMove);\n i.event.bind(element, 'pointerup', touchEnd);\n } else if (window.MSPointerEvent) {\n i.event.bind(window, 'MSPointerDown', globalTouchStart);\n i.event.bind(window, 'MSPointerUp', globalTouchEnd);\n i.event.bind(element, 'MSPointerDown', touchStart);\n i.event.bind(element, 'MSPointerMove', touchMove);\n i.event.bind(element, 'MSPointerUp', touchEnd);\n }\n }\n}\n\nmodule.exports = function (element) {\n if (!_.env.supportsTouch && !_.env.supportsIePointer) {\n return;\n }\n\n var i = instances.get(element);\n bindTouchHandler(element, i, _.env.supportsTouch, _.env.supportsIePointer);\n};\n","'use strict';\n\nvar _ = require('../../lib/helper');\nvar instances = require('../instances');\nvar updateGeometry = require('../update-geometry');\nvar updateScroll = require('../update-scroll');\n\nfunction bindSelectionHandler(element, i) {\n function getRangeNode() {\n var selection = window.getSelection ? window.getSelection() :\n document.getSelection ? document.getSelection() : '';\n if (selection.toString().length === 0) {\n return null;\n } else {\n return selection.getRangeAt(0).commonAncestorContainer;\n }\n }\n\n var scrollingLoop = null;\n var scrollDiff = {top: 0, left: 0};\n function startScrolling() {\n if (!scrollingLoop) {\n scrollingLoop = setInterval(function () {\n if (!instances.get(element)) {\n clearInterval(scrollingLoop);\n return;\n }\n\n updateScroll(element, 'top', element.scrollTop + scrollDiff.top);\n updateScroll(element, 'left', element.scrollLeft + scrollDiff.left);\n updateGeometry(element);\n }, 50); // every .1 sec\n }\n }\n function stopScrolling() {\n if (scrollingLoop) {\n clearInterval(scrollingLoop);\n scrollingLoop = null;\n }\n _.stopScrolling(element);\n }\n\n var isSelected = false;\n i.event.bind(i.ownerDocument, 'selectionchange', function () {\n if (element.contains(getRangeNode())) {\n isSelected = true;\n } else {\n isSelected = false;\n stopScrolling();\n }\n });\n i.event.bind(window, 'mouseup', function () {\n if (isSelected) {\n isSelected = false;\n stopScrolling();\n }\n });\n i.event.bind(window, 'keyup', function () {\n if (isSelected) {\n isSelected = false;\n stopScrolling();\n }\n });\n\n i.event.bind(window, 'mousemove', function (e) {\n if (isSelected) {\n var mousePosition = {x: e.pageX, y: e.pageY};\n var containerGeometry = {\n left: element.offsetLeft,\n right: element.offsetLeft + element.offsetWidth,\n top: element.offsetTop,\n bottom: element.offsetTop + element.offsetHeight\n };\n\n if (mousePosition.x < containerGeometry.left + 3) {\n scrollDiff.left = -5;\n _.startScrolling(element, 'x');\n } else if (mousePosition.x > containerGeometry.right - 3) {\n scrollDiff.left = 5;\n _.startScrolling(element, 'x');\n } else {\n scrollDiff.left = 0;\n }\n\n if (mousePosition.y < containerGeometry.top + 3) {\n if (containerGeometry.top + 3 - mousePosition.y < 5) {\n scrollDiff.top = -5;\n } else {\n scrollDiff.top = -20;\n }\n _.startScrolling(element, 'y');\n } else if (mousePosition.y > containerGeometry.bottom - 3) {\n if (mousePosition.y - containerGeometry.bottom + 3 < 5) {\n scrollDiff.top = 5;\n } else {\n scrollDiff.top = 20;\n }\n _.startScrolling(element, 'y');\n } else {\n scrollDiff.top = 0;\n }\n\n if (scrollDiff.top === 0 && scrollDiff.left === 0) {\n stopScrolling();\n } else {\n startScrolling();\n }\n }\n });\n}\n\nmodule.exports = function (element) {\n var i = instances.get(element);\n bindSelectionHandler(element, i);\n};\n","'use strict';\n\nvar instances = require('../instances');\nvar updateGeometry = require('../update-geometry');\n\nfunction bindNativeScrollHandler(element, i) {\n i.event.bind(element, 'scroll', function () {\n updateGeometry(element);\n });\n}\n\nmodule.exports = function (element) {\n var i = instances.get(element);\n bindNativeScrollHandler(element, i);\n};\n","'use strict';\n\nvar _ = require('../lib/helper');\nvar dom = require('../lib/dom');\nvar instances = require('./instances');\nvar updateGeometry = require('./update-geometry');\nvar updateScroll = require('./update-scroll');\n\nmodule.exports = function (element) {\n var i = instances.get(element);\n\n if (!i) {\n return;\n }\n\n // Recalcuate negative scrollLeft adjustment\n i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0;\n\n // Recalculate rail margins\n dom.css(i.scrollbarXRail, 'display', 'block');\n dom.css(i.scrollbarYRail, 'display', 'block');\n i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight'));\n i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom'));\n\n // Hide scrollbars not to affect scrollWidth and scrollHeight\n dom.css(i.scrollbarXRail, 'display', 'none');\n dom.css(i.scrollbarYRail, 'display', 'none');\n\n updateGeometry(element);\n\n // Update top/left scroll to trigger events\n updateScroll(element, 'top', element.scrollTop);\n updateScroll(element, 'left', element.scrollLeft);\n\n dom.css(i.scrollbarXRail, 'display', '');\n dom.css(i.scrollbarYRail, 'display', '');\n};\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar _1 = require(\"./\");\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nvar should_polyfill_1 = require(\"./should-polyfill\");\nvar to_locale_string_1 = require(\"./src/to_locale_string\");\nif (should_polyfill_1.shouldPolyfill()) {\n ecma402_abstract_1.defineProperty(Intl, 'DateTimeFormat', { value: _1.DateTimeFormat });\n ecma402_abstract_1.defineProperty(Date.prototype, 'toLocaleString', {\n value: function toLocaleString(locales, options) {\n return to_locale_string_1.toLocaleString(this, locales, options);\n },\n });\n ecma402_abstract_1.defineProperty(Date.prototype, 'toLocaleDateString', {\n value: function toLocaleDateString(locales, options) {\n return to_locale_string_1.toLocaleDateString(this, locales, options);\n },\n });\n ecma402_abstract_1.defineProperty(Date.prototype, 'toLocaleTimeString', {\n value: function toLocaleTimeString(locales, options) {\n return to_locale_string_1.toLocaleTimeString(this, locales, options);\n },\n });\n}\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar tslib_1 = require(\"tslib\");\ntslib_1.__exportStar(require(\"./src/core\"), exports);\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.LookupMatcher = void 0;\nvar utils_1 = require(\"./utils\");\nvar BestAvailableLocale_1 = require(\"./BestAvailableLocale\");\n/**\n * https://tc39.es/ecma402/#sec-lookupmatcher\n * @param availableLocales\n * @param requestedLocales\n * @param getDefaultLocale\n */\nfunction LookupMatcher(availableLocales, requestedLocales, getDefaultLocale) {\n var result = { locale: '' };\n for (var _i = 0, requestedLocales_1 = requestedLocales; _i < requestedLocales_1.length; _i++) {\n var locale = requestedLocales_1[_i];\n var noExtensionLocale = locale.replace(utils_1.UNICODE_EXTENSION_SEQUENCE_REGEX, '');\n var availableLocale = BestAvailableLocale_1.BestAvailableLocale(availableLocales, noExtensionLocale);\n if (availableLocale) {\n result.locale = availableLocale;\n if (locale !== noExtensionLocale) {\n result.extension = locale.slice(noExtensionLocale.length + 1, locale.length);\n }\n return result;\n }\n }\n result.locale = getDefaultLocale();\n return result;\n}\nexports.LookupMatcher = LookupMatcher;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BestFitMatcher = void 0;\nvar BestAvailableLocale_1 = require(\"./BestAvailableLocale\");\nvar utils_1 = require(\"./utils\");\n/**\n * https://tc39.es/ecma402/#sec-bestfitmatcher\n * @param availableLocales\n * @param requestedLocales\n * @param getDefaultLocale\n */\nfunction BestFitMatcher(availableLocales, requestedLocales, getDefaultLocale) {\n var minimizedAvailableLocaleMap = Array.from(availableLocales).reduce(function (all, l) {\n all[l] = l;\n return all;\n }, {});\n var minimizedAvailableLocales = new Set();\n availableLocales.forEach(function (locale) {\n var minimizedLocale = new Intl.Locale(locale)\n .minimize()\n .toString();\n minimizedAvailableLocaleMap[minimizedLocale] = locale;\n minimizedAvailableLocales.add(minimizedLocale);\n });\n var foundLocale;\n for (var _i = 0, requestedLocales_1 = requestedLocales; _i < requestedLocales_1.length; _i++) {\n var l = requestedLocales_1[_i];\n if (foundLocale) {\n break;\n }\n var noExtensionLocale = l.replace(utils_1.UNICODE_EXTENSION_SEQUENCE_REGEX, '');\n if (availableLocales.has(noExtensionLocale)) {\n foundLocale = noExtensionLocale;\n break;\n }\n if (minimizedAvailableLocales.has(noExtensionLocale)) {\n foundLocale = noExtensionLocale;\n break;\n }\n var locale = new Intl.Locale(noExtensionLocale);\n var maximizedRequestedLocale = locale.maximize().toString();\n var minimizedRequestedLocale = locale.minimize().toString();\n // Check minimized locale\n if (minimizedAvailableLocales.has(minimizedRequestedLocale)) {\n foundLocale = minimizedRequestedLocale;\n break;\n }\n // Lookup algo on maximized locale\n foundLocale = BestAvailableLocale_1.BestAvailableLocale(minimizedAvailableLocales, maximizedRequestedLocale);\n }\n return {\n locale: (foundLocale && minimizedAvailableLocaleMap[foundLocale]) ||\n getDefaultLocale(),\n };\n}\nexports.BestFitMatcher = BestFitMatcher;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.UnicodeExtensionValue = void 0;\nvar utils_1 = require(\"./utils\");\n/**\n * https://tc39.es/ecma402/#sec-unicodeextensionvalue\n * @param extension\n * @param key\n */\nfunction UnicodeExtensionValue(extension, key) {\n utils_1.invariant(key.length === 2, 'key must have 2 elements');\n var size = extension.length;\n var searchValue = \"-\" + key + \"-\";\n var pos = extension.indexOf(searchValue);\n if (pos !== -1) {\n var start = pos + 4;\n var end = start;\n var k = start;\n var done = false;\n while (!done) {\n var e = extension.indexOf('-', k);\n var len = void 0;\n if (e === -1) {\n len = size - k;\n }\n else {\n len = e - k;\n }\n if (len === 2) {\n done = true;\n }\n else if (e === -1) {\n end = size;\n done = true;\n }\n else {\n end = e;\n k = e + 1;\n }\n }\n return extension.slice(start, end);\n }\n searchValue = \"-\" + key;\n pos = extension.indexOf(searchValue);\n if (pos !== -1 && pos + 3 === size) {\n return '';\n }\n return undefined;\n}\nexports.UnicodeExtensionValue = UnicodeExtensionValue;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.LookupSupportedLocales = void 0;\nvar utils_1 = require(\"./utils\");\nvar BestAvailableLocale_1 = require(\"./BestAvailableLocale\");\n/**\n * https://tc39.es/ecma402/#sec-lookupsupportedlocales\n * @param availableLocales\n * @param requestedLocales\n */\nfunction LookupSupportedLocales(availableLocales, requestedLocales) {\n var subset = [];\n for (var _i = 0, requestedLocales_1 = requestedLocales; _i < requestedLocales_1.length; _i++) {\n var locale = requestedLocales_1[_i];\n var noExtensionLocale = locale.replace(utils_1.UNICODE_EXTENSION_SEQUENCE_REGEX, '');\n var availableLocale = BestAvailableLocale_1.BestAvailableLocale(availableLocales, noExtensionLocale);\n if (availableLocale) {\n subset.push(availableLocale);\n }\n }\n return subset;\n}\nexports.LookupSupportedLocales = LookupSupportedLocales;\n","\"use strict\";\n// Type-only circular import\n// eslint-disable-next-line import/no-cycle\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar internalSlotMap = new WeakMap();\nfunction getInternalSlots(x) {\n var internalSlots = internalSlotMap.get(x);\n if (!internalSlots) {\n internalSlots = Object.create(null);\n internalSlotMap.set(x, internalSlots);\n }\n return internalSlots;\n}\nexports.default = getInternalSlots;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n// @generated\n// prettier-ignore\nexports.default = {\n \"Africa/Asmera\": \"Africa/Nairobi\",\n \"Africa/Timbuktu\": \"Africa/Abidjan\",\n \"America/Argentina/ComodRivadavia\": \"America/Argentina/Catamarca\",\n \"America/Atka\": \"America/Adak\",\n \"America/Buenos_Aires\": \"America/Argentina/Buenos_Aires\",\n \"America/Catamarca\": \"America/Argentina/Catamarca\",\n \"America/Coral_Harbour\": \"America/Atikokan\",\n \"America/Cordoba\": \"America/Argentina/Cordoba\",\n \"America/Ensenada\": \"America/Tijuana\",\n \"America/Fort_Wayne\": \"America/Indiana/Indianapolis\",\n \"America/Godthab\": \"America/Nuuk\",\n \"America/Indianapolis\": \"America/Indiana/Indianapolis\",\n \"America/Jujuy\": \"America/Argentina/Jujuy\",\n \"America/Knox_IN\": \"America/Indiana/Knox\",\n \"America/Louisville\": \"America/Kentucky/Louisville\",\n \"America/Mendoza\": \"America/Argentina/Mendoza\",\n \"America/Montreal\": \"America/Toronto\",\n \"America/Porto_Acre\": \"America/Rio_Branco\",\n \"America/Rosario\": \"America/Argentina/Cordoba\",\n \"America/Santa_Isabel\": \"America/Tijuana\",\n \"America/Shiprock\": \"America/Denver\",\n \"America/Virgin\": \"America/Port_of_Spain\",\n \"Antarctica/South_Pole\": \"Pacific/Auckland\",\n \"Asia/Ashkhabad\": \"Asia/Ashgabat\",\n \"Asia/Calcutta\": \"Asia/Kolkata\",\n \"Asia/Chongqing\": \"Asia/Shanghai\",\n \"Asia/Chungking\": \"Asia/Shanghai\",\n \"Asia/Dacca\": \"Asia/Dhaka\",\n \"Asia/Harbin\": \"Asia/Shanghai\",\n \"Asia/Kashgar\": \"Asia/Urumqi\",\n \"Asia/Katmandu\": \"Asia/Kathmandu\",\n \"Asia/Macao\": \"Asia/Macau\",\n \"Asia/Rangoon\": \"Asia/Yangon\",\n \"Asia/Saigon\": \"Asia/Ho_Chi_Minh\",\n \"Asia/Tel_Aviv\": \"Asia/Jerusalem\",\n \"Asia/Thimbu\": \"Asia/Thimphu\",\n \"Asia/Ujung_Pandang\": \"Asia/Makassar\",\n \"Asia/Ulan_Bator\": \"Asia/Ulaanbaatar\",\n \"Atlantic/Faeroe\": \"Atlantic/Faroe\",\n \"Atlantic/Jan_Mayen\": \"Europe/Oslo\",\n \"Australia/ACT\": \"Australia/Sydney\",\n \"Australia/Canberra\": \"Australia/Sydney\",\n \"Australia/Currie\": \"Australia/Hobart\",\n \"Australia/LHI\": \"Australia/Lord_Howe\",\n \"Australia/NSW\": \"Australia/Sydney\",\n \"Australia/North\": \"Australia/Darwin\",\n \"Australia/Queensland\": \"Australia/Brisbane\",\n \"Australia/South\": \"Australia/Adelaide\",\n \"Australia/Tasmania\": \"Australia/Hobart\",\n \"Australia/Victoria\": \"Australia/Melbourne\",\n \"Australia/West\": \"Australia/Perth\",\n \"Australia/Yancowinna\": \"Australia/Broken_Hill\",\n \"Brazil/Acre\": \"America/Rio_Branco\",\n \"Brazil/DeNoronha\": \"America/Noronha\",\n \"Brazil/East\": \"America/Sao_Paulo\",\n \"Brazil/West\": \"America/Manaus\",\n \"Canada/Atlantic\": \"America/Halifax\",\n \"Canada/Central\": \"America/Winnipeg\",\n \"Canada/Eastern\": \"America/Toronto\",\n \"Canada/Mountain\": \"America/Edmonton\",\n \"Canada/Newfoundland\": \"America/St_Johns\",\n \"Canada/Pacific\": \"America/Vancouver\",\n \"Canada/Saskatchewan\": \"America/Regina\",\n \"Canada/Yukon\": \"America/Whitehorse\",\n \"Chile/Continental\": \"America/Santiago\",\n \"Chile/EasterIsland\": \"Pacific/Easter\",\n \"Cuba\": \"America/Havana\",\n \"Egypt\": \"Africa/Cairo\",\n \"Eire\": \"Europe/Dublin\",\n \"Etc/UCT\": \"Etc/UTC\",\n \"Europe/Belfast\": \"Europe/London\",\n \"Europe/Tiraspol\": \"Europe/Chisinau\",\n \"GB\": \"Europe/London\",\n \"GB-Eire\": \"Europe/London\",\n \"GMT+0\": \"Etc/GMT\",\n \"GMT-0\": \"Etc/GMT\",\n \"GMT0\": \"Etc/GMT\",\n \"Greenwich\": \"Etc/GMT\",\n \"Hongkong\": \"Asia/Hong_Kong\",\n \"Iceland\": \"Atlantic/Reykjavik\",\n \"Iran\": \"Asia/Tehran\",\n \"Israel\": \"Asia/Jerusalem\",\n \"Jamaica\": \"America/Jamaica\",\n \"Japan\": \"Asia/Tokyo\",\n \"Kwajalein\": \"Pacific/Kwajalein\",\n \"Libya\": \"Africa/Tripoli\",\n \"Mexico/BajaNorte\": \"America/Tijuana\",\n \"Mexico/BajaSur\": \"America/Mazatlan\",\n \"Mexico/General\": \"America/Mexico_City\",\n \"NZ\": \"Pacific/Auckland\",\n \"NZ-CHAT\": \"Pacific/Chatham\",\n \"Navajo\": \"America/Denver\",\n \"PRC\": \"Asia/Shanghai\",\n \"Pacific/Johnston\": \"Pacific/Honolulu\",\n \"Pacific/Ponape\": \"Pacific/Pohnpei\",\n \"Pacific/Samoa\": \"Pacific/Pago_Pago\",\n \"Pacific/Truk\": \"Pacific/Chuuk\",\n \"Pacific/Yap\": \"Pacific/Chuuk\",\n \"Poland\": \"Europe/Warsaw\",\n \"Portugal\": \"Europe/Lisbon\",\n \"ROC\": \"Asia/Taipei\",\n \"ROK\": \"Asia/Seoul\",\n \"Singapore\": \"Asia/Singapore\",\n \"Turkey\": \"Europe/Istanbul\",\n \"UCT\": \"Etc/UTC\",\n \"US/Alaska\": \"America/Anchorage\",\n \"US/Aleutian\": \"America/Adak\",\n \"US/Arizona\": \"America/Phoenix\",\n \"US/Central\": \"America/Chicago\",\n \"US/East-Indiana\": \"America/Indiana/Indianapolis\",\n \"US/Eastern\": \"America/New_York\",\n \"US/Hawaii\": \"Pacific/Honolulu\",\n \"US/Indiana-Starke\": \"America/Indiana/Knox\",\n \"US/Michigan\": \"America/Detroit\",\n \"US/Mountain\": \"America/Denver\",\n \"US/Pacific\": \"America/Los_Angeles\",\n \"US/Samoa\": \"Pacific/Pago_Pago\",\n \"UTC\": \"Etc/UTC\",\n \"Universal\": \"Etc/UTC\",\n \"W-SU\": \"Europe/Moscow\",\n \"Zulu\": \"Etc/UTC\"\n};\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.unpack = exports.pack = void 0;\nvar tslib_1 = require(\"tslib\");\nfunction pack(data) {\n var zoneNames = Object.keys(data.zones);\n zoneNames.sort(); // so output is stable\n return {\n zones: zoneNames.map(function (zone) {\n return tslib_1.__spreadArray([\n zone\n ], data.zones[zone].map(function (_a) {\n var ts = _a[0], others = _a.slice(1);\n return tslib_1.__spreadArray([ts === '' ? '' : ts.toString(36)], others).join(',');\n })).join('|');\n }),\n abbrvs: data.abbrvs.join('|'),\n offsets: data.offsets.map(function (o) { return o.toString(36); }).join('|'),\n };\n}\nexports.pack = pack;\nfunction unpack(data) {\n var abbrvs = data.abbrvs.split('|');\n var offsets = data.offsets.split('|').map(function (n) { return parseInt(n, 36); });\n var packedZones = data.zones;\n var zones = {};\n for (var _i = 0, packedZones_1 = packedZones; _i < packedZones_1.length; _i++) {\n var d = packedZones_1[_i];\n var _a = d.split('|'), zone = _a[0], zoneData = _a.slice(1);\n zones[zone] = zoneData\n .map(function (z) { return z.split(','); })\n .map(function (_a) {\n var ts = _a[0], abbrvIndex = _a[1], offsetIndex = _a[2], dst = _a[3];\n return [\n ts === '' ? -Infinity : parseInt(ts, 36),\n abbrvs[+abbrvIndex],\n offsets[+offsetIndex],\n dst === '1',\n ];\n });\n }\n return zones;\n}\nexports.unpack = unpack;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.FormatDateTime = void 0;\nvar PartitionDateTimePattern_1 = require(\"./PartitionDateTimePattern\");\n/**\n * https://tc39.es/ecma402/#sec-formatdatetime\n * @param dtf DateTimeFormat\n * @param x\n */\nfunction FormatDateTime(dtf, x, implDetails) {\n var parts = PartitionDateTimePattern_1.PartitionDateTimePattern(dtf, x, implDetails);\n var result = '';\n for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) {\n var part = parts_1[_i];\n result += part.value;\n }\n return result;\n}\nexports.FormatDateTime = FormatDateTime;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.InitializeDateTimeFormat = void 0;\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nvar BasicFormatMatcher_1 = require(\"./BasicFormatMatcher\");\nvar BestFitFormatMatcher_1 = require(\"./BestFitFormatMatcher\");\nvar utils_1 = require(\"./utils\");\nvar DateTimeStyleFormat_1 = require(\"./DateTimeStyleFormat\");\nvar ToDateTimeOptions_1 = require(\"./ToDateTimeOptions\");\nvar intl_localematcher_1 = require(\"@formatjs/intl-localematcher\");\nfunction isTimeRelated(opt) {\n for (var _i = 0, _a = ['hour', 'minute', 'second']; _i < _a.length; _i++) {\n var prop = _a[_i];\n var value = opt[prop];\n if (value !== undefined) {\n return true;\n }\n }\n return false;\n}\nfunction resolveHourCycle(hc, hcDefault, hour12) {\n if (hc == null) {\n hc = hcDefault;\n }\n if (hour12 !== undefined) {\n if (hour12) {\n if (hcDefault === 'h11' || hcDefault === 'h23') {\n hc = 'h11';\n }\n else {\n hc = 'h12';\n }\n }\n else {\n ecma402_abstract_1.invariant(!hour12, 'hour12 must not be set');\n if (hcDefault === 'h11' || hcDefault === 'h23') {\n hc = 'h23';\n }\n else {\n hc = 'h24';\n }\n }\n }\n return hc;\n}\nvar TYPE_REGEX = /^[a-z0-9]{3,8}$/i;\n/**\n * https://tc39.es/ecma402/#sec-initializedatetimeformat\n * @param dtf DateTimeFormat\n * @param locales locales\n * @param opts options\n */\nfunction InitializeDateTimeFormat(dtf, locales, opts, _a) {\n var getInternalSlots = _a.getInternalSlots, availableLocales = _a.availableLocales, localeData = _a.localeData, getDefaultLocale = _a.getDefaultLocale, getDefaultTimeZone = _a.getDefaultTimeZone, relevantExtensionKeys = _a.relevantExtensionKeys, tzData = _a.tzData, uppercaseLinks = _a.uppercaseLinks;\n // @ts-ignore\n var requestedLocales = ecma402_abstract_1.CanonicalizeLocaleList(locales);\n var options = ToDateTimeOptions_1.ToDateTimeOptions(opts, 'any', 'date');\n var opt = Object.create(null);\n var matcher = ecma402_abstract_1.GetOption(options, 'localeMatcher', 'string', ['lookup', 'best fit'], 'best fit');\n opt.localeMatcher = matcher;\n var calendar = ecma402_abstract_1.GetOption(options, 'calendar', 'string', undefined, undefined);\n if (calendar !== undefined && !TYPE_REGEX.test(calendar)) {\n throw new RangeError('Malformed calendar');\n }\n var internalSlots = getInternalSlots(dtf);\n opt.ca = calendar;\n var numberingSystem = ecma402_abstract_1.GetOption(options, 'numberingSystem', 'string', undefined, undefined);\n if (numberingSystem !== undefined && !TYPE_REGEX.test(numberingSystem)) {\n throw new RangeError('Malformed numbering system');\n }\n opt.nu = numberingSystem;\n var hour12 = ecma402_abstract_1.GetOption(options, 'hour12', 'boolean', undefined, undefined);\n var hourCycle = ecma402_abstract_1.GetOption(options, 'hourCycle', 'string', ['h11', 'h12', 'h23', 'h24'], undefined);\n if (hour12 !== undefined) {\n // @ts-ignore\n hourCycle = null;\n }\n opt.hc = hourCycle;\n var r = intl_localematcher_1.ResolveLocale(availableLocales, requestedLocales, opt, relevantExtensionKeys, localeData, getDefaultLocale);\n internalSlots.locale = r.locale;\n calendar = r.ca;\n internalSlots.calendar = calendar;\n internalSlots.hourCycle = r.hc;\n internalSlots.numberingSystem = r.nu;\n var dataLocale = r.dataLocale;\n internalSlots.dataLocale = dataLocale;\n var timeZone = options.timeZone;\n if (timeZone !== undefined) {\n timeZone = String(timeZone);\n if (!ecma402_abstract_1.IsValidTimeZoneName(timeZone, { tzData: tzData, uppercaseLinks: uppercaseLinks })) {\n throw new RangeError('Invalid timeZoneName');\n }\n timeZone = ecma402_abstract_1.CanonicalizeTimeZoneName(timeZone, { tzData: tzData, uppercaseLinks: uppercaseLinks });\n }\n else {\n timeZone = getDefaultTimeZone();\n }\n internalSlots.timeZone = timeZone;\n opt = Object.create(null);\n opt.weekday = ecma402_abstract_1.GetOption(options, 'weekday', 'string', ['narrow', 'short', 'long'], undefined);\n opt.era = ecma402_abstract_1.GetOption(options, 'era', 'string', ['narrow', 'short', 'long'], undefined);\n opt.year = ecma402_abstract_1.GetOption(options, 'year', 'string', ['2-digit', 'numeric'], undefined);\n opt.month = ecma402_abstract_1.GetOption(options, 'month', 'string', ['2-digit', 'numeric', 'narrow', 'short', 'long'], undefined);\n opt.day = ecma402_abstract_1.GetOption(options, 'day', 'string', ['2-digit', 'numeric'], undefined);\n opt.hour = ecma402_abstract_1.GetOption(options, 'hour', 'string', ['2-digit', 'numeric'], undefined);\n opt.minute = ecma402_abstract_1.GetOption(options, 'minute', 'string', ['2-digit', 'numeric'], undefined);\n opt.second = ecma402_abstract_1.GetOption(options, 'second', 'string', ['2-digit', 'numeric'], undefined);\n opt.timeZoneName = ecma402_abstract_1.GetOption(options, 'timeZoneName', 'string', ['short', 'long'], undefined);\n opt.fractionalSecondDigits = ecma402_abstract_1.GetNumberOption(options, 'fractionalSecondDigits', 1, 3, \n // @ts-expect-error\n undefined);\n var dataLocaleData = localeData[dataLocale];\n ecma402_abstract_1.invariant(!!dataLocaleData, \"Missing locale data for \" + dataLocale);\n var formats = dataLocaleData.formats[calendar];\n // UNSPECCED: IMPLEMENTATION DETAILS\n if (!formats) {\n throw new RangeError(\"Calendar \\\"\" + calendar + \"\\\" is not supported. Try setting \\\"calendar\\\" to 1 of the following: \" + Object.keys(dataLocaleData.formats).join(', '));\n }\n var formatMatcher = ecma402_abstract_1.GetOption(options, 'formatMatcher', 'string', ['basic', 'best fit'], 'best fit');\n var dateStyle = ecma402_abstract_1.GetOption(options, 'dateStyle', 'string', ['full', 'long', 'medium', 'short'], undefined);\n internalSlots.dateStyle = dateStyle;\n var timeStyle = ecma402_abstract_1.GetOption(options, 'timeStyle', 'string', ['full', 'long', 'medium', 'short'], undefined);\n internalSlots.timeStyle = timeStyle;\n var bestFormat;\n if (dateStyle === undefined && timeStyle === undefined) {\n if (formatMatcher === 'basic') {\n bestFormat = BasicFormatMatcher_1.BasicFormatMatcher(opt, formats);\n }\n else {\n // IMPL DETAILS START\n if (isTimeRelated(opt)) {\n var hc = resolveHourCycle(internalSlots.hourCycle, dataLocaleData.hourCycle, hour12);\n opt.hour12 = hc === 'h11' || hc === 'h12';\n }\n // IMPL DETAILS END\n bestFormat = BestFitFormatMatcher_1.BestFitFormatMatcher(opt, formats);\n }\n }\n else {\n for (var _i = 0, DATE_TIME_PROPS_1 = utils_1.DATE_TIME_PROPS; _i < DATE_TIME_PROPS_1.length; _i++) {\n var prop = DATE_TIME_PROPS_1[_i];\n var p = opt[prop];\n if (p !== undefined) {\n throw new TypeError(\"Intl.DateTimeFormat can't set option \" + prop + \" when \" + (dateStyle ? 'dateStyle' : 'timeStyle') + \" is used\");\n }\n }\n bestFormat = DateTimeStyleFormat_1.DateTimeStyleFormat(dateStyle, timeStyle, dataLocaleData);\n }\n // IMPL DETAIL START\n // For debugging\n internalSlots.format = bestFormat;\n // IMPL DETAIL END\n for (var prop in opt) {\n var p = bestFormat[prop];\n if (p !== undefined) {\n internalSlots[prop] = p;\n }\n }\n var pattern;\n var rangePatterns;\n if (internalSlots.hour !== undefined) {\n var hc = resolveHourCycle(internalSlots.hourCycle, dataLocaleData.hourCycle, hour12);\n internalSlots.hourCycle = hc;\n if (hc === 'h11' || hc === 'h12') {\n pattern = bestFormat.pattern12;\n rangePatterns = bestFormat.rangePatterns12;\n }\n else {\n pattern = bestFormat.pattern;\n rangePatterns = bestFormat.rangePatterns;\n }\n }\n else {\n // @ts-ignore\n internalSlots.hourCycle = undefined;\n pattern = bestFormat.pattern;\n rangePatterns = bestFormat.rangePatterns;\n }\n internalSlots.pattern = pattern;\n internalSlots.rangePatterns = rangePatterns;\n return dtf;\n}\nexports.InitializeDateTimeFormat = InitializeDateTimeFormat;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BasicFormatMatcher = void 0;\nvar tslib_1 = require(\"tslib\");\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nvar utils_1 = require(\"./utils\");\n/**\n * https://tc39.es/ecma402/#sec-basicformatmatcher\n * @param options\n * @param formats\n */\nfunction BasicFormatMatcher(options, formats) {\n var bestScore = -Infinity;\n var bestFormat = formats[0];\n ecma402_abstract_1.invariant(Array.isArray(formats), 'formats should be a list of things');\n for (var _i = 0, formats_1 = formats; _i < formats_1.length; _i++) {\n var format = formats_1[_i];\n var score = 0;\n for (var _a = 0, DATE_TIME_PROPS_1 = utils_1.DATE_TIME_PROPS; _a < DATE_TIME_PROPS_1.length; _a++) {\n var prop = DATE_TIME_PROPS_1[_a];\n var optionsProp = options[prop];\n var formatProp = format[prop];\n if (optionsProp === undefined && formatProp !== undefined) {\n score -= utils_1.additionPenalty;\n }\n else if (optionsProp !== undefined && formatProp === undefined) {\n score -= utils_1.removalPenalty;\n }\n else if (optionsProp !== formatProp) {\n var values = void 0;\n if (prop === 'fractionalSecondDigits') {\n values = [1, 2, 3];\n }\n else {\n values = ['2-digit', 'numeric', 'narrow', 'short', 'long'];\n }\n var optionsPropIndex = values.indexOf(optionsProp);\n var formatPropIndex = values.indexOf(formatProp);\n var delta = Math.max(-2, Math.min(formatPropIndex - optionsPropIndex, 2));\n if (delta === 2) {\n score -= utils_1.longMorePenalty;\n }\n else if (delta === 1) {\n score -= utils_1.shortMorePenalty;\n }\n else if (delta === -1) {\n score -= utils_1.shortLessPenalty;\n }\n else if (delta === -2) {\n score -= utils_1.longLessPenalty;\n }\n }\n }\n if (score > bestScore) {\n bestScore = score;\n bestFormat = format;\n }\n }\n return tslib_1.__assign({}, bestFormat);\n}\nexports.BasicFormatMatcher = BasicFormatMatcher;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BestFitFormatMatcher = exports.bestFitFormatMatcherScore = void 0;\nvar tslib_1 = require(\"tslib\");\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nvar utils_1 = require(\"./utils\");\nvar skeleton_1 = require(\"./skeleton\");\nfunction isNumericType(t) {\n return t === 'numeric' || t === '2-digit';\n}\n/**\n * Credit: https://github.com/andyearnshaw/Intl.js/blob/0958dc1ad8153f1056653ea22b8208f0df289a4e/src/12.datetimeformat.js#L611\n * with some modifications\n * @param options\n * @param format\n */\nfunction bestFitFormatMatcherScore(options, format) {\n var score = 0;\n if (options.hour12 && !format.hour12) {\n score -= utils_1.removalPenalty;\n }\n else if (!options.hour12 && format.hour12) {\n score -= utils_1.additionPenalty;\n }\n for (var _i = 0, DATE_TIME_PROPS_1 = utils_1.DATE_TIME_PROPS; _i < DATE_TIME_PROPS_1.length; _i++) {\n var prop = DATE_TIME_PROPS_1[_i];\n var optionsProp = options[prop];\n var formatProp = format[prop];\n if (optionsProp === undefined && formatProp !== undefined) {\n score -= utils_1.additionPenalty;\n }\n else if (optionsProp !== undefined && formatProp === undefined) {\n score -= utils_1.removalPenalty;\n }\n else if (optionsProp !== formatProp) {\n // extra penalty for numeric vs non-numeric\n if (isNumericType(optionsProp) !==\n isNumericType(formatProp)) {\n score -= utils_1.differentNumericTypePenalty;\n }\n else {\n var values = ['2-digit', 'numeric', 'narrow', 'short', 'long'];\n var optionsPropIndex = values.indexOf(optionsProp);\n var formatPropIndex = values.indexOf(formatProp);\n var delta = Math.max(-2, Math.min(formatPropIndex - optionsPropIndex, 2));\n if (delta === 2) {\n score -= utils_1.longMorePenalty;\n }\n else if (delta === 1) {\n score -= utils_1.shortMorePenalty;\n }\n else if (delta === -1) {\n score -= utils_1.shortLessPenalty;\n }\n else if (delta === -2) {\n score -= utils_1.longLessPenalty;\n }\n }\n }\n }\n return score;\n}\nexports.bestFitFormatMatcherScore = bestFitFormatMatcherScore;\n/**\n * https://tc39.es/ecma402/#sec-bestfitformatmatcher\n * Just alias to basic for now\n * @param options\n * @param formats\n * @param implDetails Implementation details\n */\nfunction BestFitFormatMatcher(options, formats) {\n var bestScore = -Infinity;\n var bestFormat = formats[0];\n ecma402_abstract_1.invariant(Array.isArray(formats), 'formats should be a list of things');\n for (var _i = 0, formats_1 = formats; _i < formats_1.length; _i++) {\n var format = formats_1[_i];\n var score = bestFitFormatMatcherScore(options, format);\n if (score > bestScore) {\n bestScore = score;\n bestFormat = format;\n }\n }\n var skeletonFormat = tslib_1.__assign({}, bestFormat);\n var patternFormat = { rawPattern: bestFormat.rawPattern };\n skeleton_1.processDateTimePattern(bestFormat.rawPattern, patternFormat);\n // Kinda following https://github.com/unicode-org/icu/blob/dd50e38f459d84e9bf1b0c618be8483d318458ad/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java\n // Method adjustFieldTypes\n for (var prop in skeletonFormat) {\n var skeletonValue = skeletonFormat[prop];\n var patternValue = patternFormat[prop];\n var requestedValue = options[prop];\n // Don't mess with minute/second or we can get in the situation of\n // 7:0:0 which is weird\n if (prop === 'minute' || prop === 'second') {\n continue;\n }\n // Nothing to do here\n if (!requestedValue) {\n continue;\n }\n // https://unicode.org/reports/tr35/tr35-dates.html#Matching_Skeletons\n // Looks like we should not convert numeric to alphabetic but the other way\n // around is ok\n if (isNumericType(patternValue) &&\n !isNumericType(requestedValue)) {\n continue;\n }\n if (skeletonValue === requestedValue) {\n continue;\n }\n patternFormat[prop] = requestedValue;\n }\n // Copy those over\n patternFormat.pattern = skeletonFormat.pattern;\n patternFormat.pattern12 = skeletonFormat.pattern12;\n patternFormat.skeleton = skeletonFormat.skeleton;\n patternFormat.rangePatterns = skeletonFormat.rangePatterns;\n patternFormat.rangePatterns12 = skeletonFormat.rangePatterns12;\n return patternFormat;\n}\nexports.BestFitFormatMatcher = BestFitFormatMatcher;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.DateTimeStyleFormat = void 0;\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\nfunction DateTimeStyleFormat(dateStyle, timeStyle, dataLocaleData) {\n var dateFormat, timeFormat;\n if (timeStyle !== undefined) {\n ecma402_abstract_1.invariant(timeStyle === 'full' ||\n timeStyle === 'long' ||\n timeStyle === 'medium' ||\n timeStyle === 'short', 'invalid timeStyle');\n timeFormat = dataLocaleData.timeFormat[timeStyle];\n }\n if (dateStyle !== undefined) {\n ecma402_abstract_1.invariant(dateStyle === 'full' ||\n dateStyle === 'long' ||\n dateStyle === 'medium' ||\n dateStyle === 'short', 'invalid dateStyle');\n dateFormat = dataLocaleData.dateFormat[dateStyle];\n }\n if (dateStyle !== undefined && timeStyle !== undefined) {\n var format = {};\n for (var field in dateFormat) {\n if (field !== 'pattern') {\n // @ts-ignore\n format[field] = dateFormat[field];\n }\n }\n for (var field in timeFormat) {\n if (field !== 'pattern' && field !== 'pattern12') {\n // @ts-ignore\n format[field] = timeFormat[field];\n }\n }\n var connector = dataLocaleData.dateTimeFormat[dateStyle];\n var pattern = connector\n .replace('{0}', timeFormat.pattern)\n .replace('{1}', dateFormat.pattern);\n format.pattern = pattern;\n if ('pattern12' in timeFormat) {\n var pattern12 = connector\n .replace('{0}', timeFormat.pattern12)\n .replace('{1}', dateFormat.pattern);\n format.pattern12 = pattern12;\n }\n return format;\n }\n if (timeStyle !== undefined) {\n return timeFormat;\n }\n ecma402_abstract_1.invariant(dateStyle !== undefined, 'dateStyle should not be undefined');\n return dateFormat;\n}\nexports.DateTimeStyleFormat = DateTimeStyleFormat;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.FormatDateTimeToParts = void 0;\nvar PartitionDateTimePattern_1 = require(\"./PartitionDateTimePattern\");\nvar ecma402_abstract_1 = require(\"@formatjs/ecma402-abstract\");\n/**\n * https://tc39.es/ecma402/#sec-formatdatetimetoparts\n *\n * @param dtf\n * @param x\n * @param implDetails\n */\nfunction FormatDateTimeToParts(dtf, x, implDetails) {\n var parts = PartitionDateTimePattern_1.PartitionDateTimePattern(dtf, x, implDetails);\n var result = ecma402_abstract_1.ArrayCreate(0);\n for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) {\n var part = parts_1[_i];\n result.push({\n type: part.type,\n value: part.value,\n });\n }\n return result;\n}\nexports.FormatDateTimeToParts = FormatDateTimeToParts;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.FormatDateTimeRangeToParts = void 0;\nvar PartitionDateTimeRangePattern_1 = require(\"./PartitionDateTimeRangePattern\");\nfunction FormatDateTimeRangeToParts(dtf, x, y, implDetails) {\n var parts = PartitionDateTimeRangePattern_1.PartitionDateTimeRangePattern(dtf, x, y, implDetails);\n var result = new Array(0);\n for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) {\n var part = parts_1[_i];\n result.push({\n type: part.type,\n value: part.value,\n source: part.source,\n });\n }\n return result;\n}\nexports.FormatDateTimeRangeToParts = FormatDateTimeRangeToParts;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.FormatDateTimeRange = void 0;\nvar PartitionDateTimeRangePattern_1 = require(\"./PartitionDateTimeRangePattern\");\nfunction FormatDateTimeRange(dtf, x, y, implDetails) {\n var parts = PartitionDateTimeRangePattern_1.PartitionDateTimeRangePattern(dtf, x, y, implDetails);\n var result = '';\n for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) {\n var part = parts_1[_i];\n result += part.value;\n }\n return result;\n}\nexports.FormatDateTimeRange = FormatDateTimeRange;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.shouldPolyfill = void 0;\nfunction supportsDateStyle() {\n try {\n return !!new Intl.DateTimeFormat(undefined, {\n dateStyle: 'short',\n }).resolvedOptions().dateStyle;\n }\n catch (e) {\n return false;\n }\n}\n/**\n * https://bugs.chromium.org/p/chromium/issues/detail?id=865351\n */\nfunction hasChromeLt71Bug() {\n try {\n return (new Intl.DateTimeFormat('en', {\n hourCycle: 'h11',\n hour: 'numeric',\n }).formatToParts(0)[2].type !== 'dayPeriod');\n }\n catch (e) {\n return false;\n }\n}\n/**\n * Node 14's version of Intl.DateTimeFormat does not throw\n * when dateStyle/timeStyle is used with other options.\n * This was fixed in newer V8 versions\n */\nfunction hasUnthrownDateTimeStyleBug() {\n try {\n return !!new Intl.DateTimeFormat('en', {\n dateStyle: 'short',\n hour: 'numeric',\n }).format(new Date(0));\n }\n catch (e) {\n return false;\n }\n}\nfunction supportedLocalesOf(locale) {\n if (!locale) {\n return true;\n }\n var locales = Array.isArray(locale) ? locale : [locale];\n return (Intl.DateTimeFormat.supportedLocalesOf(locales).length === locales.length);\n}\nfunction shouldPolyfill(locale) {\n return (!('DateTimeFormat' in Intl) ||\n !('formatToParts' in Intl.DateTimeFormat.prototype) ||\n !('formatRange' in Intl.DateTimeFormat.prototype) ||\n hasChromeLt71Bug() ||\n hasUnthrownDateTimeStyleBug() ||\n !supportsDateStyle() ||\n !supportedLocalesOf(locale));\n}\nexports.shouldPolyfill = shouldPolyfill;\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.toLocaleTimeString = exports.toLocaleDateString = exports.toLocaleString = void 0;\n// eslint-disable-next-line import/no-cycle\nvar core_1 = require(\"./core\");\nvar ToDateTimeOptions_1 = require(\"./abstract/ToDateTimeOptions\");\n/**\n * Number.prototype.toLocaleString ponyfill\n * https://tc39.es/ecma402/#sup-number.prototype.tolocalestring\n */\nfunction toLocaleString(x, locales, options) {\n var dtf = new core_1.DateTimeFormat(locales, options);\n return dtf.format(x);\n}\nexports.toLocaleString = toLocaleString;\nfunction toLocaleDateString(x, locales, options) {\n var dtf = new core_1.DateTimeFormat(locales, ToDateTimeOptions_1.ToDateTimeOptions(options, 'date', 'date'));\n return dtf.format(x);\n}\nexports.toLocaleDateString = toLocaleDateString;\nfunction toLocaleTimeString(x, locales, options) {\n var dtf = new core_1.DateTimeFormat(locales, ToDateTimeOptions_1.ToDateTimeOptions(options, 'time', 'time'));\n return dtf.format(x);\n}\nexports.toLocaleTimeString = toLocaleTimeString;\n","/* @generated */\t\n\n // prettier-ignore\n if (Intl.DateTimeFormat && typeof Intl.DateTimeFormat.__addLocaleData === 'function') {\n Intl.DateTimeFormat.__addLocaleData({\"data\":{\"am\":\"AM\",\"pm\":\"PM\",\"weekday\":{\"narrow\":[\"S\",\"M\",\"T\",\"W\",\"T\",\"F\",\"S\"],\"short\":[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],\"long\":[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"]},\"era\":{\"narrow\":{\"BC\":\"B\",\"AD\":\"A\"},\"short\":{\"BC\":\"BC\",\"AD\":\"AD\"},\"long\":{\"BC\":\"Before Christ\",\"AD\":\"Anno Domini\"}},\"month\":{\"narrow\":[\"J\",\"F\",\"M\",\"A\",\"M\",\"J\",\"J\",\"A\",\"S\",\"O\",\"N\",\"D\"],\"short\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],\"long\":[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"]},\"timeZoneName\":{\"America/Rio_Branco\":{\"long\":[\"Acre Standard Time\",\"Acre Summer Time\"]},\"Asia/Kabul\":{\"long\":[\"Afghanistan Time\",\"Afghanistan Time\"]},\"Africa/Maputo\":{\"long\":[\"Central Africa Time\",\"Central Africa Time\"]},\"Africa/Bujumbura\":{\"long\":[\"Central Africa Time\",\"Central Africa Time\"]},\"Africa/Gaborone\":{\"long\":[\"Central Africa Time\",\"Central Africa Time\"]},\"Africa/Lubumbashi\":{\"long\":[\"Central Africa Time\",\"Central Africa Time\"]},\"Africa/Blantyre\":{\"long\":[\"Central Africa Time\",\"Central Africa Time\"]},\"Africa/Kigali\":{\"long\":[\"Central Africa Time\",\"Central Africa Time\"]},\"Africa/Lusaka\":{\"long\":[\"Central Africa Time\",\"Central Africa Time\"]},\"Africa/Harare\":{\"long\":[\"Central Africa Time\",\"Central Africa Time\"]},\"Africa/Nairobi\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Africa/Djibouti\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Africa/Asmera\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Africa/Addis_Ababa\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Indian/Comoro\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Indian/Antananarivo\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Africa/Mogadishu\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Africa/Dar_es_Salaam\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Africa/Kampala\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Indian/Mayotte\":{\"long\":[\"East Africa Time\",\"East Africa Time\"]},\"Africa/Johannesburg\":{\"long\":[\"South Africa Standard Time\",\"South Africa Standard Time\"]},\"Africa/Maseru\":{\"long\":[\"South Africa Standard Time\",\"South Africa Standard Time\"]},\"Africa/Mbabane\":{\"long\":[\"South Africa Standard Time\",\"South Africa Standard Time\"]},\"Africa/Lagos\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Luanda\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Porto-Novo\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Kinshasa\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Bangui\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Brazzaville\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Douala\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Libreville\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Malabo\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Niamey\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Africa/Ndjamena\":{\"long\":[\"West Africa Standard Time\",\"West Africa Summer Time\"]},\"Asia/Aqtobe\":{\"long\":[\"West Kazakhstan Time\",\"West Kazakhstan Time\"]},\"America/Juneau\":{\"long\":[\"Alaska Standard Time\",\"Alaska Daylight Time\"],\"short\":[\"AKST\",\"AKDT\"]},\"Asia/Almaty\":{\"long\":[\"East Kazakhstan Time\",\"East Kazakhstan Time\"]},\"America/Manaus\":{\"long\":[\"Amazon Standard Time\",\"Amazon Summer Time\"]},\"America/Chicago\":{\"long\":[\"Central Standard Time\",\"Central Daylight Time\"],\"short\":[\"CST\",\"CDT\"]},\"America/Belize\":{\"long\":[\"Central Standard Time\",\"Central Daylight Time\"],\"short\":[\"CST\",\"CDT\"]},\"America/Winnipeg\":{\"long\":[\"Central Standard Time\",\"Central Daylight Time\"],\"short\":[\"CST\",\"CDT\"]},\"America/Costa_Rica\":{\"long\":[\"Central Standard Time\",\"Central Daylight Time\"],\"short\":[\"CST\",\"CDT\"]},\"America/Guatemala\":{\"long\":[\"Central Standard Time\",\"Central Daylight Time\"],\"short\":[\"CST\",\"CDT\"]},\"America/Tegucigalpa\":{\"long\":[\"Central Standard Time\",\"Central Daylight Time\"],\"short\":[\"CST\",\"CDT\"]},\"America/Mexico_City\":{\"long\":[\"Central Standard Time\",\"Central Daylight Time\"],\"short\":[\"CST\",\"CDT\"]},\"America/El_Salvador\":{\"long\":[\"Central Standard Time\",\"Central Daylight Time\"],\"short\":[\"CST\",\"CDT\"]},\"America/New_York\":{\"long\":[\"Eastern Standard Time\",\"Eastern Daylight Time\"],\"short\":[\"EST\",\"EDT\"]},\"America/Nassau\":{\"long\":[\"Eastern Standard Time\",\"Eastern Daylight Time\"],\"short\":[\"EST\",\"EDT\"]},\"America/Toronto\":{\"long\":[\"Eastern Standard Time\",\"Eastern Daylight Time\"],\"short\":[\"EST\",\"EDT\"]},\"America/Port-au-Prince\":{\"long\":[\"Eastern Standard Time\",\"Eastern Daylight Time\"],\"short\":[\"EST\",\"EDT\"]},\"America/Jamaica\":{\"long\":[\"Eastern Standard Time\",\"Eastern Daylight Time\"],\"short\":[\"EST\",\"EDT\"]},\"America/Cayman\":{\"long\":[\"Eastern Standard Time\",\"Eastern Daylight Time\"],\"short\":[\"EST\",\"EDT\"]},\"America/Panama\":{\"long\":[\"Eastern Standard Time\",\"Eastern Daylight Time\"],\"short\":[\"EST\",\"EDT\"]},\"America/Denver\":{\"long\":[\"Mountain Standard Time\",\"Mountain Daylight Time\"],\"short\":[\"MST\",\"MDT\"]},\"America/Edmonton\":{\"long\":[\"Mountain Standard Time\",\"Mountain Daylight Time\"],\"short\":[\"MST\",\"MDT\"]},\"America/Hermosillo\":{\"long\":[\"Mountain Standard Time\",\"Mountain Daylight Time\"],\"short\":[\"MST\",\"MDT\"]},\"America/Los_Angeles\":{\"long\":[\"Pacific Standard Time\",\"Pacific Daylight Time\"],\"short\":[\"PST\",\"PDT\"]},\"America/Vancouver\":{\"long\":[\"Pacific Standard Time\",\"Pacific Daylight Time\"],\"short\":[\"PST\",\"PDT\"]},\"America/Tijuana\":{\"long\":[\"Pacific Standard Time\",\"Pacific Daylight Time\"],\"short\":[\"PST\",\"PDT\"]},\"Asia/Anadyr\":{\"long\":[\"Anadyr Standard Time\",\"Anadyr Summer Time\"]},\"Pacific/Apia\":{\"long\":[\"Apia Standard Time\",\"Apia Daylight Time\"]},\"Asia/Riyadh\":{\"long\":[\"Arabian Standard Time\",\"Arabian Daylight Time\"]},\"Asia/Bahrain\":{\"long\":[\"Arabian Standard Time\",\"Arabian Daylight Time\"]},\"Asia/Baghdad\":{\"long\":[\"Arabian Standard Time\",\"Arabian Daylight Time\"]},\"Asia/Kuwait\":{\"long\":[\"Arabian Standard Time\",\"Arabian Daylight Time\"]},\"Asia/Qatar\":{\"long\":[\"Arabian Standard Time\",\"Arabian Daylight Time\"]},\"Asia/Aden\":{\"long\":[\"Arabian Standard Time\",\"Arabian Daylight Time\"]},\"America/Buenos_Aires\":{\"long\":[\"Argentina Standard Time\",\"Argentina Summer Time\"]},\"America/Argentina/San_Luis\":{\"long\":[\"Western Argentina Standard Time\",\"Western Argentina Summer Time\"]},\"Asia/Ashgabat\":{\"long\":[\"Turkmenistan Standard Time\",\"Turkmenistan Summer Time\"]},\"America/Halifax\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Antigua\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Anguilla\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Aruba\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Barbados\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"Atlantic/Bermuda\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Kralendijk\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Curacao\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Dominica\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Grenada\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Thule\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Guadeloupe\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/St_Kitts\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/St_Lucia\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Marigot\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Martinique\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Montserrat\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Puerto_Rico\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Lower_Princes\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Port_of_Spain\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/St_Vincent\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/Tortola\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"America/St_Thomas\":{\"long\":[\"Atlantic Standard Time\",\"Atlantic Daylight Time\"],\"short\":[\"AST\",\"ADT\"]},\"Australia/Adelaide\":{\"long\":[\"Australian Central Standard Time\",\"Australian Central Daylight Time\"]},\"Australia/Eucla\":{\"long\":[\"Australian Central Western Standard Time\",\"Australian Central Western Daylight Time\"]},\"Australia/Sydney\":{\"long\":[\"Australian Eastern Standard Time\",\"Australian Eastern Daylight Time\"]},\"Australia/Perth\":{\"long\":[\"Australian Western Standard Time\",\"Australian Western Daylight Time\"]},\"Atlantic/Azores\":{\"long\":[\"Azores Standard Time\",\"Azores Summer Time\"]},\"Asia/Thimphu\":{\"long\":[\"Bhutan Time\",\"Bhutan Time\"]},\"America/La_Paz\":{\"long\":[\"Bolivia Time\",\"Bolivia Time\"]},\"Asia/Kuching\":{\"long\":[\"Malaysia Time\",\"Malaysia Time\"]},\"America/Sao_Paulo\":{\"long\":[\"Brasilia Standard Time\",\"Brasilia Summer Time\"]},\"Europe/London\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Asia/Brunei\":{\"long\":[\"Brunei Darussalam Time\",\"Brunei Darussalam Time\"]},\"Atlantic/Cape_Verde\":{\"long\":[\"Cape Verde Standard Time\",\"Cape Verde Summer Time\"]},\"Antarctica/Casey\":{\"long\":[\"Casey Time\",\"Casey Time\"]},\"Pacific/Saipan\":{\"long\":[\"North Mariana Islands Time\",\"North Mariana Islands Time\"]},\"Pacific/Guam\":{\"long\":[\"Guam Standard Time\",\"Guam Standard Time\"]},\"Pacific/Chatham\":{\"long\":[\"Chatham Standard Time\",\"Chatham Daylight Time\"]},\"America/Santiago\":{\"long\":[\"Chile Standard Time\",\"Chile Summer Time\"]},\"Asia/Shanghai\":{\"long\":[\"China Standard Time\",\"China Daylight Time\"]},\"Asia/Choibalsan\":{\"long\":[\"Choibalsan Standard Time\",\"Choibalsan Summer Time\"]},\"Indian/Christmas\":{\"long\":[\"Christmas Island Time\",\"Christmas Island Time\"]},\"Indian/Cocos\":{\"long\":[\"Cocos Islands Time\",\"Cocos Islands Time\"]},\"America/Bogota\":{\"long\":[\"Colombia Standard Time\",\"Colombia Summer Time\"]},\"Pacific/Rarotonga\":{\"long\":[\"Cook Islands Standard Time\",\"Cook Islands Half Summer Time\"]},\"America/Havana\":{\"long\":[\"Cuba Standard Time\",\"Cuba Daylight Time\"]},\"Antarctica/Davis\":{\"long\":[\"Davis Time\",\"Davis Time\"]},\"Antarctica/DumontDUrville\":{\"long\":[\"Dumont-d’Urville Time\",\"Dumont-d’Urville Time\"]},\"Asia/Dushanbe\":{\"long\":[\"Tajikistan Time\",\"Tajikistan Time\"]},\"America/Paramaribo\":{\"long\":[\"Suriname Time\",\"Suriname Time\"]},\"Asia/Dili\":{\"long\":[\"East Timor Time\",\"East Timor Time\"]},\"Pacific/Easter\":{\"long\":[\"Easter Island Standard Time\",\"Easter Island Summer Time\"]},\"America/Guayaquil\":{\"long\":[\"Ecuador Time\",\"Ecuador Time\"]},\"Europe/Paris\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Andorra\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Tirane\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Vienna\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Sarajevo\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Brussels\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Zurich\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Prague\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Berlin\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Copenhagen\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Madrid\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Gibraltar\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Zagreb\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Budapest\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Rome\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Vaduz\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Luxembourg\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Monaco\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Podgorica\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Skopje\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Malta\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Amsterdam\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Oslo\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Warsaw\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Belgrade\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Stockholm\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Ljubljana\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Arctic/Longyearbyen\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Bratislava\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/San_Marino\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Africa/Tunis\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Vatican\":{\"long\":[\"Central European Standard Time\",\"Central European Summer Time\"]},\"Europe/Bucharest\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Europe/Mariehamn\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Europe/Sofia\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Asia/Nicosia\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Africa/Cairo\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Europe/Helsinki\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Europe/Athens\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Asia/Amman\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Asia/Beirut\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Asia/Damascus\":{\"long\":[\"Eastern European Standard Time\",\"Eastern European Summer Time\"]},\"Europe/Minsk\":{\"long\":[\"Further-eastern European Time\",\"Further-eastern European Time\"]},\"Europe/Kaliningrad\":{\"long\":[\"Further-eastern European Time\",\"Further-eastern European Time\"]},\"Atlantic/Canary\":{\"long\":[\"Western European Standard Time\",\"Western European Summer Time\"]},\"Atlantic/Faeroe\":{\"long\":[\"Western European Standard Time\",\"Western European Summer Time\"]},\"Atlantic/Stanley\":{\"long\":[\"Falkland Islands Standard Time\",\"Falkland Islands Summer Time\"]},\"Pacific/Fiji\":{\"long\":[\"Fiji Standard Time\",\"Fiji Summer Time\"]},\"America/Cayenne\":{\"long\":[\"French Guiana Time\",\"French Guiana Time\"]},\"Indian/Kerguelen\":{\"long\":[\"French Southern & Antarctic Time\",\"French Southern & Antarctic Time\"]},\"Asia/Bishkek\":{\"long\":[\"Kyrgyzstan Time\",\"Kyrgyzstan Time\"]},\"Pacific/Galapagos\":{\"long\":[\"Galapagos Time\",\"Galapagos Time\"]},\"Pacific/Gambier\":{\"long\":[\"Gambier Time\",\"Gambier Time\"]},\"Pacific/Tarawa\":{\"long\":[\"Gilbert Islands Time\",\"Gilbert Islands Time\"]},\"Atlantic/Reykjavik\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Ouagadougou\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Abidjan\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Accra\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Banjul\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Conakry\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Bamako\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Nouakchott\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Atlantic/St_Helena\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Freetown\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Dakar\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"Africa/Lome\":{\"long\":[\"Greenwich Mean Time\",\"Greenwich Mean Time\"],\"short\":[\"GMT\",\"GMT\"]},\"America/Scoresbysund\":{\"long\":[\"East Greenland Standard Time\",\"East Greenland Summer Time\"]},\"America/Godthab\":{\"long\":[\"West Greenland Standard Time\",\"West Greenland Summer Time\"]},\"Asia/Dubai\":{\"long\":[\"Gulf Standard Time\",\"Gulf Standard Time\"]},\"Asia/Muscat\":{\"long\":[\"Gulf Standard Time\",\"Gulf Standard Time\"]},\"America/Guyana\":{\"long\":[\"Guyana Time\",\"Guyana Time\"]},\"Pacific/Honolulu\":{\"long\":[\"Hawaii-Aleutian Standard Time\",\"Hawaii-Aleutian Daylight Time\"],\"short\":[\"HAST\",\"HADT\"]},\"Asia/Hong_Kong\":{\"long\":[\"Hong Kong Standard Time\",\"Hong Kong Summer Time\"]},\"Asia/Hovd\":{\"long\":[\"Hovd Standard Time\",\"Hovd Summer Time\"]},\"Asia/Calcutta\":{\"long\":[\"India Standard Time\",\"India Standard Time\"]},\"Asia/Colombo\":{\"long\":[\"Lanka Time\",\"Lanka Time\"]},\"Indian/Chagos\":{\"long\":[\"Indian Ocean Time\",\"Indian Ocean Time\"]},\"Asia/Bangkok\":{\"long\":[\"Indochina Time\",\"Indochina Time\"]},\"Asia/Phnom_Penh\":{\"long\":[\"Indochina Time\",\"Indochina Time\"]},\"Asia/Vientiane\":{\"long\":[\"Indochina Time\",\"Indochina Time\"]},\"Asia/Makassar\":{\"long\":[\"Central Indonesia Time\",\"Central Indonesia Time\"]},\"Asia/Jayapura\":{\"long\":[\"Eastern Indonesia Time\",\"Eastern Indonesia Time\"]},\"Asia/Jakarta\":{\"long\":[\"Western Indonesia Time\",\"Western Indonesia Time\"]},\"Asia/Tehran\":{\"long\":[\"Iran Standard Time\",\"Iran Daylight Time\"]},\"Asia/Irkutsk\":{\"long\":[\"Irkutsk Standard Time\",\"Irkutsk Summer Time\"]},\"Asia/Jerusalem\":{\"long\":[\"Israel Standard Time\",\"Israel Daylight Time\"]},\"Asia/Tokyo\":{\"long\":[\"Japan Standard Time\",\"Japan Daylight Time\"]},\"Asia/Kamchatka\":{\"long\":[\"Petropavlovsk-Kamchatski Standard Time\",\"Petropavlovsk-Kamchatski Summer Time\"]},\"Asia/Karachi\":{\"long\":[\"Pakistan Standard Time\",\"Pakistan Summer Time\"]},\"Asia/Qyzylorda\":{\"long\":[\"Qyzylorda Standard Time\",\"Qyzylorda Summer Time\"]},\"Asia/Seoul\":{\"long\":[\"Korean Standard Time\",\"Korean Daylight Time\"]},\"Pacific/Kosrae\":{\"long\":[\"Kosrae Time\",\"Kosrae Time\"]},\"Asia/Krasnoyarsk\":{\"long\":[\"Krasnoyarsk Standard Time\",\"Krasnoyarsk Summer Time\"]},\"Europe/Samara\":{\"long\":[\"Samara Standard Time\",\"Samara Summer Time\"]},\"Pacific/Kiritimati\":{\"long\":[\"Line Islands Time\",\"Line Islands Time\"]},\"Australia/Lord_Howe\":{\"long\":[\"Lord Howe Standard Time\",\"Lord Howe Daylight Time\"]},\"Asia/Macau\":{\"long\":[\"Macao Standard Time\",\"Macao Summer Time\"]},\"Antarctica/Macquarie\":{\"long\":[\"Macquarie Island Time\",\"Macquarie Island Time\"]},\"Asia/Magadan\":{\"long\":[\"Magadan Standard Time\",\"Magadan Summer Time\"]},\"Indian/Maldives\":{\"long\":[\"Maldives Time\",\"Maldives Time\"]},\"Pacific/Marquesas\":{\"long\":[\"Marquesas Time\",\"Marquesas Time\"]},\"Pacific/Majuro\":{\"long\":[\"Marshall Islands Time\",\"Marshall Islands Time\"]},\"Indian/Mauritius\":{\"long\":[\"Mauritius Standard Time\",\"Mauritius Summer Time\"]},\"Antarctica/Mawson\":{\"long\":[\"Mawson Time\",\"Mawson Time\"]},\"America/Santa_Isabel\":{\"long\":[\"Northwest Mexico Standard Time\",\"Northwest Mexico Daylight Time\"]},\"America/Mazatlan\":{\"long\":[\"Mexican Pacific Standard Time\",\"Mexican Pacific Daylight Time\"]},\"Asia/Ulaanbaatar\":{\"long\":[\"Ulaanbaatar Standard Time\",\"Ulaanbaatar Summer Time\"]},\"Europe/Moscow\":{\"long\":[\"Moscow Standard Time\",\"Moscow Summer Time\"]},\"Asia/Rangoon\":{\"long\":[\"Myanmar Time\",\"Myanmar Time\"]},\"Pacific/Nauru\":{\"long\":[\"Nauru Time\",\"Nauru Time\"]},\"Asia/Katmandu\":{\"long\":[\"Nepal Time\",\"Nepal Time\"]},\"Pacific/Noumea\":{\"long\":[\"New Caledonia Standard Time\",\"New Caledonia Summer Time\"]},\"Pacific/Auckland\":{\"long\":[\"New Zealand Standard Time\",\"New Zealand Daylight Time\"]},\"Antarctica/McMurdo\":{\"long\":[\"New Zealand Standard Time\",\"New Zealand Daylight Time\"]},\"America/St_Johns\":{\"long\":[\"Newfoundland Standard Time\",\"Newfoundland Daylight Time\"]},\"Pacific/Niue\":{\"long\":[\"Niue Time\",\"Niue Time\"]},\"Pacific/Norfolk\":{\"long\":[\"Norfolk Island Standard Time\",\"Norfolk Island Daylight Time\"]},\"America/Noronha\":{\"long\":[\"Fernando de Noronha Standard Time\",\"Fernando de Noronha Summer Time\"]},\"Asia/Novosibirsk\":{\"long\":[\"Novosibirsk Standard Time\",\"Novosibirsk Summer Time\"]},\"Asia/Omsk\":{\"long\":[\"Omsk Standard Time\",\"Omsk Summer Time\"]},\"Pacific/Palau\":{\"long\":[\"Palau Time\",\"Palau Time\"]},\"Pacific/Port_Moresby\":{\"long\":[\"Papua New Guinea Time\",\"Papua New Guinea Time\"]},\"America/Asuncion\":{\"long\":[\"Paraguay Standard Time\",\"Paraguay Summer Time\"]},\"America/Lima\":{\"long\":[\"Peru Standard Time\",\"Peru Summer Time\"]},\"Asia/Manila\":{\"long\":[\"Philippine Standard Time\",\"Philippine Summer Time\"]},\"Pacific/Enderbury\":{\"long\":[\"Phoenix Islands Time\",\"Phoenix Islands Time\"]},\"America/Miquelon\":{\"long\":[\"St. Pierre & Miquelon Standard Time\",\"St. Pierre & Miquelon Daylight Time\"]},\"Pacific/Pitcairn\":{\"long\":[\"Pitcairn Time\",\"Pitcairn Time\"]},\"Pacific/Ponape\":{\"long\":[\"Ponape Time\",\"Ponape Time\"]},\"Asia/Pyongyang\":{\"long\":[\"Pyongyang Time\",\"Pyongyang Time\"]},\"Indian/Reunion\":{\"long\":[\"Réunion Time\",\"Réunion Time\"]},\"Antarctica/Rothera\":{\"long\":[\"Rothera Time\",\"Rothera Time\"]},\"Asia/Sakhalin\":{\"long\":[\"Sakhalin Standard Time\",\"Sakhalin Summer Time\"]},\"Pacific/Pago_Pago\":{\"long\":[\"Samoa Standard Time\",\"Samoa Daylight Time\"]},\"Indian/Mahe\":{\"long\":[\"Seychelles Time\",\"Seychelles Time\"]},\"Asia/Singapore\":{\"long\":[\"Singapore Standard Time\",\"Singapore Standard Time\"]},\"Pacific/Guadalcanal\":{\"long\":[\"Solomon Islands Time\",\"Solomon Islands Time\"]},\"Atlantic/South_Georgia\":{\"long\":[\"South Georgia Time\",\"South Georgia Time\"]},\"Asia/Yekaterinburg\":{\"long\":[\"Yekaterinburg Standard Time\",\"Yekaterinburg Summer Time\"]},\"Antarctica/Syowa\":{\"long\":[\"Syowa Time\",\"Syowa Time\"]},\"Pacific/Tahiti\":{\"long\":[\"Tahiti Time\",\"Tahiti Time\"]},\"Asia/Taipei\":{\"long\":[\"Taipei Standard Time\",\"Taipei Daylight Time\"]},\"Asia/Tashkent\":{\"long\":[\"Uzbekistan Standard Time\",\"Uzbekistan Summer Time\"]},\"Pacific/Fakaofo\":{\"long\":[\"Tokelau Time\",\"Tokelau Time\"]},\"Pacific/Tongatapu\":{\"long\":[\"Tonga Standard Time\",\"Tonga Summer Time\"]},\"Pacific/Truk\":{\"long\":[\"Chuuk Time\",\"Chuuk Time\"]},\"Pacific/Funafuti\":{\"long\":[\"Tuvalu Time\",\"Tuvalu Time\"]},\"America/Montevideo\":{\"long\":[\"Uruguay Standard Time\",\"Uruguay Summer Time\"]},\"Pacific/Efate\":{\"long\":[\"Vanuatu Standard Time\",\"Vanuatu Summer Time\"]},\"America/Caracas\":{\"long\":[\"Venezuela Time\",\"Venezuela Time\"]},\"Asia/Vladivostok\":{\"long\":[\"Vladivostok Standard Time\",\"Vladivostok Summer Time\"]},\"Europe/Volgograd\":{\"long\":[\"Volgograd Standard Time\",\"Volgograd Summer Time\"]},\"Antarctica/Vostok\":{\"long\":[\"Vostok Time\",\"Vostok Time\"]},\"Pacific/Wake\":{\"long\":[\"Wake Island Time\",\"Wake Island Time\"]},\"Pacific/Wallis\":{\"long\":[\"Wallis & Futuna Time\",\"Wallis & Futuna Time\"]},\"Asia/Yakutsk\":{\"long\":[\"Yakutsk Standard Time\",\"Yakutsk Summer Time\"]},\"UTC\":{\"long\":[\"Coordinated Universal Time\",\"Coordinated Universal Time\"],\"short\":[\"UTC\",\"UTC\"]}},\"gmtFormat\":\"GMT{0}\",\"hourFormat\":\"+HH:mm;-HH:mm\",\"dateFormat\":{\"full\":\"EEEE, MMMM d, y\",\"long\":\"MMMM d, y\",\"medium\":\"MMM d, y\",\"short\":\"M/d/yy\"},\"timeFormat\":{\"full\":\"h:mm:ss a zzzz\",\"long\":\"h:mm:ss a z\",\"medium\":\"h:mm:ss a\",\"short\":\"h:mm a\"},\"dateTimeFormat\":{\"full\":\"{1} 'at' {0}\",\"long\":\"{1} 'at' {0}\",\"medium\":\"{1}, {0}\",\"short\":\"{1}, {0}\"},\"formats\":{\"gregory\":{\"Bh\":\"h B\",\"Bhm\":\"h:mm B\",\"Bhms\":\"h:mm:ss B\",\"d\":\"d\",\"E\":\"ccc\",\"EBhm\":\"E h:mm B\",\"EBhms\":\"E h:mm:ss B\",\"Ed\":\"d E\",\"Ehm\":\"E h:mm a\",\"EHm\":\"E HH:mm\",\"Ehms\":\"E h:mm:ss a\",\"EHms\":\"E HH:mm:ss\",\"Gy\":\"y G\",\"GyMMM\":\"MMM y G\",\"GyMMMd\":\"MMM d, y G\",\"GyMMMEd\":\"E, MMM d, y G\",\"h\":\"h a\",\"H\":\"HH\",\"hm\":\"h:mm a\",\"Hm\":\"HH:mm\",\"hms\":\"h:mm:ss a\",\"Hms\":\"HH:mm:ss\",\"hmsv\":\"h:mm:ss a v\",\"Hmsv\":\"HH:mm:ss v\",\"hmv\":\"h:mm a v\",\"Hmv\":\"HH:mm v\",\"M\":\"L\",\"Md\":\"M/d\",\"MEd\":\"E, M/d\",\"MMM\":\"LLL\",\"MMMd\":\"MMM d\",\"MMMEd\":\"E, MMM d\",\"MMMMd\":\"MMMM d\",\"ms\":\"mm:ss\",\"y\":\"y\",\"yM\":\"M/y\",\"yMd\":\"M/d/y\",\"yMEd\":\"E, M/d/y\",\"yMMM\":\"MMM y\",\"yMMMd\":\"MMM d, y\",\"yMMMEd\":\"E, MMM d, y\",\"yMMMM\":\"MMMM y\",\"EEEE, MMMM d, y\":\"EEEE, MMMM d, y\",\"MMMM d, y\":\"MMMM d, y\",\"MMM d, y\":\"MMM d, y\",\"M/d/yy\":\"M/d/yy\",\"h:mm:ss a zzzz\":\"h:mm:ss a zzzz\",\"h:mm:ss a z\":\"h:mm:ss a z\",\"h:mm:ss a\":\"h:mm:ss a\",\"h:mm a\":\"h:mm a\",\"EEEE, MMMM d, y 'at' h:mm:ss a zzzz\":\"EEEE, MMMM d, y 'at' h:mm:ss a zzzz\",\"MMMM d, y 'at' h:mm:ss a zzzz\":\"MMMM d, y 'at' h:mm:ss a zzzz\",\"MMM d, y, h:mm:ss a zzzz\":\"MMM d, y, h:mm:ss a zzzz\",\"M/d/yy, h:mm:ss a zzzz\":\"M/d/yy, h:mm:ss a zzzz\",\"d, h:mm:ss a zzzz\":\"d, h:mm:ss a zzzz\",\"E, h:mm:ss a zzzz\":\"ccc, h:mm:ss a zzzz\",\"Ed, h:mm:ss a zzzz\":\"d E, h:mm:ss a zzzz\",\"Gy, h:mm:ss a zzzz\":\"y G, h:mm:ss a zzzz\",\"GyMMM, h:mm:ss a zzzz\":\"MMM y G, h:mm:ss a zzzz\",\"GyMMMd, h:mm:ss a zzzz\":\"MMM d, y G, h:mm:ss a zzzz\",\"GyMMMEd, h:mm:ss a zzzz\":\"E, MMM d, y G, h:mm:ss a zzzz\",\"M, h:mm:ss a zzzz\":\"L, h:mm:ss a zzzz\",\"Md, h:mm:ss a zzzz\":\"M/d, h:mm:ss a zzzz\",\"MEd, h:mm:ss a zzzz\":\"E, M/d, h:mm:ss a zzzz\",\"MMM, h:mm:ss a zzzz\":\"LLL, h:mm:ss a zzzz\",\"MMMd, h:mm:ss a zzzz\":\"MMM d, h:mm:ss a zzzz\",\"MMMEd, h:mm:ss a zzzz\":\"E, MMM d, h:mm:ss a zzzz\",\"MMMMd 'at' h:mm:ss a zzzz\":\"MMMM d 'at' h:mm:ss a zzzz\",\"y, h:mm:ss a zzzz\":\"y, h:mm:ss a zzzz\",\"yM, h:mm:ss a zzzz\":\"M/y, h:mm:ss a zzzz\",\"yMd, h:mm:ss a zzzz\":\"M/d/y, h:mm:ss a zzzz\",\"yMEd, h:mm:ss a zzzz\":\"E, M/d/y, h:mm:ss a zzzz\",\"yMMM, h:mm:ss a zzzz\":\"MMM y, h:mm:ss a zzzz\",\"yMMMd, h:mm:ss a zzzz\":\"MMM d, y, h:mm:ss a zzzz\",\"yMMMEd, h:mm:ss a zzzz\":\"E, MMM d, y, h:mm:ss a zzzz\",\"yMMMM 'at' h:mm:ss a zzzz\":\"MMMM y 'at' h:mm:ss a zzzz\",\"EEEE, MMMM d, y 'at' h:mm:ss a z\":\"EEEE, MMMM d, y 'at' h:mm:ss a z\",\"MMMM d, y 'at' h:mm:ss a z\":\"MMMM d, y 'at' h:mm:ss a z\",\"MMM d, y, h:mm:ss a z\":\"MMM d, y, h:mm:ss a z\",\"M/d/yy, h:mm:ss a z\":\"M/d/yy, h:mm:ss a z\",\"d, h:mm:ss a z\":\"d, h:mm:ss a z\",\"E, h:mm:ss a z\":\"ccc, h:mm:ss a z\",\"Ed, h:mm:ss a z\":\"d E, h:mm:ss a z\",\"Gy, h:mm:ss a z\":\"y G, h:mm:ss a z\",\"GyMMM, h:mm:ss a z\":\"MMM y G, h:mm:ss a z\",\"GyMMMd, h:mm:ss a z\":\"MMM d, y G, h:mm:ss a z\",\"GyMMMEd, h:mm:ss a z\":\"E, MMM d, y G, h:mm:ss a z\",\"M, h:mm:ss a z\":\"L, h:mm:ss a z\",\"Md, h:mm:ss a z\":\"M/d, h:mm:ss a z\",\"MEd, h:mm:ss a z\":\"E, M/d, h:mm:ss a z\",\"MMM, h:mm:ss a z\":\"LLL, h:mm:ss a z\",\"MMMd, h:mm:ss a z\":\"MMM d, h:mm:ss a z\",\"MMMEd, h:mm:ss a z\":\"E, MMM d, h:mm:ss a z\",\"MMMMd 'at' h:mm:ss a z\":\"MMMM d 'at' h:mm:ss a z\",\"y, h:mm:ss a z\":\"y, h:mm:ss a z\",\"yM, h:mm:ss a z\":\"M/y, h:mm:ss a z\",\"yMd, h:mm:ss a z\":\"M/d/y, h:mm:ss a z\",\"yMEd, h:mm:ss a z\":\"E, M/d/y, h:mm:ss a z\",\"yMMM, h:mm:ss a z\":\"MMM y, h:mm:ss a z\",\"yMMMd, h:mm:ss a z\":\"MMM d, y, h:mm:ss a z\",\"yMMMEd, h:mm:ss a z\":\"E, MMM d, y, h:mm:ss a z\",\"yMMMM 'at' h:mm:ss a z\":\"MMMM y 'at' h:mm:ss a z\",\"EEEE, MMMM d, y 'at' h:mm:ss a\":\"EEEE, MMMM d, y 'at' h:mm:ss a\",\"MMMM d, y 'at' h:mm:ss a\":\"MMMM d, y 'at' h:mm:ss a\",\"MMM d, y, h:mm:ss a\":\"MMM d, y, h:mm:ss a\",\"M/d/yy, h:mm:ss a\":\"M/d/yy, h:mm:ss a\",\"d, h:mm:ss a\":\"d, h:mm:ss a\",\"E, h:mm:ss a\":\"ccc, h:mm:ss a\",\"Ed, h:mm:ss a\":\"d E, h:mm:ss a\",\"Gy, h:mm:ss a\":\"y G, h:mm:ss a\",\"GyMMM, h:mm:ss a\":\"MMM y G, h:mm:ss a\",\"GyMMMd, h:mm:ss a\":\"MMM d, y G, h:mm:ss a\",\"GyMMMEd, h:mm:ss a\":\"E, MMM d, y G, h:mm:ss a\",\"M, h:mm:ss a\":\"L, h:mm:ss a\",\"Md, h:mm:ss a\":\"M/d, h:mm:ss a\",\"MEd, h:mm:ss a\":\"E, M/d, h:mm:ss a\",\"MMM, h:mm:ss a\":\"LLL, h:mm:ss a\",\"MMMd, h:mm:ss a\":\"MMM d, h:mm:ss a\",\"MMMEd, h:mm:ss a\":\"E, MMM d, h:mm:ss a\",\"MMMMd 'at' h:mm:ss a\":\"MMMM d 'at' h:mm:ss a\",\"y, h:mm:ss a\":\"y, h:mm:ss a\",\"yM, h:mm:ss a\":\"M/y, h:mm:ss a\",\"yMd, h:mm:ss a\":\"M/d/y, h:mm:ss a\",\"yMEd, h:mm:ss a\":\"E, M/d/y, h:mm:ss a\",\"yMMM, h:mm:ss a\":\"MMM y, h:mm:ss a\",\"yMMMd, h:mm:ss a\":\"MMM d, y, h:mm:ss a\",\"yMMMEd, h:mm:ss a\":\"E, MMM d, y, h:mm:ss a\",\"yMMMM 'at' h:mm:ss a\":\"MMMM y 'at' h:mm:ss a\",\"EEEE, MMMM d, y 'at' h:mm a\":\"EEEE, MMMM d, y 'at' h:mm a\",\"MMMM d, y 'at' h:mm a\":\"MMMM d, y 'at' h:mm a\",\"MMM d, y, h:mm a\":\"MMM d, y, h:mm a\",\"M/d/yy, h:mm a\":\"M/d/yy, h:mm a\",\"d, h:mm a\":\"d, h:mm a\",\"E, h:mm a\":\"ccc, h:mm a\",\"Ed, h:mm a\":\"d E, h:mm a\",\"Gy, h:mm a\":\"y G, h:mm a\",\"GyMMM, h:mm a\":\"MMM y G, h:mm a\",\"GyMMMd, h:mm a\":\"MMM d, y G, h:mm a\",\"GyMMMEd, h:mm a\":\"E, MMM d, y G, h:mm a\",\"M, h:mm a\":\"L, h:mm a\",\"Md, h:mm a\":\"M/d, h:mm a\",\"MEd, h:mm a\":\"E, M/d, h:mm a\",\"MMM, h:mm a\":\"LLL, h:mm a\",\"MMMd, h:mm a\":\"MMM d, h:mm a\",\"MMMEd, h:mm a\":\"E, MMM d, h:mm a\",\"MMMMd 'at' h:mm a\":\"MMMM d 'at' h:mm a\",\"y, h:mm a\":\"y, h:mm a\",\"yM, h:mm a\":\"M/y, h:mm a\",\"yMd, h:mm a\":\"M/d/y, h:mm a\",\"yMEd, h:mm a\":\"E, M/d/y, h:mm a\",\"yMMM, h:mm a\":\"MMM y, h:mm a\",\"yMMMd, h:mm a\":\"MMM d, y, h:mm a\",\"yMMMEd, h:mm a\":\"E, MMM d, y, h:mm a\",\"yMMMM 'at' h:mm a\":\"MMMM y 'at' h:mm a\",\"EEEE, MMMM d, y 'at' Bh\":\"EEEE, MMMM d, y 'at' h B\",\"MMMM d, y 'at' Bh\":\"MMMM d, y 'at' h B\",\"MMM d, y, Bh\":\"MMM d, y, h B\",\"M/d/yy, Bh\":\"M/d/yy, h B\",\"d, Bh\":\"d, h B\",\"E, Bh\":\"ccc, h B\",\"Ed, Bh\":\"d E, h B\",\"Gy, Bh\":\"y G, h B\",\"GyMMM, Bh\":\"MMM y G, h B\",\"GyMMMd, Bh\":\"MMM d, y G, h B\",\"GyMMMEd, Bh\":\"E, MMM d, y G, h B\",\"M, Bh\":\"L, h B\",\"Md, Bh\":\"M/d, h B\",\"MEd, Bh\":\"E, M/d, h B\",\"MMM, Bh\":\"LLL, h B\",\"MMMd, Bh\":\"MMM d, h B\",\"MMMEd, Bh\":\"E, MMM d, h B\",\"MMMMd 'at' Bh\":\"MMMM d 'at' h B\",\"y, Bh\":\"y, h B\",\"yM, Bh\":\"M/y, h B\",\"yMd, Bh\":\"M/d/y, h B\",\"yMEd, Bh\":\"E, M/d/y, h B\",\"yMMM, Bh\":\"MMM y, h B\",\"yMMMd, Bh\":\"MMM d, y, h B\",\"yMMMEd, Bh\":\"E, MMM d, y, h B\",\"yMMMM 'at' Bh\":\"MMMM y 'at' h B\",\"EEEE, MMMM d, y 'at' Bhm\":\"EEEE, MMMM d, y 'at' h:mm B\",\"MMMM d, y 'at' Bhm\":\"MMMM d, y 'at' h:mm B\",\"MMM d, y, Bhm\":\"MMM d, y, h:mm B\",\"M/d/yy, Bhm\":\"M/d/yy, h:mm B\",\"d, Bhm\":\"d, h:mm B\",\"E, Bhm\":\"ccc, h:mm B\",\"Ed, Bhm\":\"d E, h:mm B\",\"Gy, Bhm\":\"y G, h:mm B\",\"GyMMM, Bhm\":\"MMM y G, h:mm B\",\"GyMMMd, Bhm\":\"MMM d, y G, h:mm B\",\"GyMMMEd, Bhm\":\"E, MMM d, y G, h:mm B\",\"M, Bhm\":\"L, h:mm B\",\"Md, Bhm\":\"M/d, h:mm B\",\"MEd, Bhm\":\"E, M/d, h:mm B\",\"MMM, Bhm\":\"LLL, h:mm B\",\"MMMd, Bhm\":\"MMM d, h:mm B\",\"MMMEd, Bhm\":\"E, MMM d, h:mm B\",\"MMMMd 'at' Bhm\":\"MMMM d 'at' h:mm B\",\"y, Bhm\":\"y, h:mm B\",\"yM, Bhm\":\"M/y, h:mm B\",\"yMd, Bhm\":\"M/d/y, h:mm B\",\"yMEd, Bhm\":\"E, M/d/y, h:mm B\",\"yMMM, Bhm\":\"MMM y, h:mm B\",\"yMMMd, Bhm\":\"MMM d, y, h:mm B\",\"yMMMEd, Bhm\":\"E, MMM d, y, h:mm B\",\"yMMMM 'at' Bhm\":\"MMMM y 'at' h:mm B\",\"EEEE, MMMM d, y 'at' Bhms\":\"EEEE, MMMM d, y 'at' h:mm:ss B\",\"MMMM d, y 'at' Bhms\":\"MMMM d, y 'at' h:mm:ss B\",\"MMM d, y, Bhms\":\"MMM d, y, h:mm:ss B\",\"M/d/yy, Bhms\":\"M/d/yy, h:mm:ss B\",\"d, Bhms\":\"d, h:mm:ss B\",\"E, Bhms\":\"ccc, h:mm:ss B\",\"Ed, Bhms\":\"d E, h:mm:ss B\",\"Gy, Bhms\":\"y G, h:mm:ss B\",\"GyMMM, Bhms\":\"MMM y G, h:mm:ss B\",\"GyMMMd, Bhms\":\"MMM d, y G, h:mm:ss B\",\"GyMMMEd, Bhms\":\"E, MMM d, y G, h:mm:ss B\",\"M, Bhms\":\"L, h:mm:ss B\",\"Md, Bhms\":\"M/d, h:mm:ss B\",\"MEd, Bhms\":\"E, M/d, h:mm:ss B\",\"MMM, Bhms\":\"LLL, h:mm:ss B\",\"MMMd, Bhms\":\"MMM d, h:mm:ss B\",\"MMMEd, Bhms\":\"E, MMM d, h:mm:ss B\",\"MMMMd 'at' Bhms\":\"MMMM d 'at' h:mm:ss B\",\"y, Bhms\":\"y, h:mm:ss B\",\"yM, Bhms\":\"M/y, h:mm:ss B\",\"yMd, Bhms\":\"M/d/y, h:mm:ss B\",\"yMEd, Bhms\":\"E, M/d/y, h:mm:ss B\",\"yMMM, Bhms\":\"MMM y, h:mm:ss B\",\"yMMMd, Bhms\":\"MMM d, y, h:mm:ss B\",\"yMMMEd, Bhms\":\"E, MMM d, y, h:mm:ss B\",\"yMMMM 'at' Bhms\":\"MMMM y 'at' h:mm:ss B\",\"EEEE, MMMM d, y 'at' h\":\"EEEE, MMMM d, y 'at' h a\",\"MMMM d, y 'at' h\":\"MMMM d, y 'at' h a\",\"MMM d, y, h\":\"MMM d, y, h a\",\"M/d/yy, h\":\"M/d/yy, h a\",\"d, h\":\"d, h a\",\"E, h\":\"ccc, h a\",\"Ed, h\":\"d E, h a\",\"Gy, h\":\"y G, h a\",\"GyMMM, h\":\"MMM y G, h a\",\"GyMMMd, h\":\"MMM d, y G, h a\",\"GyMMMEd, h\":\"E, MMM d, y G, h a\",\"M, h\":\"L, h a\",\"Md, h\":\"M/d, h a\",\"MEd, h\":\"E, M/d, h a\",\"MMM, h\":\"LLL, h a\",\"MMMd, h\":\"MMM d, h a\",\"MMMEd, h\":\"E, MMM d, h a\",\"MMMMd 'at' h\":\"MMMM d 'at' h a\",\"y, h\":\"y, h a\",\"yM, h\":\"M/y, h a\",\"yMd, h\":\"M/d/y, h a\",\"yMEd, h\":\"E, M/d/y, h a\",\"yMMM, h\":\"MMM y, h a\",\"yMMMd, h\":\"MMM d, y, h a\",\"yMMMEd, h\":\"E, MMM d, y, h a\",\"yMMMM 'at' h\":\"MMMM y 'at' h a\",\"EEEE, MMMM d, y 'at' H\":\"EEEE, MMMM d, y 'at' HH\",\"MMMM d, y 'at' H\":\"MMMM d, y 'at' HH\",\"MMM d, y, H\":\"MMM d, y, HH\",\"M/d/yy, H\":\"M/d/yy, HH\",\"d, H\":\"d, HH\",\"E, H\":\"ccc, HH\",\"Ed, H\":\"d E, HH\",\"Gy, H\":\"y G, HH\",\"GyMMM, H\":\"MMM y G, HH\",\"GyMMMd, H\":\"MMM d, y G, HH\",\"GyMMMEd, H\":\"E, MMM d, y G, HH\",\"M, H\":\"L, HH\",\"Md, H\":\"M/d, HH\",\"MEd, H\":\"E, M/d, HH\",\"MMM, H\":\"LLL, HH\",\"MMMd, H\":\"MMM d, HH\",\"MMMEd, H\":\"E, MMM d, HH\",\"MMMMd 'at' H\":\"MMMM d 'at' HH\",\"y, H\":\"y, HH\",\"yM, H\":\"M/y, HH\",\"yMd, H\":\"M/d/y, HH\",\"yMEd, H\":\"E, M/d/y, HH\",\"yMMM, H\":\"MMM y, HH\",\"yMMMd, H\":\"MMM d, y, HH\",\"yMMMEd, H\":\"E, MMM d, y, HH\",\"yMMMM 'at' H\":\"MMMM y 'at' HH\",\"EEEE, MMMM d, y 'at' hm\":\"EEEE, MMMM d, y 'at' h:mm a\",\"MMMM d, y 'at' hm\":\"MMMM d, y 'at' h:mm a\",\"MMM d, y, hm\":\"MMM d, y, h:mm a\",\"M/d/yy, hm\":\"M/d/yy, h:mm a\",\"d, hm\":\"d, h:mm a\",\"E, hm\":\"ccc, h:mm a\",\"Ed, hm\":\"d E, h:mm a\",\"Gy, hm\":\"y G, h:mm a\",\"GyMMM, hm\":\"MMM y G, h:mm a\",\"GyMMMd, hm\":\"MMM d, y G, h:mm a\",\"GyMMMEd, hm\":\"E, MMM d, y G, h:mm a\",\"M, hm\":\"L, h:mm a\",\"Md, hm\":\"M/d, h:mm a\",\"MEd, hm\":\"E, M/d, h:mm a\",\"MMM, hm\":\"LLL, h:mm a\",\"MMMd, hm\":\"MMM d, h:mm a\",\"MMMEd, hm\":\"E, MMM d, h:mm a\",\"MMMMd 'at' hm\":\"MMMM d 'at' h:mm a\",\"y, hm\":\"y, h:mm a\",\"yM, hm\":\"M/y, h:mm a\",\"yMd, hm\":\"M/d/y, h:mm a\",\"yMEd, hm\":\"E, M/d/y, h:mm a\",\"yMMM, hm\":\"MMM y, h:mm a\",\"yMMMd, hm\":\"MMM d, y, h:mm a\",\"yMMMEd, hm\":\"E, MMM d, y, h:mm a\",\"yMMMM 'at' hm\":\"MMMM y 'at' h:mm a\",\"EEEE, MMMM d, y 'at' Hm\":\"EEEE, MMMM d, y 'at' HH:mm\",\"MMMM d, y 'at' Hm\":\"MMMM d, y 'at' HH:mm\",\"MMM d, y, Hm\":\"MMM d, y, HH:mm\",\"M/d/yy, Hm\":\"M/d/yy, HH:mm\",\"d, Hm\":\"d, HH:mm\",\"E, Hm\":\"ccc, HH:mm\",\"Ed, Hm\":\"d E, HH:mm\",\"Gy, Hm\":\"y G, HH:mm\",\"GyMMM, Hm\":\"MMM y G, HH:mm\",\"GyMMMd, Hm\":\"MMM d, y G, HH:mm\",\"GyMMMEd, Hm\":\"E, MMM d, y G, HH:mm\",\"M, Hm\":\"L, HH:mm\",\"Md, Hm\":\"M/d, HH:mm\",\"MEd, Hm\":\"E, M/d, HH:mm\",\"MMM, Hm\":\"LLL, HH:mm\",\"MMMd, Hm\":\"MMM d, HH:mm\",\"MMMEd, Hm\":\"E, MMM d, HH:mm\",\"MMMMd 'at' Hm\":\"MMMM d 'at' HH:mm\",\"y, Hm\":\"y, HH:mm\",\"yM, Hm\":\"M/y, HH:mm\",\"yMd, Hm\":\"M/d/y, HH:mm\",\"yMEd, Hm\":\"E, M/d/y, HH:mm\",\"yMMM, Hm\":\"MMM y, HH:mm\",\"yMMMd, Hm\":\"MMM d, y, HH:mm\",\"yMMMEd, Hm\":\"E, MMM d, y, HH:mm\",\"yMMMM 'at' Hm\":\"MMMM y 'at' HH:mm\",\"EEEE, MMMM d, y 'at' hms\":\"EEEE, MMMM d, y 'at' h:mm:ss a\",\"MMMM d, y 'at' hms\":\"MMMM d, y 'at' h:mm:ss a\",\"MMM d, y, hms\":\"MMM d, y, h:mm:ss a\",\"M/d/yy, hms\":\"M/d/yy, h:mm:ss a\",\"d, hms\":\"d, h:mm:ss a\",\"E, hms\":\"ccc, h:mm:ss a\",\"Ed, hms\":\"d E, h:mm:ss a\",\"Gy, hms\":\"y G, h:mm:ss a\",\"GyMMM, hms\":\"MMM y G, h:mm:ss a\",\"GyMMMd, hms\":\"MMM d, y G, h:mm:ss a\",\"GyMMMEd, hms\":\"E, MMM d, y G, h:mm:ss a\",\"M, hms\":\"L, h:mm:ss a\",\"Md, hms\":\"M/d, h:mm:ss a\",\"MEd, hms\":\"E, M/d, h:mm:ss a\",\"MMM, hms\":\"LLL, h:mm:ss a\",\"MMMd, hms\":\"MMM d, h:mm:ss a\",\"MMMEd, hms\":\"E, MMM d, h:mm:ss a\",\"MMMMd 'at' hms\":\"MMMM d 'at' h:mm:ss a\",\"y, hms\":\"y, h:mm:ss a\",\"yM, hms\":\"M/y, h:mm:ss a\",\"yMd, hms\":\"M/d/y, h:mm:ss a\",\"yMEd, hms\":\"E, M/d/y, h:mm:ss a\",\"yMMM, hms\":\"MMM y, h:mm:ss a\",\"yMMMd, hms\":\"MMM d, y, h:mm:ss a\",\"yMMMEd, hms\":\"E, MMM d, y, h:mm:ss a\",\"yMMMM 'at' hms\":\"MMMM y 'at' h:mm:ss a\",\"EEEE, MMMM d, y 'at' Hms\":\"EEEE, MMMM d, y 'at' HH:mm:ss\",\"MMMM d, y 'at' Hms\":\"MMMM d, y 'at' HH:mm:ss\",\"MMM d, y, Hms\":\"MMM d, y, HH:mm:ss\",\"M/d/yy, Hms\":\"M/d/yy, HH:mm:ss\",\"d, Hms\":\"d, HH:mm:ss\",\"E, Hms\":\"ccc, HH:mm:ss\",\"Ed, Hms\":\"d E, HH:mm:ss\",\"Gy, Hms\":\"y G, HH:mm:ss\",\"GyMMM, Hms\":\"MMM y G, HH:mm:ss\",\"GyMMMd, Hms\":\"MMM d, y G, HH:mm:ss\",\"GyMMMEd, Hms\":\"E, MMM d, y G, HH:mm:ss\",\"M, Hms\":\"L, HH:mm:ss\",\"Md, Hms\":\"M/d, HH:mm:ss\",\"MEd, Hms\":\"E, M/d, HH:mm:ss\",\"MMM, Hms\":\"LLL, HH:mm:ss\",\"MMMd, Hms\":\"MMM d, HH:mm:ss\",\"MMMEd, Hms\":\"E, MMM d, HH:mm:ss\",\"MMMMd 'at' Hms\":\"MMMM d 'at' HH:mm:ss\",\"y, Hms\":\"y, HH:mm:ss\",\"yM, Hms\":\"M/y, HH:mm:ss\",\"yMd, Hms\":\"M/d/y, HH:mm:ss\",\"yMEd, Hms\":\"E, M/d/y, HH:mm:ss\",\"yMMM, Hms\":\"MMM y, HH:mm:ss\",\"yMMMd, Hms\":\"MMM d, y, HH:mm:ss\",\"yMMMEd, Hms\":\"E, MMM d, y, HH:mm:ss\",\"yMMMM 'at' Hms\":\"MMMM y 'at' HH:mm:ss\",\"EEEE, MMMM d, y 'at' hmsv\":\"EEEE, MMMM d, y 'at' h:mm:ss a v\",\"MMMM d, y 'at' hmsv\":\"MMMM d, y 'at' h:mm:ss a v\",\"MMM d, y, hmsv\":\"MMM d, y, h:mm:ss a v\",\"M/d/yy, hmsv\":\"M/d/yy, h:mm:ss a v\",\"d, hmsv\":\"d, h:mm:ss a v\",\"E, hmsv\":\"ccc, h:mm:ss a v\",\"Ed, hmsv\":\"d E, h:mm:ss a v\",\"Gy, hmsv\":\"y G, h:mm:ss a v\",\"GyMMM, hmsv\":\"MMM y G, h:mm:ss a v\",\"GyMMMd, hmsv\":\"MMM d, y G, h:mm:ss a v\",\"GyMMMEd, hmsv\":\"E, MMM d, y G, h:mm:ss a v\",\"M, hmsv\":\"L, h:mm:ss a v\",\"Md, hmsv\":\"M/d, h:mm:ss a v\",\"MEd, hmsv\":\"E, M/d, h:mm:ss a v\",\"MMM, hmsv\":\"LLL, h:mm:ss a v\",\"MMMd, hmsv\":\"MMM d, h:mm:ss a v\",\"MMMEd, hmsv\":\"E, MMM d, h:mm:ss a v\",\"MMMMd 'at' hmsv\":\"MMMM d 'at' h:mm:ss a v\",\"y, hmsv\":\"y, h:mm:ss a v\",\"yM, hmsv\":\"M/y, h:mm:ss a v\",\"yMd, hmsv\":\"M/d/y, h:mm:ss a v\",\"yMEd, hmsv\":\"E, M/d/y, h:mm:ss a v\",\"yMMM, hmsv\":\"MMM y, h:mm:ss a v\",\"yMMMd, hmsv\":\"MMM d, y, h:mm:ss a v\",\"yMMMEd, hmsv\":\"E, MMM d, y, h:mm:ss a v\",\"yMMMM 'at' hmsv\":\"MMMM y 'at' h:mm:ss a v\",\"EEEE, MMMM d, y 'at' Hmsv\":\"EEEE, MMMM d, y 'at' HH:mm:ss v\",\"MMMM d, y 'at' Hmsv\":\"MMMM d, y 'at' HH:mm:ss v\",\"MMM d, y, Hmsv\":\"MMM d, y, HH:mm:ss v\",\"M/d/yy, Hmsv\":\"M/d/yy, HH:mm:ss v\",\"d, Hmsv\":\"d, HH:mm:ss v\",\"E, Hmsv\":\"ccc, HH:mm:ss v\",\"Ed, Hmsv\":\"d E, HH:mm:ss v\",\"Gy, Hmsv\":\"y G, HH:mm:ss v\",\"GyMMM, Hmsv\":\"MMM y G, HH:mm:ss v\",\"GyMMMd, Hmsv\":\"MMM d, y G, HH:mm:ss v\",\"GyMMMEd, Hmsv\":\"E, MMM d, y G, HH:mm:ss v\",\"M, Hmsv\":\"L, HH:mm:ss v\",\"Md, Hmsv\":\"M/d, HH:mm:ss v\",\"MEd, Hmsv\":\"E, M/d, HH:mm:ss v\",\"MMM, Hmsv\":\"LLL, HH:mm:ss v\",\"MMMd, Hmsv\":\"MMM d, HH:mm:ss v\",\"MMMEd, Hmsv\":\"E, MMM d, HH:mm:ss v\",\"MMMMd 'at' Hmsv\":\"MMMM d 'at' HH:mm:ss v\",\"y, Hmsv\":\"y, HH:mm:ss v\",\"yM, Hmsv\":\"M/y, HH:mm:ss v\",\"yMd, Hmsv\":\"M/d/y, HH:mm:ss v\",\"yMEd, Hmsv\":\"E, M/d/y, HH:mm:ss v\",\"yMMM, Hmsv\":\"MMM y, HH:mm:ss v\",\"yMMMd, Hmsv\":\"MMM d, y, HH:mm:ss v\",\"yMMMEd, Hmsv\":\"E, MMM d, y, HH:mm:ss v\",\"yMMMM 'at' Hmsv\":\"MMMM y 'at' HH:mm:ss v\",\"EEEE, MMMM d, y 'at' hmv\":\"EEEE, MMMM d, y 'at' h:mm a v\",\"MMMM d, y 'at' hmv\":\"MMMM d, y 'at' h:mm a v\",\"MMM d, y, hmv\":\"MMM d, y, h:mm a v\",\"M/d/yy, hmv\":\"M/d/yy, h:mm a v\",\"d, hmv\":\"d, h:mm a v\",\"E, hmv\":\"ccc, h:mm a v\",\"Ed, hmv\":\"d E, h:mm a v\",\"Gy, hmv\":\"y G, h:mm a v\",\"GyMMM, hmv\":\"MMM y G, h:mm a v\",\"GyMMMd, hmv\":\"MMM d, y G, h:mm a v\",\"GyMMMEd, hmv\":\"E, MMM d, y G, h:mm a v\",\"M, hmv\":\"L, h:mm a v\",\"Md, hmv\":\"M/d, h:mm a v\",\"MEd, hmv\":\"E, M/d, h:mm a v\",\"MMM, hmv\":\"LLL, h:mm a v\",\"MMMd, hmv\":\"MMM d, h:mm a v\",\"MMMEd, hmv\":\"E, MMM d, h:mm a v\",\"MMMMd 'at' hmv\":\"MMMM d 'at' h:mm a v\",\"y, hmv\":\"y, h:mm a v\",\"yM, hmv\":\"M/y, h:mm a v\",\"yMd, hmv\":\"M/d/y, h:mm a v\",\"yMEd, hmv\":\"E, M/d/y, h:mm a v\",\"yMMM, hmv\":\"MMM y, h:mm a v\",\"yMMMd, hmv\":\"MMM d, y, h:mm a v\",\"yMMMEd, hmv\":\"E, MMM d, y, h:mm a v\",\"yMMMM 'at' hmv\":\"MMMM y 'at' h:mm a v\",\"EEEE, MMMM d, y 'at' Hmv\":\"EEEE, MMMM d, y 'at' HH:mm v\",\"MMMM d, y 'at' Hmv\":\"MMMM d, y 'at' HH:mm v\",\"MMM d, y, Hmv\":\"MMM d, y, HH:mm v\",\"M/d/yy, Hmv\":\"M/d/yy, HH:mm v\",\"d, Hmv\":\"d, HH:mm v\",\"E, Hmv\":\"ccc, HH:mm v\",\"Ed, Hmv\":\"d E, HH:mm v\",\"Gy, Hmv\":\"y G, HH:mm v\",\"GyMMM, Hmv\":\"MMM y G, HH:mm v\",\"GyMMMd, Hmv\":\"MMM d, y G, HH:mm v\",\"GyMMMEd, Hmv\":\"E, MMM d, y G, HH:mm v\",\"M, Hmv\":\"L, HH:mm v\",\"Md, Hmv\":\"M/d, HH:mm v\",\"MEd, Hmv\":\"E, M/d, HH:mm v\",\"MMM, Hmv\":\"LLL, HH:mm v\",\"MMMd, Hmv\":\"MMM d, HH:mm v\",\"MMMEd, Hmv\":\"E, MMM d, HH:mm v\",\"MMMMd 'at' Hmv\":\"MMMM d 'at' HH:mm v\",\"y, Hmv\":\"y, HH:mm v\",\"yM, Hmv\":\"M/y, HH:mm v\",\"yMd, Hmv\":\"M/d/y, HH:mm v\",\"yMEd, Hmv\":\"E, M/d/y, HH:mm v\",\"yMMM, Hmv\":\"MMM y, HH:mm v\",\"yMMMd, Hmv\":\"MMM d, y, HH:mm v\",\"yMMMEd, Hmv\":\"E, MMM d, y, HH:mm v\",\"yMMMM 'at' Hmv\":\"MMMM y 'at' HH:mm v\",\"EEEE, MMMM d, y 'at' ms\":\"EEEE, MMMM d, y 'at' mm:ss\",\"MMMM d, y 'at' ms\":\"MMMM d, y 'at' mm:ss\",\"MMM d, y, ms\":\"MMM d, y, mm:ss\",\"M/d/yy, ms\":\"M/d/yy, mm:ss\",\"d, ms\":\"d, mm:ss\",\"E, ms\":\"ccc, mm:ss\",\"Ed, ms\":\"d E, mm:ss\",\"Gy, ms\":\"y G, mm:ss\",\"GyMMM, ms\":\"MMM y G, mm:ss\",\"GyMMMd, ms\":\"MMM d, y G, mm:ss\",\"GyMMMEd, ms\":\"E, MMM d, y G, mm:ss\",\"M, ms\":\"L, mm:ss\",\"Md, ms\":\"M/d, mm:ss\",\"MEd, ms\":\"E, M/d, mm:ss\",\"MMM, ms\":\"LLL, mm:ss\",\"MMMd, ms\":\"MMM d, mm:ss\",\"MMMEd, ms\":\"E, MMM d, mm:ss\",\"MMMMd 'at' ms\":\"MMMM d 'at' mm:ss\",\"y, ms\":\"y, mm:ss\",\"yM, ms\":\"M/y, mm:ss\",\"yMd, ms\":\"M/d/y, mm:ss\",\"yMEd, ms\":\"E, M/d/y, mm:ss\",\"yMMM, ms\":\"MMM y, mm:ss\",\"yMMMd, ms\":\"MMM d, y, mm:ss\",\"yMMMEd, ms\":\"E, MMM d, y, mm:ss\",\"yMMMM 'at' ms\":\"MMMM y 'at' mm:ss\"}},\"intervalFormats\":{\"intervalFormatFallback\":\"{0} – {1}\",\"Bh\":{\"B\":\"h B – h B\",\"h\":\"h – h B\"},\"Bhm\":{\"B\":\"h:mm B – h:mm B\",\"h\":\"h:mm – h:mm B\",\"m\":\"h:mm – h:mm B\"},\"d\":{\"d\":\"d – d\"},\"Gy\":{\"G\":\"y G – y G\",\"y\":\"y – y G\"},\"GyM\":{\"G\":\"M/y GGGGG – M/y GGGGG\",\"M\":\"M/y – M/y GGGGG\",\"y\":\"M/y – M/y GGGGG\"},\"GyMd\":{\"d\":\"M/d/y – M/d/y GGGGG\",\"G\":\"M/d/y GGGGG – M/d/y GGGGG\",\"M\":\"M/d/y – M/d/y GGGGG\",\"y\":\"M/d/y – M/d/y GGGGG\"},\"GyMEd\":{\"d\":\"E, M/d/y – E, M/d/y GGGGG\",\"G\":\"E, M/d/y GGGGG – E, M/d/y GGGGG\",\"M\":\"E, M/d/y – E, M/d/y GGGGG\",\"y\":\"E, M/d/y – E, M/d/y GGGGG\"},\"GyMMM\":{\"G\":\"MMM y G – MMM y G\",\"M\":\"MMM – MMM y G\",\"y\":\"MMM y – MMM y G\"},\"GyMMMd\":{\"d\":\"MMM d – d, y G\",\"G\":\"MMM d, y G – MMM d, y G\",\"M\":\"MMM d – MMM d, y G\",\"y\":\"MMM d, y – MMM d, y G\"},\"GyMMMEd\":{\"d\":\"E, MMM d – E, MMM d, y G\",\"G\":\"E, MMM d, y G – E, MMM d, y G\",\"M\":\"E, MMM d – E, MMM d, y G\",\"y\":\"E, MMM d, y – E, MMM d, y G\"},\"h\":{\"a\":\"h a – h a\",\"h\":\"h – h a\"},\"H\":{\"H\":\"HH – HH\"},\"hm\":{\"a\":\"h:mm a – h:mm a\",\"h\":\"h:mm – h:mm a\",\"m\":\"h:mm – h:mm a\"},\"Hm\":{\"H\":\"HH:mm – HH:mm\",\"m\":\"HH:mm – HH:mm\"},\"hmv\":{\"a\":\"h:mm a – h:mm a v\",\"h\":\"h:mm – h:mm a v\",\"m\":\"h:mm – h:mm a v\"},\"Hmv\":{\"H\":\"HH:mm – HH:mm v\",\"m\":\"HH:mm – HH:mm v\"},\"hv\":{\"a\":\"h a – h a v\",\"h\":\"h – h a v\"},\"Hv\":{\"H\":\"HH – HH v\"},\"M\":{\"M\":\"M – M\"},\"Md\":{\"d\":\"M/d – M/d\",\"M\":\"M/d – M/d\"},\"MEd\":{\"d\":\"E, M/d – E, M/d\",\"M\":\"E, M/d – E, M/d\"},\"MMM\":{\"M\":\"MMM – MMM\"},\"MMMd\":{\"d\":\"MMM d – d\",\"M\":\"MMM d – MMM d\"},\"MMMEd\":{\"d\":\"E, MMM d – E, MMM d\",\"M\":\"E, MMM d – E, MMM d\"},\"y\":{\"y\":\"y – y\"},\"yM\":{\"M\":\"M/y – M/y\",\"y\":\"M/y – M/y\"},\"yMd\":{\"d\":\"M/d/y – M/d/y\",\"M\":\"M/d/y – M/d/y\",\"y\":\"M/d/y – M/d/y\"},\"yMEd\":{\"d\":\"E, M/d/y – E, M/d/y\",\"M\":\"E, M/d/y – E, M/d/y\",\"y\":\"E, M/d/y – E, M/d/y\"},\"yMMM\":{\"M\":\"MMM – MMM y\",\"y\":\"MMM y – MMM y\"},\"yMMMd\":{\"d\":\"MMM d – d, y\",\"M\":\"MMM d – MMM d, y\",\"y\":\"MMM d, y – MMM d, y\"},\"yMMMEd\":{\"d\":\"E, MMM d – E, MMM d, y\",\"M\":\"E, MMM d – E, MMM d, y\",\"y\":\"E, MMM d, y – E, MMM d, y\"},\"yMMMM\":{\"M\":\"MMMM – MMMM y\",\"y\":\"MMMM y – MMMM y\"}},\"hourCycle\":\"h12\",\"nu\":[\"latn\"],\"ca\":[\"gregory\"],\"hc\":[\"h12\",\"\",\"h23\",\"\"]},\"locale\":\"en\"}\n)\n }","// @generated\n// prettier-ignore\nif ('DateTimeFormat' in Intl && Intl.DateTimeFormat.__addTZData) {\n Intl.DateTimeFormat.__addTZData({\"zones\":[\"Africa/Accra|,0,0,0|-s9p1ak,1,1,0|-q5eqo1,1,1,0|-q5eqo0,2,2,1|-q3g8pd,2,2,1|-q3g8pc,1,1,0|-pqwd41,1,1,0|-pqwd40,2,2,1|-pkmgpd,2,2,1|-pkmgpc,1,1,0|-p84fs1,1,1,0|-p84fs0,2,2,1|-p1ujdd,2,2,1|-p1ujdc,1,1,0|-opcig1,1,1,0|-opcig0,2,2,1|-oj2m1d,2,2,1|-oj2m1c,1,1,0|-o6kl41,1,1,0|-o6kl40,2,2,1|-o0aopd,2,2,1|-o0aopc,1,1,0|-nnqt41,1,1,0|-nnqt40,2,2,1|-nhgwpd,2,2,1|-nhgwpc,1,1,0|-n4yvs1,1,1,0|-n4yvs0,2,2,1|-myozdd,2,2,1|-myozdc,1,1,0|-mm6yg1,1,1,0|-mm6yg0,2,2,1|-mfx21d,2,2,1|-mfx21c,1,1,0|-m3f141,1,1,0|-m3f140,2,2,1|-lx54pd,2,2,1|-lx54pc,1,1,0|-lkl941,1,1,0|-lkl940,2,2,1|-lebcpd,2,2,1|-lebcpc,1,1,0|-l1tbs1,1,1,0|-l1tbs0,2,2,1|-kvjfdd,2,2,1|-kvjfdc,1,1,0|-kj1eg1,1,1,0|-kj1eg0,2,2,1|-kcri1d,2,2,1|-kcri1c,1,1,0|-k09h41,1,1,0|-k09h40,2,2,1|-jtzkpd,2,2,1|-jtzkpc,1,1,0|-jhfp41,1,1,0|-jhfp40,2,2,1|-jb5spd,2,2,1|-jb5spc,1,1,0|-iynrs1,1,1,0|-iynrs0,2,2,1|-isdvdd,2,2,1|-isdvdc,1,1,0|-ifvug1,1,1,0|-ifvug0,2,2,1|-i9ly1d,2,2,1|-i9ly1c,1,1,0|-hx3x41,1,1,0|-hx3x40,2,2,1|-hqu0pd,2,2,1|-hqu0pc,1,1,0|-hea541,1,1,0|-hea540,2,2,1|-h808pd,2,2,1|-h808pc,1,1,0|-gvi7s1,1,1,0|-gvi7s0,2,2,1|-gp8bdd,2,2,1|-gp8bdc,1,1,0|-gcqag1,1,1,0|-gcqag0,2,2,1|-g6ge1d,2,2,1|-g6ge1c,1,1,0|-ftyd41,1,1,0|-ftyd40,2,2,1|-fnogpd,2,2,1|-fnogpc,1,1,0|-fhgd41,1,1,0|-fhgd40,2,2,1|-f4uopd,2,2,1|-f4uopc,1,1,0|-eyofs1,1,1,0|-eyofs0,2,2,1|-em2rdd,2,2,1|-em2rdc,1,1,0|-ek4io1,1,1,0|-ek4io0,3,3,0|-cio421,3,3,0|-cio420,1,1,0|-a39mg1,1,1,0|-a39mg0,3,3,1|-9wzqi1,3,3,1|-9wzqi0,1,1,0|-9khp41,1,1,0|-9khp40,3,3,1|-9e7t61,3,3,1|-9e7t60,1,1,0|-91nx41,1,1,0|-91nx40,3,3,1|-8ve161,3,3,1|-8ve160,1,1,0|-8ivzs1,1,1,0|-8ivzs0,3,3,1|-8cm3u1,3,3,1|-8cm3u0,1,1,0|-8042g1,1,1,0|-8042g0,3,3,1|-7tu6i1,3,3,1|-7tu6i0,1,1,0|-7hc541,1,1,0|-7hc540,3,3,1|-7b2961,3,3,1|-7b2960,1,1,0\",\"Africa/Addis_Ababa|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Africa/Algiers|,0,8,0|-154gb8c,7,9,0|-uozn3m,7,9,0|-uozn3l,8,1,0|-ry2lg1,8,1,0|-ry2lg0,9,10,1|-rsgqs1,9,10,1|-rsgqs0,8,1,0|-rjiis1,8,1,0|-rjiis0,9,10,1|-r9dpg1,9,10,1|-r9dpg0,8,1,0|-r1idg1,8,1,0|-r1idg0,9,10,1|-qqnms1,9,10,1|-qqnms0,8,1,0|-qj59g1,8,1,0|-qj59g0,9,10,1|-q7xk41,9,10,1|-q7xk40,8,1,0|-q15441,8,1,0|-q15440,9,10,1|-po6g41,9,10,1|-po6g40,8,1,0|-pgvhg1,8,1,0|-pgvhg0,9,10,1|-pbs5g1,9,10,1|-pbs5g0,8,1,0|-fte841,8,1,0|-fte840,9,10,1|-fpw801,9,10,1|-fpw800,8,1,0|-fkul41,8,1,0|-fkul40,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d62o01,11,11,1|-d62o00,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cofk41,11,11,1|-cofk40,10,10,0|-c4kqs1,10,10,0|-c4kqs0,8,1,0|-79mio1,8,1,0|-79mio0,10,10,0|-3i8is1,10,10,0|-3i8is0,8,1,0|oot7z,8,1,0|oot80,9,10,1|wlzvz,9,10,1|wlzw0,8,1,0|3tynzz,8,1,0|3tyo00,9,10,1|42lp7z,9,10,1|42lp80,10,10,0|4aiynz,10,10,0|4aiyo0,11,11,1|4jw2rz,11,11,1|4jw2s0,10,10,0|54et7z,10,10,0|54et80,8,1,0|5drxbz,8,1,0|5drxc0,9,10,1|5ni03z,9,10,1|5ni040,8,1,0|5wuynz,8,1,0|5wuyo0,10,10,0\",\"Africa/Asmara|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Africa/Bamako|,0,12,0|-u9rgl4,1,1,0\",\"Africa/Bangui|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/Banjul|,0,12,0|-u9rgl4,1,1,0\",\"Africa/Bissau|,0,14,0|-u9rek0,13,15,0|2lxk3z,13,15,0|2lxk40,1,1,0\",\"Africa/Blantyre|,0,16,0|-yvtfd8,14,11,0\",\"Africa/Brazzaville|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/Bujumbura|,0,16,0|-yvtfd8,14,11,0\",\"Africa/Cairo|,0,17,0|-1054wgl,15,11,0|-fdls81,15,11,0|-fdls80,16,6,1|-f9lf01,16,6,1|-f9lf00,15,11,0|-ezidk1,15,11,0|-ezidk0,16,6,1|-erl9o1,16,6,1|-erl9o0,15,11,0|-ehgdk1,15,11,0|-ehgdk0,16,6,1|-e6pf01,16,6,1|-e6pf00,15,11,0|-dyog81,15,11,0|-dyog80,16,6,1|-dno8c1,16,6,1|-dno8c0,15,11,0|-dfuo81,15,11,0|-dfuo80,16,6,1|-d4ugc1,16,6,1|-d4ugc0,15,11,0|-cwayw1,15,11,0|-cwayw0,16,6,1|-cm2j01,16,6,1|-cm2j00,15,11,0|-6lluw1,15,11,0|-6lluw0,16,6,1|-6e79o1,16,6,1|-6e79o0,15,11,0|-63alk1,15,11,0|-63alk0,16,6,1|-5vfcc1,16,6,1|-5vfcc0,15,11,0|-5kilg1,15,11,0|-5kilg0,16,6,1|-5cp1c1,16,6,1|-5cp1c0,15,11,0|-51otg1,15,11,0|-51otg0,16,6,1|-4tv9c1,16,6,1|-4tv9c0,15,11,0|-4iww41,15,11,0|-4iww40,16,6,1|-4b3c01,16,6,1|-4b3c00,15,11,0|-404ys1,15,11,0|-404ys0,16,6,1|-3sbeo1,16,6,1|-3sbeo0,15,11,0|-3hd1g1,15,11,0|-3hd1g0,16,6,1|-39jhc1,16,6,1|-39jhc0,15,11,0|-2yj9g1,15,11,0|-2yj9g0,16,6,1|-2qppc1,16,6,1|-2qppc0,15,11,0|-2frc41,15,11,0|-2frc40,16,6,1|-27xs01,16,6,1|-27xs00,15,11,0|-1wzes1,15,11,0|-1wzes0,16,6,1|-1p4001,16,6,1|-1p4000,15,11,0|-1e7hg1,15,11,0|-1e7hg0,16,6,1|-16c2o1,16,6,1|-16c2o0,15,11,0|-vdpg1,15,11,0|-vdpg0,16,6,1|-niao1,16,6,1|-niao0,15,11,0|-cls41,15,11,0|-cls40,16,6,1|-4qdc1,16,6,1|-4qdc0,15,11,0|6657z,15,11,0|66580,16,6,1|e1jzz,16,6,1|e1k00,15,11,0|oy2jz,15,11,0|oy2k0,16,6,1|wthbz,16,6,1|wthc0,15,11,0|17rujz,15,11,0|17ruk0,16,6,1|1fn9bz,16,6,1|1fn9c0,15,11,0|1qjrvz,15,11,0|1qjrw0,16,6,1|1yf6nz,16,6,1|1yf6o0,15,11,0|29bp7z,15,11,0|29bp80,16,6,1|2h73zz,16,6,1|2h7400,15,11,0|2s3mjz,15,11,0|2s3mk0,16,6,1|2zz1bz,16,6,1|2zz1c0,15,11,0|3axejz,15,11,0|3axek0,16,6,1|3istbz,16,6,1|3istc0,15,11,0|3tpbvz,15,11,0|3tpbw0,16,6,1|41kqnz,16,6,1|41kqo0,15,11,0|4ch97z,15,11,0|4ch980,16,6,1|4kcnzz,16,6,1|4kco00,15,11,0|4v96jz,15,11,0|4v96k0,16,6,1|534lbz,16,6,1|534lc0,15,11,0|5e2yjz,15,11,0|5e2yk0,16,6,1|5lydbz,16,6,1|5lydc0,15,11,0|5wuvvz,15,11,0|5wuvw0,16,6,1|64qanz,16,6,1|64qao0,15,11,0|6k07vz,15,11,0|6k07w0,16,6,1|6ni7zz,16,6,1|6ni800,15,11,0|7242jz,15,11,0|7242k0,16,6,1|76a5bz,16,6,1|76a5c0,15,11,0|7h8ijz,15,11,0|7h8ik0,16,6,1|7p3xbz,16,6,1|7p3xc0,15,11,0|800fvz,15,11,0|800fw0,16,6,1|87vunz,16,6,1|87vuo0,15,11,0|8isd7z,15,11,0|8isd80,16,6,1|8qnrzz,16,6,1|8qns00,15,11,0|91kajz,15,11,0|91kak0,16,6,1|99fpbz,16,6,1|99fpc0,15,11,0|9ke2jz,15,11,0|9ke2k0,16,6,1|9s9hbz,16,6,1|9s9hc0,15,11,0|a3f97z,15,11,0|a3f980,16,6,1|ab1enz,16,6,1|ab1eo0,15,11,0|alxx7z,15,11,0|alxx80,16,6,1|attbzz,16,6,1|attc00,15,11,0|b4pujz,15,11,0|b4puk0,16,6,1|bcl9bz,16,6,1|bcl9c0,15,11,0|bnjmjz,15,11,0|bnjmk0,16,6,1|bvf1bz,16,6,1|bvf1c0,15,11,0|c6bjvz,15,11,0|c6bjw0,16,6,1|ce6ynz,16,6,1|ce6yo0,15,11,0|cp3h7z,15,11,0|cp3h80,16,6,1|cwyvzz,16,6,1|cwyw00,15,11,0|d7prrz,15,11,0|d7prs0,16,6,1|dfmvnz,16,6,1|dfmvo0,15,11,0|dqfufz,15,11,0|dqfug0,16,6,1|dycybz,16,6,1|dycyc0,15,11,0|e95x3z,15,11,0|e95x40,16,6,1|eh30zz,16,6,1|eh3100,15,11,0|ervzrz,15,11,0|ervzs0,16,6,1|ezt3nz,16,6,1|ezt3o0,15,11,0|faz13z,15,11,0|faz140,16,6,1|fiw4zz,16,6,1|fiw500,15,11,0|ftp3rz,15,11,0|ftp3s0,16,6,1|g1m7nz,16,6,1|g1m7o0,15,11,0|gcf6fz,15,11,0|gcf6g0,16,6,1|gkcabz,16,6,1|gkcac0,15,11,0|gv593z,15,11,0|gv5940,16,6,1|h32czz,16,6,1|h32d00,15,11,0|hdvbrz,15,11,0|hdvbs0,16,6,1|hlsfnz,16,6,1|hlsfo0,15,11,0|hwyd3z,15,11,0|hwyd40,16,6,1|i4vgzz,16,6,1|i4vh00,15,11,0|ifofrz,15,11,0|ifofs0,16,6,1|inljnz,16,6,1|inljo0,15,11,0|iyeifz,15,11,0|iyeig0,16,6,1|j5ynnz,16,6,1|j5yno0,15,11,0|jh4l3z,15,11,0|jh4l40,16,6,1|jnyszz,16,6,1|jnyt00,15,11,0|jzunrz,15,11,0|jzuns0,16,6,1|k6bwzz,16,6,1|k6bx00,15,11,0|kikqfz,15,11,0|kikqg0,16,6,1|kop0zz,16,6,1|kop100,15,11,0|l1nrrz,15,11,0|l1nrs0,16,6,1|l6yfnz,16,6,1|l6yfo0,15,11,0|l8i2fz,15,11,0|l8i2g0,16,6,1|l9kvnz,16,6,1|l9kvo0,15,11,0|n5myfz,15,11,0|n5myg0,16,6,1|n7snnz,16,6,1|n7sno0,15,11,0|n9ljrz,15,11,0|n9ljs0,16,6,1|nch6bz,16,6,1|nch6c0,15,11,0\",\"Africa/Casablanca|,0,18,0|-tblt9g,17,1,0|-fte5c1,17,1,0|-fte5c0,18,10,1|-fpwas1,18,10,1|-fpwas0,17,1,0|-fkuqo1,17,1,0|-fkuqo0,18,10,1|-cl6w41,18,10,1|-cl6w40,17,1,0|-a7hmo1,17,1,0|-a7hmo0,18,10,1|-a0ag41,18,10,1|-a0ag40,17,1,0|-1chdc1,17,1,0|-1chdc0,18,10,1|-16c5g1,18,10,1|-16c5g0,17,1,0|2c3rzz,17,1,0|2c3s00,18,10,1|2fnh7z,18,10,1|2fnh80,17,1,0|3axhbz,17,1,0|3axhc0,18,10,1|3fnrvz,18,10,1|3fnrw0,17,1,0|3tpenz,17,1,0|3tpeo0,18,10,1|41f3vz,18,10,1|41f3w0,17,1,0|4e2qnz,17,1,0|4e2qo0,18,10,1|4hd6jz,18,10,1|4hd6k0,17,1,0|7evenz,17,1,0|7eveo0,18,10,0|8cm57z,18,10,0|8cm580,17,1,0|k1rbzz,17,1,0|k1rc00,18,10,1|k6hmjz,18,10,1|k6hmk0,17,1,0|kkj9bz,17,1,0|kkj9c0,18,10,1|kop6jz,18,10,1|kop6k0,17,1,0|l1rmnz,17,1,0|l1rmo0,18,10,1|l6t17z,18,10,1|l6t180,17,1,0|lj1unz,17,1,0|lj1uo0,18,10,1|lp657z,18,10,1|lp6580,17,1,0|m37xjz,17,1,0|m37xk0,18,10,1|m7fs7z,18,10,1|m7fs80,17,1,0|m916vz,17,1,0|m916w0,18,10,1|mb547z,18,10,1|mb5480,17,1,0|mly07z,17,1,0|mly080,18,10,1|mpjmvz,18,10,1|mpjmw0,17,1,0|mraljz,17,1,0|mralk0,18,10,1|mvb1jz,18,10,1|mvb1k0,17,1,0|n3887z,17,1,0|n38880,18,10,1|n7uw7z,18,10,1|n7uw80,17,1,0|n9npjz,17,1,0|n9npk0,18,10,1|ne147z,18,10,1|ne1480,17,1,0|nlyavz,17,1,0|nlyaw0,18,10,1|npww7z,18,10,1|npww80,17,1,0|nrppjz,17,1,0|nrppk0,18,10,1|nwr6vz,18,10,1|nwr6w0,17,1,0|o4odjz,17,1,0|o4odk0,18,10,1|o8a07z,18,10,1|o8a080,17,1,0|oa2tjz,17,1,0|oa2tk0,18,10,1|ofu87z,18,10,1|ofu880,17,1,0|oneg7z,17,1,0|oneg80,18,10,1|oqa5jz,18,10,1|oqa5k0,17,1,0|osfxjz,17,1,0|osfxk0,18,10,1|oykavz,18,10,1|oykaw0,17,1,0|p64ivz,17,1,0|p64iw0,18,10,1|p8n9jz,18,10,1|p8n9k0,17,1,0|pag2vz,17,1,0|pag2w0,18,10,1|phadjz,18,10,1|phadk0,18,10,0|pr0djz,18,10,0|pr0dk0,17,1,1|pst6vz,17,1,1|pst6w0,18,10,0|q90ivz,18,10,0|q90iw0,17,1,1|qb6avz,17,1,1|qb6aw0,18,10,0|qrdmvz,18,10,0|qrdmw0,17,1,1|qt6g7z,17,1,1|qt6g80,18,10,0|r9ds7z,18,10,0|r9ds80,17,1,1|rbjk7z,17,1,1|rbjk80,18,10,0|rrqw7z,18,10,0|rrqw80,17,1,1|rtwo7z,17,1,1|rtwo80,18,10,0|sa407z,18,10,0|sa4080,17,1,1|sbwtjz,17,1,1|sbwtk0,18,10,0|ss45jz,18,10,0|ss45k0,17,1,1|su9xjz,17,1,1|su9xk0,18,10,0|tah9jz,18,10,0|tah9k0,17,1,1|tca2vz,17,1,1|tca2w0,18,10,0|tsudjz,18,10,0|tsudk0,17,1,1|tun6vz,17,1,1|tun6w0,18,10,0|uauivz,18,10,0|uauiw0,17,1,1|ud0avz,17,1,1|ud0aw0,18,10,0|ut7mvz,18,10,0|ut7mw0,17,1,1|uv0g7z,17,1,1|uv0g80,18,10,0|vb7s7z,18,10,0|vb7s80,17,1,1|vddk7z,17,1,1|vddk80,18,10,0|vtkw7z,18,10,0|vtkw80,17,1,1|vvqo7z,17,1,1|vvqo80,18,10,0|wby07z,18,10,0|wby080,17,1,1|wdqtjz,17,1,1|wdqtk0,18,10,0|wty5jz,18,10,0|wty5k0,17,1,1|ww3xjz,17,1,1|ww3xk0,18,10,0|xcb9jz,18,10,0|xcb9k0,17,1,1|xe42vz,17,1,1|xe42w0,18,10,0|xubevz,18,10,0|xubew0,17,1,1|xwh6vz,17,1,1|xwh6w0,18,10,0|ycoivz,18,10,0|ycoiw0,17,1,1|yeuavz,17,1,1|yeuaw0,18,10,0|yv1mvz,18,10,0|yv1mw0,17,1,1|ywug7z,17,1,1|ywug80,18,10,0|zd1s7z,18,10,0|zd1s80,17,1,1|zf7k7z,17,1,1|zf7k80,18,10,0\",\"Africa/Ceuta|,0,19,0|-100edc0,8,1,0|-qyiys1,8,1,0|-qyiys0,9,10,1|-qqluw1,9,10,1|-qqluw0,8,1,0|-nusqs1,8,1,0|-nusqs0,9,10,1|-nm0001,9,10,1|-nm0000,8,1,0|-mt71g1,8,1,0|-mt71g0,9,10,1|-mkjuo1,9,10,1|-mkjuo0,8,1,0|-matxg1,8,1,0|-matxg0,9,10,1|-m1ts01,9,10,1|-m1ts00,8,1,0|-lrqtc1,8,1,0|-lrqtc0,9,10,1|-liqqo1,9,10,1|-liqqo0,8,1,0|-1chdc1,8,1,0|-1chdc0,9,10,1|-16c5g1,9,10,1|-16c5g0,8,1,0|2c3rzz,8,1,0|2c3s00,9,10,1|2fnh7z,9,10,1|2fnh80,8,1,0|3axhbz,8,1,0|3axhc0,9,10,1|3fnrvz,9,10,1|3fnrw0,8,1,0|3tpenz,8,1,0|3tpeo0,9,10,1|41f3vz,9,10,1|41f3w0,8,1,0|4e2qnz,8,1,0|4e2qo0,9,10,1|4hd6jz,9,10,1|4hd6k0,8,1,0|7evenz,8,1,0|7eveo0,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Africa/Conakry|,0,12,0|-u9rgl4,1,1,0\",\"Africa/Dakar|,0,12,0|-u9rgl4,1,1,0\",\"Africa/Dar_es_Salaam|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Africa/Djibouti|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Africa/Douala|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/El_Aaiun|,0,20,0|-isdxk0,13,15,0|3a22rz,13,15,0|3a22s0,17,1,0|3axhbz,17,1,0|3axhc0,18,10,1|3fnrvz,18,10,1|3fnrw0,17,1,0|3tpenz,17,1,0|3tpeo0,18,10,1|41f3vz,18,10,1|41f3w0,17,1,0|4e2qnz,17,1,0|4e2qo0,18,10,1|4hd6jz,18,10,1|4hd6k0,17,1,0|k1rbzz,17,1,0|k1rc00,18,10,1|k6hmjz,18,10,1|k6hmk0,17,1,0|kkj9bz,17,1,0|kkj9c0,18,10,1|kop6jz,18,10,1|kop6k0,17,1,0|l1rmnz,17,1,0|l1rmo0,18,10,1|l6t17z,18,10,1|l6t180,17,1,0|lj1unz,17,1,0|lj1uo0,18,10,1|lp657z,18,10,1|lp6580,17,1,0|m37xjz,17,1,0|m37xk0,18,10,1|m7fs7z,18,10,1|m7fs80,17,1,0|m916vz,17,1,0|m916w0,18,10,1|mb547z,18,10,1|mb5480,17,1,0|mly07z,17,1,0|mly080,18,10,1|mpjmvz,18,10,1|mpjmw0,17,1,0|mraljz,17,1,0|mralk0,18,10,1|mvb1jz,18,10,1|mvb1k0,17,1,0|n3887z,17,1,0|n38880,18,10,1|n7uw7z,18,10,1|n7uw80,17,1,0|n9npjz,17,1,0|n9npk0,18,10,1|ne147z,18,10,1|ne1480,17,1,0|nlyavz,17,1,0|nlyaw0,18,10,1|npww7z,18,10,1|npww80,17,1,0|nrppjz,17,1,0|nrppk0,18,10,1|nwr6vz,18,10,1|nwr6w0,17,1,0|o4odjz,17,1,0|o4odk0,18,10,1|o8a07z,18,10,1|o8a080,17,1,0|oa2tjz,17,1,0|oa2tk0,18,10,1|ofu87z,18,10,1|ofu880,17,1,0|oneg7z,17,1,0|oneg80,18,10,1|oqa5jz,18,10,1|oqa5k0,17,1,0|osfxjz,17,1,0|osfxk0,18,10,1|oykavz,18,10,1|oykaw0,17,1,0|p64ivz,17,1,0|p64iw0,18,10,1|p8n9jz,18,10,1|p8n9k0,17,1,0|pag2vz,17,1,0|pag2w0,18,10,1|phadjz,18,10,1|phadk0,18,10,0|pr0djz,18,10,0|pr0dk0,17,1,1|pst6vz,17,1,1|pst6w0,18,10,0|q90ivz,18,10,0|q90iw0,17,1,1|qb6avz,17,1,1|qb6aw0,18,10,0|qrdmvz,18,10,0|qrdmw0,17,1,1|qt6g7z,17,1,1|qt6g80,18,10,0|r9ds7z,18,10,0|r9ds80,17,1,1|rbjk7z,17,1,1|rbjk80,18,10,0|rrqw7z,18,10,0|rrqw80,17,1,1|rtwo7z,17,1,1|rtwo80,18,10,0|sa407z,18,10,0|sa4080,17,1,1|sbwtjz,17,1,1|sbwtk0,18,10,0|ss45jz,18,10,0|ss45k0,17,1,1|su9xjz,17,1,1|su9xk0,18,10,0|tah9jz,18,10,0|tah9k0,17,1,1|tca2vz,17,1,1|tca2w0,18,10,0|tsudjz,18,10,0|tsudk0,17,1,1|tun6vz,17,1,1|tun6w0,18,10,0|uauivz,18,10,0|uauiw0,17,1,1|ud0avz,17,1,1|ud0aw0,18,10,0|ut7mvz,18,10,0|ut7mw0,17,1,1|uv0g7z,17,1,1|uv0g80,18,10,0|vb7s7z,18,10,0|vb7s80,17,1,1|vddk7z,17,1,1|vddk80,18,10,0|vtkw7z,18,10,0|vtkw80,17,1,1|vvqo7z,17,1,1|vvqo80,18,10,0|wby07z,18,10,0|wby080,17,1,1|wdqtjz,17,1,1|wdqtk0,18,10,0|wty5jz,18,10,0|wty5k0,17,1,1|ww3xjz,17,1,1|ww3xk0,18,10,0|xcb9jz,18,10,0|xcb9k0,17,1,1|xe42vz,17,1,1|xe42w0,18,10,0|xubevz,18,10,0|xubew0,17,1,1|xwh6vz,17,1,1|xwh6w0,18,10,0|ycoivz,18,10,0|ycoiw0,17,1,1|yeuavz,17,1,1|yeuaw0,18,10,0|yv1mvz,18,10,0|yv1mw0,17,1,1|ywug7z,17,1,1|ywug80,18,10,0|zd1s7z,18,10,0|zd1s80,17,1,1|zf7k7z,17,1,1|zf7k80,18,10,0\",\"Africa/Freetown|,0,12,0|-u9rgl4,1,1,0\",\"Africa/Gaborone|,0,16,0|-yvtfd8,14,11,0\",\"Africa/Harare|,0,16,0|-yvtfd8,14,11,0\",\"Africa/Johannesburg|,0,21,0|-14nj6io,19,22,0|-yvtdi1,19,22,0|-yvtdi0,19,11,0|-e8lpc1,19,11,0|-e8lpc0,19,6,1|-dz8qs1,19,6,1|-dz8qs0,19,11,0|-dpvmo1,19,11,0|-dpvmo0,19,6,1|-dgio41,19,6,1|-dgio40,19,11,0\",\"Africa/Juba|,0,23,0|-kcrsis,14,11,0|662fz,14,11,0|662g0,20,6,1|er8zz,20,6,1|er900,14,11,0|ow53z,14,11,0|ow540,20,6,1|xj6bz,20,6,1|xj6c0,14,11,0|17px3z,14,11,0|17px40,20,6,1|1gcybz,20,6,1|1gcyc0,14,11,0|1qfzrz,14,11,0|1qfzs0,20,6,1|1z4vnz,20,6,1|1z4vo0,14,11,0|2962fz,14,11,0|2962g0,20,6,1|2hwszz,20,6,1|2hwt00,14,11,0|2rw53z,14,11,0|2rw540,20,6,1|30oqbz,20,6,1|30oqc0,14,11,0|3am7rz,14,11,0|3am7s0,20,6,1|3jiibz,20,6,1|3jiic0,14,11,0|3tcafz,14,11,0|3tcag0,20,6,1|42afnz,20,6,1|42afo0,14,11,0|4cfbrz,14,11,0|4cfbs0,20,6,1|4l2czz,20,6,1|4l2d00,14,11,0|4v5efz,14,11,0|4v5eg0,20,6,1|53uabz,20,6,1|53uac0,14,11,0|5dvh3z,14,11,0|5dvh40,20,6,1|5mo2bz,20,6,1|5mo2c0,14,11,0|5wljrz,14,11,0|5wljs0,20,6,1|65fznz,20,6,1|65fzo0,14,11,0|6fbmfz,14,11,0|6fbmg0,20,6,1|6o7wzz,20,6,1|6o7x00,14,11,0|6y1p3z,14,11,0|6y1p40,20,6,1|76zubz,20,6,1|76zuc0,14,11,0|7h4qfz,14,11,0|7h4qg0,20,6,1|7ptmbz,20,6,1|7ptmc0,14,11,0|7zut3z,14,11,0|7zut40,20,6,1|88ljnz,20,6,1|88ljo0,14,11,0|fodfrz,14,11,0|fodfs0,5,6,0|qntgzz,5,6,0|qnth00,14,11,0\",\"Africa/Kampala|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Africa/Khartoum|,0,24,0|-kcrsow,14,11,0|662fz,14,11,0|662g0,20,6,1|er8zz,20,6,1|er900,14,11,0|ow53z,14,11,0|ow540,20,6,1|xj6bz,20,6,1|xj6c0,14,11,0|17px3z,14,11,0|17px40,20,6,1|1gcybz,20,6,1|1gcyc0,14,11,0|1qfzrz,14,11,0|1qfzs0,20,6,1|1z4vnz,20,6,1|1z4vo0,14,11,0|2962fz,14,11,0|2962g0,20,6,1|2hwszz,20,6,1|2hwt00,14,11,0|2rw53z,14,11,0|2rw540,20,6,1|30oqbz,20,6,1|30oqc0,14,11,0|3am7rz,14,11,0|3am7s0,20,6,1|3jiibz,20,6,1|3jiic0,14,11,0|3tcafz,14,11,0|3tcag0,20,6,1|42afnz,20,6,1|42afo0,14,11,0|4cfbrz,14,11,0|4cfbs0,20,6,1|4l2czz,20,6,1|4l2d00,14,11,0|4v5efz,14,11,0|4v5eg0,20,6,1|53uabz,20,6,1|53uac0,14,11,0|5dvh3z,14,11,0|5dvh40,20,6,1|5mo2bz,20,6,1|5mo2c0,14,11,0|5wljrz,14,11,0|5wljs0,20,6,1|65fznz,20,6,1|65fzo0,14,11,0|6fbmfz,14,11,0|6fbmg0,20,6,1|6o7wzz,20,6,1|6o7x00,14,11,0|6y1p3z,14,11,0|6y1p40,20,6,1|76zubz,20,6,1|76zuc0,14,11,0|7h4qfz,14,11,0|7h4qg0,20,6,1|7ptmbz,20,6,1|7ptmc0,14,11,0|7zut3z,14,11,0|7zut40,20,6,1|88ljnz,20,6,1|88ljo0,14,11,0|fodfrz,14,11,0|fodfs0,5,6,0|oypgzz,5,6,0|oyph00,14,11,0\",\"Africa/Kigali|,0,16,0|-yvtfd8,14,11,0\",\"Africa/Kinshasa|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/Lagos|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/Libreville|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/Lome|,0,12,0|-u9rgl4,1,1,0\",\"Africa/Luanda|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/Lubumbashi|,0,16,0|-yvtfd8,14,11,0\",\"Africa/Lusaka|,0,16,0|-yvtfd8,14,11,0\",\"Africa/Malabo|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/Maputo|,0,16,0|-yvtfd8,14,11,0\",\"Africa/Maseru|,0,21,0|-14nj6io,19,22,0|-yvtdi1,19,22,0|-yvtdi0,19,11,0|-e8lpc1,19,11,0|-e8lpc0,19,6,1|-dz8qs1,19,6,1|-dz8qs0,19,11,0|-dpvmo1,19,11,0|-dpvmo0,19,6,1|-dgio41,19,6,1|-dgio40,19,11,0\",\"Africa/Mbabane|,0,21,0|-14nj6io,19,22,0|-yvtdi1,19,22,0|-yvtdi0,19,11,0|-e8lpc1,19,11,0|-e8lpc0,19,6,1|-dz8qs1,19,6,1|-dz8qs0,19,11,0|-dpvmo1,19,11,0|-dpvmo0,19,6,1|-dgio41,19,6,1|-dgio40,19,11,0\",\"Africa/Mogadishu|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Africa/Monrovia|,0,25,0|-19xcbc4,21,25,0|-qj6zc5,21,25,0|-qj6zc4,21,26,0|11v0q5,21,26,0|11v0q6,1,1,0\",\"Africa/Nairobi|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Africa/Ndjamena|,0,27,0|-u9rk4c,12,10,0|53sl7z,12,10,0|53sl80,22,11,1|5bavrz,22,11,1|5bavs0,12,10,0\",\"Africa/Niamey|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/Nouakchott|,0,12,0|-u9rgl4,1,1,0\",\"Africa/Ouagadougou|,0,12,0|-u9rgl4,1,1,0\",\"Africa/Porto-Novo|,0,13,0|-xnxnan,1,1,0|-w3k001,1,1,0|-w3k000,0,13,0|-t85smo,0,13,0|-t85smn,3,3,0|-q9qc21,3,3,0|-q9qc20,12,10,0\",\"Africa/Sao_Tome|,0,28,0|-18vsjww,0,29,0|-u9rhc1,0,29,0|-u9rhc0,1,1,0|p1uqrz,1,1,0|p1uqs0,12,10,0|pkmo3z,12,10,0|pkmo40,1,1,0\",\"Africa/Tripoli|,0,30,0|-q3gfrw,10,10,0|-9ia581,10,10,0|-9ia580,11,11,1|-9e82w1,11,11,1|-9e82w0,10,10,0|-8gxp81,10,10,0|-8gxp80,11,11,1|-8cmdk1,11,11,1|-8cmdk0,10,10,0|-7fuo41,10,10,0|-7fuo40,11,11,1|-7b2iw1,11,11,1|-7b2iw0,10,10,0|-5qotg1,10,10,0|-5qotg0,15,11,0|69gifz,15,11,0|69gig0,10,10,0|6e397z,10,10,0|6e3980,11,11,1|6ni2fz,11,11,1|6ni2g0,10,10,0|6wv6jz,10,10,0|6wv6k0,11,11,1|769zrz,11,11,1|769zs0,10,10,0|7foyjz,10,10,0|7foyk0,11,11,1|7p3rrz,11,11,1|7p3rs0,10,10,0|7yq57z,10,10,0|7yq580,11,11,1|87vp3z,11,11,1|87vp40,10,10,0|8hed7z,10,10,0|8hed80,11,11,1|8qrbrz,11,11,1|8qrbs0,10,10,0|900qjz,10,10,0|900qk0,11,11,1|99fjrz,11,11,1|99fjs0,10,10,0|9iuijz,10,10,0|9iuik0,11,11,1|9s9brz,11,11,1|9s9bs0,10,10,0|a1mfvz,10,10,0|a1mfw0,11,11,1|ab193z,11,11,1|ab1940,10,10,0|am3h7z,10,10,0|am3h80,15,11,0|dyil3z,15,11,0|dyil40,10,10,0|e833vz,10,10,0|e833w0,11,11,1|ehhx3z,11,11,1|ehhx40,15,11,0|md8vzz,15,11,0|md8w00,10,10,0|mkeanz,10,10,0|mkeao0,11,11,1|mv76nz,11,11,1|mv76o0,15,11,0\",\"Africa/Tunis|,0,31,0|-1a9dr7w,7,9,0|-uozn3m,7,9,0|-uozn3l,10,10,0|-g12881,10,10,0|-g12880,11,11,1|-fpwdk1,11,11,1|-fpwdk0,10,10,0|-fkt1k1,10,10,0|-fkt1k0,11,11,1|-eqk5k1,11,11,1|-eqk5k0,10,10,0|-eimw41,10,10,0|-eimw40,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dxuo01,11,11,1|-dxuo00,10,10,0|-dxfrw1,10,10,0|-dxfrw0,11,11,1|-dp3uo1,11,11,1|-dp3uo0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d62tk1,11,11,1|-d62tk0,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cofmw1,11,11,1|-cofmw0,10,10,0|3tnh7z,10,10,0|3tnh80,11,11,1|417p7z,11,11,1|417p80,10,10,0|4ch97z,10,10,0|4ch980,11,11,1|4kcl7z,11,11,1|4kcl80,10,10,0|9lzh7z,10,10,0|9lzh80,11,11,1|9ryajz,11,11,1|9ryak0,10,10,0|a1bbvz,10,10,0|a1bbw0,11,11,1|aaod7z,11,11,1|aaod80,10,10,0|alxx7z,10,10,0|alxx80,11,11,1|atrejz,11,11,1|atrek0,10,10,0|ifs7vz,10,10,0|ifs7w0,11,11,1|inlrzz,11,11,1|inls00,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0\",\"Africa/Windhoek|,0,32,0|-14nj4i0,23,22,0|-yvtdi1,23,22,0|-yvtdi0,19,11,0|-e8lpc1,19,11,0|-e8lpc0,19,6,1|-dz8qs1,19,6,1|-dz8qs0,19,11,0|ajtx3z,19,11,0|ajtx40,14,11,0|cmzh3z,14,11,0|cmzh40,12,10,1|cvkyrz,12,10,1|cvkys0,14,11,0|d6drzz,14,11,0|d6ds00,12,10,1|deb1fz,12,10,1|deb1g0,14,11,0|dpgtbz,14,11,0|dpgtc0,12,10,1|dx143z,12,10,1|dx1440,14,11,0|e86vzz,14,11,0|e86w00,12,10,1|eg45fz,12,10,1|eg45g0,14,11,0|eqwynz,14,11,0|eqwyo0,12,10,1|eyu83z,12,10,1|eyu840,14,11,0|f9n1bz,14,11,0|f9n1c0,12,10,1|fhkarz,12,10,1|fhkas0,14,11,0|fsd3zz,14,11,0|fsd400,12,10,1|g0adfz,12,10,1|g0adg0,14,11,0|gb36nz,14,11,0|gb36o0,12,10,1|gj0g3z,12,10,1|gj0g40,14,11,0|gu67zz,14,11,0|gu6800,12,10,1|h1qirz,12,10,1|h1qis0,14,11,0|hcwanz,14,11,0|hcwao0,12,10,1|hktk3z,12,10,1|hktk40,14,11,0|hvmdbz,14,11,0|hvmdc0,12,10,1|i3jmrz,12,10,1|i3jms0,14,11,0|iecfzz,14,11,0|iecg00,12,10,1|im9pfz,12,10,1|im9pg0,14,11,0|ix2inz,14,11,0|ix2io0,12,10,1|j4zs3z,12,10,1|j4zs40,14,11,0|jfslbz,14,11,0|jfslc0,12,10,1|jnpurz,12,10,1|jnpus0,14,11,0|jyvmnz,14,11,0|jyvmo0,12,10,1|k6sw3z,12,10,1|k6sw40,14,11,0|khlpbz,14,11,0|khlpc0,12,10,1|kpiyrz,12,10,1|kpiys0,14,11,0|l0brzz,14,11,0|l0bs00,12,10,1|l891fz,12,10,1|l891g0,14,11,0|lj1unz,14,11,0|lj1uo0,12,10,1|lqz43z,12,10,1|lqz440,14,11,0|m1rxbz,14,11,0|m1rxc0,12,10,1|m9p6rz,12,10,1|m9p6s0,14,11,0|mkuynz,14,11,0|mkuyo0,12,10,1|msf9fz,12,10,1|msf9g0,14,11,0|n3l1bz,14,11,0|n3l1c0,12,10,1|nbiarz,12,10,1|nbias0,14,11,0|nmb3zz,14,11,0|nmb400,12,10,1|nu8dfz,12,10,1|nu8dg0,14,11,0|o516nz,14,11,0|o516o0,12,10,1|ocyg3z,12,10,1|ocyg40,14,11,0|onr9bz,14,11,0|onr9c0,12,10,1|ovoirz,12,10,1|ovois0,14,11,0\",\"America/Adak|,0,33,0|-1hc7qjz,0,34,0|-1078omb,0,34,0|-1078oma,24,35,0|-ek1nw1,24,35,0|-ek1nw0,25,36,1|-cq2tg1,25,36,1|-cq2tg0,26,36,1|-cnomo1,26,36,1|-cnomo0,24,35,0|-1fq441,24,35,0|-1fq440,27,35,0|-cs3w1,27,35,0|-cs3w0,28,36,1|-3f5c1,28,36,1|-3f5c0,27,35,0|5xyrz,27,35,0|5xys0,28,36,1|faxbz,28,36,1|faxc0,27,35,0|oo1fz,27,35,0|oo1g0,28,36,1|ydynz,28,36,1|ydyo0,27,35,0|17r2rz,27,35,0|17r2s0,28,36,1|1h41bz,28,36,1|1h41c0,27,35,0|1qh5fz,27,35,0|1qh5g0,28,36,1|1zu3zz,28,36,1|1zu400,27,35,0|23ftfz,27,35,0|23ftg0,28,36,1|2ik6nz,28,36,1|2ik6o0,27,35,0|2oomrz,27,35,0|2ooms0,28,36,1|31a9bz,28,36,1|31a9c0,27,35,0|3andfz,27,35,0|3andg0,28,36,1|3kdanz,28,36,1|3kdao0,27,35,0|3tdg3z,27,35,0|3tdg40,28,36,1|433dbz,28,36,1|433dc0,27,35,0|4cghfz,27,35,0|4cghg0,28,36,1|4ltfzz,28,36,1|4ltg00,27,35,0|4v6k3z,27,35,0|4v6k40,28,36,1|54jinz,28,36,1|54jio0,27,35,0|5dwmrz,27,35,0|5dwms0,28,36,1|5n9lbz,28,36,1|5n9lc0,27,35,0|5wmpfz,27,35,0|5wmpg0,28,36,1|65znzz,28,36,1|65zo00,27,35,0|6fcs3z,27,35,0|6fcs40,28,36,1|6p2pbz,28,36,1|6p2pc0,27,35,0|6y2urz,27,35,0|6y2us0,28,36,1|77srzz,28,36,1|77ss00,29,36,0|79e13z,29,36,0|79e140,30,36,0|7h5tbz,30,36,0|7h5tc0,31,37,1|7qirvz,31,37,1|7qirw0,30,36,0|7zvvzz,30,36,0|7zvw00,31,37,1|898ujz,31,37,1|898uk0,30,36,0|8ilynz,30,36,0|8ilyo0,31,37,1|8ryx7z,31,37,1|8ryx80,30,36,0|9095bz,30,36,0|9095c0,31,37,1|9aozvz,31,37,1|9aozw0,30,36,0|9iz7zz,30,36,0|9iz800,31,37,1|9ts17z,31,37,1|9ts180,30,36,0|a1panz,30,36,0|a1pao0,31,37,1|aci3vz,31,37,1|aci3w0,30,36,0|akfdbz,30,36,0|akfdc0,31,37,1|av86jz,31,37,1|av86k0,30,36,0|b3ienz,30,36,0|b3ieo0,31,37,1|bdy97z,31,37,1|bdy980,30,36,0|bm8hbz,30,36,0|bm8hc0,31,37,1|bwobvz,31,37,1|bwobw0,30,36,0|c4yjzz,30,36,0|c4yk00,31,37,1|cfrd7z,31,37,1|cfrd80,30,36,0|cnomnz,30,36,0|cnomo0,31,37,1|cyhfvz,31,37,1|cyhfw0,30,36,0|d6epbz,30,36,0|d6epc0,31,37,1|dh7ijz,31,37,1|dh7ik0,30,36,0|dphqnz,30,36,0|dphqo0,31,37,1|dzxl7z,31,37,1|dzxl80,30,36,0|e87tbz,30,36,0|e87tc0,31,37,1|einnvz,31,37,1|einnw0,30,36,0|eqxvzz,30,36,0|eqxw00,31,37,1|f1dqjz,31,37,1|f1dqk0,30,36,0|f9nynz,30,36,0|f9nyo0,31,37,1|fkgrvz,31,37,1|fkgrw0,30,36,0|fse1bz,30,36,0|fse1c0,31,37,1|g36ujz,31,37,1|g36uk0,30,36,0|gb43zz,30,36,0|gb4400,31,37,1|glwx7z,31,37,1|glwx80,30,36,0|gu75bz,30,36,0|gu75c0,31,37,1|h4mzvz,31,37,1|h4mzw0,30,36,0|hcx7zz,30,36,0|hcx800,31,37,1|hnd2jz,31,37,1|hnd2k0,30,36,0|hvnanz,30,36,0|hvnao0,31,37,1|i6g3vz,31,37,1|i6g3w0,30,36,0|ieddbz,30,36,0|ieddc0,31,37,1|ip66jz,31,37,1|ip66k0,30,36,0|ix3fzz,30,36,0|ix3g00,31,37,1|j7w97z,31,37,1|j7w980,30,36,0|jeqmnz,30,36,0|jeqmo0,31,37,1|jqzajz,31,37,1|jqzak0,30,36,0|jxgpbz,30,36,0|jxgpc0,31,37,1|k9pd7z,31,37,1|k9pd80,30,36,0|kg6rzz,30,36,0|kg6s00,31,37,1|ksffvz,31,37,1|ksffw0,30,36,0|kz9tbz,30,36,0|kz9tc0,31,37,1|lbih7z,31,37,1|lbih80,30,36,0|lhzvzz,30,36,0|lhzw00,31,37,1|lu8jvz,31,37,1|lu8jw0,30,36,0|m0pynz,30,36,0|m0pyo0,31,37,1|mcymjz,31,37,1|mcymk0,30,36,0|mjg1bz,30,36,0|mjg1c0,31,37,1|mvop7z,31,37,1|mvop80,30,36,0|n263zz,30,36,0|n26400,31,37,1|neervz,31,37,1|neerw0,30,36,0|nkw6nz,30,36,0|nkw6o0,31,37,1|nx4ujz,31,37,1|nx4uk0,30,36,0|o3z7zz,30,36,0|o3z800,31,37,1|og7vvz,31,37,1|og7vw0,30,36,0|ompanz,30,36,0|ompao0,31,37,1|oyxyjz,31,37,1|oyxyk0,30,36,0|p5fdbz,30,36,0|p5fdc0,31,37,1|pho17z,31,37,1|pho180,30,36,0|po5fzz,30,36,0|po5g00,31,37,1|q0e3vz,31,37,1|q0e3w0,30,36,0|q6vinz,30,36,0|q6vio0,31,37,1|qj46jz,31,37,1|qj46k0,30,36,0|qpyjzz,30,36,0|qpyk00,31,37,1|r277vz,31,37,1|r277w0,30,36,0|r8omnz,30,36,0|r8omo0,31,37,1|rkxajz,31,37,1|rkxak0,30,36,0|rrepbz,30,36,0|rrepc0,31,37,1|s3nd7z,31,37,1|s3nd80,30,36,0|sa4rzz,30,36,0|sa4s00,31,37,1|smdfvz,31,37,1|smdfw0,30,36,0|ssuunz,30,36,0|ssuuo0,31,37,1|t53ijz,31,37,1|t53ik0,30,36,0|tbkxbz,30,36,0|tbkxc0,31,37,1|tntl7z,31,37,1|tntl80,30,36,0|tunynz,30,36,0|tunyo0,31,37,1|u6wmjz,31,37,1|u6wmk0,30,36,0|ude1bz,30,36,0|ude1c0,31,37,1|upmp7z,31,37,1|upmp80,30,36,0|uw43zz,30,36,0|uw4400,31,37,1|v8crvz,31,37,1|v8crw0,30,36,0|veu6nz,30,36,0|veu6o0,31,37,1|vr2ujz,31,37,1|vr2uk0,30,36,0|vxk9bz,30,36,0|vxk9c0,31,37,1|w9sx7z,31,37,1|w9sx80,30,36,0|wgnanz,30,36,0|wgnao0,31,37,1|wsvyjz,31,37,1|wsvyk0,30,36,0|wzddbz,30,36,0|wzddc0,31,37,1|xbm17z,31,37,1|xbm180,30,36,0|xi3fzz,30,36,0|xi3g00,31,37,1|xuc3vz,31,37,1|xuc3w0,30,36,0|y0tinz,30,36,0|y0tio0,31,37,1|yd26jz,31,37,1|yd26k0,30,36,0|yjjlbz,30,36,0|yjjlc0,31,37,1|yvs97z,31,37,1|yvs980,30,36,0|z29nzz,30,36,0|z29o00,31,37,1|zeibvz,31,37,1|zeibw0,30,36,0\",\"America/Anchorage|,0,38,0|-1hc7qjz,0,39,0|-1078tkp,0,39,0|-1078tko,32,36,0|-ek1qo1,32,36,0|-ek1qo0,33,37,1|-cq2tg1,33,37,1|-cq2tg0,34,37,1|-cnopg1,34,37,1|-cnopg0,32,36,0|-1fq6w1,32,36,0|-1fq6w0,29,36,0|-cs6o1,29,36,0|-cs6o0,35,37,1|-3f841,35,37,1|-3f840,29,36,0|5xvzz,29,36,0|5xw00,35,37,1|faujz,35,37,1|fauk0,29,36,0|onynz,29,36,0|onyo0,35,37,1|ydvvz,35,37,1|ydvw0,29,36,0|17qzzz,29,36,0|17r000,35,37,1|1h3yjz,35,37,1|1h3yk0,29,36,0|1qh2nz,29,36,0|1qh2o0,35,37,1|1zu17z,35,37,1|1zu180,29,36,0|23fqnz,29,36,0|23fqo0,35,37,1|2ik3vz,35,37,1|2ik3w0,29,36,0|2oojzz,29,36,0|2ook00,35,37,1|31a6jz,35,37,1|31a6k0,29,36,0|3ananz,29,36,0|3anao0,35,37,1|3kd7vz,35,37,1|3kd7w0,29,36,0|3tddbz,29,36,0|3tddc0,35,37,1|433ajz,35,37,1|433ak0,29,36,0|4cgenz,29,36,0|4cgeo0,35,37,1|4ltd7z,35,37,1|4ltd80,29,36,0|4v6hbz,29,36,0|4v6hc0,35,37,1|54jfvz,35,37,1|54jfw0,29,36,0|5dwjzz,29,36,0|5dwk00,35,37,1|5n9ijz,35,37,1|5n9ik0,29,36,0|5wmmnz,29,36,0|5wmmo0,35,37,1|65zl7z,35,37,1|65zl80,29,36,0|6fcpbz,29,36,0|6fcpc0,35,37,1|6p2mjz,35,37,1|6p2mk0,29,36,0|6y2rzz,29,36,0|6y2s00,35,37,1|77sp7z,35,37,1|77sp80,36,37,0|79dybz,36,37,0|79dyc0,37,37,0|7h5qjz,37,37,0|7h5qk0,38,40,1|7qip3z,38,40,1|7qip40,37,37,0|7zvt7z,37,37,0|7zvt80,38,40,1|898rrz,38,40,1|898rs0,37,37,0|8ilvvz,37,37,0|8ilvw0,38,40,1|8ryufz,38,40,1|8ryug0,37,37,0|9092jz,37,37,0|9092k0,38,40,1|9aox3z,38,40,1|9aox40,37,37,0|9iz57z,37,37,0|9iz580,38,40,1|9tryfz,38,40,1|9tryg0,37,37,0|a1p7vz,37,37,0|a1p7w0,38,40,1|aci13z,38,40,1|aci140,37,37,0|akfajz,37,37,0|akfak0,38,40,1|av83rz,38,40,1|av83s0,37,37,0|b3ibvz,37,37,0|b3ibw0,38,40,1|bdy6fz,38,40,1|bdy6g0,37,37,0|bm8ejz,37,37,0|bm8ek0,38,40,1|bwo93z,38,40,1|bwo940,37,37,0|c4yh7z,37,37,0|c4yh80,38,40,1|cfrafz,38,40,1|cfrag0,37,37,0|cnojvz,37,37,0|cnojw0,38,40,1|cyhd3z,38,40,1|cyhd40,37,37,0|d6emjz,37,37,0|d6emk0,38,40,1|dh7frz,38,40,1|dh7fs0,37,37,0|dphnvz,37,37,0|dphnw0,38,40,1|dzxifz,38,40,1|dzxig0,37,37,0|e87qjz,37,37,0|e87qk0,38,40,1|einl3z,38,40,1|einl40,37,37,0|eqxt7z,37,37,0|eqxt80,38,40,1|f1dnrz,38,40,1|f1dns0,37,37,0|f9nvvz,37,37,0|f9nvw0,38,40,1|fkgp3z,38,40,1|fkgp40,37,37,0|fsdyjz,37,37,0|fsdyk0,38,40,1|g36rrz,38,40,1|g36rs0,37,37,0|gb417z,37,37,0|gb4180,38,40,1|glwufz,38,40,1|glwug0,37,37,0|gu72jz,37,37,0|gu72k0,38,40,1|h4mx3z,38,40,1|h4mx40,37,37,0|hcx57z,37,37,0|hcx580,38,40,1|hnczrz,38,40,1|hnczs0,37,37,0|hvn7vz,37,37,0|hvn7w0,38,40,1|i6g13z,38,40,1|i6g140,37,37,0|iedajz,37,37,0|iedak0,38,40,1|ip63rz,38,40,1|ip63s0,37,37,0|ix3d7z,37,37,0|ix3d80,38,40,1|j7w6fz,38,40,1|j7w6g0,37,37,0|jeqjvz,37,37,0|jeqjw0,38,40,1|jqz7rz,38,40,1|jqz7s0,37,37,0|jxgmjz,37,37,0|jxgmk0,38,40,1|k9pafz,38,40,1|k9pag0,37,37,0|kg6p7z,37,37,0|kg6p80,38,40,1|ksfd3z,38,40,1|ksfd40,37,37,0|kz9qjz,37,37,0|kz9qk0,38,40,1|lbiefz,38,40,1|lbieg0,37,37,0|lhzt7z,37,37,0|lhzt80,38,40,1|lu8h3z,38,40,1|lu8h40,37,37,0|m0pvvz,37,37,0|m0pvw0,38,40,1|mcyjrz,38,40,1|mcyjs0,37,37,0|mjfyjz,37,37,0|mjfyk0,38,40,1|mvomfz,38,40,1|mvomg0,37,37,0|n2617z,37,37,0|n26180,38,40,1|neep3z,38,40,1|neep40,37,37,0|nkw3vz,37,37,0|nkw3w0,38,40,1|nx4rrz,38,40,1|nx4rs0,37,37,0|o3z57z,37,37,0|o3z580,38,40,1|og7t3z,38,40,1|og7t40,37,37,0|omp7vz,37,37,0|omp7w0,38,40,1|oyxvrz,38,40,1|oyxvs0,37,37,0|p5fajz,37,37,0|p5fak0,38,40,1|phnyfz,38,40,1|phnyg0,37,37,0|po5d7z,37,37,0|po5d80,38,40,1|q0e13z,38,40,1|q0e140,37,37,0|q6vfvz,37,37,0|q6vfw0,38,40,1|qj43rz,38,40,1|qj43s0,37,37,0|qpyh7z,37,37,0|qpyh80,38,40,1|r2753z,38,40,1|r27540,37,37,0|r8ojvz,37,37,0|r8ojw0,38,40,1|rkx7rz,38,40,1|rkx7s0,37,37,0|rremjz,37,37,0|rremk0,38,40,1|s3nafz,38,40,1|s3nag0,37,37,0|sa4p7z,37,37,0|sa4p80,38,40,1|smdd3z,38,40,1|smdd40,37,37,0|ssurvz,37,37,0|ssurw0,38,40,1|t53frz,38,40,1|t53fs0,37,37,0|tbkujz,37,37,0|tbkuk0,38,40,1|tntifz,38,40,1|tntig0,37,37,0|tunvvz,37,37,0|tunvw0,38,40,1|u6wjrz,38,40,1|u6wjs0,37,37,0|uddyjz,37,37,0|uddyk0,38,40,1|upmmfz,38,40,1|upmmg0,37,37,0|uw417z,37,37,0|uw4180,38,40,1|v8cp3z,38,40,1|v8cp40,37,37,0|veu3vz,37,37,0|veu3w0,38,40,1|vr2rrz,38,40,1|vr2rs0,37,37,0|vxk6jz,37,37,0|vxk6k0,38,40,1|w9sufz,38,40,1|w9sug0,37,37,0|wgn7vz,37,37,0|wgn7w0,38,40,1|wsvvrz,38,40,1|wsvvs0,37,37,0|wzdajz,37,37,0|wzdak0,38,40,1|xblyfz,38,40,1|xblyg0,37,37,0|xi3d7z,37,37,0|xi3d80,38,40,1|xuc13z,38,40,1|xuc140,37,37,0|y0tfvz,37,37,0|y0tfw0,38,40,1|yd23rz,38,40,1|yd23s0,37,37,0|yjjijz,37,37,0|yjjik0,38,40,1|yvs6fz,38,40,1|yvs6g0,37,37,0|z29l7z,37,37,0|z29l80,38,40,1|zei93z,38,40,1|zei940,37,37,0\",\"America/Anguilla|,0,41,0|-u6m79w,32,42,0\",\"America/Antigua|,0,41,0|-u6m79w,32,42,0\",\"America/Araguaina|,0,43,0|-t85j2o,39,44,0|-jylag1,39,44,0|-jylag0,40,45,1|-jpb141,40,45,1|-jpb140,39,44,0|-jfsd01,39,44,0|-jfsd00,40,45,1|-j6j3s1,40,45,1|-j6j3s0,39,44,0|-ahcyc1,39,44,0|-ahcyc0,40,45,1|-aad3o1,40,45,1|-aad3o0,39,44,0|-9yl101,39,44,0|-9yl100,40,45,1|-9sd141,40,45,1|-9sd140,39,44,0|-9ft3o1,39,44,0|-9ft3o0,40,45,1|-99j941,40,45,1|-99j940,39,44,0|-8wzbo1,39,44,0|-8wzbo0,40,45,1|-8scqg1,40,45,1|-8scqg0,39,44,0|-35xmc1,39,44,0|-35xmc0,40,45,1|-31nzs1,40,45,1|-31nzs0,39,44,0|-2kdp01,39,44,0|-2kdp00,40,45,1|-2hcig1,40,45,1|-2hcig0,39,44,0|-24qqc1,39,44,0|-24qqc0,40,45,1|-204541,40,45,1|-204540,39,44,0|-1nid01,39,44,0|-1nid00,40,45,1|-1hc7s1,40,45,1|-1hc7s0,39,44,0|-14qfo1,39,44,0|-14qfo0,40,45,1|-yifs1,40,45,1|-yifs0,39,44,0|89jcbz,39,44,0|89jcc0,40,45,1|8gdk7z,40,45,1|8gdk80,39,44,0|8rwgbz,39,44,0|8rwgc0,40,45,1|8xns7z,40,45,1|8xns80,39,44,0|9aodnz,39,44,0|9aodo0,40,45,1|9g2qvz,40,45,1|9g2qw0,39,44,0|9t1hnz,39,44,0|9t1ho0,40,45,1|9yfuvz,40,45,1|9yfuw0,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|ahvuvz,40,45,1|ahvuw0,39,44,0|dggyzz,39,44,0|dggz00,40,45,1|dml9jz,40,45,1|dml9k0,39,44,0|dyu2zz,39,44,0|dyu300,40,45,1|e5oavz,40,45,1|e5oaw0,39,44,0|ehm0bz,39,44,0|ehm0c0,40,45,1|ep4avz,40,45,1|ep4aw0,39,44,0|f0n6zz,39,44,0|f0n700,40,45,1|f7hevz,40,45,1|f7hew0,39,44,0|fj0azz,39,44,0|fj0b00,40,45,1|fqkg7z,40,45,1|fqkg80,39,44,0|g23cbz,39,44,0|g23cc0,40,45,1|g8xk7z,40,45,1|g8xk80,39,44,0|gl6dnz,39,44,0|gl6do0,40,45,1|grnmvz,40,45,1|grnmw0,39,44,0|h4zcbz,39,44,0|h4zcc0,40,45,1|hadpjz,40,45,1|hadpk0,39,44,0|mc82zz,39,44,0|mc8300,40,45,1|micdjz,40,45,1|micdk0,39,44,0\",\"America/Argentina/Buenos_Aires|,0,46,0|-138aaic,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1otjz,40,45,1|b1otk0,39,44,0|bdkobz,39,44,0|bdkoc0,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0|k8ytnz,39,44,0|k8yto0,40,45,1|kgiyvz,40,45,1|kgiyw0,39,44,0\",\"America/Argentina/Catamarca|,0,48,0|-138a95g,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1otjz,40,45,1|b1otk0,42,42,0|bdkr3z,42,42,0|bdkr40,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|hym0bz,39,44,0|hym0c0,42,42,0|hzl9rz,42,42,0|hzl9s0,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0\",\"America/Argentina/Cordoba|,0,47,0|-138a9g0,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1otjz,40,45,1|b1otk0,42,42,0|bdkr3z,42,42,0|bdkr40,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0|k8ytnz,39,44,0|k8yto0,40,45,1|kgiyvz,40,45,1|kgiyw0,39,44,0\",\"America/Argentina/Jujuy|,0,49,0|-138a98o,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,42,42,0|av7n3z,42,42,0|av7n40,39,44,1|b2etnz,39,44,1|b2eto0,42,42,0|bcutrz,42,42,0|bcuts0,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0\",\"America/Argentina/La_Rioja|,0,50,0|-138a8yc,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1l47z,40,45,1|b1l480,42,42,0|b51cfz,42,42,0|b51cg0,39,44,0|bdkobz,39,44,0|bdkoc0,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|hym0bz,39,44,0|hym0c0,42,42,0|hzl9rz,42,42,0|hzl9s0,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0\",\"America/Argentina/Mendoza|,0,51,0|-138a8l8,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,42,42,0|aujkfz,42,42,0|aujkg0,39,44,1|b1l6zz,39,44,1|b1l700,42,42,0|bdbhrz,42,42,0|bdbhs0,39,44,1|bkeyzz,39,44,1|bkez00,42,42,0|bwatrz,42,42,0|bwats0,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|hy5cbz,39,44,0|hy5cc0,42,42,0|i4mr3z,42,42,0|i4mr40,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0\",\"America/Argentina/Rio_Gallegos|,0,52,0|-138a8ik,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1otjz,40,45,1|b1otk0,39,44,0|bdkobz,39,44,0|bdkoc0,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|hym0bz,39,44,0|hym0c0,42,42,0|hzl9rz,42,42,0|hzl9s0,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0\",\"America/Argentina/Salta|,0,53,0|-138a97w,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1otjz,40,45,1|b1otk0,42,42,0|bdkr3z,42,42,0|bdkr40,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0\",\"America/Argentina/San_Juan|,0,54,0|-138a8n8,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1l47z,40,45,1|b1l480,42,42,0|b51cfz,42,42,0|b51cg0,39,44,0|bdkobz,39,44,0|bdkoc0,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|hyk5nz,39,44,0|hyk5o0,42,42,0|i1e33z,42,42,0|i1e340,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0\",\"America/Argentina/San_Luis|,0,55,0|-138a91o,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|ajh9jz,40,45,1|ajh9k0,42,42,0|aujkfz,42,42,0|aujkg0,39,44,1|b1l6zz,39,44,1|b1l700,42,42,0|b6bn3z,42,42,0|b6bn40,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|hyk5nz,39,44,0|hyk5o0,42,42,0|i1e33z,42,42,0|i1e340,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|juz1jz,40,45,1|juz1k0,39,44,1|jxg0bz,39,44,1|jxg0c0,42,42,0|k8lxrz,42,42,0|k8lxs0,39,44,1|kg62zz,39,44,1|kg6300,42,42,0|krc0fz,42,42,0|krc0g0,39,44,0\",\"America/Argentina/Tucuman|,0,56,0|-138a998,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1otjz,40,45,1|b1otk0,42,42,0|bdkr3z,42,42,0|bdkr40,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|hym0bz,39,44,0|hym0c0,42,42,0|hz8b3z,42,42,0|hz8b40,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0|k8ytnz,39,44,0|k8yto0,40,45,1|kgiyvz,40,45,1|kgiyw0,39,44,0\",\"America/Argentina/Ushuaia|,0,57,0|-138a8oo,41,47,0|-px7ys1,41,47,0|-px7ys0,42,42,0|-kecq81,42,42,0|-kecq80,39,44,1|-k84qc1,39,44,1|-k84qc0,42,42,0|-jxzu81,42,42,0|-jxzu80,39,44,1|-jqwd01,39,44,1|-jqwd00,42,42,0|-jeakw1,42,42,0|-jeakw0,39,44,1|-j84fo1,39,44,1|-j84fo0,42,42,0|-ivink1,42,42,0|-ivink0,39,44,1|-ipcic1,39,44,1|-ipcic0,42,42,0|-icqq81,42,42,0|-icqq80,39,44,1|-i6kl01,39,44,1|-i6kl00,42,42,0|-htysw1,42,42,0|-htysw0,39,44,1|-hnqt01,39,44,1|-hnqt00,42,42,0|-hb50w1,42,42,0|-hb50w0,39,44,1|-h4yvo1,39,44,1|-h4yvo0,42,42,0|-gsd3k1,42,42,0|-gsd3k0,39,44,1|-gm6yc1,39,44,1|-gm6yc0,42,42,0|-g9l681,42,42,0|-g9l680,39,44,1|-g3f101,39,44,1|-g3f100,42,42,0|-fqt8w1,42,42,0|-fqt8w0,39,44,1|-fkl901,39,44,1|-fkl900,42,42,0|-feb8w1,42,42,0|-feb8w0,39,44,1|-ewd101,39,44,1|-ewd100,42,42,0|-eq30w1,42,42,0|-eq30w0,39,44,1|-dse501,39,44,1|-dse500,42,42,0|-doj681,42,42,0|-doj680,39,44,1|-cfvuc1,39,44,1|-cfvuc0,42,42,0|-c4vgw1,42,42,0|-c4vgw0,39,44,1|-39hec1,39,44,1|-39hec0,42,42,0|-35mfk1,42,42,0|-35mfk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2pxm81,42,42,0|-2pxm80,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|9veobz,39,44,0|9veoc0,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|aiyqvz,40,45,1|aiyqw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1otjz,40,45,1|b1otk0,39,44,0|bdkobz,39,44,0|bdkoc0,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c3hxjz,40,45,1|c3hxk0,39,44,0|fj0azz,39,44,0|fj0b00,39,44,1|fqtsbz,39,44,1|fqtsc0,39,44,0|hyiazz,39,44,0|hyib00,42,42,0|hzl9rz,42,42,0|hzl9s0,39,44,0|jtudnz,39,44,0|jtudo0,40,45,1|jxsw7z,40,45,1|jxsw80,39,44,0\",\"America/Aruba|,0,58,0|-u7lckd,43,59,0|-2lx4u1,43,59,0|-2lx4u0,32,42,0\",\"America/Asuncion|,0,60,0|-15r0ynk,44,60,0|-jy93zl,44,60,0|-jy93zk,42,42,0|1fnkfz,42,42,0|1fnkg0,39,44,0|27sgbz,39,44,0|27sgc0,42,42,0|2zzcfz,42,42,0|2zzcg0,39,44,1|37sqzz,39,44,1|37sr00,42,42,0|3it4fz,42,42,0|3it4g0,39,44,1|3qkobz,39,44,1|3qkoc0,42,42,0|41l1rz,42,42,0|41l1s0,39,44,1|49clnz,39,44,1|49clo0,42,42,0|4kcz3z,42,42,0|4kcz40,39,44,1|4tpxnz,39,44,1|4tpxo0,42,42,0|534wfz,42,42,0|534wg0,39,44,1|5cjpnz,39,44,1|5cjpo0,42,42,0|5lyofz,42,42,0|5lyog0,39,44,1|5vbmzz,39,44,1|5vbn00,42,42,0|64qlrz,42,42,0|64qls0,39,44,1|6e3kbz,39,44,1|6e3kc0,42,42,0|6nij3z,42,42,0|6nij40,39,44,1|6wvhnz,39,44,1|6wvho0,42,42,0|76agfz,42,42,0|76agg0,39,44,1|7fp9nz,39,44,1|7fp9o0,42,42,0|7p48fz,42,42,0|7p48g0,39,44,1|7yh6zz,39,44,1|7yh700,42,42,0|87w5rz,42,42,0|87w5s0,39,44,1|8h94bz,39,44,1|8h94c0,42,42,0|8qo33z,42,42,0|8qo340,39,44,1|9011nz,39,44,1|9011o0,42,42,0|99g0fz,42,42,0|99g0g0,39,44,1|9iutnz,39,44,1|9iuto0,42,42,0|9s9sfz,42,42,0|9s9sg0,39,44,1|a1mqzz,39,44,1|a1mr00,42,42,0|ac4lrz,42,42,0|ac4ls0,39,44,1|akeobz,39,44,1|akeoc0,42,42,0|attn3z,42,42,0|attn40,39,44,1|b36lnz,39,44,1|b36lo0,42,42,0|bcutrz,42,42,0|bcuts0,39,44,1|bkeyzz,39,44,1|bkez00,42,42,0|bvmr3z,42,42,0|bvmr40,39,44,1|c4qgbz,39,44,1|c4qgc0,42,42,0|ce79rz,42,42,0|ce79s0,39,44,1|clv4bz,39,44,1|clv4c0,42,42,0|cwz73z,42,42,0|cwz740,39,44,1|d4l6zz,39,44,1|d4l700,42,42,0|dfr4fz,42,42,0|dfr4g0,39,44,1|dnkizz,39,44,1|dnkj00,42,42,0|dyu5rz,42,42,0|dyu5s0,39,44,1|e61cbz,39,44,1|e61cc0,42,42,0|ehk8fz,42,42,0|ehk8g0,39,44,1|ep4dnz,39,44,1|ep4do0,42,42,0|f0ab3z,42,42,0|f0ab40,39,44,1|f87ezz,39,44,1|f87f00,42,42,0|fj0drz,42,42,0|fj0ds0,39,44,1|fqxhnz,39,44,1|fqxho0,42,42,0|g1qgfz,42,42,0|g1qgg0,39,44,1|g9nkbz,39,44,1|g9nkc0,42,42,0|gkthrz,42,42,0|gkths0,39,44,1|gu6gbz,39,44,1|gu6gc0,42,42,0|h1qr3z,42,42,0|h1qr40,39,44,1|hcwizz,39,44,1|hcwj00,42,42,0|hktsfz,42,42,0|hktsg0,39,44,1|hvmlnz,39,44,1|hvmlo0,42,42,0|i5pn3z,42,42,0|i5pn40,39,44,1|id9sbz,39,44,1|id9sc0,42,42,0|iofprz,42,42,0|iofps0,39,44,1|ivzuzz,39,44,1|ivzv00,42,42,0|j75sfz,42,42,0|j75sg0,39,44,1|jepxnz,39,44,1|jepxo0,42,42,0|jq8trz,42,42,0|jq8ts0,39,44,1|jxg0bz,39,44,1|jxg0c0,42,42,0|k8ywfz,42,42,0|k8ywg0,39,44,1|kg62zz,39,44,1|kg6300,42,42,0|kroz3z,42,42,0|kroz40,39,44,1|l0oyzz,39,44,1|l0oz00,42,42,0|l9p4fz,42,42,0|l9p4g0,39,44,1|ljf1nz,39,44,1|ljf1o0,42,42,0|lsf73z,42,42,0|lsf740,39,44,1|m254bz,39,44,1|m254c0,42,42,0|mbi8fz,42,42,0|mbi8g0,39,44,1|mk59nz,39,44,1|mk59o0,42,42,0|mu8b3z,42,42,0|mu8b40,39,44,1|n2vcbz,39,44,1|n2vcc0,42,42,0|ncydrz,42,42,0|ncyds0,39,44,1|nllezz,39,44,1|nllf00,42,42,0|nvogfz,42,42,0|nvogg0,39,44,1|o4ogbz,39,44,1|o4ogc0,42,42,0|oeej3z,42,42,0|oeej40,39,44,1|oneizz,39,44,1|onej00,42,42,0|ox4lrz,42,42,0|ox4ls0,39,44,1|p64lnz,39,44,1|p64lo0,42,42,0|pg7n3z,42,42,0|pg7n40,39,44,1|pouobz,39,44,1|pouoc0,42,42,0|pyxprz,42,42,0|pyxps0,39,44,1|q7kqzz,39,44,1|q7kr00,42,42,0|qhnsfz,42,42,0|qhnsg0,39,44,1|qqnsbz,39,44,1|qqnsc0,42,42,0|r0dv3z,42,42,0|r0dv40,39,44,1|r9duzz,39,44,1|r9dv00,42,42,0|rj3xrz,42,42,0|rj3xs0,39,44,1|rs3xnz,39,44,1|rs3xo0,42,42,0|s1u0fz,42,42,0|s1u0g0,39,44,1|sau0bz,39,44,1|sau0c0,42,42,0|skx1rz,42,42,0|skx1s0,39,44,1|stk2zz,39,44,1|stk300,42,42,0|t3n4fz,42,42,0|t3n4g0,39,44,1|tca5nz,39,44,1|tca5o0,42,42,0|tmd73z,42,42,0|tmd740,39,44,1|tvd6zz,39,44,1|tvd700,42,42,0|u539rz,42,42,0|u539s0,39,44,1|ue39nz,39,44,1|ue39o0,42,42,0|untcfz,42,42,0|untcg0,39,44,1|uwtcbz,39,44,1|uwtcc0,42,42,0|v6wdrz,42,42,0|v6wds0,39,44,1|vfjezz,39,44,1|vfjf00,42,42,0|vpmgfz,42,42,0|vpmgg0,39,44,1|vy9hnz,39,44,1|vy9ho0,42,42,0|w8cj3z,42,42,0|w8cj40,39,44,1|whcizz,39,44,1|whcj00,42,42,0|wr2lrz,42,42,0|wr2ls0,39,44,1|x02lnz,39,44,1|x02lo0,42,42,0|x9sofz,42,42,0|x9sog0,39,44,1|xisobz,39,44,1|xisoc0,42,42,0|xsir3z,42,42,0|xsir40,39,44,1|y1iqzz,39,44,1|y1ir00,42,42,0|yblsfz,42,42,0|yblsg0,39,44,1|yk8tnz,39,44,1|yk8to0,42,42,0|yubv3z,42,42,0|yubv40,39,44,1|z2ywbz,39,44,1|z2ywc0,42,42,0|zd1xrz,42,42,0|zd1xs0,39,44,1\",\"America/Atikokan|,0,61,0|-1353b18,45,62,0|-qzov41,45,62,0|-qzov40,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-f9ofc1,45,62,0|-f9ofc0,46,63,1|-ek21s1,46,63,1|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,49,63,0\",\"America/Bahia|,0,64,0|-t85kv8,39,44,0|-jylag1,39,44,0|-jylag0,40,45,1|-jpb141,40,45,1|-jpb140,39,44,0|-jfsd01,39,44,0|-jfsd00,40,45,1|-j6j3s1,40,45,1|-j6j3s0,39,44,0|-ahcyc1,39,44,0|-ahcyc0,40,45,1|-aad3o1,40,45,1|-aad3o0,39,44,0|-9yl101,39,44,0|-9yl100,40,45,1|-9sd141,40,45,1|-9sd140,39,44,0|-9ft3o1,39,44,0|-9ft3o0,40,45,1|-99j941,40,45,1|-99j940,39,44,0|-8wzbo1,39,44,0|-8wzbo0,40,45,1|-8scqg1,40,45,1|-8scqg0,39,44,0|-35xmc1,39,44,0|-35xmc0,40,45,1|-31nzs1,40,45,1|-31nzs0,39,44,0|-2kdp01,39,44,0|-2kdp00,40,45,1|-2hcig1,40,45,1|-2hcig0,39,44,0|-24qqc1,39,44,0|-24qqc0,40,45,1|-204541,40,45,1|-204540,39,44,0|-1nid01,39,44,0|-1nid00,40,45,1|-1hc7s1,40,45,1|-1hc7s0,39,44,0|-14qfo1,39,44,0|-14qfo0,40,45,1|-yifs1,40,45,1|-yifs0,39,44,0|89jcbz,39,44,0|89jcc0,40,45,1|8gdk7z,40,45,1|8gdk80,39,44,0|8rwgbz,39,44,0|8rwgc0,40,45,1|8xns7z,40,45,1|8xns80,39,44,0|9aodnz,39,44,0|9aodo0,40,45,1|9g2qvz,40,45,1|9g2qw0,39,44,0|9t1hnz,39,44,0|9t1ho0,40,45,1|9yfuvz,40,45,1|9yfuw0,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|ahvuvz,40,45,1|ahvuw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b0yw7z,40,45,1|b0yw80,39,44,0|bdkobz,39,44,0|bdkoc0,40,45,1|bjc07z,40,45,1|bjc080,39,44,0|bwnpnz,39,44,0|bwnpo0,40,45,1|c1p47z,40,45,1|c1p480,39,44,0|cf0tnz,39,44,0|cf0to0,40,45,1|cli2vz,40,45,1|cli2w0,39,44,0|cxqwbz,39,44,0|cxqwc0,40,45,1|d485jz,40,45,1|d485k0,39,44,0|dggyzz,39,44,0|dggz00,40,45,1|dml9jz,40,45,1|dml9k0,39,44,0|dyu2zz,39,44,0|dyu300,40,45,1|e5oavz,40,45,1|e5oaw0,39,44,0|ehm0bz,39,44,0|ehm0c0,40,45,1|ep4avz,40,45,1|ep4aw0,39,44,0|f0n6zz,39,44,0|f0n700,40,45,1|f7hevz,40,45,1|f7hew0,39,44,0|fj0azz,39,44,0|fj0b00,40,45,1|fqkg7z,40,45,1|fqkg80,39,44,0|g23cbz,39,44,0|g23cc0,40,45,1|g8xk7z,40,45,1|g8xk80,39,44,0|gl6dnz,39,44,0|gl6do0,40,45,1|grnmvz,40,45,1|grnmw0,39,44,0|h4zcbz,39,44,0|h4zcc0,40,45,1|hadpjz,40,45,1|hadpk0,39,44,0|lt51nz,39,44,0|lt51o0,40,45,1|lzz9jz,40,45,1|lzz9k0,39,44,0\",\"America/Bahia_Banderas|,0,65,0|-p1u4k0,50,66,0|-m7mko1,50,66,0|-m7mko0,45,62,0|-kf67c1,45,62,0|-kf67c0,50,66,0|-k6j3c1,50,66,0|-k6j3c0,45,62,0|-jypm01,45,62,0|-jypm00,50,66,0|-jpan81,50,66,0|-jpan80,45,62,0|-eg9601,45,62,0|-eg9600,50,66,0|-axv381,50,66,0|-axv380,51,40,0|m7z,51,40,0|m80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gcwozz,50,66,0|gcwp00,52,62,1|gkgu7z,52,62,1|gkgu80,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jftabz,50,66,0|jftac0,52,62,1|jqm3jz,52,62,1|jqm3k0,50,66,0|jywbnz,50,66,0|jywbo0,52,62,1|k9c67z,52,62,1|k9c680,50,66,0|khmebz,50,66,0|khmec0,52,62,1|ks28vz,52,62,1|ks28w0,50,66,0|l0cgzz,50,66,0|l0ch00,46,63,1|lb57fz,46,63,1|lb57g0,45,62,0|lj2gvz,45,62,0|lj2gw0,46,63,1|ltva3z,46,63,1|ltva40,45,62,0|m1sjjz,45,62,0|m1sjk0,46,63,1|mclcrz,46,63,1|mclcs0,45,62,0|mkvkvz,45,62,0|mkvkw0,46,63,1|mvbffz,46,63,1|mvbfg0,45,62,0|n3lnjz,45,62,0|n3lnk0,46,63,1|ne1i3z,46,63,1|ne1i40,45,62,0|nmbq7z,45,62,0|nmbq80,46,63,1|nwrkrz,46,63,1|nwrks0,45,62,0|o51svz,45,62,0|o51sw0,46,63,1|ofum3z,46,63,1|ofum40,45,62,0|onrvjz,45,62,0|onrvk0,46,63,1|oykorz,46,63,1|oykos0,45,62,0|p6hy7z,45,62,0|p6hy80,46,63,1|pharfz,46,63,1|pharg0,45,62,0|ppkzjz,45,62,0|ppkzk0,46,63,1|q00u3z,46,63,1|q00u40,45,62,0|q8b27z,45,62,0|q8b280,46,63,1|qiqwrz,46,63,1|qiqws0,45,62,0|qr14vz,45,62,0|qr14w0,46,63,1|r1ty3z,46,63,1|r1ty40,45,62,0|r9r7jz,45,62,0|r9r7k0,46,63,1|rkk0rz,46,63,1|rkk0s0,45,62,0|rsha7z,45,62,0|rsha80,46,63,1|s3a3fz,46,63,1|s3a3g0,45,62,0|sbkbjz,45,62,0|sbkbk0,46,63,1|sm063z,46,63,1|sm0640,45,62,0|suae7z,45,62,0|suae80,46,63,1|t4q8rz,46,63,1|t4q8s0,45,62,0|td0gvz,45,62,0|td0gw0,46,63,1|tngbfz,46,63,1|tngbg0,45,62,0|tvqjjz,45,62,0|tvqjk0,46,63,1|u6jcrz,46,63,1|u6jcs0,45,62,0|uegm7z,45,62,0|uegm80,46,63,1|up9ffz,46,63,1|up9fg0,45,62,0|ux6ovz,45,62,0|ux6ow0,46,63,1|v7zi3z,46,63,1|v7zi40,45,62,0|vg9q7z,45,62,0|vg9q80,46,63,1|vqpkrz,46,63,1|vqpks0,45,62,0|vyzsvz,45,62,0|vyzsw0,46,63,1|w9fnfz,46,63,1|w9fng0,45,62,0|whpvjz,45,62,0|whpvk0,46,63,1|wsiorz,46,63,1|wsios0,45,62,0|x0fy7z,45,62,0|x0fy80,46,63,1|xb8rfz,46,63,1|xb8rg0,45,62,0|xj60vz,45,62,0|xj60w0,46,63,1|xtyu3z,46,63,1|xtyu40,45,62,0|y1w3jz,45,62,0|y1w3k0,46,63,1|ycowrz,46,63,1|ycows0,45,62,0|ykz4vz,45,62,0|ykz4w0,46,63,1|yvezfz,46,63,1|yvezg0,45,62,0|z3p7jz,45,62,0|z3p7k0,46,63,1|ze523z,46,63,1|ze5240,45,62,0\",\"America/Barbados|,0,67,0|-o0aiaj,53,67,0|-jtzeak,53,67,0|-jtzeaj,32,42,0|3vvnbz,32,42,0|3vvnc0,54,44,1|41mz7z,54,44,1|41mz80,32,42,0|4bq0nz,32,42,0|4bq0o0,54,44,1|4kd1vz,54,44,1|4kd1w0,32,42,0|4ug3bz,32,42,0|4ug3c0,54,44,1|5334jz,54,44,1|5334k0,32,42,0|5dj4nz,32,42,0|5dj4o0,54,44,1|5lnn7z,54,44,1|5lnn80,32,42,0\",\"America/Belem|,0,68,0|-t85j0s,39,44,0|-jylag1,39,44,0|-jylag0,40,45,1|-jpb141,40,45,1|-jpb140,39,44,0|-jfsd01,39,44,0|-jfsd00,40,45,1|-j6j3s1,40,45,1|-j6j3s0,39,44,0|-ahcyc1,39,44,0|-ahcyc0,40,45,1|-aad3o1,40,45,1|-aad3o0,39,44,0|-9yl101,39,44,0|-9yl100,40,45,1|-9sd141,40,45,1|-9sd140,39,44,0|-9ft3o1,39,44,0|-9ft3o0,40,45,1|-99j941,40,45,1|-99j940,39,44,0|-8wzbo1,39,44,0|-8wzbo0,40,45,1|-8scqg1,40,45,1|-8scqg0,39,44,0|-35xmc1,39,44,0|-35xmc0,40,45,1|-31nzs1,40,45,1|-31nzs0,39,44,0|-2kdp01,39,44,0|-2kdp00,40,45,1|-2hcig1,40,45,1|-2hcig0,39,44,0|-24qqc1,39,44,0|-24qqc0,40,45,1|-204541,40,45,1|-204540,39,44,0|-1nid01,39,44,0|-1nid00,40,45,1|-1hc7s1,40,45,1|-1hc7s0,39,44,0|-14qfo1,39,44,0|-14qfo0,40,45,1|-yifs1,40,45,1|-yifs0,39,44,0|89jcbz,39,44,0|89jcc0,40,45,1|8gdk7z,40,45,1|8gdk80,39,44,0|8rwgbz,39,44,0|8rwgc0,40,45,1|8xns7z,40,45,1|8xns80,39,44,0|9aodnz,39,44,0|9aodo0,40,45,1|9g2qvz,40,45,1|9g2qw0,39,44,0\",\"America/Belize|,0,69,0|-u52ic0,45,62,0|-qqoy01,45,62,0|-qqoy00,55,70,1|-qk7ne1,55,70,1|-qk7ne0,45,62,0|-q7yvc1,45,62,0|-q7yvc0,55,70,1|-q14m21,55,70,1|-q14m20,45,62,0|-pp8so1,45,62,0|-pp8so0,55,70,1|-pieje1,55,70,1|-pieje0,45,62,0|-p6iq01,45,62,0|-p6iq00,55,70,1|-ozogq1,55,70,1|-ozogq0,45,62,0|-onfoo1,45,62,0|-onfoo0,55,70,1|-ogye21,55,70,1|-ogye20,45,62,0|-o4pm01,45,62,0|-o4pm00,55,70,1|-ny8be1,55,70,1|-ny8be0,45,62,0|-nlzjc1,45,62,0|-nlzjc0,55,70,1|-nf5a21,55,70,1|-nf5a20,45,62,0|-n39go1,45,62,0|-n39go0,55,70,1|-mwf7e1,55,70,1|-mwf7e0,45,62,0|-mkje01,45,62,0|-mkje00,55,70,1|-mdp4q1,55,70,1|-mdp4q0,45,62,0|-m1tbc1,45,62,0|-m1tbc0,55,70,1|-luz221,55,70,1|-luz220,45,62,0|-liqa01,45,62,0|-liqa00,55,70,1|-lc8ze1,55,70,1|-lc8ze0,45,62,0|-l007c1,45,62,0|-l007c0,55,70,1|-ktiwq1,55,70,1|-ktiwq0,45,62,0|-kha4o1,45,62,0|-kha4o0,55,70,1|-kafve1,55,70,1|-kafve0,45,62,0|-jyk201,45,62,0|-jyk200,55,70,1|-jrpsq1,55,70,1|-jrpsq0,45,62,0|-jftzc1,45,62,0|-jftzc0,55,70,1|-j8zq21,55,70,1|-j8zq20,45,62,0|-iwqy01,45,62,0|-iwqy00,55,70,1|-iq9ne1,55,70,1|-iq9ne0,45,62,0|-ie0vc1,45,62,0|-ie0vc0,55,70,1|-i7jkq1,55,70,1|-i7jkq0,45,62,0|-hvaso1,45,62,0|-hvaso0,55,70,1|-hoti21,55,70,1|-hoti20,45,62,0|-hckq01,45,62,0|-hckq00,55,70,1|-h5qgq1,55,70,1|-h5qgq0,45,62,0|-gtunc1,45,62,0|-gtunc0,55,70,1|-gn0e21,55,70,1|-gn0e20,45,62,0|-gb4ko1,45,62,0|-gb4ko0,55,70,1|-g4abe1,55,70,1|-g4abe0,45,62,0|-fs1jc1,45,62,0|-fs1jc0,55,70,1|-flk8q1,55,70,1|-flk8q0,45,62,0|-f9bgo1,45,62,0|-f9bgo0,55,70,1|-f2u621,55,70,1|-f2u620,45,62,0|-eqle01,45,62,0|-eqle00,55,70,1|-ejr4q1,55,70,1|-ejr4q0,45,62,0|-ecwso1,45,62,0|-ecwso0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cjqks1,48,63,1|-cjqks0,45,62,0|-blvzc1,45,62,0|-blvzc0,55,70,1|-bf1q21,55,70,1|-bf1q20,45,62,0|-b35wo1,45,62,0|-b35wo0,55,70,1|-awbne1,55,70,1|-awbne0,45,62,0|-akfu01,45,62,0|-akfu00,55,70,1|-adlkq1,55,70,1|-adlkq0,45,62,0|-a1cso1,45,62,0|-a1cso0,55,70,1|-9uvi21,55,70,1|-9uvi20,45,62,0|-9imq01,45,62,0|-9imq00,55,70,1|-9c5fe1,55,70,1|-9c5fe0,45,62,0|-8zwnc1,45,62,0|-8zwnc0,55,70,1|-8t2e21,55,70,1|-8t2e20,45,62,0|-8h6ko1,45,62,0|-8h6ko0,55,70,1|-8acbe1,55,70,1|-8acbe0,45,62,0|-7ygi01,45,62,0|-7ygi00,55,70,1|-7rm8q1,55,70,1|-7rm8q0,45,62,0|-7fqfc1,45,62,0|-7fqfc0,55,70,1|-78w621,55,70,1|-78w620,45,62,0|-6wne01,45,62,0|-6wne00,55,70,1|-6q63e1,55,70,1|-6q63e0,45,62,0|-6dxbc1,45,62,0|-6dxbc0,55,70,1|-67g0q1,55,70,1|-67g0q0,45,62,0|-5v78o1,45,62,0|-5v78o0,55,70,1|-5ocze1,55,70,1|-5ocze0,45,62,0|-5ch601,45,62,0|-5ch600,55,70,1|-55mwq1,55,70,1|-55mwq0,45,62,0|-4tr3c1,45,62,0|-4tr3c0,55,70,1|-4mwu21,55,70,1|-4mwu20,45,62,0|-4ao201,45,62,0|-4ao200,55,70,1|-446re1,55,70,1|-446re0,45,62,0|-3rxzc1,45,62,0|-3rxzc0,55,70,1|-3lgoq1,55,70,1|-3lgoq0,45,62,0|-397wo1,45,62,0|-397wo0,55,70,1|-32qm21,55,70,1|-32qm20,45,62,0|-2qhu01,45,62,0|-2qhu00,55,70,1|-2jnkq1,55,70,1|-2jnkq0,45,62,0|-27rrc1,45,62,0|-27rrc0,55,70,1|-20xi21,55,70,1|-20xi20,45,62,0|-1p1oo1,45,62,0|-1p1oo0,55,70,1|-1i7fe1,55,70,1|-1i7fe0,45,62,0|-15ync1,45,62,0|-15ync0,55,70,1|-zhcq1,55,70,1|-zhcq0,45,62,0|21s0nz,45,62,0|21s0o0,46,63,1|2565vz,46,63,1|2565w0,45,62,0|6rj4nz,45,62,0|6rj4o0,46,63,1|6uer7z,46,63,1|6uer80,45,62,0\",\"America/Blanc-Sablon|,0,71,0|-18vs838,32,42,0|-qzp0o1,32,42,0|-qzp0o0,54,44,1|-qpm4s1,54,44,1|-qpm4s0,32,42,0|-ek27c1,32,42,0|-ek27c0,33,44,1|-cq2tg1,33,44,1|-cq2tg0,34,44,1|-cnp641,34,44,1|-cnp640,32,42,0\",\"America/Boa_Vista|,0,72,0|-t85grk,42,42,0|-jyl7o1,42,42,0|-jyl7o0,39,44,1|-jpayc1,39,44,1|-jpayc0,42,42,0|-jfsa81,42,42,0|-jfsa80,39,44,1|-j6j101,39,44,1|-j6j100,42,42,0|-ahcvk1,42,42,0|-ahcvk0,39,44,1|-aad0w1,39,44,1|-aad0w0,42,42,0|-9yky81,42,42,0|-9yky80,39,44,1|-9scyc1,39,44,1|-9scyc0,42,42,0|-9ft0w1,42,42,0|-9ft0w0,39,44,1|-99j6c1,39,44,1|-99j6c0,42,42,0|-8wz8w1,42,42,0|-8wz8w0,39,44,1|-8scno1,39,44,1|-8scno0,42,42,0|-35xjk1,42,42,0|-35xjk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2kdm81,42,42,0|-2kdm80,39,44,1|-2hcfo1,39,44,1|-2hcfo0,42,42,0|-24qnk1,42,42,0|-24qnk0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1nia81,42,42,0|-1nia80,39,44,1|-1hc501,39,44,1|-1hc500,42,42,0|-14qcw1,42,42,0|-14qcw0,39,44,1|-yid01,39,44,1|-yid00,42,42,0|89jf3z,42,42,0|89jf40,39,44,1|8gdmzz,39,44,1|8gdn00,42,42,0|8rwj3z,42,42,0|8rwj40,39,44,1|8xnuzz,39,44,1|8xnv00,42,42,0|9aogfz,42,42,0|9aogg0,39,44,1|9g2tnz,39,44,1|9g2to0,42,42,0|fj0drz,42,42,0|fj0ds0,39,44,1|fqkizz,39,44,1|fqkj00,42,42,0|g23f3z,42,42,0|g23f40,39,44,1|g2gazz,39,44,1|g2gb00,42,42,0\",\"America/Bogota|,0,73,0|-18s2sy8,53,73,0|-srdoy9,53,73,0|-srdoy8,56,63,0|bnnsjz,56,63,0|bnnsk0,42,42,1|c4xxrz,42,42,1|c4xxs0,56,63,0\",\"America/Boise|,0,74,0|-18y0gg0,51,40,0|-r0emw1,51,40,0|-r0emw0,57,66,1|-qplto1,57,66,1|-qplto0,51,40,0|-qhok81,51,40,0|-qhok80,57,66,1|-q6vr01,57,66,1|-q6vr00,51,40,0|-oc9iw1,51,40,0|-oc9iw0,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-1e8kc1,50,66,0|-1e8kc0,52,62,1|-14vls1,52,62,1|-14vls0,50,66,0|-viho1,50,66,0|-viho0,52,62,1|-m5j41,52,62,1|-m5j40,50,66,0|-csf01,50,66,0|-csf00,52,62,1|-3fgg1,52,62,1|-3fgg0,50,66,0|5xnnz,50,66,0|5xno0,52,62,1|fam7z,52,62,1|fam80,50,66,0|onqbz,50,66,0|onqc0,52,62,1|ydnjz,52,62,1|ydnk0,50,66,0|17qrnz,50,66,0|17qro0,52,62,1|1h3q7z,52,62,1|1h3q80,50,66,0|1qgubz,50,66,0|1qguc0,52,62,1|1ztsvz,52,62,1|1ztsw0,50,66,0|24vczz,50,66,0|24vd00,52,62,1|2ijvjz,52,62,1|2ijvk0,50,66,0|2oobnz,50,66,0|2oobo0,52,62,1|319y7z,52,62,1|319y80,50,66,0|3an2bz,50,66,0|3an2c0,52,62,1|3kczjz,52,62,1|3kczk0,50,66,0|3td4zz,50,66,0|3td500,52,62,1|43327z,52,62,1|433280,50,66,0|4cg6bz,50,66,0|4cg6c0,52,62,1|4lt4vz,52,62,1|4lt4w0,50,66,0|4v68zz,50,66,0|4v6900,52,62,1|54j7jz,52,62,1|54j7k0,50,66,0|5dwbnz,50,66,0|5dwbo0,52,62,1|5n9a7z,52,62,1|5n9a80,50,66,0|5wmebz,50,66,0|5wmec0,52,62,1|65zcvz,52,62,1|65zcw0,50,66,0|6fcgzz,50,66,0|6fch00,52,62,1|6p2e7z,52,62,1|6p2e80,50,66,0|6y2jnz,50,66,0|6y2jo0,52,62,1|77sgvz,52,62,1|77sgw0,50,66,0|7h5kzz,50,66,0|7h5l00,52,62,1|7qijjz,52,62,1|7qijk0,50,66,0|7zvnnz,50,66,0|7zvno0,52,62,1|898m7z,52,62,1|898m80,50,66,0|8ilqbz,50,66,0|8ilqc0,52,62,1|8ryovz,52,62,1|8ryow0,50,66,0|908wzz,50,66,0|908x00,52,62,1|9aorjz,52,62,1|9aork0,50,66,0|9iyznz,50,66,0|9iyzo0,52,62,1|9trsvz,52,62,1|9trsw0,50,66,0|a1p2bz,50,66,0|a1p2c0,52,62,1|achvjz,52,62,1|achvk0,50,66,0|akf4zz,50,66,0|akf500,52,62,1|av7y7z,52,62,1|av7y80,50,66,0|b3i6bz,50,66,0|b3i6c0,52,62,1|bdy0vz,52,62,1|bdy0w0,50,66,0|bm88zz,50,66,0|bm8900,52,62,1|bwo3jz,52,62,1|bwo3k0,50,66,0|c4ybnz,50,66,0|c4ybo0,52,62,1|cfr4vz,52,62,1|cfr4w0,50,66,0|cnoebz,50,66,0|cnoec0,52,62,1|cyh7jz,52,62,1|cyh7k0,50,66,0|d6egzz,50,66,0|d6eh00,52,62,1|dh7a7z,52,62,1|dh7a80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gb3vnz,50,66,0|gb3vo0,52,62,1|glwovz,52,62,1|glwow0,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jeqebz,50,66,0|jeqec0,52,62,1|jqz27z,52,62,1|jqz280,50,66,0|jxggzz,50,66,0|jxgh00,52,62,1|k9p4vz,52,62,1|k9p4w0,50,66,0|kg6jnz,50,66,0|kg6jo0,52,62,1|ksf7jz,52,62,1|ksf7k0,50,66,0|kz9kzz,50,66,0|kz9l00,52,62,1|lbi8vz,52,62,1|lbi8w0,50,66,0|lhznnz,50,66,0|lhzno0,52,62,1|lu8bjz,52,62,1|lu8bk0,50,66,0|m0pqbz,50,66,0|m0pqc0,52,62,1|mcye7z,52,62,1|mcye80,50,66,0|mjfszz,50,66,0|mjft00,52,62,1|mvogvz,52,62,1|mvogw0,50,66,0|n25vnz,50,66,0|n25vo0,52,62,1|neejjz,52,62,1|neejk0,50,66,0|nkvybz,50,66,0|nkvyc0,52,62,1|nx4m7z,52,62,1|nx4m80,50,66,0|o3yznz,50,66,0|o3yzo0,52,62,1|og7njz,52,62,1|og7nk0,50,66,0|omp2bz,50,66,0|omp2c0,52,62,1|oyxq7z,52,62,1|oyxq80,50,66,0|p5f4zz,50,66,0|p5f500,52,62,1|phnsvz,52,62,1|phnsw0,50,66,0|po57nz,50,66,0|po57o0,52,62,1|q0dvjz,52,62,1|q0dvk0,50,66,0|q6vabz,50,66,0|q6vac0,52,62,1|qj3y7z,52,62,1|qj3y80,50,66,0|qpybnz,50,66,0|qpybo0,52,62,1|r26zjz,52,62,1|r26zk0,50,66,0|r8oebz,50,66,0|r8oec0,52,62,1|rkx27z,52,62,1|rkx280,50,66,0|rregzz,50,66,0|rreh00,52,62,1|s3n4vz,52,62,1|s3n4w0,50,66,0|sa4jnz,50,66,0|sa4jo0,52,62,1|smd7jz,52,62,1|smd7k0,50,66,0|ssumbz,50,66,0|ssumc0,52,62,1|t53a7z,52,62,1|t53a80,50,66,0|tbkozz,50,66,0|tbkp00,52,62,1|tntcvz,52,62,1|tntcw0,50,66,0|tunqbz,50,66,0|tunqc0,52,62,1|u6we7z,52,62,1|u6we80,50,66,0|uddszz,50,66,0|uddt00,52,62,1|upmgvz,52,62,1|upmgw0,50,66,0|uw3vnz,50,66,0|uw3vo0,52,62,1|v8cjjz,52,62,1|v8cjk0,50,66,0|vetybz,50,66,0|vetyc0,52,62,1|vr2m7z,52,62,1|vr2m80,50,66,0|vxk0zz,50,66,0|vxk100,52,62,1|w9sovz,52,62,1|w9sow0,50,66,0|wgn2bz,50,66,0|wgn2c0,52,62,1|wsvq7z,52,62,1|wsvq80,50,66,0|wzd4zz,50,66,0|wzd500,52,62,1|xblsvz,52,62,1|xblsw0,50,66,0|xi37nz,50,66,0|xi37o0,52,62,1|xubvjz,52,62,1|xubvk0,50,66,0|y0tabz,50,66,0|y0tac0,52,62,1|yd1y7z,52,62,1|yd1y80,50,66,0|yjjczz,50,66,0|yjjd00,52,62,1|yvs0vz,52,62,1|yvs0w0,50,66,0|z29fnz,50,66,0|z29fo0,52,62,1|zei3jz,52,62,1|zei3k0,50,66,0\",\"America/Cambridge_Bay|,60,1,0|-q3gdc0,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-2g1tw1,50,66,0|-2g1tw0,61,63,1|-26btw1,61,63,1|-26btw0,50,66,0|5dwbnz,50,66,0|5dwbo0,52,62,1|5n9a7z,52,62,1|5n9a80,50,66,0|5wmebz,50,66,0|5wmec0,52,62,1|65zcvz,52,62,1|65zcw0,50,66,0|6fcgzz,50,66,0|6fch00,52,62,1|6p2e7z,52,62,1|6p2e80,50,66,0|6y2jnz,50,66,0|6y2jo0,52,62,1|77sgvz,52,62,1|77sgw0,50,66,0|7h5kzz,50,66,0|7h5l00,52,62,1|7qijjz,52,62,1|7qijk0,50,66,0|7zvnnz,50,66,0|7zvno0,52,62,1|898m7z,52,62,1|898m80,50,66,0|8ilqbz,50,66,0|8ilqc0,52,62,1|8ryovz,52,62,1|8ryow0,50,66,0|908wzz,50,66,0|908x00,52,62,1|9aorjz,52,62,1|9aork0,50,66,0|9iyznz,50,66,0|9iyzo0,52,62,1|9trsvz,52,62,1|9trsw0,50,66,0|a1p2bz,50,66,0|a1p2c0,52,62,1|achvjz,52,62,1|achvk0,50,66,0|akf4zz,50,66,0|akf500,52,62,1|av7y7z,52,62,1|av7y80,50,66,0|b3i6bz,50,66,0|b3i6c0,52,62,1|bdy0vz,52,62,1|bdy0w0,50,66,0|bm88zz,50,66,0|bm8900,52,62,1|bwo3jz,52,62,1|bwo3k0,50,66,0|c4ybnz,50,66,0|c4ybo0,52,62,1|cfr4vz,52,62,1|cfr4w0,50,66,0|cnoebz,50,66,0|cnoec0,52,62,1|cyh7jz,52,62,1|cyh7k0,50,66,0|d6egzz,50,66,0|d6eh00,52,62,1|dh7a7z,52,62,1|dh7a80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,49,63,0|g3jcjz,49,63,0|g3jck0,45,62,0|gb3vnz,45,62,0|gb3vo0,52,62,1|glwovz,52,62,1|glwow0,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jeqebz,50,66,0|jeqec0,52,62,1|jqz27z,52,62,1|jqz280,50,66,0|jxggzz,50,66,0|jxgh00,52,62,1|k9p4vz,52,62,1|k9p4w0,50,66,0|kg6jnz,50,66,0|kg6jo0,52,62,1|ksf7jz,52,62,1|ksf7k0,50,66,0|kz9kzz,50,66,0|kz9l00,52,62,1|lbi8vz,52,62,1|lbi8w0,50,66,0|lhznnz,50,66,0|lhzno0,52,62,1|lu8bjz,52,62,1|lu8bk0,50,66,0|m0pqbz,50,66,0|m0pqc0,52,62,1|mcye7z,52,62,1|mcye80,50,66,0|mjfszz,50,66,0|mjft00,52,62,1|mvogvz,52,62,1|mvogw0,50,66,0|n25vnz,50,66,0|n25vo0,52,62,1|neejjz,52,62,1|neejk0,50,66,0|nkvybz,50,66,0|nkvyc0,52,62,1|nx4m7z,52,62,1|nx4m80,50,66,0|o3yznz,50,66,0|o3yzo0,52,62,1|og7njz,52,62,1|og7nk0,50,66,0|omp2bz,50,66,0|omp2c0,52,62,1|oyxq7z,52,62,1|oyxq80,50,66,0|p5f4zz,50,66,0|p5f500,52,62,1|phnsvz,52,62,1|phnsw0,50,66,0|po57nz,50,66,0|po57o0,52,62,1|q0dvjz,52,62,1|q0dvk0,50,66,0|q6vabz,50,66,0|q6vac0,52,62,1|qj3y7z,52,62,1|qj3y80,50,66,0|qpybnz,50,66,0|qpybo0,52,62,1|r26zjz,52,62,1|r26zk0,50,66,0|r8oebz,50,66,0|r8oec0,52,62,1|rkx27z,52,62,1|rkx280,50,66,0|rregzz,50,66,0|rreh00,52,62,1|s3n4vz,52,62,1|s3n4w0,50,66,0|sa4jnz,50,66,0|sa4jo0,52,62,1|smd7jz,52,62,1|smd7k0,50,66,0|ssumbz,50,66,0|ssumc0,52,62,1|t53a7z,52,62,1|t53a80,50,66,0|tbkozz,50,66,0|tbkp00,52,62,1|tntcvz,52,62,1|tntcw0,50,66,0|tunqbz,50,66,0|tunqc0,52,62,1|u6we7z,52,62,1|u6we80,50,66,0|uddszz,50,66,0|uddt00,52,62,1|upmgvz,52,62,1|upmgw0,50,66,0|uw3vnz,50,66,0|uw3vo0,52,62,1|v8cjjz,52,62,1|v8cjk0,50,66,0|vetybz,50,66,0|vetyc0,52,62,1|vr2m7z,52,62,1|vr2m80,50,66,0|vxk0zz,50,66,0|vxk100,52,62,1|w9sovz,52,62,1|w9sow0,50,66,0|wgn2bz,50,66,0|wgn2c0,52,62,1|wsvq7z,52,62,1|wsvq80,50,66,0|wzd4zz,50,66,0|wzd500,52,62,1|xblsvz,52,62,1|xblsw0,50,66,0|xi37nz,50,66,0|xi37o0,52,62,1|xubvjz,52,62,1|xubvk0,50,66,0|y0tabz,50,66,0|y0tac0,52,62,1|yd1y7z,52,62,1|yd1y80,50,66,0|yjjczz,50,66,0|yjjd00,52,62,1|yvs0vz,52,62,1|yvs0w0,50,66,0|z29fnz,50,66,0|z29fo0,52,62,1|zei3jz,52,62,1|zei3k0,50,66,0\",\"America/Campo_Grande|,0,75,0|-t85hvw,42,42,0|-jyl7o1,42,42,0|-jyl7o0,39,44,1|-jpayc1,39,44,1|-jpayc0,42,42,0|-jfsa81,42,42,0|-jfsa80,39,44,1|-j6j101,39,44,1|-j6j100,42,42,0|-ahcvk1,42,42,0|-ahcvk0,39,44,1|-aad0w1,39,44,1|-aad0w0,42,42,0|-9yky81,42,42,0|-9yky80,39,44,1|-9scyc1,39,44,1|-9scyc0,42,42,0|-9ft0w1,42,42,0|-9ft0w0,39,44,1|-99j6c1,39,44,1|-99j6c0,42,42,0|-8wz8w1,42,42,0|-8wz8w0,39,44,1|-8scno1,39,44,1|-8scno0,42,42,0|-35xjk1,42,42,0|-35xjk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2kdm81,42,42,0|-2kdm80,39,44,1|-2hcfo1,39,44,1|-2hcfo0,42,42,0|-24qnk1,42,42,0|-24qnk0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1nia81,42,42,0|-1nia80,39,44,1|-1hc501,39,44,1|-1hc500,42,42,0|-14qcw1,42,42,0|-14qcw0,39,44,1|-yid01,39,44,1|-yid00,42,42,0|89jf3z,42,42,0|89jf40,39,44,1|8gdmzz,39,44,1|8gdn00,42,42,0|8rwj3z,42,42,0|8rwj40,39,44,1|8xnuzz,39,44,1|8xnv00,42,42,0|9aogfz,42,42,0|9aogg0,39,44,1|9g2tnz,39,44,1|9g2to0,42,42,0|9t1kfz,42,42,0|9t1kg0,39,44,1|9yfxnz,39,44,1|9yfxo0,42,42,0|abrn3z,42,42,0|abrn40,39,44,1|ahvxnz,39,44,1|ahvxo0,42,42,0|auuofz,42,42,0|auuog0,39,44,1|b0yyzz,39,44,1|b0yz00,42,42,0|bdkr3z,42,42,0|bdkr40,39,44,1|bjc2zz,39,44,1|bjc300,42,42,0|bwnsfz,42,42,0|bwnsg0,39,44,1|c1p6zz,39,44,1|c1p700,42,42,0|cf0wfz,42,42,0|cf0wg0,39,44,1|cli5nz,39,44,1|cli5o0,42,42,0|cxqz3z,42,42,0|cxqz40,39,44,1|d488bz,39,44,1|d488c0,42,42,0|dgh1rz,42,42,0|dgh1s0,39,44,1|dmlcbz,39,44,1|dmlcc0,42,42,0|dyu5rz,42,42,0|dyu5s0,39,44,1|e5odnz,39,44,1|e5odo0,42,42,0|ehm33z,42,42,0|ehm340,39,44,1|ep4dnz,39,44,1|ep4do0,42,42,0|f0n9rz,42,42,0|f0n9s0,39,44,1|f7hhnz,39,44,1|f7hho0,42,42,0|fj0drz,42,42,0|fj0ds0,39,44,1|fqkizz,39,44,1|fqkj00,42,42,0|g23f3z,42,42,0|g23f40,39,44,1|g8xmzz,39,44,1|g8xn00,42,42,0|gl6gfz,42,42,0|gl6gg0,39,44,1|grnpnz,39,44,1|grnpo0,42,42,0|h4zf3z,42,42,0|h4zf40,39,44,1|hadsbz,39,44,1|hadsc0,42,42,0|hmzkfz,42,42,0|hmzkg0,39,44,1|ht3uzz,39,44,1|ht3v00,42,42,0|i6j9rz,42,42,0|i6j9s0,39,44,1|ic6wbz,39,44,1|ic6wc0,42,42,0|iofprz,42,42,0|iofps0,39,44,1|iuwyzz,39,44,1|iuwz00,42,42,0|j88ofz,42,42,0|j88og0,39,44,1|je00bz,39,44,1|je00c0,42,42,0|jpvv3z,42,42,0|jpvv40,39,44,1|jwd4bz,39,44,1|jwd4c0,42,42,0|k8ywfz,42,42,0|k8ywg0,39,44,1|kf36zz,39,44,1|kf3700,42,42,0|kroz3z,42,42,0|kroz40,39,44,1|ky68bz,39,44,1|ky68c0,42,42,0|laf1rz,42,42,0|laf1s0,39,44,1|lgwazz,39,44,1|lgwb00,42,42,0|lt54fz,42,42,0|lt54g0,39,44,1|lzzcbz,39,44,1|lzzcc0,42,42,0|mc85rz,42,42,0|mc85s0,39,44,1|micgbz,39,44,1|micgc0,42,42,0|muy8fz,42,42,0|muy8g0,39,44,1|n12izz,39,44,1|n12j00,42,42,0|ndob3z,42,42,0|ndob40,39,44,1|nk5kbz,39,44,1|nk5kc0,42,42,0|nwedrz,42,42,0|nweds0,39,44,1|o2vmzz,39,44,1|o2vn00,42,42,0|of4gfz,42,42,0|of4gg0,39,44,1|ollpnz,39,44,1|ollpo0,42,42,0|oxuj3z,42,42,0|oxuj40,39,44,1|p4bsbz,39,44,1|p4bsc0,42,42,0|phnhrz,42,42,0|phnhs0,39,44,1|pn1uzz,39,44,1|pn1v00,42,42,0\",\"America/Cancun|,0,76,0|-p1u7c0,45,62,0|690gnz,45,62,0|690go0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|ex1snz,62,42,1|ex1so0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,45,62,0|gcwm7z,45,62,0|gcwm80,46,63,1|gkgrfz,46,63,1|gkgrg0,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jft7jz,45,62,0|jft7k0,46,63,1|jqm0rz,46,63,1|jqm0s0,45,62,0|jyw8vz,45,62,0|jyw8w0,46,63,1|k9c3fz,46,63,1|k9c3g0,45,62,0|khmbjz,45,62,0|khmbk0,46,63,1|ks263z,46,63,1|ks2640,45,62,0|l0ce7z,45,62,0|l0ce80,46,63,1|lb57fz,46,63,1|lb57g0,45,62,0|lj2gvz,45,62,0|lj2gw0,46,63,1|ltva3z,46,63,1|ltva40,45,62,0|m1sjjz,45,62,0|m1sjk0,46,63,1|mclcrz,46,63,1|mclcs0,45,62,0|mkvkvz,45,62,0|mkvkw0,46,63,1|mvbffz,46,63,1|mvbfg0,45,62,0|n3lnjz,45,62,0|n3lnk0,46,63,1|ne1i3z,46,63,1|ne1i40,45,62,0|nj327z,45,62,0|nj3280,49,63,0\",\"America/Caracas|,0,77,0|-15r0wxs,41,78,0|-u7lcxx,41,78,0|-u7lcxw,43,59,0|-2lx4u1,43,59,0|-2lx4u0,42,42,0|jsrsrz,42,42,0|jsrss0,43,59,0|o6hkrz,43,59,0|o6hks0,42,42,0\",\"America/Cayenne|,0,79,0|-uj7yb4,42,42,0|-16brk1,42,42,0|-16brk0,39,44,0\",\"America/Cayman|,0,80,0|-15r0uls,41,81,0|-w757vd,41,81,0|-w757vc,49,63,0\",\"America/Chicago|,0,82,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-pv01s1,45,62,0|-pv01s0,46,63,1|-pnsv81,46,63,1|-pnsv80,45,62,0|-pg8kg1,45,62,0|-pg8kg0,46,63,1|-p52sk1,46,63,1|-p52sk0,45,62,0|-ovpog1,45,62,0|-ovpog0,46,63,1|-oo5j81,46,63,1|-oo5j80,45,62,0|-oczls1,45,62,0|-oczls0,46,63,1|-o52hw1,46,63,1|-o52hw0,45,62,0|-nu9j41,45,62,0|-nu9j40,46,63,1|-nmcf81,46,63,1|-nmcf80,45,62,0|-nbjgg1,45,62,0|-nbjgg0,46,63,1|-n3mck1,46,63,1|-n3mck0,45,62,0|-mstds1,45,62,0|-mstds0,46,63,1|-mkw9w1,46,63,1|-mkw9w0,45,62,0|-ma3b41,45,62,0|-ma3b40,46,63,1|-m26781,46,63,1|-m26780,45,62,0|-lr09s1,45,62,0|-lr09s0,46,63,1|-lj35w1,46,63,1|-lj35w0,45,62,0|-l8a741,45,62,0|-l8a740,46,63,1|-l0d381,46,63,1|-l0d380,45,62,0|-kpk4g1,45,62,0|-kpk4g0,46,63,1|-khn0k1,46,63,1|-khn0k0,45,62,0|-k6u1s1,45,62,0|-k6u1s0,46,63,1|-jywxw1,46,63,1|-jywxw0,45,62,0|-jo3z41,45,62,0|-jo3z40,46,63,1|-jg6v81,46,63,1|-jg6v80,45,62,0|-j50xs1,45,62,0|-j50xs0,46,63,1|-ixgsk1,46,63,1|-ixgsk0,45,62,0|-imav41,45,62,0|-imav40,46,63,1|-iedr81,46,63,1|-iedr80,45,62,0|-i3ksg1,45,62,0|-i3ksg0,46,63,1|-hvnok1,46,63,1|-hvnok0,45,62,0|-hnqf41,45,62,0|-hnqf40,49,63,0|-haev81,49,63,0|-haev80,45,62,0|-h24n41,45,62,0|-h24n40,46,63,1|-gu7j81,46,63,1|-gu7j80,45,62,0|-gjekg1,45,62,0|-gjekg0,46,63,1|-gbhgk1,46,63,1|-gbhgk0,45,62,0|-g0bj41,45,62,0|-g0bj40,46,63,1|-fsrdw1,46,63,1|-fsrdw0,45,62,0|-fhlgg1,45,62,0|-fhlgg0,46,63,1|-f9ock1,46,63,1|-f9ock0,45,62,0|-eyvds1,45,62,0|-eyvds0,46,63,1|-eqy9w1,46,63,1|-eqy9w0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-ccw1s1,45,62,0|-ccw1s0,46,63,1|-c4yxw1,46,63,1|-c4yxw0,45,62,0|-bu5z41,45,62,0|-bu5z40,46,63,1|-bm8v81,46,63,1|-bm8v80,45,62,0|-bbfwg1,45,62,0|-bbfwg0,46,63,1|-b3isk1,46,63,1|-b3isk0,45,62,0|-aspts1,45,62,0|-aspts0,46,63,1|-akspw1,46,63,1|-akspw0,45,62,0|-a9msg1,45,62,0|-a9msg0,46,63,1|-a22n81,46,63,1|-a22n80,45,62,0|-9qwps1,45,62,0|-9qwps0,46,63,1|-9izlw1,46,63,1|-9izlw0,45,62,0|-986n41,45,62,0|-986n40,46,63,1|-909j81,46,63,1|-909j80,45,62,0|-8pgkg1,45,62,0|-8pgkg0,46,63,1|-8hjgk1,46,63,1|-8hjgk0,45,62,0|-86qhs1,45,62,0|-86qhs0,46,63,1|-7ytdw1,46,63,1|-7ytdw0,45,62,0|-7o0f41,45,62,0|-7o0f40,46,63,1|-7eahw1,46,63,1|-7eahw0,45,62,0|-74xds1,45,62,0|-74xds0,46,63,1|-6vkf81,46,63,1|-6vkf80,45,62,0|-6m7b41,45,62,0|-6m7b40,46,63,1|-6cuck1,46,63,1|-6cuck0,45,62,0|-63h8g1,45,62,0|-63h8g0,46,63,1|-5u49w1,46,63,1|-5u49w0,45,62,0|-5kr5s1,45,62,0|-5kr5s0,46,63,1|-5be781,46,63,1|-5be780,45,62,0|-521341,45,62,0|-521340,46,63,1|-4sb5w1,46,63,1|-4sb5w0,45,62,0|-4iy1s1,45,62,0|-4iy1s0,46,63,1|-49l381,46,63,1|-49l380,45,62,0|-407z41,45,62,0|-407z40,46,63,1|-3qv0k1,46,63,1|-3qv0k0,45,62,0|-3hhwg1,45,62,0|-3hhwg0,46,63,1|-384xw1,46,63,1|-384xw0,45,62,0|-2yrts1,45,62,0|-2yrts0,46,63,1|-2pev81,46,63,1|-2pev80,45,62,0|-2g1r41,45,62,0|-2g1r40,46,63,1|-26btw1,46,63,1|-26btw0,45,62,0|-1xbog1,45,62,0|-1xbog0,46,63,1|-1nlr81,46,63,1|-1nlr80,45,62,0|-1e8n41,45,62,0|-1e8n40,46,63,1|-14vok1,46,63,1|-14vok0,45,62,0|-vikg1,45,62,0|-vikg0,46,63,1|-m5lw1,46,63,1|-m5lw0,45,62,0|-cshs1,45,62,0|-cshs0,46,63,1|-3fj81,46,63,1|-3fj80,45,62,0|5xkvz,45,62,0|5xkw0,46,63,1|fajfz,46,63,1|fajg0,45,62,0|onnjz,45,62,0|onnk0,46,63,1|ydkrz,46,63,1|ydks0,45,62,0|17qovz,45,62,0|17qow0,46,63,1|1h3nfz,46,63,1|1h3ng0,45,62,0|1qgrjz,45,62,0|1qgrk0,46,63,1|1ztq3z,46,63,1|1ztq40,45,62,0|23ffjz,45,62,0|23ffk0,46,63,1|2ijsrz,46,63,1|2ijss0,45,62,0|2oo8vz,45,62,0|2oo8w0,46,63,1|319vfz,46,63,1|319vg0,45,62,0|3amzjz,45,62,0|3amzk0,46,63,1|3kcwrz,46,63,1|3kcws0,45,62,0|3td27z,45,62,0|3td280,46,63,1|432zfz,46,63,1|432zg0,45,62,0|4cg3jz,45,62,0|4cg3k0,46,63,1|4lt23z,46,63,1|4lt240,45,62,0|4v667z,45,62,0|4v6680,46,63,1|54j4rz,46,63,1|54j4s0,45,62,0|5dw8vz,45,62,0|5dw8w0,46,63,1|5n97fz,46,63,1|5n97g0,45,62,0|5wmbjz,45,62,0|5wmbk0,46,63,1|65za3z,46,63,1|65za40,45,62,0|6fce7z,45,62,0|6fce80,46,63,1|6p2bfz,46,63,1|6p2bg0,45,62,0|6y2gvz,45,62,0|6y2gw0,46,63,1|77se3z,46,63,1|77se40,45,62,0|7h5i7z,45,62,0|7h5i80,46,63,1|7qigrz,46,63,1|7qigs0,45,62,0|7zvkvz,45,62,0|7zvkw0,46,63,1|898jfz,46,63,1|898jg0,45,62,0|8ilnjz,45,62,0|8ilnk0,46,63,1|8rym3z,46,63,1|8rym40,45,62,0|908u7z,45,62,0|908u80,46,63,1|9aoorz,46,63,1|9aoos0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trq3z,46,63,1|9trq40,45,62,0|a1ozjz,45,62,0|a1ozk0,46,63,1|achsrz,46,63,1|achss0,45,62,0|akf27z,45,62,0|akf280,46,63,1|av7vfz,46,63,1|av7vg0,45,62,0|b3i3jz,45,62,0|b3i3k0,46,63,1|bdxy3z,46,63,1|bdxy40,45,62,0|bm867z,45,62,0|bm8680,46,63,1|bwo0rz,46,63,1|bwo0s0,45,62,0|c4y8vz,45,62,0|c4y8w0,46,63,1|cfr23z,46,63,1|cfr240,45,62,0|cnobjz,45,62,0|cnobk0,46,63,1|cyh4rz,46,63,1|cyh4s0,45,62,0|d6ee7z,45,62,0|d6ee80,46,63,1|dh77fz,46,63,1|dh77g0,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,45,62,0|gb3svz,45,62,0|gb3sw0,46,63,1|glwm3z,46,63,1|glwm40,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Chihuahua|,0,83,0|-p1u4k0,50,66,0|-m7mko1,50,66,0|-m7mko0,45,62,0|-kf67c1,45,62,0|-kf67c0,50,66,0|-k6j3c1,50,66,0|-k6j3c0,45,62,0|-jypm01,45,62,0|-jypm00,50,66,0|-jpan81,50,66,0|-jpan80,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxnnz,45,62,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gcwozz,50,66,0|gcwp00,52,62,1|gkgu7z,52,62,1|gkgu80,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jftabz,50,66,0|jftac0,52,62,1|jqm3jz,52,62,1|jqm3k0,50,66,0|jywbnz,50,66,0|jywbo0,52,62,1|k9c67z,52,62,1|k9c680,50,66,0|khmebz,50,66,0|khmec0,52,62,1|ks28vz,52,62,1|ks28w0,50,66,0|l0cgzz,50,66,0|l0ch00,52,62,1|lb5a7z,52,62,1|lb5a80,50,66,0|lj2jnz,50,66,0|lj2jo0,52,62,1|ltvcvz,52,62,1|ltvcw0,50,66,0|m1smbz,50,66,0|m1smc0,52,62,1|mclfjz,52,62,1|mclfk0,50,66,0|mkvnnz,50,66,0|mkvno0,52,62,1|mvbi7z,52,62,1|mvbi80,50,66,0|n3lqbz,50,66,0|n3lqc0,52,62,1|ne1kvz,52,62,1|ne1kw0,50,66,0|nmbszz,50,66,0|nmbt00,52,62,1|nwrnjz,52,62,1|nwrnk0,50,66,0|o51vnz,50,66,0|o51vo0,52,62,1|ofuovz,52,62,1|ofuow0,50,66,0|onrybz,50,66,0|onryc0,52,62,1|oykrjz,52,62,1|oykrk0,50,66,0|p6i0zz,50,66,0|p6i100,52,62,1|phau7z,52,62,1|phau80,50,66,0|ppl2bz,50,66,0|ppl2c0,52,62,1|q00wvz,52,62,1|q00ww0,50,66,0|q8b4zz,50,66,0|q8b500,52,62,1|qiqzjz,52,62,1|qiqzk0,50,66,0|qr17nz,50,66,0|qr17o0,52,62,1|r1u0vz,52,62,1|r1u0w0,50,66,0|r9rabz,50,66,0|r9rac0,52,62,1|rkk3jz,52,62,1|rkk3k0,50,66,0|rshczz,50,66,0|rshd00,52,62,1|s3a67z,52,62,1|s3a680,50,66,0|sbkebz,50,66,0|sbkec0,52,62,1|sm08vz,52,62,1|sm08w0,50,66,0|suagzz,50,66,0|suah00,52,62,1|t4qbjz,52,62,1|t4qbk0,50,66,0|td0jnz,50,66,0|td0jo0,52,62,1|tnge7z,52,62,1|tnge80,50,66,0|tvqmbz,50,66,0|tvqmc0,52,62,1|u6jfjz,52,62,1|u6jfk0,50,66,0|uegozz,50,66,0|uegp00,52,62,1|up9i7z,52,62,1|up9i80,50,66,0|ux6rnz,50,66,0|ux6ro0,52,62,1|v7zkvz,52,62,1|v7zkw0,50,66,0|vg9szz,50,66,0|vg9t00,52,62,1|vqpnjz,52,62,1|vqpnk0,50,66,0|vyzvnz,50,66,0|vyzvo0,52,62,1|w9fq7z,52,62,1|w9fq80,50,66,0|whpybz,50,66,0|whpyc0,52,62,1|wsirjz,52,62,1|wsirk0,50,66,0|x0g0zz,50,66,0|x0g100,52,62,1|xb8u7z,52,62,1|xb8u80,50,66,0|xj63nz,50,66,0|xj63o0,52,62,1|xtywvz,52,62,1|xtyww0,50,66,0|y1w6bz,50,66,0|y1w6c0,52,62,1|ycozjz,52,62,1|ycozk0,50,66,0|ykz7nz,50,66,0|ykz7o0,52,62,1|yvf27z,52,62,1|yvf280,50,66,0|z3pabz,50,66,0|z3pac0,52,62,1|ze54vz,52,62,1|ze54w0,50,66,0\",\"America/Costa_Rica|,0,84,0|-15r0trn,63,84,0|-pjw8fo,63,84,0|-pjw8fn,45,62,0|4rxcnz,45,62,0|4rxco0,46,63,1|4wyr7z,46,63,1|4wyr80,45,62,0|5anfbz,45,62,0|5anfc0,46,63,1|5fotvz,46,63,1|5fotw0,45,62,0|azhhzz,45,62,0|azhi00,46,63,1|b7v9vz,46,63,1|b7v9w0,45,62,0|bi7knz,45,62,0|bi7ko0,46,63,1|bl51vz,46,63,1|bl51w0,45,62,0\",\"America/Creston|,0,85,0|-18vrx38,50,66,0|-rshz81,50,66,0|-rshz80,51,40,0|-qx64g1,51,40,0|-qx64g0,50,66,0\",\"America/Cuiaba|,0,86,0|-t85hm4,42,42,0|-jyl7o1,42,42,0|-jyl7o0,39,44,1|-jpayc1,39,44,1|-jpayc0,42,42,0|-jfsa81,42,42,0|-jfsa80,39,44,1|-j6j101,39,44,1|-j6j100,42,42,0|-ahcvk1,42,42,0|-ahcvk0,39,44,1|-aad0w1,39,44,1|-aad0w0,42,42,0|-9yky81,42,42,0|-9yky80,39,44,1|-9scyc1,39,44,1|-9scyc0,42,42,0|-9ft0w1,42,42,0|-9ft0w0,39,44,1|-99j6c1,39,44,1|-99j6c0,42,42,0|-8wz8w1,42,42,0|-8wz8w0,39,44,1|-8scno1,39,44,1|-8scno0,42,42,0|-35xjk1,42,42,0|-35xjk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2kdm81,42,42,0|-2kdm80,39,44,1|-2hcfo1,39,44,1|-2hcfo0,42,42,0|-24qnk1,42,42,0|-24qnk0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1nia81,42,42,0|-1nia80,39,44,1|-1hc501,39,44,1|-1hc500,42,42,0|-14qcw1,42,42,0|-14qcw0,39,44,1|-yid01,39,44,1|-yid00,42,42,0|89jf3z,42,42,0|89jf40,39,44,1|8gdmzz,39,44,1|8gdn00,42,42,0|8rwj3z,42,42,0|8rwj40,39,44,1|8xnuzz,39,44,1|8xnv00,42,42,0|9aogfz,42,42,0|9aogg0,39,44,1|9g2tnz,39,44,1|9g2to0,42,42,0|9t1kfz,42,42,0|9t1kg0,39,44,1|9yfxnz,39,44,1|9yfxo0,42,42,0|abrn3z,42,42,0|abrn40,39,44,1|ahvxnz,39,44,1|ahvxo0,42,42,0|auuofz,42,42,0|auuog0,39,44,1|b0yyzz,39,44,1|b0yz00,42,42,0|bdkr3z,42,42,0|bdkr40,39,44,1|bjc2zz,39,44,1|bjc300,42,42,0|bwnsfz,42,42,0|bwnsg0,39,44,1|c1p6zz,39,44,1|c1p700,42,42,0|cf0wfz,42,42,0|cf0wg0,39,44,1|cli5nz,39,44,1|cli5o0,42,42,0|cxqz3z,42,42,0|cxqz40,39,44,1|d488bz,39,44,1|d488c0,42,42,0|dgh1rz,42,42,0|dgh1s0,39,44,1|dmlcbz,39,44,1|dmlcc0,42,42,0|dyu5rz,42,42,0|dyu5s0,39,44,1|e5odnz,39,44,1|e5odo0,42,42,0|ehm33z,42,42,0|ehm340,39,44,1|ep4dnz,39,44,1|ep4do0,42,42,0|f0n9rz,42,42,0|f0n9s0,39,44,1|f7hhnz,39,44,1|f7hho0,42,42,0|fj0drz,42,42,0|fj0ds0,39,44,1|fqkizz,39,44,1|fqkj00,42,42,0|g23f3z,42,42,0|g23f40,39,44,1|g8xmzz,39,44,1|g8xn00,42,42,0|gl6gfz,42,42,0|gl6gg0,39,44,1|grnpnz,39,44,1|grnpo0,42,42,0|h4zf3z,42,42,0|h4zf40,39,44,1|hadsbz,39,44,1|hadsc0,42,42,0|i6j9rz,42,42,0|i6j9s0,39,44,1|ic6wbz,39,44,1|ic6wc0,42,42,0|iofprz,42,42,0|iofps0,39,44,1|iuwyzz,39,44,1|iuwz00,42,42,0|j88ofz,42,42,0|j88og0,39,44,1|je00bz,39,44,1|je00c0,42,42,0|jpvv3z,42,42,0|jpvv40,39,44,1|jwd4bz,39,44,1|jwd4c0,42,42,0|k8ywfz,42,42,0|k8ywg0,39,44,1|kf36zz,39,44,1|kf3700,42,42,0|kroz3z,42,42,0|kroz40,39,44,1|ky68bz,39,44,1|ky68c0,42,42,0|laf1rz,42,42,0|laf1s0,39,44,1|lgwazz,39,44,1|lgwb00,42,42,0|lt54fz,42,42,0|lt54g0,39,44,1|lzzcbz,39,44,1|lzzcc0,42,42,0|mc85rz,42,42,0|mc85s0,39,44,1|micgbz,39,44,1|micgc0,42,42,0|muy8fz,42,42,0|muy8g0,39,44,1|n12izz,39,44,1|n12j00,42,42,0|ndob3z,42,42,0|ndob40,39,44,1|nk5kbz,39,44,1|nk5kc0,42,42,0|nwedrz,42,42,0|nweds0,39,44,1|o2vmzz,39,44,1|o2vn00,42,42,0|of4gfz,42,42,0|of4gg0,39,44,1|ollpnz,39,44,1|ollpo0,42,42,0|oxuj3z,42,42,0|oxuj40,39,44,1|p4bsbz,39,44,1|p4bsc0,42,42,0|phnhrz,42,42,0|phnhs0,39,44,1|pn1uzz,39,44,1|pn1v00,42,42,0\",\"America/Curacao|,0,58,0|-u7lckd,43,59,0|-2lx4u1,43,59,0|-2lx4u0,32,42,0\",\"America/Danmarkshavn|,0,87,0|-rvusjk,39,44,0|5ct4jz,39,44,0|5ct4k0,40,45,1|5lsw3z,40,45,1|5lsw40,39,44,0|5v5xfz,39,44,0|5v5xg0,40,45,1|64iyrz,40,45,1|64iys0,39,44,0|6dw03z,39,44,0|6dw040,40,45,1|6n91fz,40,45,1|6n91g0,39,44,0|6wm2rz,39,44,0|6wm2s0,40,45,1|75z43z,40,45,1|75z440,39,44,0|7fc5fz,39,44,0|7fc5g0,40,45,1|7p25fz,40,45,1|7p25g0,39,44,0|7yf6rz,39,44,0|7yf6s0,40,45,1|87s83z,40,45,1|87s840,39,44,0|8h59fz,39,44,0|8h59g0,40,45,1|8qiarz,40,45,1|8qias0,39,44,0|8zvc3z,39,44,0|8zvc40,40,45,1|998dfz,40,45,1|998dg0,39,44,0|9ilerz,39,44,0|9iles0,40,45,1|9ryg3z,40,45,1|9ryg40,39,44,0|a1bhfz,39,44,0|a1bhg0,40,45,1|aaoirz,40,45,1|aaois0,39,44,0|ak1k3z,39,44,0|ak1k40,40,45,1|atrk3z,40,45,1|atrk40,39,44,0|b34lfz,39,44,0|b34lg0,40,45,1|bchmrz,40,45,1|bchms0,39,44,0|bluo3z,39,44,0|bluo40,40,45,1|bv7pfz,40,45,1|bv7pg0,39,44,0|c4kqrz,39,44,0|c4kqs0,40,45,1|cdxs3z,40,45,1|cdxs40,39,44,0|cnatfz,39,44,0|cnatg0,40,45,1|cwnurz,40,45,1|cwnus0,39,44,0|d60w3z,39,44,0|d60w40,40,45,1|dfdxfz,40,45,1|dfdxg0,39,44,0|dkhezz,39,44,0|dkhf00,1,1,0\",\"America/Dawson|,0,88,0|-1079suk,36,37,0|-qzoms1,36,37,0|-qzoms0,64,40,1|-qplqw1,64,40,1|-qplqw0,36,37,0|-qess41,36,37,0|-qess40,64,40,1|-q6kps1,64,40,1|-q6kps0,36,37,0|-ek1tg1,36,37,0|-ek1tg0,65,40,1|-cq2tg1,65,40,1|-cq2tg0,66,40,1|-cnos81,66,40,1|-cnos80,36,37,0|-2g1oc1,36,37,0|-2g1oc0,67,66,1|-26boc1,67,66,1|-26boc0,36,37,0|1ztvnz,36,37,0|1ztvo0,51,40,0|5dwefz,51,40,0|5dweg0,57,66,1|5n9czz,57,66,1|5n9d00,51,40,0|5wmh3z,51,40,0|5wmh40,57,66,1|65zfnz,57,66,1|65zfo0,51,40,0|6fcjrz,51,40,0|6fcjs0,57,66,1|6p2gzz,57,66,1|6p2h00,51,40,0|6y2mfz,51,40,0|6y2mg0,57,66,1|77sjnz,57,66,1|77sjo0,51,40,0|7h5nrz,51,40,0|7h5ns0,57,66,1|7qimbz,57,66,1|7qimc0,51,40,0|7zvqfz,51,40,0|7zvqg0,57,66,1|898ozz,57,66,1|898p00,51,40,0|8ilt3z,51,40,0|8ilt40,57,66,1|8ryrnz,57,66,1|8ryro0,51,40,0|908zrz,51,40,0|908zs0,57,66,1|9aoubz,57,66,1|9aouc0,51,40,0|9iz2fz,51,40,0|9iz2g0,57,66,1|9trvnz,57,66,1|9trvo0,51,40,0|a1p53z,51,40,0|a1p540,57,66,1|achybz,57,66,1|achyc0,51,40,0|akf7rz,51,40,0|akf7s0,57,66,1|av80zz,57,66,1|av8100,51,40,0|b3i93z,51,40,0|b3i940,57,66,1|bdy3nz,57,66,1|bdy3o0,51,40,0|bm8brz,51,40,0|bm8bs0,57,66,1|bwo6bz,57,66,1|bwo6c0,51,40,0|c4yefz,51,40,0|c4yeg0,57,66,1|cfr7nz,57,66,1|cfr7o0,51,40,0|cnoh3z,51,40,0|cnoh40,57,66,1|cyhabz,57,66,1|cyhac0,51,40,0|d6ejrz,51,40,0|d6ejs0,57,66,1|dh7czz,57,66,1|dh7d00,51,40,0|dphl3z,51,40,0|dphl40,57,66,1|dzxfnz,57,66,1|dzxfo0,51,40,0|e87nrz,51,40,0|e87ns0,57,66,1|einibz,57,66,1|einic0,51,40,0|eqxqfz,51,40,0|eqxqg0,57,66,1|f1dkzz,57,66,1|f1dl00,51,40,0|f9nt3z,51,40,0|f9nt40,57,66,1|fkgmbz,57,66,1|fkgmc0,51,40,0|fsdvrz,51,40,0|fsdvs0,57,66,1|g36ozz,57,66,1|g36p00,51,40,0|gb3yfz,51,40,0|gb3yg0,57,66,1|glwrnz,57,66,1|glwro0,51,40,0|gu6zrz,51,40,0|gu6zs0,57,66,1|h4mubz,57,66,1|h4muc0,51,40,0|hcx2fz,51,40,0|hcx2g0,57,66,1|hncwzz,57,66,1|hncx00,51,40,0|hvn53z,51,40,0|hvn540,57,66,1|i6fybz,57,66,1|i6fyc0,51,40,0|ied7rz,51,40,0|ied7s0,57,66,1|ip60zz,57,66,1|ip6100,51,40,0|ix3afz,51,40,0|ix3ag0,57,66,1|j7w3nz,57,66,1|j7w3o0,51,40,0|jeqh3z,51,40,0|jeqh40,57,66,1|jqz4zz,57,66,1|jqz500,51,40,0|jxgjrz,51,40,0|jxgjs0,57,66,1|k9p7nz,57,66,1|k9p7o0,51,40,0|kg6mfz,51,40,0|kg6mg0,57,66,1|ksfabz,57,66,1|ksfac0,51,40,0|kz9nrz,51,40,0|kz9ns0,57,66,1|lbibnz,57,66,1|lbibo0,51,40,0|lhzqfz,51,40,0|lhzqg0,57,66,1|lu8ebz,57,66,1|lu8ec0,51,40,0|m0pt3z,51,40,0|m0pt40,57,66,1|mcygzz,57,66,1|mcyh00,51,40,0|mjfvrz,51,40,0|mjfvs0,57,66,1|mvojnz,57,66,1|mvojo0,51,40,0|n25yfz,51,40,0|n25yg0,57,66,1|neembz,57,66,1|neemc0,51,40,0|nkw13z,51,40,0|nkw140,57,66,1|nx4ozz,57,66,1|nx4p00,51,40,0|o3z2fz,51,40,0|o3z2g0,57,66,1|og7qbz,57,66,1|og7qc0,51,40,0|omp53z,51,40,0|omp540,57,66,1|oyxszz,57,66,1|oyxt00,51,40,0|p5f7rz,51,40,0|p5f7s0,57,66,1|phnvnz,57,66,1|phnvo0,51,40,0|po5afz,51,40,0|po5ag0,57,66,1|q0dybz,57,66,1|q0dyc0,51,40,0|q6vd3z,51,40,0|q6vd40,57,66,1|qj3vfz,57,66,1|qj3vg0,50,66,0\",\"America/Dawson_Creek|,0,89,0|-18vrweg,51,40,0|-qzopk1,51,40,0|-qzopk0,57,66,1|-qplto1,57,66,1|-qplto0,51,40,0|-ek1w81,51,40,0|-ek1w80,68,66,1|-cq2tg1,68,66,1|-cq2tg0,69,66,1|-cnov01,69,66,1|-cnov00,51,40,0|-bu5tk1,51,40,0|-bu5tk0,57,66,1|-bm8po1,57,66,1|-bm8po0,51,40,0|-bbfqw1,51,40,0|-bbfqw0,57,66,1|-b3in01,57,66,1|-b3in00,51,40,0|-aspo81,51,40,0|-aspo80,57,66,1|-akskc1,57,66,1|-akskc0,51,40,0|-a9mmw1,51,40,0|-a9mmw0,57,66,1|-a22ho1,57,66,1|-a22ho0,51,40,0|-9qwk81,51,40,0|-9qwk80,57,66,1|-9izgc1,57,66,1|-9izgc0,51,40,0|-986hk1,51,40,0|-986hk0,57,66,1|-909do1,57,66,1|-909do0,51,40,0|-8pgew1,51,40,0|-8pgew0,57,66,1|-8hjb01,57,66,1|-8hjb00,51,40,0|-86qc81,51,40,0|-86qc80,57,66,1|-7yt8c1,57,66,1|-7yt8c0,51,40,0|-7o09k1,51,40,0|-7o09k0,57,66,1|-7g35o1,57,66,1|-7g35o0,51,40,0|-74x881,51,40,0|-74x880,57,66,1|-6x04c1,57,66,1|-6x04c0,51,40,0|-6m75k1,51,40,0|-6m75k0,57,66,1|-6ea1o1,57,66,1|-6ea1o0,51,40,0|-63h2w1,51,40,0|-63h2w0,57,66,1|-5vjz01,57,66,1|-5vjz00,51,40,0|-5kr081,51,40,0|-5kr080,57,66,1|-5ctwc1,57,66,1|-5ctwc0,51,40,0|-520xk1,51,40,0|-520xk0,57,66,1|-4u3to1,57,66,1|-4u3to0,51,40,0|-4ixw81,51,40,0|-4ixw80,57,66,1|-4bdr01,57,66,1|-4bdr00,51,40,0|-407tk1,51,40,0|-407tk0,57,66,1|-3quv01,57,66,1|-3quv00,51,40,0|-3hhqw1,51,40,0|-3hhqw0,57,66,1|-384sc1,57,66,1|-384sc0,51,40,0|-2yro81,51,40,0|-2yro80,57,66,1|-2pepo1,57,66,1|-2pepo0,51,40,0|-2g1lk1,51,40,0|-2g1lk0,57,66,1|-26boc1,57,66,1|-26boc0,51,40,0|-1xbiw1,51,40,0|-1xbiw0,57,66,1|-1nllo1,57,66,1|-1nllo0,51,40,0|-1e8hk1,51,40,0|-1e8hk0,57,66,1|-14vj01,57,66,1|-14vj00,51,40,0|-view1,51,40,0|-view0,57,66,1|-m5gc1,57,66,1|-m5gc0,51,40,0|-csc81,51,40,0|-csc80,57,66,1|-3fdo1,57,66,1|-3fdo0,51,40,0|5xqfz,51,40,0|5xqg0,57,66,1|faozz,57,66,1|fap00,51,40,0|ont3z,51,40,0|ont40,57,66,1|ydqbz,57,66,1|ydqc0,51,40,0|17qufz,51,40,0|17qug0,57,66,1|1e0ozz,57,66,1|1e0p00,50,66,0\",\"America/Denver|,0,90,0|-18y0j80,50,66,0|-r0epo1,50,66,0|-r0epo0,52,62,1|-qplwg1,52,62,1|-qplwg0,50,66,0|-qhon01,50,66,0|-qhon00,52,62,1|-q6vts1,52,62,1|-q6vts0,50,66,0|-pyykc1,50,66,0|-pyykc0,52,62,1|-pnssg1,52,62,1|-pnssg0,50,66,0|-pg8ho1,50,66,0|-pg8ho0,52,62,1|-pdcv41,52,62,1|-pdcv40,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-2g1oc1,50,66,0|-2g1oc0,52,62,1|-26br41,52,62,1|-26br40,50,66,0|-1xblo1,50,66,0|-1xblo0,52,62,1|-1nlog1,52,62,1|-1nlog0,50,66,0|-1e8kc1,50,66,0|-1e8kc0,52,62,1|-14vls1,52,62,1|-14vls0,50,66,0|-viho1,50,66,0|-viho0,52,62,1|-m5j41,52,62,1|-m5j40,50,66,0|-csf01,50,66,0|-csf00,52,62,1|-3fgg1,52,62,1|-3fgg0,50,66,0|5xnnz,50,66,0|5xno0,52,62,1|fam7z,52,62,1|fam80,50,66,0|onqbz,50,66,0|onqc0,52,62,1|ydnjz,52,62,1|ydnk0,50,66,0|17qrnz,50,66,0|17qro0,52,62,1|1h3q7z,52,62,1|1h3q80,50,66,0|1qgubz,50,66,0|1qguc0,52,62,1|1ztsvz,52,62,1|1ztsw0,50,66,0|23fibz,50,66,0|23fic0,52,62,1|2ijvjz,52,62,1|2ijvk0,50,66,0|2oobnz,50,66,0|2oobo0,52,62,1|319y7z,52,62,1|319y80,50,66,0|3an2bz,50,66,0|3an2c0,52,62,1|3kczjz,52,62,1|3kczk0,50,66,0|3td4zz,50,66,0|3td500,52,62,1|43327z,52,62,1|433280,50,66,0|4cg6bz,50,66,0|4cg6c0,52,62,1|4lt4vz,52,62,1|4lt4w0,50,66,0|4v68zz,50,66,0|4v6900,52,62,1|54j7jz,52,62,1|54j7k0,50,66,0|5dwbnz,50,66,0|5dwbo0,52,62,1|5n9a7z,52,62,1|5n9a80,50,66,0|5wmebz,50,66,0|5wmec0,52,62,1|65zcvz,52,62,1|65zcw0,50,66,0|6fcgzz,50,66,0|6fch00,52,62,1|6p2e7z,52,62,1|6p2e80,50,66,0|6y2jnz,50,66,0|6y2jo0,52,62,1|77sgvz,52,62,1|77sgw0,50,66,0|7h5kzz,50,66,0|7h5l00,52,62,1|7qijjz,52,62,1|7qijk0,50,66,0|7zvnnz,50,66,0|7zvno0,52,62,1|898m7z,52,62,1|898m80,50,66,0|8ilqbz,50,66,0|8ilqc0,52,62,1|8ryovz,52,62,1|8ryow0,50,66,0|908wzz,50,66,0|908x00,52,62,1|9aorjz,52,62,1|9aork0,50,66,0|9iyznz,50,66,0|9iyzo0,52,62,1|9trsvz,52,62,1|9trsw0,50,66,0|a1p2bz,50,66,0|a1p2c0,52,62,1|achvjz,52,62,1|achvk0,50,66,0|akf4zz,50,66,0|akf500,52,62,1|av7y7z,52,62,1|av7y80,50,66,0|b3i6bz,50,66,0|b3i6c0,52,62,1|bdy0vz,52,62,1|bdy0w0,50,66,0|bm88zz,50,66,0|bm8900,52,62,1|bwo3jz,52,62,1|bwo3k0,50,66,0|c4ybnz,50,66,0|c4ybo0,52,62,1|cfr4vz,52,62,1|cfr4w0,50,66,0|cnoebz,50,66,0|cnoec0,52,62,1|cyh7jz,52,62,1|cyh7k0,50,66,0|d6egzz,50,66,0|d6eh00,52,62,1|dh7a7z,52,62,1|dh7a80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gb3vnz,50,66,0|gb3vo0,52,62,1|glwovz,52,62,1|glwow0,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jeqebz,50,66,0|jeqec0,52,62,1|jqz27z,52,62,1|jqz280,50,66,0|jxggzz,50,66,0|jxgh00,52,62,1|k9p4vz,52,62,1|k9p4w0,50,66,0|kg6jnz,50,66,0|kg6jo0,52,62,1|ksf7jz,52,62,1|ksf7k0,50,66,0|kz9kzz,50,66,0|kz9l00,52,62,1|lbi8vz,52,62,1|lbi8w0,50,66,0|lhznnz,50,66,0|lhzno0,52,62,1|lu8bjz,52,62,1|lu8bk0,50,66,0|m0pqbz,50,66,0|m0pqc0,52,62,1|mcye7z,52,62,1|mcye80,50,66,0|mjfszz,50,66,0|mjft00,52,62,1|mvogvz,52,62,1|mvogw0,50,66,0|n25vnz,50,66,0|n25vo0,52,62,1|neejjz,52,62,1|neejk0,50,66,0|nkvybz,50,66,0|nkvyc0,52,62,1|nx4m7z,52,62,1|nx4m80,50,66,0|o3yznz,50,66,0|o3yzo0,52,62,1|og7njz,52,62,1|og7nk0,50,66,0|omp2bz,50,66,0|omp2c0,52,62,1|oyxq7z,52,62,1|oyxq80,50,66,0|p5f4zz,50,66,0|p5f500,52,62,1|phnsvz,52,62,1|phnsw0,50,66,0|po57nz,50,66,0|po57o0,52,62,1|q0dvjz,52,62,1|q0dvk0,50,66,0|q6vabz,50,66,0|q6vac0,52,62,1|qj3y7z,52,62,1|qj3y80,50,66,0|qpybnz,50,66,0|qpybo0,52,62,1|r26zjz,52,62,1|r26zk0,50,66,0|r8oebz,50,66,0|r8oec0,52,62,1|rkx27z,52,62,1|rkx280,50,66,0|rregzz,50,66,0|rreh00,52,62,1|s3n4vz,52,62,1|s3n4w0,50,66,0|sa4jnz,50,66,0|sa4jo0,52,62,1|smd7jz,52,62,1|smd7k0,50,66,0|ssumbz,50,66,0|ssumc0,52,62,1|t53a7z,52,62,1|t53a80,50,66,0|tbkozz,50,66,0|tbkp00,52,62,1|tntcvz,52,62,1|tntcw0,50,66,0|tunqbz,50,66,0|tunqc0,52,62,1|u6we7z,52,62,1|u6we80,50,66,0|uddszz,50,66,0|uddt00,52,62,1|upmgvz,52,62,1|upmgw0,50,66,0|uw3vnz,50,66,0|uw3vo0,52,62,1|v8cjjz,52,62,1|v8cjk0,50,66,0|vetybz,50,66,0|vetyc0,52,62,1|vr2m7z,52,62,1|vr2m80,50,66,0|vxk0zz,50,66,0|vxk100,52,62,1|w9sovz,52,62,1|w9sow0,50,66,0|wgn2bz,50,66,0|wgn2c0,52,62,1|wsvq7z,52,62,1|wsvq80,50,66,0|wzd4zz,50,66,0|wzd500,52,62,1|xblsvz,52,62,1|xblsw0,50,66,0|xi37nz,50,66,0|xi37o0,52,62,1|xubvjz,52,62,1|xubvk0,50,66,0|y0tabz,50,66,0|y0tac0,52,62,1|yd1y7z,52,62,1|yd1y80,50,66,0|yjjczz,50,66,0|yjjd00,52,62,1|yvs0vz,52,62,1|yvs0w0,50,66,0|z29fnz,50,66,0|z29fo0,52,62,1|zei3jz,52,62,1|zei3k0,50,66,0\",\"America/Detroit|,0,91,0|-xx8dyd,45,62,0|-sih341,45,62,0|-sih340,49,63,0|-ek24k1,49,63,0|-ek24k0,70,42,1|-cq2tg1,70,42,1|-cq2tg0,71,42,1|-cnp3c1,71,42,1|-cnp3c0,49,63,0|-bbfz81,49,63,0|-bbfz80,62,42,1|-b3ivc1,62,42,1|-b3ivc0,49,63,0|-1bxjed,49,63,0|-1bxjec,62,42,1|-14vrc1,62,42,1|-14vrc0,49,63,0|-vin81,49,63,0|-vin80,62,42,1|-m5oo1,62,42,1|-m5oo0,49,63,0|1qgorz,49,63,0|1qgos0,62,42,1|1ztnbz,62,42,1|1ztnc0,49,63,0|23fcrz,49,63,0|23fcs0,62,42,1|2ijpzz,62,42,1|2ijq00,49,63,0|2rwu3z,49,63,0|2rwu40,62,42,1|319snz,62,42,1|319so0,49,63,0|3amwrz,49,63,0|3amws0,62,42,1|3kctzz,62,42,1|3kcu00,49,63,0|3tczfz,49,63,0|3tczg0,62,42,1|432wnz,62,42,1|432wo0,49,63,0|4cg0rz,49,63,0|4cg0s0,62,42,1|4lszbz,62,42,1|4lszc0,49,63,0|4v63fz,49,63,0|4v63g0,62,42,1|54j1zz,62,42,1|54j200,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0|7h5ffz,49,63,0|7h5fg0,62,42,1|7qidzz,62,42,1|7qie00,49,63,0|7zvi3z,49,63,0|7zvi40,62,42,1|898gnz,62,42,1|898go0,49,63,0|8ilkrz,49,63,0|8ilks0,62,42,1|8ryjbz,62,42,1|8ryjc0,49,63,0|908rfz,49,63,0|908rg0,62,42,1|9aolzz,62,42,1|9aom00,49,63,0|9iyu3z,49,63,0|9iyu40,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1owrz,49,63,0|a1ows0,62,42,1|achpzz,62,42,1|achq00,49,63,0|akezfz,49,63,0|akezg0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3i0rz,49,63,0|b3i0s0,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm83fz,49,63,0|bm83g0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y63z,49,63,0|c4y640,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno8rz,49,63,0|cno8s0,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6ebfz,49,63,0|d6ebg0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,49,63,0|fsdnfz,49,63,0|fsdng0,62,42,1|g36gnz,62,42,1|g36go0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Dominica|,0,41,0|-u6m79w,32,42,0\",\"America/Edmonton|,0,92,0|-x1yazk,50,66,0|-qzosc1,50,66,0|-qzosc0,52,62,1|-qplwg1,52,62,1|-qplwg0,50,66,0|-qgypo1,50,66,0|-qgypo0,52,62,1|-qepb41,52,62,1|-qepb40,50,66,0|-pxipo1,50,66,0|-pxipo0,52,62,1|-pnssg1,52,62,1|-pnssg0,50,66,0|-pesn01,50,66,0|-pesn00,52,62,1|-p6vj41,52,62,1|-p6vj40,50,66,0|-ovplo1,50,66,0|-ovplo0,52,62,1|-oo5gg1,52,62,1|-oo5gg0,50,66,0|-oczj01,50,66,0|-oczj00,52,62,1|-o52f41,52,62,1|-o52f40,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-bu5wc1,50,66,0|-bu5wc0,52,62,1|-bm8sg1,52,62,1|-bm8sg0,50,66,0|17qrnz,50,66,0|17qro0,52,62,1|1h3q7z,52,62,1|1h3q80,50,66,0|1qgubz,50,66,0|1qguc0,52,62,1|1ztsvz,52,62,1|1ztsw0,50,66,0|296wzz,50,66,0|296x00,52,62,1|2ijvjz,52,62,1|2ijvk0,50,66,0|2rwznz,50,66,0|2rwzo0,52,62,1|319y7z,52,62,1|319y80,50,66,0|3an2bz,50,66,0|3an2c0,52,62,1|3kczjz,52,62,1|3kczk0,50,66,0|3td4zz,50,66,0|3td500,52,62,1|43327z,52,62,1|433280,50,66,0|4cg6bz,50,66,0|4cg6c0,52,62,1|4lt4vz,52,62,1|4lt4w0,50,66,0|4v68zz,50,66,0|4v6900,52,62,1|54j7jz,52,62,1|54j7k0,50,66,0|5dwbnz,50,66,0|5dwbo0,52,62,1|5n9a7z,52,62,1|5n9a80,50,66,0|5wmebz,50,66,0|5wmec0,52,62,1|65zcvz,52,62,1|65zcw0,50,66,0|6fcgzz,50,66,0|6fch00,52,62,1|6p2e7z,52,62,1|6p2e80,50,66,0|6y2jnz,50,66,0|6y2jo0,52,62,1|77sgvz,52,62,1|77sgw0,50,66,0|7h5kzz,50,66,0|7h5l00,52,62,1|7qijjz,52,62,1|7qijk0,50,66,0|7zvnnz,50,66,0|7zvno0,52,62,1|898m7z,52,62,1|898m80,50,66,0|8ilqbz,50,66,0|8ilqc0,52,62,1|8ryovz,52,62,1|8ryow0,50,66,0|908wzz,50,66,0|908x00,52,62,1|9aorjz,52,62,1|9aork0,50,66,0|9iyznz,50,66,0|9iyzo0,52,62,1|9trsvz,52,62,1|9trsw0,50,66,0|a1p2bz,50,66,0|a1p2c0,52,62,1|achvjz,52,62,1|achvk0,50,66,0|akf4zz,50,66,0|akf500,52,62,1|av7y7z,52,62,1|av7y80,50,66,0|b3i6bz,50,66,0|b3i6c0,52,62,1|bdy0vz,52,62,1|bdy0w0,50,66,0|bm88zz,50,66,0|bm8900,52,62,1|bwo3jz,52,62,1|bwo3k0,50,66,0|c4ybnz,50,66,0|c4ybo0,52,62,1|cfr4vz,52,62,1|cfr4w0,50,66,0|cnoebz,50,66,0|cnoec0,52,62,1|cyh7jz,52,62,1|cyh7k0,50,66,0|d6egzz,50,66,0|d6eh00,52,62,1|dh7a7z,52,62,1|dh7a80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gb3vnz,50,66,0|gb3vo0,52,62,1|glwovz,52,62,1|glwow0,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jeqebz,50,66,0|jeqec0,52,62,1|jqz27z,52,62,1|jqz280,50,66,0|jxggzz,50,66,0|jxgh00,52,62,1|k9p4vz,52,62,1|k9p4w0,50,66,0|kg6jnz,50,66,0|kg6jo0,52,62,1|ksf7jz,52,62,1|ksf7k0,50,66,0|kz9kzz,50,66,0|kz9l00,52,62,1|lbi8vz,52,62,1|lbi8w0,50,66,0|lhznnz,50,66,0|lhzno0,52,62,1|lu8bjz,52,62,1|lu8bk0,50,66,0|m0pqbz,50,66,0|m0pqc0,52,62,1|mcye7z,52,62,1|mcye80,50,66,0|mjfszz,50,66,0|mjft00,52,62,1|mvogvz,52,62,1|mvogw0,50,66,0|n25vnz,50,66,0|n25vo0,52,62,1|neejjz,52,62,1|neejk0,50,66,0|nkvybz,50,66,0|nkvyc0,52,62,1|nx4m7z,52,62,1|nx4m80,50,66,0|o3yznz,50,66,0|o3yzo0,52,62,1|og7njz,52,62,1|og7nk0,50,66,0|omp2bz,50,66,0|omp2c0,52,62,1|oyxq7z,52,62,1|oyxq80,50,66,0|p5f4zz,50,66,0|p5f500,52,62,1|phnsvz,52,62,1|phnsw0,50,66,0|po57nz,50,66,0|po57o0,52,62,1|q0dvjz,52,62,1|q0dvk0,50,66,0|q6vabz,50,66,0|q6vac0,52,62,1|qj3y7z,52,62,1|qj3y80,50,66,0|qpybnz,50,66,0|qpybo0,52,62,1|r26zjz,52,62,1|r26zk0,50,66,0|r8oebz,50,66,0|r8oec0,52,62,1|rkx27z,52,62,1|rkx280,50,66,0|rregzz,50,66,0|rreh00,52,62,1|s3n4vz,52,62,1|s3n4w0,50,66,0|sa4jnz,50,66,0|sa4jo0,52,62,1|smd7jz,52,62,1|smd7k0,50,66,0|ssumbz,50,66,0|ssumc0,52,62,1|t53a7z,52,62,1|t53a80,50,66,0|tbkozz,50,66,0|tbkp00,52,62,1|tntcvz,52,62,1|tntcw0,50,66,0|tunqbz,50,66,0|tunqc0,52,62,1|u6we7z,52,62,1|u6we80,50,66,0|uddszz,50,66,0|uddt00,52,62,1|upmgvz,52,62,1|upmgw0,50,66,0|uw3vnz,50,66,0|uw3vo0,52,62,1|v8cjjz,52,62,1|v8cjk0,50,66,0|vetybz,50,66,0|vetyc0,52,62,1|vr2m7z,52,62,1|vr2m80,50,66,0|vxk0zz,50,66,0|vxk100,52,62,1|w9sovz,52,62,1|w9sow0,50,66,0|wgn2bz,50,66,0|wgn2c0,52,62,1|wsvq7z,52,62,1|wsvq80,50,66,0|wzd4zz,50,66,0|wzd500,52,62,1|xblsvz,52,62,1|xblsw0,50,66,0|xi37nz,50,66,0|xi37o0,52,62,1|xubvjz,52,62,1|xubvk0,50,66,0|y0tabz,50,66,0|y0tac0,52,62,1|yd1y7z,52,62,1|yd1y80,50,66,0|yjjczz,50,66,0|yjjd00,52,62,1|yvs0vz,52,62,1|yvs0w0,50,66,0|z29fnz,50,66,0|z29fo0,52,62,1|zei3jz,52,62,1|zei3k0,50,66,0\",\"America/Eirunepe|,0,93,0|-t85f28,56,63,0|-jyl4w1,56,63,0|-jyl4w0,42,42,1|-jpavk1,42,42,1|-jpavk0,56,63,0|-jfs7g1,56,63,0|-jfs7g0,42,42,1|-j6iy81,42,42,1|-j6iy80,56,63,0|-ahcss1,56,63,0|-ahcss0,42,42,1|-aacy41,42,42,1|-aacy40,56,63,0|-9ykvg1,56,63,0|-9ykvg0,42,42,1|-9scvk1,42,42,1|-9scvk0,56,63,0|-9fsy41,56,63,0|-9fsy40,42,42,1|-99j3k1,42,42,1|-99j3k0,56,63,0|-8wz641,56,63,0|-8wz640,42,42,1|-8sckw1,42,42,1|-8sckw0,56,63,0|-35xgs1,56,63,0|-35xgs0,42,42,1|-31nu81,42,42,1|-31nu80,56,63,0|-2kdjg1,56,63,0|-2kdjg0,42,42,1|-2hccw1,42,42,1|-2hccw0,56,63,0|-24qks1,56,63,0|-24qks0,42,42,1|-203zk1,42,42,1|-203zk0,56,63,0|-1ni7g1,56,63,0|-1ni7g0,42,42,1|-1hc281,42,42,1|-1hc280,56,63,0|-14qa41,56,63,0|-14qa40,42,42,1|-yia81,42,42,1|-yia80,56,63,0|89jhvz,56,63,0|89jhw0,42,42,1|8gdprz,42,42,1|8gdps0,56,63,0|8rwlvz,56,63,0|8rwlw0,42,42,1|8xnxrz,42,42,1|8xnxs0,56,63,0|9aoj7z,56,63,0|9aoj80,42,42,1|9g2wfz,42,42,1|9g2wg0,56,63,0|cf0z7z,56,63,0|cf0z80,42,42,1|cli8fz,42,42,1|cli8g0,56,63,0|k2yb7z,56,63,0|k2yb80,42,42,0|mw14fz,42,42,0|mw14g0,56,63,0\",\"America/El_Salvador|,0,94,0|-pkm4tc,45,62,0|91ojbz,45,62,0|91ojc0,46,63,1|998ojz,46,63,1|998ok0,45,62,0|9kelzz,45,62,0|9kem00,46,63,1|9ryr7z,46,63,1|9ryr80,45,62,0\",\"America/Fort_Nelson|,0,95,0|-18vrvy1,51,40,0|-qzopk1,51,40,0|-qzopk0,57,66,1|-qplto1,57,66,1|-qplto0,51,40,0|-ek1w81,51,40,0|-ek1w80,68,66,1|-cq2tg1,68,66,1|-cq2tg0,69,66,1|-cnov01,69,66,1|-cnov00,51,40,0|-bu5tk1,51,40,0|-bu5tk0,57,66,1|-bm8po1,57,66,1|-bm8po0,51,40,0|-bbfqw1,51,40,0|-bbfqw0,57,66,1|-b3in01,57,66,1|-b3in00,51,40,0|-aspo81,51,40,0|-aspo80,57,66,1|-akskc1,57,66,1|-akskc0,51,40,0|-a9mmw1,51,40,0|-a9mmw0,57,66,1|-a22ho1,57,66,1|-a22ho0,51,40,0|-9qwk81,51,40,0|-9qwk80,57,66,1|-9izgc1,57,66,1|-9izgc0,51,40,0|-986hk1,51,40,0|-986hk0,57,66,1|-909do1,57,66,1|-909do0,51,40,0|-8pgew1,51,40,0|-8pgew0,57,66,1|-8hjb01,57,66,1|-8hjb00,51,40,0|-86qc81,51,40,0|-86qc80,57,66,1|-7yt8c1,57,66,1|-7yt8c0,51,40,0|-7o09k1,51,40,0|-7o09k0,57,66,1|-7g35o1,57,66,1|-7g35o0,51,40,0|-74x881,51,40,0|-74x880,57,66,1|-6x04c1,57,66,1|-6x04c0,51,40,0|-6m75k1,51,40,0|-6m75k0,57,66,1|-6ea1o1,57,66,1|-6ea1o0,51,40,0|-63h2w1,51,40,0|-63h2w0,57,66,1|-5vjz01,57,66,1|-5vjz00,51,40,0|-5kr081,51,40,0|-5kr080,57,66,1|-5ctwc1,57,66,1|-5ctwc0,51,40,0|-520xk1,51,40,0|-520xk0,57,66,1|-4u3to1,57,66,1|-4u3to0,51,40,0|-4ixw81,51,40,0|-4ixw80,57,66,1|-4bdr01,57,66,1|-4bdr00,51,40,0|-407tk1,51,40,0|-407tk0,57,66,1|-3quv01,57,66,1|-3quv00,51,40,0|-3hhqw1,51,40,0|-3hhqw0,57,66,1|-384sc1,57,66,1|-384sc0,51,40,0|-2yro81,51,40,0|-2yro80,57,66,1|-2pepo1,57,66,1|-2pepo0,51,40,0|-2g1lk1,51,40,0|-2g1lk0,57,66,1|-26boc1,57,66,1|-26boc0,51,40,0|-1xbiw1,51,40,0|-1xbiw0,57,66,1|-1nllo1,57,66,1|-1nllo0,51,40,0|-1e8hk1,51,40,0|-1e8hk0,57,66,1|-14vj01,57,66,1|-14vj00,51,40,0|-view1,51,40,0|-view0,57,66,1|-m5gc1,57,66,1|-m5gc0,51,40,0|-csc81,51,40,0|-csc80,57,66,1|-3fdo1,57,66,1|-3fdo0,51,40,0|5xqfz,51,40,0|5xqg0,57,66,1|faozz,57,66,1|fap00,51,40,0|ont3z,51,40,0|ont40,57,66,1|ydqbz,57,66,1|ydqc0,51,40,0|17qufz,51,40,0|17qug0,57,66,1|1h3szz,57,66,1|1h3t00,51,40,0|1qgx3z,51,40,0|1qgx40,57,66,1|1ztvnz,57,66,1|1ztvo0,51,40,0|296zrz,51,40,0|296zs0,57,66,1|2ijybz,57,66,1|2ijyc0,51,40,0|2rx2fz,51,40,0|2rx2g0,57,66,1|31a0zz,57,66,1|31a100,51,40,0|3an53z,51,40,0|3an540,57,66,1|3kd2bz,57,66,1|3kd2c0,51,40,0|3td7rz,51,40,0|3td7s0,57,66,1|4334zz,57,66,1|433500,51,40,0|4cg93z,51,40,0|4cg940,57,66,1|4lt7nz,57,66,1|4lt7o0,51,40,0|4v6brz,51,40,0|4v6bs0,57,66,1|54jabz,57,66,1|54jac0,51,40,0|5dwefz,51,40,0|5dweg0,57,66,1|5n9czz,57,66,1|5n9d00,51,40,0|5wmh3z,51,40,0|5wmh40,57,66,1|65zfnz,57,66,1|65zfo0,51,40,0|6fcjrz,51,40,0|6fcjs0,57,66,1|6p2gzz,57,66,1|6p2h00,51,40,0|6y2mfz,51,40,0|6y2mg0,57,66,1|77sjnz,57,66,1|77sjo0,51,40,0|7h5nrz,51,40,0|7h5ns0,57,66,1|7qimbz,57,66,1|7qimc0,51,40,0|7zvqfz,51,40,0|7zvqg0,57,66,1|898ozz,57,66,1|898p00,51,40,0|8ilt3z,51,40,0|8ilt40,57,66,1|8ryrnz,57,66,1|8ryro0,51,40,0|908zrz,51,40,0|908zs0,57,66,1|9aoubz,57,66,1|9aouc0,51,40,0|9iz2fz,51,40,0|9iz2g0,57,66,1|9trvnz,57,66,1|9trvo0,51,40,0|a1p53z,51,40,0|a1p540,57,66,1|achybz,57,66,1|achyc0,51,40,0|akf7rz,51,40,0|akf7s0,57,66,1|av80zz,57,66,1|av8100,51,40,0|b3i93z,51,40,0|b3i940,57,66,1|bdy3nz,57,66,1|bdy3o0,51,40,0|bm8brz,51,40,0|bm8bs0,57,66,1|bwo6bz,57,66,1|bwo6c0,51,40,0|c4yefz,51,40,0|c4yeg0,57,66,1|cfr7nz,57,66,1|cfr7o0,51,40,0|cnoh3z,51,40,0|cnoh40,57,66,1|cyhabz,57,66,1|cyhac0,51,40,0|d6ejrz,51,40,0|d6ejs0,57,66,1|dh7czz,57,66,1|dh7d00,51,40,0|dphl3z,51,40,0|dphl40,57,66,1|dzxfnz,57,66,1|dzxfo0,51,40,0|e87nrz,51,40,0|e87ns0,57,66,1|einibz,57,66,1|einic0,51,40,0|eqxqfz,51,40,0|eqxqg0,57,66,1|f1dkzz,57,66,1|f1dl00,51,40,0|f9nt3z,51,40,0|f9nt40,57,66,1|fkgmbz,57,66,1|fkgmc0,51,40,0|fsdvrz,51,40,0|fsdvs0,57,66,1|g36ozz,57,66,1|g36p00,51,40,0|gb3yfz,51,40,0|gb3yg0,57,66,1|glwrnz,57,66,1|glwro0,51,40,0|gu6zrz,51,40,0|gu6zs0,57,66,1|h4mubz,57,66,1|h4muc0,51,40,0|hcx2fz,51,40,0|hcx2g0,57,66,1|hncwzz,57,66,1|hncx00,51,40,0|hvn53z,51,40,0|hvn540,57,66,1|i6fybz,57,66,1|i6fyc0,51,40,0|ied7rz,51,40,0|ied7s0,57,66,1|ip60zz,57,66,1|ip6100,51,40,0|ix3afz,51,40,0|ix3ag0,57,66,1|j7w3nz,57,66,1|j7w3o0,51,40,0|jeqh3z,51,40,0|jeqh40,57,66,1|jqz4zz,57,66,1|jqz500,51,40,0|jxgjrz,51,40,0|jxgjs0,57,66,1|k9p7nz,57,66,1|k9p7o0,51,40,0|kg6mfz,51,40,0|kg6mg0,57,66,1|ksfabz,57,66,1|ksfac0,51,40,0|kz9nrz,51,40,0|kz9ns0,57,66,1|lbibnz,57,66,1|lbibo0,51,40,0|lhzqfz,51,40,0|lhzqg0,57,66,1|lu8ebz,57,66,1|lu8ec0,51,40,0|m0pt3z,51,40,0|m0pt40,57,66,1|mcygzz,57,66,1|mcyh00,51,40,0|mjfvrz,51,40,0|mjfvs0,57,66,1|mvojnz,57,66,1|mvojo0,51,40,0|n25yfz,51,40,0|n25yg0,57,66,1|neembz,57,66,1|neemc0,51,40,0|nkw13z,51,40,0|nkw140,50,66,0\",\"America/Fortaleza|,0,96,0|-t85kvc,39,44,0|-jylag1,39,44,0|-jylag0,40,45,1|-jpb141,40,45,1|-jpb140,39,44,0|-jfsd01,39,44,0|-jfsd00,40,45,1|-j6j3s1,40,45,1|-j6j3s0,39,44,0|-ahcyc1,39,44,0|-ahcyc0,40,45,1|-aad3o1,40,45,1|-aad3o0,39,44,0|-9yl101,39,44,0|-9yl100,40,45,1|-9sd141,40,45,1|-9sd140,39,44,0|-9ft3o1,39,44,0|-9ft3o0,40,45,1|-99j941,40,45,1|-99j940,39,44,0|-8wzbo1,39,44,0|-8wzbo0,40,45,1|-8scqg1,40,45,1|-8scqg0,39,44,0|-35xmc1,39,44,0|-35xmc0,40,45,1|-31nzs1,40,45,1|-31nzs0,39,44,0|-2kdp01,39,44,0|-2kdp00,40,45,1|-2hcig1,40,45,1|-2hcig0,39,44,0|-24qqc1,39,44,0|-24qqc0,40,45,1|-204541,40,45,1|-204540,39,44,0|-1nid01,39,44,0|-1nid00,40,45,1|-1hc7s1,40,45,1|-1hc7s0,39,44,0|-14qfo1,39,44,0|-14qfo0,40,45,1|-yifs1,40,45,1|-yifs0,39,44,0|89jcbz,39,44,0|89jcc0,40,45,1|8gdk7z,40,45,1|8gdk80,39,44,0|8rwgbz,39,44,0|8rwgc0,40,45,1|8xns7z,40,45,1|8xns80,39,44,0|9aodnz,39,44,0|9aodo0,40,45,1|9g2qvz,40,45,1|9g2qw0,39,44,0|9t1hnz,39,44,0|9t1ho0,40,45,1|9yfuvz,40,45,1|9yfuw0,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|ahvuvz,40,45,1|ahvuw0,39,44,0|fj0azz,39,44,0|fj0b00,40,45,1|fqkg7z,40,45,1|fqkg80,39,44,0|g23cbz,39,44,0|g23cc0,40,45,1|g2t6vz,40,45,1|g2t6w0,39,44,0|gl6dnz,39,44,0|gl6do0,40,45,1|grnmvz,40,45,1|grnmw0,39,44,0\",\"America/Glace_Bay|,0,97,0|-z94kwc,32,42,0|-qzp0o1,32,42,0|-qzp0o0,54,44,1|-qpm4s1,54,44,1|-qpm4s0,32,42,0|-ek27c1,32,42,0|-ek27c0,33,44,1|-cq2tg1,33,44,1|-cq2tg0,34,44,1|-cnp641,34,44,1|-cnp640,32,42,0|-8pgq01,32,42,0|-8pgq00,54,44,1|-8hjm41,54,44,1|-8hjm40,32,42,0|17qjbz,32,42,0|17qjc0,54,44,1|1h3hvz,54,44,1|1h3hw0,32,42,0|1qglzz,32,42,0|1qgm00,54,44,1|1ztkjz,54,44,1|1ztkk0,32,42,0|296onz,32,42,0|296oo0,54,44,1|2ijn7z,54,44,1|2ijn80,32,42,0|2rwrbz,32,42,0|2rwrc0,54,44,1|319pvz,54,44,1|319pw0,32,42,0|3amtzz,32,42,0|3amu00,54,44,1|3kcr7z,54,44,1|3kcr80,32,42,0|3tcwnz,32,42,0|3tcwo0,54,44,1|432tvz,54,44,1|432tw0,32,42,0|4cfxzz,32,42,0|4cfy00,54,44,1|4lswjz,54,44,1|4lswk0,32,42,0|4v60nz,32,42,0|4v60o0,54,44,1|54iz7z,54,44,1|54iz80,32,42,0|5dw3bz,32,42,0|5dw3c0,54,44,1|5n91vz,54,44,1|5n91w0,32,42,0|5wm5zz,32,42,0|5wm600,54,44,1|65z4jz,54,44,1|65z4k0,32,42,0|6fc8nz,32,42,0|6fc8o0,54,44,1|6p25vz,54,44,1|6p25w0,32,42,0|6y2bbz,32,42,0|6y2bc0,54,44,1|77s8jz,54,44,1|77s8k0,32,42,0|7h5cnz,32,42,0|7h5co0,54,44,1|7qib7z,54,44,1|7qib80,32,42,0|7zvfbz,32,42,0|7zvfc0,54,44,1|898dvz,54,44,1|898dw0,32,42,0|8ilhzz,32,42,0|8ili00,54,44,1|8rygjz,54,44,1|8rygk0,32,42,0|908onz,32,42,0|908oo0,54,44,1|9aoj7z,54,44,1|9aoj80,32,42,0|9iyrbz,32,42,0|9iyrc0,54,44,1|9trkjz,54,44,1|9trkk0,32,42,0|a1otzz,32,42,0|a1ou00,54,44,1|achn7z,54,44,1|achn80,32,42,0|akewnz,32,42,0|akewo0,54,44,1|av7pvz,54,44,1|av7pw0,32,42,0|b3hxzz,32,42,0|b3hy00,54,44,1|bdxsjz,54,44,1|bdxsk0,32,42,0|bm80nz,32,42,0|bm80o0,54,44,1|bwnv7z,54,44,1|bwnv80,32,42,0|c4y3bz,32,42,0|c4y3c0,54,44,1|cfqwjz,54,44,1|cfqwk0,32,42,0|cno5zz,32,42,0|cno600,54,44,1|cygz7z,54,44,1|cygz80,32,42,0|d6e8nz,32,42,0|d6e8o0,54,44,1|dh71vz,54,44,1|dh71w0,32,42,0|dph9zz,32,42,0|dpha00,54,44,1|dzx4jz,54,44,1|dzx4k0,32,42,0|e87cnz,32,42,0|e87co0,54,44,1|ein77z,54,44,1|ein780,32,42,0|eqxfbz,32,42,0|eqxfc0,54,44,1|f1d9vz,54,44,1|f1d9w0,32,42,0|f9nhzz,32,42,0|f9ni00,54,44,1|fkgb7z,54,44,1|fkgb80,32,42,0|fsdknz,32,42,0|fsdko0,54,44,1|g36dvz,54,44,1|g36dw0,32,42,0|gb3nbz,32,42,0|gb3nc0,54,44,1|glwgjz,54,44,1|glwgk0,32,42,0|gu6onz,32,42,0|gu6oo0,54,44,1|h4mj7z,54,44,1|h4mj80,32,42,0|hcwrbz,32,42,0|hcwrc0,54,44,1|hnclvz,54,44,1|hnclw0,32,42,0|hvmtzz,32,42,0|hvmu00,54,44,1|i6fn7z,54,44,1|i6fn80,32,42,0|iecwnz,32,42,0|iecwo0,54,44,1|ip5pvz,54,44,1|ip5pw0,32,42,0|ix2zbz,32,42,0|ix2zc0,54,44,1|j7vsjz,54,44,1|j7vsk0,32,42,0|jeq5zz,32,42,0|jeq600,54,44,1|jqytvz,54,44,1|jqytw0,32,42,0|jxg8nz,32,42,0|jxg8o0,54,44,1|k9owjz,54,44,1|k9owk0,32,42,0|kg6bbz,32,42,0|kg6bc0,54,44,1|ksez7z,54,44,1|ksez80,32,42,0|kz9cnz,32,42,0|kz9co0,54,44,1|lbi0jz,54,44,1|lbi0k0,32,42,0|lhzfbz,32,42,0|lhzfc0,54,44,1|lu837z,54,44,1|lu8380,32,42,0|m0phzz,32,42,0|m0pi00,54,44,1|mcy5vz,54,44,1|mcy5w0,32,42,0|mjfknz,32,42,0|mjfko0,54,44,1|mvo8jz,54,44,1|mvo8k0,32,42,0|n25nbz,32,42,0|n25nc0,54,44,1|neeb7z,54,44,1|neeb80,32,42,0|nkvpzz,32,42,0|nkvq00,54,44,1|nx4dvz,54,44,1|nx4dw0,32,42,0|o3yrbz,32,42,0|o3yrc0,54,44,1|og7f7z,54,44,1|og7f80,32,42,0|omotzz,32,42,0|omou00,54,44,1|oyxhvz,54,44,1|oyxhw0,32,42,0|p5ewnz,32,42,0|p5ewo0,54,44,1|phnkjz,54,44,1|phnkk0,32,42,0|po4zbz,32,42,0|po4zc0,54,44,1|q0dn7z,54,44,1|q0dn80,32,42,0|q6v1zz,32,42,0|q6v200,54,44,1|qj3pvz,54,44,1|qj3pw0,32,42,0|qpy3bz,32,42,0|qpy3c0,54,44,1|r26r7z,54,44,1|r26r80,32,42,0|r8o5zz,32,42,0|r8o600,54,44,1|rkwtvz,54,44,1|rkwtw0,32,42,0|rre8nz,32,42,0|rre8o0,54,44,1|s3mwjz,54,44,1|s3mwk0,32,42,0|sa4bbz,32,42,0|sa4bc0,54,44,1|smcz7z,54,44,1|smcz80,32,42,0|ssudzz,32,42,0|ssue00,54,44,1|t531vz,54,44,1|t531w0,32,42,0|tbkgnz,32,42,0|tbkgo0,54,44,1|tnt4jz,54,44,1|tnt4k0,32,42,0|tunhzz,32,42,0|tuni00,54,44,1|u6w5vz,54,44,1|u6w5w0,32,42,0|uddknz,32,42,0|uddko0,54,44,1|upm8jz,54,44,1|upm8k0,32,42,0|uw3nbz,32,42,0|uw3nc0,54,44,1|v8cb7z,54,44,1|v8cb80,32,42,0|vetpzz,32,42,0|vetq00,54,44,1|vr2dvz,54,44,1|vr2dw0,32,42,0|vxjsnz,32,42,0|vxjso0,54,44,1|w9sgjz,54,44,1|w9sgk0,32,42,0|wgmtzz,32,42,0|wgmu00,54,44,1|wsvhvz,54,44,1|wsvhw0,32,42,0|wzcwnz,32,42,0|wzcwo0,54,44,1|xblkjz,54,44,1|xblkk0,32,42,0|xi2zbz,32,42,0|xi2zc0,54,44,1|xubn7z,54,44,1|xubn80,32,42,0|y0t1zz,32,42,0|y0t200,54,44,1|yd1pvz,54,44,1|yd1pw0,32,42,0|yjj4nz,32,42,0|yjj4o0,54,44,1|yvrsjz,54,44,1|yvrsk0,32,42,0|z297bz,32,42,0|z297c0,54,44,1|zehv7z,54,44,1|zehv80,32,42,0\",\"America/Goose_Bay|,0,98,0|-18vs7h8,24,99,0|-qzp20l,24,99,0|-qzp20k,72,100,1|-qpm64l,72,100,1|-qpm64k,24,99,0|-i52u8l,24,99,0|-i52u8k,24,101,0|-hk3aa1,24,101,0|-hk3aa0,72,102,1|-hcj521,72,102,1|-hcj520,24,101,0|-h1d7m1,24,101,0|-h1d7m0,72,102,1|-gtt2e1,72,102,1|-gtt2e0,24,101,0|-gin4y1,24,101,0|-gin4y0,72,102,1|-gb2zq1,72,102,1|-gb2zq0,24,101,0|-fzk3m1,24,101,0|-fzk3m0,72,102,1|-fscx21,72,102,1|-fscx20,24,101,0|-fgu0y1,24,101,0|-fgu0y0,72,102,1|-f99vq1,72,102,1|-f99vq0,24,101,0|-ey3ya1,24,101,0|-ey3ya0,72,102,1|-eqjt21,72,102,1|-eqjt20,24,101,0|-efdvm1,24,101,0|-efdvm0,25,102,1|-cq2tg1,25,102,1|-cq2tg0,26,102,1|-cnp7i1,26,102,1|-cnp7i0,24,101,0|-cc6be1,24,101,0|-cc6be0,72,102,1|-c4m661,72,102,1|-c4m660,24,101,0|-btg8q1,24,101,0|-btg8q0,72,102,1|-blw3i1,72,102,1|-blw3i0,24,101,0|-baq621,24,101,0|-baq620,72,102,1|-b360u1,72,102,1|-b360u0,24,101,0|-as03e1,24,101,0|-as03e0,72,102,1|-akfy61,72,102,1|-akfy60,24,101,0|-a8x221,24,101,0|-a8x220,72,102,1|-a1cwu1,72,102,1|-a1cwu0,24,101,0|-9qwwq1,24,101,0|-9qwwq0,72,102,1|-9izsu1,72,102,1|-9izsu0,24,101,0|-986u21,24,101,0|-986u20,72,102,1|-909q61,72,102,1|-909q60,24,101,0|-8pgre1,24,101,0|-8pgre0,72,102,1|-8hjni1,72,102,1|-8hjni0,24,101,0|-86qoq1,24,101,0|-86qoq0,72,102,1|-7ytku1,72,102,1|-7ytku0,24,101,0|-7o0m21,24,101,0|-7o0m20,72,102,1|-7g3i61,72,102,1|-7g3i60,24,101,0|-74xkq1,24,101,0|-74xkq0,72,102,1|-6x0gu1,72,102,1|-6x0gu0,24,101,0|-6m7i21,24,101,0|-6m7i20,72,102,1|-6eae61,72,102,1|-6eae60,24,101,0|-63hfe1,24,101,0|-63hfe0,72,102,1|-5vkbi1,72,102,1|-5vkbi0,24,101,0|-5krcq1,24,101,0|-5krcq0,72,102,1|-5cu8u1,72,102,1|-5cu8u0,24,101,0|-521a21,24,101,0|-521a20,72,102,1|-4sbcu1,72,102,1|-4sbcu0,24,101,0|-4iy8q1,24,101,0|-4iy8q0,72,102,1|-49la61,72,102,1|-49la60,24,101,0|-408621,24,101,0|-408620,72,102,1|-3qv7i1,72,102,1|-3qv7i0,24,101,0|-3hi3e1,24,101,0|-3hi3e0,72,102,1|-3854u1,72,102,1|-3854u0,24,101,0|-2ys0q1,24,101,0|-2ys0q0,72,102,1|-2pf261,72,102,1|-2pf260,24,101,0|-2g1y21,24,101,0|-2g1y20,72,102,1|-26c0u1,72,102,1|-26c0u0,24,101,0|-1zdy21,24,101,0|-1zdy20,32,42,0|-1xbu01,32,42,0|-1xbu00,54,44,1|-1nlws1,54,44,1|-1nlws0,32,42,0|-1e8so1,32,42,0|-1e8so0,54,44,1|-14vu41,54,44,1|-14vu40,32,42,0|-viq01,32,42,0|-viq00,54,44,1|-m5rg1,54,44,1|-m5rg0,32,42,0|-csnc1,32,42,0|-csnc0,54,44,1|-3fos1,54,44,1|-3fos0,32,42,0|5xfbz,32,42,0|5xfc0,54,44,1|fadvz,54,44,1|fadw0,32,42,0|onhzz,32,42,0|oni00,54,44,1|ydf7z,54,44,1|ydf80,32,42,0|17qjbz,32,42,0|17qjc0,54,44,1|1h3hvz,54,44,1|1h3hw0,32,42,0|1qglzz,32,42,0|1qgm00,54,44,1|1ztkjz,54,44,1|1ztkk0,32,42,0|296onz,32,42,0|296oo0,54,44,1|2ijn7z,54,44,1|2ijn80,32,42,0|2rwrbz,32,42,0|2rwrc0,54,44,1|319pvz,54,44,1|319pw0,32,42,0|3amtzz,32,42,0|3amu00,54,44,1|3kcr7z,54,44,1|3kcr80,32,42,0|3tcwnz,32,42,0|3tcwo0,54,44,1|432tvz,54,44,1|432tw0,32,42,0|4cfxzz,32,42,0|4cfy00,54,44,1|4lswjz,54,44,1|4lswk0,32,42,0|4v60nz,32,42,0|4v60o0,54,44,1|54iz7z,54,44,1|54iz80,32,42,0|5dw3bz,32,42,0|5dw3c0,54,44,1|5n91vz,54,44,1|5n91w0,32,42,0|5wm5zz,32,42,0|5wm600,54,44,1|65z4jz,54,44,1|65z4k0,32,42,0|6fc8nz,32,42,0|6fc8o0,54,44,1|6p25vz,54,44,1|6p25w0,32,42,0|6y2bbz,32,42,0|6y2bc0,54,44,1|77s8jz,54,44,1|77s8k0,32,42,0|7h5cnz,32,42,0|7h5co0,54,44,1|7qib7z,54,44,1|7qib80,32,42,0|7zvfbz,32,42,0|7zvfc0,54,44,1|898dvz,54,44,1|898dw0,32,42,0|8ilhzz,32,42,0|8ili00,54,44,1|8rygjz,54,44,1|8rygk0,32,42,0|908j5n,32,42,0|908j5o,54,44,1|9aodpn,54,44,1|9aodpo,32,42,0|9iyltn,32,42,0|9iylto,73,45,1|9trc9n,73,45,1|9trc9o,32,42,0|a1oohn,32,42,0|a1ooho,54,44,1|achhpn,54,44,1|achhpo,32,42,0|aker5n,32,42,0|aker5o,54,44,1|av7kdn,54,44,1|av7kdo,32,42,0|b3hshn,32,42,0|b3hsho,54,44,1|bdxn1n,54,44,1|bdxn1o,32,42,0|bm7v5n,32,42,0|bm7v5o,54,44,1|bwnppn,54,44,1|bwnppo,32,42,0|c4xxtn,32,42,0|c4xxto,54,44,1|cfqr1n,54,44,1|cfqr1o,32,42,0|cno0hn,32,42,0|cno0ho,54,44,1|cygtpn,54,44,1|cygtpo,32,42,0|d6e35n,32,42,0|d6e35o,54,44,1|dh6wdn,54,44,1|dh6wdo,32,42,0|dph4hn,32,42,0|dph4ho,54,44,1|dzwz1n,54,44,1|dzwz1o,32,42,0|e8775n,32,42,0|e8775o,54,44,1|ein1pn,54,44,1|ein1po,32,42,0|eqx9tn,32,42,0|eqx9to,54,44,1|f1d4dn,54,44,1|f1d4do,32,42,0|f9nchn,32,42,0|f9ncho,54,44,1|fkg5pn,54,44,1|fkg5po,32,42,0|fsdf5n,32,42,0|fsdf5o,54,44,1|g368dn,54,44,1|g368do,32,42,0|gb3htn,32,42,0|gb3hto,54,44,1|glwb1n,54,44,1|glwb1o,32,42,0|gu6j5n,32,42,0|gu6j5o,54,44,1|h4mdpn,54,44,1|h4mdpo,32,42,0|hcwltn,32,42,0|hcwlto,54,44,1|hncgdn,54,44,1|hncgdo,32,42,0|hvmohn,32,42,0|hvmoho,54,44,1|i6fhpn,54,44,1|i6fhpo,32,42,0|iecr5n,32,42,0|iecr5o,54,44,1|ip5kdn,54,44,1|ip5kdo,32,42,0|ix2ttn,32,42,0|ix2tto,54,44,1|j7vn1n,54,44,1|j7vn1o,32,42,0|jeq0hn,32,42,0|jeq0ho,54,44,1|jqyodn,54,44,1|jqyodo,32,42,0|jxg35n,32,42,0|jxg35o,54,44,1|k9or1n,54,44,1|k9or1o,32,42,0|kg65tn,32,42,0|kg65to,54,44,1|ksetpn,54,44,1|ksetpo,32,42,0|kz975n,32,42,0|kz975o,54,44,1|lbhv1n,54,44,1|lbhv1o,32,42,0|lhz9tn,32,42,0|lhz9to,54,44,1|lu837z,54,44,1|lu8380,32,42,0|m0phzz,32,42,0|m0pi00,54,44,1|mcy5vz,54,44,1|mcy5w0,32,42,0|mjfknz,32,42,0|mjfko0,54,44,1|mvo8jz,54,44,1|mvo8k0,32,42,0|n25nbz,32,42,0|n25nc0,54,44,1|neeb7z,54,44,1|neeb80,32,42,0|nkvpzz,32,42,0|nkvq00,54,44,1|nx4dvz,54,44,1|nx4dw0,32,42,0|o3yrbz,32,42,0|o3yrc0,54,44,1|og7f7z,54,44,1|og7f80,32,42,0|omotzz,32,42,0|omou00,54,44,1|oyxhvz,54,44,1|oyxhw0,32,42,0|p5ewnz,32,42,0|p5ewo0,54,44,1|phnkjz,54,44,1|phnkk0,32,42,0|po4zbz,32,42,0|po4zc0,54,44,1|q0dn7z,54,44,1|q0dn80,32,42,0|q6v1zz,32,42,0|q6v200,54,44,1|qj3pvz,54,44,1|qj3pw0,32,42,0|qpy3bz,32,42,0|qpy3c0,54,44,1|r26r7z,54,44,1|r26r80,32,42,0|r8o5zz,32,42,0|r8o600,54,44,1|rkwtvz,54,44,1|rkwtw0,32,42,0|rre8nz,32,42,0|rre8o0,54,44,1|s3mwjz,54,44,1|s3mwk0,32,42,0|sa4bbz,32,42,0|sa4bc0,54,44,1|smcz7z,54,44,1|smcz80,32,42,0|ssudzz,32,42,0|ssue00,54,44,1|t531vz,54,44,1|t531w0,32,42,0|tbkgnz,32,42,0|tbkgo0,54,44,1|tnt4jz,54,44,1|tnt4k0,32,42,0|tunhzz,32,42,0|tuni00,54,44,1|u6w5vz,54,44,1|u6w5w0,32,42,0|uddknz,32,42,0|uddko0,54,44,1|upm8jz,54,44,1|upm8k0,32,42,0|uw3nbz,32,42,0|uw3nc0,54,44,1|v8cb7z,54,44,1|v8cb80,32,42,0|vetpzz,32,42,0|vetq00,54,44,1|vr2dvz,54,44,1|vr2dw0,32,42,0|vxjsnz,32,42,0|vxjso0,54,44,1|w9sgjz,54,44,1|w9sgk0,32,42,0|wgmtzz,32,42,0|wgmu00,54,44,1|wsvhvz,54,44,1|wsvhw0,32,42,0|wzcwnz,32,42,0|wzcwo0,54,44,1|xblkjz,54,44,1|xblkk0,32,42,0|xi2zbz,32,42,0|xi2zc0,54,44,1|xubn7z,54,44,1|xubn80,32,42,0|y0t1zz,32,42,0|y0t200,54,44,1|yd1pvz,54,44,1|yd1pw0,32,42,0|yjj4nz,32,42,0|yjj4o0,54,44,1|yvrsjz,54,44,1|yvrsk0,32,42,0|z297bz,32,42,0|z297c0,54,44,1|zehv7z,54,44,1|zehv80,32,42,0\",\"America/Grand_Turk|,0,103,0|-15r0w5s,74,104,0|-u85og3,74,104,0|-u85og2,49,63,0|4v63fz,49,63,0|4v63g0,62,42,1|54j1zz,62,42,1|54j200,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0|7h5ffz,49,63,0|7h5fg0,62,42,1|7qidzz,62,42,1|7qie00,49,63,0|7zvi3z,49,63,0|7zvi40,62,42,1|898gnz,62,42,1|898go0,49,63,0|8ilkrz,49,63,0|8ilks0,62,42,1|8ryjbz,62,42,1|8ryjc0,49,63,0|908rfz,49,63,0|908rg0,62,42,1|9aolzz,62,42,1|9aom00,49,63,0|9iyu3z,49,63,0|9iyu40,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1owrz,49,63,0|a1ows0,62,42,1|achpzz,62,42,1|achq00,49,63,0|akezfz,49,63,0|akezg0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3i0rz,49,63,0|b3i0s0,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm83fz,49,63,0|bm83g0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y63z,49,63,0|c4y640,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno8rz,49,63,0|cno8s0,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6ebfz,49,63,0|d6ebg0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,49,63,0|fsdnfz,49,63,0|fsdng0,62,42,1|g36gnz,62,42,1|g36go0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,32,42,0|p5ezfz,32,42,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Grenada|,0,41,0|-u6m79w,32,42,0\",\"America/Guadeloupe|,0,41,0|-u6m79w,32,42,0\",\"America/Guatemala|,0,105,0|-qqqskk,45,62,0|219hzz,45,62,0|219i00,46,63,1|25xxvz,46,63,1|25xxw0,45,62,0|6zgbbz,45,62,0|6zgbc0,46,63,1|75tv7z,46,63,1|75tv80,45,62,0|b2q5zz,45,62,0|b2q600,46,63,1|bbd77z,46,63,1|bbd780,45,62,0|iyitzz,45,62,0|iyiu00,46,63,1|j6fxvz,46,63,1|j6fxw0,45,62,0\",\"America/Guayaquil|,0,106,0|-15r0ujs,75,107,0|-kcr84p,75,107,0|-kcr84o,56,63,0|byetvz,56,63,0|byetw0,42,42,1|c1yj3z,42,42,1|c1yj40,56,63,0\",\"America/Guyana|,0,108,0|-smcak8,76,109,0|2wsiez,76,109,0|2wsif0,39,44,0|ayjxnz,39,44,0|ayjxo0,42,42,0\",\"America/Halifax|,0,110,0|-z94k80,32,42,0|-s1x3k1,32,42,0|-s1x3k0,54,44,1|-rsiac1,54,44,1|-rsiac0,32,42,0|-qzp0o1,32,42,0|-qzp0o0,54,44,1|-qpm4s1,54,44,1|-qpm4s0,32,42,0|-pwt681,32,42,0|-pwt680,54,44,1|-pr1uc1,54,44,1|-pr1uc0,32,42,0|-pe6sw1,32,42,0|-pe6sw0,54,44,1|-p7wyc1,54,44,1|-p7wyc0,32,42,0|-ovpzk1,32,42,0|-ovpzk0,54,44,1|-op5101,54,44,1|-op5100,32,42,0|-ocmy81,32,42,0|-ocmy80,54,44,1|-o6eyc1,54,44,1|-o6eyc0,32,42,0|-ntwvk1,32,42,0|-ntwvk0,54,44,1|-nn0t01,54,44,1|-nn0t00,32,42,0|-nb6sw1,32,42,0|-nb6sw0,54,44,1|-n3kt01,54,44,1|-n3kt00,32,42,0|-mrqsw1,32,42,0|-mrqsw0,54,44,1|-mlkno1,54,44,1|-mlkno0,32,42,0|-m9qnk1,32,42,0|-m9qnk0,54,44,1|-m24no1,54,44,1|-m24no0,32,42,0|-lqank1,32,42,0|-lqank0,54,44,1|-lk6d01,54,44,1|-lk6d00,32,42,0|-l7kkw1,32,42,0|-l7kkw0,54,44,1|-l1pjo1,54,44,1|-l1pjo0,32,42,0|-koui81,32,42,0|-koui80,54,44,1|-kibec1,54,44,1|-kibec0,32,42,0|-k64fk1,32,42,0|-k64fk0,54,44,1|-jyvec1,54,44,1|-jyvec0,32,42,0|-jnrbk1,32,42,0|-jnrbk0,54,44,1|-jg5bo1,54,44,1|-jg5bo0,32,42,0|-j518w1,32,42,0|-j518w0,54,44,1|-ix2ac1,54,44,1|-ix2ac0,32,42,0|-il8a81,32,42,0|-il8a80,54,44,1|-if3zo1,54,44,1|-if3zo0,32,42,0|-i1sa81,32,42,0|-i1sa80,54,44,1|-hvm501,54,44,1|-hvm500,32,42,0|-hj0cw1,32,42,0|-hj0cw0,54,44,1|-hdlzo1,54,44,1|-hdlzo0,32,42,0|-h1rzk1,32,42,0|-h1rzk0,54,44,1|-gu5zo1,54,44,1|-gu5zo0,32,42,0|-gj1ww1,32,42,0|-gj1ww0,54,44,1|-gbfx01,54,44,1|-gbfx00,32,42,0|-fyvzk1,32,42,0|-fyvzk0,54,44,1|-fspuc1,54,44,1|-fspuc0,32,42,0|-fh8sw1,32,42,0|-fh8sw0,54,44,1|-f9mt01,54,44,1|-f9mt00,32,42,0|-eyiq81,32,42,0|-eyiq80,54,44,1|-eqwqc1,54,44,1|-eqwqc0,32,42,0|-ek27c1,32,42,0|-ek27c0,33,44,1|-cq2tg1,33,44,1|-cq2tg0,34,44,1|-cnp641,34,44,1|-cnp640,32,42,0|-ccw7c1,32,42,0|-ccw7c0,54,44,1|-c4z3g1,54,44,1|-c4z3g0,32,42,0|-bu64o1,32,42,0|-bu64o0,54,44,1|-bm90s1,54,44,1|-bm90s0,32,42,0|-bbg201,32,42,0|-bbg200,54,44,1|-b3iy41,54,44,1|-b3iy40,32,42,0|-aspzc1,32,42,0|-aspzc0,54,44,1|-aksvg1,54,44,1|-aksvg0,32,42,0|-9qwvc1,32,42,0|-9qwvc0,54,44,1|-9izrg1,54,44,1|-9izrg0,32,42,0|-986so1,32,42,0|-986so0,54,44,1|-909os1,54,44,1|-909os0,32,42,0|-8pgq01,32,42,0|-8pgq00,54,44,1|-8hjm41,54,44,1|-8hjm40,32,42,0|-86qnc1,32,42,0|-86qnc0,54,44,1|-7ytjg1,54,44,1|-7ytjg0,32,42,0|-74xjc1,32,42,0|-74xjc0,54,44,1|-6x0fg1,54,44,1|-6x0fg0,32,42,0|-6m7go1,32,42,0|-6m7go0,54,44,1|-6eacs1,54,44,1|-6eacs0,32,42,0|-63he01,32,42,0|-63he00,54,44,1|-5vka41,54,44,1|-5vka40,32,42,0|-5krbc1,32,42,0|-5krbc0,54,44,1|-5cu7g1,54,44,1|-5cu7g0,32,42,0|-4084o1,32,42,0|-4084o0,54,44,1|-3qv641,54,44,1|-3qv640,32,42,0|-3hi201,32,42,0|-3hi200,54,44,1|-3853g1,54,44,1|-3853g0,32,42,0|-2yrzc1,32,42,0|-2yrzc0,54,44,1|-2pf0s1,54,44,1|-2pf0s0,32,42,0|-2g1wo1,32,42,0|-2g1wo0,54,44,1|-26bzg1,54,44,1|-26bzg0,32,42,0|-1xbu01,32,42,0|-1xbu00,54,44,1|-1nlws1,54,44,1|-1nlws0,32,42,0|-1e8so1,32,42,0|-1e8so0,54,44,1|-14vu41,54,44,1|-14vu40,32,42,0|-viq01,32,42,0|-viq00,54,44,1|-m5rg1,54,44,1|-m5rg0,32,42,0|-csnc1,32,42,0|-csnc0,54,44,1|-3fos1,54,44,1|-3fos0,32,42,0|5xfbz,32,42,0|5xfc0,54,44,1|fadvz,54,44,1|fadw0,32,42,0|onhzz,32,42,0|oni00,54,44,1|ydf7z,54,44,1|ydf80,32,42,0|17qjbz,32,42,0|17qjc0,54,44,1|1h3hvz,54,44,1|1h3hw0,32,42,0|1qglzz,32,42,0|1qgm00,54,44,1|1ztkjz,54,44,1|1ztkk0,32,42,0|296onz,32,42,0|296oo0,54,44,1|2ijn7z,54,44,1|2ijn80,32,42,0|2rwrbz,32,42,0|2rwrc0,54,44,1|319pvz,54,44,1|319pw0,32,42,0|3amtzz,32,42,0|3amu00,54,44,1|3kcr7z,54,44,1|3kcr80,32,42,0|3tcwnz,32,42,0|3tcwo0,54,44,1|432tvz,54,44,1|432tw0,32,42,0|4cfxzz,32,42,0|4cfy00,54,44,1|4lswjz,54,44,1|4lswk0,32,42,0|4v60nz,32,42,0|4v60o0,54,44,1|54iz7z,54,44,1|54iz80,32,42,0|5dw3bz,32,42,0|5dw3c0,54,44,1|5n91vz,54,44,1|5n91w0,32,42,0|5wm5zz,32,42,0|5wm600,54,44,1|65z4jz,54,44,1|65z4k0,32,42,0|6fc8nz,32,42,0|6fc8o0,54,44,1|6p25vz,54,44,1|6p25w0,32,42,0|6y2bbz,32,42,0|6y2bc0,54,44,1|77s8jz,54,44,1|77s8k0,32,42,0|7h5cnz,32,42,0|7h5co0,54,44,1|7qib7z,54,44,1|7qib80,32,42,0|7zvfbz,32,42,0|7zvfc0,54,44,1|898dvz,54,44,1|898dw0,32,42,0|8ilhzz,32,42,0|8ili00,54,44,1|8rygjz,54,44,1|8rygk0,32,42,0|908onz,32,42,0|908oo0,54,44,1|9aoj7z,54,44,1|9aoj80,32,42,0|9iyrbz,32,42,0|9iyrc0,54,44,1|9trkjz,54,44,1|9trkk0,32,42,0|a1otzz,32,42,0|a1ou00,54,44,1|achn7z,54,44,1|achn80,32,42,0|akewnz,32,42,0|akewo0,54,44,1|av7pvz,54,44,1|av7pw0,32,42,0|b3hxzz,32,42,0|b3hy00,54,44,1|bdxsjz,54,44,1|bdxsk0,32,42,0|bm80nz,32,42,0|bm80o0,54,44,1|bwnv7z,54,44,1|bwnv80,32,42,0|c4y3bz,32,42,0|c4y3c0,54,44,1|cfqwjz,54,44,1|cfqwk0,32,42,0|cno5zz,32,42,0|cno600,54,44,1|cygz7z,54,44,1|cygz80,32,42,0|d6e8nz,32,42,0|d6e8o0,54,44,1|dh71vz,54,44,1|dh71w0,32,42,0|dph9zz,32,42,0|dpha00,54,44,1|dzx4jz,54,44,1|dzx4k0,32,42,0|e87cnz,32,42,0|e87co0,54,44,1|ein77z,54,44,1|ein780,32,42,0|eqxfbz,32,42,0|eqxfc0,54,44,1|f1d9vz,54,44,1|f1d9w0,32,42,0|f9nhzz,32,42,0|f9ni00,54,44,1|fkgb7z,54,44,1|fkgb80,32,42,0|fsdknz,32,42,0|fsdko0,54,44,1|g36dvz,54,44,1|g36dw0,32,42,0|gb3nbz,32,42,0|gb3nc0,54,44,1|glwgjz,54,44,1|glwgk0,32,42,0|gu6onz,32,42,0|gu6oo0,54,44,1|h4mj7z,54,44,1|h4mj80,32,42,0|hcwrbz,32,42,0|hcwrc0,54,44,1|hnclvz,54,44,1|hnclw0,32,42,0|hvmtzz,32,42,0|hvmu00,54,44,1|i6fn7z,54,44,1|i6fn80,32,42,0|iecwnz,32,42,0|iecwo0,54,44,1|ip5pvz,54,44,1|ip5pw0,32,42,0|ix2zbz,32,42,0|ix2zc0,54,44,1|j7vsjz,54,44,1|j7vsk0,32,42,0|jeq5zz,32,42,0|jeq600,54,44,1|jqytvz,54,44,1|jqytw0,32,42,0|jxg8nz,32,42,0|jxg8o0,54,44,1|k9owjz,54,44,1|k9owk0,32,42,0|kg6bbz,32,42,0|kg6bc0,54,44,1|ksez7z,54,44,1|ksez80,32,42,0|kz9cnz,32,42,0|kz9co0,54,44,1|lbi0jz,54,44,1|lbi0k0,32,42,0|lhzfbz,32,42,0|lhzfc0,54,44,1|lu837z,54,44,1|lu8380,32,42,0|m0phzz,32,42,0|m0pi00,54,44,1|mcy5vz,54,44,1|mcy5w0,32,42,0|mjfknz,32,42,0|mjfko0,54,44,1|mvo8jz,54,44,1|mvo8k0,32,42,0|n25nbz,32,42,0|n25nc0,54,44,1|neeb7z,54,44,1|neeb80,32,42,0|nkvpzz,32,42,0|nkvq00,54,44,1|nx4dvz,54,44,1|nx4dw0,32,42,0|o3yrbz,32,42,0|o3yrc0,54,44,1|og7f7z,54,44,1|og7f80,32,42,0|omotzz,32,42,0|omou00,54,44,1|oyxhvz,54,44,1|oyxhw0,32,42,0|p5ewnz,32,42,0|p5ewo0,54,44,1|phnkjz,54,44,1|phnkk0,32,42,0|po4zbz,32,42,0|po4zc0,54,44,1|q0dn7z,54,44,1|q0dn80,32,42,0|q6v1zz,32,42,0|q6v200,54,44,1|qj3pvz,54,44,1|qj3pw0,32,42,0|qpy3bz,32,42,0|qpy3c0,54,44,1|r26r7z,54,44,1|r26r80,32,42,0|r8o5zz,32,42,0|r8o600,54,44,1|rkwtvz,54,44,1|rkwtw0,32,42,0|rre8nz,32,42,0|rre8o0,54,44,1|s3mwjz,54,44,1|s3mwk0,32,42,0|sa4bbz,32,42,0|sa4bc0,54,44,1|smcz7z,54,44,1|smcz80,32,42,0|ssudzz,32,42,0|ssue00,54,44,1|t531vz,54,44,1|t531w0,32,42,0|tbkgnz,32,42,0|tbkgo0,54,44,1|tnt4jz,54,44,1|tnt4k0,32,42,0|tunhzz,32,42,0|tuni00,54,44,1|u6w5vz,54,44,1|u6w5w0,32,42,0|uddknz,32,42,0|uddko0,54,44,1|upm8jz,54,44,1|upm8k0,32,42,0|uw3nbz,32,42,0|uw3nc0,54,44,1|v8cb7z,54,44,1|v8cb80,32,42,0|vetpzz,32,42,0|vetq00,54,44,1|vr2dvz,54,44,1|vr2dw0,32,42,0|vxjsnz,32,42,0|vxjso0,54,44,1|w9sgjz,54,44,1|w9sgk0,32,42,0|wgmtzz,32,42,0|wgmu00,54,44,1|wsvhvz,54,44,1|wsvhw0,32,42,0|wzcwnz,32,42,0|wzcwo0,54,44,1|xblkjz,54,44,1|xblkk0,32,42,0|xi2zbz,32,42,0|xi2zc0,54,44,1|xubn7z,54,44,1|xubn80,32,42,0|y0t1zz,32,42,0|y0t200,54,44,1|yd1pvz,54,44,1|yd1pw0,32,42,0|yjj4nz,32,42,0|yjj4o0,54,44,1|yvrsjz,54,44,1|yvrsk0,32,42,0|z297bz,32,42,0|z297c0,54,44,1|zehv7z,54,44,1|zehv80,32,42,0\",\"America/Havana|,0,111,0|-15r0u2w,77,112,0|-n7762p,77,112,0|-n7762o,45,63,0|-louq41,45,63,0|-louq40,46,42,1|-likvk1,46,42,1|-likvk0,45,63,0|-ffsvg1,45,63,0|-ffsvg0,46,42,1|-fb4fk1,46,42,1|-fb4fk0,45,63,0|-ex2ss1,45,63,0|-ex2ss0,46,42,1|-es1e81,46,42,1|-es1e80,45,63,0|-edzrg1,45,63,0|-edzrg0,46,42,1|-e9bbk1,46,42,1|-e9bbk0,45,63,0|-cttjg1,45,63,0|-cttjg0,46,42,1|-cp53k1,46,42,1|-cp53k0,45,63,0|-cb3gs1,45,63,0|-cb3gs0,46,42,1|-c6f0w1,46,42,1|-c6f0w0,45,63,0|-2e5gs1,45,63,0|-2e5gs0,46,42,1|-27xgw1,46,42,1|-27xgw0,45,63,0|-1vj3g1,45,63,0|-1vj3g0,46,42,1|-1p1u81,46,42,1|-1p1u80,45,63,0|-1fdm41,45,63,0|-1fdm40,46,42,1|-17enk1,46,42,1|-17enk0,45,63,0|-w8q41,45,63,0|-w8q40,46,42,1|-ookw1,46,42,1|-ookw0,45,63,0|-csq41,45,63,0|-csq40,46,42,1|-3frk1,46,42,1|-3frk0,45,63,0|5xcjz,45,63,0|5xck0,46,42,1|fab3z,46,42,1|fab40,45,63,0|onf7z,45,63,0|onf80,46,42,1|ydcfz,46,42,1|ydcg0,45,63,0|17qgjz,45,63,0|17qgk0,46,42,1|1g0j3z,46,42,1|1g0j40,45,63,0|1qgj7z,45,63,0|1qgj80,46,42,1|1ysgfz,46,42,1|1ysgg0,45,63,0|296lvz,45,63,0|296lw0,46,42,1|2hkdrz,46,42,1|2hkds0,45,63,0|2rwojz,45,63,0|2rwok0,46,42,1|319n3z,46,42,1|319n40,45,63,0|3amr7z,45,63,0|3amr80,46,42,1|3kcofz,46,42,1|3kcog0,45,63,0|3tctvz,45,63,0|3tctw0,46,42,1|432r3z,46,42,1|432r40,45,63,0|4cstvz,45,63,0|4cstw0,46,42,1|4kpxrz,46,42,1|4kpxs0,45,63,0|4t05vz,45,63,0|4t05w0,46,42,1|53sz3z,46,42,1|53sz40,45,63,0|5bq8jz,45,63,0|5bq8k0,46,42,1|5mj1rz,46,42,1|5mj1s0,45,63,0|5xc0jz,45,63,0|5xc0k0,46,42,1|6594fz,46,42,1|6594g0,45,63,0|6g237z,45,63,0|6g2380,46,42,1|6nz73z,46,42,1|6nz740,45,63,0|6ys5vz,45,63,0|6ys5w0,46,42,1|76p9rz,46,42,1|76p9s0,45,63,0|7hi8jz,45,63,0|7hi8k0,46,42,1|7psb3z,46,42,1|7psb40,45,63,0|808b7z,45,63,0|808b80,46,42,1|88idrz,46,42,1|88ids0,45,63,0|8gfn7z,45,63,0|8gfn80,46,42,1|8r8gfz,46,42,1|8r8gg0,45,63,0|8z5pvz,45,63,0|8z5pw0,46,42,1|99yj3z,46,42,1|99yj40,45,63,0|9i8r7z,45,63,0|9i8r80,46,42,1|9solrz,46,42,1|9sols0,45,63,0|a0ytvz,45,63,0|a0ytw0,46,42,1|abeofz,46,42,1|abeog0,45,63,0|aketvz,45,63,0|aketw0,46,42,1|auhprz,46,42,1|auhps0,45,63,0|b3hv7z,45,63,0|b3hv80,46,42,1|bd7v7z,46,42,1|bd7v80,45,63,0|bm7xvz,45,63,0|bm7xw0,46,42,1|bvxxvz,46,42,1|bvxxw0,45,63,0|c4y0jz,45,63,0|c4y0k0,46,42,1|ceo0jz,46,42,1|ceo0k0,45,63,0|cno37z,45,63,0|cno380,46,42,1|cxe37z,46,42,1|cxe380,45,63,0|d6e5vz,45,63,0|d6e5w0,46,42,1|dg45vz,46,42,1|dg45w0,45,63,0|dph77z,45,63,0|dph780,46,42,1|dyu8jz,46,42,1|dyu8k0,45,63,0|e879vz,45,63,0|e879w0,46,42,1|ehx9vz,46,42,1|ehx9w0,45,63,0|eqkdvz,45,63,0|eqkdw0,46,42,1|f1d9vz,46,42,1|f1d9w0,45,63,0|f9agjz,45,63,0|f9agk0,46,42,1|fkgb7z,46,42,1|fkgb80,45,63,0|fsdhvz,45,63,0|fsdhw0,46,42,1|g36dvz,46,42,1|g36dw0,45,63,0|gb3kjz,45,63,0|gb3kk0,46,42,1|glwgjz,46,42,1|glwgk0,45,63,0|gu6lvz,45,63,0|gu6lw0,46,42,1|h4mj7z,46,42,1|h4mj80,45,63,0|hcwojz,45,63,0|hcwok0,46,42,1|hnclvz,46,42,1|hnclw0,45,63,0|hv9sjz,45,63,0|hv9sk0,46,42,1|j7vsjz,46,42,1|j7vsk0,45,63,0|jeq37z,45,63,0|jeq380,46,42,1|jqlv7z,46,42,1|jqlv80,45,63,0|jxt4jz,45,63,0|jxt4k0,46,42,1|k9bxvz,46,42,1|k9bxw0,45,63,0|kg68jz,45,63,0|kg68k0,46,42,1|ks20jz,46,42,1|ks20k0,45,63,0|kz99vz,45,63,0|kz99w0,46,42,1|lb51vz,46,42,1|lb51w0,45,63,0|licb7z,45,63,0|licb80,46,42,1|lul1vz,46,42,1|lul1w0,45,63,0|m1sb7z,45,63,0|m1sb80,46,42,1|mcy5vz,46,42,1|mcy5w0,45,63,0|mjfhvz,45,63,0|mjfhw0,46,42,1|mvo8jz,46,42,1|mvo8k0,45,63,0|n25kjz,45,63,0|n25kk0,46,42,1|neeb7z,46,42,1|neeb80,45,63,0|nkvn7z,45,63,0|nkvn80,46,42,1|nx4dvz,46,42,1|nx4dw0,45,63,0|o3yojz,45,63,0|o3yok0,46,42,1|og7f7z,46,42,1|og7f80,45,63,0|omor7z,45,63,0|omor80,46,42,1|oyxhvz,46,42,1|oyxhw0,45,63,0|p5etvz,45,63,0|p5etw0,46,42,1|phnkjz,46,42,1|phnkk0,45,63,0|po4wjz,45,63,0|po4wk0,46,42,1|q0dn7z,46,42,1|q0dn80,45,63,0|q6uz7z,45,63,0|q6uz80,46,42,1|qj3pvz,46,42,1|qj3pw0,45,63,0|qpy0jz,45,63,0|qpy0k0,46,42,1|r26r7z,46,42,1|r26r80,45,63,0|r8o37z,45,63,0|r8o380,46,42,1|rkwtvz,46,42,1|rkwtw0,45,63,0|rre5vz,45,63,0|rre5w0,46,42,1|s3mwjz,46,42,1|s3mwk0,45,63,0|sa48jz,45,63,0|sa48k0,46,42,1|smcz7z,46,42,1|smcz80,45,63,0|ssub7z,45,63,0|ssub80,46,42,1|t531vz,46,42,1|t531w0,45,63,0|tbkdvz,45,63,0|tbkdw0,46,42,1|tnt4jz,46,42,1|tnt4k0,45,63,0|tunf7z,45,63,0|tunf80,46,42,1|u6w5vz,46,42,1|u6w5w0,45,63,0|uddhvz,45,63,0|uddhw0,46,42,1|upm8jz,46,42,1|upm8k0,45,63,0|uw3kjz,45,63,0|uw3kk0,46,42,1|v8cb7z,46,42,1|v8cb80,45,63,0|vetn7z,45,63,0|vetn80,46,42,1|vr2dvz,46,42,1|vr2dw0,45,63,0|vxjpvz,45,63,0|vxjpw0,46,42,1|w9sgjz,46,42,1|w9sgk0,45,63,0|wgmr7z,45,63,0|wgmr80,46,42,1|wsvhvz,46,42,1|wsvhw0,45,63,0|wzctvz,45,63,0|wzctw0,46,42,1|xblkjz,46,42,1|xblkk0,45,63,0|xi2wjz,45,63,0|xi2wk0,46,42,1|xubn7z,46,42,1|xubn80,45,63,0|y0sz7z,45,63,0|y0sz80,46,42,1|yd1pvz,46,42,1|yd1pw0,45,63,0|yjj1vz,45,63,0|yjj1w0,46,42,1|yvrsjz,46,42,1|yvrsk0,45,63,0|z294jz,45,63,0|z294k0,46,42,1|zehv7z,46,42,1|zehv80,45,63,0\",\"America/Hermosillo|,0,113,0|-p1u4k0,50,66,0|-m7mko1,50,66,0|-m7mko0,45,62,0|-kf67c1,45,62,0|-kf67c0,50,66,0|-k6j3c1,50,66,0|-k6j3c0,45,62,0|-jypm01,45,62,0|-jypm00,50,66,0|-jpan81,50,66,0|-jpan80,45,62,0|-eg9601,45,62,0|-eg9600,50,66,0|-axv381,50,66,0|-axv380,51,40,0|m7z,51,40,0|m80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0\",\"America/Indiana/Indianapolis|,0,114,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-evzog1,45,62,0|-evzog0,46,63,1|-eqy9w1,46,63,1|-eqy9w0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-ccw1s1,45,62,0|-ccw1s0,46,63,1|-c4yxw1,46,63,1|-c4yxw0,45,62,0|-bu5z41,45,62,0|-bu5z40,46,63,1|-bm8v81,46,63,1|-bm8v80,45,62,0|-bbfwg1,45,62,0|-bbfwg0,46,63,1|-b3isk1,46,63,1|-b3isk0,45,62,0|-aspts1,45,62,0|-aspts0,46,63,1|-akspw1,46,63,1|-akspw0,45,62,0|-a9msg1,45,62,0|-a9msg0,46,63,1|-a22n81,46,63,1|-a22n80,45,62,0|-9qwps1,45,62,0|-9qwps0,46,63,1|-9izlw1,46,63,1|-9izlw0,45,62,0|-986n41,45,62,0|-986n40,46,63,1|-909j81,46,63,1|-909j80,45,62,0|-8pgkg1,45,62,0|-8pgkg0,46,63,1|-8hjgk1,46,63,1|-8hjgk0,45,62,0|-86qhs1,45,62,0|-86qhs0,46,63,1|-7ytdw1,46,63,1|-7ytdw0,45,62,0|-7o0f41,45,62,0|-7o0f40,49,63,0|-6ea781,49,63,0|-6ea780,45,62,0|-63h8g1,45,62,0|-63h8g0,49,63,0|-cskk1,49,63,0|-cskk0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Indiana/Knox|,0,115,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-bu5z41,45,62,0|-bu5z40,46,63,1|-bm8v81,46,63,1|-bm8v80,45,62,0|-bbfwg1,45,62,0|-bbfwg0,46,63,1|-b3isk1,46,63,1|-b3isk0,45,62,0|-aspts1,45,62,0|-aspts0,46,63,1|-akspw1,46,63,1|-akspw0,45,62,0|-a9msg1,45,62,0|-a9msg0,46,63,1|-a22n81,46,63,1|-a22n80,45,62,0|-9qwps1,45,62,0|-9qwps0,46,63,1|-9izlw1,46,63,1|-9izlw0,45,62,0|-986n41,45,62,0|-986n40,46,63,1|-909j81,46,63,1|-909j80,45,62,0|-8pgkg1,45,62,0|-8pgkg0,46,63,1|-8hjgk1,46,63,1|-8hjgk0,45,62,0|-86qhs1,45,62,0|-86qhs0,46,63,1|-7ytdw1,46,63,1|-7ytdw0,45,62,0|-7o0f41,45,62,0|-7o0f40,46,63,1|-7eahw1,46,63,1|-7eahw0,45,62,0|-74xds1,45,62,0|-74xds0,46,63,1|-6vkf81,46,63,1|-6vkf80,45,62,0|-6m7b41,45,62,0|-6m7b40,46,63,1|-6ea781,46,63,1|-6ea780,45,62,0|-63h8g1,45,62,0|-63h8g0,46,63,1|-5vk4k1,46,63,1|-5vk4k0,45,62,0|-5kr5s1,45,62,0|-5kr5s0,46,63,1|-5be781,46,63,1|-5be780,45,62,0|-521341,45,62,0|-521340,46,63,1|-4sb5w1,46,63,1|-4sb5w0,45,62,0|-4iy1s1,45,62,0|-4iy1s0,46,63,1|-49l381,46,63,1|-49l380,45,62,0|-407z41,45,62,0|-407z40,49,63,0|-384xw1,49,63,0|-384xw0,45,62,0|-1e8n41,45,62,0|-1e8n40,46,63,1|-14vok1,46,63,1|-14vok0,45,62,0|-vikg1,45,62,0|-vikg0,46,63,1|-m5lw1,46,63,1|-m5lw0,45,62,0|-cshs1,45,62,0|-cshs0,46,63,1|-3fj81,46,63,1|-3fj80,45,62,0|5xkvz,45,62,0|5xkw0,46,63,1|fajfz,46,63,1|fajg0,45,62,0|onnjz,45,62,0|onnk0,46,63,1|ydkrz,46,63,1|ydks0,45,62,0|17qovz,45,62,0|17qow0,46,63,1|1h3nfz,46,63,1|1h3ng0,45,62,0|1qgrjz,45,62,0|1qgrk0,46,63,1|1ztq3z,46,63,1|1ztq40,45,62,0|23ffjz,45,62,0|23ffk0,46,63,1|2ijsrz,46,63,1|2ijss0,45,62,0|2oo8vz,45,62,0|2oo8w0,46,63,1|319vfz,46,63,1|319vg0,45,62,0|3amzjz,45,62,0|3amzk0,46,63,1|3kcwrz,46,63,1|3kcws0,45,62,0|3td27z,45,62,0|3td280,46,63,1|432zfz,46,63,1|432zg0,45,62,0|4cg3jz,45,62,0|4cg3k0,46,63,1|4lt23z,46,63,1|4lt240,45,62,0|4v667z,45,62,0|4v6680,46,63,1|54j4rz,46,63,1|54j4s0,45,62,0|5dw8vz,45,62,0|5dw8w0,46,63,1|5n97fz,46,63,1|5n97g0,45,62,0|5wmbjz,45,62,0|5wmbk0,46,63,1|65za3z,46,63,1|65za40,45,62,0|6fce7z,45,62,0|6fce80,46,63,1|6p2bfz,46,63,1|6p2bg0,45,62,0|6y2gvz,45,62,0|6y2gw0,46,63,1|77se3z,46,63,1|77se40,45,62,0|7h5i7z,45,62,0|7h5i80,46,63,1|7qigrz,46,63,1|7qigs0,45,62,0|7zvkvz,45,62,0|7zvkw0,46,63,1|898jfz,46,63,1|898jg0,45,62,0|8ilnjz,45,62,0|8ilnk0,46,63,1|8rym3z,46,63,1|8rym40,45,62,0|908u7z,45,62,0|908u80,46,63,1|9aoorz,46,63,1|9aoos0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trq3z,46,63,1|9trq40,45,62,0|a1ozjz,45,62,0|a1ozk0,46,63,1|achsrz,46,63,1|achss0,45,62,0|akf27z,45,62,0|akf280,46,63,1|av7vfz,46,63,1|av7vg0,45,62,0|b3i3jz,45,62,0|b3i3k0,46,63,1|bdxy3z,46,63,1|bdxy40,49,63,0|ix323z,49,63,0|ix3240,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Indiana/Marengo|,0,116,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-9qwps1,45,62,0|-9qwps0,46,63,1|-9izlw1,46,63,1|-9izlw0,45,62,0|-86qhs1,45,62,0|-86qhs0,46,63,1|-7ytdw1,46,63,1|-7ytdw0,45,62,0|-7o0f41,45,62,0|-7o0f40,46,63,1|-7g3b81,46,63,1|-7g3b80,45,62,0|-74xds1,45,62,0|-74xds0,46,63,1|-6x09w1,46,63,1|-6x09w0,45,62,0|-6m7b41,45,62,0|-6m7b40,46,63,1|-6ea781,46,63,1|-6ea780,45,62,0|-63h8g1,45,62,0|-63h8g0,46,63,1|-5vk4k1,46,63,1|-5vk4k0,45,62,0|-5kr5s1,45,62,0|-5kr5s0,46,63,1|-5cu1w1,46,63,1|-5cu1w0,45,62,0|-521341,45,62,0|-521340,46,63,1|-4u3z81,46,63,1|-4u3z80,45,62,0|-4iy1s1,45,62,0|-4iy1s0,49,63,0|-cskk1,49,63,0|-cskk0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|onkrz,49,63,0|onks0,62,42,1|ydhzz,62,42,1|ydi00,49,63,0|17qm3z,49,63,0|17qm40,62,42,1|1h3knz,62,42,1|1h3ko0,49,63,0|1qgorz,49,63,0|1qgos0,62,42,1|1ztnbz,62,42,1|1ztnc0,49,63,0|23fcrz,49,63,0|23fcs0,46,63,1|2ijsrz,46,63,1|2ijss0,49,63,0|2oo63z,49,63,0|2oo640,62,42,1|319snz,62,42,1|319so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Indiana/Petersburg|,0,117,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-7nnm01,45,62,0|-7nnm00,46,63,1|-7g3b81,46,63,1|-7g3b80,45,62,0|-74xds1,45,62,0|-74xds0,46,63,1|-6x09w1,46,63,1|-6x09w0,45,62,0|-6m7b41,45,62,0|-6m7b40,46,63,1|-6ea781,46,63,1|-6ea780,45,62,0|-63h8g1,45,62,0|-63h8g0,46,63,1|-5vk4k1,46,63,1|-5vk4k0,45,62,0|-5kr5s1,45,62,0|-5kr5s0,46,63,1|-5cu1w1,46,63,1|-5cu1w0,45,62,0|-521341,45,62,0|-521340,46,63,1|-4u3z81,46,63,1|-4u3z80,45,62,0|-4iy1s1,45,62,0|-4iy1s0,46,63,1|-49l381,46,63,1|-49l380,45,62,0|-407z41,45,62,0|-407z40,46,63,1|-3qv0k1,46,63,1|-3qv0k0,45,62,0|-3hhwg1,45,62,0|-3hhwg0,46,63,1|-384xw1,46,63,1|-384xw0,45,62,0|-2yrts1,45,62,0|-2yrts0,46,63,1|-2pev81,46,63,1|-2pev80,45,62,0|-2g1r41,45,62,0|-2g1r40,49,63,0|-1nlr81,49,63,0|-1nlr80,45,62,0|-1e8n41,45,62,0|-1e8n40,46,63,1|-14vok1,46,63,1|-14vok0,45,62,0|-vikg1,45,62,0|-vikg0,46,63,1|-m5lw1,46,63,1|-m5lw0,45,62,0|-cshs1,45,62,0|-cshs0,46,63,1|-3fj81,46,63,1|-3fj80,45,62,0|5xkvz,45,62,0|5xkw0,46,63,1|fajfz,46,63,1|fajg0,45,62,0|onnjz,45,62,0|onnk0,46,63,1|ydkrz,46,63,1|ydks0,45,62,0|17qovz,45,62,0|17qow0,46,63,1|1h3nfz,46,63,1|1h3ng0,45,62,0|1qgrjz,45,62,0|1qgrk0,46,63,1|1ztq3z,46,63,1|1ztq40,45,62,0|23ffjz,45,62,0|23ffk0,46,63,1|2ijsrz,46,63,1|2ijss0,45,62,0|2oo8vz,45,62,0|2oo8w0,46,63,1|319vfz,46,63,1|319vg0,45,62,0|3amzjz,45,62,0|3amzk0,46,63,1|3kcwrz,46,63,1|3kcws0,45,62,0|3td27z,45,62,0|3td280,46,63,1|432zfz,46,63,1|432zg0,49,63,0|ix323z,49,63,0|ix3240,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Indiana/Tell_City|,0,118,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-7nnm01,45,62,0|-7nnm00,46,63,1|-7g3b81,46,63,1|-7g3b80,45,62,0|-74xds1,45,62,0|-74xds0,46,63,1|-6x09w1,46,63,1|-6x09w0,45,62,0|-6m7b41,45,62,0|-6m7b40,46,63,1|-6ea781,46,63,1|-6ea780,45,62,0|-63h8g1,45,62,0|-63h8g0,46,63,1|-5vk4k1,46,63,1|-5vk4k0,45,62,0|-5kr5s1,45,62,0|-5kr5s0,46,63,1|-5cu1w1,46,63,1|-5cu1w0,45,62,0|-521341,45,62,0|-521340,46,63,1|-4u3z81,46,63,1|-4u3z80,45,62,0|-4iy1s1,45,62,0|-4iy1s0,46,63,1|-49l381,46,63,1|-49l380,45,62,0|-407z41,45,62,0|-407z40,46,63,1|-3qv0k1,46,63,1|-3qv0k0,45,62,0|-3hhwg1,45,62,0|-3hhwg0,46,63,1|-384xw1,46,63,1|-384xw0,45,62,0|-2yrts1,45,62,0|-2yrts0,49,63,0|-14vok1,49,63,0|-14vok0,45,62,0|-vikg1,45,62,0|-vikg0,46,63,1|-m5lw1,46,63,1|-m5lw0,45,62,0|-cshs1,45,62,0|-cshs0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|ix323z,49,63,0|ix3240,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Indiana/Vevay|,0,119,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-86qhs1,45,62,0|-86qhs0,49,63,0|-cskk1,49,63,0|-cskk0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|onkrz,49,63,0|onks0,62,42,1|ydhzz,62,42,1|ydi00,49,63,0|17qm3z,49,63,0|17qm40,62,42,1|1h3knz,62,42,1|1h3ko0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Indiana/Vincennes|,0,120,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-ccw1s1,45,62,0|-ccw1s0,46,63,1|-c4yxw1,46,63,1|-c4yxw0,45,62,0|-8pgkg1,45,62,0|-8pgkg0,46,63,1|-8hjgk1,46,63,1|-8hjgk0,45,62,0|-86qhs1,45,62,0|-86qhs0,46,63,1|-7ytdw1,46,63,1|-7ytdw0,45,62,0|-7nnm01,45,62,0|-7nnm00,46,63,1|-7g3b81,46,63,1|-7g3b80,45,62,0|-74xds1,45,62,0|-74xds0,46,63,1|-6x09w1,46,63,1|-6x09w0,45,62,0|-6m7b41,45,62,0|-6m7b40,46,63,1|-6ea781,46,63,1|-6ea780,45,62,0|-63h8g1,45,62,0|-63h8g0,46,63,1|-5vk4k1,46,63,1|-5vk4k0,45,62,0|-5kr5s1,45,62,0|-5kr5s0,46,63,1|-5cu1w1,46,63,1|-5cu1w0,45,62,0|-521341,45,62,0|-521340,46,63,1|-4sb5w1,46,63,1|-4sb5w0,45,62,0|-4iy1s1,45,62,0|-4iy1s0,46,63,1|-4bdwk1,46,63,1|-4bdwk0,45,62,0|-407z41,45,62,0|-407z40,46,63,1|-3qv0k1,46,63,1|-3qv0k0,45,62,0|-3hhwg1,45,62,0|-3hhwg0,46,63,1|-384xw1,46,63,1|-384xw0,45,62,0|-2yrts1,45,62,0|-2yrts0,49,63,0|-cskk1,49,63,0|-cskk0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|ix323z,49,63,0|ix3240,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Indiana/Winamac|,0,121,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-ccw1s1,45,62,0|-ccw1s0,46,63,1|-c4yxw1,46,63,1|-c4yxw0,45,62,0|-bu5z41,45,62,0|-bu5z40,46,63,1|-bm8v81,46,63,1|-bm8v80,45,62,0|-bbfwg1,45,62,0|-bbfwg0,46,63,1|-b3isk1,46,63,1|-b3isk0,45,62,0|-aspts1,45,62,0|-aspts0,46,63,1|-akspw1,46,63,1|-akspw0,45,62,0|-a9msg1,45,62,0|-a9msg0,46,63,1|-a22n81,46,63,1|-a22n80,45,62,0|-9qwps1,45,62,0|-9qwps0,46,63,1|-9izlw1,46,63,1|-9izlw0,45,62,0|-986n41,45,62,0|-986n40,46,63,1|-909j81,46,63,1|-909j80,45,62,0|-8pgkg1,45,62,0|-8pgkg0,46,63,1|-8hjgk1,46,63,1|-8hjgk0,45,62,0|-86qhs1,45,62,0|-86qhs0,46,63,1|-7ytdw1,46,63,1|-7ytdw0,45,62,0|-7o0f41,45,62,0|-7o0f40,46,63,1|-7eahw1,46,63,1|-7eahw0,45,62,0|-74xds1,45,62,0|-74xds0,46,63,1|-6vkf81,46,63,1|-6vkf80,45,62,0|-6m7b41,45,62,0|-6m7b40,46,63,1|-6ea781,46,63,1|-6ea780,45,62,0|-63h8g1,45,62,0|-63h8g0,46,63,1|-5vk4k1,46,63,1|-5vk4k0,45,62,0|-5kr5s1,45,62,0|-5kr5s0,46,63,1|-5cu1w1,46,63,1|-5cu1w0,45,62,0|-521341,45,62,0|-521340,46,63,1|-4u3z81,46,63,1|-4u3z80,45,62,0|-4iy1s1,45,62,0|-4iy1s0,49,63,0|-cskk1,49,63,0|-cskk0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|ix323z,49,63,0|ix3240,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Inuvik|,60,1,0|-8ve5c0,51,40,0|-2g1r41,51,40,0|-2g1r40,78,62,1|-26br41,78,62,1|-26br40,51,40,0|4v6brz,51,40,0|4v6bs0,50,66,0|5dwbnz,50,66,0|5dwbo0,52,62,1|5n9a7z,52,62,1|5n9a80,50,66,0|5wmebz,50,66,0|5wmec0,52,62,1|65zcvz,52,62,1|65zcw0,50,66,0|6fcgzz,50,66,0|6fch00,52,62,1|6p2e7z,52,62,1|6p2e80,50,66,0|6y2jnz,50,66,0|6y2jo0,52,62,1|77sgvz,52,62,1|77sgw0,50,66,0|7h5kzz,50,66,0|7h5l00,52,62,1|7qijjz,52,62,1|7qijk0,50,66,0|7zvnnz,50,66,0|7zvno0,52,62,1|898m7z,52,62,1|898m80,50,66,0|8ilqbz,50,66,0|8ilqc0,52,62,1|8ryovz,52,62,1|8ryow0,50,66,0|908wzz,50,66,0|908x00,52,62,1|9aorjz,52,62,1|9aork0,50,66,0|9iyznz,50,66,0|9iyzo0,52,62,1|9trsvz,52,62,1|9trsw0,50,66,0|a1p2bz,50,66,0|a1p2c0,52,62,1|achvjz,52,62,1|achvk0,50,66,0|akf4zz,50,66,0|akf500,52,62,1|av7y7z,52,62,1|av7y80,50,66,0|b3i6bz,50,66,0|b3i6c0,52,62,1|bdy0vz,52,62,1|bdy0w0,50,66,0|bm88zz,50,66,0|bm8900,52,62,1|bwo3jz,52,62,1|bwo3k0,50,66,0|c4ybnz,50,66,0|c4ybo0,52,62,1|cfr4vz,52,62,1|cfr4w0,50,66,0|cnoebz,50,66,0|cnoec0,52,62,1|cyh7jz,52,62,1|cyh7k0,50,66,0|d6egzz,50,66,0|d6eh00,52,62,1|dh7a7z,52,62,1|dh7a80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gb3vnz,50,66,0|gb3vo0,52,62,1|glwovz,52,62,1|glwow0,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jeqebz,50,66,0|jeqec0,52,62,1|jqz27z,52,62,1|jqz280,50,66,0|jxggzz,50,66,0|jxgh00,52,62,1|k9p4vz,52,62,1|k9p4w0,50,66,0|kg6jnz,50,66,0|kg6jo0,52,62,1|ksf7jz,52,62,1|ksf7k0,50,66,0|kz9kzz,50,66,0|kz9l00,52,62,1|lbi8vz,52,62,1|lbi8w0,50,66,0|lhznnz,50,66,0|lhzno0,52,62,1|lu8bjz,52,62,1|lu8bk0,50,66,0|m0pqbz,50,66,0|m0pqc0,52,62,1|mcye7z,52,62,1|mcye80,50,66,0|mjfszz,50,66,0|mjft00,52,62,1|mvogvz,52,62,1|mvogw0,50,66,0|n25vnz,50,66,0|n25vo0,52,62,1|neejjz,52,62,1|neejk0,50,66,0|nkvybz,50,66,0|nkvyc0,52,62,1|nx4m7z,52,62,1|nx4m80,50,66,0|o3yznz,50,66,0|o3yzo0,52,62,1|og7njz,52,62,1|og7nk0,50,66,0|omp2bz,50,66,0|omp2c0,52,62,1|oyxq7z,52,62,1|oyxq80,50,66,0|p5f4zz,50,66,0|p5f500,52,62,1|phnsvz,52,62,1|phnsw0,50,66,0|po57nz,50,66,0|po57o0,52,62,1|q0dvjz,52,62,1|q0dvk0,50,66,0|q6vabz,50,66,0|q6vac0,52,62,1|qj3y7z,52,62,1|qj3y80,50,66,0|qpybnz,50,66,0|qpybo0,52,62,1|r26zjz,52,62,1|r26zk0,50,66,0|r8oebz,50,66,0|r8oec0,52,62,1|rkx27z,52,62,1|rkx280,50,66,0|rregzz,50,66,0|rreh00,52,62,1|s3n4vz,52,62,1|s3n4w0,50,66,0|sa4jnz,50,66,0|sa4jo0,52,62,1|smd7jz,52,62,1|smd7k0,50,66,0|ssumbz,50,66,0|ssumc0,52,62,1|t53a7z,52,62,1|t53a80,50,66,0|tbkozz,50,66,0|tbkp00,52,62,1|tntcvz,52,62,1|tntcw0,50,66,0|tunqbz,50,66,0|tunqc0,52,62,1|u6we7z,52,62,1|u6we80,50,66,0|uddszz,50,66,0|uddt00,52,62,1|upmgvz,52,62,1|upmgw0,50,66,0|uw3vnz,50,66,0|uw3vo0,52,62,1|v8cjjz,52,62,1|v8cjk0,50,66,0|vetybz,50,66,0|vetyc0,52,62,1|vr2m7z,52,62,1|vr2m80,50,66,0|vxk0zz,50,66,0|vxk100,52,62,1|w9sovz,52,62,1|w9sow0,50,66,0|wgn2bz,50,66,0|wgn2c0,52,62,1|wsvq7z,52,62,1|wsvq80,50,66,0|wzd4zz,50,66,0|wzd500,52,62,1|xblsvz,52,62,1|xblsw0,50,66,0|xi37nz,50,66,0|xi37o0,52,62,1|xubvjz,52,62,1|xubvk0,50,66,0|y0tabz,50,66,0|y0tac0,52,62,1|yd1y7z,52,62,1|yd1y80,50,66,0|yjjczz,50,66,0|yjjd00,52,62,1|yvs0vz,52,62,1|yvs0w0,50,66,0|z29fnz,50,66,0|z29fo0,52,62,1|zei3jz,52,62,1|zei3k0,50,66,0\",\"America/Iqaluit|,60,1,0|-eb6ao0,70,42,1|-cq2tg1,70,42,1|-cq2tg0,71,42,1|-cnp3c1,71,42,1|-cnp3c0,49,63,0|-2g1zg1,49,63,0|-2g1zg0,79,44,1|-26bzg1,79,44,1|-26bzg0,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0|7h5ffz,49,63,0|7h5fg0,62,42,1|7qidzz,62,42,1|7qie00,49,63,0|7zvi3z,49,63,0|7zvi40,62,42,1|898gnz,62,42,1|898go0,49,63,0|8ilkrz,49,63,0|8ilks0,62,42,1|8ryjbz,62,42,1|8ryjc0,49,63,0|908rfz,49,63,0|908rg0,62,42,1|9aolzz,62,42,1|9aom00,49,63,0|9iyu3z,49,63,0|9iyu40,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1owrz,49,63,0|a1ows0,62,42,1|achpzz,62,42,1|achq00,49,63,0|akezfz,49,63,0|akezg0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3i0rz,49,63,0|b3i0s0,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm83fz,49,63,0|bm83g0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y63z,49,63,0|c4y640,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno8rz,49,63,0|cno8s0,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6ebfz,49,63,0|d6ebg0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Jamaica|,0,104,0|-15r0v42,74,104,0|-u85og3,74,104,0|-u85og2,49,63,0|23fcrz,49,63,0|23fcs0,62,42,1|2ijpzz,62,42,1|2ijq00,49,63,0|2oo63z,49,63,0|2oo640,62,42,1|319snz,62,42,1|319so0,49,63,0|3amwrz,49,63,0|3amws0,62,42,1|3kctzz,62,42,1|3kcu00,49,63,0|3tczfz,49,63,0|3tczg0,62,42,1|432wnz,62,42,1|432wo0,49,63,0|4cg0rz,49,63,0|4cg0s0,62,42,1|4lszbz,62,42,1|4lszc0,49,63,0|4v63fz,49,63,0|4v63g0,62,42,1|54j1zz,62,42,1|54j200,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0\",\"America/Juneau|,0,122,0|-1hc7qjz,0,123,0|-1078wfw,0,123,0|-1078wfv,51,40,0|-ek1w81,51,40,0|-ek1w80,68,66,1|-cq2tg1,68,66,1|-cq2tg0,69,66,1|-cnov01,69,66,1|-cnov00,51,40,0|-csc81,51,40,0|-csc80,57,66,1|-3fdo1,57,66,1|-3fdo0,51,40,0|5xqfz,51,40,0|5xqg0,57,66,1|faozz,57,66,1|fap00,51,40,0|ont3z,51,40,0|ont40,57,66,1|ydqbz,57,66,1|ydqc0,51,40,0|17qufz,51,40,0|17qug0,57,66,1|1h3szz,57,66,1|1h3t00,51,40,0|1qgx3z,51,40,0|1qgx40,57,66,1|1ztvnz,57,66,1|1ztvo0,51,40,0|23fl3z,51,40,0|23fl40,57,66,1|2ijybz,57,66,1|2ijyc0,51,40,0|2ooefz,51,40,0|2ooeg0,57,66,1|31a0zz,57,66,1|31a100,51,40,0|3an53z,51,40,0|3an540,57,66,1|3kd2bz,57,66,1|3kd2c0,51,40,0|3td7rz,51,40,0|3td7s0,57,66,1|4334zz,57,66,1|433500,51,40,0|4cg93z,51,40,0|4cg940,57,66,1|4lt7nz,57,66,1|4lt7o0,51,40,0|4v6brz,51,40,0|4v6bs0,57,66,1|54jabz,57,66,1|54jac0,51,40,0|5dwefz,51,40,0|5dweg0,64,40,1|5n9frz,64,40,1|5n9fs0,51,40,0|5wmh3z,51,40,0|5wmh40,57,66,1|65zfnz,57,66,1|65zfo0,51,40,0|6fcjrz,51,40,0|6fcjs0,57,66,1|6p2gzz,57,66,1|6p2h00,51,40,0|6y2mfz,51,40,0|6y2mg0,57,66,1|77sjnz,57,66,1|77sjo0,36,37,0|79dybz,36,37,0|79dyc0,37,37,0|7h5qjz,37,37,0|7h5qk0,38,40,1|7qip3z,38,40,1|7qip40,37,37,0|7zvt7z,37,37,0|7zvt80,38,40,1|898rrz,38,40,1|898rs0,37,37,0|8ilvvz,37,37,0|8ilvw0,38,40,1|8ryufz,38,40,1|8ryug0,37,37,0|9092jz,37,37,0|9092k0,38,40,1|9aox3z,38,40,1|9aox40,37,37,0|9iz57z,37,37,0|9iz580,38,40,1|9tryfz,38,40,1|9tryg0,37,37,0|a1p7vz,37,37,0|a1p7w0,38,40,1|aci13z,38,40,1|aci140,37,37,0|akfajz,37,37,0|akfak0,38,40,1|av83rz,38,40,1|av83s0,37,37,0|b3ibvz,37,37,0|b3ibw0,38,40,1|bdy6fz,38,40,1|bdy6g0,37,37,0|bm8ejz,37,37,0|bm8ek0,38,40,1|bwo93z,38,40,1|bwo940,37,37,0|c4yh7z,37,37,0|c4yh80,38,40,1|cfrafz,38,40,1|cfrag0,37,37,0|cnojvz,37,37,0|cnojw0,38,40,1|cyhd3z,38,40,1|cyhd40,37,37,0|d6emjz,37,37,0|d6emk0,38,40,1|dh7frz,38,40,1|dh7fs0,37,37,0|dphnvz,37,37,0|dphnw0,38,40,1|dzxifz,38,40,1|dzxig0,37,37,0|e87qjz,37,37,0|e87qk0,38,40,1|einl3z,38,40,1|einl40,37,37,0|eqxt7z,37,37,0|eqxt80,38,40,1|f1dnrz,38,40,1|f1dns0,37,37,0|f9nvvz,37,37,0|f9nvw0,38,40,1|fkgp3z,38,40,1|fkgp40,37,37,0|fsdyjz,37,37,0|fsdyk0,38,40,1|g36rrz,38,40,1|g36rs0,37,37,0|gb417z,37,37,0|gb4180,38,40,1|glwufz,38,40,1|glwug0,37,37,0|gu72jz,37,37,0|gu72k0,38,40,1|h4mx3z,38,40,1|h4mx40,37,37,0|hcx57z,37,37,0|hcx580,38,40,1|hnczrz,38,40,1|hnczs0,37,37,0|hvn7vz,37,37,0|hvn7w0,38,40,1|i6g13z,38,40,1|i6g140,37,37,0|iedajz,37,37,0|iedak0,38,40,1|ip63rz,38,40,1|ip63s0,37,37,0|ix3d7z,37,37,0|ix3d80,38,40,1|j7w6fz,38,40,1|j7w6g0,37,37,0|jeqjvz,37,37,0|jeqjw0,38,40,1|jqz7rz,38,40,1|jqz7s0,37,37,0|jxgmjz,37,37,0|jxgmk0,38,40,1|k9pafz,38,40,1|k9pag0,37,37,0|kg6p7z,37,37,0|kg6p80,38,40,1|ksfd3z,38,40,1|ksfd40,37,37,0|kz9qjz,37,37,0|kz9qk0,38,40,1|lbiefz,38,40,1|lbieg0,37,37,0|lhzt7z,37,37,0|lhzt80,38,40,1|lu8h3z,38,40,1|lu8h40,37,37,0|m0pvvz,37,37,0|m0pvw0,38,40,1|mcyjrz,38,40,1|mcyjs0,37,37,0|mjfyjz,37,37,0|mjfyk0,38,40,1|mvomfz,38,40,1|mvomg0,37,37,0|n2617z,37,37,0|n26180,38,40,1|neep3z,38,40,1|neep40,37,37,0|nkw3vz,37,37,0|nkw3w0,38,40,1|nx4rrz,38,40,1|nx4rs0,37,37,0|o3z57z,37,37,0|o3z580,38,40,1|og7t3z,38,40,1|og7t40,37,37,0|omp7vz,37,37,0|omp7w0,38,40,1|oyxvrz,38,40,1|oyxvs0,37,37,0|p5fajz,37,37,0|p5fak0,38,40,1|phnyfz,38,40,1|phnyg0,37,37,0|po5d7z,37,37,0|po5d80,38,40,1|q0e13z,38,40,1|q0e140,37,37,0|q6vfvz,37,37,0|q6vfw0,38,40,1|qj43rz,38,40,1|qj43s0,37,37,0|qpyh7z,37,37,0|qpyh80,38,40,1|r2753z,38,40,1|r27540,37,37,0|r8ojvz,37,37,0|r8ojw0,38,40,1|rkx7rz,38,40,1|rkx7s0,37,37,0|rremjz,37,37,0|rremk0,38,40,1|s3nafz,38,40,1|s3nag0,37,37,0|sa4p7z,37,37,0|sa4p80,38,40,1|smdd3z,38,40,1|smdd40,37,37,0|ssurvz,37,37,0|ssurw0,38,40,1|t53frz,38,40,1|t53fs0,37,37,0|tbkujz,37,37,0|tbkuk0,38,40,1|tntifz,38,40,1|tntig0,37,37,0|tunvvz,37,37,0|tunvw0,38,40,1|u6wjrz,38,40,1|u6wjs0,37,37,0|uddyjz,37,37,0|uddyk0,38,40,1|upmmfz,38,40,1|upmmg0,37,37,0|uw417z,37,37,0|uw4180,38,40,1|v8cp3z,38,40,1|v8cp40,37,37,0|veu3vz,37,37,0|veu3w0,38,40,1|vr2rrz,38,40,1|vr2rs0,37,37,0|vxk6jz,37,37,0|vxk6k0,38,40,1|w9sufz,38,40,1|w9sug0,37,37,0|wgn7vz,37,37,0|wgn7w0,38,40,1|wsvvrz,38,40,1|wsvvs0,37,37,0|wzdajz,37,37,0|wzdak0,38,40,1|xblyfz,38,40,1|xblyg0,37,37,0|xi3d7z,37,37,0|xi3d80,38,40,1|xuc13z,38,40,1|xuc140,37,37,0|y0tfvz,37,37,0|y0tfw0,38,40,1|yd23rz,38,40,1|yd23s0,37,37,0|yjjijz,37,37,0|yjjik0,38,40,1|yvs6fz,38,40,1|yvs6g0,37,37,0|z29l7z,37,37,0|z29l80,38,40,1|zei93z,38,40,1|zei940,37,37,0\",\"America/Kentucky/Louisville|,0,124,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-pefr41,45,62,0|-pefr40,46,63,1|-p841w1,46,63,1|-p841w0,45,62,0|-eyvds1,45,62,0|-eyvds0,46,63,1|-eqy9w1,46,63,1|-eqy9w0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-ccw7ad,45,62,0|-ccw7ac,46,63,1|-cb3b81,46,63,1|-cb3b80,45,62,0|-a9msg1,45,62,0|-a9msg0,46,63,1|-a22n81,46,63,1|-a22n80,45,62,0|-9qwps1,45,62,0|-9qwps0,46,63,1|-9izlw1,46,63,1|-9izlw0,45,62,0|-986n41,45,62,0|-986n40,46,63,1|-909j81,46,63,1|-909j80,45,62,0|-8pgkg1,45,62,0|-8pgkg0,46,63,1|-8hjgk1,46,63,1|-8hjgk0,45,62,0|-86qhs1,45,62,0|-86qhs0,46,63,1|-7ytdw1,46,63,1|-7ytdw0,45,62,0|-7o0f41,45,62,0|-7o0f40,46,63,1|-7g3b81,46,63,1|-7g3b80,45,62,0|-74xds1,45,62,0|-74xds0,46,63,1|-6vkf81,46,63,1|-6vkf80,45,62,0|-6m7b41,45,62,0|-6m7b40,46,63,1|-6cuck1,46,63,1|-6cuck0,45,62,0|-63h8g1,45,62,0|-63h8g0,46,63,1|-5u49w1,46,63,1|-5u49w0,45,62,0|-5kr5s1,45,62,0|-5kr5s0,46,63,1|-5be781,46,63,1|-5be780,45,62,0|-521341,45,62,0|-521340,46,63,1|-4sb5w1,46,63,1|-4sb5w0,45,62,0|-4iy1s1,45,62,0|-4iy1s0,46,63,1|-4emkk1,46,63,1|-4emkk0,49,63,0|-vin81,49,63,0|-vin80,62,42,1|-m5oo1,62,42,1|-m5oo0,49,63,0|-cskk1,49,63,0|-cskk0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|onkrz,49,63,0|onks0,62,42,1|ydhzz,62,42,1|ydi00,49,63,0|17qm3z,49,63,0|17qm40,62,42,1|1h3knz,62,42,1|1h3ko0,49,63,0|1qgorz,49,63,0|1qgos0,62,42,1|1ztnbz,62,42,1|1ztnc0,49,63,0|23fcrz,49,63,0|23fcs0,46,63,1|2ijsrz,46,63,1|2ijss0,49,63,0|2oo63z,49,63,0|2oo640,62,42,1|319snz,62,42,1|319so0,49,63,0|3amwrz,49,63,0|3amws0,62,42,1|3kctzz,62,42,1|3kcu00,49,63,0|3tczfz,49,63,0|3tczg0,62,42,1|432wnz,62,42,1|432wo0,49,63,0|4cg0rz,49,63,0|4cg0s0,62,42,1|4lszbz,62,42,1|4lszc0,49,63,0|4v63fz,49,63,0|4v63g0,62,42,1|54j1zz,62,42,1|54j200,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0|7h5ffz,49,63,0|7h5fg0,62,42,1|7qidzz,62,42,1|7qie00,49,63,0|7zvi3z,49,63,0|7zvi40,62,42,1|898gnz,62,42,1|898go0,49,63,0|8ilkrz,49,63,0|8ilks0,62,42,1|8ryjbz,62,42,1|8ryjc0,49,63,0|908rfz,49,63,0|908rg0,62,42,1|9aolzz,62,42,1|9aom00,49,63,0|9iyu3z,49,63,0|9iyu40,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1owrz,49,63,0|a1ows0,62,42,1|achpzz,62,42,1|achq00,49,63,0|akezfz,49,63,0|akezg0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3i0rz,49,63,0|b3i0s0,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm83fz,49,63,0|bm83g0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y63z,49,63,0|c4y640,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno8rz,49,63,0|cno8s0,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6ebfz,49,63,0|d6ebg0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,49,63,0|fsdnfz,49,63,0|fsdng0,62,42,1|g36gnz,62,42,1|g36go0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Kentucky/Monticello|,0,125,0|-18y0m00,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-vikg1,45,62,0|-vikg0,46,63,1|-m5lw1,46,63,1|-m5lw0,45,62,0|-cshs1,45,62,0|-cshs0,46,63,1|-3fj81,46,63,1|-3fj80,45,62,0|5xkvz,45,62,0|5xkw0,46,63,1|fajfz,46,63,1|fajg0,45,62,0|onnjz,45,62,0|onnk0,46,63,1|ydkrz,46,63,1|ydks0,45,62,0|17qovz,45,62,0|17qow0,46,63,1|1h3nfz,46,63,1|1h3ng0,45,62,0|1qgrjz,45,62,0|1qgrk0,46,63,1|1ztq3z,46,63,1|1ztq40,45,62,0|23ffjz,45,62,0|23ffk0,46,63,1|2ijsrz,46,63,1|2ijss0,45,62,0|2oo8vz,45,62,0|2oo8w0,46,63,1|319vfz,46,63,1|319vg0,45,62,0|3amzjz,45,62,0|3amzk0,46,63,1|3kcwrz,46,63,1|3kcws0,45,62,0|3td27z,45,62,0|3td280,46,63,1|432zfz,46,63,1|432zg0,45,62,0|4cg3jz,45,62,0|4cg3k0,46,63,1|4lt23z,46,63,1|4lt240,45,62,0|4v667z,45,62,0|4v6680,46,63,1|54j4rz,46,63,1|54j4s0,45,62,0|5dw8vz,45,62,0|5dw8w0,46,63,1|5n97fz,46,63,1|5n97g0,45,62,0|5wmbjz,45,62,0|5wmbk0,46,63,1|65za3z,46,63,1|65za40,45,62,0|6fce7z,45,62,0|6fce80,46,63,1|6p2bfz,46,63,1|6p2bg0,45,62,0|6y2gvz,45,62,0|6y2gw0,46,63,1|77se3z,46,63,1|77se40,45,62,0|7h5i7z,45,62,0|7h5i80,46,63,1|7qigrz,46,63,1|7qigs0,45,62,0|7zvkvz,45,62,0|7zvkw0,46,63,1|898jfz,46,63,1|898jg0,45,62,0|8ilnjz,45,62,0|8ilnk0,46,63,1|8rym3z,46,63,1|8rym40,45,62,0|908u7z,45,62,0|908u80,46,63,1|9aoorz,46,63,1|9aoos0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trq3z,46,63,1|9trq40,45,62,0|a1ozjz,45,62,0|a1ozk0,46,63,1|achsrz,46,63,1|achss0,45,62,0|akf27z,45,62,0|akf280,46,63,1|av7vfz,46,63,1|av7vg0,45,62,0|b3i3jz,45,62,0|b3i3k0,46,63,1|bdxy3z,46,63,1|bdxy40,45,62,0|bm867z,45,62,0|bm8680,46,63,1|bwo0rz,46,63,1|bwo0s0,45,62,0|c4y8vz,45,62,0|c4y8w0,46,63,1|cfr23z,46,63,1|cfr240,45,62,0|cnobjz,45,62,0|cnobk0,46,63,1|cyh4rz,46,63,1|cyh4s0,45,62,0|d6ee7z,45,62,0|d6ee80,46,63,1|dh77fz,46,63,1|dh77g0,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Kralendijk|,0,58,0|-u7lckd,43,59,0|-2lx4u1,43,59,0|-2lx4u0,32,42,0\",\"America/La_Paz|,0,126,0|-15r0wpo,41,126,0|-jxzspp,41,126,0|-jxzspo,27,127,1|-jpva5p,27,127,1|-jpva5o,42,42,0\",\"America/Lima|,0,128,0|-15r0v2c,0,129,0|-w25lpp,0,129,0|-w25lpo,56,63,0|-gp8241,56,63,0|-gp8240,42,42,1|-gklgw1,42,42,1|-gklgw0,56,63,0|-gbhm41,56,63,0|-gbhm40,42,42,1|-g24nk1,42,42,1|-g24nk0,56,63,0|-fsrjg1,56,63,0|-fsrjg0,42,42,1|-fjekw1,42,42,1|-fjekw0,56,63,0|8cmlvz,56,63,0|8cmlw0,42,42,1|8h973z,42,42,1|8h9740,56,63,0|8vej7z,56,63,0|8vej80,42,42,1|9014fz,42,42,1|9014g0,56,63,0|afs5vz,56,63,0|afs5w0,42,42,1|aker3z,42,42,1|aker40,56,63,0|cixpvz,56,63,0|cixpw0,42,42,1|cnkb3z,42,42,1|cnkb40,56,63,0\",\"America/Los_Angeles|,0,130,0|-18y0gg0,51,40,0|-r0emw1,51,40,0|-r0emw0,57,66,1|-qplto1,57,66,1|-qplto0,51,40,0|-qhok81,51,40,0|-qhok80,57,66,1|-q6vr01,57,66,1|-q6vr00,51,40,0|-ek1w81,51,40,0|-ek1w80,68,66,1|-cq2tg1,68,66,1|-cq2tg0,69,66,1|-cnov01,69,66,1|-cnov00,51,40,0|-bdliud,51,40,0|-bdliuc,57,66,1|-ayj0c1,57,66,1|-ayj0c0,51,40,0|-a9mpo1,51,40,0|-a9mpo0,57,66,1|-a22ho1,57,66,1|-a22ho0,51,40,0|-9qwn01,51,40,0|-9qwn00,57,66,1|-9izgc1,57,66,1|-9izgc0,51,40,0|-986kc1,51,40,0|-986kc0,57,66,1|-909do1,57,66,1|-909do0,51,40,0|-8pgho1,51,40,0|-8pgho0,57,66,1|-8hjb01,57,66,1|-8hjb00,51,40,0|-86qf01,51,40,0|-86qf00,57,66,1|-7yt8c1,57,66,1|-7yt8c0,51,40,0|-7o0cc1,51,40,0|-7o0cc0,57,66,1|-7g35o1,57,66,1|-7g35o0,51,40,0|-74xb01,51,40,0|-74xb00,57,66,1|-6x04c1,57,66,1|-6x04c0,51,40,0|-6m78c1,51,40,0|-6m78c0,57,66,1|-6ea1o1,57,66,1|-6ea1o0,51,40,0|-63h5o1,51,40,0|-63h5o0,57,66,1|-5vjz01,57,66,1|-5vjz00,51,40,0|-5kr301,51,40,0|-5kr300,57,66,1|-5ctwc1,57,66,1|-5ctwc0,51,40,0|-5210c1,51,40,0|-5210c0,57,66,1|-4u3to1,57,66,1|-4u3to0,51,40,0|-4ixz01,51,40,0|-4ixz00,57,66,1|-4bdr01,57,66,1|-4bdr00,51,40,0|-407wc1,51,40,0|-407wc0,57,66,1|-3quv01,57,66,1|-3quv00,51,40,0|-3hhto1,51,40,0|-3hhto0,57,66,1|-384sc1,57,66,1|-384sc0,51,40,0|-2yrr01,51,40,0|-2yrr00,57,66,1|-2pepo1,57,66,1|-2pepo0,51,40,0|-2g1oc1,51,40,0|-2g1oc0,57,66,1|-26boc1,57,66,1|-26boc0,51,40,0|-1xblo1,51,40,0|-1xblo0,57,66,1|-1nllo1,57,66,1|-1nllo0,51,40,0|-1e8hk1,51,40,0|-1e8hk0,57,66,1|-14vj01,57,66,1|-14vj00,51,40,0|-view1,51,40,0|-view0,57,66,1|-m5gc1,57,66,1|-m5gc0,51,40,0|-csc81,51,40,0|-csc80,57,66,1|-3fdo1,57,66,1|-3fdo0,51,40,0|5xqfz,51,40,0|5xqg0,57,66,1|faozz,57,66,1|fap00,51,40,0|ont3z,51,40,0|ont40,57,66,1|ydqbz,57,66,1|ydqc0,51,40,0|17qufz,51,40,0|17qug0,57,66,1|1h3szz,57,66,1|1h3t00,51,40,0|1qgx3z,51,40,0|1qgx40,57,66,1|1ztvnz,57,66,1|1ztvo0,51,40,0|23fl3z,51,40,0|23fl40,57,66,1|2ijybz,57,66,1|2ijyc0,51,40,0|2ooefz,51,40,0|2ooeg0,57,66,1|31a0zz,57,66,1|31a100,51,40,0|3an53z,51,40,0|3an540,57,66,1|3kd2bz,57,66,1|3kd2c0,51,40,0|3td7rz,51,40,0|3td7s0,57,66,1|4334zz,57,66,1|433500,51,40,0|4cg93z,51,40,0|4cg940,57,66,1|4lt7nz,57,66,1|4lt7o0,51,40,0|4v6brz,51,40,0|4v6bs0,57,66,1|54jabz,57,66,1|54jac0,51,40,0|5dwefz,51,40,0|5dweg0,57,66,1|5n9czz,57,66,1|5n9d00,51,40,0|5wmh3z,51,40,0|5wmh40,57,66,1|65zfnz,57,66,1|65zfo0,51,40,0|6fcjrz,51,40,0|6fcjs0,57,66,1|6p2gzz,57,66,1|6p2h00,51,40,0|6y2mfz,51,40,0|6y2mg0,57,66,1|77sjnz,57,66,1|77sjo0,51,40,0|7h5nrz,51,40,0|7h5ns0,57,66,1|7qimbz,57,66,1|7qimc0,51,40,0|7zvqfz,51,40,0|7zvqg0,57,66,1|898ozz,57,66,1|898p00,51,40,0|8ilt3z,51,40,0|8ilt40,57,66,1|8ryrnz,57,66,1|8ryro0,51,40,0|908zrz,51,40,0|908zs0,57,66,1|9aoubz,57,66,1|9aouc0,51,40,0|9iz2fz,51,40,0|9iz2g0,57,66,1|9trvnz,57,66,1|9trvo0,51,40,0|a1p53z,51,40,0|a1p540,57,66,1|achybz,57,66,1|achyc0,51,40,0|akf7rz,51,40,0|akf7s0,57,66,1|av80zz,57,66,1|av8100,51,40,0|b3i93z,51,40,0|b3i940,57,66,1|bdy3nz,57,66,1|bdy3o0,51,40,0|bm8brz,51,40,0|bm8bs0,57,66,1|bwo6bz,57,66,1|bwo6c0,51,40,0|c4yefz,51,40,0|c4yeg0,57,66,1|cfr7nz,57,66,1|cfr7o0,51,40,0|cnoh3z,51,40,0|cnoh40,57,66,1|cyhabz,57,66,1|cyhac0,51,40,0|d6ejrz,51,40,0|d6ejs0,57,66,1|dh7czz,57,66,1|dh7d00,51,40,0|dphl3z,51,40,0|dphl40,57,66,1|dzxfnz,57,66,1|dzxfo0,51,40,0|e87nrz,51,40,0|e87ns0,57,66,1|einibz,57,66,1|einic0,51,40,0|eqxqfz,51,40,0|eqxqg0,57,66,1|f1dkzz,57,66,1|f1dl00,51,40,0|f9nt3z,51,40,0|f9nt40,57,66,1|fkgmbz,57,66,1|fkgmc0,51,40,0|fsdvrz,51,40,0|fsdvs0,57,66,1|g36ozz,57,66,1|g36p00,51,40,0|gb3yfz,51,40,0|gb3yg0,57,66,1|glwrnz,57,66,1|glwro0,51,40,0|gu6zrz,51,40,0|gu6zs0,57,66,1|h4mubz,57,66,1|h4muc0,51,40,0|hcx2fz,51,40,0|hcx2g0,57,66,1|hncwzz,57,66,1|hncx00,51,40,0|hvn53z,51,40,0|hvn540,57,66,1|i6fybz,57,66,1|i6fyc0,51,40,0|ied7rz,51,40,0|ied7s0,57,66,1|ip60zz,57,66,1|ip6100,51,40,0|ix3afz,51,40,0|ix3ag0,57,66,1|j7w3nz,57,66,1|j7w3o0,51,40,0|jeqh3z,51,40,0|jeqh40,57,66,1|jqz4zz,57,66,1|jqz500,51,40,0|jxgjrz,51,40,0|jxgjs0,57,66,1|k9p7nz,57,66,1|k9p7o0,51,40,0|kg6mfz,51,40,0|kg6mg0,57,66,1|ksfabz,57,66,1|ksfac0,51,40,0|kz9nrz,51,40,0|kz9ns0,57,66,1|lbibnz,57,66,1|lbibo0,51,40,0|lhzqfz,51,40,0|lhzqg0,57,66,1|lu8ebz,57,66,1|lu8ec0,51,40,0|m0pt3z,51,40,0|m0pt40,57,66,1|mcygzz,57,66,1|mcyh00,51,40,0|mjfvrz,51,40,0|mjfvs0,57,66,1|mvojnz,57,66,1|mvojo0,51,40,0|n25yfz,51,40,0|n25yg0,57,66,1|neembz,57,66,1|neemc0,51,40,0|nkw13z,51,40,0|nkw140,57,66,1|nx4ozz,57,66,1|nx4p00,51,40,0|o3z2fz,51,40,0|o3z2g0,57,66,1|og7qbz,57,66,1|og7qc0,51,40,0|omp53z,51,40,0|omp540,57,66,1|oyxszz,57,66,1|oyxt00,51,40,0|p5f7rz,51,40,0|p5f7s0,57,66,1|phnvnz,57,66,1|phnvo0,51,40,0|po5afz,51,40,0|po5ag0,57,66,1|q0dybz,57,66,1|q0dyc0,51,40,0|q6vd3z,51,40,0|q6vd40,57,66,1|qj40zz,57,66,1|qj4100,51,40,0|qpyefz,51,40,0|qpyeg0,57,66,1|r272bz,57,66,1|r272c0,51,40,0|r8oh3z,51,40,0|r8oh40,57,66,1|rkx4zz,57,66,1|rkx500,51,40,0|rrejrz,51,40,0|rrejs0,57,66,1|s3n7nz,57,66,1|s3n7o0,51,40,0|sa4mfz,51,40,0|sa4mg0,57,66,1|smdabz,57,66,1|smdac0,51,40,0|ssup3z,51,40,0|ssup40,57,66,1|t53czz,57,66,1|t53d00,51,40,0|tbkrrz,51,40,0|tbkrs0,57,66,1|tntfnz,57,66,1|tntfo0,51,40,0|tunt3z,51,40,0|tunt40,57,66,1|u6wgzz,57,66,1|u6wh00,51,40,0|uddvrz,51,40,0|uddvs0,57,66,1|upmjnz,57,66,1|upmjo0,51,40,0|uw3yfz,51,40,0|uw3yg0,57,66,1|v8cmbz,57,66,1|v8cmc0,51,40,0|veu13z,51,40,0|veu140,57,66,1|vr2ozz,57,66,1|vr2p00,51,40,0|vxk3rz,51,40,0|vxk3s0,57,66,1|w9srnz,57,66,1|w9sro0,51,40,0|wgn53z,51,40,0|wgn540,57,66,1|wsvszz,57,66,1|wsvt00,51,40,0|wzd7rz,51,40,0|wzd7s0,57,66,1|xblvnz,57,66,1|xblvo0,51,40,0|xi3afz,51,40,0|xi3ag0,57,66,1|xubybz,57,66,1|xubyc0,51,40,0|y0td3z,51,40,0|y0td40,57,66,1|yd20zz,57,66,1|yd2100,51,40,0|yjjfrz,51,40,0|yjjfs0,57,66,1|yvs3nz,57,66,1|yvs3o0,51,40,0|z29ifz,51,40,0|z29ig0,57,66,1|zei6bz,57,66,1|zei6c0,51,40,0\",\"America/Lower_Princes|,0,58,0|-u7lckd,43,59,0|-2lx4u1,43,59,0|-2lx4u0,32,42,0\",\"America/Maceio|,0,131,0|-t85ldw,39,44,0|-jylag1,39,44,0|-jylag0,40,45,1|-jpb141,40,45,1|-jpb140,39,44,0|-jfsd01,39,44,0|-jfsd00,40,45,1|-j6j3s1,40,45,1|-j6j3s0,39,44,0|-ahcyc1,39,44,0|-ahcyc0,40,45,1|-aad3o1,40,45,1|-aad3o0,39,44,0|-9yl101,39,44,0|-9yl100,40,45,1|-9sd141,40,45,1|-9sd140,39,44,0|-9ft3o1,39,44,0|-9ft3o0,40,45,1|-99j941,40,45,1|-99j940,39,44,0|-8wzbo1,39,44,0|-8wzbo0,40,45,1|-8scqg1,40,45,1|-8scqg0,39,44,0|-35xmc1,39,44,0|-35xmc0,40,45,1|-31nzs1,40,45,1|-31nzs0,39,44,0|-2kdp01,39,44,0|-2kdp00,40,45,1|-2hcig1,40,45,1|-2hcig0,39,44,0|-24qqc1,39,44,0|-24qqc0,40,45,1|-204541,40,45,1|-204540,39,44,0|-1nid01,39,44,0|-1nid00,40,45,1|-1hc7s1,40,45,1|-1hc7s0,39,44,0|-14qfo1,39,44,0|-14qfo0,40,45,1|-yifs1,40,45,1|-yifs0,39,44,0|89jcbz,39,44,0|89jcc0,40,45,1|8gdk7z,40,45,1|8gdk80,39,44,0|8rwgbz,39,44,0|8rwgc0,40,45,1|8xns7z,40,45,1|8xns80,39,44,0|9aodnz,39,44,0|9aodo0,40,45,1|9g2qvz,40,45,1|9g2qw0,39,44,0|9t1hnz,39,44,0|9t1ho0,40,45,1|9yfuvz,40,45,1|9yfuw0,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|ahvuvz,40,45,1|ahvuw0,39,44,0|dggyzz,39,44,0|dggz00,40,45,1|dml9jz,40,45,1|dml9k0,39,44,0|fj0azz,39,44,0|fj0b00,40,45,1|fqkg7z,40,45,1|fqkg80,39,44,0|g23cbz,39,44,0|g23cc0,40,45,1|g2t6vz,40,45,1|g2t6w0,39,44,0|gl6dnz,39,44,0|gl6do0,40,45,1|grnmvz,40,45,1|grnmw0,39,44,0\",\"America/Managua|,0,132,0|-15r0tcs,21,133,0|-ijh6op,21,133,0|-ijh6oo,45,62,0|1qkbbz,45,62,0|1qkbc0,49,63,0|2ob1vz,49,63,0|2ob1w0,45,62,0|4t08nz,45,62,0|4t08o0,46,63,1|4y3hvz,46,63,1|4y3hw0,45,62,0|5bqbbz,45,62,0|5bqbc0,46,63,1|5gtkjz,46,63,1|5gtkk0,45,62,0|bhcefz,45,62,0|bhceg0,49,63,0|bv2gjz,49,63,0|bv2gk0,45,62,0|c05vbz,45,62,0|c05vc0,49,63,0|e3bcjz,49,63,0|e3bck0,45,62,0|iepvbz,45,62,0|iepvc0,46,63,1|inpv7z,46,63,1|inpv80,45,62,0|iyizjz,45,62,0|iyizk0,46,63,1|j6g0nz,46,63,1|j6g0o0,45,62,0\",\"America/Manaus|,0,134,0|-t85gvw,42,42,0|-jyl7o1,42,42,0|-jyl7o0,39,44,1|-jpayc1,39,44,1|-jpayc0,42,42,0|-jfsa81,42,42,0|-jfsa80,39,44,1|-j6j101,39,44,1|-j6j100,42,42,0|-ahcvk1,42,42,0|-ahcvk0,39,44,1|-aad0w1,39,44,1|-aad0w0,42,42,0|-9yky81,42,42,0|-9yky80,39,44,1|-9scyc1,39,44,1|-9scyc0,42,42,0|-9ft0w1,42,42,0|-9ft0w0,39,44,1|-99j6c1,39,44,1|-99j6c0,42,42,0|-8wz8w1,42,42,0|-8wz8w0,39,44,1|-8scno1,39,44,1|-8scno0,42,42,0|-35xjk1,42,42,0|-35xjk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2kdm81,42,42,0|-2kdm80,39,44,1|-2hcfo1,39,44,1|-2hcfo0,42,42,0|-24qnk1,42,42,0|-24qnk0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1nia81,42,42,0|-1nia80,39,44,1|-1hc501,39,44,1|-1hc500,42,42,0|-14qcw1,42,42,0|-14qcw0,39,44,1|-yid01,39,44,1|-yid00,42,42,0|89jf3z,42,42,0|89jf40,39,44,1|8gdmzz,39,44,1|8gdn00,42,42,0|8rwj3z,42,42,0|8rwj40,39,44,1|8xnuzz,39,44,1|8xnv00,42,42,0|9aogfz,42,42,0|9aogg0,39,44,1|9g2tnz,39,44,1|9g2to0,42,42,0|cf0wfz,42,42,0|cf0wg0,39,44,1|cli5nz,39,44,1|cli5o0,42,42,0\",\"America/Marigot|,0,41,0|-u6m79w,32,42,0\",\"America/Martinique|,0,135,0|-15r0y0s,80,135,0|-umcvct,80,135,0|-umcvcs,32,42,0|5ct1rz,32,42,0|5ct1s0,54,44,1|5lt1nz,54,44,1|5lt1o0,32,42,0\",\"America/Matamoros|,0,136,0|-p1u7c0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trq3z,46,63,1|9trq40,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,45,62,0|gcwm7z,45,62,0|gcwm80,46,63,1|gkgrfz,46,63,1|gkgrg0,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jft7jz,45,62,0|jft7k0,46,63,1|jqm0rz,46,63,1|jqm0s0,45,62,0|jyw8vz,45,62,0|jyw8w0,46,63,1|k9c3fz,46,63,1|k9c3g0,45,62,0|khmbjz,45,62,0|khmbk0,46,63,1|ks263z,46,63,1|ks2640,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Mazatlan|,0,137,0|-p1u4k0,50,66,0|-m7mko1,50,66,0|-m7mko0,45,62,0|-kf67c1,45,62,0|-kf67c0,50,66,0|-k6j3c1,50,66,0|-k6j3c0,45,62,0|-jypm01,45,62,0|-jypm00,50,66,0|-jpan81,50,66,0|-jpan80,45,62,0|-eg9601,45,62,0|-eg9600,50,66,0|-axv381,50,66,0|-axv380,51,40,0|m7z,51,40,0|m80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gcwozz,50,66,0|gcwp00,52,62,1|gkgu7z,52,62,1|gkgu80,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jftabz,50,66,0|jftac0,52,62,1|jqm3jz,52,62,1|jqm3k0,50,66,0|jywbnz,50,66,0|jywbo0,52,62,1|k9c67z,52,62,1|k9c680,50,66,0|khmebz,50,66,0|khmec0,52,62,1|ks28vz,52,62,1|ks28w0,50,66,0|l0cgzz,50,66,0|l0ch00,52,62,1|lb5a7z,52,62,1|lb5a80,50,66,0|lj2jnz,50,66,0|lj2jo0,52,62,1|ltvcvz,52,62,1|ltvcw0,50,66,0|m1smbz,50,66,0|m1smc0,52,62,1|mclfjz,52,62,1|mclfk0,50,66,0|mkvnnz,50,66,0|mkvno0,52,62,1|mvbi7z,52,62,1|mvbi80,50,66,0|n3lqbz,50,66,0|n3lqc0,52,62,1|ne1kvz,52,62,1|ne1kw0,50,66,0|nmbszz,50,66,0|nmbt00,52,62,1|nwrnjz,52,62,1|nwrnk0,50,66,0|o51vnz,50,66,0|o51vo0,52,62,1|ofuovz,52,62,1|ofuow0,50,66,0|onrybz,50,66,0|onryc0,52,62,1|oykrjz,52,62,1|oykrk0,50,66,0|p6i0zz,50,66,0|p6i100,52,62,1|phau7z,52,62,1|phau80,50,66,0|ppl2bz,50,66,0|ppl2c0,52,62,1|q00wvz,52,62,1|q00ww0,50,66,0|q8b4zz,50,66,0|q8b500,52,62,1|qiqzjz,52,62,1|qiqzk0,50,66,0|qr17nz,50,66,0|qr17o0,52,62,1|r1u0vz,52,62,1|r1u0w0,50,66,0|r9rabz,50,66,0|r9rac0,52,62,1|rkk3jz,52,62,1|rkk3k0,50,66,0|rshczz,50,66,0|rshd00,52,62,1|s3a67z,52,62,1|s3a680,50,66,0|sbkebz,50,66,0|sbkec0,52,62,1|sm08vz,52,62,1|sm08w0,50,66,0|suagzz,50,66,0|suah00,52,62,1|t4qbjz,52,62,1|t4qbk0,50,66,0|td0jnz,50,66,0|td0jo0,52,62,1|tnge7z,52,62,1|tnge80,50,66,0|tvqmbz,50,66,0|tvqmc0,52,62,1|u6jfjz,52,62,1|u6jfk0,50,66,0|uegozz,50,66,0|uegp00,52,62,1|up9i7z,52,62,1|up9i80,50,66,0|ux6rnz,50,66,0|ux6ro0,52,62,1|v7zkvz,52,62,1|v7zkw0,50,66,0|vg9szz,50,66,0|vg9t00,52,62,1|vqpnjz,52,62,1|vqpnk0,50,66,0|vyzvnz,50,66,0|vyzvo0,52,62,1|w9fq7z,52,62,1|w9fq80,50,66,0|whpybz,50,66,0|whpyc0,52,62,1|wsirjz,52,62,1|wsirk0,50,66,0|x0g0zz,50,66,0|x0g100,52,62,1|xb8u7z,52,62,1|xb8u80,50,66,0|xj63nz,50,66,0|xj63o0,52,62,1|xtywvz,52,62,1|xtyww0,50,66,0|y1w6bz,50,66,0|y1w6c0,52,62,1|ycozjz,52,62,1|ycozk0,50,66,0|ykz7nz,50,66,0|ykz7o0,52,62,1|yvf27z,52,62,1|yvf280,50,66,0|z3pabz,50,66,0|z3pac0,52,62,1|ze54vz,52,62,1|ze54w0,50,66,0\",\"America/Menominee|,0,138,0|-17zjvrx,45,62,0|-r0esg1,45,62,0|-r0esg0,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-qhops1,45,62,0|-qhops0,46,63,1|-q6vwk1,46,63,1|-q6vwk0,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-ccw1s1,45,62,0|-ccw1s0,46,63,1|-c4yxw1,46,63,1|-c4yxw0,45,62,0|-1xbog1,45,62,0|-1xbog0,46,63,1|-1nlr81,46,63,1|-1nlr80,45,62,0|-cshs1,45,62,0|-cshs0,49,63,0|1qgorz,49,63,0|1qgos0,46,63,1|1ztq3z,46,63,1|1ztq40,45,62,0|23ffjz,45,62,0|23ffk0,46,63,1|2ijsrz,46,63,1|2ijss0,45,62,0|2oo8vz,45,62,0|2oo8w0,46,63,1|319vfz,46,63,1|319vg0,45,62,0|3amzjz,45,62,0|3amzk0,46,63,1|3kcwrz,46,63,1|3kcws0,45,62,0|3td27z,45,62,0|3td280,46,63,1|432zfz,46,63,1|432zg0,45,62,0|4cg3jz,45,62,0|4cg3k0,46,63,1|4lt23z,46,63,1|4lt240,45,62,0|4v667z,45,62,0|4v6680,46,63,1|54j4rz,46,63,1|54j4s0,45,62,0|5dw8vz,45,62,0|5dw8w0,46,63,1|5n97fz,46,63,1|5n97g0,45,62,0|5wmbjz,45,62,0|5wmbk0,46,63,1|65za3z,46,63,1|65za40,45,62,0|6fce7z,45,62,0|6fce80,46,63,1|6p2bfz,46,63,1|6p2bg0,45,62,0|6y2gvz,45,62,0|6y2gw0,46,63,1|77se3z,46,63,1|77se40,45,62,0|7h5i7z,45,62,0|7h5i80,46,63,1|7qigrz,46,63,1|7qigs0,45,62,0|7zvkvz,45,62,0|7zvkw0,46,63,1|898jfz,46,63,1|898jg0,45,62,0|8ilnjz,45,62,0|8ilnk0,46,63,1|8rym3z,46,63,1|8rym40,45,62,0|908u7z,45,62,0|908u80,46,63,1|9aoorz,46,63,1|9aoos0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trq3z,46,63,1|9trq40,45,62,0|a1ozjz,45,62,0|a1ozk0,46,63,1|achsrz,46,63,1|achss0,45,62,0|akf27z,45,62,0|akf280,46,63,1|av7vfz,46,63,1|av7vg0,45,62,0|b3i3jz,45,62,0|b3i3k0,46,63,1|bdxy3z,46,63,1|bdxy40,45,62,0|bm867z,45,62,0|bm8680,46,63,1|bwo0rz,46,63,1|bwo0s0,45,62,0|c4y8vz,45,62,0|c4y8w0,46,63,1|cfr23z,46,63,1|cfr240,45,62,0|cnobjz,45,62,0|cnobk0,46,63,1|cyh4rz,46,63,1|cyh4s0,45,62,0|d6ee7z,45,62,0|d6ee80,46,63,1|dh77fz,46,63,1|dh77g0,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,45,62,0|gb3svz,45,62,0|gb3sw0,46,63,1|glwm3z,46,63,1|glwm40,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Merida|,0,139,0|-p1u7c0,45,62,0|690gnz,45,62,0|690go0,49,63,0|6qpf7z,49,63,0|6qpf80,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,45,62,0|gcwm7z,45,62,0|gcwm80,46,63,1|gkgrfz,46,63,1|gkgrg0,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jft7jz,45,62,0|jft7k0,46,63,1|jqm0rz,46,63,1|jqm0s0,45,62,0|jyw8vz,45,62,0|jyw8w0,46,63,1|k9c3fz,46,63,1|k9c3g0,45,62,0|khmbjz,45,62,0|khmbk0,46,63,1|ks263z,46,63,1|ks2640,45,62,0|l0ce7z,45,62,0|l0ce80,46,63,1|lb57fz,46,63,1|lb57g0,45,62,0|lj2gvz,45,62,0|lj2gw0,46,63,1|ltva3z,46,63,1|ltva40,45,62,0|m1sjjz,45,62,0|m1sjk0,46,63,1|mclcrz,46,63,1|mclcs0,45,62,0|mkvkvz,45,62,0|mkvkw0,46,63,1|mvbffz,46,63,1|mvbfg0,45,62,0|n3lnjz,45,62,0|n3lnk0,46,63,1|ne1i3z,46,63,1|ne1i40,45,62,0|nmbq7z,45,62,0|nmbq80,46,63,1|nwrkrz,46,63,1|nwrks0,45,62,0|o51svz,45,62,0|o51sw0,46,63,1|ofum3z,46,63,1|ofum40,45,62,0|onrvjz,45,62,0|onrvk0,46,63,1|oykorz,46,63,1|oykos0,45,62,0|p6hy7z,45,62,0|p6hy80,46,63,1|pharfz,46,63,1|pharg0,45,62,0|ppkzjz,45,62,0|ppkzk0,46,63,1|q00u3z,46,63,1|q00u40,45,62,0|q8b27z,45,62,0|q8b280,46,63,1|qiqwrz,46,63,1|qiqws0,45,62,0|qr14vz,45,62,0|qr14w0,46,63,1|r1ty3z,46,63,1|r1ty40,45,62,0|r9r7jz,45,62,0|r9r7k0,46,63,1|rkk0rz,46,63,1|rkk0s0,45,62,0|rsha7z,45,62,0|rsha80,46,63,1|s3a3fz,46,63,1|s3a3g0,45,62,0|sbkbjz,45,62,0|sbkbk0,46,63,1|sm063z,46,63,1|sm0640,45,62,0|suae7z,45,62,0|suae80,46,63,1|t4q8rz,46,63,1|t4q8s0,45,62,0|td0gvz,45,62,0|td0gw0,46,63,1|tngbfz,46,63,1|tngbg0,45,62,0|tvqjjz,45,62,0|tvqjk0,46,63,1|u6jcrz,46,63,1|u6jcs0,45,62,0|uegm7z,45,62,0|uegm80,46,63,1|up9ffz,46,63,1|up9fg0,45,62,0|ux6ovz,45,62,0|ux6ow0,46,63,1|v7zi3z,46,63,1|v7zi40,45,62,0|vg9q7z,45,62,0|vg9q80,46,63,1|vqpkrz,46,63,1|vqpks0,45,62,0|vyzsvz,45,62,0|vyzsw0,46,63,1|w9fnfz,46,63,1|w9fng0,45,62,0|whpvjz,45,62,0|whpvk0,46,63,1|wsiorz,46,63,1|wsios0,45,62,0|x0fy7z,45,62,0|x0fy80,46,63,1|xb8rfz,46,63,1|xb8rg0,45,62,0|xj60vz,45,62,0|xj60w0,46,63,1|xtyu3z,46,63,1|xtyu40,45,62,0|y1w3jz,45,62,0|y1w3k0,46,63,1|ycowrz,46,63,1|ycows0,45,62,0|ykz4vz,45,62,0|ykz4w0,46,63,1|yvezfz,46,63,1|yvezg0,45,62,0|z3p7jz,45,62,0|z3p7k0,46,63,1|ze523z,46,63,1|ze5240,45,62,0\",\"America/Metlakatla|,0,140,0|-1hc7qjz,0,141,0|-1078wyv,0,141,0|-1078wyu,51,40,0|-ek1w81,51,40,0|-ek1w80,68,66,1|-cq2tg1,68,66,1|-cq2tg0,69,66,1|-cnov01,69,66,1|-cnov00,51,40,0|-csc81,51,40,0|-csc80,57,66,1|-3fdo1,57,66,1|-3fdo0,51,40,0|5xqfz,51,40,0|5xqg0,57,66,1|faozz,57,66,1|fap00,51,40,0|ont3z,51,40,0|ont40,57,66,1|ydqbz,57,66,1|ydqc0,51,40,0|17qufz,51,40,0|17qug0,57,66,1|1h3szz,57,66,1|1h3t00,51,40,0|1qgx3z,51,40,0|1qgx40,57,66,1|1ztvnz,57,66,1|1ztvo0,51,40,0|23fl3z,51,40,0|23fl40,57,66,1|2ijybz,57,66,1|2ijyc0,51,40,0|2ooefz,51,40,0|2ooeg0,57,66,1|31a0zz,57,66,1|31a100,51,40,0|3an53z,51,40,0|3an540,57,66,1|3kd2bz,57,66,1|3kd2c0,51,40,0|3td7rz,51,40,0|3td7s0,57,66,1|4334zz,57,66,1|433500,51,40,0|4cg93z,51,40,0|4cg940,57,66,1|4lt7nz,57,66,1|4lt7o0,51,40,0|4v6brz,51,40,0|4v6bs0,57,66,1|54jabz,57,66,1|54jac0,51,40,0|5dwefz,51,40,0|5dweg0,57,66,1|5n9czz,57,66,1|5n9d00,51,40,0|5wmh3z,51,40,0|5wmh40,57,66,1|65zfnz,57,66,1|65zfo0,51,40,0|6fcjrz,51,40,0|6fcjs0,57,66,1|6p2gzz,57,66,1|6p2h00,51,40,0|6y2mfz,51,40,0|6y2mg0,57,66,1|77sjnz,57,66,1|77sjo0,51,40,0|nx4rrz,51,40,0|nx4rs0,37,37,0|o3z57z,37,37,0|o3z580,38,40,1|og7t3z,38,40,1|og7t40,37,37,0|omp7vz,37,37,0|omp7w0,38,40,1|oyxvrz,38,40,1|oyxvs0,37,37,0|p5fajz,37,37,0|p5fak0,38,40,1|phnyfz,38,40,1|phnyg0,51,40,0|plmjrz,51,40,0|plmjs0,37,37,0|po5d7z,37,37,0|po5d80,38,40,1|q0e13z,38,40,1|q0e140,37,37,0|q6vfvz,37,37,0|q6vfw0,38,40,1|qj43rz,38,40,1|qj43s0,37,37,0|qpyh7z,37,37,0|qpyh80,38,40,1|r2753z,38,40,1|r27540,37,37,0|r8ojvz,37,37,0|r8ojw0,38,40,1|rkx7rz,38,40,1|rkx7s0,37,37,0|rremjz,37,37,0|rremk0,38,40,1|s3nafz,38,40,1|s3nag0,37,37,0|sa4p7z,37,37,0|sa4p80,38,40,1|smdd3z,38,40,1|smdd40,37,37,0|ssurvz,37,37,0|ssurw0,38,40,1|t53frz,38,40,1|t53fs0,37,37,0|tbkujz,37,37,0|tbkuk0,38,40,1|tntifz,38,40,1|tntig0,37,37,0|tunvvz,37,37,0|tunvw0,38,40,1|u6wjrz,38,40,1|u6wjs0,37,37,0|uddyjz,37,37,0|uddyk0,38,40,1|upmmfz,38,40,1|upmmg0,37,37,0|uw417z,37,37,0|uw4180,38,40,1|v8cp3z,38,40,1|v8cp40,37,37,0|veu3vz,37,37,0|veu3w0,38,40,1|vr2rrz,38,40,1|vr2rs0,37,37,0|vxk6jz,37,37,0|vxk6k0,38,40,1|w9sufz,38,40,1|w9sug0,37,37,0|wgn7vz,37,37,0|wgn7w0,38,40,1|wsvvrz,38,40,1|wsvvs0,37,37,0|wzdajz,37,37,0|wzdak0,38,40,1|xblyfz,38,40,1|xblyg0,37,37,0|xi3d7z,37,37,0|xi3d80,38,40,1|xuc13z,38,40,1|xuc140,37,37,0|y0tfvz,37,37,0|y0tfw0,38,40,1|yd23rz,38,40,1|yd23s0,37,37,0|yjjijz,37,37,0|yjjik0,38,40,1|yvs6fz,38,40,1|yvs6g0,37,37,0|z29l7z,37,37,0|z29l80,38,40,1|zei93z,38,40,1|zei940,37,37,0\",\"America/Mexico_City|,0,142,0|-p1u4k0,50,66,0|-m7mko1,50,66,0|-m7mko0,45,62,0|-kf67c1,45,62,0|-kf67c0,50,66,0|-k6j3c1,50,66,0|-k6j3c0,45,62,0|-jypm01,45,62,0|-jypm00,50,66,0|-jpan81,50,66,0|-jpan80,45,62,0|-g4n8o1,45,62,0|-g4n8o0,46,63,1|-fxg241,46,63,1|-fxg240,45,62,0|-f60y01,45,62,0|-f60y00,46,63,1|-f07rg1,46,63,1|-f07rg0,45,62,0|-dlc7c1,45,62,0|-dlc7c0,47,63,1|-deaks1,47,63,1|-deaks0,45,62,0|-adljc1,45,62,0|-adljc0,46,63,1|-a4yi41,46,63,1|-a4yi40,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,45,62,0|gcwm7z,45,62,0|gcwm80,46,63,1|gkgrfz,46,63,1|gkgrg0,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jft7jz,45,62,0|jft7k0,46,63,1|jqm0rz,46,63,1|jqm0s0,45,62,0|jyw8vz,45,62,0|jyw8w0,46,63,1|k9c3fz,46,63,1|k9c3g0,45,62,0|khmbjz,45,62,0|khmbk0,46,63,1|ks263z,46,63,1|ks2640,45,62,0|l0ce7z,45,62,0|l0ce80,46,63,1|lb57fz,46,63,1|lb57g0,45,62,0|lj2gvz,45,62,0|lj2gw0,46,63,1|ltva3z,46,63,1|ltva40,45,62,0|m1sjjz,45,62,0|m1sjk0,46,63,1|mclcrz,46,63,1|mclcs0,45,62,0|mkvkvz,45,62,0|mkvkw0,46,63,1|mvbffz,46,63,1|mvbfg0,45,62,0|n3lnjz,45,62,0|n3lnk0,46,63,1|ne1i3z,46,63,1|ne1i40,45,62,0|nmbq7z,45,62,0|nmbq80,46,63,1|nwrkrz,46,63,1|nwrks0,45,62,0|o51svz,45,62,0|o51sw0,46,63,1|ofum3z,46,63,1|ofum40,45,62,0|onrvjz,45,62,0|onrvk0,46,63,1|oykorz,46,63,1|oykos0,45,62,0|p6hy7z,45,62,0|p6hy80,46,63,1|pharfz,46,63,1|pharg0,45,62,0|ppkzjz,45,62,0|ppkzk0,46,63,1|q00u3z,46,63,1|q00u40,45,62,0|q8b27z,45,62,0|q8b280,46,63,1|qiqwrz,46,63,1|qiqws0,45,62,0|qr14vz,45,62,0|qr14w0,46,63,1|r1ty3z,46,63,1|r1ty40,45,62,0|r9r7jz,45,62,0|r9r7k0,46,63,1|rkk0rz,46,63,1|rkk0s0,45,62,0|rsha7z,45,62,0|rsha80,46,63,1|s3a3fz,46,63,1|s3a3g0,45,62,0|sbkbjz,45,62,0|sbkbk0,46,63,1|sm063z,46,63,1|sm0640,45,62,0|suae7z,45,62,0|suae80,46,63,1|t4q8rz,46,63,1|t4q8s0,45,62,0|td0gvz,45,62,0|td0gw0,46,63,1|tngbfz,46,63,1|tngbg0,45,62,0|tvqjjz,45,62,0|tvqjk0,46,63,1|u6jcrz,46,63,1|u6jcs0,45,62,0|uegm7z,45,62,0|uegm80,46,63,1|up9ffz,46,63,1|up9fg0,45,62,0|ux6ovz,45,62,0|ux6ow0,46,63,1|v7zi3z,46,63,1|v7zi40,45,62,0|vg9q7z,45,62,0|vg9q80,46,63,1|vqpkrz,46,63,1|vqpks0,45,62,0|vyzsvz,45,62,0|vyzsw0,46,63,1|w9fnfz,46,63,1|w9fng0,45,62,0|whpvjz,45,62,0|whpvk0,46,63,1|wsiorz,46,63,1|wsios0,45,62,0|x0fy7z,45,62,0|x0fy80,46,63,1|xb8rfz,46,63,1|xb8rg0,45,62,0|xj60vz,45,62,0|xj60w0,46,63,1|xtyu3z,46,63,1|xtyu40,45,62,0|y1w3jz,45,62,0|y1w3k0,46,63,1|ycowrz,46,63,1|ycows0,45,62,0|ykz4vz,45,62,0|ykz4w0,46,63,1|yvezfz,46,63,1|yvezg0,45,62,0|z3p7jz,45,62,0|z3p7k0,46,63,1|ze523z,46,63,1|ze5240,45,62,0\",\"America/Miquelon|,0,143,0|-ulmyxk,32,42,0|5e3cfz,32,42,0|5e3cg0,39,44,0|908lvz,39,44,0|908lw0,40,45,1|9aogfz,40,45,1|9aogg0,39,44,0|9iyojz,39,44,0|9iyok0,40,45,1|9trhrz,40,45,1|9trhs0,39,44,0|a1or7z,39,44,0|a1or80,40,45,1|achkfz,40,45,1|achkg0,39,44,0|aketvz,39,44,0|aketw0,40,45,1|av7n3z,40,45,1|av7n40,39,44,0|b3hv7z,39,44,0|b3hv80,40,45,1|bdxprz,40,45,1|bdxps0,39,44,0|bm7xvz,39,44,0|bm7xw0,40,45,1|bwnsfz,40,45,1|bwnsg0,39,44,0|c4y0jz,39,44,0|c4y0k0,40,45,1|cfqtrz,40,45,1|cfqts0,39,44,0|cno37z,39,44,0|cno380,40,45,1|cygwfz,40,45,1|cygwg0,39,44,0|d6e5vz,39,44,0|d6e5w0,40,45,1|dh6z3z,40,45,1|dh6z40,39,44,0|dph77z,39,44,0|dph780,40,45,1|dzx1rz,40,45,1|dzx1s0,39,44,0|e879vz,39,44,0|e879w0,40,45,1|ein4fz,40,45,1|ein4g0,39,44,0|eqxcjz,39,44,0|eqxck0,40,45,1|f1d73z,40,45,1|f1d740,39,44,0|f9nf7z,39,44,0|f9nf80,40,45,1|fkg8fz,40,45,1|fkg8g0,39,44,0|fsdhvz,39,44,0|fsdhw0,40,45,1|g36b3z,40,45,1|g36b40,39,44,0|gb3kjz,39,44,0|gb3kk0,40,45,1|glwdrz,40,45,1|glwds0,39,44,0|gu6lvz,39,44,0|gu6lw0,40,45,1|h4mgfz,40,45,1|h4mgg0,39,44,0|hcwojz,39,44,0|hcwok0,40,45,1|hncj3z,40,45,1|hncj40,39,44,0|hvmr7z,39,44,0|hvmr80,40,45,1|i6fkfz,40,45,1|i6fkg0,39,44,0|iectvz,39,44,0|iectw0,40,45,1|ip5n3z,40,45,1|ip5n40,39,44,0|ix2wjz,39,44,0|ix2wk0,40,45,1|j7vprz,40,45,1|j7vps0,39,44,0|jeq37z,39,44,0|jeq380,40,45,1|jqyr3z,40,45,1|jqyr40,39,44,0|jxg5vz,39,44,0|jxg5w0,40,45,1|k9otrz,40,45,1|k9ots0,39,44,0|kg68jz,39,44,0|kg68k0,40,45,1|ksewfz,40,45,1|ksewg0,39,44,0|kz99vz,39,44,0|kz99w0,40,45,1|lbhxrz,40,45,1|lbhxs0,39,44,0|lhzcjz,39,44,0|lhzck0,40,45,1|lu80fz,40,45,1|lu80g0,39,44,0|m0pf7z,39,44,0|m0pf80,40,45,1|mcy33z,40,45,1|mcy340,39,44,0|mjfhvz,39,44,0|mjfhw0,40,45,1|mvo5rz,40,45,1|mvo5s0,39,44,0|n25kjz,39,44,0|n25kk0,40,45,1|nee8fz,40,45,1|nee8g0,39,44,0|nkvn7z,39,44,0|nkvn80,40,45,1|nx4b3z,40,45,1|nx4b40,39,44,0|o3yojz,39,44,0|o3yok0,40,45,1|og7cfz,40,45,1|og7cg0,39,44,0|omor7z,39,44,0|omor80,40,45,1|oyxf3z,40,45,1|oyxf40,39,44,0|p5etvz,39,44,0|p5etw0,40,45,1|phnhrz,40,45,1|phnhs0,39,44,0|po4wjz,39,44,0|po4wk0,40,45,1|q0dkfz,40,45,1|q0dkg0,39,44,0|q6uz7z,39,44,0|q6uz80,40,45,1|qj3n3z,40,45,1|qj3n40,39,44,0|qpy0jz,39,44,0|qpy0k0,40,45,1|r26ofz,40,45,1|r26og0,39,44,0|r8o37z,39,44,0|r8o380,40,45,1|rkwr3z,40,45,1|rkwr40,39,44,0|rre5vz,39,44,0|rre5w0,40,45,1|s3mtrz,40,45,1|s3mts0,39,44,0|sa48jz,39,44,0|sa48k0,40,45,1|smcwfz,40,45,1|smcwg0,39,44,0|ssub7z,39,44,0|ssub80,40,45,1|t52z3z,40,45,1|t52z40,39,44,0|tbkdvz,39,44,0|tbkdw0,40,45,1|tnt1rz,40,45,1|tnt1s0,39,44,0|tunf7z,39,44,0|tunf80,40,45,1|u6w33z,40,45,1|u6w340,39,44,0|uddhvz,39,44,0|uddhw0,40,45,1|upm5rz,40,45,1|upm5s0,39,44,0|uw3kjz,39,44,0|uw3kk0,40,45,1|v8c8fz,40,45,1|v8c8g0,39,44,0|vetn7z,39,44,0|vetn80,40,45,1|vr2b3z,40,45,1|vr2b40,39,44,0|vxjpvz,39,44,0|vxjpw0,40,45,1|w9sdrz,40,45,1|w9sds0,39,44,0|wgmr7z,39,44,0|wgmr80,40,45,1|wsvf3z,40,45,1|wsvf40,39,44,0|wzctvz,39,44,0|wzctw0,40,45,1|xblhrz,40,45,1|xblhs0,39,44,0|xi2wjz,39,44,0|xi2wk0,40,45,1|xubkfz,40,45,1|xubkg0,39,44,0|y0sz7z,39,44,0|y0sz80,40,45,1|yd1n3z,40,45,1|yd1n40,39,44,0|yjj1vz,39,44,0|yjj1w0,40,45,1|yvrprz,40,45,1|yvrps0,39,44,0|z294jz,39,44,0|z294k0,40,45,1|zehsfz,40,45,1|zehsg0,39,44,0\",\"America/Moncton|,0,144,0|-18wys04,49,63,0|-z94i41,49,63,0|-z94i40,32,42,0|-qzp0o1,32,42,0|-qzp0o0,54,44,1|-qpm4s1,54,44,1|-qpm4s0,32,42,0|-j2ve41,32,42,0|-j2ve40,54,44,1|-iy6y81,54,44,1|-iy6y80,32,42,0|-ik5bg1,32,42,0|-ik5bg0,54,44,1|-ifgvk1,54,44,1|-ifgvk0,32,42,0|-i1f8s1,32,42,0|-i1f8s0,54,44,1|-hwqsw1,54,44,1|-hwqsw0,32,42,0|-hip641,32,42,0|-hip640,54,44,1|-he0q81,54,44,1|-he0q80,32,42,0|-gzz3g1,32,42,0|-gzz3g0,54,44,1|-gvank1,54,44,1|-gvank0,32,42,0|-gh90s1,32,42,0|-gh90s0,54,44,1|-gckkw1,54,44,1|-gckkw0,32,42,0|-fyxrg1,32,42,0|-fyxrg0,54,44,1|-fstgw1,54,44,1|-fstgw0,32,42,0|-fgiss1,32,42,0|-fgiss0,54,44,1|-fa3e81,54,44,1|-fa3e80,32,42,0|-eying1,32,42,0|-eying0,54,44,1|-er0cw1,54,44,1|-er0cw0,32,42,0|-ek27c1,32,42,0|-ek27c0,33,44,1|-cq2tg1,33,44,1|-cq2tg0,34,44,1|-cnp641,34,44,1|-cnp640,32,42,0|-ccw7c1,32,42,0|-ccw7c0,54,44,1|-c4z3g1,54,44,1|-c4z3g0,32,42,0|-bu64o1,32,42,0|-bu64o0,54,44,1|-bm90s1,54,44,1|-bm90s0,32,42,0|-bbg201,32,42,0|-bbg200,54,44,1|-b3iy41,54,44,1|-b3iy40,32,42,0|-aspzc1,32,42,0|-aspzc0,54,44,1|-aksvg1,54,44,1|-aksvg0,32,42,0|-a9my01,32,42,0|-a9my00,54,44,1|-a22ss1,54,44,1|-a22ss0,32,42,0|-9qwvc1,32,42,0|-9qwvc0,54,44,1|-9izrg1,54,44,1|-9izrg0,32,42,0|-986so1,32,42,0|-986so0,54,44,1|-909os1,54,44,1|-909os0,32,42,0|-8pgq01,32,42,0|-8pgq00,54,44,1|-8hjm41,54,44,1|-8hjm40,32,42,0|-86qnc1,32,42,0|-86qnc0,54,44,1|-7ytjg1,54,44,1|-7ytjg0,32,42,0|-7o0ko1,32,42,0|-7o0ko0,54,44,1|-7g3gs1,54,44,1|-7g3gs0,32,42,0|-74xjc1,32,42,0|-74xjc0,54,44,1|-6x0fg1,54,44,1|-6x0fg0,32,42,0|-6m7go1,32,42,0|-6m7go0,54,44,1|-6cui41,54,44,1|-6cui40,32,42,0|-63he01,32,42,0|-63he00,54,44,1|-5u4fg1,54,44,1|-5u4fg0,32,42,0|-5krbc1,32,42,0|-5krbc0,54,44,1|-5becs1,54,44,1|-5becs0,32,42,0|-5218o1,32,42,0|-5218o0,54,44,1|-4sbbg1,54,44,1|-4sbbg0,32,42,0|-4iy7c1,32,42,0|-4iy7c0,54,44,1|-49l8s1,54,44,1|-49l8s0,32,42,0|-4084o1,32,42,0|-4084o0,54,44,1|-3qv641,54,44,1|-3qv640,32,42,0|-3hi201,32,42,0|-3hi200,54,44,1|-3853g1,54,44,1|-3853g0,32,42,0|-2yrzc1,32,42,0|-2yrzc0,54,44,1|-2pf0s1,54,44,1|-2pf0s0,32,42,0|-2g1wo1,32,42,0|-2g1wo0,54,44,1|-26bzg1,54,44,1|-26bzg0,32,42,0|-1xbu01,32,42,0|-1xbu00,54,44,1|-1nlws1,54,44,1|-1nlws0,32,42,0|-1e8so1,32,42,0|-1e8so0,54,44,1|-14vu41,54,44,1|-14vu40,32,42,0|-viq01,32,42,0|-viq00,54,44,1|-m5rg1,54,44,1|-m5rg0,32,42,0|-csnc1,32,42,0|-csnc0,54,44,1|-3fos1,54,44,1|-3fos0,32,42,0|5xfbz,32,42,0|5xfc0,54,44,1|fadvz,54,44,1|fadw0,32,42,0|onhzz,32,42,0|oni00,54,44,1|ydf7z,54,44,1|ydf80,32,42,0|17qjbz,32,42,0|17qjc0,54,44,1|1h3hvz,54,44,1|1h3hw0,32,42,0|296onz,32,42,0|296oo0,54,44,1|2ijn7z,54,44,1|2ijn80,32,42,0|2rwrbz,32,42,0|2rwrc0,54,44,1|319pvz,54,44,1|319pw0,32,42,0|3amtzz,32,42,0|3amu00,54,44,1|3kcr7z,54,44,1|3kcr80,32,42,0|3tcwnz,32,42,0|3tcwo0,54,44,1|432tvz,54,44,1|432tw0,32,42,0|4cfxzz,32,42,0|4cfy00,54,44,1|4lswjz,54,44,1|4lswk0,32,42,0|4v60nz,32,42,0|4v60o0,54,44,1|54iz7z,54,44,1|54iz80,32,42,0|5dw3bz,32,42,0|5dw3c0,54,44,1|5n91vz,54,44,1|5n91w0,32,42,0|5wm5zz,32,42,0|5wm600,54,44,1|65z4jz,54,44,1|65z4k0,32,42,0|6fc8nz,32,42,0|6fc8o0,54,44,1|6p25vz,54,44,1|6p25w0,32,42,0|6y2bbz,32,42,0|6y2bc0,54,44,1|77s8jz,54,44,1|77s8k0,32,42,0|7h5cnz,32,42,0|7h5co0,54,44,1|7qib7z,54,44,1|7qib80,32,42,0|7zvfbz,32,42,0|7zvfc0,54,44,1|898dvz,54,44,1|898dw0,32,42,0|8ilhzz,32,42,0|8ili00,54,44,1|8rygjz,54,44,1|8rygk0,32,42,0|908onz,32,42,0|908oo0,54,44,1|9aoj7z,54,44,1|9aoj80,32,42,0|9iyrbz,32,42,0|9iyrc0,54,44,1|9trkjz,54,44,1|9trkk0,32,42,0|a1otzz,32,42,0|a1ou00,54,44,1|achn7z,54,44,1|achn80,32,42,0|akewnz,32,42,0|akewo0,54,44,1|av7pvz,54,44,1|av7pw0,32,42,0|b3hxzz,32,42,0|b3hy00,54,44,1|bdxsjz,54,44,1|bdxsk0,32,42,0|bm80nz,32,42,0|bm80o0,54,44,1|bwnv7z,54,44,1|bwnv80,32,42,0|c4xxtn,32,42,0|c4xxto,54,44,1|cfqr1n,54,44,1|cfqr1o,32,42,0|cno0hn,32,42,0|cno0ho,54,44,1|cygtpn,54,44,1|cygtpo,32,42,0|d6e35n,32,42,0|d6e35o,54,44,1|dh6wdn,54,44,1|dh6wdo,32,42,0|dph4hn,32,42,0|dph4ho,54,44,1|dzwz1n,54,44,1|dzwz1o,32,42,0|e8775n,32,42,0|e8775o,54,44,1|ein1pn,54,44,1|ein1po,32,42,0|eqx9tn,32,42,0|eqx9to,54,44,1|f1d4dn,54,44,1|f1d4do,32,42,0|f9nchn,32,42,0|f9ncho,54,44,1|fkg5pn,54,44,1|fkg5po,32,42,0|fsdf5n,32,42,0|fsdf5o,54,44,1|g368dn,54,44,1|g368do,32,42,0|gb3htn,32,42,0|gb3hto,54,44,1|glwb1n,54,44,1|glwb1o,32,42,0|gu6j5n,32,42,0|gu6j5o,54,44,1|h4mdpn,54,44,1|h4mdpo,32,42,0|hcwltn,32,42,0|hcwlto,54,44,1|hncgdn,54,44,1|hncgdo,32,42,0|hvmohn,32,42,0|hvmoho,54,44,1|i6fhpn,54,44,1|i6fhpo,32,42,0|iecr5n,32,42,0|iecr5o,54,44,1|ip5kdn,54,44,1|ip5kdo,32,42,0|ix2ttn,32,42,0|ix2tto,54,44,1|j7vn1n,54,44,1|j7vn1o,32,42,0|jeq5zz,32,42,0|jeq600,54,44,1|jqytvz,54,44,1|jqytw0,32,42,0|jxg8nz,32,42,0|jxg8o0,54,44,1|k9owjz,54,44,1|k9owk0,32,42,0|kg6bbz,32,42,0|kg6bc0,54,44,1|ksez7z,54,44,1|ksez80,32,42,0|kz9cnz,32,42,0|kz9co0,54,44,1|lbi0jz,54,44,1|lbi0k0,32,42,0|lhzfbz,32,42,0|lhzfc0,54,44,1|lu837z,54,44,1|lu8380,32,42,0|m0phzz,32,42,0|m0pi00,54,44,1|mcy5vz,54,44,1|mcy5w0,32,42,0|mjfknz,32,42,0|mjfko0,54,44,1|mvo8jz,54,44,1|mvo8k0,32,42,0|n25nbz,32,42,0|n25nc0,54,44,1|neeb7z,54,44,1|neeb80,32,42,0|nkvpzz,32,42,0|nkvq00,54,44,1|nx4dvz,54,44,1|nx4dw0,32,42,0|o3yrbz,32,42,0|o3yrc0,54,44,1|og7f7z,54,44,1|og7f80,32,42,0|omotzz,32,42,0|omou00,54,44,1|oyxhvz,54,44,1|oyxhw0,32,42,0|p5ewnz,32,42,0|p5ewo0,54,44,1|phnkjz,54,44,1|phnkk0,32,42,0|po4zbz,32,42,0|po4zc0,54,44,1|q0dn7z,54,44,1|q0dn80,32,42,0|q6v1zz,32,42,0|q6v200,54,44,1|qj3pvz,54,44,1|qj3pw0,32,42,0|qpy3bz,32,42,0|qpy3c0,54,44,1|r26r7z,54,44,1|r26r80,32,42,0|r8o5zz,32,42,0|r8o600,54,44,1|rkwtvz,54,44,1|rkwtw0,32,42,0|rre8nz,32,42,0|rre8o0,54,44,1|s3mwjz,54,44,1|s3mwk0,32,42,0|sa4bbz,32,42,0|sa4bc0,54,44,1|smcz7z,54,44,1|smcz80,32,42,0|ssudzz,32,42,0|ssue00,54,44,1|t531vz,54,44,1|t531w0,32,42,0|tbkgnz,32,42,0|tbkgo0,54,44,1|tnt4jz,54,44,1|tnt4k0,32,42,0|tunhzz,32,42,0|tuni00,54,44,1|u6w5vz,54,44,1|u6w5w0,32,42,0|uddknz,32,42,0|uddko0,54,44,1|upm8jz,54,44,1|upm8k0,32,42,0|uw3nbz,32,42,0|uw3nc0,54,44,1|v8cb7z,54,44,1|v8cb80,32,42,0|vetpzz,32,42,0|vetq00,54,44,1|vr2dvz,54,44,1|vr2dw0,32,42,0|vxjsnz,32,42,0|vxjso0,54,44,1|w9sgjz,54,44,1|w9sgk0,32,42,0|wgmtzz,32,42,0|wgmu00,54,44,1|wsvhvz,54,44,1|wsvhw0,32,42,0|wzcwnz,32,42,0|wzcwo0,54,44,1|xblkjz,54,44,1|xblkk0,32,42,0|xi2zbz,32,42,0|xi2zc0,54,44,1|xubn7z,54,44,1|xubn80,32,42,0|y0t1zz,32,42,0|y0t200,54,44,1|yd1pvz,54,44,1|yd1pw0,32,42,0|yjj4nz,32,42,0|yjj4o0,54,44,1|yvrsjz,54,44,1|yvrsk0,32,42,0|z297bz,32,42,0|z297c0,54,44,1|zehv7z,54,44,1|zehv80,32,42,0\",\"America/Monterrey|,0,145,0|-p1u7c0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trq3z,46,63,1|9trq40,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,45,62,0|gcwm7z,45,62,0|gcwm80,46,63,1|gkgrfz,46,63,1|gkgrg0,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jft7jz,45,62,0|jft7k0,46,63,1|jqm0rz,46,63,1|jqm0s0,45,62,0|jyw8vz,45,62,0|jyw8w0,46,63,1|k9c3fz,46,63,1|k9c3g0,45,62,0|khmbjz,45,62,0|khmbk0,46,63,1|ks263z,46,63,1|ks2640,45,62,0|l0ce7z,45,62,0|l0ce80,46,63,1|lb57fz,46,63,1|lb57g0,45,62,0|lj2gvz,45,62,0|lj2gw0,46,63,1|ltva3z,46,63,1|ltva40,45,62,0|m1sjjz,45,62,0|m1sjk0,46,63,1|mclcrz,46,63,1|mclcs0,45,62,0|mkvkvz,45,62,0|mkvkw0,46,63,1|mvbffz,46,63,1|mvbfg0,45,62,0|n3lnjz,45,62,0|n3lnk0,46,63,1|ne1i3z,46,63,1|ne1i40,45,62,0|nmbq7z,45,62,0|nmbq80,46,63,1|nwrkrz,46,63,1|nwrks0,45,62,0|o51svz,45,62,0|o51sw0,46,63,1|ofum3z,46,63,1|ofum40,45,62,0|onrvjz,45,62,0|onrvk0,46,63,1|oykorz,46,63,1|oykos0,45,62,0|p6hy7z,45,62,0|p6hy80,46,63,1|pharfz,46,63,1|pharg0,45,62,0|ppkzjz,45,62,0|ppkzk0,46,63,1|q00u3z,46,63,1|q00u40,45,62,0|q8b27z,45,62,0|q8b280,46,63,1|qiqwrz,46,63,1|qiqws0,45,62,0|qr14vz,45,62,0|qr14w0,46,63,1|r1ty3z,46,63,1|r1ty40,45,62,0|r9r7jz,45,62,0|r9r7k0,46,63,1|rkk0rz,46,63,1|rkk0s0,45,62,0|rsha7z,45,62,0|rsha80,46,63,1|s3a3fz,46,63,1|s3a3g0,45,62,0|sbkbjz,45,62,0|sbkbk0,46,63,1|sm063z,46,63,1|sm0640,45,62,0|suae7z,45,62,0|suae80,46,63,1|t4q8rz,46,63,1|t4q8s0,45,62,0|td0gvz,45,62,0|td0gw0,46,63,1|tngbfz,46,63,1|tngbg0,45,62,0|tvqjjz,45,62,0|tvqjk0,46,63,1|u6jcrz,46,63,1|u6jcs0,45,62,0|uegm7z,45,62,0|uegm80,46,63,1|up9ffz,46,63,1|up9fg0,45,62,0|ux6ovz,45,62,0|ux6ow0,46,63,1|v7zi3z,46,63,1|v7zi40,45,62,0|vg9q7z,45,62,0|vg9q80,46,63,1|vqpkrz,46,63,1|vqpks0,45,62,0|vyzsvz,45,62,0|vyzsw0,46,63,1|w9fnfz,46,63,1|w9fng0,45,62,0|whpvjz,45,62,0|whpvk0,46,63,1|wsiorz,46,63,1|wsios0,45,62,0|x0fy7z,45,62,0|x0fy80,46,63,1|xb8rfz,46,63,1|xb8rg0,45,62,0|xj60vz,45,62,0|xj60w0,46,63,1|xtyu3z,46,63,1|xtyu40,45,62,0|y1w3jz,45,62,0|y1w3k0,46,63,1|ycowrz,46,63,1|ycows0,45,62,0|ykz4vz,45,62,0|ykz4w0,46,63,1|yvezfz,46,63,1|yvezg0,45,62,0|z3p7jz,45,62,0|z3p7k0,46,63,1|ze523z,46,63,1|ze5240,45,62,0\",\"America/Montevideo|,0,146,0|-w4mll9,21,146,0|-px809a,21,146,0|-px8099,42,42,0|-o50vk1,42,42,0|-o50vk0,39,44,1|-nvm2c1,39,44,1|-nvm2c0,81,101,0|-nm74y1,81,101,0|-nm74y0,39,44,1|-ncu501,39,44,1|-ncu500,81,101,0|-n3f7m1,81,101,0|-n3f7m0,39,44,1|-mu27o1,39,44,1|-mu27o0,81,101,0|-ivo8y1,81,101,0|-ivo8y0,39,44,1|-inr3o1,39,44,1|-inr3o0,81,101,0|-icy6a1,81,101,0|-icy6a0,39,44,1|-i51101,39,44,1|-i51100,81,101,0|-hu83m1,81,101,0|-hu83m0,39,44,1|-hmayc1,39,44,1|-hmayc0,81,101,0|-hbi0y1,81,101,0|-hbi0y0,39,44,1|-h3kvo1,39,44,1|-h3kvo0,81,101,0|-gsezm1,81,101,0|-gsezm0,39,44,1|-gkut01,39,44,1|-gkut00,81,101,0|-g9owy1,81,101,0|-g9owy0,39,44,1|-g24qc1,39,44,1|-g24qc0,81,101,0|-fseoy1,81,101,0|-fseoy0,39,44,1|-fj1p01,39,44,1|-fj1p00,81,101,0|-f88rm1,81,101,0|-f88rm0,39,44,1|-f0bmc1,39,44,1|-f0bmc0,81,101,0|-etxya1,81,101,0|-etxya0,39,44,1|-e482c1,39,44,1|-e482c0,82,102,1|-dzlfq1,82,102,1|-dzlfq0,39,44,0|-5jbp01,39,44,0|-5jbp00,82,102,1|-5abnq1,82,102,1|-5abnq0,39,44,0|-572yc1,39,44,0|-572yc0,40,45,1|-54kag1,40,45,1|-54kag0,39,44,0|-2h5101,39,44,0|-2h5100,40,45,1|-285141,40,45,1|-285140,39,44,0|-u1901,39,44,0|-u1900,82,102,1|-kd521,82,102,1|-kd520,39,44,0|5vcbz,39,44,0|5vcc0,40,45,1|8fuvz,40,45,1|8fuw0,39,44,0|17dcbz,39,44,0|17dcc0,40,45,1|1botjz,40,45,1|1botk0,39,44,0|23s0bz,39,44,0|23s0c0,83,147,1|26nlhz,83,147,1|26nli0,82,102,1|2fnqxz,82,102,1|2fnqy0,39,44,0|2lf6zz,39,44,0|2lf700,40,45,1|2qgljz,40,45,1|2qglk0,39,44,0|3mvcbz,39,44,0|3mvcc0,40,45,1|3qtuvz,40,45,1|3qtuw0,39,44,0|44vhnz,39,44,0|44vho0,40,45,1|49jxjz,40,45,1|49jxk0,39,44,0|4obhnz,39,44,0|4obho0,40,45,1|4sa07z,40,45,1|4sa080,39,44,0|4v5sbz,39,44,0|4v5sc0,40,45,1|5bq07z,40,45,1|5bq080,39,44,0|9d8yzz,39,44,0|9d8z00,40,45,1|9h5mvz,40,45,1|9h5mw0,39,44,0|9vx6zz,39,44,0|9vx700,40,45,1|a08o7z,40,45,1|a08o80,39,44,0|achhnz,39,44,0|achho0,40,45,1|ails7z,40,45,1|ails80,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b1otjz,40,45,1|b1otk0,39,44,0|bdxmzz,39,44,0|bdxn00,40,45,1|bkew7z,40,45,1|bkew80,39,44,0|bwaqzz,39,44,0|bwar00,40,45,1|c34yvz,40,45,1|c34yw0,39,44,0|i49pnz,39,44,0|i49po0,40,45,1|idzsfz,40,45,1|idzsg0,39,44,0|io2tvz,39,44,0|io2tw0,40,45,1|ivzxrz,40,45,1|ivzxs0,39,44,0|j6fxvz,39,44,0|j6fxw0,40,45,1|jeq0fz,40,45,1|jeq0g0,39,44,0|jpiz7z,39,44,0|jpiz80,40,45,1|jxg33z,40,45,1|jxg340,39,44,0|k891vz,39,44,0|k891w0,40,45,1|kg65rz,40,45,1|kg65s0,39,44,0|kqz4jz,39,44,0|kqz4k0,40,45,1|kz973z,40,45,1|kz9740,39,44,0|l9p77z,39,44,0|l9p780,40,45,1|lhz9rz,40,45,1|lhz9s0,39,44,0|lsf9vz,39,44,0|lsf9w0,40,45,1|m0pcfz,40,45,1|m0pcg0,39,44,0|mbib7z,39,44,0|mbib80,40,45,1|mjff3z,40,45,1|mjff40,39,44,0|mu8dvz,39,44,0|mu8dw0,40,45,1|n25hrz,40,45,1|n25hs0,39,44,0|ncygjz,39,44,0|ncygk0,40,45,1|nkvkfz,40,45,1|nkvkg0,39,44,0\",\"America/Montserrat|,0,41,0|-u6m79w,32,42,0\",\"America/Nassau|,0,148,0|-u6m4c6,49,63,0|-efufg1,49,63,0|-efufg0,70,42,1|-d1oy81,70,42,1|-d1oy80,49,63,0|-d03gs1,49,63,0|-d03gs0,70,42,1|-cq2tg1,70,42,1|-cq2tg0,71,42,1|-cmrww1,71,42,1|-cmrww0,49,63,0|-2yrwk1,49,63,0|-2yrwk0,62,42,1|-2pey01,62,42,1|-2pey00,49,63,0|-2g1tw1,49,63,0|-2g1tw0,62,42,1|-26bwo1,62,42,1|-26bwo0,49,63,0|-1xbr81,49,63,0|-1xbr80,62,42,1|-1nlu01,62,42,1|-1nlu00,49,63,0|-1e8pw1,49,63,0|-1e8pw0,62,42,1|-14vrc1,62,42,1|-14vrc0,49,63,0|-vin81,49,63,0|-vin80,62,42,1|-m5oo1,62,42,1|-m5oo0,49,63,0|-cskk1,49,63,0|-cskk0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|onkrz,49,63,0|onks0,62,42,1|ydhzz,62,42,1|ydi00,49,63,0|17qm3z,49,63,0|17qm40,62,42,1|1h3knz,62,42,1|1h3ko0,49,63,0|1qgorz,49,63,0|1qgos0,62,42,1|1ztnbz,62,42,1|1ztnc0,49,63,0|296rfz,49,63,0|296rg0,62,42,1|2ijpzz,62,42,1|2ijq00,49,63,0|2rwu3z,49,63,0|2rwu40,62,42,1|319snz,62,42,1|319so0,49,63,0|3amwrz,49,63,0|3amws0,62,42,1|3kctzz,62,42,1|3kcu00,49,63,0|3tczfz,49,63,0|3tczg0,62,42,1|432wnz,62,42,1|432wo0,49,63,0|4cg0rz,49,63,0|4cg0s0,62,42,1|4lszbz,62,42,1|4lszc0,49,63,0|4v63fz,49,63,0|4v63g0,62,42,1|54j1zz,62,42,1|54j200,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0|7h5ffz,49,63,0|7h5fg0,62,42,1|7qidzz,62,42,1|7qie00,49,63,0|7zvi3z,49,63,0|7zvi40,62,42,1|898gnz,62,42,1|898go0,49,63,0|8ilkrz,49,63,0|8ilks0,62,42,1|8ryjbz,62,42,1|8ryjc0,49,63,0|908rfz,49,63,0|908rg0,62,42,1|9aolzz,62,42,1|9aom00,49,63,0|9iyu3z,49,63,0|9iyu40,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1owrz,49,63,0|a1ows0,62,42,1|achpzz,62,42,1|achq00,49,63,0|akezfz,49,63,0|akezg0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3i0rz,49,63,0|b3i0s0,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm83fz,49,63,0|bm83g0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y63z,49,63,0|c4y640,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno8rz,49,63,0|cno8s0,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6ebfz,49,63,0|d6ebg0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,49,63,0|fsdnfz,49,63,0|fsdng0,62,42,1|g36gnz,62,42,1|g36go0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/New_York|,0,149,0|-18y0os0,49,63,0|-r0ev81,49,63,0|-r0ev80,62,42,1|-qpm201,62,42,1|-qpm200,49,63,0|-qhosk1,49,63,0|-qhosk0,62,42,1|-q6vzc1,62,42,1|-q6vzc0,49,63,0|-pyypw1,49,63,0|-pyypw0,62,42,1|-pnsy01,62,42,1|-pnsy00,49,63,0|-pessk1,49,63,0|-pessk0,62,42,1|-p6voo1,62,42,1|-p6voo0,49,63,0|-ovpr81,49,63,0|-ovpr80,62,42,1|-oo5m01,62,42,1|-oo5m00,49,63,0|-oczok1,49,63,0|-oczok0,62,42,1|-o52ko1,62,42,1|-o52ko0,49,63,0|-nu9lw1,49,63,0|-nu9lw0,62,42,1|-nmci01,62,42,1|-nmci00,49,63,0|-nbjj81,49,63,0|-nbjj80,62,42,1|-n3mfc1,62,42,1|-n3mfc0,49,63,0|-mstgk1,49,63,0|-mstgk0,62,42,1|-mkwco1,62,42,1|-mkwco0,49,63,0|-ma3dw1,49,63,0|-ma3dw0,62,42,1|-m26a01,62,42,1|-m26a00,49,63,0|-lr0ck1,49,63,0|-lr0ck0,62,42,1|-lj38o1,62,42,1|-lj38o0,49,63,0|-l8a9w1,49,63,0|-l8a9w0,62,42,1|-l0d601,62,42,1|-l0d600,49,63,0|-kpk781,49,63,0|-kpk780,62,42,1|-khn3c1,62,42,1|-khn3c0,49,63,0|-k6u4k1,49,63,0|-k6u4k0,62,42,1|-jyx0o1,62,42,1|-jyx0o0,49,63,0|-jo41w1,49,63,0|-jo41w0,62,42,1|-jg6y01,62,42,1|-jg6y00,49,63,0|-j510k1,49,63,0|-j510k0,62,42,1|-ixgvc1,62,42,1|-ixgvc0,49,63,0|-imaxw1,49,63,0|-imaxw0,62,42,1|-iedu01,62,42,1|-iedu00,49,63,0|-i3kv81,49,63,0|-i3kv80,62,42,1|-hvnrc1,62,42,1|-hvnrc0,49,63,0|-hkusk1,49,63,0|-hkusk0,62,42,1|-hcxoo1,62,42,1|-hcxoo0,49,63,0|-h24pw1,49,63,0|-h24pw0,62,42,1|-gu7m01,62,42,1|-gu7m00,49,63,0|-gjen81,49,63,0|-gjen80,62,42,1|-gbhjc1,62,42,1|-gbhjc0,49,63,0|-g0blw1,49,63,0|-g0blw0,62,42,1|-fsrgo1,62,42,1|-fsrgo0,49,63,0|-fhlj81,49,63,0|-fhlj80,62,42,1|-f9ofc1,62,42,1|-f9ofc0,49,63,0|-eyvgk1,49,63,0|-eyvgk0,62,42,1|-eqyco1,62,42,1|-eqyco0,49,63,0|-ek24k1,49,63,0|-ek24k0,70,42,1|-cq2tg1,70,42,1|-cq2tg0,71,42,1|-cnp3c1,71,42,1|-cnp3c0,49,63,0|-ccw4k1,49,63,0|-ccw4k0,62,42,1|-c4z0o1,62,42,1|-c4z0o0,49,63,0|-bu61w1,49,63,0|-bu61w0,62,42,1|-bm8y01,62,42,1|-bm8y00,49,63,0|-bbfz81,49,63,0|-bbfz80,62,42,1|-b3ivc1,62,42,1|-b3ivc0,49,63,0|-aspwk1,49,63,0|-aspwk0,62,42,1|-aksso1,62,42,1|-aksso0,49,63,0|-a9mv81,49,63,0|-a9mv80,62,42,1|-a22q01,62,42,1|-a22q00,49,63,0|-9qwsk1,49,63,0|-9qwsk0,62,42,1|-9izoo1,62,42,1|-9izoo0,49,63,0|-986pw1,49,63,0|-986pw0,62,42,1|-909m01,62,42,1|-909m00,49,63,0|-8pgn81,49,63,0|-8pgn80,62,42,1|-8hjjc1,62,42,1|-8hjjc0,49,63,0|-86qkk1,49,63,0|-86qkk0,62,42,1|-7ytgo1,62,42,1|-7ytgo0,49,63,0|-7o0hw1,49,63,0|-7o0hw0,62,42,1|-7eako1,62,42,1|-7eako0,49,63,0|-74xgk1,49,63,0|-74xgk0,62,42,1|-6vki01,62,42,1|-6vki00,49,63,0|-6m7dw1,49,63,0|-6m7dw0,62,42,1|-6cufc1,62,42,1|-6cufc0,49,63,0|-63hb81,49,63,0|-63hb80,62,42,1|-5u4co1,62,42,1|-5u4co0,49,63,0|-5kr8k1,49,63,0|-5kr8k0,62,42,1|-5bea01,62,42,1|-5bea00,49,63,0|-5215w1,49,63,0|-5215w0,62,42,1|-4sb8o1,62,42,1|-4sb8o0,49,63,0|-4iy4k1,49,63,0|-4iy4k0,62,42,1|-49l601,62,42,1|-49l600,49,63,0|-4081w1,49,63,0|-4081w0,62,42,1|-3qv3c1,62,42,1|-3qv3c0,49,63,0|-3hhz81,49,63,0|-3hhz80,62,42,1|-3850o1,62,42,1|-3850o0,49,63,0|-2yrwk1,49,63,0|-2yrwk0,62,42,1|-2pey01,62,42,1|-2pey00,49,63,0|-2g1tw1,49,63,0|-2g1tw0,62,42,1|-26bwo1,62,42,1|-26bwo0,49,63,0|-1xbr81,49,63,0|-1xbr80,62,42,1|-1nlu01,62,42,1|-1nlu00,49,63,0|-1e8pw1,49,63,0|-1e8pw0,62,42,1|-14vrc1,62,42,1|-14vrc0,49,63,0|-vin81,49,63,0|-vin80,62,42,1|-m5oo1,62,42,1|-m5oo0,49,63,0|-cskk1,49,63,0|-cskk0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|onkrz,49,63,0|onks0,62,42,1|ydhzz,62,42,1|ydi00,49,63,0|17qm3z,49,63,0|17qm40,62,42,1|1h3knz,62,42,1|1h3ko0,49,63,0|1qgorz,49,63,0|1qgos0,62,42,1|1ztnbz,62,42,1|1ztnc0,49,63,0|23fcrz,49,63,0|23fcs0,62,42,1|2ijpzz,62,42,1|2ijq00,49,63,0|2oo63z,49,63,0|2oo640,62,42,1|319snz,62,42,1|319so0,49,63,0|3amwrz,49,63,0|3amws0,62,42,1|3kctzz,62,42,1|3kcu00,49,63,0|3tczfz,49,63,0|3tczg0,62,42,1|432wnz,62,42,1|432wo0,49,63,0|4cg0rz,49,63,0|4cg0s0,62,42,1|4lszbz,62,42,1|4lszc0,49,63,0|4v63fz,49,63,0|4v63g0,62,42,1|54j1zz,62,42,1|54j200,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0|7h5ffz,49,63,0|7h5fg0,62,42,1|7qidzz,62,42,1|7qie00,49,63,0|7zvi3z,49,63,0|7zvi40,62,42,1|898gnz,62,42,1|898go0,49,63,0|8ilkrz,49,63,0|8ilks0,62,42,1|8ryjbz,62,42,1|8ryjc0,49,63,0|908rfz,49,63,0|908rg0,62,42,1|9aolzz,62,42,1|9aom00,49,63,0|9iyu3z,49,63,0|9iyu40,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1owrz,49,63,0|a1ows0,62,42,1|achpzz,62,42,1|achq00,49,63,0|akezfz,49,63,0|akezg0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3i0rz,49,63,0|b3i0s0,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm83fz,49,63,0|bm83g0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y63z,49,63,0|c4y640,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno8rz,49,63,0|cno8s0,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6ebfz,49,63,0|d6ebg0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,49,63,0|fsdnfz,49,63,0|fsdng0,62,42,1|g36gnz,62,42,1|g36go0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Nipigon|,0,150,0|-1353bnk,49,63,0|-qzoxw1,49,63,0|-qzoxw0,62,42,1|-qpm201,62,42,1|-qpm200,49,63,0|-f9oi41,49,63,0|-f9oi40,62,42,1|-ek24k1,62,42,1|-ek24k0,70,42,1|-cq2tg1,70,42,1|-cq2tg0,71,42,1|-cnp3c1,71,42,1|-cnp3c0,49,63,0|296rfz,49,63,0|296rg0,62,42,1|2ijpzz,62,42,1|2ijq00,49,63,0|2rwu3z,49,63,0|2rwu40,62,42,1|319snz,62,42,1|319so0,49,63,0|3amwrz,49,63,0|3amws0,62,42,1|3kctzz,62,42,1|3kcu00,49,63,0|3tczfz,49,63,0|3tczg0,62,42,1|432wnz,62,42,1|432wo0,49,63,0|4cg0rz,49,63,0|4cg0s0,62,42,1|4lszbz,62,42,1|4lszc0,49,63,0|4v63fz,49,63,0|4v63g0,62,42,1|54j1zz,62,42,1|54j200,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0|7h5ffz,49,63,0|7h5fg0,62,42,1|7qidzz,62,42,1|7qie00,49,63,0|7zvi3z,49,63,0|7zvi40,62,42,1|898gnz,62,42,1|898go0,49,63,0|8ilkrz,49,63,0|8ilks0,62,42,1|8ryjbz,62,42,1|8ryjc0,49,63,0|908rfz,49,63,0|908rg0,62,42,1|9aolzz,62,42,1|9aom00,49,63,0|9iyu3z,49,63,0|9iyu40,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1owrz,49,63,0|a1ows0,62,42,1|achpzz,62,42,1|achq00,49,63,0|akezfz,49,63,0|akezg0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3i0rz,49,63,0|b3i0s0,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm83fz,49,63,0|bm83g0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y63z,49,63,0|c4y640,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno8rz,49,63,0|cno8s0,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6ebfz,49,63,0|d6ebg0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,49,63,0|fsdnfz,49,63,0|fsdng0,62,42,1|g36gnz,62,42,1|g36go0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Nome|,0,151,0|-1hc7qjz,0,152,0|-1078qpb,0,152,0|-1078qpa,24,35,0|-ek1nw1,24,35,0|-ek1nw0,25,36,1|-cq2tg1,25,36,1|-cq2tg0,26,36,1|-cnomo1,26,36,1|-cnomo0,24,35,0|-1fq441,24,35,0|-1fq440,27,35,0|-cs3w1,27,35,0|-cs3w0,28,36,1|-3f5c1,28,36,1|-3f5c0,27,35,0|5xyrz,27,35,0|5xys0,28,36,1|faxbz,28,36,1|faxc0,27,35,0|oo1fz,27,35,0|oo1g0,28,36,1|ydynz,28,36,1|ydyo0,27,35,0|17r2rz,27,35,0|17r2s0,28,36,1|1h41bz,28,36,1|1h41c0,27,35,0|1qh5fz,27,35,0|1qh5g0,28,36,1|1zu3zz,28,36,1|1zu400,27,35,0|23ftfz,27,35,0|23ftg0,28,36,1|2ik6nz,28,36,1|2ik6o0,27,35,0|2oomrz,27,35,0|2ooms0,28,36,1|31a9bz,28,36,1|31a9c0,27,35,0|3andfz,27,35,0|3andg0,28,36,1|3kdanz,28,36,1|3kdao0,27,35,0|3tdg3z,27,35,0|3tdg40,28,36,1|433dbz,28,36,1|433dc0,27,35,0|4cghfz,27,35,0|4cghg0,28,36,1|4ltfzz,28,36,1|4ltg00,27,35,0|4v6k3z,27,35,0|4v6k40,28,36,1|54jinz,28,36,1|54jio0,27,35,0|5dwmrz,27,35,0|5dwms0,28,36,1|5n9lbz,28,36,1|5n9lc0,27,35,0|5wmpfz,27,35,0|5wmpg0,28,36,1|65znzz,28,36,1|65zo00,27,35,0|6fcs3z,27,35,0|6fcs40,28,36,1|6p2pbz,28,36,1|6p2pc0,27,35,0|6y2urz,27,35,0|6y2us0,28,36,1|77srzz,28,36,1|77ss00,36,37,0|79dybz,36,37,0|79dyc0,37,37,0|7h5qjz,37,37,0|7h5qk0,38,40,1|7qip3z,38,40,1|7qip40,37,37,0|7zvt7z,37,37,0|7zvt80,38,40,1|898rrz,38,40,1|898rs0,37,37,0|8ilvvz,37,37,0|8ilvw0,38,40,1|8ryufz,38,40,1|8ryug0,37,37,0|9092jz,37,37,0|9092k0,38,40,1|9aox3z,38,40,1|9aox40,37,37,0|9iz57z,37,37,0|9iz580,38,40,1|9tryfz,38,40,1|9tryg0,37,37,0|a1p7vz,37,37,0|a1p7w0,38,40,1|aci13z,38,40,1|aci140,37,37,0|akfajz,37,37,0|akfak0,38,40,1|av83rz,38,40,1|av83s0,37,37,0|b3ibvz,37,37,0|b3ibw0,38,40,1|bdy6fz,38,40,1|bdy6g0,37,37,0|bm8ejz,37,37,0|bm8ek0,38,40,1|bwo93z,38,40,1|bwo940,37,37,0|c4yh7z,37,37,0|c4yh80,38,40,1|cfrafz,38,40,1|cfrag0,37,37,0|cnojvz,37,37,0|cnojw0,38,40,1|cyhd3z,38,40,1|cyhd40,37,37,0|d6emjz,37,37,0|d6emk0,38,40,1|dh7frz,38,40,1|dh7fs0,37,37,0|dphnvz,37,37,0|dphnw0,38,40,1|dzxifz,38,40,1|dzxig0,37,37,0|e87qjz,37,37,0|e87qk0,38,40,1|einl3z,38,40,1|einl40,37,37,0|eqxt7z,37,37,0|eqxt80,38,40,1|f1dnrz,38,40,1|f1dns0,37,37,0|f9nvvz,37,37,0|f9nvw0,38,40,1|fkgp3z,38,40,1|fkgp40,37,37,0|fsdyjz,37,37,0|fsdyk0,38,40,1|g36rrz,38,40,1|g36rs0,37,37,0|gb417z,37,37,0|gb4180,38,40,1|glwufz,38,40,1|glwug0,37,37,0|gu72jz,37,37,0|gu72k0,38,40,1|h4mx3z,38,40,1|h4mx40,37,37,0|hcx57z,37,37,0|hcx580,38,40,1|hnczrz,38,40,1|hnczs0,37,37,0|hvn7vz,37,37,0|hvn7w0,38,40,1|i6g13z,38,40,1|i6g140,37,37,0|iedajz,37,37,0|iedak0,38,40,1|ip63rz,38,40,1|ip63s0,37,37,0|ix3d7z,37,37,0|ix3d80,38,40,1|j7w6fz,38,40,1|j7w6g0,37,37,0|jeqjvz,37,37,0|jeqjw0,38,40,1|jqz7rz,38,40,1|jqz7s0,37,37,0|jxgmjz,37,37,0|jxgmk0,38,40,1|k9pafz,38,40,1|k9pag0,37,37,0|kg6p7z,37,37,0|kg6p80,38,40,1|ksfd3z,38,40,1|ksfd40,37,37,0|kz9qjz,37,37,0|kz9qk0,38,40,1|lbiefz,38,40,1|lbieg0,37,37,0|lhzt7z,37,37,0|lhzt80,38,40,1|lu8h3z,38,40,1|lu8h40,37,37,0|m0pvvz,37,37,0|m0pvw0,38,40,1|mcyjrz,38,40,1|mcyjs0,37,37,0|mjfyjz,37,37,0|mjfyk0,38,40,1|mvomfz,38,40,1|mvomg0,37,37,0|n2617z,37,37,0|n26180,38,40,1|neep3z,38,40,1|neep40,37,37,0|nkw3vz,37,37,0|nkw3w0,38,40,1|nx4rrz,38,40,1|nx4rs0,37,37,0|o3z57z,37,37,0|o3z580,38,40,1|og7t3z,38,40,1|og7t40,37,37,0|omp7vz,37,37,0|omp7w0,38,40,1|oyxvrz,38,40,1|oyxvs0,37,37,0|p5fajz,37,37,0|p5fak0,38,40,1|phnyfz,38,40,1|phnyg0,37,37,0|po5d7z,37,37,0|po5d80,38,40,1|q0e13z,38,40,1|q0e140,37,37,0|q6vfvz,37,37,0|q6vfw0,38,40,1|qj43rz,38,40,1|qj43s0,37,37,0|qpyh7z,37,37,0|qpyh80,38,40,1|r2753z,38,40,1|r27540,37,37,0|r8ojvz,37,37,0|r8ojw0,38,40,1|rkx7rz,38,40,1|rkx7s0,37,37,0|rremjz,37,37,0|rremk0,38,40,1|s3nafz,38,40,1|s3nag0,37,37,0|sa4p7z,37,37,0|sa4p80,38,40,1|smdd3z,38,40,1|smdd40,37,37,0|ssurvz,37,37,0|ssurw0,38,40,1|t53frz,38,40,1|t53fs0,37,37,0|tbkujz,37,37,0|tbkuk0,38,40,1|tntifz,38,40,1|tntig0,37,37,0|tunvvz,37,37,0|tunvw0,38,40,1|u6wjrz,38,40,1|u6wjs0,37,37,0|uddyjz,37,37,0|uddyk0,38,40,1|upmmfz,38,40,1|upmmg0,37,37,0|uw417z,37,37,0|uw4180,38,40,1|v8cp3z,38,40,1|v8cp40,37,37,0|veu3vz,37,37,0|veu3w0,38,40,1|vr2rrz,38,40,1|vr2rs0,37,37,0|vxk6jz,37,37,0|vxk6k0,38,40,1|w9sufz,38,40,1|w9sug0,37,37,0|wgn7vz,37,37,0|wgn7w0,38,40,1|wsvvrz,38,40,1|wsvvs0,37,37,0|wzdajz,37,37,0|wzdak0,38,40,1|xblyfz,38,40,1|xblyg0,37,37,0|xi3d7z,37,37,0|xi3d80,38,40,1|xuc13z,38,40,1|xuc140,37,37,0|y0tfvz,37,37,0|y0tfw0,38,40,1|yd23rz,38,40,1|yd23s0,37,37,0|yjjijz,37,37,0|yjjik0,38,40,1|yvs6fz,38,40,1|yvs6g0,37,37,0|z29l7z,37,37,0|z29l80,38,40,1|zei93z,38,40,1|zei940,37,37,0\",\"America/Noronha|,0,153,0|-t85lzw,40,45,0|-jyld81,40,45,0|-jyld80,13,15,1|-jpb3w1,13,15,1|-jpb3w0,40,45,0|-jfsfs1,40,45,0|-jfsfs0,13,15,1|-j6j6k1,13,15,1|-j6j6k0,40,45,0|-ahd141,40,45,0|-ahd140,13,15,1|-aad6g1,13,15,1|-aad6g0,40,45,0|-9yl3s1,40,45,0|-9yl3s0,13,15,1|-9sd3w1,13,15,1|-9sd3w0,40,45,0|-9ft6g1,40,45,0|-9ft6g0,13,15,1|-99jbw1,13,15,1|-99jbw0,40,45,0|-8wzeg1,40,45,0|-8wzeg0,13,15,1|-8sct81,13,15,1|-8sct80,40,45,0|-35xp41,40,45,0|-35xp40,13,15,1|-31o2k1,13,15,1|-31o2k0,40,45,0|-2kdrs1,40,45,0|-2kdrs0,13,15,1|-2hcl81,13,15,1|-2hcl80,40,45,0|-24qt41,40,45,0|-24qt40,13,15,1|-2047w1,13,15,1|-2047w0,40,45,0|-1nifs1,40,45,0|-1nifs0,13,15,1|-1hcak1,13,15,1|-1hcak0,40,45,0|-14qig1,40,45,0|-14qig0,13,15,1|-yiik1,13,15,1|-yiik0,40,45,0|89j9jz,40,45,0|89j9k0,13,15,1|8gdhfz,13,15,1|8gdhg0,40,45,0|8rwdjz,40,45,0|8rwdk0,13,15,1|8xnpfz,13,15,1|8xnpg0,40,45,0|9aoavz,40,45,0|9aoaw0,13,15,1|9g2o3z,13,15,1|9g2o40,40,45,0|9t1evz,40,45,0|9t1ew0,13,15,1|9yfs3z,13,15,1|9yfs40,40,45,0|abrhjz,40,45,0|abrhk0,13,15,1|ahvs3z,13,15,1|ahvs40,40,45,0|fj087z,40,45,0|fj0880,13,15,1|fqkdfz,13,15,1|fqkdg0,40,45,0|g239jz,40,45,0|g239k0,13,15,1|g2g5fz,13,15,1|g2g5g0,40,45,0|gl6avz,40,45,0|gl6aw0,13,15,1|grnk3z,13,15,1|grnk40,40,45,0\",\"America/North_Dakota/Beulah|,0,154,0|-18y0j80,50,66,0|-r0epo1,50,66,0|-r0epo0,52,62,1|-qplwg1,52,62,1|-qplwg0,50,66,0|-qhon01,50,66,0|-qhon00,52,62,1|-q6vts1,52,62,1|-q6vts0,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-1e8kc1,50,66,0|-1e8kc0,52,62,1|-14vls1,52,62,1|-14vls0,50,66,0|-viho1,50,66,0|-viho0,52,62,1|-m5j41,52,62,1|-m5j40,50,66,0|-csf01,50,66,0|-csf00,52,62,1|-3fgg1,52,62,1|-3fgg0,50,66,0|5xnnz,50,66,0|5xno0,52,62,1|fam7z,52,62,1|fam80,50,66,0|onqbz,50,66,0|onqc0,52,62,1|ydnjz,52,62,1|ydnk0,50,66,0|17qrnz,50,66,0|17qro0,52,62,1|1h3q7z,52,62,1|1h3q80,50,66,0|1qgubz,50,66,0|1qguc0,52,62,1|1ztsvz,52,62,1|1ztsw0,50,66,0|23fibz,50,66,0|23fic0,52,62,1|2ijvjz,52,62,1|2ijvk0,50,66,0|2oobnz,50,66,0|2oobo0,52,62,1|319y7z,52,62,1|319y80,50,66,0|3an2bz,50,66,0|3an2c0,52,62,1|3kczjz,52,62,1|3kczk0,50,66,0|3td4zz,50,66,0|3td500,52,62,1|43327z,52,62,1|433280,50,66,0|4cg6bz,50,66,0|4cg6c0,52,62,1|4lt4vz,52,62,1|4lt4w0,50,66,0|4v68zz,50,66,0|4v6900,52,62,1|54j7jz,52,62,1|54j7k0,50,66,0|5dwbnz,50,66,0|5dwbo0,52,62,1|5n9a7z,52,62,1|5n9a80,50,66,0|5wmebz,50,66,0|5wmec0,52,62,1|65zcvz,52,62,1|65zcw0,50,66,0|6fcgzz,50,66,0|6fch00,52,62,1|6p2e7z,52,62,1|6p2e80,50,66,0|6y2jnz,50,66,0|6y2jo0,52,62,1|77sgvz,52,62,1|77sgw0,50,66,0|7h5kzz,50,66,0|7h5l00,52,62,1|7qijjz,52,62,1|7qijk0,50,66,0|7zvnnz,50,66,0|7zvno0,52,62,1|898m7z,52,62,1|898m80,50,66,0|8ilqbz,50,66,0|8ilqc0,52,62,1|8ryovz,52,62,1|8ryow0,50,66,0|908wzz,50,66,0|908x00,52,62,1|9aorjz,52,62,1|9aork0,50,66,0|9iyznz,50,66,0|9iyzo0,52,62,1|9trsvz,52,62,1|9trsw0,50,66,0|a1p2bz,50,66,0|a1p2c0,52,62,1|achvjz,52,62,1|achvk0,50,66,0|akf4zz,50,66,0|akf500,52,62,1|av7y7z,52,62,1|av7y80,50,66,0|b3i6bz,50,66,0|b3i6c0,52,62,1|bdy0vz,52,62,1|bdy0w0,50,66,0|bm88zz,50,66,0|bm8900,52,62,1|bwo3jz,52,62,1|bwo3k0,50,66,0|c4ybnz,50,66,0|c4ybo0,52,62,1|cfr4vz,52,62,1|cfr4w0,50,66,0|cnoebz,50,66,0|cnoec0,52,62,1|cyh7jz,52,62,1|cyh7k0,50,66,0|d6egzz,50,66,0|d6eh00,52,62,1|dh7a7z,52,62,1|dh7a80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gb3vnz,50,66,0|gb3vo0,52,62,1|glwovz,52,62,1|glwow0,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jeqebz,50,66,0|jeqec0,52,62,1|jqz27z,52,62,1|jqz280,50,66,0|jxggzz,50,66,0|jxgh00,52,62,1|k9p4vz,52,62,1|k9p4w0,50,66,0|kg6jnz,50,66,0|kg6jo0,52,62,1|ksf7jz,52,62,1|ksf7k0,50,66,0|kz9kzz,50,66,0|kz9l00,52,62,1|lbi8vz,52,62,1|lbi8w0,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/North_Dakota/Center|,0,155,0|-18y0j80,50,66,0|-r0epo1,50,66,0|-r0epo0,52,62,1|-qplwg1,52,62,1|-qplwg0,50,66,0|-qhon01,50,66,0|-qhon00,52,62,1|-q6vts1,52,62,1|-q6vts0,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-1e8kc1,50,66,0|-1e8kc0,52,62,1|-14vls1,52,62,1|-14vls0,50,66,0|-viho1,50,66,0|-viho0,52,62,1|-m5j41,52,62,1|-m5j40,50,66,0|-csf01,50,66,0|-csf00,52,62,1|-3fgg1,52,62,1|-3fgg0,50,66,0|5xnnz,50,66,0|5xno0,52,62,1|fam7z,52,62,1|fam80,50,66,0|onqbz,50,66,0|onqc0,52,62,1|ydnjz,52,62,1|ydnk0,50,66,0|17qrnz,50,66,0|17qro0,52,62,1|1h3q7z,52,62,1|1h3q80,50,66,0|1qgubz,50,66,0|1qguc0,52,62,1|1ztsvz,52,62,1|1ztsw0,50,66,0|23fibz,50,66,0|23fic0,52,62,1|2ijvjz,52,62,1|2ijvk0,50,66,0|2oobnz,50,66,0|2oobo0,52,62,1|319y7z,52,62,1|319y80,50,66,0|3an2bz,50,66,0|3an2c0,52,62,1|3kczjz,52,62,1|3kczk0,50,66,0|3td4zz,50,66,0|3td500,52,62,1|43327z,52,62,1|433280,50,66,0|4cg6bz,50,66,0|4cg6c0,52,62,1|4lt4vz,52,62,1|4lt4w0,50,66,0|4v68zz,50,66,0|4v6900,52,62,1|54j7jz,52,62,1|54j7k0,50,66,0|5dwbnz,50,66,0|5dwbo0,52,62,1|5n9a7z,52,62,1|5n9a80,50,66,0|5wmebz,50,66,0|5wmec0,52,62,1|65zcvz,52,62,1|65zcw0,50,66,0|6fcgzz,50,66,0|6fch00,52,62,1|6p2e7z,52,62,1|6p2e80,50,66,0|6y2jnz,50,66,0|6y2jo0,52,62,1|77sgvz,52,62,1|77sgw0,50,66,0|7h5kzz,50,66,0|7h5l00,52,62,1|7qijjz,52,62,1|7qijk0,50,66,0|7zvnnz,50,66,0|7zvno0,52,62,1|898m7z,52,62,1|898m80,50,66,0|8ilqbz,50,66,0|8ilqc0,52,62,1|8ryovz,52,62,1|8ryow0,50,66,0|908wzz,50,66,0|908x00,52,62,1|9aorjz,52,62,1|9aork0,50,66,0|9iyznz,50,66,0|9iyzo0,52,62,1|9trsvz,52,62,1|9trsw0,50,66,0|a1p2bz,50,66,0|a1p2c0,52,62,1|achvjz,52,62,1|achvk0,50,66,0|akf4zz,50,66,0|akf500,52,62,1|av7y7z,52,62,1|av7y80,50,66,0|b3i6bz,50,66,0|b3i6c0,52,62,1|bdy0vz,52,62,1|bdy0w0,50,66,0|bm88zz,50,66,0|bm8900,52,62,1|bwo3jz,52,62,1|bwo3k0,45,62,0|c4y8vz,45,62,0|c4y8w0,46,63,1|cfr23z,46,63,1|cfr240,45,62,0|cnobjz,45,62,0|cnobk0,46,63,1|cyh4rz,46,63,1|cyh4s0,45,62,0|d6ee7z,45,62,0|d6ee80,46,63,1|dh77fz,46,63,1|dh77g0,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,45,62,0|gb3svz,45,62,0|gb3sw0,46,63,1|glwm3z,46,63,1|glwm40,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/North_Dakota/New_Salem|,0,156,0|-18y0j80,50,66,0|-r0epo1,50,66,0|-r0epo0,52,62,1|-qplwg1,52,62,1|-qplwg0,50,66,0|-qhon01,50,66,0|-qhon00,52,62,1|-q6vts1,52,62,1|-q6vts0,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-1e8kc1,50,66,0|-1e8kc0,52,62,1|-14vls1,52,62,1|-14vls0,50,66,0|-viho1,50,66,0|-viho0,52,62,1|-m5j41,52,62,1|-m5j40,50,66,0|-csf01,50,66,0|-csf00,52,62,1|-3fgg1,52,62,1|-3fgg0,50,66,0|5xnnz,50,66,0|5xno0,52,62,1|fam7z,52,62,1|fam80,50,66,0|onqbz,50,66,0|onqc0,52,62,1|ydnjz,52,62,1|ydnk0,50,66,0|17qrnz,50,66,0|17qro0,52,62,1|1h3q7z,52,62,1|1h3q80,50,66,0|1qgubz,50,66,0|1qguc0,52,62,1|1ztsvz,52,62,1|1ztsw0,50,66,0|23fibz,50,66,0|23fic0,52,62,1|2ijvjz,52,62,1|2ijvk0,50,66,0|2oobnz,50,66,0|2oobo0,52,62,1|319y7z,52,62,1|319y80,50,66,0|3an2bz,50,66,0|3an2c0,52,62,1|3kczjz,52,62,1|3kczk0,50,66,0|3td4zz,50,66,0|3td500,52,62,1|43327z,52,62,1|433280,50,66,0|4cg6bz,50,66,0|4cg6c0,52,62,1|4lt4vz,52,62,1|4lt4w0,50,66,0|4v68zz,50,66,0|4v6900,52,62,1|54j7jz,52,62,1|54j7k0,50,66,0|5dwbnz,50,66,0|5dwbo0,52,62,1|5n9a7z,52,62,1|5n9a80,50,66,0|5wmebz,50,66,0|5wmec0,52,62,1|65zcvz,52,62,1|65zcw0,50,66,0|6fcgzz,50,66,0|6fch00,52,62,1|6p2e7z,52,62,1|6p2e80,50,66,0|6y2jnz,50,66,0|6y2jo0,52,62,1|77sgvz,52,62,1|77sgw0,50,66,0|7h5kzz,50,66,0|7h5l00,52,62,1|7qijjz,52,62,1|7qijk0,50,66,0|7zvnnz,50,66,0|7zvno0,52,62,1|898m7z,52,62,1|898m80,50,66,0|8ilqbz,50,66,0|8ilqc0,52,62,1|8ryovz,52,62,1|8ryow0,50,66,0|908wzz,50,66,0|908x00,52,62,1|9aorjz,52,62,1|9aork0,50,66,0|9iyznz,50,66,0|9iyzo0,52,62,1|9trsvz,52,62,1|9trsw0,50,66,0|a1p2bz,50,66,0|a1p2c0,52,62,1|achvjz,52,62,1|achvk0,50,66,0|akf4zz,50,66,0|akf500,52,62,1|av7y7z,52,62,1|av7y80,50,66,0|b3i6bz,50,66,0|b3i6c0,52,62,1|bdy0vz,52,62,1|bdy0w0,50,66,0|bm88zz,50,66,0|bm8900,52,62,1|bwo3jz,52,62,1|bwo3k0,50,66,0|c4ybnz,50,66,0|c4ybo0,52,62,1|cfr4vz,52,62,1|cfr4w0,50,66,0|cnoebz,50,66,0|cnoec0,52,62,1|cyh7jz,52,62,1|cyh7k0,50,66,0|d6egzz,50,66,0|d6eh00,52,62,1|dh7a7z,52,62,1|dh7a80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gb3vnz,50,66,0|gb3vo0,52,62,1|glwovz,52,62,1|glwow0,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Nuuk|,0,157,0|-rvumf4,39,44,0|5ct4jz,39,44,0|5ct4k0,40,45,1|5lsw3z,40,45,1|5lsw40,39,44,0|5v5xfz,39,44,0|5v5xg0,40,45,1|64iyrz,40,45,1|64iys0,39,44,0|6dw03z,39,44,0|6dw040,40,45,1|6n91fz,40,45,1|6n91g0,39,44,0|6wm2rz,39,44,0|6wm2s0,40,45,1|75z43z,40,45,1|75z440,39,44,0|7fc5fz,39,44,0|7fc5g0,40,45,1|7p25fz,40,45,1|7p25g0,39,44,0|7yf6rz,39,44,0|7yf6s0,40,45,1|87s83z,40,45,1|87s840,39,44,0|8h59fz,39,44,0|8h59g0,40,45,1|8qiarz,40,45,1|8qias0,39,44,0|8zvc3z,39,44,0|8zvc40,40,45,1|998dfz,40,45,1|998dg0,39,44,0|9ilerz,39,44,0|9iles0,40,45,1|9ryg3z,40,45,1|9ryg40,39,44,0|a1bhfz,39,44,0|a1bhg0,40,45,1|aaoirz,40,45,1|aaois0,39,44,0|ak1k3z,39,44,0|ak1k40,40,45,1|atrk3z,40,45,1|atrk40,39,44,0|b34lfz,39,44,0|b34lg0,40,45,1|bchmrz,40,45,1|bchms0,39,44,0|bluo3z,39,44,0|bluo40,40,45,1|bv7pfz,40,45,1|bv7pg0,39,44,0|c4kqrz,39,44,0|c4kqs0,40,45,1|cdxs3z,40,45,1|cdxs40,39,44,0|cnatfz,39,44,0|cnatg0,40,45,1|cwnurz,40,45,1|cwnus0,39,44,0|d60w3z,39,44,0|d60w40,40,45,1|dfdxfz,40,45,1|dfdxg0,39,44,0|dp3xfz,39,44,0|dp3xg0,40,45,1|dzwtfz,40,45,1|dzwtg0,39,44,0|e7u03z,39,44,0|e7u040,40,45,1|eimw3z,40,45,1|eimw40,39,44,0|eqk2rz,39,44,0|eqk2s0,40,45,1|f1cyrz,40,45,1|f1cys0,39,44,0|f9a5fz,39,44,0|f9a5g0,40,45,1|fkg03z,40,45,1|fkg040,39,44,0|fs083z,39,44,0|fs0840,40,45,1|g362rz,40,45,1|g362s0,39,44,0|gaqarz,39,44,0|gaqas0,40,45,1|glw5fz,40,45,1|glw5g0,39,44,0|gttc3z,39,44,0|gttc40,40,45,1|h4m83z,40,45,1|h4m840,39,44,0|hcjerz,39,44,0|hcjes0,40,45,1|hncarz,40,45,1|hncas0,39,44,0|hv9hfz,39,44,0|hv9hg0,40,45,1|i6fc3z,40,45,1|i6fc40,39,44,0|idzk3z,39,44,0|idzk40,40,45,1|ip5erz,40,45,1|ip5es0,39,44,0|iwpmrz,39,44,0|iwpms0,40,45,1|j7vhfz,40,45,1|j7vhg0,39,44,0|jffpfz,39,44,0|jffpg0,40,45,1|jqlk3z,40,45,1|jqlk40,39,44,0|jyiqrz,39,44,0|jyiqs0,40,45,1|k9bmrz,40,45,1|k9bms0,39,44,0|kh8tfz,39,44,0|kh8tg0,40,45,1|ks1pfz,40,45,1|ks1pg0,39,44,0|kzyw3z,39,44,0|kzyw40,40,45,1|lb4qrz,40,45,1|lb4qs0,39,44,0|lioyrz,39,44,0|lioys0,40,45,1|ltutfz,40,45,1|ltutg0,39,44,0|m1f1fz,39,44,0|m1f1g0,40,45,1|mckw3z,40,45,1|mckw40,39,44,0|mki2rz,39,44,0|mki2s0,40,45,1|mvayrz,40,45,1|mvays0,39,44,0|n385fz,39,44,0|n385g0,40,45,1|ne11fz,40,45,1|ne11g0,39,44,0|nly83z,39,44,0|nly840,40,45,1|nwr43z,40,45,1|nwr440,39,44,0|o4oarz,39,44,0|o4oas0,40,45,1|ofu5fz,40,45,1|ofu5g0,39,44,0|onedfz,39,44,0|onedg0,40,45,1|oyk83z,40,45,1|oyk840,39,44,0|p64g3z,39,44,0|p64g40,40,45,1|phaarz,40,45,1|phaas0,39,44,0|pp7hfz,39,44,0|pp7hg0,40,45,1|q00dfz,40,45,1|q00dg0,39,44,0|q7xk3z,39,44,0|q7xk40,40,45,1|qiqg3z,40,45,1|qiqg40,39,44,0|qqnmrz,39,44,0|qqnms0,40,45,1|r1thfz,40,45,1|r1thg0,39,44,0|r9dpfz,39,44,0|r9dpg0,40,45,1|rkjk3z,40,45,1|rkjk40,39,44,0|rs3s3z,39,44,0|rs3s40,40,45,1|s39mrz,40,45,1|s39ms0,39,44,0|sb6tfz,39,44,0|sb6tg0,40,45,1|slzpfz,40,45,1|slzpg0,39,44,0|stww3z,39,44,0|stww40,40,45,1|t4ps3z,40,45,1|t4ps40,39,44,0|tcmyrz,39,44,0|tcmys0,40,45,1|tnfurz,40,45,1|tnfus0,39,44,0|tvd1fz,39,44,0|tvd1g0,40,45,1|u6iw3z,40,45,1|u6iw40,39,44,0|ue343z,39,44,0|ue3440,40,45,1|up8yrz,40,45,1|up8ys0,39,44,0|uwt6rz,39,44,0|uwt6s0,40,45,1|v7z1fz,40,45,1|v7z1g0,39,44,0|vfw83z,39,44,0|vfw840,40,45,1|vqp43z,40,45,1|vqp440,39,44,0|vymarz,39,44,0|vymas0,40,45,1|w9f6rz,40,45,1|w9f6s0,39,44,0|whcdfz,39,44,0|whcdg0,40,45,1|wsi83z,40,45,1|wsi840,39,44,0|x02g3z,39,44,0|x02g40,40,45,1|xb8arz,40,45,1|xb8as0,39,44,0|xisirz,39,44,0|xisis0,40,45,1|xtydfz,40,45,1|xtydg0,39,44,0|y1ilfz,39,44,0|y1ilg0,40,45,1|ycog3z,40,45,1|ycog40,39,44,0|yklmrz,39,44,0|yklms0,40,45,1|yveirz,40,45,1|yveis0,39,44,0|z3bpfz,39,44,0|z3bpg0,40,45,1|ze4lfz,40,45,1|ze4lg0,39,44,0\",\"America/Ojinaga|,0,158,0|-p1u4k0,50,66,0|-m7mko1,50,66,0|-m7mko0,45,62,0|-kf67c1,45,62,0|-kf67c0,50,66,0|-k6j3c1,50,66,0|-k6j3c0,45,62,0|-jypm01,45,62,0|-jypm00,50,66,0|-jpan81,50,66,0|-jpan80,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxnnz,45,62,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gcwozz,50,66,0|gcwp00,52,62,1|gkgu7z,52,62,1|gkgu80,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jftabz,50,66,0|jftac0,52,62,1|jqm3jz,52,62,1|jqm3k0,50,66,0|jywbnz,50,66,0|jywbo0,52,62,1|k9c67z,52,62,1|k9c680,50,66,0|khmebz,50,66,0|khmec0,52,62,1|ks28vz,52,62,1|ks28w0,50,66,0|kz9kzz,50,66,0|kz9l00,52,62,1|lbi8vz,52,62,1|lbi8w0,50,66,0|lhznnz,50,66,0|lhzno0,52,62,1|lu8bjz,52,62,1|lu8bk0,50,66,0|m0pqbz,50,66,0|m0pqc0,52,62,1|mcye7z,52,62,1|mcye80,50,66,0|mjfszz,50,66,0|mjft00,52,62,1|mvogvz,52,62,1|mvogw0,50,66,0|n25vnz,50,66,0|n25vo0,52,62,1|neejjz,52,62,1|neejk0,50,66,0|nkvybz,50,66,0|nkvyc0,52,62,1|nx4m7z,52,62,1|nx4m80,50,66,0|o3yznz,50,66,0|o3yzo0,52,62,1|og7njz,52,62,1|og7nk0,50,66,0|omp2bz,50,66,0|omp2c0,52,62,1|oyxq7z,52,62,1|oyxq80,50,66,0|p5f4zz,50,66,0|p5f500,52,62,1|phnsvz,52,62,1|phnsw0,50,66,0|po57nz,50,66,0|po57o0,52,62,1|q0dvjz,52,62,1|q0dvk0,50,66,0|q6vabz,50,66,0|q6vac0,52,62,1|qj3y7z,52,62,1|qj3y80,50,66,0|qpybnz,50,66,0|qpybo0,52,62,1|r26zjz,52,62,1|r26zk0,50,66,0|r8oebz,50,66,0|r8oec0,52,62,1|rkx27z,52,62,1|rkx280,50,66,0|rregzz,50,66,0|rreh00,52,62,1|s3n4vz,52,62,1|s3n4w0,50,66,0|sa4jnz,50,66,0|sa4jo0,52,62,1|smd7jz,52,62,1|smd7k0,50,66,0|ssumbz,50,66,0|ssumc0,52,62,1|t53a7z,52,62,1|t53a80,50,66,0|tbkozz,50,66,0|tbkp00,52,62,1|tntcvz,52,62,1|tntcw0,50,66,0|tunqbz,50,66,0|tunqc0,52,62,1|u6we7z,52,62,1|u6we80,50,66,0|uddszz,50,66,0|uddt00,52,62,1|upmgvz,52,62,1|upmgw0,50,66,0|uw3vnz,50,66,0|uw3vo0,52,62,1|v8cjjz,52,62,1|v8cjk0,50,66,0|vetybz,50,66,0|vetyc0,52,62,1|vr2m7z,52,62,1|vr2m80,50,66,0|vxk0zz,50,66,0|vxk100,52,62,1|w9sovz,52,62,1|w9sow0,50,66,0|wgn2bz,50,66,0|wgn2c0,52,62,1|wsvq7z,52,62,1|wsvq80,50,66,0|wzd4zz,50,66,0|wzd500,52,62,1|xblsvz,52,62,1|xblsw0,50,66,0|xi37nz,50,66,0|xi37o0,52,62,1|xubvjz,52,62,1|xubvk0,50,66,0|y0tabz,50,66,0|y0tac0,52,62,1|yd1y7z,52,62,1|yd1y80,50,66,0|yjjczz,50,66,0|yjjd00,52,62,1|yvs0vz,52,62,1|yvs0w0,50,66,0|z29fnz,50,66,0|z29fo0,52,62,1|zei3jz,52,62,1|zei3k0,50,66,0\",\"America/Panama|,0,80,0|-15r0uls,41,81,0|-w757vd,41,81,0|-w757vc,49,63,0\",\"America/Pangnirtung|,60,1,0|-pkmlc0,32,42,0|-ek27c1,32,42,0|-ek27c0,33,44,1|-cq2tg1,33,44,1|-cq2tg0,34,44,1|-cnp641,34,44,1|-cnp640,32,42,0|-2g2281,32,42,0|-2g2280,73,45,1|-26c281,73,45,1|-26c280,32,42,0|5dw3bz,32,42,0|5dw3c0,54,44,1|5n91vz,54,44,1|5n91w0,32,42,0|5wm5zz,32,42,0|5wm600,54,44,1|65z4jz,54,44,1|65z4k0,32,42,0|6fc8nz,32,42,0|6fc8o0,54,44,1|6p25vz,54,44,1|6p25w0,32,42,0|6y2bbz,32,42,0|6y2bc0,54,44,1|77s8jz,54,44,1|77s8k0,32,42,0|7h5cnz,32,42,0|7h5co0,54,44,1|7qib7z,54,44,1|7qib80,32,42,0|7zvfbz,32,42,0|7zvfc0,54,44,1|898dvz,54,44,1|898dw0,32,42,0|8ilhzz,32,42,0|8ili00,54,44,1|8rygjz,54,44,1|8rygk0,32,42,0|908onz,32,42,0|908oo0,54,44,1|9aoj7z,54,44,1|9aoj80,32,42,0|9iyrbz,32,42,0|9iyrc0,54,44,1|9trkjz,54,44,1|9trkk0,32,42,0|a1otzz,32,42,0|a1ou00,54,44,1|achn7z,54,44,1|achn80,32,42,0|akewnz,32,42,0|akewo0,54,44,1|av7pvz,54,44,1|av7pw0,32,42,0|b3hxzz,32,42,0|b3hy00,54,44,1|bdxsjz,54,44,1|bdxsk0,32,42,0|bm80nz,32,42,0|bm80o0,54,44,1|bwnv7z,54,44,1|bwnv80,32,42,0|c4y3bz,32,42,0|c4y3c0,54,44,1|cfqwjz,54,44,1|cfqwk0,32,42,0|cno5zz,32,42,0|cno600,54,44,1|cygz7z,54,44,1|cygz80,32,42,0|d6e8nz,32,42,0|d6e8o0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Paramaribo|,0,159,0|-usj4g8,7,160,0|-i9lsfx,7,160,0|-i9lsfw,7,161,0|-cnnf4d,7,161,0|-cnnf4c,81,101,0|7p471z,81,101,0|7p4720,39,44,0\",\"America/Phoenix|,0,162,0|-18y0j80,50,66,0|-r0epo1,50,66,0|-r0epo0,52,62,1|-qplwg1,52,62,1|-qplwg0,50,66,0|-qhon01,50,66,0|-qhon00,52,62,1|-q6vts1,52,62,1|-q6vts0,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-dkikmd,58,62,1|-dkikmc,50,66,0|-dftz6d,50,66,0|-dftz6c,58,62,1|-d6f5yd,58,62,1|-d6f5yc,50,66,0|-1e8kc1,50,66,0|-1e8kc0,52,62,1|-14vls1,52,62,1|-14vls0,50,66,0\",\"America/Port-au-Prince|,0,163,0|-15r0vxs,84,164,0|-rmk9ad,84,164,0|-rmk9ac,49,63,0|6ys5vz,49,63,0|6ys5w0,62,42,1|77s5rz,62,42,1|77s5s0,49,63,0|7h59vz,49,63,0|7h59w0,62,42,1|7qi8fz,62,42,1|7qi8g0,49,63,0|7zvcjz,49,63,0|7zvck0,62,42,1|898b3z,62,42,1|898b40,49,63,0|8ilf7z,49,63,0|8ilf80,62,42,1|8rydrz,62,42,1|8ryds0,49,63,0|91bhvz,49,63,0|91bhw0,62,42,1|9aogfz,62,42,1|9aogg0,49,63,0|9iyrbz,49,63,0|9iyrc0,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1otzz,49,63,0|a1ou00,62,42,1|achpzz,62,42,1|achq00,49,63,0|akewnz,49,63,0|akewo0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3hxzz,49,63,0|b3hy00,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm80nz,49,63,0|bm80o0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y3bz,49,63,0|c4y3c0,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno5zz,49,63,0|cno600,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6e8nz,49,63,0|d6e8o0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dph9zz,49,63,0|dpha00,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87cnz,49,63,0|e87co0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|iectvz,49,63,0|iectw0,62,42,1|ip5n3z,62,42,1|ip5n40,49,63,0|ix2wjz,49,63,0|ix2wk0,62,42,1|j7vprz,62,42,1|j7vps0,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Port_of_Spain|,0,41,0|-u6m79w,32,42,0\",\"America/Porto_Velho|,0,165,0|-t85g60,42,42,0|-jyl7o1,42,42,0|-jyl7o0,39,44,1|-jpayc1,39,44,1|-jpayc0,42,42,0|-jfsa81,42,42,0|-jfsa80,39,44,1|-j6j101,39,44,1|-j6j100,42,42,0|-ahcvk1,42,42,0|-ahcvk0,39,44,1|-aad0w1,39,44,1|-aad0w0,42,42,0|-9yky81,42,42,0|-9yky80,39,44,1|-9scyc1,39,44,1|-9scyc0,42,42,0|-9ft0w1,42,42,0|-9ft0w0,39,44,1|-99j6c1,39,44,1|-99j6c0,42,42,0|-8wz8w1,42,42,0|-8wz8w0,39,44,1|-8scno1,39,44,1|-8scno0,42,42,0|-35xjk1,42,42,0|-35xjk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2kdm81,42,42,0|-2kdm80,39,44,1|-2hcfo1,39,44,1|-2hcfo0,42,42,0|-24qnk1,42,42,0|-24qnk0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1nia81,42,42,0|-1nia80,39,44,1|-1hc501,39,44,1|-1hc500,42,42,0|-14qcw1,42,42,0|-14qcw0,39,44,1|-yid01,39,44,1|-yid00,42,42,0|89jf3z,42,42,0|89jf40,39,44,1|8gdmzz,39,44,1|8gdn00,42,42,0|8rwj3z,42,42,0|8rwj40,39,44,1|8xnuzz,39,44,1|8xnv00,42,42,0|9aogfz,42,42,0|9aogg0,39,44,1|9g2tnz,39,44,1|9g2to0,42,42,0\",\"America/Puerto_Rico|,0,166,0|-10xhp3b,32,42,0|-efsnk1,32,42,0|-efsnk0,33,44,1|-cq2tg1,33,44,1|-cq2tg0,34,44,1|-cnp641,34,44,1|-cnp640,32,42,0\",\"America/Punta_Arenas|,0,167,0|-15r0w78,85,168,0|-vauawr,85,168,0|-vauawq,56,63,0|-rx8i41,56,63,0|-rx8i40,85,168,0|-qs16wr,85,168,0|-qs16wq,42,42,0|-qcwsw1,42,42,0|-qcwsw0,85,168,0|-m3etkr,85,168,0|-m3etkq,42,42,1|-lsgfk1,42,42,1|-lsgfk0,56,63,0|-lkl0s1,56,63,0|-lkl0s0,42,42,1|-l9oi81,42,42,1|-l9oi80,56,63,0|-l1t3g1,56,63,0|-l1t3g0,42,42,1|-kqwkw1,42,42,1|-kqwkw0,56,63,0|-kj1641,56,63,0|-kj1640,42,42,1|-k84nk1,42,42,1|-k84nk0,56,63,0|-k098s1,56,63,0|-k098s0,42,42,1|-jpavk1,42,42,1|-jpavk0,56,63,0|-jhfgs1,56,63,0|-jhfgs0,42,42,0|-eeay81,42,42,0|-eeay80,56,63,0|-eb5ws1,56,63,0|-eb5ws0,42,42,0|-bvifk1,42,42,0|-bvifk0,56,63,0|-bsvzk1,56,63,0|-bsvzk0,42,42,0|-lsvk1,42,42,0|-lsvk0,39,44,1|-e8qc1,39,44,1|-e8qc0,42,42,0|-1zww1,42,42,0|-1zww0,39,44,1|4hcbz,39,44,1|4hcc0,42,42,0|ekdrz,42,42,0|ekds0,39,44,1|mhhnz,39,44,1|mhho0,42,42,0|xagfz,42,42,0|xagg0,39,44,1|157kbz,39,44,1|157kc0,42,42,0|1gdhrz,42,42,0|1gdhs0,39,44,1|1nxmzz,39,44,1|1nxn00,42,42,0|1ydn3z,42,42,0|1ydn40,39,44,1|26npnz,39,44,1|26npo0,42,42,0|2htn3z,42,42,0|2htn40,39,44,1|2pdsbz,39,44,1|2pdsc0,42,42,0|30jprz,42,42,0|30jps0,39,44,1|38gtnz,39,44,1|38gto0,42,42,0|3j9sfz,42,42,0|3j9sg0,39,44,1|3r6wbz,39,44,1|3r6wc0,42,42,0|41zv3z,42,42,0|41zv40,39,44,1|49wyzz,39,44,1|49wz00,42,42,0|4l2wfz,42,42,0|4l2wg0,39,44,1|4sn1nz,39,44,1|4sn1o0,42,42,0|53sz3z,42,42,0|53sz40,39,44,1|5bd4bz,39,44,1|5bd4c0,42,42,0|5mj1rz,42,42,0|5mj1s0,39,44,1|5ug5nz,39,44,1|5ug5o0,42,42,0|6594fz,42,42,0|6594g0,39,44,1|6d68bz,39,44,1|6d68c0,42,42,0|6nz73z,42,42,0|6nz740,39,44,1|6vwazz,39,44,1|6vwb00,42,42,0|76p9rz,42,42,0|76p9s0,39,44,1|7emdnz,39,44,1|7emdo0,42,42,0|7psb3z,42,42,0|7psb40,39,44,1|7xcgbz,39,44,1|7xcgc0,42,42,0|88idrz,42,42,0|88ids0,39,44,1|8g2izz,39,44,1|8g2j00,42,42,0|8r8gfz,42,42,0|8r8gg0,39,44,1|90lezz,39,44,1|90lf00,42,42,0|99yj3z,42,42,0|99yj40,39,44,1|9hvmzz,39,44,1|9hvn00,42,42,0|9solrz,42,42,0|9sols0,39,44,1|a0lpnz,39,44,1|a0lpo0,42,42,0|abrn3z,42,42,0|abrn40,39,44,1|ajbsbz,39,44,1|ajbsc0,42,42,0|at1v3z,42,42,0|at1v40,39,44,1|b21uzz,39,44,1|b21v00,42,42,0|bd7sfz,42,42,0|bd7sg0,39,44,1|bl4wbz,39,44,1|bl4wc0,42,42,0|bvxv3z,42,42,0|bvxv40,39,44,1|c3uyzz,39,44,1|c3uz00,42,42,0|cenxrz,42,42,0|cenxs0,39,44,1|cml1nz,39,44,1|cml1o0,42,42,0|cxe0fz,42,42,0|cxe0g0,39,44,1|d5b4bz,39,44,1|d5b4c0,42,42,0|dgh1rz,42,42,0|dgh1s0,39,44,1|do16zz,39,44,1|do1700,42,42,0|dz74fz,42,42,0|dz74g0,39,44,1|e7u5nz,39,44,1|e7u5o0,42,42,0|ehx73z,42,42,0|ehx740,39,44,1|epuazz,39,44,1|epub00,42,42,0|ezxcfz,42,42,0|ezxcg0,39,44,1|f9n9nz,39,44,1|f9n9o0,42,42,0|fjdcfz,42,42,0|fjdcg0,39,44,1|fragbz,39,44,1|fragc0,42,42,0|g2gdrz,42,42,0|g2gds0,39,44,1|ga0izz,39,44,1|ga0j00,42,42,0|gl6gfz,42,42,0|gl6gg0,39,44,1|gsqlnz,39,44,1|gsqlo0,42,42,0|h3wj3z,42,42,0|h3wj40,39,44,1|hbgobz,39,44,1|hbgoc0,42,42,0|hmmlrz,42,42,0|hmmls0,39,44,1|hujpnz,39,44,1|hujpo0,42,42,0|i5cofz,42,42,0|i5cog0,39,44,1|id9sbz,39,44,1|id9sc0,42,42,0|io2r3z,42,42,0|io2r40,39,44,1|ivzuzz,39,44,1|ivzv00,42,42,0|j75sfz,42,42,0|j75sg0,39,44,1|jepxnz,39,44,1|jepxo0,42,42,0|jpvv3z,42,42,0|jpvv40,39,44,1|jyiwbz,39,44,1|jyiwc0,42,42,0|k8lxrz,42,42,0|k8lxs0,39,44,1|kgj1nz,39,44,1|kgj1o0,42,42,0|krc0fz,42,42,0|krc0g0,39,44,1|l0c0bz,39,44,1|l0c0c0,42,42,0|la233z,42,42,0|la2340,39,44,1|lkuwbz,39,44,1|lkuwc0,42,42,0|lq9f3z,42,42,0|lq9f40,39,44,1|m380bz,39,44,1|m380c0,42,42,0|m9pf3z,42,42,0|m9pf40,39,44,1|mly2zz,39,44,1|mly300,42,42,0|mssgfz,42,42,0|mssgg0,39,44,1|n4o5nz,39,44,1|n4o5o0,42,42,0|nbij3z,42,42,0|nbij40,39,44,1|o776zz,39,44,1|o77700,42,42,0|obvsfz,42,42,0|obvsg0,39,44,1|ohn4bz,39,44,1|ohn4c0,39,44,0\",\"America/Rainy_River|,0,169,0|-1353ahk,45,62,0|-qzov41,45,62,0|-qzov40,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-f9ofc1,45,62,0|-f9ofc0,46,63,1|-ek21s1,46,63,1|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|296u7z,45,62,0|296u80,46,63,1|2ijsrz,46,63,1|2ijss0,45,62,0|2rwwvz,45,62,0|2rwww0,46,63,1|319vfz,46,63,1|319vg0,45,62,0|3amzjz,45,62,0|3amzk0,46,63,1|3kcwrz,46,63,1|3kcws0,45,62,0|3td27z,45,62,0|3td280,46,63,1|432zfz,46,63,1|432zg0,45,62,0|4cg3jz,45,62,0|4cg3k0,46,63,1|4lt23z,46,63,1|4lt240,45,62,0|4v667z,45,62,0|4v6680,46,63,1|54j4rz,46,63,1|54j4s0,45,62,0|5dw8vz,45,62,0|5dw8w0,46,63,1|5n97fz,46,63,1|5n97g0,45,62,0|5wmbjz,45,62,0|5wmbk0,46,63,1|65za3z,46,63,1|65za40,45,62,0|6fce7z,45,62,0|6fce80,46,63,1|6p2bfz,46,63,1|6p2bg0,45,62,0|6y2gvz,45,62,0|6y2gw0,46,63,1|77se3z,46,63,1|77se40,45,62,0|7h5i7z,45,62,0|7h5i80,46,63,1|7qigrz,46,63,1|7qigs0,45,62,0|7zvkvz,45,62,0|7zvkw0,46,63,1|898jfz,46,63,1|898jg0,45,62,0|8ilnjz,45,62,0|8ilnk0,46,63,1|8rym3z,46,63,1|8rym40,45,62,0|908u7z,45,62,0|908u80,46,63,1|9aoorz,46,63,1|9aoos0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trq3z,46,63,1|9trq40,45,62,0|a1ozjz,45,62,0|a1ozk0,46,63,1|achsrz,46,63,1|achss0,45,62,0|akf27z,45,62,0|akf280,46,63,1|av7vfz,46,63,1|av7vg0,45,62,0|b3i3jz,45,62,0|b3i3k0,46,63,1|bdxy3z,46,63,1|bdxy40,45,62,0|bm867z,45,62,0|bm8680,46,63,1|bwo0rz,46,63,1|bwo0s0,45,62,0|c4y8vz,45,62,0|c4y8w0,46,63,1|cfr23z,46,63,1|cfr240,45,62,0|cnobjz,45,62,0|cnobk0,46,63,1|cyh4rz,46,63,1|cyh4s0,45,62,0|d6ee7z,45,62,0|d6ee80,46,63,1|dh77fz,46,63,1|dh77g0,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,45,62,0|gb3svz,45,62,0|gb3sw0,46,63,1|glwm3z,46,63,1|glwm40,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Rankin_Inlet|,60,1,0|-6s8lc0,45,62,0|-2g1wo1,45,62,0|-2g1wo0,86,42,1|-26bwo1,86,42,1|-26bwo0,45,62,0|5dw8vz,45,62,0|5dw8w0,46,63,1|5n97fz,46,63,1|5n97g0,45,62,0|5wmbjz,45,62,0|5wmbk0,46,63,1|65za3z,46,63,1|65za40,45,62,0|6fce7z,45,62,0|6fce80,46,63,1|6p2bfz,46,63,1|6p2bg0,45,62,0|6y2gvz,45,62,0|6y2gw0,46,63,1|77se3z,46,63,1|77se40,45,62,0|7h5i7z,45,62,0|7h5i80,46,63,1|7qigrz,46,63,1|7qigs0,45,62,0|7zvkvz,45,62,0|7zvkw0,46,63,1|898jfz,46,63,1|898jg0,45,62,0|8ilnjz,45,62,0|8ilnk0,46,63,1|8rym3z,46,63,1|8rym40,45,62,0|908u7z,45,62,0|908u80,46,63,1|9aoorz,46,63,1|9aoos0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trq3z,46,63,1|9trq40,45,62,0|a1ozjz,45,62,0|a1ozk0,46,63,1|achsrz,46,63,1|achss0,45,62,0|akf27z,45,62,0|akf280,46,63,1|av7vfz,46,63,1|av7vg0,45,62,0|b3i3jz,45,62,0|b3i3k0,46,63,1|bdxy3z,46,63,1|bdxy40,45,62,0|bm867z,45,62,0|bm8680,46,63,1|bwo0rz,46,63,1|bwo0s0,45,62,0|c4y8vz,45,62,0|c4y8w0,46,63,1|cfr23z,46,63,1|cfr240,45,62,0|cnobjz,45,62,0|cnobk0,46,63,1|cyh4rz,46,63,1|cyh4s0,45,62,0|d6ee7z,45,62,0|d6ee80,46,63,1|dh77fz,46,63,1|dh77g0,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,49,63,0|gb3svz,49,63,0|gb3sw0,46,63,1|glwm3z,46,63,1|glwm40,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Recife|,0,170,0|-t85ljc,39,44,0|-jylag1,39,44,0|-jylag0,40,45,1|-jpb141,40,45,1|-jpb140,39,44,0|-jfsd01,39,44,0|-jfsd00,40,45,1|-j6j3s1,40,45,1|-j6j3s0,39,44,0|-ahcyc1,39,44,0|-ahcyc0,40,45,1|-aad3o1,40,45,1|-aad3o0,39,44,0|-9yl101,39,44,0|-9yl100,40,45,1|-9sd141,40,45,1|-9sd140,39,44,0|-9ft3o1,39,44,0|-9ft3o0,40,45,1|-99j941,40,45,1|-99j940,39,44,0|-8wzbo1,39,44,0|-8wzbo0,40,45,1|-8scqg1,40,45,1|-8scqg0,39,44,0|-35xmc1,39,44,0|-35xmc0,40,45,1|-31nzs1,40,45,1|-31nzs0,39,44,0|-2kdp01,39,44,0|-2kdp00,40,45,1|-2hcig1,40,45,1|-2hcig0,39,44,0|-24qqc1,39,44,0|-24qqc0,40,45,1|-204541,40,45,1|-204540,39,44,0|-1nid01,39,44,0|-1nid00,40,45,1|-1hc7s1,40,45,1|-1hc7s0,39,44,0|-14qfo1,39,44,0|-14qfo0,40,45,1|-yifs1,40,45,1|-yifs0,39,44,0|89jcbz,39,44,0|89jcc0,40,45,1|8gdk7z,40,45,1|8gdk80,39,44,0|8rwgbz,39,44,0|8rwgc0,40,45,1|8xns7z,40,45,1|8xns80,39,44,0|9aodnz,39,44,0|9aodo0,40,45,1|9g2qvz,40,45,1|9g2qw0,39,44,0|9t1hnz,39,44,0|9t1ho0,40,45,1|9yfuvz,40,45,1|9yfuw0,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|ahvuvz,40,45,1|ahvuw0,39,44,0|fj0azz,39,44,0|fj0b00,40,45,1|fqkg7z,40,45,1|fqkg80,39,44,0|g23cbz,39,44,0|g23cc0,40,45,1|g2g87z,40,45,1|g2g880,39,44,0|gl6dnz,39,44,0|gl6do0,40,45,1|grnmvz,40,45,1|grnmw0,39,44,0\",\"America/Regina|,0,171,0|-xkq9yc,50,66,0|-qzosc1,50,66,0|-qzosc0,52,62,1|-qplwg1,52,62,1|-qplwg0,50,66,0|-kp78k1,50,66,0|-kp78k0,52,62,1|-kha4o1,52,62,1|-kha4o0,50,66,0|-k6h5w1,50,66,0|-k6h5w0,52,62,1|-jyk201,52,62,1|-jyk200,50,66,0|-jnr381,50,66,0|-jnr380,52,62,1|-jftzc1,52,62,1|-jftzc0,50,66,0|-j4o1w1,50,66,0|-j4o1w0,52,62,1|-ix3wo1,52,62,1|-ix3wo0,50,66,0|-ilxz81,50,66,0|-ilxz80,52,62,1|-ie0vc1,52,62,1|-ie0vc0,50,66,0|-h2un81,50,66,0|-h2un80,52,62,1|-gthoo1,52,62,1|-gthoo0,50,66,0|-gk4kk1,50,66,0|-gk4kk0,52,62,1|-gb4ko1,52,62,1|-gb4ko0,50,66,0|-g1ehw1,50,66,0|-g1ehw0,52,62,1|-fs1jc1,52,62,1|-fs1jc0,50,66,0|-fibgk1,50,66,0|-fibgk0,52,62,1|-f8yi01,52,62,1|-f8yi00,50,66,0|-ezldw1,50,66,0|-ezldw0,52,62,1|-eq8fc1,52,62,1|-eq8fc0,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-cdlwc1,50,66,0|-cdlwc0,52,62,1|-c48xs1,52,62,1|-c48xs0,50,66,0|-bu5wc1,50,66,0|-bu5wc0,52,62,1|-bm8sg1,52,62,1|-bm8sg0,50,66,0|-bbfto1,50,66,0|-bbfto0,52,62,1|-b3ips1,52,62,1|-b3ips0,50,66,0|-aspr01,50,66,0|-aspr00,52,62,1|-aksn41,52,62,1|-aksn40,50,66,0|-a9mpo1,50,66,0|-a9mpo0,52,62,1|-a22kg1,52,62,1|-a22kg0,50,66,0|-9qwn01,50,66,0|-9qwn00,52,62,1|-9izj41,52,62,1|-9izj40,50,66,0|-986kc1,50,66,0|-986kc0,52,62,1|-909gg1,52,62,1|-909gg0,50,66,0|-8pgho1,50,66,0|-8pgho0,52,62,1|-8hjds1,52,62,1|-8hjds0,50,66,0|-86qf01,50,66,0|-86qf00,52,62,1|-7ytb41,52,62,1|-7ytb40,50,66,0|-7o0cc1,50,66,0|-7o0cc0,52,62,1|-7g38g1,52,62,1|-7g38g0,50,66,0|-74xb01,50,66,0|-74xb00,52,62,1|-6x0741,52,62,1|-6x0740,50,66,0|-6m78c1,50,66,0|-6m78c0,52,62,1|-6ea4g1,52,62,1|-6ea4g0,50,66,0|-5kr301,50,66,0|-5kr300,52,62,1|-5be4g1,52,62,1|-5be4g0,50,66,0|-5210c1,50,66,0|-5210c0,45,62,0\",\"America/Resolute|,60,1,0|-bnp9c0,45,62,0|-2g1wo1,45,62,0|-2g1wo0,86,42,1|-26bwo1,86,42,1|-26bwo0,45,62,0|5dw8vz,45,62,0|5dw8w0,46,63,1|5n97fz,46,63,1|5n97g0,45,62,0|5wmbjz,45,62,0|5wmbk0,46,63,1|65za3z,46,63,1|65za40,45,62,0|6fce7z,45,62,0|6fce80,46,63,1|6p2bfz,46,63,1|6p2bg0,45,62,0|6y2gvz,45,62,0|6y2gw0,46,63,1|77se3z,46,63,1|77se40,45,62,0|7h5i7z,45,62,0|7h5i80,46,63,1|7qigrz,46,63,1|7qigs0,45,62,0|7zvkvz,45,62,0|7zvkw0,46,63,1|898jfz,46,63,1|898jg0,45,62,0|8ilnjz,45,62,0|8ilnk0,46,63,1|8rym3z,46,63,1|8rym40,45,62,0|908u7z,45,62,0|908u80,46,63,1|9aoorz,46,63,1|9aoos0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trq3z,46,63,1|9trq40,45,62,0|a1ozjz,45,62,0|a1ozk0,46,63,1|achsrz,46,63,1|achss0,45,62,0|akf27z,45,62,0|akf280,46,63,1|av7vfz,46,63,1|av7vg0,45,62,0|b3i3jz,45,62,0|b3i3k0,46,63,1|bdxy3z,46,63,1|bdxy40,45,62,0|bm867z,45,62,0|bm8680,46,63,1|bwo0rz,46,63,1|bwo0s0,45,62,0|c4y8vz,45,62,0|c4y8w0,46,63,1|cfr23z,46,63,1|cfr240,45,62,0|cnobjz,45,62,0|cnobk0,46,63,1|cyh4rz,46,63,1|cyh4s0,45,62,0|d6ee7z,45,62,0|d6ee80,46,63,1|dh77fz,46,63,1|dh77g0,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxa3z,46,63,1|dzxa40,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|eincrz,46,63,1|eincs0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1dffz,46,63,1|f1dfg0,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkggrz,46,63,1|fkggs0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36jfz,46,63,1|g36jg0,49,63,0|gb3svz,49,63,0|gb3sw0,46,63,1|glwm3z,46,63,1|glwm40,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4morz,46,63,1|h4mos0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncrfz,46,63,1|hncrg0,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fsrz,46,63,1|i6fss0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5vfz,46,63,1|ip5vg0,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,49,63,0|jeqbjz,49,63,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Rio_Branco|,0,172,0|-t85fg0,56,63,0|-jyl4w1,56,63,0|-jyl4w0,42,42,1|-jpavk1,42,42,1|-jpavk0,56,63,0|-jfs7g1,56,63,0|-jfs7g0,42,42,1|-j6iy81,42,42,1|-j6iy80,56,63,0|-ahcss1,56,63,0|-ahcss0,42,42,1|-aacy41,42,42,1|-aacy40,56,63,0|-9ykvg1,56,63,0|-9ykvg0,42,42,1|-9scvk1,42,42,1|-9scvk0,56,63,0|-9fsy41,56,63,0|-9fsy40,42,42,1|-99j3k1,42,42,1|-99j3k0,56,63,0|-8wz641,56,63,0|-8wz640,42,42,1|-8sckw1,42,42,1|-8sckw0,56,63,0|-35xgs1,56,63,0|-35xgs0,42,42,1|-31nu81,42,42,1|-31nu80,56,63,0|-2kdjg1,56,63,0|-2kdjg0,42,42,1|-2hccw1,42,42,1|-2hccw0,56,63,0|-24qks1,56,63,0|-24qks0,42,42,1|-203zk1,42,42,1|-203zk0,56,63,0|-1ni7g1,56,63,0|-1ni7g0,42,42,1|-1hc281,42,42,1|-1hc280,56,63,0|-14qa41,56,63,0|-14qa40,42,42,1|-yia81,42,42,1|-yia80,56,63,0|89jhvz,56,63,0|89jhw0,42,42,1|8gdprz,42,42,1|8gdps0,56,63,0|8rwlvz,56,63,0|8rwlw0,42,42,1|8xnxrz,42,42,1|8xnxs0,56,63,0|9aoj7z,56,63,0|9aoj80,42,42,1|9g2wfz,42,42,1|9g2wg0,56,63,0|k2yb7z,56,63,0|k2yb80,42,42,0|mw14fz,42,42,0|mw14g0,56,63,0\",\"America/Santarem|,0,173,0|-t85hvc,42,42,0|-jyl7o1,42,42,0|-jyl7o0,39,44,1|-jpayc1,39,44,1|-jpayc0,42,42,0|-jfsa81,42,42,0|-jfsa80,39,44,1|-j6j101,39,44,1|-j6j100,42,42,0|-ahcvk1,42,42,0|-ahcvk0,39,44,1|-aad0w1,39,44,1|-aad0w0,42,42,0|-9yky81,42,42,0|-9yky80,39,44,1|-9scyc1,39,44,1|-9scyc0,42,42,0|-9ft0w1,42,42,0|-9ft0w0,39,44,1|-99j6c1,39,44,1|-99j6c0,42,42,0|-8wz8w1,42,42,0|-8wz8w0,39,44,1|-8scno1,39,44,1|-8scno0,42,42,0|-35xjk1,42,42,0|-35xjk0,39,44,1|-31nx01,39,44,1|-31nx00,42,42,0|-2kdm81,42,42,0|-2kdm80,39,44,1|-2hcfo1,39,44,1|-2hcfo0,42,42,0|-24qnk1,42,42,0|-24qnk0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1nia81,42,42,0|-1nia80,39,44,1|-1hc501,39,44,1|-1hc500,42,42,0|-14qcw1,42,42,0|-14qcw0,39,44,1|-yid01,39,44,1|-yid00,42,42,0|89jf3z,42,42,0|89jf40,39,44,1|8gdmzz,39,44,1|8gdn00,42,42,0|8rwj3z,42,42,0|8rwj40,39,44,1|8xnuzz,39,44,1|8xnv00,42,42,0|9aogfz,42,42,0|9aogg0,39,44,1|9g2tnz,39,44,1|9g2to0,42,42,0|k2y8fz,42,42,0|k2y8g0,39,44,0\",\"America/Santiago|,0,168,0|-15r0w8q,85,168,0|-vauawr,85,168,0|-vauawq,56,63,0|-rx8i41,56,63,0|-rx8i40,85,168,0|-qs16wr,85,168,0|-qs16wq,42,42,0|-qcwsw1,42,42,0|-qcwsw0,85,168,0|-m3etkr,85,168,0|-m3etkq,42,42,1|-lsgfk1,42,42,1|-lsgfk0,56,63,0|-lkl0s1,56,63,0|-lkl0s0,42,42,1|-l9oi81,42,42,1|-l9oi80,56,63,0|-l1t3g1,56,63,0|-l1t3g0,42,42,1|-kqwkw1,42,42,1|-kqwkw0,56,63,0|-kj1641,56,63,0|-kj1640,42,42,1|-k84nk1,42,42,1|-k84nk0,56,63,0|-k098s1,56,63,0|-k098s0,42,42,1|-jpavk1,42,42,1|-jpavk0,56,63,0|-jhfgs1,56,63,0|-jhfgs0,42,42,0|-eeay81,42,42,0|-eeay80,56,63,0|-eb5ws1,56,63,0|-eb5ws0,42,42,0|-c8vww1,42,42,0|-c8vww0,39,44,1|-c6f3o1,39,44,1|-c6f3o0,42,42,0|-bvifk1,42,42,0|-bvifk0,56,63,0|-bsvzk1,56,63,0|-bsvzk0,42,42,0|-lsvk1,42,42,0|-lsvk0,39,44,1|-e8qc1,39,44,1|-e8qc0,42,42,0|-1zww1,42,42,0|-1zww0,39,44,1|4hcbz,39,44,1|4hcc0,42,42,0|ekdrz,42,42,0|ekds0,39,44,1|mhhnz,39,44,1|mhho0,42,42,0|xagfz,42,42,0|xagg0,39,44,1|157kbz,39,44,1|157kc0,42,42,0|1gdhrz,42,42,0|1gdhs0,39,44,1|1nxmzz,39,44,1|1nxn00,42,42,0|1ydn3z,42,42,0|1ydn40,39,44,1|26npnz,39,44,1|26npo0,42,42,0|2htn3z,42,42,0|2htn40,39,44,1|2pdsbz,39,44,1|2pdsc0,42,42,0|30jprz,42,42,0|30jps0,39,44,1|38gtnz,39,44,1|38gto0,42,42,0|3j9sfz,42,42,0|3j9sg0,39,44,1|3r6wbz,39,44,1|3r6wc0,42,42,0|41zv3z,42,42,0|41zv40,39,44,1|49wyzz,39,44,1|49wz00,42,42,0|4l2wfz,42,42,0|4l2wg0,39,44,1|4sn1nz,39,44,1|4sn1o0,42,42,0|53sz3z,42,42,0|53sz40,39,44,1|5bd4bz,39,44,1|5bd4c0,42,42,0|5mj1rz,42,42,0|5mj1s0,39,44,1|5ug5nz,39,44,1|5ug5o0,42,42,0|6594fz,42,42,0|6594g0,39,44,1|6d68bz,39,44,1|6d68c0,42,42,0|6nz73z,42,42,0|6nz740,39,44,1|6vwazz,39,44,1|6vwb00,42,42,0|76p9rz,42,42,0|76p9s0,39,44,1|7emdnz,39,44,1|7emdo0,42,42,0|7psb3z,42,42,0|7psb40,39,44,1|7xcgbz,39,44,1|7xcgc0,42,42,0|88idrz,42,42,0|88ids0,39,44,1|8g2izz,39,44,1|8g2j00,42,42,0|8r8gfz,42,42,0|8r8gg0,39,44,1|90lezz,39,44,1|90lf00,42,42,0|99yj3z,42,42,0|99yj40,39,44,1|9hvmzz,39,44,1|9hvn00,42,42,0|9solrz,42,42,0|9sols0,39,44,1|a0lpnz,39,44,1|a0lpo0,42,42,0|abrn3z,42,42,0|abrn40,39,44,1|ajbsbz,39,44,1|ajbsc0,42,42,0|at1v3z,42,42,0|at1v40,39,44,1|b21uzz,39,44,1|b21v00,42,42,0|bd7sfz,42,42,0|bd7sg0,39,44,1|bl4wbz,39,44,1|bl4wc0,42,42,0|bvxv3z,42,42,0|bvxv40,39,44,1|c3uyzz,39,44,1|c3uz00,42,42,0|cenxrz,42,42,0|cenxs0,39,44,1|cml1nz,39,44,1|cml1o0,42,42,0|cxe0fz,42,42,0|cxe0g0,39,44,1|d5b4bz,39,44,1|d5b4c0,42,42,0|dgh1rz,42,42,0|dgh1s0,39,44,1|do16zz,39,44,1|do1700,42,42,0|dz74fz,42,42,0|dz74g0,39,44,1|e7u5nz,39,44,1|e7u5o0,42,42,0|ehx73z,42,42,0|ehx740,39,44,1|epuazz,39,44,1|epub00,42,42,0|ezxcfz,42,42,0|ezxcg0,39,44,1|f9n9nz,39,44,1|f9n9o0,42,42,0|fjdcfz,42,42,0|fjdcg0,39,44,1|fragbz,39,44,1|fragc0,42,42,0|g2gdrz,42,42,0|g2gds0,39,44,1|ga0izz,39,44,1|ga0j00,42,42,0|gl6gfz,42,42,0|gl6gg0,39,44,1|gsqlnz,39,44,1|gsqlo0,42,42,0|h3wj3z,42,42,0|h3wj40,39,44,1|hbgobz,39,44,1|hbgoc0,42,42,0|hmmlrz,42,42,0|hmmls0,39,44,1|hujpnz,39,44,1|hujpo0,42,42,0|i5cofz,42,42,0|i5cog0,39,44,1|id9sbz,39,44,1|id9sc0,42,42,0|io2r3z,42,42,0|io2r40,39,44,1|ivzuzz,39,44,1|ivzv00,42,42,0|j75sfz,42,42,0|j75sg0,39,44,1|jepxnz,39,44,1|jepxo0,42,42,0|jpvv3z,42,42,0|jpvv40,39,44,1|jyiwbz,39,44,1|jyiwc0,42,42,0|k8lxrz,42,42,0|k8lxs0,39,44,1|kgj1nz,39,44,1|kgj1o0,42,42,0|krc0fz,42,42,0|krc0g0,39,44,1|l0c0bz,39,44,1|l0c0c0,42,42,0|la233z,42,42,0|la2340,39,44,1|lkuwbz,39,44,1|lkuwc0,42,42,0|lq9f3z,42,42,0|lq9f40,39,44,1|m380bz,39,44,1|m380c0,42,42,0|m9pf3z,42,42,0|m9pf40,39,44,1|mly2zz,39,44,1|mly300,42,42,0|mssgfz,42,42,0|mssgg0,39,44,1|n4o5nz,39,44,1|n4o5o0,42,42,0|nbij3z,42,42,0|nbij40,39,44,1|o776zz,39,44,1|o77700,42,42,0|obvsfz,42,42,0|obvsg0,39,44,1|opx9nz,39,44,1|opx9o0,42,42,0|oulv3z,42,42,0|oulv40,39,44,1|p8ncbz,39,44,1|p8ncc0,42,42,0|pdbxrz,42,42,0|pdbxs0,39,44,1|ppklnz,39,44,1|ppklo0,42,42,0|pxhv3z,42,42,0|pxhv40,39,44,1|q8aobz,39,44,1|q8aoc0,42,42,0|qg7xrz,42,42,0|qg7xs0,39,44,1|qr0qzz,39,44,1|qr0r00,42,42,0|qyy0fz,42,42,0|qyy0g0,39,44,1|r9qtnz,39,44,1|r9qto0,42,42,0|rho33z,42,42,0|rho340,39,44,1|rsgwbz,39,44,1|rsgwc0,42,42,0|s0e5rz,42,42,0|s0e5s0,39,44,1|sbjxnz,39,44,1|sbjxo0,42,42,0|sjh73z,42,42,0|sjh740,39,44,1|sua0bz,39,44,1|sua0c0,42,42,0|t279rz,42,42,0|t279s0,39,44,1|td02zz,39,44,1|td0300,42,42,0|tkxcfz,42,42,0|tkxcg0,39,44,1|tvq5nz,39,44,1|tvq5o0,42,42,0|u3nf3z,42,42,0|u3nf40,39,44,1|ueg8bz,39,44,1|ueg8c0,42,42,0|umdhrz,42,42,0|umdhs0,39,44,1|uxj9nz,39,44,1|uxj9o0,42,42,0|v53kfz,42,42,0|v53kg0,39,44,1|vg9cbz,39,44,1|vg9cc0,42,42,0|vo6lrz,42,42,0|vo6ls0,39,44,1|vyzezz,39,44,1|vyzf00,42,42,0|w6wofz,42,42,0|w6wog0,39,44,1|whphnz,39,44,1|whpho0,42,42,0|wpmr3z,42,42,0|wpmr40,39,44,1|x0fkbz,39,44,1|x0fkc0,42,42,0|x8ctrz,42,42,0|x8cts0,39,44,1|xj5mzz,39,44,1|xj5n00,42,42,0|xr2wfz,42,42,0|xr2wg0,39,44,1|y28obz,39,44,1|y28oc0,42,42,0|y9sz3z,42,42,0|y9sz40,39,44,1|ykyqzz,39,44,1|ykyr00,42,42,0|ysw0fz,42,42,0|ysw0g0,39,44,1|z3otnz,39,44,1|z3oto0,42,42,0|zbm33z,42,42,0|zbm340,39,44,1\",\"America/Santo_Domingo|,0,174,0|-15r0we0,87,175,0|-j6hz1d,87,175,0|-j6hz1c,49,63,0|-1nlws1,49,63,0|-1nlws0,62,42,1|-1hdww1,62,42,1|-1hdww0,49,63,0|-3fos1,49,63,0|-3fos0,43,59,1|2mshz,43,59,1|2msi0,49,63,0|fadvz,49,63,0|fadw0,43,59,1|jrghz,43,59,1|jrgi0,49,63,0|ydf7z,49,63,0|ydf80,43,59,1|12l8hz,43,59,1|12l8i0,49,63,0|1h3hvz,49,63,0|1h3hw0,43,59,1|1lf0hz,43,59,1|1lf0i0,49,63,0|1ztkjz,49,63,0|1ztkk0,43,59,1|246xtz,43,59,1|246xu0,49,63,0|2ijn7z,49,63,0|2ijn80,32,42,0|g36gnz,32,42,0|g36go0,49,63,0|g4z9zz,49,63,0|g4za00,32,42,0\",\"America/Sao_Paulo|,0,176,0|-t85jd8,39,44,0|-jylag1,39,44,0|-jylag0,40,45,1|-jpb141,40,45,1|-jpb140,39,44,0|-jfsd01,39,44,0|-jfsd00,40,45,1|-j6j3s1,40,45,1|-j6j3s0,39,44,0|-ahcyc1,39,44,0|-ahcyc0,40,45,1|-aad3o1,40,45,1|-aad3o0,39,44,0|-9yl101,39,44,0|-9yl100,40,45,1|-9sd141,40,45,1|-9sd140,39,44,0|-9ft3o1,39,44,0|-9ft3o0,40,45,1|-99j941,40,45,1|-99j940,39,44,0|-8wzbo1,39,44,0|-8wzbo0,40,45,1|-8scqg1,40,45,1|-8scqg0,39,44,0|-38cno1,39,44,0|-38cno0,40,45,1|-31nzs1,40,45,1|-31nzs0,39,44,0|-2kdp01,39,44,0|-2kdp00,40,45,1|-2hcig1,40,45,1|-2hcig0,39,44,0|-24qqc1,39,44,0|-24qqc0,40,45,1|-204541,40,45,1|-204540,39,44,0|-1nid01,39,44,0|-1nid00,40,45,1|-1hc7s1,40,45,1|-1hc7s0,39,44,0|-14qfo1,39,44,0|-14qfo0,40,45,1|-yifs1,40,45,1|-yifs0,39,44,0|89jcbz,39,44,0|89jcc0,40,45,1|8gdk7z,40,45,1|8gdk80,39,44,0|8rwgbz,39,44,0|8rwgc0,40,45,1|8xns7z,40,45,1|8xns80,39,44,0|9aodnz,39,44,0|9aodo0,40,45,1|9g2qvz,40,45,1|9g2qw0,39,44,0|9t1hnz,39,44,0|9t1ho0,40,45,1|9yfuvz,40,45,1|9yfuw0,39,44,0|abrkbz,39,44,0|abrkc0,40,45,1|ahvuvz,40,45,1|ahvuw0,39,44,0|auulnz,39,44,0|auulo0,40,45,1|b0yw7z,40,45,1|b0yw80,39,44,0|bdkobz,39,44,0|bdkoc0,40,45,1|bjc07z,40,45,1|bjc080,39,44,0|bwnpnz,39,44,0|bwnpo0,40,45,1|c1p47z,40,45,1|c1p480,39,44,0|cf0tnz,39,44,0|cf0to0,40,45,1|cli2vz,40,45,1|cli2w0,39,44,0|cxqwbz,39,44,0|cxqwc0,40,45,1|d485jz,40,45,1|d485k0,39,44,0|dggyzz,39,44,0|dggz00,40,45,1|dml9jz,40,45,1|dml9k0,39,44,0|dyu2zz,39,44,0|dyu300,40,45,1|e5oavz,40,45,1|e5oaw0,39,44,0|ehm0bz,39,44,0|ehm0c0,40,45,1|ep4avz,40,45,1|ep4aw0,39,44,0|f0n6zz,39,44,0|f0n700,40,45,1|f7hevz,40,45,1|f7hew0,39,44,0|fj0azz,39,44,0|fj0b00,40,45,1|fqkg7z,40,45,1|fqkg80,39,44,0|g23cbz,39,44,0|g23cc0,40,45,1|g8xk7z,40,45,1|g8xk80,39,44,0|gl6dnz,39,44,0|gl6do0,40,45,1|grnmvz,40,45,1|grnmw0,39,44,0|h4zcbz,39,44,0|h4zcc0,40,45,1|hadpjz,40,45,1|hadpk0,39,44,0|hmzhnz,39,44,0|hmzho0,40,45,1|ht3s7z,40,45,1|ht3s80,39,44,0|i6j6zz,39,44,0|i6j700,40,45,1|ic6tjz,40,45,1|ic6tk0,39,44,0|iofmzz,39,44,0|iofn00,40,45,1|iuww7z,40,45,1|iuww80,39,44,0|j88lnz,39,44,0|j88lo0,40,45,1|jdzxjz,40,45,1|jdzxk0,39,44,0|jpvsbz,39,44,0|jpvsc0,40,45,1|jwd1jz,40,45,1|jwd1k0,39,44,0|k8ytnz,39,44,0|k8yto0,40,45,1|kf347z,40,45,1|kf3480,39,44,0|krowbz,39,44,0|krowc0,40,45,1|ky65jz,40,45,1|ky65k0,39,44,0|laeyzz,39,44,0|laez00,40,45,1|lgw87z,40,45,1|lgw880,39,44,0|lt51nz,39,44,0|lt51o0,40,45,1|lzz9jz,40,45,1|lzz9k0,39,44,0|mc82zz,39,44,0|mc8300,40,45,1|micdjz,40,45,1|micdk0,39,44,0|muy5nz,39,44,0|muy5o0,40,45,1|n12g7z,40,45,1|n12g80,39,44,0|ndo8bz,39,44,0|ndo8c0,40,45,1|nk5hjz,40,45,1|nk5hk0,39,44,0|nweazz,39,44,0|nweb00,40,45,1|o2vk7z,40,45,1|o2vk80,39,44,0|of4dnz,39,44,0|of4do0,40,45,1|ollmvz,40,45,1|ollmw0,39,44,0|oxugbz,39,44,0|oxugc0,40,45,1|p4bpjz,40,45,1|p4bpk0,39,44,0|phnezz,39,44,0|phnf00,40,45,1|pn1s7z,40,45,1|pn1s80,39,44,0\",\"America/Scoresbysund|,0,177,0|-rvurxk,40,45,0|5ct1rz,40,45,0|5ct1s0,13,15,1|5lt4fz,13,15,1|5lt4g0,40,45,0|5v607z,40,45,0|5v6080,17,1,1|64iyrz,17,1,1|64iys0,13,15,0|6dw03z,13,15,0|6dw040,17,1,1|6n91fz,17,1,1|6n91g0,13,15,0|6wm2rz,13,15,0|6wm2s0,17,1,1|75z43z,17,1,1|75z440,13,15,0|7fc5fz,13,15,0|7fc5g0,17,1,1|7p25fz,17,1,1|7p25g0,13,15,0|7yf6rz,13,15,0|7yf6s0,17,1,1|87s83z,17,1,1|87s840,13,15,0|8h59fz,13,15,0|8h59g0,17,1,1|8qiarz,17,1,1|8qias0,13,15,0|8zvc3z,13,15,0|8zvc40,17,1,1|998dfz,17,1,1|998dg0,13,15,0|9ilerz,13,15,0|9iles0,17,1,1|9ryg3z,17,1,1|9ryg40,13,15,0|a1bhfz,13,15,0|a1bhg0,17,1,1|aaoirz,17,1,1|aaois0,13,15,0|ak1k3z,13,15,0|ak1k40,17,1,1|atrk3z,17,1,1|atrk40,13,15,0|b34lfz,13,15,0|b34lg0,17,1,1|bchmrz,17,1,1|bchms0,13,15,0|bluo3z,13,15,0|bluo40,17,1,1|bv7pfz,17,1,1|bv7pg0,13,15,0|c4kqrz,13,15,0|c4kqs0,17,1,1|cdxs3z,17,1,1|cdxs40,13,15,0|cnatfz,13,15,0|cnatg0,17,1,1|cwnurz,17,1,1|cwnus0,13,15,0|d60w3z,13,15,0|d60w40,17,1,1|dfdxfz,17,1,1|dfdxg0,13,15,0|dp3xfz,13,15,0|dp3xg0,17,1,1|dzwtfz,17,1,1|dzwtg0,13,15,0|e7u03z,13,15,0|e7u040,17,1,1|eimw3z,17,1,1|eimw40,13,15,0|eqk2rz,13,15,0|eqk2s0,17,1,1|f1cyrz,17,1,1|f1cys0,13,15,0|f9a5fz,13,15,0|f9a5g0,17,1,1|fkg03z,17,1,1|fkg040,13,15,0|fs083z,13,15,0|fs0840,17,1,1|g362rz,17,1,1|g362s0,13,15,0|gaqarz,13,15,0|gaqas0,17,1,1|glw5fz,17,1,1|glw5g0,13,15,0|gttc3z,13,15,0|gttc40,17,1,1|h4m83z,17,1,1|h4m840,13,15,0|hcjerz,13,15,0|hcjes0,17,1,1|hncarz,17,1,1|hncas0,13,15,0|hv9hfz,13,15,0|hv9hg0,17,1,1|i6fc3z,17,1,1|i6fc40,13,15,0|idzk3z,13,15,0|idzk40,17,1,1|ip5erz,17,1,1|ip5es0,13,15,0|iwpmrz,13,15,0|iwpms0,17,1,1|j7vhfz,17,1,1|j7vhg0,13,15,0|jffpfz,13,15,0|jffpg0,17,1,1|jqlk3z,17,1,1|jqlk40,13,15,0|jyiqrz,13,15,0|jyiqs0,17,1,1|k9bmrz,17,1,1|k9bms0,13,15,0|kh8tfz,13,15,0|kh8tg0,17,1,1|ks1pfz,17,1,1|ks1pg0,13,15,0|kzyw3z,13,15,0|kzyw40,17,1,1|lb4qrz,17,1,1|lb4qs0,13,15,0|lioyrz,13,15,0|lioys0,17,1,1|ltutfz,17,1,1|ltutg0,13,15,0|m1f1fz,13,15,0|m1f1g0,17,1,1|mckw3z,17,1,1|mckw40,13,15,0|mki2rz,13,15,0|mki2s0,17,1,1|mvayrz,17,1,1|mvays0,13,15,0|n385fz,13,15,0|n385g0,17,1,1|ne11fz,17,1,1|ne11g0,13,15,0|nly83z,13,15,0|nly840,17,1,1|nwr43z,17,1,1|nwr440,13,15,0|o4oarz,13,15,0|o4oas0,17,1,1|ofu5fz,17,1,1|ofu5g0,13,15,0|onedfz,13,15,0|onedg0,17,1,1|oyk83z,17,1,1|oyk840,13,15,0|p64g3z,13,15,0|p64g40,17,1,1|phaarz,17,1,1|phaas0,13,15,0|pp7hfz,13,15,0|pp7hg0,17,1,1|q00dfz,17,1,1|q00dg0,13,15,0|q7xk3z,13,15,0|q7xk40,17,1,1|qiqg3z,17,1,1|qiqg40,13,15,0|qqnmrz,13,15,0|qqnms0,17,1,1|r1thfz,17,1,1|r1thg0,13,15,0|r9dpfz,13,15,0|r9dpg0,17,1,1|rkjk3z,17,1,1|rkjk40,13,15,0|rs3s3z,13,15,0|rs3s40,17,1,1|s39mrz,17,1,1|s39ms0,13,15,0|sb6tfz,13,15,0|sb6tg0,17,1,1|slzpfz,17,1,1|slzpg0,13,15,0|stww3z,13,15,0|stww40,17,1,1|t4ps3z,17,1,1|t4ps40,13,15,0|tcmyrz,13,15,0|tcmys0,17,1,1|tnfurz,17,1,1|tnfus0,13,15,0|tvd1fz,13,15,0|tvd1g0,17,1,1|u6iw3z,17,1,1|u6iw40,13,15,0|ue343z,13,15,0|ue3440,17,1,1|up8yrz,17,1,1|up8ys0,13,15,0|uwt6rz,13,15,0|uwt6s0,17,1,1|v7z1fz,17,1,1|v7z1g0,13,15,0|vfw83z,13,15,0|vfw840,17,1,1|vqp43z,17,1,1|vqp440,13,15,0|vymarz,13,15,0|vymas0,17,1,1|w9f6rz,17,1,1|w9f6s0,13,15,0|whcdfz,13,15,0|whcdg0,17,1,1|wsi83z,17,1,1|wsi840,13,15,0|x02g3z,13,15,0|x02g40,17,1,1|xb8arz,17,1,1|xb8as0,13,15,0|xisirz,13,15,0|xisis0,17,1,1|xtydfz,17,1,1|xtydg0,13,15,0|y1ilfz,13,15,0|y1ilg0,17,1,1|ycog3z,17,1,1|ycog40,13,15,0|yklmrz,13,15,0|yklms0,17,1,1|yveirz,17,1,1|yveis0,13,15,0|z3bpfz,13,15,0|z3bpg0,17,1,1|ze4lfz,17,1,1|ze4lg0,13,15,0\",\"America/Sitka|,0,178,0|-1hc7qjz,0,179,0|-1078wa0,0,179,0|-1078w9z,51,40,0|-ek1w81,51,40,0|-ek1w80,68,66,1|-cq2tg1,68,66,1|-cq2tg0,69,66,1|-cnov01,69,66,1|-cnov00,51,40,0|-csc81,51,40,0|-csc80,57,66,1|-3fdo1,57,66,1|-3fdo0,51,40,0|5xqfz,51,40,0|5xqg0,57,66,1|faozz,57,66,1|fap00,51,40,0|ont3z,51,40,0|ont40,57,66,1|ydqbz,57,66,1|ydqc0,51,40,0|17qufz,51,40,0|17qug0,57,66,1|1h3szz,57,66,1|1h3t00,51,40,0|1qgx3z,51,40,0|1qgx40,57,66,1|1ztvnz,57,66,1|1ztvo0,51,40,0|23fl3z,51,40,0|23fl40,57,66,1|2ijybz,57,66,1|2ijyc0,51,40,0|2ooefz,51,40,0|2ooeg0,57,66,1|31a0zz,57,66,1|31a100,51,40,0|3an53z,51,40,0|3an540,57,66,1|3kd2bz,57,66,1|3kd2c0,51,40,0|3td7rz,51,40,0|3td7s0,57,66,1|4334zz,57,66,1|433500,51,40,0|4cg93z,51,40,0|4cg940,57,66,1|4lt7nz,57,66,1|4lt7o0,51,40,0|4v6brz,51,40,0|4v6bs0,57,66,1|54jabz,57,66,1|54jac0,51,40,0|5dwefz,51,40,0|5dweg0,57,66,1|5n9czz,57,66,1|5n9d00,51,40,0|5wmh3z,51,40,0|5wmh40,57,66,1|65zfnz,57,66,1|65zfo0,51,40,0|6fcjrz,51,40,0|6fcjs0,57,66,1|6p2gzz,57,66,1|6p2h00,51,40,0|6y2mfz,51,40,0|6y2mg0,57,66,1|77sjnz,57,66,1|77sjo0,36,37,0|79dybz,36,37,0|79dyc0,37,37,0|7h5qjz,37,37,0|7h5qk0,38,40,1|7qip3z,38,40,1|7qip40,37,37,0|7zvt7z,37,37,0|7zvt80,38,40,1|898rrz,38,40,1|898rs0,37,37,0|8ilvvz,37,37,0|8ilvw0,38,40,1|8ryufz,38,40,1|8ryug0,37,37,0|9092jz,37,37,0|9092k0,38,40,1|9aox3z,38,40,1|9aox40,37,37,0|9iz57z,37,37,0|9iz580,38,40,1|9tryfz,38,40,1|9tryg0,37,37,0|a1p7vz,37,37,0|a1p7w0,38,40,1|aci13z,38,40,1|aci140,37,37,0|akfajz,37,37,0|akfak0,38,40,1|av83rz,38,40,1|av83s0,37,37,0|b3ibvz,37,37,0|b3ibw0,38,40,1|bdy6fz,38,40,1|bdy6g0,37,37,0|bm8ejz,37,37,0|bm8ek0,38,40,1|bwo93z,38,40,1|bwo940,37,37,0|c4yh7z,37,37,0|c4yh80,38,40,1|cfrafz,38,40,1|cfrag0,37,37,0|cnojvz,37,37,0|cnojw0,38,40,1|cyhd3z,38,40,1|cyhd40,37,37,0|d6emjz,37,37,0|d6emk0,38,40,1|dh7frz,38,40,1|dh7fs0,37,37,0|dphnvz,37,37,0|dphnw0,38,40,1|dzxifz,38,40,1|dzxig0,37,37,0|e87qjz,37,37,0|e87qk0,38,40,1|einl3z,38,40,1|einl40,37,37,0|eqxt7z,37,37,0|eqxt80,38,40,1|f1dnrz,38,40,1|f1dns0,37,37,0|f9nvvz,37,37,0|f9nvw0,38,40,1|fkgp3z,38,40,1|fkgp40,37,37,0|fsdyjz,37,37,0|fsdyk0,38,40,1|g36rrz,38,40,1|g36rs0,37,37,0|gb417z,37,37,0|gb4180,38,40,1|glwufz,38,40,1|glwug0,37,37,0|gu72jz,37,37,0|gu72k0,38,40,1|h4mx3z,38,40,1|h4mx40,37,37,0|hcx57z,37,37,0|hcx580,38,40,1|hnczrz,38,40,1|hnczs0,37,37,0|hvn7vz,37,37,0|hvn7w0,38,40,1|i6g13z,38,40,1|i6g140,37,37,0|iedajz,37,37,0|iedak0,38,40,1|ip63rz,38,40,1|ip63s0,37,37,0|ix3d7z,37,37,0|ix3d80,38,40,1|j7w6fz,38,40,1|j7w6g0,37,37,0|jeqjvz,37,37,0|jeqjw0,38,40,1|jqz7rz,38,40,1|jqz7s0,37,37,0|jxgmjz,37,37,0|jxgmk0,38,40,1|k9pafz,38,40,1|k9pag0,37,37,0|kg6p7z,37,37,0|kg6p80,38,40,1|ksfd3z,38,40,1|ksfd40,37,37,0|kz9qjz,37,37,0|kz9qk0,38,40,1|lbiefz,38,40,1|lbieg0,37,37,0|lhzt7z,37,37,0|lhzt80,38,40,1|lu8h3z,38,40,1|lu8h40,37,37,0|m0pvvz,37,37,0|m0pvw0,38,40,1|mcyjrz,38,40,1|mcyjs0,37,37,0|mjfyjz,37,37,0|mjfyk0,38,40,1|mvomfz,38,40,1|mvomg0,37,37,0|n2617z,37,37,0|n26180,38,40,1|neep3z,38,40,1|neep40,37,37,0|nkw3vz,37,37,0|nkw3w0,38,40,1|nx4rrz,38,40,1|nx4rs0,37,37,0|o3z57z,37,37,0|o3z580,38,40,1|og7t3z,38,40,1|og7t40,37,37,0|omp7vz,37,37,0|omp7w0,38,40,1|oyxvrz,38,40,1|oyxvs0,37,37,0|p5fajz,37,37,0|p5fak0,38,40,1|phnyfz,38,40,1|phnyg0,37,37,0|po5d7z,37,37,0|po5d80,38,40,1|q0e13z,38,40,1|q0e140,37,37,0|q6vfvz,37,37,0|q6vfw0,38,40,1|qj43rz,38,40,1|qj43s0,37,37,0|qpyh7z,37,37,0|qpyh80,38,40,1|r2753z,38,40,1|r27540,37,37,0|r8ojvz,37,37,0|r8ojw0,38,40,1|rkx7rz,38,40,1|rkx7s0,37,37,0|rremjz,37,37,0|rremk0,38,40,1|s3nafz,38,40,1|s3nag0,37,37,0|sa4p7z,37,37,0|sa4p80,38,40,1|smdd3z,38,40,1|smdd40,37,37,0|ssurvz,37,37,0|ssurw0,38,40,1|t53frz,38,40,1|t53fs0,37,37,0|tbkujz,37,37,0|tbkuk0,38,40,1|tntifz,38,40,1|tntig0,37,37,0|tunvvz,37,37,0|tunvw0,38,40,1|u6wjrz,38,40,1|u6wjs0,37,37,0|uddyjz,37,37,0|uddyk0,38,40,1|upmmfz,38,40,1|upmmg0,37,37,0|uw417z,37,37,0|uw4180,38,40,1|v8cp3z,38,40,1|v8cp40,37,37,0|veu3vz,37,37,0|veu3w0,38,40,1|vr2rrz,38,40,1|vr2rs0,37,37,0|vxk6jz,37,37,0|vxk6k0,38,40,1|w9sufz,38,40,1|w9sug0,37,37,0|wgn7vz,37,37,0|wgn7w0,38,40,1|wsvvrz,38,40,1|wsvvs0,37,37,0|wzdajz,37,37,0|wzdak0,38,40,1|xblyfz,38,40,1|xblyg0,37,37,0|xi3d7z,37,37,0|xi3d80,38,40,1|xuc13z,38,40,1|xuc140,37,37,0|y0tfvz,37,37,0|y0tfw0,38,40,1|yd23rz,38,40,1|yd23s0,37,37,0|yjjijz,37,37,0|yjjik0,38,40,1|yvs6fz,38,40,1|yvs6g0,37,37,0|z29l7z,37,37,0|z29l80,38,40,1|zei93z,38,40,1|zei940,37,37,0\",\"America/St_Barthelemy|,0,41,0|-u6m79w,32,42,0\",\"America/St_Johns|,0,99,0|-18vs8wk,24,99,0|-ris3cl,24,99,0|-ris3ck,72,100,1|-rag64l,72,100,1|-rag64k,24,99,0|-qzp20l,24,99,0|-qzp20k,72,100,1|-qpm64l,72,100,1|-qpm64k,24,99,0|-qfsmcl,24,99,0|-qfsmck,72,100,1|-qapd4l,72,100,1|-qapd4k,24,99,0|-px4ecl,24,99,0|-px4eck,72,100,1|-pnrfsl,72,100,1|-pnrfsk,24,99,0|-peebol,24,99,0|-peebok,72,100,1|-p51d4l,72,100,1|-p51d4k,24,99,0|-ovbacl,24,99,0|-ovback,72,100,1|-ombagl,72,100,1|-ombagk,24,99,0|-ocl7ol,24,99,0|-ocl7ok,72,100,1|-o3l7sl,72,100,1|-o3l7sk,24,99,0|-ntv50l,24,99,0|-ntv50k,72,100,1|-nkv54l,72,100,1|-nkv54k,24,99,0|-nb52cl,24,99,0|-nb52ck,72,100,1|-n252gl,72,100,1|-n252gk,24,99,0|-msezol,24,99,0|-msezok,72,100,1|-mj214l,72,100,1|-mj214k,24,99,0|-m9ox0l,24,99,0|-m9ox0k,72,100,1|-m0bygl,72,100,1|-m0bygk,24,99,0|-lqlvol,24,99,0|-lqlvok,72,100,1|-lhlvsl,72,100,1|-lhlvsk,24,99,0|-l7vt0l,24,99,0|-l7vt0k,72,100,1|-kyvt4l,72,100,1|-kyvt4k,24,99,0|-kp5qcl,24,99,0|-kp5qck,72,100,1|-kg5qgl,72,100,1|-kg5qgk,24,99,0|-k6fnol,24,99,0|-k6fnok,72,100,1|-jxfnsl,72,100,1|-jxfnsk,24,99,0|-jnpl0l,24,99,0|-jnpl0k,72,100,1|-jecmgl,72,100,1|-jecmgk,24,99,0|-j4mjol,24,99,0|-j4mjok,72,100,1|-ivmjsl,72,100,1|-ivmjsk,24,99,0|-ilwh0l,24,99,0|-ilwh0k,72,100,1|-icwh4l,72,100,1|-icwh4k,24,99,0|-i52u8l,24,99,0|-i52u8k,24,101,0|-i36ee1,24,101,0|-i36ee0,72,102,1|-hu6ei1,72,102,1|-hu6ei0,24,101,0|-hk3aa1,24,101,0|-hk3aa0,72,102,1|-hcj521,72,102,1|-hcj520,24,101,0|-h1d7m1,24,101,0|-h1d7m0,72,102,1|-gtt2e1,72,102,1|-gtt2e0,24,101,0|-gin4y1,24,101,0|-gin4y0,72,102,1|-gb2zq1,72,102,1|-gb2zq0,24,101,0|-fzk3m1,24,101,0|-fzk3m0,72,102,1|-fscx21,72,102,1|-fscx20,24,101,0|-fgu0y1,24,101,0|-fgu0y0,72,102,1|-f99vq1,72,102,1|-f99vq0,24,101,0|-ey3ya1,24,101,0|-ey3ya0,72,102,1|-eqjt21,72,102,1|-eqjt20,24,101,0|-efdvm1,24,101,0|-efdvm0,25,102,1|-cq2tg1,25,102,1|-cq2tg0,26,102,1|-cnp7i1,26,102,1|-cnp7i0,24,101,0|-cc6be1,24,101,0|-cc6be0,72,102,1|-c4m661,72,102,1|-c4m660,24,101,0|-btg8q1,24,101,0|-btg8q0,72,102,1|-blw3i1,72,102,1|-blw3i0,24,101,0|-baq621,24,101,0|-baq620,72,102,1|-b360u1,72,102,1|-b360u0,24,101,0|-as03e1,24,101,0|-as03e0,72,102,1|-akfy61,72,102,1|-akfy60,24,101,0|-a8x221,24,101,0|-a8x220,72,102,1|-a1cwu1,72,102,1|-a1cwu0,24,101,0|-9qwwq1,24,101,0|-9qwwq0,72,102,1|-9izsu1,72,102,1|-9izsu0,24,101,0|-986u21,24,101,0|-986u20,72,102,1|-909q61,72,102,1|-909q60,24,101,0|-8pgre1,24,101,0|-8pgre0,72,102,1|-8hjni1,72,102,1|-8hjni0,24,101,0|-86qoq1,24,101,0|-86qoq0,72,102,1|-7ytku1,72,102,1|-7ytku0,24,101,0|-7o0m21,24,101,0|-7o0m20,72,102,1|-7g3i61,72,102,1|-7g3i60,24,101,0|-74xkq1,24,101,0|-74xkq0,72,102,1|-6x0gu1,72,102,1|-6x0gu0,24,101,0|-6m7i21,24,101,0|-6m7i20,72,102,1|-6eae61,72,102,1|-6eae60,24,101,0|-63hfe1,24,101,0|-63hfe0,72,102,1|-5vkbi1,72,102,1|-5vkbi0,24,101,0|-5krcq1,24,101,0|-5krcq0,72,102,1|-5cu8u1,72,102,1|-5cu8u0,24,101,0|-521a21,24,101,0|-521a20,72,102,1|-4sbcu1,72,102,1|-4sbcu0,24,101,0|-4iy8q1,24,101,0|-4iy8q0,72,102,1|-49la61,72,102,1|-49la60,24,101,0|-408621,24,101,0|-408620,72,102,1|-3qv7i1,72,102,1|-3qv7i0,24,101,0|-3hi3e1,24,101,0|-3hi3e0,72,102,1|-3854u1,72,102,1|-3854u0,24,101,0|-2ys0q1,24,101,0|-2ys0q0,72,102,1|-2pf261,72,102,1|-2pf260,24,101,0|-2g1y21,24,101,0|-2g1y20,72,102,1|-26c0u1,72,102,1|-26c0u0,24,101,0|-1xbve1,24,101,0|-1xbve0,72,102,1|-1nly61,72,102,1|-1nly60,24,101,0|-1e8u21,24,101,0|-1e8u20,72,102,1|-14vvi1,72,102,1|-14vvi0,24,101,0|-vire1,24,101,0|-vire0,72,102,1|-m5su1,72,102,1|-m5su0,24,101,0|-csoq1,24,101,0|-csoq0,72,102,1|-3fq61,72,102,1|-3fq60,24,101,0|5xdxz,24,101,0|5xdy0,72,102,1|fachz,72,102,1|faci0,24,101,0|onglz,24,101,0|ongm0,72,102,1|yddtz,72,102,1|yddu0,24,101,0|17qhxz,24,101,0|17qhy0,72,102,1|1h3ghz,72,102,1|1h3gi0,24,101,0|1qgklz,24,101,0|1qgkm0,72,102,1|1ztj5z,72,102,1|1ztj60,24,101,0|296n9z,24,101,0|296na0,72,102,1|2ijltz,72,102,1|2ijlu0,24,101,0|2rwpxz,24,101,0|2rwpy0,72,102,1|319ohz,72,102,1|319oi0,24,101,0|3amslz,24,101,0|3amsm0,72,102,1|3kcptz,72,102,1|3kcpu0,24,101,0|3tcv9z,24,101,0|3tcva0,72,102,1|432shz,72,102,1|432si0,24,101,0|4cfwlz,24,101,0|4cfwm0,72,102,1|4lsv5z,72,102,1|4lsv60,24,101,0|4v5z9z,24,101,0|4v5za0,72,102,1|54ixtz,72,102,1|54ixu0,24,101,0|5dw1xz,24,101,0|5dw1y0,72,102,1|5n90hz,72,102,1|5n90i0,24,101,0|5wm4lz,24,101,0|5wm4m0,72,102,1|65z35z,72,102,1|65z360,24,101,0|6fc79z,24,101,0|6fc7a0,72,102,1|6p24hz,72,102,1|6p24i0,24,101,0|6y29xz,24,101,0|6y29y0,72,102,1|77s75z,72,102,1|77s760,24,101,0|7h5b9z,24,101,0|7h5ba0,72,102,1|7qi9tz,72,102,1|7qi9u0,24,101,0|7zvdxz,24,101,0|7zvdy0,72,102,1|898chz,72,102,1|898ci0,24,101,0|8ilglz,24,101,0|8ilgm0,72,102,1|8ryf5z,72,102,1|8ryf60,24,101,0|908hrn,24,101,0|908hro,72,102,1|9aocbn,72,102,1|9aocbo,24,101,0|9iykfn,24,101,0|9iykfo,88,147,1|9travn,88,147,1|9travo,24,101,0|a1on3n,24,101,0|a1on3o,72,102,1|achgbn,72,102,1|achgbo,24,101,0|akeprn,24,101,0|akepro,72,102,1|av7izn,72,102,1|av7izo,24,101,0|b3hr3n,24,101,0|b3hr3o,72,102,1|bdxlnn,72,102,1|bdxlno,24,101,0|bm7trn,24,101,0|bm7tro,72,102,1|bwnobn,72,102,1|bwnobo,24,101,0|c4xwfn,24,101,0|c4xwfo,72,102,1|cfqpnn,72,102,1|cfqpno,24,101,0|cnnz3n,24,101,0|cnnz3o,72,102,1|cygsbn,72,102,1|cygsbo,24,101,0|d6e1rn,24,101,0|d6e1ro,72,102,1|dh6uzn,72,102,1|dh6uzo,24,101,0|dph33n,24,101,0|dph33o,72,102,1|dzwxnn,72,102,1|dzwxno,24,101,0|e875rn,24,101,0|e875ro,72,102,1|ein0bn,72,102,1|ein0bo,24,101,0|eqx8fn,24,101,0|eqx8fo,72,102,1|f1d2zn,72,102,1|f1d2zo,24,101,0|f9nb3n,24,101,0|f9nb3o,72,102,1|fkg4bn,72,102,1|fkg4bo,24,101,0|fsddrn,24,101,0|fsddro,72,102,1|g366zn,72,102,1|g366zo,24,101,0|gb3gfn,24,101,0|gb3gfo,72,102,1|glw9nn,72,102,1|glw9no,24,101,0|gu6hrn,24,101,0|gu6hro,72,102,1|h4mcbn,72,102,1|h4mcbo,24,101,0|hcwkfn,24,101,0|hcwkfo,72,102,1|hncezn,72,102,1|hncezo,24,101,0|hvmn3n,24,101,0|hvmn3o,72,102,1|i6fgbn,72,102,1|i6fgbo,24,101,0|iecprn,24,101,0|iecpro,72,102,1|ip5izn,72,102,1|ip5izo,24,101,0|ix2sfn,24,101,0|ix2sfo,72,102,1|j7vlnn,72,102,1|j7vlno,24,101,0|jepz3n,24,101,0|jepz3o,72,102,1|jqymzn,72,102,1|jqymzo,24,101,0|jxg1rn,24,101,0|jxg1ro,72,102,1|k9opnn,72,102,1|k9opno,24,101,0|kg64fn,24,101,0|kg64fo,72,102,1|ksesbn,72,102,1|ksesbo,24,101,0|kz95rn,24,101,0|kz95ro,72,102,1|lbhtnn,72,102,1|lbhtno,24,101,0|lhz8fn,24,101,0|lhz8fo,72,102,1|lu81tz,72,102,1|lu81u0,24,101,0|m0pglz,24,101,0|m0pgm0,72,102,1|mcy4hz,72,102,1|mcy4i0,24,101,0|mjfj9z,24,101,0|mjfja0,72,102,1|mvo75z,72,102,1|mvo760,24,101,0|n25lxz,24,101,0|n25ly0,72,102,1|nee9tz,72,102,1|nee9u0,24,101,0|nkvolz,24,101,0|nkvom0,72,102,1|nx4chz,72,102,1|nx4ci0,24,101,0|o3ypxz,24,101,0|o3ypy0,72,102,1|og7dtz,72,102,1|og7du0,24,101,0|omoslz,24,101,0|omosm0,72,102,1|oyxghz,72,102,1|oyxgi0,24,101,0|p5ev9z,24,101,0|p5eva0,72,102,1|phnj5z,72,102,1|phnj60,24,101,0|po4xxz,24,101,0|po4xy0,72,102,1|q0dltz,72,102,1|q0dlu0,24,101,0|q6v0lz,24,101,0|q6v0m0,72,102,1|qj3ohz,72,102,1|qj3oi0,24,101,0|qpy1xz,24,101,0|qpy1y0,72,102,1|r26ptz,72,102,1|r26pu0,24,101,0|r8o4lz,24,101,0|r8o4m0,72,102,1|rkwshz,72,102,1|rkwsi0,24,101,0|rre79z,24,101,0|rre7a0,72,102,1|s3mv5z,72,102,1|s3mv60,24,101,0|sa49xz,24,101,0|sa49y0,72,102,1|smcxtz,72,102,1|smcxu0,24,101,0|ssuclz,24,101,0|ssucm0,72,102,1|t530hz,72,102,1|t530i0,24,101,0|tbkf9z,24,101,0|tbkfa0,72,102,1|tnt35z,72,102,1|tnt360,24,101,0|tunglz,24,101,0|tungm0,72,102,1|u6w4hz,72,102,1|u6w4i0,24,101,0|uddj9z,24,101,0|uddja0,72,102,1|upm75z,72,102,1|upm760,24,101,0|uw3lxz,24,101,0|uw3ly0,72,102,1|v8c9tz,72,102,1|v8c9u0,24,101,0|vetolz,24,101,0|vetom0,72,102,1|vr2chz,72,102,1|vr2ci0,24,101,0|vxjr9z,24,101,0|vxjra0,72,102,1|w9sf5z,72,102,1|w9sf60,24,101,0|wgmslz,24,101,0|wgmsm0,72,102,1|wsvghz,72,102,1|wsvgi0,24,101,0|wzcv9z,24,101,0|wzcva0,72,102,1|xblj5z,72,102,1|xblj60,24,101,0|xi2xxz,24,101,0|xi2xy0,72,102,1|xubltz,72,102,1|xublu0,24,101,0|y0t0lz,24,101,0|y0t0m0,72,102,1|yd1ohz,72,102,1|yd1oi0,24,101,0|yjj39z,24,101,0|yjj3a0,72,102,1|yvrr5z,72,102,1|yvrr60,24,101,0|z295xz,24,101,0|z295y0,72,102,1|zehttz,72,102,1|zehtu0,24,101,0\",\"America/St_Kitts|,0,41,0|-u6m79w,32,42,0\",\"America/St_Lucia|,0,41,0|-u6m79w,32,42,0\",\"America/St_Thomas|,0,41,0|-u6m79w,32,42,0\",\"America/St_Vincent|,0,41,0|-u6m79w,32,42,0\",\"America/Swift_Current|,0,180,0|-xkq9d4,50,66,0|-qzosc1,50,66,0|-qzosc0,52,62,1|-qplwg1,52,62,1|-qplwg0,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-ccvz01,50,66,0|-ccvz00,52,62,1|-c48xs1,52,62,1|-c48xs0,50,66,0|-bu5wc1,50,66,0|-bu5wc0,52,62,1|-bm8sg1,52,62,1|-bm8sg0,50,66,0|-bbfto1,50,66,0|-bbfto0,52,62,1|-b3ips1,52,62,1|-b3ips0,50,66,0|-aspr01,50,66,0|-aspr00,52,62,1|-aksn41,52,62,1|-aksn40,50,66,0|-6m78c1,50,66,0|-6m78c0,52,62,1|-6cu9s1,52,62,1|-6cu9s0,50,66,0|-5kr301,50,66,0|-5kr300,52,62,1|-5be4g1,52,62,1|-5be4g0,50,66,0|-5210c1,50,66,0|-5210c0,52,62,1|-4u3wg1,52,62,1|-4u3wg0,50,66,0|-4ixz01,50,66,0|-4ixz00,52,62,1|-4bdts1,52,62,1|-4bdts0,50,66,0|17qrnz,50,66,0|17qro0,45,62,0\",\"America/Tegucigalpa|,0,181,0|-pfzh6k,45,62,0|91ojbz,45,62,0|91ojc0,46,63,1|998ojz,46,63,1|998ok0,45,62,0|9kelzz,45,62,0|9kem00,46,63,1|9ryr7z,46,63,1|9ryr80,45,62,0|iyvsnz,45,62,0|iyvso0,46,63,1|j3m37z,46,63,1|j3m380,45,62,0\",\"America/Thule|,0,182,0|-rvuj9g,32,42,0|b34zbz,32,42,0|b34zc0,54,44,1|bchxvz,54,44,1|bchxw0,32,42,0|blv1zz,32,42,0|blv200,54,44,1|bv80jz,54,44,1|bv80k0,32,42,0|c4y3bz,32,42,0|c4y3c0,54,44,1|cfqwjz,54,44,1|cfqwk0,32,42,0|cno5zz,32,42,0|cno600,54,44,1|cygz7z,54,44,1|cygz80,32,42,0|d6e8nz,32,42,0|d6e8o0,54,44,1|dh71vz,54,44,1|dh71w0,32,42,0|dph9zz,32,42,0|dpha00,54,44,1|dzx4jz,54,44,1|dzx4k0,32,42,0|e87cnz,32,42,0|e87co0,54,44,1|ein77z,54,44,1|ein780,32,42,0|eqxfbz,32,42,0|eqxfc0,54,44,1|f1d9vz,54,44,1|f1d9w0,32,42,0|f9nhzz,32,42,0|f9ni00,54,44,1|fkgb7z,54,44,1|fkgb80,32,42,0|fsdknz,32,42,0|fsdko0,54,44,1|g36dvz,54,44,1|g36dw0,32,42,0|gb3nbz,32,42,0|gb3nc0,54,44,1|glwgjz,54,44,1|glwgk0,32,42,0|gu6onz,32,42,0|gu6oo0,54,44,1|h4mj7z,54,44,1|h4mj80,32,42,0|hcwrbz,32,42,0|hcwrc0,54,44,1|hnclvz,54,44,1|hnclw0,32,42,0|hvmtzz,32,42,0|hvmu00,54,44,1|i6fn7z,54,44,1|i6fn80,32,42,0|iecwnz,32,42,0|iecwo0,54,44,1|ip5pvz,54,44,1|ip5pw0,32,42,0|ix2zbz,32,42,0|ix2zc0,54,44,1|j7vsjz,54,44,1|j7vsk0,32,42,0|jeq5zz,32,42,0|jeq600,54,44,1|jqytvz,54,44,1|jqytw0,32,42,0|jxg8nz,32,42,0|jxg8o0,54,44,1|k9owjz,54,44,1|k9owk0,32,42,0|kg6bbz,32,42,0|kg6bc0,54,44,1|ksez7z,54,44,1|ksez80,32,42,0|kz9cnz,32,42,0|kz9co0,54,44,1|lbi0jz,54,44,1|lbi0k0,32,42,0|lhzfbz,32,42,0|lhzfc0,54,44,1|lu837z,54,44,1|lu8380,32,42,0|m0phzz,32,42,0|m0pi00,54,44,1|mcy5vz,54,44,1|mcy5w0,32,42,0|mjfknz,32,42,0|mjfko0,54,44,1|mvo8jz,54,44,1|mvo8k0,32,42,0|n25nbz,32,42,0|n25nc0,54,44,1|neeb7z,54,44,1|neeb80,32,42,0|nkvpzz,32,42,0|nkvq00,54,44,1|nx4dvz,54,44,1|nx4dw0,32,42,0|o3yrbz,32,42,0|o3yrc0,54,44,1|og7f7z,54,44,1|og7f80,32,42,0|omotzz,32,42,0|omou00,54,44,1|oyxhvz,54,44,1|oyxhw0,32,42,0|p5ewnz,32,42,0|p5ewo0,54,44,1|phnkjz,54,44,1|phnkk0,32,42,0|po4zbz,32,42,0|po4zc0,54,44,1|q0dn7z,54,44,1|q0dn80,32,42,0|q6v1zz,32,42,0|q6v200,54,44,1|qj3pvz,54,44,1|qj3pw0,32,42,0|qpy3bz,32,42,0|qpy3c0,54,44,1|r26r7z,54,44,1|r26r80,32,42,0|r8o5zz,32,42,0|r8o600,54,44,1|rkwtvz,54,44,1|rkwtw0,32,42,0|rre8nz,32,42,0|rre8o0,54,44,1|s3mwjz,54,44,1|s3mwk0,32,42,0|sa4bbz,32,42,0|sa4bc0,54,44,1|smcz7z,54,44,1|smcz80,32,42,0|ssudzz,32,42,0|ssue00,54,44,1|t531vz,54,44,1|t531w0,32,42,0|tbkgnz,32,42,0|tbkgo0,54,44,1|tnt4jz,54,44,1|tnt4k0,32,42,0|tunhzz,32,42,0|tuni00,54,44,1|u6w5vz,54,44,1|u6w5w0,32,42,0|uddknz,32,42,0|uddko0,54,44,1|upm8jz,54,44,1|upm8k0,32,42,0|uw3nbz,32,42,0|uw3nc0,54,44,1|v8cb7z,54,44,1|v8cb80,32,42,0|vetpzz,32,42,0|vetq00,54,44,1|vr2dvz,54,44,1|vr2dw0,32,42,0|vxjsnz,32,42,0|vxjso0,54,44,1|w9sgjz,54,44,1|w9sgk0,32,42,0|wgmtzz,32,42,0|wgmu00,54,44,1|wsvhvz,54,44,1|wsvhw0,32,42,0|wzcwnz,32,42,0|wzcwo0,54,44,1|xblkjz,54,44,1|xblkk0,32,42,0|xi2zbz,32,42,0|xi2zc0,54,44,1|xubn7z,54,44,1|xubn80,32,42,0|y0t1zz,32,42,0|y0t200,54,44,1|yd1pvz,54,44,1|yd1pw0,32,42,0|yjj4nz,32,42,0|yjj4o0,54,44,1|yvrsjz,54,44,1|yvrsk0,32,42,0|z297bz,32,42,0|z297c0,54,44,1|zehv7z,54,44,1|zehv80,32,42,0\",\"America/Thunder_Bay|,0,183,0|-1353bh0,45,62,0|-vbavc1,45,62,0|-vbavc0,49,63,0|-ek24k1,49,63,0|-ek24k0,70,42,1|-cq2tg1,70,42,1|-cq2tg0,71,42,1|-cnp3c1,71,42,1|-cnp3c0,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|onkrz,49,63,0|onks0,62,42,1|ydhzz,62,42,1|ydi00,49,63,0|17qm3z,49,63,0|17qm40,62,42,1|1h3knz,62,42,1|1h3ko0,49,63,0|296rfz,49,63,0|296rg0,62,42,1|2ijpzz,62,42,1|2ijq00,49,63,0|2rwu3z,49,63,0|2rwu40,62,42,1|319snz,62,42,1|319so0,49,63,0|3amwrz,49,63,0|3amws0,62,42,1|3kctzz,62,42,1|3kcu00,49,63,0|3tczfz,49,63,0|3tczg0,62,42,1|432wnz,62,42,1|432wo0,49,63,0|4cg0rz,49,63,0|4cg0s0,62,42,1|4lszbz,62,42,1|4lszc0,49,63,0|4v63fz,49,63,0|4v63g0,62,42,1|54j1zz,62,42,1|54j200,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0|7h5ffz,49,63,0|7h5fg0,62,42,1|7qidzz,62,42,1|7qie00,49,63,0|7zvi3z,49,63,0|7zvi40,62,42,1|898gnz,62,42,1|898go0,49,63,0|8ilkrz,49,63,0|8ilks0,62,42,1|8ryjbz,62,42,1|8ryjc0,49,63,0|908rfz,49,63,0|908rg0,62,42,1|9aolzz,62,42,1|9aom00,49,63,0|9iyu3z,49,63,0|9iyu40,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1owrz,49,63,0|a1ows0,62,42,1|achpzz,62,42,1|achq00,49,63,0|akezfz,49,63,0|akezg0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3i0rz,49,63,0|b3i0s0,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm83fz,49,63,0|bm83g0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y63z,49,63,0|c4y640,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno8rz,49,63,0|cno8s0,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6ebfz,49,63,0|d6ebg0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,49,63,0|fsdnfz,49,63,0|fsdng0,62,42,1|g36gnz,62,42,1|g36go0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Tijuana|,0,184,0|-p1u1s0,50,66,0|-o0a9w1,50,66,0|-o0a9w0,51,40,0|-m7mhw1,51,40,0|-m7mhw0,50,66,0|-kf64k1,50,66,0|-kf64k0,51,40,0|-k84cg1,51,40,0|-k84cg0,57,66,1|-jyrdw1,57,66,1|-jyrdw0,51,40,0|-eg90g1,51,40,0|-eg90g0,68,66,1|-cq2tg1,68,66,1|-cq2tg0,69,66,1|-clhdw1,69,66,1|-clhdw0,51,40,0|-bcgxs1,51,40,0|-bcgxs0,57,66,1|-axv381,57,66,1|-axv380,51,40,0|-86qf01,51,40,0|-86qf00,57,66,1|-7yt8c1,57,66,1|-7yt8c0,51,40,0|-7o0cc1,51,40,0|-7o0cc0,57,66,1|-7g35o1,57,66,1|-7g35o0,51,40,0|-74xb01,51,40,0|-74xb00,57,66,1|-6x04c1,57,66,1|-6x04c0,51,40,0|-6m78c1,51,40,0|-6m78c0,57,66,1|-6ea1o1,57,66,1|-6ea1o0,51,40,0|-63h5o1,51,40,0|-63h5o0,57,66,1|-5vjz01,57,66,1|-5vjz00,51,40,0|-5kr301,51,40,0|-5kr300,57,66,1|-5ctwc1,57,66,1|-5ctwc0,51,40,0|-5210c1,51,40,0|-5210c0,57,66,1|-4u3to1,57,66,1|-4u3to0,51,40,0|3an53z,51,40,0|3an540,57,66,1|3kd2bz,57,66,1|3kd2c0,51,40,0|3td7rz,51,40,0|3td7s0,57,66,1|4334zz,57,66,1|433500,51,40,0|4cg93z,51,40,0|4cg940,57,66,1|4lt7nz,57,66,1|4lt7o0,51,40,0|4v6brz,51,40,0|4v6bs0,57,66,1|54jabz,57,66,1|54jac0,51,40,0|5dwefz,51,40,0|5dweg0,57,66,1|5n9czz,57,66,1|5n9d00,51,40,0|5wmh3z,51,40,0|5wmh40,57,66,1|65zfnz,57,66,1|65zfo0,51,40,0|6fcjrz,51,40,0|6fcjs0,57,66,1|6p2gzz,57,66,1|6p2h00,51,40,0|6y2mfz,51,40,0|6y2mg0,57,66,1|77sjnz,57,66,1|77sjo0,51,40,0|7h5nrz,51,40,0|7h5ns0,57,66,1|7qimbz,57,66,1|7qimc0,51,40,0|7zvqfz,51,40,0|7zvqg0,57,66,1|898ozz,57,66,1|898p00,51,40,0|8ilt3z,51,40,0|8ilt40,57,66,1|8ryrnz,57,66,1|8ryro0,51,40,0|908zrz,51,40,0|908zs0,57,66,1|9aoubz,57,66,1|9aouc0,51,40,0|9iz2fz,51,40,0|9iz2g0,57,66,1|9trvnz,57,66,1|9trvo0,51,40,0|a1p53z,51,40,0|a1p540,57,66,1|achybz,57,66,1|achyc0,51,40,0|akf7rz,51,40,0|akf7s0,57,66,1|av80zz,57,66,1|av8100,51,40,0|b3i93z,51,40,0|b3i940,57,66,1|bdy3nz,57,66,1|bdy3o0,51,40,0|bm8brz,51,40,0|bm8bs0,57,66,1|bwo6bz,57,66,1|bwo6c0,51,40,0|c4yefz,51,40,0|c4yeg0,57,66,1|cfr7nz,57,66,1|cfr7o0,51,40,0|cnoh3z,51,40,0|cnoh40,57,66,1|cyhabz,57,66,1|cyhac0,51,40,0|d6ejrz,51,40,0|d6ejs0,57,66,1|dh7czz,57,66,1|dh7d00,51,40,0|dphl3z,51,40,0|dphl40,57,66,1|dzxfnz,57,66,1|dzxfo0,51,40,0|e87nrz,51,40,0|e87ns0,57,66,1|einibz,57,66,1|einic0,51,40,0|eqxqfz,51,40,0|eqxqg0,57,66,1|f1dkzz,57,66,1|f1dl00,51,40,0|f9nt3z,51,40,0|f9nt40,57,66,1|fkgmbz,57,66,1|fkgmc0,51,40,0|fsdvrz,51,40,0|fsdvs0,57,66,1|g36ozz,57,66,1|g36p00,51,40,0|gb3yfz,51,40,0|gb3yg0,57,66,1|glwrnz,57,66,1|glwro0,51,40,0|gu6zrz,51,40,0|gu6zs0,57,66,1|h4mubz,57,66,1|h4muc0,51,40,0|hcx2fz,51,40,0|hcx2g0,57,66,1|hncwzz,57,66,1|hncx00,51,40,0|hvn53z,51,40,0|hvn540,57,66,1|i6fybz,57,66,1|i6fyc0,51,40,0|ied7rz,51,40,0|ied7s0,57,66,1|ip60zz,57,66,1|ip6100,51,40,0|ix3afz,51,40,0|ix3ag0,57,66,1|j7w3nz,57,66,1|j7w3o0,51,40,0|jftd3z,51,40,0|jftd40,57,66,1|jqm6bz,57,66,1|jqm6c0,51,40,0|jywefz,51,40,0|jyweg0,57,66,1|k9c8zz,57,66,1|k9c900,51,40,0|khmh3z,51,40,0|khmh40,57,66,1|ks2bnz,57,66,1|ks2bo0,51,40,0|kz9nrz,51,40,0|kz9ns0,57,66,1|lbibnz,57,66,1|lbibo0,51,40,0|lhzqfz,51,40,0|lhzqg0,57,66,1|lu8ebz,57,66,1|lu8ec0,51,40,0|m0pt3z,51,40,0|m0pt40,57,66,1|mcygzz,57,66,1|mcyh00,51,40,0|mjfvrz,51,40,0|mjfvs0,57,66,1|mvojnz,57,66,1|mvojo0,51,40,0|n25yfz,51,40,0|n25yg0,57,66,1|neembz,57,66,1|neemc0,51,40,0|nkw13z,51,40,0|nkw140,57,66,1|nx4ozz,57,66,1|nx4p00,51,40,0|o3z2fz,51,40,0|o3z2g0,57,66,1|og7qbz,57,66,1|og7qc0,51,40,0|omp53z,51,40,0|omp540,57,66,1|oyxszz,57,66,1|oyxt00,51,40,0|p5f7rz,51,40,0|p5f7s0,57,66,1|phnvnz,57,66,1|phnvo0,51,40,0|po5afz,51,40,0|po5ag0,57,66,1|q0dybz,57,66,1|q0dyc0,51,40,0|q6vd3z,51,40,0|q6vd40,57,66,1|qj40zz,57,66,1|qj4100,51,40,0|qpyefz,51,40,0|qpyeg0,57,66,1|r272bz,57,66,1|r272c0,51,40,0|r8oh3z,51,40,0|r8oh40,57,66,1|rkx4zz,57,66,1|rkx500,51,40,0|rrejrz,51,40,0|rrejs0,57,66,1|s3n7nz,57,66,1|s3n7o0,51,40,0|sa4mfz,51,40,0|sa4mg0,57,66,1|smdabz,57,66,1|smdac0,51,40,0|ssup3z,51,40,0|ssup40,57,66,1|t53czz,57,66,1|t53d00,51,40,0|tbkrrz,51,40,0|tbkrs0,57,66,1|tntfnz,57,66,1|tntfo0,51,40,0|tunt3z,51,40,0|tunt40,57,66,1|u6wgzz,57,66,1|u6wh00,51,40,0|uddvrz,51,40,0|uddvs0,57,66,1|upmjnz,57,66,1|upmjo0,51,40,0|uw3yfz,51,40,0|uw3yg0,57,66,1|v8cmbz,57,66,1|v8cmc0,51,40,0|veu13z,51,40,0|veu140,57,66,1|vr2ozz,57,66,1|vr2p00,51,40,0|vxk3rz,51,40,0|vxk3s0,57,66,1|w9srnz,57,66,1|w9sro0,51,40,0|wgn53z,51,40,0|wgn540,57,66,1|wsvszz,57,66,1|wsvt00,51,40,0|wzd7rz,51,40,0|wzd7s0,57,66,1|xblvnz,57,66,1|xblvo0,51,40,0|xi3afz,51,40,0|xi3ag0,57,66,1|xubybz,57,66,1|xubyc0,51,40,0|y0td3z,51,40,0|y0td40,57,66,1|yd20zz,57,66,1|yd2100,51,40,0|yjjfrz,51,40,0|yjjfs0,57,66,1|yvs3nz,57,66,1|yvs3o0,51,40,0|z29ifz,51,40,0|z29ig0,57,66,1|zei6bz,57,66,1|zei6c0,51,40,0\",\"America/Toronto|,0,185,0|-1353das,49,63,0|-qzoxw1,49,63,0|-qzoxw0,62,42,1|-qpm201,62,42,1|-qpm200,49,63,0|-qhn4u1,49,63,0|-qhn4u0,62,42,1|-q6w4w1,62,42,1|-q6w4w0,49,63,0|-px5wk1,49,63,0|-px5wk0,62,42,1|-pplww1,62,42,1|-pplww0,49,63,0|-pdpwk1,49,63,0|-pdpwk0,62,42,1|-p7e7c1,62,42,1|-p7e7c0,49,63,0|-ouztw1,49,63,0|-ouztw0,62,42,1|-ooiko1,62,42,1|-ooiko0,49,63,0|-oc9r81,49,63,0|-oc9r80,62,42,1|-o5si01,62,42,1|-o5si00,49,63,0|-ntwn81,49,63,0|-ntwn80,62,42,1|-nmpgo1,62,42,1|-nmpgo0,49,63,0|-nb6kk1,49,63,0|-nb6kk0,62,42,1|-n3ze01,62,42,1|-n3ze00,49,63,0|-msghw1,49,63,0|-msghw0,62,42,1|-ml9bc1,62,42,1|-ml9bc0,49,63,0|-m9qf81,49,63,0|-m9qf80,62,42,1|-m26a01,62,42,1|-m26a00,49,63,0|-lr0ck1,49,63,0|-lr0ck0,62,42,1|-lj38o1,62,42,1|-lj38o0,49,63,0|-l8a9w1,49,63,0|-l8a9w0,62,42,1|-l0d601,62,42,1|-l0d600,49,63,0|-kpk781,49,63,0|-kpk780,62,42,1|-khn3c1,62,42,1|-khn3c0,49,63,0|-k6u4k1,49,63,0|-k6u4k0,62,42,1|-jyx0o1,62,42,1|-jyx0o0,49,63,0|-jnr381,49,63,0|-jnr380,62,42,1|-jg6y01,62,42,1|-jg6y00,49,63,0|-j510k1,49,63,0|-j510k0,62,42,1|-ix3wo1,62,42,1|-ix3wo0,49,63,0|-imaxw1,49,63,0|-imaxw0,62,42,1|-iedu01,62,42,1|-iedu00,49,63,0|-i3kv81,49,63,0|-i3kv80,62,42,1|-hvnrc1,62,42,1|-hvnrc0,49,63,0|-hkusk1,49,63,0|-hkusk0,62,42,1|-hcxoo1,62,42,1|-hcxoo0,49,63,0|-h24pw1,49,63,0|-h24pw0,62,42,1|-gu7m01,62,42,1|-gu7m00,49,63,0|-gjen81,49,63,0|-gjen80,62,42,1|-gbhjc1,62,42,1|-gbhjc0,49,63,0|-g0blw1,49,63,0|-g0blw0,62,42,1|-fsrgo1,62,42,1|-fsrgo0,49,63,0|-fhlj81,49,63,0|-fhlj80,62,42,1|-ek24k1,62,42,1|-ek24k0,70,42,1|-cq2tg1,70,42,1|-cq2tg0,71,42,1|-cnp3c1,71,42,1|-cnp3c0,49,63,0|-ccw4k1,49,63,0|-ccw4k0,62,42,1|-c4z0o1,62,42,1|-c4z0o0,49,63,0|-bu67g1,49,63,0|-bu67g0,62,42,1|-bm93k1,62,42,1|-bm93k0,49,63,0|-bbg4s1,49,63,0|-bbg4s0,62,42,1|-b3j0w1,62,42,1|-b3j0w0,49,63,0|-asq241,49,63,0|-asq240,62,42,1|-ahka81,62,42,1|-ahka80,49,63,0|-a9mv81,49,63,0|-a9mv80,62,42,1|-9yu201,62,42,1|-9yu200,49,63,0|-9qwsk1,49,63,0|-9qwsk0,62,42,1|-9izoo1,62,42,1|-9izoo0,49,63,0|-986pw1,49,63,0|-986pw0,62,42,1|-909m01,62,42,1|-909m00,49,63,0|-8pgn81,49,63,0|-8pgn80,62,42,1|-8hjjc1,62,42,1|-8hjjc0,49,63,0|-86qkk1,49,63,0|-86qkk0,62,42,1|-7ytgo1,62,42,1|-7ytgo0,49,63,0|-7o0hw1,49,63,0|-7o0hw0,62,42,1|-7g3e01,62,42,1|-7g3e00,49,63,0|-74xgk1,49,63,0|-74xgk0,62,42,1|-6x0co1,62,42,1|-6x0co0,49,63,0|-6m7dw1,49,63,0|-6m7dw0,62,42,1|-6cufc1,62,42,1|-6cufc0,49,63,0|-63hb81,49,63,0|-63hb80,62,42,1|-5u4co1,62,42,1|-5u4co0,49,63,0|-5kr8k1,49,63,0|-5kr8k0,62,42,1|-5bea01,62,42,1|-5bea00,49,63,0|-5215w1,49,63,0|-5215w0,62,42,1|-4sb8o1,62,42,1|-4sb8o0,49,63,0|-4iy4k1,49,63,0|-4iy4k0,62,42,1|-49l601,62,42,1|-49l600,49,63,0|-4081w1,49,63,0|-4081w0,62,42,1|-3qv3c1,62,42,1|-3qv3c0,49,63,0|-3hhz81,49,63,0|-3hhz80,62,42,1|-3850o1,62,42,1|-3850o0,49,63,0|-2yrwk1,49,63,0|-2yrwk0,62,42,1|-2pey01,62,42,1|-2pey00,49,63,0|-2g1tw1,49,63,0|-2g1tw0,62,42,1|-26bwo1,62,42,1|-26bwo0,49,63,0|-1xbr81,49,63,0|-1xbr80,62,42,1|-1nlu01,62,42,1|-1nlu00,49,63,0|-1e8pw1,49,63,0|-1e8pw0,62,42,1|-14vrc1,62,42,1|-14vrc0,49,63,0|-vin81,49,63,0|-vin80,62,42,1|-m5oo1,62,42,1|-m5oo0,49,63,0|-cskk1,49,63,0|-cskk0,62,42,1|-3fm01,62,42,1|-3fm00,49,63,0|5xi3z,49,63,0|5xi40,62,42,1|fagnz,62,42,1|fago0,49,63,0|onkrz,49,63,0|onks0,62,42,1|ydhzz,62,42,1|ydi00,49,63,0|17qm3z,49,63,0|17qm40,62,42,1|1h3knz,62,42,1|1h3ko0,49,63,0|1qgorz,49,63,0|1qgos0,62,42,1|1ztnbz,62,42,1|1ztnc0,49,63,0|296rfz,49,63,0|296rg0,62,42,1|2ijpzz,62,42,1|2ijq00,49,63,0|2rwu3z,49,63,0|2rwu40,62,42,1|319snz,62,42,1|319so0,49,63,0|3amwrz,49,63,0|3amws0,62,42,1|3kctzz,62,42,1|3kcu00,49,63,0|3tczfz,49,63,0|3tczg0,62,42,1|432wnz,62,42,1|432wo0,49,63,0|4cg0rz,49,63,0|4cg0s0,62,42,1|4lszbz,62,42,1|4lszc0,49,63,0|4v63fz,49,63,0|4v63g0,62,42,1|54j1zz,62,42,1|54j200,49,63,0|5dw63z,49,63,0|5dw640,62,42,1|5n94nz,62,42,1|5n94o0,49,63,0|5wm8rz,49,63,0|5wm8s0,62,42,1|65z7bz,62,42,1|65z7c0,49,63,0|6fcbfz,49,63,0|6fcbg0,62,42,1|6p28nz,62,42,1|6p28o0,49,63,0|6y2e3z,49,63,0|6y2e40,62,42,1|77sbbz,62,42,1|77sbc0,49,63,0|7h5ffz,49,63,0|7h5fg0,62,42,1|7qidzz,62,42,1|7qie00,49,63,0|7zvi3z,49,63,0|7zvi40,62,42,1|898gnz,62,42,1|898go0,49,63,0|8ilkrz,49,63,0|8ilks0,62,42,1|8ryjbz,62,42,1|8ryjc0,49,63,0|908rfz,49,63,0|908rg0,62,42,1|9aolzz,62,42,1|9aom00,49,63,0|9iyu3z,49,63,0|9iyu40,62,42,1|9trnbz,62,42,1|9trnc0,49,63,0|a1owrz,49,63,0|a1ows0,62,42,1|achpzz,62,42,1|achq00,49,63,0|akezfz,49,63,0|akezg0,62,42,1|av7snz,62,42,1|av7so0,49,63,0|b3i0rz,49,63,0|b3i0s0,62,42,1|bdxvbz,62,42,1|bdxvc0,49,63,0|bm83fz,49,63,0|bm83g0,62,42,1|bwnxzz,62,42,1|bwny00,49,63,0|c4y63z,49,63,0|c4y640,62,42,1|cfqzbz,62,42,1|cfqzc0,49,63,0|cno8rz,49,63,0|cno8s0,62,42,1|cyh1zz,62,42,1|cyh200,49,63,0|d6ebfz,49,63,0|d6ebg0,62,42,1|dh74nz,62,42,1|dh74o0,49,63,0|dphcrz,49,63,0|dphcs0,62,42,1|dzx7bz,62,42,1|dzx7c0,49,63,0|e87ffz,49,63,0|e87fg0,62,42,1|ein9zz,62,42,1|eina00,49,63,0|eqxi3z,49,63,0|eqxi40,62,42,1|f1dcnz,62,42,1|f1dco0,49,63,0|f9nkrz,49,63,0|f9nks0,62,42,1|fkgdzz,62,42,1|fkge00,49,63,0|fsdnfz,49,63,0|fsdng0,62,42,1|g36gnz,62,42,1|g36go0,49,63,0|gb3q3z,49,63,0|gb3q40,62,42,1|glwjbz,62,42,1|glwjc0,49,63,0|gu6rfz,49,63,0|gu6rg0,62,42,1|h4mlzz,62,42,1|h4mm00,49,63,0|hcwu3z,49,63,0|hcwu40,62,42,1|hnconz,62,42,1|hncoo0,49,63,0|hvmwrz,49,63,0|hvmws0,62,42,1|i6fpzz,62,42,1|i6fq00,49,63,0|ieczfz,49,63,0|ieczg0,62,42,1|ip5snz,62,42,1|ip5so0,49,63,0|ix323z,49,63,0|ix3240,62,42,1|j7vvbz,62,42,1|j7vvc0,49,63,0|jeq8rz,49,63,0|jeq8s0,62,42,1|jqywnz,62,42,1|jqywo0,49,63,0|jxgbfz,49,63,0|jxgbg0,62,42,1|k9ozbz,62,42,1|k9ozc0,49,63,0|kg6e3z,49,63,0|kg6e40,62,42,1|ksf1zz,62,42,1|ksf200,49,63,0|kz9ffz,49,63,0|kz9fg0,62,42,1|lbi3bz,62,42,1|lbi3c0,49,63,0|lhzi3z,49,63,0|lhzi40,62,42,1|lu85zz,62,42,1|lu8600,49,63,0|m0pkrz,49,63,0|m0pks0,62,42,1|mcy8nz,62,42,1|mcy8o0,49,63,0|mjfnfz,49,63,0|mjfng0,62,42,1|mvobbz,62,42,1|mvobc0,49,63,0|n25q3z,49,63,0|n25q40,62,42,1|needzz,62,42,1|neee00,49,63,0|nkvsrz,49,63,0|nkvss0,62,42,1|nx4gnz,62,42,1|nx4go0,49,63,0|o3yu3z,49,63,0|o3yu40,62,42,1|og7hzz,62,42,1|og7i00,49,63,0|omowrz,49,63,0|omows0,62,42,1|oyxknz,62,42,1|oyxko0,49,63,0|p5ezfz,49,63,0|p5ezg0,62,42,1|phnnbz,62,42,1|phnnc0,49,63,0|po523z,49,63,0|po5240,62,42,1|q0dpzz,62,42,1|q0dq00,49,63,0|q6v4rz,49,63,0|q6v4s0,62,42,1|qj3snz,62,42,1|qj3so0,49,63,0|qpy63z,49,63,0|qpy640,62,42,1|r26tzz,62,42,1|r26u00,49,63,0|r8o8rz,49,63,0|r8o8s0,62,42,1|rkwwnz,62,42,1|rkwwo0,49,63,0|rrebfz,49,63,0|rrebg0,62,42,1|s3mzbz,62,42,1|s3mzc0,49,63,0|sa4e3z,49,63,0|sa4e40,62,42,1|smd1zz,62,42,1|smd200,49,63,0|ssugrz,49,63,0|ssugs0,62,42,1|t534nz,62,42,1|t534o0,49,63,0|tbkjfz,49,63,0|tbkjg0,62,42,1|tnt7bz,62,42,1|tnt7c0,49,63,0|tunkrz,49,63,0|tunks0,62,42,1|u6w8nz,62,42,1|u6w8o0,49,63,0|uddnfz,49,63,0|uddng0,62,42,1|upmbbz,62,42,1|upmbc0,49,63,0|uw3q3z,49,63,0|uw3q40,62,42,1|v8cdzz,62,42,1|v8ce00,49,63,0|vetsrz,49,63,0|vetss0,62,42,1|vr2gnz,62,42,1|vr2go0,49,63,0|vxjvfz,49,63,0|vxjvg0,62,42,1|w9sjbz,62,42,1|w9sjc0,49,63,0|wgmwrz,49,63,0|wgmws0,62,42,1|wsvknz,62,42,1|wsvko0,49,63,0|wzczfz,49,63,0|wzczg0,62,42,1|xblnbz,62,42,1|xblnc0,49,63,0|xi323z,49,63,0|xi3240,62,42,1|xubpzz,62,42,1|xubq00,49,63,0|y0t4rz,49,63,0|y0t4s0,62,42,1|yd1snz,62,42,1|yd1so0,49,63,0|yjj7fz,49,63,0|yjj7g0,62,42,1|yvrvbz,62,42,1|yvrvc0,49,63,0|z29a3z,49,63,0|z29a40,62,42,1|zehxzz,62,42,1|zehy00,49,63,0\",\"America/Tortola|,0,41,0|-u6m79w,32,42,0\",\"America/Vancouver|,0,186,0|-18vrvv8,51,40,0|-qzopk1,51,40,0|-qzopk0,57,66,1|-qplto1,57,66,1|-qplto0,51,40,0|-ek1w81,51,40,0|-ek1w80,68,66,1|-cq2tg1,68,66,1|-cq2tg0,69,66,1|-cnov01,69,66,1|-cnov00,51,40,0|-ccvw81,51,40,0|-ccvw80,57,66,1|-c4ysc1,57,66,1|-c4ysc0,51,40,0|-bu5tk1,51,40,0|-bu5tk0,57,66,1|-bm8po1,57,66,1|-bm8po0,51,40,0|-bbfqw1,51,40,0|-bbfqw0,57,66,1|-b3in01,57,66,1|-b3in00,51,40,0|-aspo81,51,40,0|-aspo80,57,66,1|-akskc1,57,66,1|-akskc0,51,40,0|-a9mmw1,51,40,0|-a9mmw0,57,66,1|-a22ho1,57,66,1|-a22ho0,51,40,0|-9qwk81,51,40,0|-9qwk80,57,66,1|-9izgc1,57,66,1|-9izgc0,51,40,0|-986hk1,51,40,0|-986hk0,57,66,1|-909do1,57,66,1|-909do0,51,40,0|-8pgew1,51,40,0|-8pgew0,57,66,1|-8hjb01,57,66,1|-8hjb00,51,40,0|-86qc81,51,40,0|-86qc80,57,66,1|-7yt8c1,57,66,1|-7yt8c0,51,40,0|-7o09k1,51,40,0|-7o09k0,57,66,1|-7g35o1,57,66,1|-7g35o0,51,40,0|-74x881,51,40,0|-74x880,57,66,1|-6x04c1,57,66,1|-6x04c0,51,40,0|-6m75k1,51,40,0|-6m75k0,57,66,1|-6ea1o1,57,66,1|-6ea1o0,51,40,0|-63h2w1,51,40,0|-63h2w0,57,66,1|-5vjz01,57,66,1|-5vjz00,51,40,0|-5kr081,51,40,0|-5kr080,57,66,1|-5ctwc1,57,66,1|-5ctwc0,51,40,0|-520xk1,51,40,0|-520xk0,57,66,1|-4u3to1,57,66,1|-4u3to0,51,40,0|-4ixw81,51,40,0|-4ixw80,57,66,1|-4bdr01,57,66,1|-4bdr00,51,40,0|-407tk1,51,40,0|-407tk0,57,66,1|-3quv01,57,66,1|-3quv00,51,40,0|-3hhqw1,51,40,0|-3hhqw0,57,66,1|-384sc1,57,66,1|-384sc0,51,40,0|-2yro81,51,40,0|-2yro80,57,66,1|-2pepo1,57,66,1|-2pepo0,51,40,0|-2g1lk1,51,40,0|-2g1lk0,57,66,1|-26boc1,57,66,1|-26boc0,51,40,0|-1xbiw1,51,40,0|-1xbiw0,57,66,1|-1nllo1,57,66,1|-1nllo0,51,40,0|-1e8hk1,51,40,0|-1e8hk0,57,66,1|-14vj01,57,66,1|-14vj00,51,40,0|-view1,51,40,0|-view0,57,66,1|-m5gc1,57,66,1|-m5gc0,51,40,0|-csc81,51,40,0|-csc80,57,66,1|-3fdo1,57,66,1|-3fdo0,51,40,0|5xqfz,51,40,0|5xqg0,57,66,1|faozz,57,66,1|fap00,51,40,0|ont3z,51,40,0|ont40,57,66,1|ydqbz,57,66,1|ydqc0,51,40,0|17qufz,51,40,0|17qug0,57,66,1|1h3szz,57,66,1|1h3t00,51,40,0|1qgx3z,51,40,0|1qgx40,57,66,1|1ztvnz,57,66,1|1ztvo0,51,40,0|296zrz,51,40,0|296zs0,57,66,1|2ijybz,57,66,1|2ijyc0,51,40,0|2rx2fz,51,40,0|2rx2g0,57,66,1|31a0zz,57,66,1|31a100,51,40,0|3an53z,51,40,0|3an540,57,66,1|3kd2bz,57,66,1|3kd2c0,51,40,0|3td7rz,51,40,0|3td7s0,57,66,1|4334zz,57,66,1|433500,51,40,0|4cg93z,51,40,0|4cg940,57,66,1|4lt7nz,57,66,1|4lt7o0,51,40,0|4v6brz,51,40,0|4v6bs0,57,66,1|54jabz,57,66,1|54jac0,51,40,0|5dwefz,51,40,0|5dweg0,57,66,1|5n9czz,57,66,1|5n9d00,51,40,0|5wmh3z,51,40,0|5wmh40,57,66,1|65zfnz,57,66,1|65zfo0,51,40,0|6fcjrz,51,40,0|6fcjs0,57,66,1|6p2gzz,57,66,1|6p2h00,51,40,0|6y2mfz,51,40,0|6y2mg0,57,66,1|77sjnz,57,66,1|77sjo0,51,40,0|7h5nrz,51,40,0|7h5ns0,57,66,1|7qimbz,57,66,1|7qimc0,51,40,0|7zvqfz,51,40,0|7zvqg0,57,66,1|898ozz,57,66,1|898p00,51,40,0|8ilt3z,51,40,0|8ilt40,57,66,1|8ryrnz,57,66,1|8ryro0,51,40,0|908zrz,51,40,0|908zs0,57,66,1|9aoubz,57,66,1|9aouc0,51,40,0|9iz2fz,51,40,0|9iz2g0,57,66,1|9trvnz,57,66,1|9trvo0,51,40,0|a1p53z,51,40,0|a1p540,57,66,1|achybz,57,66,1|achyc0,51,40,0|akf7rz,51,40,0|akf7s0,57,66,1|av80zz,57,66,1|av8100,51,40,0|b3i93z,51,40,0|b3i940,57,66,1|bdy3nz,57,66,1|bdy3o0,51,40,0|bm8brz,51,40,0|bm8bs0,57,66,1|bwo6bz,57,66,1|bwo6c0,51,40,0|c4yefz,51,40,0|c4yeg0,57,66,1|cfr7nz,57,66,1|cfr7o0,51,40,0|cnoh3z,51,40,0|cnoh40,57,66,1|cyhabz,57,66,1|cyhac0,51,40,0|d6ejrz,51,40,0|d6ejs0,57,66,1|dh7czz,57,66,1|dh7d00,51,40,0|dphl3z,51,40,0|dphl40,57,66,1|dzxfnz,57,66,1|dzxfo0,51,40,0|e87nrz,51,40,0|e87ns0,57,66,1|einibz,57,66,1|einic0,51,40,0|eqxqfz,51,40,0|eqxqg0,57,66,1|f1dkzz,57,66,1|f1dl00,51,40,0|f9nt3z,51,40,0|f9nt40,57,66,1|fkgmbz,57,66,1|fkgmc0,51,40,0|fsdvrz,51,40,0|fsdvs0,57,66,1|g36ozz,57,66,1|g36p00,51,40,0|gb3yfz,51,40,0|gb3yg0,57,66,1|glwrnz,57,66,1|glwro0,51,40,0|gu6zrz,51,40,0|gu6zs0,57,66,1|h4mubz,57,66,1|h4muc0,51,40,0|hcx2fz,51,40,0|hcx2g0,57,66,1|hncwzz,57,66,1|hncx00,51,40,0|hvn53z,51,40,0|hvn540,57,66,1|i6fybz,57,66,1|i6fyc0,51,40,0|ied7rz,51,40,0|ied7s0,57,66,1|ip60zz,57,66,1|ip6100,51,40,0|ix3afz,51,40,0|ix3ag0,57,66,1|j7w3nz,57,66,1|j7w3o0,51,40,0|jeqh3z,51,40,0|jeqh40,57,66,1|jqz4zz,57,66,1|jqz500,51,40,0|jxgjrz,51,40,0|jxgjs0,57,66,1|k9p7nz,57,66,1|k9p7o0,51,40,0|kg6mfz,51,40,0|kg6mg0,57,66,1|ksfabz,57,66,1|ksfac0,51,40,0|kz9nrz,51,40,0|kz9ns0,57,66,1|lbibnz,57,66,1|lbibo0,51,40,0|lhzqfz,51,40,0|lhzqg0,57,66,1|lu8ebz,57,66,1|lu8ec0,51,40,0|m0pt3z,51,40,0|m0pt40,57,66,1|mcygzz,57,66,1|mcyh00,51,40,0|mjfvrz,51,40,0|mjfvs0,57,66,1|mvojnz,57,66,1|mvojo0,51,40,0|n25yfz,51,40,0|n25yg0,57,66,1|neembz,57,66,1|neemc0,51,40,0|nkw13z,51,40,0|nkw140,57,66,1|nx4ozz,57,66,1|nx4p00,51,40,0|o3z2fz,51,40,0|o3z2g0,57,66,1|og7qbz,57,66,1|og7qc0,51,40,0|omp53z,51,40,0|omp540,57,66,1|oyxszz,57,66,1|oyxt00,51,40,0|p5f7rz,51,40,0|p5f7s0,57,66,1|phnvnz,57,66,1|phnvo0,51,40,0|po5afz,51,40,0|po5ag0,57,66,1|q0dybz,57,66,1|q0dyc0,51,40,0|q6vd3z,51,40,0|q6vd40,57,66,1|qj40zz,57,66,1|qj4100,51,40,0|qpyefz,51,40,0|qpyeg0,57,66,1|r272bz,57,66,1|r272c0,51,40,0|r8oh3z,51,40,0|r8oh40,57,66,1|rkx4zz,57,66,1|rkx500,51,40,0|rrejrz,51,40,0|rrejs0,57,66,1|s3n7nz,57,66,1|s3n7o0,51,40,0|sa4mfz,51,40,0|sa4mg0,57,66,1|smdabz,57,66,1|smdac0,51,40,0|ssup3z,51,40,0|ssup40,57,66,1|t53czz,57,66,1|t53d00,51,40,0|tbkrrz,51,40,0|tbkrs0,57,66,1|tntfnz,57,66,1|tntfo0,51,40,0|tunt3z,51,40,0|tunt40,57,66,1|u6wgzz,57,66,1|u6wh00,51,40,0|uddvrz,51,40,0|uddvs0,57,66,1|upmjnz,57,66,1|upmjo0,51,40,0|uw3yfz,51,40,0|uw3yg0,57,66,1|v8cmbz,57,66,1|v8cmc0,51,40,0|veu13z,51,40,0|veu140,57,66,1|vr2ozz,57,66,1|vr2p00,51,40,0|vxk3rz,51,40,0|vxk3s0,57,66,1|w9srnz,57,66,1|w9sro0,51,40,0|wgn53z,51,40,0|wgn540,57,66,1|wsvszz,57,66,1|wsvt00,51,40,0|wzd7rz,51,40,0|wzd7s0,57,66,1|xblvnz,57,66,1|xblvo0,51,40,0|xi3afz,51,40,0|xi3ag0,57,66,1|xubybz,57,66,1|xubyc0,51,40,0|y0td3z,51,40,0|y0td40,57,66,1|yd20zz,57,66,1|yd2100,51,40,0|yjjfrz,51,40,0|yjjfs0,57,66,1|yvs3nz,57,66,1|yvs3o0,51,40,0|z29ifz,51,40,0|z29ig0,57,66,1|zei6bz,57,66,1|zei6c0,51,40,0\",\"America/Whitehorse|,0,187,0|-1079tno,36,37,0|-qzoms1,36,37,0|-qzoms0,64,40,1|-qplqw1,64,40,1|-qplqw0,36,37,0|-qess41,36,37,0|-qess40,64,40,1|-q6kps1,64,40,1|-q6kps0,36,37,0|-ek1tg1,36,37,0|-ek1tg0,65,40,1|-cq2tg1,65,40,1|-cq2tg0,66,40,1|-cnos81,66,40,1|-cnos80,36,37,0|-2g1oc1,36,37,0|-2g1oc0,67,66,1|-26boc1,67,66,1|-26boc0,36,37,0|-1cspo1,36,37,0|-1cspo0,51,40,0|5dwefz,51,40,0|5dweg0,57,66,1|5n9czz,57,66,1|5n9d00,51,40,0|5wmh3z,51,40,0|5wmh40,57,66,1|65zfnz,57,66,1|65zfo0,51,40,0|6fcjrz,51,40,0|6fcjs0,57,66,1|6p2gzz,57,66,1|6p2h00,51,40,0|6y2mfz,51,40,0|6y2mg0,57,66,1|77sjnz,57,66,1|77sjo0,51,40,0|7h5nrz,51,40,0|7h5ns0,57,66,1|7qimbz,57,66,1|7qimc0,51,40,0|7zvqfz,51,40,0|7zvqg0,57,66,1|898ozz,57,66,1|898p00,51,40,0|8ilt3z,51,40,0|8ilt40,57,66,1|8ryrnz,57,66,1|8ryro0,51,40,0|908zrz,51,40,0|908zs0,57,66,1|9aoubz,57,66,1|9aouc0,51,40,0|9iz2fz,51,40,0|9iz2g0,57,66,1|9trvnz,57,66,1|9trvo0,51,40,0|a1p53z,51,40,0|a1p540,57,66,1|achybz,57,66,1|achyc0,51,40,0|akf7rz,51,40,0|akf7s0,57,66,1|av80zz,57,66,1|av8100,51,40,0|b3i93z,51,40,0|b3i940,57,66,1|bdy3nz,57,66,1|bdy3o0,51,40,0|bm8brz,51,40,0|bm8bs0,57,66,1|bwo6bz,57,66,1|bwo6c0,51,40,0|c4yefz,51,40,0|c4yeg0,57,66,1|cfr7nz,57,66,1|cfr7o0,51,40,0|cnoh3z,51,40,0|cnoh40,57,66,1|cyhabz,57,66,1|cyhac0,51,40,0|d6ejrz,51,40,0|d6ejs0,57,66,1|dh7czz,57,66,1|dh7d00,51,40,0|dphl3z,51,40,0|dphl40,57,66,1|dzxfnz,57,66,1|dzxfo0,51,40,0|e87nrz,51,40,0|e87ns0,57,66,1|einibz,57,66,1|einic0,51,40,0|eqxqfz,51,40,0|eqxqg0,57,66,1|f1dkzz,57,66,1|f1dl00,51,40,0|f9nt3z,51,40,0|f9nt40,57,66,1|fkgmbz,57,66,1|fkgmc0,51,40,0|fsdvrz,51,40,0|fsdvs0,57,66,1|g36ozz,57,66,1|g36p00,51,40,0|gb3yfz,51,40,0|gb3yg0,57,66,1|glwrnz,57,66,1|glwro0,51,40,0|gu6zrz,51,40,0|gu6zs0,57,66,1|h4mubz,57,66,1|h4muc0,51,40,0|hcx2fz,51,40,0|hcx2g0,57,66,1|hncwzz,57,66,1|hncx00,51,40,0|hvn53z,51,40,0|hvn540,57,66,1|i6fybz,57,66,1|i6fyc0,51,40,0|ied7rz,51,40,0|ied7s0,57,66,1|ip60zz,57,66,1|ip6100,51,40,0|ix3afz,51,40,0|ix3ag0,57,66,1|j7w3nz,57,66,1|j7w3o0,51,40,0|jeqh3z,51,40,0|jeqh40,57,66,1|jqz4zz,57,66,1|jqz500,51,40,0|jxgjrz,51,40,0|jxgjs0,57,66,1|k9p7nz,57,66,1|k9p7o0,51,40,0|kg6mfz,51,40,0|kg6mg0,57,66,1|ksfabz,57,66,1|ksfac0,51,40,0|kz9nrz,51,40,0|kz9ns0,57,66,1|lbibnz,57,66,1|lbibo0,51,40,0|lhzqfz,51,40,0|lhzqg0,57,66,1|lu8ebz,57,66,1|lu8ec0,51,40,0|m0pt3z,51,40,0|m0pt40,57,66,1|mcygzz,57,66,1|mcyh00,51,40,0|mjfvrz,51,40,0|mjfvs0,57,66,1|mvojnz,57,66,1|mvojo0,51,40,0|n25yfz,51,40,0|n25yg0,57,66,1|neembz,57,66,1|neemc0,51,40,0|nkw13z,51,40,0|nkw140,57,66,1|nx4ozz,57,66,1|nx4p00,51,40,0|o3z2fz,51,40,0|o3z2g0,57,66,1|og7qbz,57,66,1|og7qc0,51,40,0|omp53z,51,40,0|omp540,57,66,1|oyxszz,57,66,1|oyxt00,51,40,0|p5f7rz,51,40,0|p5f7s0,57,66,1|phnvnz,57,66,1|phnvo0,51,40,0|po5afz,51,40,0|po5ag0,57,66,1|q0dybz,57,66,1|q0dyc0,51,40,0|q6vd3z,51,40,0|q6vd40,57,66,1|qj3vfz,57,66,1|qj3vg0,50,66,0\",\"America/Winnipeg|,0,188,0|-171bfcc,45,62,0|-s0s7c1,45,62,0|-s0s7c0,46,63,1|-rt8241,46,63,1|-rt8240,45,62,0|-qzov41,45,62,0|-qzov40,46,63,1|-qplz81,46,63,1|-qplz80,45,62,0|-h11r41,45,62,0|-h11r40,46,63,1|-gu7j81,46,63,1|-gu7j80,45,62,0|-ek21s1,45,62,0|-ek21s0,47,63,1|-cq2tg1,47,63,1|-cq2tg0,48,63,1|-cnp0k1,48,63,1|-cnp0k0,45,62,0|-cc64g1,45,62,0|-cc64g0,46,63,1|-c490k1,46,63,1|-c490k0,45,62,0|-bu5z41,45,62,0|-bu5z40,46,63,1|-bm8v81,46,63,1|-bm8v80,45,62,0|-bbfwg1,45,62,0|-bbfwg0,46,63,1|-b3isk1,46,63,1|-b3isk0,45,62,0|-aspts1,45,62,0|-aspts0,46,63,1|-akspw1,46,63,1|-akspw0,45,62,0|-a9kxs1,45,62,0|-a9kxs0,46,63,1|-a1rj81,46,63,1|-a1rj80,45,62,0|-9qwps1,45,62,0|-9qwps0,46,63,1|-9izlw1,46,63,1|-9izlw0,45,62,0|-986n41,45,62,0|-986n40,46,63,1|-909j81,46,63,1|-909j80,45,62,0|-8pgkg1,45,62,0|-8pgkg0,46,63,1|-8hjgk1,46,63,1|-8hjgk0,45,62,0|-86qhs1,45,62,0|-86qhs0,46,63,1|-7ytdw1,46,63,1|-7ytdw0,45,62,0|-7o0f41,45,62,0|-7o0f40,46,63,1|-7g3b81,46,63,1|-7g3b80,45,62,0|-74xds1,45,62,0|-74xds0,46,63,1|-6x09w1,46,63,1|-6x09w0,45,62,0|-6m7b41,45,62,0|-6m7b40,46,63,1|-6ea781,46,63,1|-6ea780,45,62,0|-63h8g1,45,62,0|-63h8g0,46,63,1|-5vk4k1,46,63,1|-5vk4k0,45,62,0|-5kr5s1,45,62,0|-5kr5s0,46,63,1|-5be781,46,63,1|-5be780,45,62,0|-521341,45,62,0|-521340,46,63,1|-4u3z81,46,63,1|-4u3z80,45,62,0|-3hhwg1,45,62,0|-3hhwg0,46,63,1|-39xr81,46,63,1|-39xr80,45,62,0|-1xbog1,45,62,0|-1xbog0,46,63,1|-1nlog1,46,63,1|-1nlog0,45,62,0|-1e8n41,45,62,0|-1e8n40,46,63,1|-14vls1,46,63,1|-14vls0,45,62,0|-vikg1,45,62,0|-vikg0,46,63,1|-m5j41,46,63,1|-m5j40,45,62,0|-cshs1,45,62,0|-cshs0,46,63,1|-3fgg1,46,63,1|-3fgg0,45,62,0|5xkvz,45,62,0|5xkw0,46,63,1|fam7z,46,63,1|fam80,45,62,0|onnjz,45,62,0|onnk0,46,63,1|ydnjz,46,63,1|ydnk0,45,62,0|17qovz,45,62,0|17qow0,46,63,1|1h3q7z,46,63,1|1h3q80,45,62,0|1qgrjz,45,62,0|1qgrk0,46,63,1|1ztsvz,46,63,1|1ztsw0,45,62,0|296u7z,45,62,0|296u80,46,63,1|2ijvjz,46,63,1|2ijvk0,45,62,0|2rwwvz,45,62,0|2rwww0,46,63,1|319y7z,46,63,1|319y80,45,62,0|3amzjz,45,62,0|3amzk0,46,63,1|3kczjz,46,63,1|3kczk0,45,62,0|3td27z,45,62,0|3td280,46,63,1|43327z,46,63,1|433280,45,62,0|4cg3jz,45,62,0|4cg3k0,46,63,1|4lt4vz,46,63,1|4lt4w0,45,62,0|4v667z,45,62,0|4v6680,46,63,1|54j7jz,46,63,1|54j7k0,45,62,0|5dw8vz,45,62,0|5dw8w0,46,63,1|5n9a7z,46,63,1|5n9a80,45,62,0|5wmbjz,45,62,0|5wmbk0,46,63,1|65zcvz,46,63,1|65zcw0,45,62,0|6fce7z,45,62,0|6fce80,46,63,1|6p2e7z,46,63,1|6p2e80,45,62,0|6y2gvz,45,62,0|6y2gw0,46,63,1|77sgvz,46,63,1|77sgw0,45,62,0|7h5i7z,45,62,0|7h5i80,46,63,1|7qijjz,46,63,1|7qijk0,45,62,0|7zvkvz,45,62,0|7zvkw0,46,63,1|898m7z,46,63,1|898m80,45,62,0|8ilnjz,45,62,0|8ilnk0,46,63,1|8ryovz,46,63,1|8ryow0,45,62,0|908u7z,45,62,0|908u80,46,63,1|9aorjz,46,63,1|9aork0,45,62,0|9iywvz,45,62,0|9iyww0,46,63,1|9trsvz,46,63,1|9trsw0,45,62,0|a1ozjz,45,62,0|a1ozk0,46,63,1|achvjz,46,63,1|achvk0,45,62,0|akf27z,45,62,0|akf280,46,63,1|av7y7z,46,63,1|av7y80,45,62,0|b3i3jz,45,62,0|b3i3k0,46,63,1|bdy0vz,46,63,1|bdy0w0,45,62,0|bm867z,45,62,0|bm8680,46,63,1|bwo3jz,46,63,1|bwo3k0,45,62,0|c4y8vz,45,62,0|c4y8w0,46,63,1|cfr4vz,46,63,1|cfr4w0,45,62,0|cnobjz,45,62,0|cnobk0,46,63,1|cyh7jz,46,63,1|cyh7k0,45,62,0|d6ee7z,45,62,0|d6ee80,46,63,1|dh7a7z,46,63,1|dh7a80,45,62,0|dphfjz,45,62,0|dphfk0,46,63,1|dzxcvz,46,63,1|dzxcw0,45,62,0|e87i7z,45,62,0|e87i80,46,63,1|einfjz,46,63,1|einfk0,45,62,0|eqxkvz,45,62,0|eqxkw0,46,63,1|f1di7z,46,63,1|f1di80,45,62,0|f9nnjz,45,62,0|f9nnk0,46,63,1|fkgjjz,46,63,1|fkgjk0,45,62,0|fsdq7z,45,62,0|fsdq80,46,63,1|g36m7z,46,63,1|g36m80,45,62,0|gb3svz,45,62,0|gb3sw0,46,63,1|glwovz,46,63,1|glwow0,45,62,0|gu6u7z,45,62,0|gu6u80,46,63,1|h4mrjz,46,63,1|h4mrk0,45,62,0|hcwwvz,45,62,0|hcwww0,46,63,1|hncu7z,46,63,1|hncu80,45,62,0|hvmzjz,45,62,0|hvmzk0,46,63,1|i6fvjz,46,63,1|i6fvk0,45,62,0|ied27z,45,62,0|ied280,46,63,1|ip5y7z,46,63,1|ip5y80,45,62,0|ix34vz,45,62,0|ix34w0,46,63,1|j7vy3z,46,63,1|j7vy40,45,62,0|jeqbjz,45,62,0|jeqbk0,46,63,1|jqyzfz,46,63,1|jqyzg0,45,62,0|jxge7z,45,62,0|jxge80,46,63,1|k9p23z,46,63,1|k9p240,45,62,0|kg6gvz,45,62,0|kg6gw0,46,63,1|ksf4rz,46,63,1|ksf4s0,45,62,0|kz9i7z,45,62,0|kz9i80,46,63,1|lbi63z,46,63,1|lbi640,45,62,0|lhzkvz,45,62,0|lhzkw0,46,63,1|lu88rz,46,63,1|lu88s0,45,62,0|m0pnjz,45,62,0|m0pnk0,46,63,1|mcybfz,46,63,1|mcybg0,45,62,0|mjfq7z,45,62,0|mjfq80,46,63,1|mvoe3z,46,63,1|mvoe40,45,62,0|n25svz,45,62,0|n25sw0,46,63,1|neegrz,46,63,1|neegs0,45,62,0|nkvvjz,45,62,0|nkvvk0,46,63,1|nx4jfz,46,63,1|nx4jg0,45,62,0|o3ywvz,45,62,0|o3yww0,46,63,1|og7krz,46,63,1|og7ks0,45,62,0|omozjz,45,62,0|omozk0,46,63,1|oyxnfz,46,63,1|oyxng0,45,62,0|p5f27z,45,62,0|p5f280,46,63,1|phnq3z,46,63,1|phnq40,45,62,0|po54vz,45,62,0|po54w0,46,63,1|q0dsrz,46,63,1|q0dss0,45,62,0|q6v7jz,45,62,0|q6v7k0,46,63,1|qj3vfz,46,63,1|qj3vg0,45,62,0|qpy8vz,45,62,0|qpy8w0,46,63,1|r26wrz,46,63,1|r26ws0,45,62,0|r8objz,45,62,0|r8obk0,46,63,1|rkwzfz,46,63,1|rkwzg0,45,62,0|rree7z,45,62,0|rree80,46,63,1|s3n23z,46,63,1|s3n240,45,62,0|sa4gvz,45,62,0|sa4gw0,46,63,1|smd4rz,46,63,1|smd4s0,45,62,0|ssujjz,45,62,0|ssujk0,46,63,1|t537fz,46,63,1|t537g0,45,62,0|tbkm7z,45,62,0|tbkm80,46,63,1|tnta3z,46,63,1|tnta40,45,62,0|tunnjz,45,62,0|tunnk0,46,63,1|u6wbfz,46,63,1|u6wbg0,45,62,0|uddq7z,45,62,0|uddq80,46,63,1|upme3z,46,63,1|upme40,45,62,0|uw3svz,45,62,0|uw3sw0,46,63,1|v8cgrz,46,63,1|v8cgs0,45,62,0|vetvjz,45,62,0|vetvk0,46,63,1|vr2jfz,46,63,1|vr2jg0,45,62,0|vxjy7z,45,62,0|vxjy80,46,63,1|w9sm3z,46,63,1|w9sm40,45,62,0|wgmzjz,45,62,0|wgmzk0,46,63,1|wsvnfz,46,63,1|wsvng0,45,62,0|wzd27z,45,62,0|wzd280,46,63,1|xblq3z,46,63,1|xblq40,45,62,0|xi34vz,45,62,0|xi34w0,46,63,1|xubsrz,46,63,1|xubss0,45,62,0|y0t7jz,45,62,0|y0t7k0,46,63,1|yd1vfz,46,63,1|yd1vg0,45,62,0|yjja7z,45,62,0|yjja80,46,63,1|yvry3z,46,63,1|yvry40,45,62,0|z29cvz,45,62,0|z29cw0,46,63,1|zei0rz,46,63,1|zei0s0,45,62,0\",\"America/Yakutat|,0,189,0|-1hc7qjz,0,190,0|-1078vgi,0,190,0|-1078vgh,36,37,0|-ek1tg1,36,37,0|-ek1tg0,65,40,1|-cq2tg1,65,40,1|-cq2tg0,66,40,1|-cnos81,66,40,1|-cnos80,36,37,0|-cs9g1,36,37,0|-cs9g0,64,40,1|-3faw1,64,40,1|-3faw0,36,37,0|5xt7z,36,37,0|5xt80,64,40,1|farrz,64,40,1|fars0,36,37,0|onvvz,36,37,0|onvw0,64,40,1|ydt3z,64,40,1|ydt40,36,37,0|17qx7z,36,37,0|17qx80,64,40,1|1h3vrz,64,40,1|1h3vs0,36,37,0|1qgzvz,36,37,0|1qgzw0,64,40,1|1ztyfz,64,40,1|1ztyg0,36,37,0|23fnvz,36,37,0|23fnw0,64,40,1|2ik13z,64,40,1|2ik140,36,37,0|2ooh7z,36,37,0|2ooh80,64,40,1|31a3rz,64,40,1|31a3s0,36,37,0|3an7vz,36,37,0|3an7w0,64,40,1|3kd53z,64,40,1|3kd540,36,37,0|3tdajz,36,37,0|3tdak0,64,40,1|4337rz,64,40,1|4337s0,36,37,0|4cgbvz,36,37,0|4cgbw0,64,40,1|4ltafz,64,40,1|4ltag0,36,37,0|4v6ejz,36,37,0|4v6ek0,64,40,1|54jd3z,64,40,1|54jd40,36,37,0|5dwh7z,36,37,0|5dwh80,64,40,1|5n9frz,64,40,1|5n9fs0,36,37,0|5wmjvz,36,37,0|5wmjw0,64,40,1|65zifz,64,40,1|65zig0,36,37,0|6fcmjz,36,37,0|6fcmk0,64,40,1|6p2jrz,64,40,1|6p2js0,36,37,0|6y2p7z,36,37,0|6y2p80,64,40,1|77smfz,64,40,1|77smg0,36,37,0|79dybz,36,37,0|79dyc0,37,37,0|7h5qjz,37,37,0|7h5qk0,38,40,1|7qip3z,38,40,1|7qip40,37,37,0|7zvt7z,37,37,0|7zvt80,38,40,1|898rrz,38,40,1|898rs0,37,37,0|8ilvvz,37,37,0|8ilvw0,38,40,1|8ryufz,38,40,1|8ryug0,37,37,0|9092jz,37,37,0|9092k0,38,40,1|9aox3z,38,40,1|9aox40,37,37,0|9iz57z,37,37,0|9iz580,38,40,1|9tryfz,38,40,1|9tryg0,37,37,0|a1p7vz,37,37,0|a1p7w0,38,40,1|aci13z,38,40,1|aci140,37,37,0|akfajz,37,37,0|akfak0,38,40,1|av83rz,38,40,1|av83s0,37,37,0|b3ibvz,37,37,0|b3ibw0,38,40,1|bdy6fz,38,40,1|bdy6g0,37,37,0|bm8ejz,37,37,0|bm8ek0,38,40,1|bwo93z,38,40,1|bwo940,37,37,0|c4yh7z,37,37,0|c4yh80,38,40,1|cfrafz,38,40,1|cfrag0,37,37,0|cnojvz,37,37,0|cnojw0,38,40,1|cyhd3z,38,40,1|cyhd40,37,37,0|d6emjz,37,37,0|d6emk0,38,40,1|dh7frz,38,40,1|dh7fs0,37,37,0|dphnvz,37,37,0|dphnw0,38,40,1|dzxifz,38,40,1|dzxig0,37,37,0|e87qjz,37,37,0|e87qk0,38,40,1|einl3z,38,40,1|einl40,37,37,0|eqxt7z,37,37,0|eqxt80,38,40,1|f1dnrz,38,40,1|f1dns0,37,37,0|f9nvvz,37,37,0|f9nvw0,38,40,1|fkgp3z,38,40,1|fkgp40,37,37,0|fsdyjz,37,37,0|fsdyk0,38,40,1|g36rrz,38,40,1|g36rs0,37,37,0|gb417z,37,37,0|gb4180,38,40,1|glwufz,38,40,1|glwug0,37,37,0|gu72jz,37,37,0|gu72k0,38,40,1|h4mx3z,38,40,1|h4mx40,37,37,0|hcx57z,37,37,0|hcx580,38,40,1|hnczrz,38,40,1|hnczs0,37,37,0|hvn7vz,37,37,0|hvn7w0,38,40,1|i6g13z,38,40,1|i6g140,37,37,0|iedajz,37,37,0|iedak0,38,40,1|ip63rz,38,40,1|ip63s0,37,37,0|ix3d7z,37,37,0|ix3d80,38,40,1|j7w6fz,38,40,1|j7w6g0,37,37,0|jeqjvz,37,37,0|jeqjw0,38,40,1|jqz7rz,38,40,1|jqz7s0,37,37,0|jxgmjz,37,37,0|jxgmk0,38,40,1|k9pafz,38,40,1|k9pag0,37,37,0|kg6p7z,37,37,0|kg6p80,38,40,1|ksfd3z,38,40,1|ksfd40,37,37,0|kz9qjz,37,37,0|kz9qk0,38,40,1|lbiefz,38,40,1|lbieg0,37,37,0|lhzt7z,37,37,0|lhzt80,38,40,1|lu8h3z,38,40,1|lu8h40,37,37,0|m0pvvz,37,37,0|m0pvw0,38,40,1|mcyjrz,38,40,1|mcyjs0,37,37,0|mjfyjz,37,37,0|mjfyk0,38,40,1|mvomfz,38,40,1|mvomg0,37,37,0|n2617z,37,37,0|n26180,38,40,1|neep3z,38,40,1|neep40,37,37,0|nkw3vz,37,37,0|nkw3w0,38,40,1|nx4rrz,38,40,1|nx4rs0,37,37,0|o3z57z,37,37,0|o3z580,38,40,1|og7t3z,38,40,1|og7t40,37,37,0|omp7vz,37,37,0|omp7w0,38,40,1|oyxvrz,38,40,1|oyxvs0,37,37,0|p5fajz,37,37,0|p5fak0,38,40,1|phnyfz,38,40,1|phnyg0,37,37,0|po5d7z,37,37,0|po5d80,38,40,1|q0e13z,38,40,1|q0e140,37,37,0|q6vfvz,37,37,0|q6vfw0,38,40,1|qj43rz,38,40,1|qj43s0,37,37,0|qpyh7z,37,37,0|qpyh80,38,40,1|r2753z,38,40,1|r27540,37,37,0|r8ojvz,37,37,0|r8ojw0,38,40,1|rkx7rz,38,40,1|rkx7s0,37,37,0|rremjz,37,37,0|rremk0,38,40,1|s3nafz,38,40,1|s3nag0,37,37,0|sa4p7z,37,37,0|sa4p80,38,40,1|smdd3z,38,40,1|smdd40,37,37,0|ssurvz,37,37,0|ssurw0,38,40,1|t53frz,38,40,1|t53fs0,37,37,0|tbkujz,37,37,0|tbkuk0,38,40,1|tntifz,38,40,1|tntig0,37,37,0|tunvvz,37,37,0|tunvw0,38,40,1|u6wjrz,38,40,1|u6wjs0,37,37,0|uddyjz,37,37,0|uddyk0,38,40,1|upmmfz,38,40,1|upmmg0,37,37,0|uw417z,37,37,0|uw4180,38,40,1|v8cp3z,38,40,1|v8cp40,37,37,0|veu3vz,37,37,0|veu3w0,38,40,1|vr2rrz,38,40,1|vr2rs0,37,37,0|vxk6jz,37,37,0|vxk6k0,38,40,1|w9sufz,38,40,1|w9sug0,37,37,0|wgn7vz,37,37,0|wgn7w0,38,40,1|wsvvrz,38,40,1|wsvvs0,37,37,0|wzdajz,37,37,0|wzdak0,38,40,1|xblyfz,38,40,1|xblyg0,37,37,0|xi3d7z,37,37,0|xi3d80,38,40,1|xuc13z,38,40,1|xuc140,37,37,0|y0tfvz,37,37,0|y0tfw0,38,40,1|yd23rz,38,40,1|yd23s0,37,37,0|yjjijz,37,37,0|yjjik0,38,40,1|yvs6fz,38,40,1|yvs6g0,37,37,0|z29l7z,37,37,0|z29l80,38,40,1|zei93z,38,40,1|zei940,37,37,0\",\"America/Yellowknife|,60,1,0|-i9m2o0,50,66,0|-ek1z01,50,66,0|-ek1z00,58,62,1|-cq2tg1,58,62,1|-cq2tg0,59,62,1|-cnoxs1,59,62,1|-cnoxs0,50,66,0|-2g1tw1,50,66,0|-2g1tw0,61,63,1|-26btw1,61,63,1|-26btw0,50,66,0|5dwbnz,50,66,0|5dwbo0,52,62,1|5n9a7z,52,62,1|5n9a80,50,66,0|5wmebz,50,66,0|5wmec0,52,62,1|65zcvz,52,62,1|65zcw0,50,66,0|6fcgzz,50,66,0|6fch00,52,62,1|6p2e7z,52,62,1|6p2e80,50,66,0|6y2jnz,50,66,0|6y2jo0,52,62,1|77sgvz,52,62,1|77sgw0,50,66,0|7h5kzz,50,66,0|7h5l00,52,62,1|7qijjz,52,62,1|7qijk0,50,66,0|7zvnnz,50,66,0|7zvno0,52,62,1|898m7z,52,62,1|898m80,50,66,0|8ilqbz,50,66,0|8ilqc0,52,62,1|8ryovz,52,62,1|8ryow0,50,66,0|908wzz,50,66,0|908x00,52,62,1|9aorjz,52,62,1|9aork0,50,66,0|9iyznz,50,66,0|9iyzo0,52,62,1|9trsvz,52,62,1|9trsw0,50,66,0|a1p2bz,50,66,0|a1p2c0,52,62,1|achvjz,52,62,1|achvk0,50,66,0|akf4zz,50,66,0|akf500,52,62,1|av7y7z,52,62,1|av7y80,50,66,0|b3i6bz,50,66,0|b3i6c0,52,62,1|bdy0vz,52,62,1|bdy0w0,50,66,0|bm88zz,50,66,0|bm8900,52,62,1|bwo3jz,52,62,1|bwo3k0,50,66,0|c4ybnz,50,66,0|c4ybo0,52,62,1|cfr4vz,52,62,1|cfr4w0,50,66,0|cnoebz,50,66,0|cnoec0,52,62,1|cyh7jz,52,62,1|cyh7k0,50,66,0|d6egzz,50,66,0|d6eh00,52,62,1|dh7a7z,52,62,1|dh7a80,50,66,0|dphibz,50,66,0|dphic0,52,62,1|dzxcvz,52,62,1|dzxcw0,50,66,0|e87kzz,50,66,0|e87l00,52,62,1|einfjz,52,62,1|einfk0,50,66,0|eqxnnz,50,66,0|eqxno0,52,62,1|f1di7z,52,62,1|f1di80,50,66,0|f9nqbz,50,66,0|f9nqc0,52,62,1|fkgjjz,52,62,1|fkgjk0,50,66,0|fsdszz,50,66,0|fsdt00,52,62,1|g36m7z,52,62,1|g36m80,50,66,0|gb3vnz,50,66,0|gb3vo0,52,62,1|glwovz,52,62,1|glwow0,50,66,0|gu6wzz,50,66,0|gu6x00,52,62,1|h4mrjz,52,62,1|h4mrk0,50,66,0|hcwznz,50,66,0|hcwzo0,52,62,1|hncu7z,52,62,1|hncu80,50,66,0|hvn2bz,50,66,0|hvn2c0,52,62,1|i6fvjz,52,62,1|i6fvk0,50,66,0|ied4zz,50,66,0|ied500,52,62,1|ip5y7z,52,62,1|ip5y80,50,66,0|ix37nz,50,66,0|ix37o0,52,62,1|j7w0vz,52,62,1|j7w0w0,50,66,0|jeqebz,50,66,0|jeqec0,52,62,1|jqz27z,52,62,1|jqz280,50,66,0|jxggzz,50,66,0|jxgh00,52,62,1|k9p4vz,52,62,1|k9p4w0,50,66,0|kg6jnz,50,66,0|kg6jo0,52,62,1|ksf7jz,52,62,1|ksf7k0,50,66,0|kz9kzz,50,66,0|kz9l00,52,62,1|lbi8vz,52,62,1|lbi8w0,50,66,0|lhznnz,50,66,0|lhzno0,52,62,1|lu8bjz,52,62,1|lu8bk0,50,66,0|m0pqbz,50,66,0|m0pqc0,52,62,1|mcye7z,52,62,1|mcye80,50,66,0|mjfszz,50,66,0|mjft00,52,62,1|mvogvz,52,62,1|mvogw0,50,66,0|n25vnz,50,66,0|n25vo0,52,62,1|neejjz,52,62,1|neejk0,50,66,0|nkvybz,50,66,0|nkvyc0,52,62,1|nx4m7z,52,62,1|nx4m80,50,66,0|o3yznz,50,66,0|o3yzo0,52,62,1|og7njz,52,62,1|og7nk0,50,66,0|omp2bz,50,66,0|omp2c0,52,62,1|oyxq7z,52,62,1|oyxq80,50,66,0|p5f4zz,50,66,0|p5f500,52,62,1|phnsvz,52,62,1|phnsw0,50,66,0|po57nz,50,66,0|po57o0,52,62,1|q0dvjz,52,62,1|q0dvk0,50,66,0|q6vabz,50,66,0|q6vac0,52,62,1|qj3y7z,52,62,1|qj3y80,50,66,0|qpybnz,50,66,0|qpybo0,52,62,1|r26zjz,52,62,1|r26zk0,50,66,0|r8oebz,50,66,0|r8oec0,52,62,1|rkx27z,52,62,1|rkx280,50,66,0|rregzz,50,66,0|rreh00,52,62,1|s3n4vz,52,62,1|s3n4w0,50,66,0|sa4jnz,50,66,0|sa4jo0,52,62,1|smd7jz,52,62,1|smd7k0,50,66,0|ssumbz,50,66,0|ssumc0,52,62,1|t53a7z,52,62,1|t53a80,50,66,0|tbkozz,50,66,0|tbkp00,52,62,1|tntcvz,52,62,1|tntcw0,50,66,0|tunqbz,50,66,0|tunqc0,52,62,1|u6we7z,52,62,1|u6we80,50,66,0|uddszz,50,66,0|uddt00,52,62,1|upmgvz,52,62,1|upmgw0,50,66,0|uw3vnz,50,66,0|uw3vo0,52,62,1|v8cjjz,52,62,1|v8cjk0,50,66,0|vetybz,50,66,0|vetyc0,52,62,1|vr2m7z,52,62,1|vr2m80,50,66,0|vxk0zz,50,66,0|vxk100,52,62,1|w9sovz,52,62,1|w9sow0,50,66,0|wgn2bz,50,66,0|wgn2c0,52,62,1|wsvq7z,52,62,1|wsvq80,50,66,0|wzd4zz,50,66,0|wzd500,52,62,1|xblsvz,52,62,1|xblsw0,50,66,0|xi37nz,50,66,0|xi37o0,52,62,1|xubvjz,52,62,1|xubvk0,50,66,0|y0tabz,50,66,0|y0tac0,52,62,1|yd1y7z,52,62,1|yd1y80,50,66,0|yjjczz,50,66,0|yjjd00,52,62,1|yvs0vz,52,62,1|yvs0w0,50,66,0|z29fnz,50,66,0|z29fo0,52,62,1|zei3jz,52,62,1|zei3k0,50,66,0\",\"Antarctica/Casey|,60,1,0|-irxc0,89,191,0|kro7bz,89,191,0|kro7c0,90,192,0|kyrizz,90,192,0|kyrj00,89,191,0|ltqknz,89,191,0|ltqko0,90,192,0|lzr5vz,90,192,0|lzr5w0,89,191,0|ofen3z,89,191,0|ofen40,90,192,0|p5dwjz,90,192,0|p5dwk0,89,191,0|pg70vz,89,191,0|pg70w0,90,192,0|pogv3z,90,192,0|pogv40,89,191,0|pytbfz,89,191,0|pytbg0,90,192,0|q6tz3z,90,192,0|q6tz40,89,191,0|qhmv5n,89,191,0|qhmv5o,90,192,0\",\"Antarctica/Davis|,60,1,0|-6rmdc0,91,193,0|-2p2zg1,91,193,0|-2p2zg0,60,1,0|-h6io1,60,1,0|-h6io0,91,193,0|kroa3z,91,193,0|kroa40,92,194,0|kz30vz,92,194,0|kz30w0,91,193,0|ltqnfz,91,193,0|ltqng0,92,194,0|lzre7z,92,194,0|lzre80,91,193,0\",\"Antarctica/DumontDUrville|,60,1,0|-c05eo0,93,195,0|-9dkmg1,93,195,0|-9dkmg0,60,1,0|-6vdk01,60,1,0|-6vdk00,93,195,0\",\"Antarctica/Macquarie|,60,1,0|-10mb9c0,94,195,0|-rsj4w1,94,195,0|-rsj4w0,95,192,1|-rjj281,95,192,1|-rjj280,94,195,0|-qhmeg1,94,195,0|-qhmeg0,60,1,0|-bd1xc1,60,1,0|-bd1xc0,94,195,0|-16cow1,94,195,0|-16cow0,95,192,1|-wznk1,95,192,1|-wznk0,94,195,0|-m6rk1,94,195,0|-m6rk0,95,192,1|-fcgw1,95,192,1|-fcgw0,94,195,0|-3gow1,94,195,0|-3gow0,95,192,1|3dlrz,95,192,1|3dls0,94,195,0|f9drz,94,195,0|f9ds0,95,192,1|mgn3z,95,192,1|mgn40,94,195,0|ycf3z,94,195,0|ycf40,95,192,1|14gsfz,95,192,1|14gsg0,94,195,0|1h2hrz,94,195,0|1h2hs0,95,192,1|1njtrz,95,192,1|1njts0,94,195,0|1zskfz,94,195,0|1zskg0,95,192,1|269wfz,95,192,1|269wg0,94,195,0|2iin3z,94,195,0|2iin40,95,192,1|2ozz3z,95,192,1|2ozz40,94,195,0|318prz,94,195,0|318ps0,95,192,1|3830fz,95,192,1|3830g0,94,195,0|3kbr3z,94,195,0|3kbr40,95,192,1|3qt33z,95,192,1|3qt340,94,195,0|431trz,94,195,0|431ts0,95,192,1|49j5rz,95,192,1|49j5s0,94,195,0|4lrwfz,94,195,0|4lrwg0,95,192,1|4s98fz,95,192,1|4s98g0,94,195,0|54hz3z,94,195,0|54hz40,95,192,1|5azb3z,95,192,1|5azb40,94,195,0|5n81rz,94,195,0|5n81s0,95,192,1|5tpdrz,95,192,1|5tpds0,94,195,0|65y4fz,94,195,0|65y4g0,95,192,1|6dvb3z,95,192,1|6dvb40,94,195,0|6p15rz,94,195,0|6p15s0,95,192,1|6wldrz,95,192,1|6wlds0,94,195,0|77r8fz,94,195,0|77r8g0,95,192,1|7e8kfz,95,192,1|7e8kg0,94,195,0|7qhb3z,94,195,0|7qhb40,95,192,1|7wyn3z,95,192,1|7wyn40,94,195,0|897drz,94,195,0|897ds0,95,192,1|8foprz,95,192,1|8fops0,94,195,0|8rkhrz,94,195,0|8rkhs0,95,192,1|8z4prz,95,192,1|8z4ps0,94,195,0|9anj3z,94,195,0|9anj40,95,192,1|9i7r3z,95,192,1|9i7r40,94,195,0|9tqkfz,94,195,0|9tqkg0,95,192,1|a0xtrz,95,192,1|a0xts0,94,195,0|acgn3z,94,195,0|acgn40,95,192,1|ajnwfz,95,192,1|ajnwg0,94,195,0|av6prz,94,195,0|av6ps0,95,192,1|b33wfz,95,192,1|b33wg0,94,195,0|bctwfz,94,195,0|bctwg0,95,192,1|bltz3z,95,192,1|bltz40,94,195,0|bvjz3z,94,195,0|bvjz40,95,192,1|c4k1rz,95,192,1|c4k1s0,94,195,0|cea1rz,94,195,0|cea1s0,95,192,1|cna4fz,95,192,1|cna4g0,94,195,0|cx04fz,94,195,0|cx04g0,95,192,1|d6073z,95,192,1|d60740,94,195,0|dfq73z,94,195,0|dfq740,95,192,1|dp38fz,95,192,1|dp38g0,94,195,0|dyt8fz,94,195,0|dyt8g0,95,192,1|e7tb3z,95,192,1|e7tb40,94,195,0|ehjb3z,94,195,0|ehjb40,95,192,1|eqjdrz,95,192,1|eqjds0,94,195,0|f09drz,94,195,0|f09ds0,95,192,1|f99gfz,95,192,1|f99gg0,94,195,0|fizgfz,94,195,0|fizgg0,95,192,1|frzj3z,95,192,1|frzj40,94,195,0|fzwprz,94,195,0|fzwps0,95,192,1|gaplrz,95,192,1|gapls0,94,195,0|gkskfz,94,195,0|gkskg0,95,192,1|gtsn3z,95,192,1|gtsn40,94,195,0|h3in3z,94,195,0|h3in40,95,192,1|hciprz,95,192,1|hcips0,94,195,0|hm8prz,94,195,0|hm8ps0,95,192,1|hv8sfz,95,192,1|hv8sg0,94,195,0|i4ysfz,94,195,0|i4ysg0,95,192,1|idyv3z,95,192,1|idyv40,94,195,0|inov3z,94,195,0|inov40,95,192,1|ix1wfz,95,192,1|ix1wg0,94,195,0|j6exrz,94,195,0|j6exs0,95,192,1|jff0fz,95,192,1|jff0g0,94,195,0|jphz3z,94,195,0|jphz40,95,192,1|jyv0fz,95,192,1|jyv0g0,94,195,0|k881rz,94,195,0|k881s0,95,192,1|khl33z,95,192,1|khl340,94,195,0|kqy4fz,94,195,0|kqy4g0,95,192,1|lj18fz,95,192,1|lj18g0,94,195,0|lse9rz,94,195,0|lse9s0,95,192,1|m1rb3z,95,192,1|m1rb40,94,195,0|mbhb3z,94,195,0|mbhb40,95,192,1|mkucfz,95,192,1|mkucg0,94,195,0|mu7drz,94,195,0|mu7ds0,95,192,1|n3kf3z,95,192,1|n3kf40,94,195,0|ncxgfz,94,195,0|ncxgg0,95,192,1|nmahrz,95,192,1|nmahs0,94,195,0|nvnj3z,94,195,0|nvnj40,95,192,1|o50kfz,95,192,1|o50kg0,94,195,0|oedlrz,94,195,0|oedls0,95,192,1|onqn3z,95,192,1|onqn40,94,195,0|ox3ofz,94,195,0|ox3og0,95,192,1|p6gprz,95,192,1|p6gps0,94,195,0|pg6prz,94,195,0|pg6ps0,95,192,1|ppjr3z,95,192,1|ppjr40,94,195,0|pywsfz,94,195,0|pywsg0,95,192,1|q89trz,95,192,1|q89ts0,94,195,0|qhmv3z,94,195,0|qhmv40,95,192,1|qqzwfz,95,192,1|qqzwg0,94,195,0|r0cxrz,94,195,0|r0cxs0,95,192,1|r9pz3z,95,192,1|r9pz40,94,195,0|rj30fz,94,195,0|rj30g0,95,192,1|rsg1rz,95,192,1|rsg1s0,94,195,0|s1t33z,94,195,0|s1t340,95,192,1|sbj33z,95,192,1|sbj340,94,195,0|skw4fz,94,195,0|skw4g0,95,192,1|su95rz,95,192,1|su95s0,94,195,0|t3m73z,94,195,0|t3m740,95,192,1|tcz8fz,95,192,1|tcz8g0,94,195,0|tmc9rz,94,195,0|tmc9s0,95,192,1|tvpb3z,95,192,1|tvpb40,94,195,0|u52cfz,94,195,0|u52cg0,95,192,1|uefdrz,95,192,1|uefds0,94,195,0|unsf3z,94,195,0|unsf40,95,192,1|ux5gfz,95,192,1|ux5gg0,94,195,0|v6vgfz,94,195,0|v6vgg0,95,192,1|vg8hrz,95,192,1|vg8hs0,94,195,0|vplj3z,94,195,0|vplj40,95,192,1|vyykfz,95,192,1|vyykg0,94,195,0|w8blrz,94,195,0|w8bls0,95,192,1|whon3z,95,192,1|whon40,94,195,0|wr1ofz,94,195,0|wr1og0,95,192,1|x0eprz,95,192,1|x0eps0,94,195,0|x9rr3z,94,195,0|x9rr40,95,192,1|xj4sfz,95,192,1|xj4sg0,94,195,0|xshtrz,94,195,0|xshts0,95,192,1|y1uv3z,95,192,1|y1uv40,94,195,0|ybkv3z,94,195,0|ybkv40,95,192,1|ykxwfz,95,192,1|ykxwg0,94,195,0|yuaxrz,94,195,0|yuaxs0,95,192,1|z3nz3z,95,192,1|z3nz40,94,195,0|zd10fz,94,195,0|zd10g0,95,192,1\",\"Antarctica/Mawson|,60,1,0|-8aelc0,96,196,0|krocvz,96,196,0|krocw0,92,194,0\",\"Antarctica/McMurdo|,0,197,0|-1gsoz14,97,198,0|-m01p21,97,198,0|-m01p20,98,199,1|-ltxei1,98,199,1|-ltxei0,97,198,0|-lieie1,97,198,0|-lieie0,98,200,1|-lahd41,98,200,1|-lahd40,97,198,0|-kzofq1,97,198,0|-kzofq0,98,200,1|-krrag1,98,200,1|-krrag0,97,198,0|-kgyd21,97,198,0|-kgyd20,98,200,1|-k917s1,98,200,1|-k917s0,97,198,0|-jy8ae1,97,198,0|-jy8ae0,98,200,1|-jpy6g1,98,200,1|-jpy6g0,97,198,0|-jfi7q1,97,198,0|-jfi7q0,98,200,1|-j783s1,98,200,1|-j783s0,97,198,0|-iws521,97,198,0|-iws520,98,200,1|-imc941,98,200,1|-imc940,97,198,0|-ief121,97,198,0|-ief120,98,200,1|-i3m6g1,98,200,1|-i3m6g0,97,198,0|-hvoye1,97,198,0|-hvoye0,98,200,1|-hkw3s1,98,200,1|-hkw3s0,97,198,0|-hcyvq1,97,198,0|-hcyvq0,98,200,1|-h26141,98,200,1|-h26140,97,198,0|-gu8t21,97,198,0|-gu8t20,98,200,1|-gjfyg1,98,200,1|-gjfyg0,97,198,0|-gbiqe1,97,198,0|-gbiqe0,98,200,1|-g0cx41,98,200,1|-g0cx40,97,198,0|-fssnq1,97,198,0|-fssnq0,98,200,1|-fhmug1,98,200,1|-fhmug0,97,198,0|-f9pme1,97,198,0|-f9pme0,98,200,1|-ciy9c1,98,200,1|-ciy9c0,98,200,0|2ivg7z,98,200,0|2ivg80,99,201,1|2omuvz,99,201,1|2omuw0,98,200,0|318k7z,98,200,0|318k80,99,201,1|382uvz,99,201,1|382uw0,98,200,0|3kbljz,98,200,0|3kblk0,99,201,1|3qsxjz,99,201,1|3qsxk0,98,200,0|431o7z,98,200,0|431o80,99,201,1|49j07z,99,201,1|49j080,98,200,0|4lrqvz,98,200,0|4lrqw0,99,201,1|4s92vz,99,201,1|4s92w0,98,200,0|54htjz,98,200,0|54htk0,99,201,1|5az5jz,99,201,1|5az5k0,98,200,0|5n7w7z,98,200,0|5n7w80,99,201,1|5tp87z,99,201,1|5tp880,98,200,0|65xyvz,98,200,0|65xyw0,99,201,1|6cs9jz,99,201,1|6cs9k0,98,200,0|6p107z,98,200,0|6p1080,99,201,1|6vic7z,99,201,1|6vic80,98,200,0|77r2vz,98,200,0|77r2w0,99,201,1|7e8evz,99,201,1|7e8ew0,98,200,0|7qh5jz,98,200,0|7qh5k0,99,201,1|7wyhjz,99,201,1|7wyhk0,98,200,0|89787z,98,200,0|897880,99,201,1|8fok7z,99,201,1|8fok80,98,200,0|8rxavz,98,200,0|8rxaw0,99,201,1|8yemvz,99,201,1|8yemw0,98,200,0|9andjz,98,200,0|9andk0,99,201,1|9hho7z,99,201,1|9hho80,98,200,0|9tqevz,98,200,0|9tqew0,99,201,1|a07qvz,99,201,1|a07qw0,98,200,0|abdljz,98,200,0|abdlk0,99,201,1|ajnqvz,99,201,1|ajnqw0,98,200,0|au3o7z,98,200,0|au3o80,99,201,1|b2dtjz,99,201,1|b2dtk0,98,200,0|bctqvz,98,200,0|bctqw0,99,201,1|bl3w7z,99,201,1|bl3w80,98,200,0|bvjtjz,98,200,0|bvjtk0,99,201,1|c46xjz,99,201,1|c46xk0,98,200,0|ce9w7z,98,200,0|ce9w80,99,201,1|cmx07z,99,201,1|cmx080,98,200,0|cwzyvz,98,200,0|cwzyw0,99,201,1|d5n2vz,99,201,1|d5n2w0,98,200,0|dfq1jz,98,200,0|dfq1k0,99,201,1|dod5jz,99,201,1|dod5k0,98,200,0|dyt2vz,98,200,0|dyt2w0,99,201,1|e7387z,99,201,1|e73880,98,200,0|ehj5jz,98,200,0|ehj5k0,99,201,1|eptavz,99,201,1|eptaw0,98,200,0|f0987z,98,200,0|f09880,99,201,1|f8wc7z,99,201,1|f8wc80,98,200,0|fizavz,98,200,0|fizaw0,99,201,1|frmevz,99,201,1|frmew0,98,200,0|g1pdjz,98,200,0|g1pdk0,99,201,1|gachjz,99,201,1|gachk0,98,200,0|gksevz,98,200,0|gksew0,99,201,1|gt2k7z,99,201,1|gt2k80,98,200,0|h3ihjz,98,200,0|h3ihk0,99,201,1|hbsmvz,99,201,1|hbsmw0,98,200,0|hm8k7z,98,200,0|hm8k80,99,201,1|huvo7z,99,201,1|huvo80,98,200,0|i4ymvz,98,200,0|i4ymw0,99,201,1|idlqvz,99,201,1|idlqw0,98,200,0|inopjz,98,200,0|inopk0,99,201,1|iwbtjz,99,201,1|iwbtk0,98,200,0|j6es7z,98,200,0|j6es80,99,201,1|jf1w7z,99,201,1|jf1w80,98,200,0|jp4uvz,98,200,0|jp4uw0,99,201,1|jyuuvz,99,201,1|jyuuw0,98,200,0|k7uxjz,98,200,0|k7uxk0,99,201,1|khkxjz,99,201,1|khkxk0,98,200,0|kql07z,98,200,0|kql080,99,201,1|l0b07z,99,201,1|l0b080,98,200,0|l9b2vz,98,200,0|l9b2w0,99,201,1|lj12vz,99,201,1|lj12w0,98,200,0|ls15jz,98,200,0|ls15k0,99,201,1|m1r5jz,99,201,1|m1r5k0,98,200,0|mb46vz,98,200,0|mb46w0,99,201,1|mku6vz,99,201,1|mku6w0,98,200,0|mtu9jz,98,200,0|mtu9k0,99,201,1|n3k9jz,99,201,1|n3k9k0,98,200,0|nckc7z,98,200,0|nckc80,99,201,1|nmac7z,99,201,1|nmac80,98,200,0|nvaevz,98,200,0|nvaew0,99,201,1|o50evz,99,201,1|o50ew0,98,200,0|oe0hjz,98,200,0|oe0hk0,99,201,1|onqhjz,99,201,1|onqhk0,98,200,0|owqk7z,98,200,0|owqk80,99,201,1|p6gk7z,99,201,1|p6gk80,98,200,0|pftljz,98,200,0|pftlk0,99,201,1|ppjljz,99,201,1|ppjlk0,98,200,0|pyjo7z,98,200,0|pyjo80,99,201,1|q89o7z,99,201,1|q89o80,98,200,0|qh9qvz,98,200,0|qh9qw0,99,201,1|qqzqvz,99,201,1|qqzqw0,98,200,0|qzztjz,98,200,0|qzztk0,99,201,1|r9ptjz,99,201,1|r9ptk0,98,200,0|ripw7z,98,200,0|ripw80,99,201,1|rsfw7z,99,201,1|rsfw80,98,200,0|s1fyvz,98,200,0|s1fyw0,99,201,1|sbixjz,99,201,1|sbixk0,98,200,0|skj07z,98,200,0|skj080,99,201,1|su907z,99,201,1|su9080,98,200,0|t392vz,98,200,0|t392w0,99,201,1|tcz2vz,99,201,1|tcz2w0,98,200,0|tlz5jz,98,200,0|tlz5k0,99,201,1|tvp5jz,99,201,1|tvp5k0,98,200,0|u4p87z,98,200,0|u4p880,99,201,1|uef87z,99,201,1|uef880,98,200,0|unfavz,98,200,0|unfaw0,99,201,1|ux5avz,99,201,1|ux5aw0,98,200,0|v6ic7z,98,200,0|v6ic80,99,201,1|vg8c7z,99,201,1|vg8c80,98,200,0|vp8evz,98,200,0|vp8ew0,99,201,1|vyyevz,99,201,1|vyyew0,98,200,0|w7yhjz,98,200,0|w7yhk0,99,201,1|whohjz,99,201,1|whohk0,98,200,0|wqok7z,98,200,0|wqok80,99,201,1|x0ek7z,99,201,1|x0ek80,98,200,0|x9emvz,98,200,0|x9emw0,99,201,1|xj4mvz,99,201,1|xj4mw0,98,200,0|xs4pjz,98,200,0|xs4pk0,99,201,1|y1upjz,99,201,1|y1upk0,98,200,0|yb7qvz,98,200,0|yb7qw0,99,201,1|ykxqvz,99,201,1|ykxqw0,98,200,0|ytxtjz,98,200,0|ytxtk0,99,201,1|z3ntjz,99,201,1|z3ntk0,98,200,0|zcnw7z,98,200,0|zcnw80,99,201,1\",\"Antarctica/Palmer|,60,1,0|-2lxhc0,39,44,1|-2ivzo1,39,44,1|-2ivzo0,42,42,0|-275ow1,42,42,0|-275ow0,39,44,1|-2042c1,39,44,1|-2042c0,42,42,0|-1odrk1,42,42,0|-1odrk0,39,44,1|-1fovo1,39,44,1|-1fovo0,42,42,0|-16brk1,42,42,0|-16brk0,39,44,1|-wluc1,39,44,1|-wluc0,42,42,0|-n8q81,42,42,0|-n8q80,39,44,1|-dvro1,39,44,1|-dvro0,42,42,0|-4ink1,42,42,0|-4ink0,39,44,0|24aizz,39,44,0|24aj00,40,45,1|29bxjz,40,45,1|29bxk0,39,44,0|6fn4bz,39,44,0|6fn4c0,42,42,0|6nz73z,42,42,0|6nz740,39,44,1|6vwazz,39,44,1|6vwb00,42,42,0|76p9rz,42,42,0|76p9s0,39,44,1|7emdnz,39,44,1|7emdo0,42,42,0|7psb3z,42,42,0|7psb40,39,44,1|7xcgbz,39,44,1|7xcgc0,42,42,0|88idrz,42,42,0|88ids0,39,44,1|8g2izz,39,44,1|8g2j00,42,42,0|8r8gfz,42,42,0|8r8gg0,39,44,1|90lezz,39,44,1|90lf00,42,42,0|99yj3z,42,42,0|99yj40,39,44,1|9hvmzz,39,44,1|9hvn00,42,42,0|9solrz,42,42,0|9sols0,39,44,1|a0lpnz,39,44,1|a0lpo0,42,42,0|abrn3z,42,42,0|abrn40,39,44,1|ajbsbz,39,44,1|ajbsc0,42,42,0|at1v3z,42,42,0|at1v40,39,44,1|b21uzz,39,44,1|b21v00,42,42,0|bd7sfz,42,42,0|bd7sg0,39,44,1|bl4wbz,39,44,1|bl4wc0,42,42,0|bvxv3z,42,42,0|bvxv40,39,44,1|c3uyzz,39,44,1|c3uz00,42,42,0|cenxrz,42,42,0|cenxs0,39,44,1|cml1nz,39,44,1|cml1o0,42,42,0|cxe0fz,42,42,0|cxe0g0,39,44,1|d5b4bz,39,44,1|d5b4c0,42,42,0|dgh1rz,42,42,0|dgh1s0,39,44,1|do16zz,39,44,1|do1700,42,42,0|dz74fz,42,42,0|dz74g0,39,44,1|e7u5nz,39,44,1|e7u5o0,42,42,0|ehx73z,42,42,0|ehx740,39,44,1|epuazz,39,44,1|epub00,42,42,0|ezxcfz,42,42,0|ezxcg0,39,44,1|f9n9nz,39,44,1|f9n9o0,42,42,0|fjdcfz,42,42,0|fjdcg0,39,44,1|fragbz,39,44,1|fragc0,42,42,0|g2gdrz,42,42,0|g2gds0,39,44,1|ga0izz,39,44,1|ga0j00,42,42,0|gl6gfz,42,42,0|gl6gg0,39,44,1|gsqlnz,39,44,1|gsqlo0,42,42,0|h3wj3z,42,42,0|h3wj40,39,44,1|hbgobz,39,44,1|hbgoc0,42,42,0|hmmlrz,42,42,0|hmmls0,39,44,1|hujpnz,39,44,1|hujpo0,42,42,0|i5cofz,42,42,0|i5cog0,39,44,1|id9sbz,39,44,1|id9sc0,42,42,0|io2r3z,42,42,0|io2r40,39,44,1|ivzuzz,39,44,1|ivzv00,42,42,0|j75sfz,42,42,0|j75sg0,39,44,1|jepxnz,39,44,1|jepxo0,42,42,0|jpvv3z,42,42,0|jpvv40,39,44,1|jyiwbz,39,44,1|jyiwc0,42,42,0|k8lxrz,42,42,0|k8lxs0,39,44,1|kgj1nz,39,44,1|kgj1o0,42,42,0|krc0fz,42,42,0|krc0g0,39,44,1|l0c0bz,39,44,1|l0c0c0,42,42,0|la233z,42,42,0|la2340,39,44,1|lkuwbz,39,44,1|lkuwc0,42,42,0|lq9f3z,42,42,0|lq9f40,39,44,1|m380bz,39,44,1|m380c0,42,42,0|m9pf3z,42,42,0|m9pf40,39,44,1|mly2zz,39,44,1|mly300,42,42,0|mssgfz,42,42,0|mssgg0,39,44,1|n4o5nz,39,44,1|n4o5o0,42,42,0|nbij3z,42,42,0|nbij40,39,44,1|o776zz,39,44,1|o77700,42,42,0|obvsfz,42,42,0|obvsg0,39,44,1|ohn4bz,39,44,1|ohn4c0,39,44,0\",\"Antarctica/Rothera|,60,1,0|3lxs00,39,44,0\",\"Antarctica/Syowa|,60,1,0|-6qsqo0,100,6,0\",\"Antarctica/Troll|,60,1,0|ibruo0,17,1,0|idzk3z,17,1,0|idzk40,101,11,1|ip5erz,101,11,1|ip5es0,17,1,0|iwpmrz,17,1,0|iwpms0,101,11,1|j7vhfz,101,11,1|j7vhg0,17,1,0|jffpfz,17,1,0|jffpg0,101,11,1|jqlk3z,101,11,1|jqlk40,17,1,0|jyiqrz,17,1,0|jyiqs0,101,11,1|k9bmrz,101,11,1|k9bms0,17,1,0|kh8tfz,17,1,0|kh8tg0,101,11,1|ks1pfz,101,11,1|ks1pg0,17,1,0|kzyw3z,17,1,0|kzyw40,101,11,1|lb4qrz,101,11,1|lb4qs0,17,1,0|lioyrz,17,1,0|lioys0,101,11,1|ltutfz,101,11,1|ltutg0,17,1,0|m1f1fz,17,1,0|m1f1g0,101,11,1|mckw3z,101,11,1|mckw40,17,1,0|mki2rz,17,1,0|mki2s0,101,11,1|mvayrz,101,11,1|mvays0,17,1,0|n385fz,17,1,0|n385g0,101,11,1|ne11fz,101,11,1|ne11g0,17,1,0|nly83z,17,1,0|nly840,101,11,1|nwr43z,101,11,1|nwr440,17,1,0|o4oarz,17,1,0|o4oas0,101,11,1|ofu5fz,101,11,1|ofu5g0,17,1,0|onedfz,17,1,0|onedg0,101,11,1|oyk83z,101,11,1|oyk840,17,1,0|p64g3z,17,1,0|p64g40,101,11,1|phaarz,101,11,1|phaas0,17,1,0|pp7hfz,17,1,0|pp7hg0,101,11,1|q00dfz,101,11,1|q00dg0,17,1,0|q7xk3z,17,1,0|q7xk40,101,11,1|qiqg3z,101,11,1|qiqg40,17,1,0|qqnmrz,17,1,0|qqnms0,101,11,1|r1thfz,101,11,1|r1thg0,17,1,0|r9dpfz,17,1,0|r9dpg0,101,11,1|rkjk3z,101,11,1|rkjk40,17,1,0|rs3s3z,17,1,0|rs3s40,101,11,1|s39mrz,101,11,1|s39ms0,17,1,0|sb6tfz,17,1,0|sb6tg0,101,11,1|slzpfz,101,11,1|slzpg0,17,1,0|stww3z,17,1,0|stww40,101,11,1|t4ps3z,101,11,1|t4ps40,17,1,0|tcmyrz,17,1,0|tcmys0,101,11,1|tnfurz,101,11,1|tnfus0,17,1,0|tvd1fz,17,1,0|tvd1g0,101,11,1|u6iw3z,101,11,1|u6iw40,17,1,0|ue343z,17,1,0|ue3440,101,11,1|up8yrz,101,11,1|up8ys0,17,1,0|uwt6rz,17,1,0|uwt6s0,101,11,1|v7z1fz,101,11,1|v7z1g0,17,1,0|vfw83z,17,1,0|vfw840,101,11,1|vqp43z,101,11,1|vqp440,17,1,0|vymarz,17,1,0|vymas0,101,11,1|w9f6rz,101,11,1|w9f6s0,17,1,0|whcdfz,17,1,0|whcdg0,101,11,1|wsi83z,101,11,1|wsi840,17,1,0|x02g3z,17,1,0|x02g40,101,11,1|xb8arz,101,11,1|xb8as0,17,1,0|xisirz,17,1,0|xisis0,101,11,1|xtydfz,101,11,1|xtydg0,17,1,0|y1ilfz,17,1,0|y1ilg0,101,11,1|ycog3z,101,11,1|ycog40,17,1,0|yklmrz,17,1,0|yklms0,101,11,1|yveirz,101,11,1|yveis0,17,1,0|z3bpfz,17,1,0|z3bpg0,101,11,1|ze4lfz,101,11,1|ze4lg0,17,1,0\",\"Antarctica/Vostok|,60,1,0|-6aaao0,96,196,0\",\"Arctic/Longyearbyen|,0,202,0|-1353tzo,10,10,0|-rzayo1,10,10,0|-rzayo0,11,11,1|-rskiw1,11,11,1|-rskiw0,10,10,0|-fc7s81,10,10,0|-fc7s80,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cnnmk1,11,11,1|-cnnmk0,10,10,0|-5mxh81,10,10,0|-5mxh80,11,11,1|-5d7h81,11,11,1|-5d7h80,10,10,0|-53ufw1,10,10,0|-53ufw0,11,11,1|-4uhek1,11,11,1|-4uhek0,10,10,0|-4l4d81,10,10,0|-4l4d80,11,11,1|-4brbw1,11,11,1|-4brbw0,10,10,0|-42eak1,10,10,0|-42eak0,11,11,1|-3t1981,11,11,1|-3t1980,10,10,0|-3jo7w1,10,10,0|-3jo7w0,11,11,1|-3ab6k1,11,11,1|-3ab6k0,10,10,0|-30y581,10,10,0|-30y580,11,11,1|-2r8581,11,11,1|-2r8580,10,10,0|-2g2ak1,10,10,0|-2g2ak0,11,11,1|-28i2k1,11,11,1|-28i2k0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Asia/Aden|,0,203,0|-bwgbbg,100,6,0\",\"Asia/Almaty|,0,204,0|-nu1a90,92,194,0|-kmr9w1,92,194,0|-kmr9w0,96,196,0|5vaxzz,96,196,0|5vay00,91,193,1|64pr7z,91,193,1|64pr80,96,196,0|6e2vbz,96,196,0|6e2vc0,91,193,1|6nhojz,91,193,1|6nhok0,96,196,0|6wusnz,96,196,0|6wuso0,91,193,1|769lvz,91,193,1|769lw0,96,196,0|7foknz,96,196,0|7foko0,91,193,1|7p1rjz,91,193,1|7p1rk0,96,196,0|7yesvz,96,196,0|7yesw0,91,193,1|87ru7z,91,193,1|87ru80,96,196,0|8h4vjz,96,196,0|8h4vk0,91,193,1|8qhwvz,91,193,1|8qhww0,96,196,0|8zuy7z,96,196,0|8zuy80,91,193,1|997zjz,91,193,1|997zk0,96,196,0|9il0vz,96,196,0|9il0w0,91,193,1|9ry27z,91,193,1|9ry280,96,196,0|a1b3jz,96,196,0|a1b3k0,91,193,1|aao4vz,91,193,1|aao4w0,96,196,0|ak167z,96,196,0|ak1680,91,193,1|atr67z,91,193,1|atr680,96,196,0|b347jz,96,196,0|b347k0,96,196,1|bchbnz,96,196,1|bchbo0,92,194,0|bi8qbz,92,194,0|bi8qc0,96,196,0|blua7z,96,196,0|blua80,91,193,1|bv7bjz,91,193,1|bv7bk0,96,196,0|c4kcvz,96,196,0|c4kcw0,91,193,1|cdxe7z,91,193,1|cdxe80,96,196,0|cnafjz,96,196,0|cnafk0,91,193,1|cwngvz,91,193,1|cwngw0,96,196,0|d60i7z,96,196,0|d60i80,91,193,1|dfdjjz,91,193,1|dfdjk0,96,196,0|dp3jjz,96,196,0|dp3jk0,91,193,1|dzwfjz,91,193,1|dzwfk0,96,196,0|e7tm7z,96,196,0|e7tm80,91,193,1|eimi7z,91,193,1|eimi80,96,196,0|eqjovz,96,196,0|eqjow0,91,193,1|f1ckvz,91,193,1|f1ckw0,96,196,0|f99rjz,96,196,0|f99rk0,91,193,1|fkfm7z,91,193,1|fkfm80,96,196,0|frzu7z,96,196,0|frzu80,91,193,1|g35ovz,91,193,1|g35ow0,96,196,0|gapwvz,96,196,0|gapww0,91,193,1|glvrjz,91,193,1|glvrk0,96,196,0|gtsy7z,96,196,0|gtsy80,91,193,1|h4lu7z,91,193,1|h4lu80,96,196,0|hcj0vz,96,196,0|hcj0w0,91,193,1|hnbwvz,91,193,1|hnbww0,96,196,0|hv93jz,96,196,0|hv93k0,91,193,1|i6ey7z,91,193,1|i6ey80,96,196,0\",\"Asia/Amman|,0,205,0|-kcrtbk,15,11,0|1sed3z,15,11,0|1sed40,16,6,1|1yeybz,16,6,1|1yeyc0,15,11,0|29bmfz,15,11,0|29bmg0,16,6,1|2h6vnz,16,6,1|2h6vo0,15,11,0|2s3jrz,15,11,0|2s3js0,16,6,1|2zyszz,16,6,1|2zyt00,15,11,0|3axbrz,15,11,0|3axbs0,16,6,1|3kdznz,16,6,1|3kdzo0,15,11,0|3tp93z,15,11,0|3tp940,16,6,1|41kibz,16,6,1|41kic0,15,11,0|4cfbrz,15,11,0|4cfbs0,16,6,1|4kakzz,16,6,1|4kal00,15,11,0|7ygt3z,15,11,0|7ygt40,16,6,1|87vmbz,16,6,1|87vmc0,15,11,0|8heafz,15,11,0|8heag0,16,6,1|8qr8zz,16,6,1|8qr900,15,11,0|904d3z,15,11,0|904d40,16,6,1|99hbnz,16,6,1|99hbo0,15,11,0|9iufrz,15,11,0|9iufs0,16,6,1|9skczz,16,6,1|9skd00,15,11,0|a3ivrz,15,11,0|a3ivs0,16,6,1|abafnz,16,6,1|abafo0,15,11,0|alqfrz,15,11,0|alqfs0,16,6,1|au0ibz,16,6,1|au0ic0,15,11,0|b3zufz,15,11,0|b3zug0,16,6,1|bcdmbz,16,6,1|bcdmc0,15,11,0|bmgnrz,15,11,0|bmgns0,16,6,1|bvgnnz,16,6,1|bvgno0,15,11,0|c4trrz,15,11,0|c4trs0,16,6,1|ce6qbz,16,6,1|ce6qc0,15,11,0|cnjufz,15,11,0|cnjug0,16,6,1|cw6vnz,16,6,1|cw6vo0,15,11,0|d6mvrz,15,11,0|d6mvs0,16,6,1|dex13z,16,6,1|dex140,15,11,0|dpcyfz,15,11,0|dpcyg0,16,6,1|dy02fz,16,6,1|dy02g0,15,11,0|e8313z,15,11,0|e83140,16,6,1|egq53z,16,6,1|egq540,15,11,0|eqt3rz,15,11,0|eqt3s0,16,6,1|ezg7rz,16,6,1|ezg7s0,15,11,0|fe5ufz,15,11,0|fe5ug0,16,6,1|fij93z,16,6,1|fij940,15,11,0|fs7efz,15,11,0|fs7eg0,16,6,1|g1mafz,16,6,1|g1mag0,15,11,0|gaxh3z,15,11,0|gaxh40,16,6,1|gkcd3z,16,6,1|gkcd40,15,11,0|gtpefz,15,11,0|gtpeg0,16,6,1|h32frz,16,6,1|h32fs0,15,11,0|hcfh3z,15,11,0|hcfh40,16,6,1|hn8d3z,16,6,1|hn8d40,15,11,0|hv5jrz,15,11,0|hv5js0,16,6,1|i5lh3z,16,6,1|i5lh40,15,11,0|ie8l3z,15,11,0|ie8l40,16,6,1|inlmfz,16,6,1|inlmg0,15,11,0|iwynrz,15,11,0|iwyns0,16,6,1|j7rjrz,16,6,1|j7rjs0,15,11,0|jfoqfz,15,11,0|jfoqg0,16,6,1|jqhmfz,16,6,1|jqhmg0,15,11,0|jyet3z,15,11,0|jyet40,16,6,1|k9knrz,16,6,1|k9kns0,15,11,0|kh4vrz,15,11,0|kh4vs0,16,6,1|ksaqfz,16,6,1|ksaqg0,15,11,0|kzuyfz,15,11,0|kzuyg0,16,6,1|lb0t3z,16,6,1|lb0t40,15,11,0|lixzrz,15,11,0|lixzs0,16,6,1|ltqvrz,16,6,1|ltqvs0,15,11,0|m1o2fz,15,11,0|m1o2g0,16,6,1|my2nnz,16,6,1|my2no0,15,11,0|n347rz,15,11,0|n347s0,16,6,1|nea2fz,16,6,1|nea2g0,15,11,0|nluafz,15,11,0|nluag0,16,6,1|nx053z,16,6,1|nx0540,15,11,0|o4xbrz,15,11,0|o4xbs0,16,6,1|ofq7rz,16,6,1|ofq7s0,15,11,0|onnefz,15,11,0|onneg0,16,6,1|oygafz,16,6,1|oygag0,15,11,0|p6dh3z,15,11,0|p6dh40,16,6,1|ph6d3z,16,6,1|ph6d40,15,11,0|pp3jrz,15,11,0|pp3js0,16,6,1|pzwfrz,16,6,1|pzwfs0,15,11,0|q7tmfz,15,11,0|q7tmg0,16,6,1|qizh3z,16,6,1|qizh40,15,11,0|qqjp3z,15,11,0|qqjp40,16,6,1|r1pjrz,16,6,1|r1pjs0,15,11,0|r9mqfz,15,11,0|r9mqg0,16,6,1|rkfmfz,16,6,1|rkfmg0,15,11,0|rsct3z,15,11,0|rsct40,16,6,1|s35p3z,16,6,1|s35p40,15,11,0|sb2vrz,15,11,0|sb2vs0,16,6,1|slvrrz,16,6,1|slvrs0,15,11,0|stsyfz,15,11,0|stsyg0,16,6,1|t4yt3z,16,6,1|t4yt40,15,11,0|tcj13z,15,11,0|tcj140,16,6,1|tnovrz,16,6,1|tnovs0,15,11,0|tv93rz,15,11,0|tv93s0,16,6,1|u6eyfz,16,6,1|u6eyg0,15,11,0|uec53z,15,11,0|uec540,16,6,1|up513z,16,6,1|up5140,15,11,0|ux27rz,15,11,0|ux27s0,16,6,1|v7v3rz,16,6,1|v7v3s0,15,11,0|vfsafz,15,11,0|vfsag0,16,6,1|vql6fz,16,6,1|vql6g0,15,11,0|vyid3z,15,11,0|vyid40,16,6,1|w9o7rz,16,6,1|w9o7s0,15,11,0|wh8frz,15,11,0|wh8fs0,16,6,1|wseafz,16,6,1|wseag0,15,11,0|x0bh3z,15,11,0|x0bh40,16,6,1|xb4d3z,16,6,1|xb4d40,15,11,0|xj1jrz,15,11,0|xj1js0,16,6,1|xtufrz,16,6,1|xtufs0,15,11,0|y1rmfz,15,11,0|y1rmg0,16,6,1|yckifz,16,6,1|yckig0,15,11,0|ykhp3z,15,11,0|ykhp40,16,6,1|yvnjrz,16,6,1|yvnjs0,15,11,0|z37rrz,15,11,0|z37rs0,16,6,1|zedmfz,16,6,1|zedmg0,15,11,0\",\"Asia/Anadyr|,0,206,0|-nu1sv8,102,200,0|-kmrtc1,102,200,0|-kmrtc0,103,201,0|5vaejz,103,201,0|5vaek0,104,207,1|64p7rz,104,207,1|64p7s0,103,201,0|6e2bvz,103,201,0|6e2bw0,103,201,1|6nh7vz,103,201,1|6nh7w0,102,200,0|6wubzz,102,200,0|6wuc00,103,201,1|76957z,103,201,1|769580,102,200,0|7fo3zz,102,200,0|7fo400,103,201,1|7p1avz,103,201,1|7p1aw0,102,200,0|7yec7z,102,200,0|7yec80,103,201,1|87rdjz,103,201,1|87rdk0,102,200,0|8h4evz,102,200,0|8h4ew0,103,201,1|8qhg7z,103,201,1|8qhg80,102,200,0|8zuhjz,102,200,0|8zuhk0,103,201,1|997ivz,103,201,1|997iw0,102,200,0|9ikk7z,102,200,0|9ikk80,103,201,1|9rxljz,103,201,1|9rxlk0,102,200,0|a1amvz,102,200,0|a1amw0,103,201,1|aano7z,103,201,1|aano80,102,200,0|ak0pjz,102,200,0|ak0pk0,103,201,1|atqpjz,103,201,1|atqpk0,102,200,0|b33qvz,102,200,0|b33qw0,102,200,1|bcguzz,102,200,1|bcgv00,90,192,0|bi89nz,90,192,0|bi89o0,102,200,0|blttjz,102,200,0|blttk0,103,201,1|bv6uvz,103,201,1|bv6uw0,102,200,0|c4jw7z,102,200,0|c4jw80,103,201,1|cdwxjz,103,201,1|cdwxk0,102,200,0|cn9yvz,102,200,0|cn9yw0,103,201,1|cwn07z,103,201,1|cwn080,102,200,0|d601jz,102,200,0|d601k0,103,201,1|dfd2vz,103,201,1|dfd2w0,102,200,0|dp32vz,102,200,0|dp32w0,103,201,1|dzvyvz,103,201,1|dzvyw0,102,200,0|e7t5jz,102,200,0|e7t5k0,103,201,1|eim1jz,103,201,1|eim1k0,102,200,0|eqj87z,102,200,0|eqj880,103,201,1|f1c47z,103,201,1|f1c480,102,200,0|f99avz,102,200,0|f99aw0,103,201,1|fkf5jz,103,201,1|fkf5k0,102,200,0|frzdjz,102,200,0|frzdk0,103,201,1|g3587z,103,201,1|g35880,102,200,0|gapg7z,102,200,0|gapg80,103,201,1|glvavz,103,201,1|glvaw0,102,200,0|gtshjz,102,200,0|gtshk0,103,201,1|h4ldjz,103,201,1|h4ldk0,102,200,0|hcik7z,102,200,0|hcik80,103,201,1|hnbg7z,103,201,1|hnbg80,102,200,0|hv8mvz,102,200,0|hv8mw0,103,201,1|i6ehjz,103,201,1|i6ehk0,102,200,0|idypjz,102,200,0|idypk0,103,201,1|ip4k7z,103,201,1|ip4k80,102,200,0|iwos7z,102,200,0|iwos80,103,201,1|j7umvz,103,201,1|j7umw0,102,200,0|jfeuvz,102,200,0|jfeuw0,103,201,1|jqkpjz,103,201,1|jqkpk0,102,200,0|jyhw7z,102,200,0|jyhw80,103,201,1|k9as7z,103,201,1|k9as80,102,200,0|kh7yvz,102,200,0|kh7yw0,103,201,1|ks0uvz,103,201,1|ks0uw0,102,200,0|kzy1jz,102,200,0|kzy1k0,102,200,1|lb3yzz,102,200,1|lb3z00,90,192,0|lio6zz,90,192,0|lio700,102,200,0\",\"Asia/Aqtau|,0,208,0|-nu15b4,105,209,0|-kmr741,105,209,0|-kmr740,92,194,0|64pwrz,92,194,0|64pws0,96,196,0|6e2vbz,96,196,0|6e2vc0,96,196,1|6nhrbz,96,196,1|6nhrc0,92,194,0|6wuvfz,92,194,0|6wuvg0,96,196,1|769onz,96,196,1|769oo0,92,194,0|7fonfz,92,194,0|7fong0,96,196,1|7p1ubz,96,196,1|7p1uc0,92,194,0|7yevnz,92,194,0|7yevo0,96,196,1|87rwzz,96,196,1|87rx00,92,194,0|8h4ybz,92,194,0|8h4yc0,96,196,1|8qhznz,96,196,1|8qhzo0,92,194,0|8zv0zz,92,194,0|8zv100,96,196,1|9982bz,96,196,1|9982c0,92,194,0|9il3nz,92,194,0|9il3o0,96,196,1|9ry4zz,96,196,1|9ry500,92,194,0|a1b6bz,92,194,0|a1b6c0,96,196,1|aao7nz,96,196,1|aao7o0,92,194,0|ak18zz,92,194,0|ak1900,96,196,1|atr8zz,96,196,1|atr900,92,194,0|b34abz,92,194,0|b34ac0,92,194,1|bchefz,92,194,1|bcheg0,105,209,0|bi8t3z,105,209,0|bi8t40,92,194,0|bluczz,92,194,0|blud00,96,196,1|bv7ebz,96,196,1|bv7ec0,92,194,0|c4kfnz,92,194,0|c4kfo0,96,196,1|cdxgzz,96,196,1|cdxh00,92,194,0|cnaibz,92,194,0|cnaic0,96,196,1|cwnjnz,96,196,1|cwnjo0,105,209,0|d60nrz,105,209,0|d60ns0,92,194,1|dfdp3z,92,194,1|dfdp40,105,209,0|dp3p3z,105,209,0|dp3p40,92,194,1|dzwl3z,92,194,1|dzwl40,105,209,0|e7trrz,105,209,0|e7trs0,92,194,1|eimnrz,92,194,1|eimns0,105,209,0|eqjufz,105,209,0|eqjug0,92,194,1|f1cqfz,92,194,1|f1cqg0,105,209,0|f99x3z,105,209,0|f99x40,92,194,1|fkfrrz,92,194,1|fkfrs0,105,209,0|frzzrz,105,209,0|frzzs0,92,194,1|g35ufz,92,194,1|g35ug0,105,209,0|gaq2fz,105,209,0|gaq2g0,92,194,1|glvx3z,92,194,1|glvx40,105,209,0|gtt3rz,105,209,0|gtt3s0,92,194,1|h4lzrz,92,194,1|h4lzs0,105,209,0|hcj6fz,105,209,0|hcj6g0,92,194,1|hnc2fz,92,194,1|hnc2g0,105,209,0|hv993z,105,209,0|hv9940,92,194,1|i6f3rz,92,194,1|i6f3s0,92,194,0\",\"Asia/Aqtobe|,0,210,0|-nu16l4,105,209,0|-kmr741,105,209,0|-kmr740,92,194,0|5vb0rz,92,194,0|5vb0s0,96,196,1|64ptzz,96,196,1|64pu00,96,196,0|6e2vbz,96,196,0|6e2vc0,96,196,1|6nhrbz,96,196,1|6nhrc0,92,194,0|6wuvfz,92,194,0|6wuvg0,96,196,1|769onz,96,196,1|769oo0,92,194,0|7fonfz,92,194,0|7fong0,96,196,1|7p1ubz,96,196,1|7p1uc0,92,194,0|7yevnz,92,194,0|7yevo0,96,196,1|87rwzz,96,196,1|87rx00,92,194,0|8h4ybz,92,194,0|8h4yc0,96,196,1|8qhznz,96,196,1|8qhzo0,92,194,0|8zv0zz,92,194,0|8zv100,96,196,1|9982bz,96,196,1|9982c0,92,194,0|9il3nz,92,194,0|9il3o0,96,196,1|9ry4zz,96,196,1|9ry500,92,194,0|a1b6bz,92,194,0|a1b6c0,96,196,1|aao7nz,96,196,1|aao7o0,92,194,0|ak18zz,92,194,0|ak1900,96,196,1|atr8zz,96,196,1|atr900,92,194,0|b34abz,92,194,0|b34ac0,92,194,1|bchefz,92,194,1|bcheg0,105,209,0|bi8t3z,105,209,0|bi8t40,92,194,0|bluczz,92,194,0|blud00,96,196,1|bv7ebz,96,196,1|bv7ec0,92,194,0|c4kfnz,92,194,0|c4kfo0,96,196,1|cdxgzz,96,196,1|cdxh00,92,194,0|cnaibz,92,194,0|cnaic0,96,196,1|cwnjnz,96,196,1|cwnjo0,92,194,0|d60kzz,92,194,0|d60l00,96,196,1|dfdmbz,96,196,1|dfdmc0,92,194,0|dp3mbz,92,194,0|dp3mc0,96,196,1|dzwibz,96,196,1|dzwic0,92,194,0|e7tozz,92,194,0|e7tp00,96,196,1|eimkzz,96,196,1|eiml00,92,194,0|eqjrnz,92,194,0|eqjro0,96,196,1|f1cnnz,96,196,1|f1cno0,92,194,0|f99ubz,92,194,0|f99uc0,96,196,1|fkfozz,96,196,1|fkfp00,92,194,0|frzwzz,92,194,0|frzx00,96,196,1|g35rnz,96,196,1|g35ro0,92,194,0|gapznz,92,194,0|gapzo0,96,196,1|glvubz,96,196,1|glvuc0,92,194,0|gtt0zz,92,194,0|gtt100,96,196,1|h4lwzz,96,196,1|h4lx00,92,194,0|hcj3nz,92,194,0|hcj3o0,96,196,1|hnbznz,96,196,1|hnbzo0,92,194,0|hv96bz,92,194,0|hv96c0,96,196,1|i6f0zz,96,196,1|i6f100,92,194,0\",\"Asia/Ashgabat|,0,211,0|-nu16t8,105,209,0|-kmr741,105,209,0|-kmr740,92,194,0|5vb0rz,92,194,0|5vb0s0,96,196,1|64ptzz,96,196,1|64pu00,92,194,0|6e2y3z,92,194,0|6e2y40,96,196,1|6nhrbz,96,196,1|6nhrc0,92,194,0|6wuvfz,92,194,0|6wuvg0,96,196,1|769onz,96,196,1|769oo0,92,194,0|7fonfz,92,194,0|7fong0,96,196,1|7p1ubz,96,196,1|7p1uc0,92,194,0|7yevnz,92,194,0|7yevo0,96,196,1|87rwzz,96,196,1|87rx00,92,194,0|8h4ybz,92,194,0|8h4yc0,96,196,1|8qhznz,96,196,1|8qhzo0,92,194,0|8zv0zz,92,194,0|8zv100,96,196,1|9982bz,96,196,1|9982c0,92,194,0|9il3nz,92,194,0|9il3o0,96,196,1|9ry4zz,96,196,1|9ry500,92,194,0|a1b6bz,92,194,0|a1b6c0,96,196,1|aao7nz,96,196,1|aao7o0,92,194,0|ak18zz,92,194,0|ak1900,96,196,1|atr8zz,96,196,1|atr900,92,194,0|b34abz,92,194,0|b34ac0,92,194,1|bchefz,92,194,1|bcheg0,105,209,0|bi8t3z,105,209,0|bi8t40,92,194,0\",\"Asia/Atyrau|,0,212,0|-nu15m8,100,6,0|-kmr4c1,100,6,0|-kmr4c0,92,194,0|64pwrz,92,194,0|64pws0,96,196,0|6e2vbz,96,196,0|6e2vc0,96,196,1|6nhrbz,96,196,1|6nhrc0,92,194,0|6wuvfz,92,194,0|6wuvg0,96,196,1|769onz,96,196,1|769oo0,92,194,0|7fonfz,92,194,0|7fong0,96,196,1|7p1ubz,96,196,1|7p1uc0,92,194,0|7yevnz,92,194,0|7yevo0,96,196,1|87rwzz,96,196,1|87rx00,92,194,0|8h4ybz,92,194,0|8h4yc0,96,196,1|8qhznz,96,196,1|8qhzo0,92,194,0|8zv0zz,92,194,0|8zv100,96,196,1|9982bz,96,196,1|9982c0,92,194,0|9il3nz,92,194,0|9il3o0,96,196,1|9ry4zz,96,196,1|9ry500,92,194,0|a1b6bz,92,194,0|a1b6c0,96,196,1|aao7nz,96,196,1|aao7o0,92,194,0|ak18zz,92,194,0|ak1900,96,196,1|atr8zz,96,196,1|atr900,92,194,0|b34abz,92,194,0|b34ac0,92,194,1|bchefz,92,194,1|bcheg0,105,209,0|bi8t3z,105,209,0|bi8t40,92,194,0|bluczz,92,194,0|blud00,96,196,1|bv7ebz,96,196,1|bv7ec0,92,194,0|c4kfnz,92,194,0|c4kfo0,96,196,1|cdxgzz,96,196,1|cdxh00,92,194,0|cnaibz,92,194,0|cnaic0,96,196,1|cwnjnz,96,196,1|cwnjo0,92,194,0|d60kzz,92,194,0|d60l00,96,196,1|dfdmbz,96,196,1|dfdmc0,92,194,0|dp3mbz,92,194,0|dp3mc0,96,196,1|dzwibz,96,196,1|dzwic0,92,194,0|e7tozz,92,194,0|e7tp00,96,196,1|eimkzz,96,196,1|eiml00,92,194,0|eqjrnz,92,194,0|eqjro0,96,196,1|f1cnnz,96,196,1|f1cno0,92,194,0|f99ubz,92,194,0|f99uc0,92,194,1|fkfrrz,92,194,1|fkfrs0,105,209,0|frzzrz,105,209,0|frzzs0,92,194,1|g35ufz,92,194,1|g35ug0,105,209,0|gaq2fz,105,209,0|gaq2g0,92,194,1|glvx3z,92,194,1|glvx40,105,209,0|gtt3rz,105,209,0|gtt3s0,92,194,1|h4lzrz,92,194,1|h4lzs0,105,209,0|hcj6fz,105,209,0|hcj6g0,92,194,1|hnc2fz,92,194,1|hnc2g0,105,209,0|hv993z,105,209,0|hv9940,92,194,1|i6f3rz,92,194,1|i6f3s0,92,194,0\",\"Asia/Baghdad|,0,213,0|-15r1hk4,53,214,0|-r50g81,53,214,0|-r50g80,100,6,0|6fmnnz,100,6,0|6fmno0,105,209,1|6nhwvz,105,209,1|6nhww0,100,6,0|6wt6bz,100,6,0|6wt6c0,105,209,1|769u7z,105,209,1|769u80,100,6,0|7foszz,100,6,0|7fot00,105,209,1|7p3m7z,105,209,1|7p3m80,100,6,0|7ygqbz,100,6,0|7ygqc0,105,209,1|87rzrz,105,209,1|87rzs0,100,6,0|8h513z,100,6,0|8h5140,105,209,1|8qi2fz,105,209,1|8qi2g0,100,6,0|8zv3rz,100,6,0|8zv3s0,105,209,1|99853z,105,209,1|998540,100,6,0|9il6fz,100,6,0|9il6g0,105,209,1|9ry7rz,105,209,1|9ry7s0,100,6,0|a1b93z,100,6,0|a1b940,105,209,1|aaoafz,105,209,1|aaoag0,100,6,0|ak1brz,100,6,0|ak1bs0,105,209,1|atrbrz,105,209,1|atrbs0,100,6,0|b36dbz,100,6,0|b36dc0,105,209,1|bcl9bz,105,209,1|bcl9c0,100,6,0|bm05bz,100,6,0|bm05c0,105,209,1|bvf1bz,105,209,1|bvf1c0,100,6,0|c4s2nz,100,6,0|c4s2o0,105,209,1|ce6ynz,105,209,1|ce6yo0,100,6,0|cnjzzz,100,6,0|cnk000,105,209,1|cwyvzz,105,209,1|cwyw00,100,6,0|d6bxbz,100,6,0|d6bxc0,105,209,1|dfqtbz,105,209,1|dfqtc0,100,6,0|dp5pbz,100,6,0|dp5pc0,105,209,1|dyklbz,105,209,1|dyklc0,100,6,0|e7xmnz,100,6,0|e7xmo0,105,209,1|ehcinz,105,209,1|ehcio0,100,6,0|eqpjzz,100,6,0|eqpk00,105,209,1|f04fzz,105,209,1|f04g00,100,6,0|f9hhbz,100,6,0|f9hhc0,105,209,1|fiwdbz,105,209,1|fiwdc0,100,6,0|fsb9bz,100,6,0|fsb9c0,105,209,1|g1q5bz,105,209,1|g1q5c0,100,6,0|gb36nz,100,6,0|gb36o0,105,209,1|gki2nz,105,209,1|gki2o0,100,6,0|gtv3zz,100,6,0|gtv400,105,209,1|h39zzz,105,209,1|h3a000,100,6,0|hcn1bz,100,6,0|hcn1c0,105,209,1|hm1xbz,105,209,1|hm1xc0,100,6,0|hvgtbz,100,6,0|hvgtc0,105,209,1|i4vpbz,105,209,1|i4vpc0,100,6,0|ie8qnz,100,6,0|ie8qo0,105,209,1|innmnz,105,209,1|innmo0,100,6,0|ix0nzz,100,6,0|ix0o00,105,209,1|j6fjzz,105,209,1|j6fk00,100,6,0|jfslbz,100,6,0|jfslc0,105,209,1|jp7hbz,105,209,1|jp7hc0,100,6,0\",\"Asia/Bahrain|,0,215,0|-q3gmvk,105,209,0|19d0vz,105,209,0|19d0w0,100,6,0\",\"Asia/Baku|,0,216,0|-nu158c,100,6,0|-6p7kc1,100,6,0|-6p7kc0,105,209,0|5vb3jz,105,209,0|5vb3k0,92,194,1|64pwrz,92,194,1|64pws0,105,209,0|6e30vz,105,209,0|6e30w0,92,194,1|6nhu3z,92,194,1|6nhu40,105,209,0|6wuy7z,105,209,0|6wuy80,92,194,1|769rfz,92,194,1|769rg0,105,209,0|7foq7z,105,209,0|7foq80,92,194,1|7p1x3z,92,194,1|7p1x40,105,209,0|7yeyfz,105,209,0|7yeyg0,92,194,1|87rzrz,92,194,1|87rzs0,105,209,0|8h513z,105,209,0|8h5140,92,194,1|8qi2fz,92,194,1|8qi2g0,105,209,0|8zv3rz,105,209,0|8zv3s0,92,194,1|99853z,92,194,1|998540,105,209,0|9il6fz,105,209,0|9il6g0,92,194,1|9ry7rz,92,194,1|9ry7s0,105,209,0|a1b93z,105,209,0|a1b940,92,194,1|aaoafz,92,194,1|aaoag0,105,209,0|ak1brz,105,209,0|ak1bs0,92,194,1|atrbrz,92,194,1|atrbs0,105,209,0|b34d3z,105,209,0|b34d40,105,209,1|bchh7z,105,209,1|bchh80,100,6,0|bluijz,100,6,0|bluik0,105,209,1|bv7jvz,105,209,1|bv7jw0,105,209,0|dp3xfz,105,209,0|dp3xg0,92,194,1|dzwtfz,92,194,1|dzwtg0,105,209,0|e7txbz,105,209,0|e7txc0,92,194,1|eimtbz,92,194,1|eimtc0,105,209,0|eqjzzz,105,209,0|eqk000,92,194,1|f1cvzz,92,194,1|f1cw00,105,209,0|f9a2nz,105,209,0|f9a2o0,92,194,1|fkfxbz,92,194,1|fkfxc0,105,209,0|fs05bz,105,209,0|fs05c0,92,194,1|g35zzz,92,194,1|g36000,105,209,0|gaq7zz,105,209,0|gaq800,92,194,1|glw2nz,92,194,1|glw2o0,105,209,0|gtt9bz,105,209,0|gtt9c0,92,194,1|h4m5bz,92,194,1|h4m5c0,105,209,0|hcjbzz,105,209,0|hcjc00,92,194,1|hnc7zz,92,194,1|hnc800,105,209,0|hv9enz,105,209,0|hv9eo0,92,194,1|i6f9bz,92,194,1|i6f9c0,105,209,0|idzhbz,105,209,0|idzhc0,92,194,1|ip5bzz,92,194,1|ip5c00,105,209,0|iwpjzz,105,209,0|iwpk00,92,194,1|j7venz,92,194,1|j7veo0,105,209,0|jffmnz,105,209,0|jffmo0,92,194,1|jqlhbz,92,194,1|jqlhc0,105,209,0|jyinzz,105,209,0|jyio00,92,194,1|k9bjzz,92,194,1|k9bk00,105,209,0|kh8qnz,105,209,0|kh8qo0,92,194,1|ks1mnz,92,194,1|ks1mo0,105,209,0|kzytbz,105,209,0|kzytc0,92,194,1|lb4nzz,92,194,1|lb4o00,105,209,0|liovzz,105,209,0|liow00,92,194,1|ltuqnz,92,194,1|ltuqo0,105,209,0|m1eynz,105,209,0|m1eyo0,92,194,1|mcktbz,92,194,1|mcktc0,105,209,0|mkhzzz,105,209,0|mki000,92,194,1|mvavzz,92,194,1|mvaw00,105,209,0|n382nz,105,209,0|n382o0,92,194,1|ne0ynz,92,194,1|ne0yo0,105,209,0|nly5bz,105,209,0|nly5c0,92,194,1|nwr1bz,92,194,1|nwr1c0,105,209,0\",\"Asia/Bangkok|,0,217,0|-1ayyla4,53,217,0|-pysda5,53,217,0|-pysda4,91,193,0\",\"Asia/Barnaul|,0,218,0|-q4ljic,96,196,0|-kmrco1,96,196,0|-kmrco0,91,193,0|5vav7z,91,193,0|5vav80,89,191,1|64pofz,89,191,1|64pog0,91,193,0|6e2sjz,91,193,0|6e2sk0,89,191,1|6nhlrz,89,191,1|6nhls0,91,193,0|6wupvz,91,193,0|6wupw0,89,191,1|769j3z,89,191,1|769j40,91,193,0|7fohvz,91,193,0|7fohw0,89,191,1|7p1orz,89,191,1|7p1os0,91,193,0|7yeq3z,91,193,0|7yeq40,89,191,1|87rrfz,89,191,1|87rrg0,91,193,0|8h4srz,91,193,0|8h4ss0,89,191,1|8qhu3z,89,191,1|8qhu40,91,193,0|8zuvfz,91,193,0|8zuvg0,89,191,1|997wrz,89,191,1|997ws0,91,193,0|9iky3z,91,193,0|9iky40,89,191,1|9rxzfz,89,191,1|9rxzg0,91,193,0|a1b0rz,91,193,0|a1b0s0,89,191,1|aao23z,89,191,1|aao240,91,193,0|ak13fz,91,193,0|ak13g0,89,191,1|atr3fz,89,191,1|atr3g0,91,193,0|b344rz,91,193,0|b344s0,91,193,1|bch8vz,91,193,1|bch8w0,96,196,0|bi8njz,96,196,0|bi8nk0,91,193,0|blu7fz,91,193,0|blu7g0,89,191,1|bv78rz,89,191,1|bv78s0,91,193,0|c4ka3z,91,193,0|c4ka40,89,191,1|cdxbfz,89,191,1|cdxbg0,91,193,0|cnacrz,91,193,0|cnacs0,89,191,1|cwne3z,89,191,1|cwne40,91,193,0|d60ffz,91,193,0|d60fg0,89,191,1|d98v3z,89,191,1|d98v40,91,193,1|dfdjjz,91,193,1|dfdjk0,96,196,0|dp3jjz,96,196,0|dp3jk0,91,193,1|dzwfjz,91,193,1|dzwfk0,96,196,0|e7tm7z,96,196,0|e7tm80,91,193,1|eimi7z,91,193,1|eimi80,96,196,0|eqjovz,96,196,0|eqjow0,91,193,1|f1ckvz,91,193,1|f1ckw0,96,196,0|f99rjz,96,196,0|f99rk0,91,193,1|fkfm7z,91,193,1|fkfm80,96,196,0|frzu7z,96,196,0|frzu80,91,193,1|g35ovz,91,193,1|g35ow0,96,196,0|gapwvz,96,196,0|gapww0,91,193,1|glvrjz,91,193,1|glvrk0,96,196,0|gtsy7z,96,196,0|gtsy80,91,193,1|h4lu7z,91,193,1|h4lu80,96,196,0|hcj0vz,96,196,0|hcj0w0,91,193,1|hnbwvz,91,193,1|hnbww0,96,196,0|hv93jz,96,196,0|hv93k0,91,193,1|i6ey7z,91,193,1|i6ey80,96,196,0|idz67z,96,196,0|idz680,91,193,1|ip50vz,91,193,1|ip50w0,96,196,0|iwp8vz,96,196,0|iwp8w0,91,193,1|j7v3jz,91,193,1|j7v3k0,96,196,0|jffbjz,96,196,0|jffbk0,91,193,1|jql67z,91,193,1|jql680,96,196,0|jyicvz,96,196,0|jyicw0,91,193,1|k9b8vz,91,193,1|k9b8w0,96,196,0|kh8fjz,96,196,0|kh8fk0,91,193,1|ks1bjz,91,193,1|ks1bk0,96,196,0|kzyi7z,96,196,0|kzyi80,91,193,1|lb4cvz,91,193,1|lb4cw0,96,196,0|liokvz,96,196,0|liokw0,91,193,0|ne0krz,91,193,0|ne0ks0,96,196,0|o4nwvz,96,196,0|o4nww0,91,193,0\",\"Asia/Beirut|,0,219,0|-1ayy98o,15,11,0|-pyzew1,15,11,0|-pyzew0,16,6,1|-po4r01,16,6,1|-po4r00,15,11,0|-pfwdk1,15,11,0|-pfwdk0,16,6,1|-p6hkc1,16,6,1|-p6hkc0,15,11,0|-oxj9k1,15,11,0|-oxj9k0,16,6,1|-ongdo1,16,6,1|-ongdo0,15,11,0|-oddc81,15,11,0|-oddc80,16,6,1|-o5t701,16,6,1|-o5t700,15,11,0|-6m2iw1,15,11,0|-6m2iw0,16,6,1|-6e79o1,16,6,1|-6e79o0,15,11,0|-63alk1,15,11,0|-63alk0,16,6,1|-5vfcc1,16,6,1|-5vfcc0,15,11,0|-5kio81,15,11,0|-5kio80,16,6,1|-5cnf01,16,6,1|-5cnf00,15,11,0|-51ow81,15,11,0|-51ow80,16,6,1|-4ttn01,16,6,1|-4ttn00,15,11,0|-4iwyw1,15,11,0|-4iwyw0,16,6,1|-4b1po1,16,6,1|-4b1po0,15,11,0|1ag2fz,15,11,0|1ag2g0,16,6,1|1fn0zz,16,6,1|1fn100,15,11,0|1qjp3z,15,11,0|1qjp40,16,6,1|1yeybz,16,6,1|1yeyc0,15,11,0|29bmfz,15,11,0|29bmg0,16,6,1|2h6vnz,16,6,1|2h6vo0,15,11,0|2s3jrz,15,11,0|2s3js0,16,6,1|2zyszz,16,6,1|2zyt00,15,11,0|3axbrz,15,11,0|3axbs0,16,6,1|3iskzz,16,6,1|3isl00,15,11,0|3tp93z,15,11,0|3tp940,16,6,1|41kibz,16,6,1|41kic0,15,11,0|4cfbrz,15,11,0|4cfbs0,16,6,1|4kakzz,16,6,1|4kal00,15,11,0|7h8frz,15,11,0|7h8fs0,16,6,1|7pvgzz,16,6,1|7pvh00,15,11,0|800d3z,15,11,0|800d40,16,6,1|88nebz,16,6,1|88nec0,15,11,0|8isafz,15,11,0|8isag0,16,6,1|8rfbnz,16,6,1|8rfbo0,15,11,0|91k7rz,15,11,0|91k7s0,16,6,1|9a78zz,16,6,1|9a7900,15,11,0|9lzefz,15,11,0|9lzeg0,16,6,1|9t10zz,16,6,1|9t1100,15,11,0|a3ml3z,15,11,0|a3ml40,16,6,1|absybz,16,6,1|absyc0,15,11,0|alxufz,15,11,0|alxug0,16,6,1|aukvnz,16,6,1|aukvo0,15,11,0|b4prrz,15,11,0|b4prs0,16,6,1|bdcszz,16,6,1|bdct00,15,11,0|bnjjrz,15,11,0|bnjjs0,16,6,1|bvkczz,16,6,1|bvkd00,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxgzz,16,6,1|cdxh00,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cwnjnz,16,6,1|cwnjo0,15,11,0|d60nrz,15,11,0|d60ns0,16,6,1|dfdmbz,16,6,1|dfdmc0,15,11,0|dp3p3z,15,11,0|dp3p40,16,6,1|dygnnz,16,6,1|dygno0,15,11,0|e7trrz,15,11,0|e7trs0,16,6,1|eh6qbz,16,6,1|eh6qc0,15,11,0|eqjufz,15,11,0|eqjug0,16,6,1|ezwszz,16,6,1|ezwt00,15,11,0|f99x3z,15,11,0|f99x40,16,6,1|fkfozz,16,6,1|fkfp00,15,11,0|frzzrz,15,11,0|frzzs0,16,6,1|g35rnz,16,6,1|g35ro0,15,11,0|gaq2fz,15,11,0|gaq2g0,16,6,1|glvubz,16,6,1|glvuc0,15,11,0|gtt3rz,15,11,0|gtt3s0,16,6,1|h4lwzz,16,6,1|h4lx00,15,11,0|hcj6fz,15,11,0|hcj6g0,16,6,1|hnbznz,16,6,1|hnbzo0,15,11,0|hv993z,15,11,0|hv9940,16,6,1|i6f0zz,16,6,1|i6f100,15,11,0|idzbrz,15,11,0|idzbs0,16,6,1|ip53nz,16,6,1|ip53o0,15,11,0|iwpefz,15,11,0|iwpeg0,16,6,1|j7v6bz,16,6,1|j7v6c0,15,11,0|jffh3z,15,11,0|jffh40,16,6,1|jql8zz,16,6,1|jql900,15,11,0|jyiifz,15,11,0|jyiig0,16,6,1|k9bbnz,16,6,1|k9bbo0,15,11,0|kh8l3z,15,11,0|kh8l40,16,6,1|ks1ebz,16,6,1|ks1ec0,15,11,0|kzynrz,15,11,0|kzyns0,16,6,1|lb4fnz,16,6,1|lb4fo0,15,11,0|lioqfz,15,11,0|lioqg0,16,6,1|ltuibz,16,6,1|ltuic0,15,11,0|m1et3z,15,11,0|m1et40,16,6,1|mckkzz,16,6,1|mckl00,15,11,0|mkhufz,15,11,0|mkhug0,16,6,1|mvannz,16,6,1|mvano0,15,11,0|n37x3z,15,11,0|n37x40,16,6,1|ne0qbz,16,6,1|ne0qc0,15,11,0|nlxzrz,15,11,0|nlxzs0,16,6,1|nwqszz,16,6,1|nwqt00,15,11,0|o4o2fz,15,11,0|o4o2g0,16,6,1|oftubz,16,6,1|oftuc0,15,11,0|one53z,15,11,0|one540,16,6,1|oyjwzz,16,6,1|oyjx00,15,11,0|p647rz,15,11,0|p647s0,16,6,1|ph9znz,16,6,1|ph9zo0,15,11,0|pp793z,15,11,0|pp7940,16,6,1|q002bz,16,6,1|q002c0,15,11,0|q7xbrz,15,11,0|q7xbs0,16,6,1|qiq4zz,16,6,1|qiq500,15,11,0|qqnefz,15,11,0|qqneg0,16,6,1|r1t6bz,16,6,1|r1t6c0,15,11,0|r9dh3z,15,11,0|r9dh40,16,6,1|rkj8zz,16,6,1|rkj900,15,11,0|rs3jrz,15,11,0|rs3js0,16,6,1|s39bnz,16,6,1|s39bo0,15,11,0|sb6l3z,15,11,0|sb6l40,16,6,1|slzebz,16,6,1|slzec0,15,11,0|stwnrz,15,11,0|stwns0,16,6,1|t4pgzz,16,6,1|t4ph00,15,11,0|tcmqfz,15,11,0|tcmqg0,16,6,1|tnfjnz,16,6,1|tnfjo0,15,11,0|tvct3z,15,11,0|tvct40,16,6,1|u6ikzz,16,6,1|u6il00,15,11,0|ue2vrz,15,11,0|ue2vs0,16,6,1|up8nnz,16,6,1|up8no0,15,11,0|uwsyfz,15,11,0|uwsyg0,16,6,1|v7yqbz,16,6,1|v7yqc0,15,11,0|vfvzrz,15,11,0|vfvzs0,16,6,1|vqoszz,16,6,1|vqot00,15,11,0|vym2fz,15,11,0|vym2g0,16,6,1|w9evnz,16,6,1|w9evo0,15,11,0|whc53z,15,11,0|whc540,16,6,1|wshwzz,16,6,1|wshx00,15,11,0|x027rz,15,11,0|x027s0,16,6,1|xb7znz,16,6,1|xb7zo0,15,11,0|xisafz,15,11,0|xisag0,16,6,1|xty2bz,16,6,1|xty2c0,15,11,0|y1id3z,15,11,0|y1id40,16,6,1|yco4zz,16,6,1|yco500,15,11,0|yklefz,15,11,0|ykleg0,16,6,1|yve7nz,16,6,1|yve7o0,15,11,0|z3bh3z,15,11,0|z3bh40,16,6,1|ze4abz,16,6,1|ze4ac0,15,11,0\",\"Asia/Bishkek|,0,220,0|-nu19tc,92,194,0|-kmr9w1,92,194,0|-kmr9w0,96,196,0|5vaxzz,96,196,0|5vay00,91,193,1|64pr7z,91,193,1|64pr80,96,196,0|6e2vbz,96,196,0|6e2vc0,91,193,1|6nhojz,91,193,1|6nhok0,96,196,0|6wusnz,96,196,0|6wuso0,91,193,1|769lvz,91,193,1|769lw0,96,196,0|7foknz,96,196,0|7foko0,91,193,1|7p1rjz,91,193,1|7p1rk0,96,196,0|7yesvz,96,196,0|7yesw0,91,193,1|87ru7z,91,193,1|87ru80,96,196,0|8h4vjz,96,196,0|8h4vk0,91,193,1|8qhwvz,91,193,1|8qhww0,96,196,0|8zuy7z,96,196,0|8zuy80,91,193,1|997zjz,91,193,1|997zk0,96,196,0|9il0vz,96,196,0|9il0w0,91,193,1|9ry27z,91,193,1|9ry280,96,196,0|a1b3jz,96,196,0|a1b3k0,91,193,1|aao4vz,91,193,1|aao4w0,96,196,0|ak167z,96,196,0|ak1680,91,193,1|atr67z,91,193,1|atr680,96,196,0|b347jz,96,196,0|b347k0,96,196,1|bazjjz,96,196,1|bazjk0,92,194,0|bmk4rz,92,194,0|bmk4s0,96,196,1|bv75zz,96,196,1|bv7600,92,194,0|c5a7fz,92,194,0|c5a7g0,96,196,1|cdx8nz,96,196,1|cdx8o0,92,194,0|co0a3z,92,194,0|co0a40,96,196,1|cwnbbz,96,196,1|cwnbc0,92,194,0|d6qcrz,92,194,0|d6qcs0,96,196,1|dfddzz,96,196,1|dfde00,92,194,0|dpgffz,92,194,0|dpgfg0,96,196,1|dygfbz,96,196,1|dygfc0,92,194,0|e7tqdz,92,194,0|e7tqe0,96,196,1|eimjlz,96,196,1|eimjm0,92,194,0|eqjt1z,92,194,0|eqjt20,96,196,1|f1cm9z,96,196,1|f1cma0,92,194,0|f99vpz,92,194,0|f99vq0,96,196,1|fkfnlz,96,196,1|fkfnm0,92,194,0|frzydz,92,194,0|frzye0,96,196,1|g35q9z,96,196,1|g35qa0,92,194,0|gaq11z,92,194,0|gaq120,96,196,1|glvsxz,96,196,1|glvsy0,92,194,0|gtt2dz,92,194,0|gtt2e0,96,196,1|h4lvlz,96,196,1|h4lvm0,92,194,0|hcj51z,92,194,0|hcj520,96,196,1|hnby9z,96,196,1|hnbya0,92,194,0|hv97pz,92,194,0|hv97q0,96,196,1|i6ezlz,96,196,1|i6ezm0,92,194,0|idzadz,92,194,0|idzae0,96,196,1|il2knz,96,196,1|il2ko0,96,196,0\",\"Asia/Brunei|,0,221,0|-mvofy4,106,222,0|-jb6i61,106,222,0|-jb6i60,89,191,0\",\"Asia/Chita|,0,223,0|-q4cfog,89,191,0|-kmri81,89,191,0|-kmri80,107,224,0|5vapnz,107,224,0|5vapo0,93,195,1|64pivz,93,195,1|64piw0,107,224,0|6e2mzz,107,224,0|6e2n00,93,195,1|6nhg7z,93,195,1|6nhg80,107,224,0|6wukbz,107,224,0|6wukc0,93,195,1|769djz,93,195,1|769dk0,107,224,0|7focbz,107,224,0|7focc0,93,195,1|7p1j7z,93,195,1|7p1j80,107,224,0|7yekjz,107,224,0|7yekk0,93,195,1|87rlvz,93,195,1|87rlw0,107,224,0|8h4n7z,107,224,0|8h4n80,93,195,1|8qhojz,93,195,1|8qhok0,107,224,0|8zupvz,107,224,0|8zupw0,93,195,1|997r7z,93,195,1|997r80,107,224,0|9iksjz,107,224,0|9iksk0,93,195,1|9rxtvz,93,195,1|9rxtw0,107,224,0|a1av7z,107,224,0|a1av80,93,195,1|aanwjz,93,195,1|aanwk0,107,224,0|ak0xvz,107,224,0|ak0xw0,93,195,1|atqxvz,93,195,1|atqxw0,107,224,0|b33z7z,107,224,0|b33z80,107,224,1|bch3bz,107,224,1|bch3c0,89,191,0|bi8hzz,89,191,0|bi8i00,107,224,0|blu1vz,107,224,0|blu1w0,93,195,1|bv737z,93,195,1|bv7380,107,224,0|c4k4jz,107,224,0|c4k4k0,93,195,1|cdx5vz,93,195,1|cdx5w0,107,224,0|cna77z,107,224,0|cna780,93,195,1|cwn8jz,93,195,1|cwn8k0,107,224,0|d609vz,107,224,0|d609w0,93,195,1|dfdb7z,93,195,1|dfdb80,107,224,0|dp3b7z,107,224,0|dp3b80,93,195,1|dzw77z,93,195,1|dzw780,107,224,0|e7tdvz,107,224,0|e7tdw0,93,195,1|eim9vz,93,195,1|eim9w0,107,224,0|eqjgjz,107,224,0|eqjgk0,93,195,1|f1ccjz,93,195,1|f1cck0,107,224,0|f99j7z,107,224,0|f99j80,93,195,1|fkfdvz,93,195,1|fkfdw0,107,224,0|frzlvz,107,224,0|frzlw0,93,195,1|g35gjz,93,195,1|g35gk0,107,224,0|gapojz,107,224,0|gapok0,93,195,1|glvj7z,93,195,1|glvj80,107,224,0|gtspvz,107,224,0|gtspw0,93,195,1|h4llvz,93,195,1|h4llw0,107,224,0|hcisjz,107,224,0|hcisk0,93,195,1|hnbojz,93,195,1|hnbok0,107,224,0|hv8v7z,107,224,0|hv8v80,93,195,1|i6epvz,93,195,1|i6epw0,107,224,0|idyxvz,107,224,0|idyxw0,93,195,1|ip4sjz,93,195,1|ip4sk0,107,224,0|iwp0jz,107,224,0|iwp0k0,93,195,1|j7uv7z,93,195,1|j7uv80,107,224,0|jff37z,107,224,0|jff380,93,195,1|jqkxvz,93,195,1|jqkxw0,107,224,0|jyi4jz,107,224,0|jyi4k0,93,195,1|k9b0jz,93,195,1|k9b0k0,107,224,0|kh877z,107,224,0|kh8780,93,195,1|ks137z,93,195,1|ks1380,107,224,0|kzy9vz,107,224,0|kzy9w0,93,195,1|lb44jz,93,195,1|lb44k0,107,224,0|liocjz,107,224,0|liock0,93,195,0|ne0cfz,93,195,0|ne0cg0,89,191,0|o4nrbz,89,191,0|o4nrc0,107,224,0\",\"Asia/Choibalsan|,0,225,0|-xmct7c,91,193,0|46akjz,91,193,0|46akk0,89,191,0|6wun3z,89,191,0|6wun40,93,195,1|769djz,93,195,1|769dk0,107,224,0|7focbz,107,224,0|7focc0,93,195,1|7p1avz,93,195,1|7p1aw0,107,224,0|7yeezz,107,224,0|7yef00,93,195,1|87rdjz,93,195,1|87rdk0,107,224,0|8h4hnz,107,224,0|8h4ho0,93,195,1|8qhg7z,93,195,1|8qhg80,107,224,0|8zukbz,107,224,0|8zukc0,93,195,1|997ivz,93,195,1|997iw0,107,224,0|9ikmzz,107,224,0|9ikn00,93,195,1|9rxljz,93,195,1|9rxlk0,107,224,0|a1apnz,107,224,0|a1apo0,93,195,1|aano7z,93,195,1|aano80,107,224,0|ak0sbz,107,224,0|ak0sc0,93,195,1|atqpjz,93,195,1|atqpk0,107,224,0|b33tnz,107,224,0|b33to0,93,195,1|bcgs7z,93,195,1|bcgs80,107,224,0|bltwbz,107,224,0|bltwc0,93,195,1|bv6uvz,93,195,1|bv6uw0,107,224,0|c4jyzz,107,224,0|c4jz00,93,195,1|cdwxjz,93,195,1|cdwxk0,107,224,0|cna1nz,107,224,0|cna1o0,93,195,1|cwn07z,93,195,1|cwn080,107,224,0|d604bz,107,224,0|d604c0,93,195,1|dfd2vz,93,195,1|dfd2w0,107,224,0|dp35nz,107,224,0|dp35o0,93,195,1|dyg47z,93,195,1|dyg480,107,224,0|e7t8bz,107,224,0|e7t8c0,93,195,1|eh66vz,93,195,1|eh66w0,107,224,0|eqjazz,107,224,0|eqjb00,93,195,1|ezw9jz,93,195,1|ezw9k0,107,224,0|gcgn7z,107,224,0|gcgn80,93,195,1|gkdr3z,93,195,1|gkdr40,107,224,0|gtqv7z,107,224,0|gtqv80,93,195,1|h33trz,93,195,1|h33ts0,107,224,0|hcgxvz,107,224,0|hcgxw0,93,195,1|hltwfz,93,195,1|hltwg0,107,224,0|hv70jz,107,224,0|hv70k0,93,195,1|i4jz3z,93,195,1|i4jz40,107,224,0|idx37z,107,224,0|idx380,93,195,1|ina1rz,93,195,1|ina1s0,107,224,0|iwn5vz,107,224,0|iwn5w0,93,195,1|j6d33z,93,195,1|j6d340,107,224,0|jyjtnz,107,224,0|jyjto0,89,191,0|nlvtzz,89,191,0|nlvu00,107,224,1|nv8mzz,107,224,1|nv8n00,89,191,0|o4lwnz,89,191,0|o4lwo0,107,224,1|odypnz,107,224,1|odypo0,89,191,0\",\"Asia/Colombo|,0,226,0|-1ayyhgc,21,227,0|-xehasl,21,227,0|-xehask,108,228,0|-elvwm1,108,228,0|-elvwm0,96,196,1|-e9lco1,96,196,1|-e9lco0,109,229,1|-cmw9u1,109,229,1|-cmw9u0,108,228,0|drxa1z,108,228,0|drxa20,109,229,0|dzufbz,109,229,0|dzufc0,96,196,0|ixq61z,96,196,0|ixq620,108,228,0\",\"Asia/Damascus|,0,230,0|-q3gk20,15,11,0|-pxwdc1,15,11,0|-pxwdc0,16,6,1|-pp9c41,16,6,1|-pp9c40,15,11,0|-pf6ao1,15,11,0|-pf6ao0,16,6,1|-p6j9g1,16,6,1|-p6j9g0,15,11,0|-owg801,15,11,0|-owg800,16,6,1|-ont6s1,16,6,1|-ont6s0,15,11,0|-odq5c1,15,11,0|-odq5c0,16,6,1|-o4q5g1,16,6,1|-o4q5g0,15,11,0|-408lc1,15,11,0|-408lc0,16,6,1|-3s9ms1,16,6,1|-3s9ms0,15,11,0|-3hcyo1,15,11,0|-3hcyo0,16,6,1|-39jk41,16,6,1|-39jk40,15,11,0|-2yj6o1,15,11,0|-2yj6o0,16,6,1|-2qnxg1,16,6,1|-2qnxg0,15,11,0|-2fr9c1,15,11,0|-2fr9c0,16,6,1|-27xus1,16,6,1|-27xus0,15,11,0|-1xcao1,15,11,0|-1xcao0,16,6,1|-1p42s1,16,6,1|-1p42s0,15,11,0|-1e7eo1,15,11,0|-1e7eo0,16,6,1|-16c5g1,16,6,1|-16c5g0,15,11,0|-vdmo1,15,11,0|-vdmo0,16,6,1|-nidg1,16,6,1|-nidg0,15,11,0|-clpc1,15,11,0|-clpc0,16,6,1|-4qg41,16,6,1|-4qg40,15,11,0|667zz,15,11,0|66800,16,6,1|e1h7z,16,6,1|e1h80,15,11,0|oy5bz,15,11,0|oy5c0,16,6,1|wtejz,16,6,1|wtek0,15,11,0|17rxbz,15,11,0|17rxc0,16,6,1|1fn6jz,16,6,1|1fn6k0,15,11,0|1qjunz,15,11,0|1qjuo0,16,6,1|1yf3vz,16,6,1|1yf3w0,15,11,0|29brzz,15,11,0|29bs00,16,6,1|2h717z,16,6,1|2h7180,15,11,0|2s3pbz,15,11,0|2s3pc0,16,6,1|2zyyjz,16,6,1|2zyyk0,15,11,0|3axhbz,15,11,0|3axhc0,16,6,1|3isqjz,16,6,1|3isqk0,15,11,0|3tpenz,15,11,0|3tpeo0,16,6,1|4013vz,16,6,1|4013w0,15,11,0|4chbzz,15,11,0|4chc00,16,6,1|4it17z,16,6,1|4it180,15,11,0|6xa2nz,15,11,0|6xa2o0,16,6,1|76a2jz,16,6,1|76a2k0,15,11,0|7g3unz,15,11,0|7g3uo0,16,6,1|7p3ujz,16,6,1|7p3uk0,15,11,0|8ezenz,15,11,0|8ezeo0,16,6,1|8r2ijz,16,6,1|8r2ik0,15,11,0|8yfenz,15,11,0|8yfeo0,16,6,1|9az6jz,16,6,1|9az6k0,15,11,0|9hz3zz,15,11,0|9hz400,16,6,1|9tsyjz,16,6,1|9tsyk0,15,11,0|a1knzz,15,11,0|a1ko00,16,6,1|ab1bvz,16,6,1|ab1bw0,15,11,0|akefzz,15,11,0|akeg00,16,6,1|atrejz,16,6,1|atrek0,15,11,0|b367rz,15,11,0|b367s0,16,6,1|bcl0zz,16,6,1|bcl100,15,11,0|bmcyfz,15,11,0|bmcyg0,16,6,1|bveszz,16,6,1|bvet00,15,11,0|c4gt3z,15,11,0|c4gt40,16,6,1|cdvmbz,16,6,1|cdvmc0,15,11,0|cnjufz,15,11,0|cnjug0,16,6,1|cwynnz,16,6,1|cwyno0,15,11,0|d6brrz,15,11,0|d6brs0,16,6,1|dfqkzz,16,6,1|dfql00,15,11,0|dp5jrz,15,11,0|dp5js0,16,6,1|dykczz,16,6,1|dykd00,15,11,0|e7vmfz,15,11,0|e7vmg0,16,6,1|ehcabz,16,6,1|ehcac0,15,11,0|eqlp3z,15,11,0|eqlp40,16,6,1|f047nz,16,6,1|f047o0,15,11,0|f9hbrz,15,11,0|f9hbs0,16,6,1|fiw4zz,16,6,1|fiw500,15,11,0|fsb3rz,15,11,0|fsb3s0,16,6,1|g1pwzz,16,6,1|g1px00,15,11,0|gb313z,15,11,0|gb3140,16,6,1|gkhubz,16,6,1|gkhuc0,15,11,0|gtuyfz,15,11,0|gtuyg0,16,6,1|h39rnz,16,6,1|h39ro0,15,11,0|hcmvrz,15,11,0|hcmvs0,16,6,1|hm1ozz,16,6,1|hm1p00,15,11,0|hvgnrz,15,11,0|hvgns0,16,6,1|i4vgzz,16,6,1|i4vh00,15,11,0|ie8l3z,15,11,0|ie8l40,16,6,1|innebz,16,6,1|innec0,15,11,0|ix0ifz,15,11,0|ix0ig0,16,6,1|j5ynnz,16,6,1|j5yno0,15,11,0|jfoqfz,15,11,0|jfoqg0,16,6,1|jquibz,16,6,1|jquic0,15,11,0|jyrrrz,15,11,0|jyrrs0,16,6,1|k9mfnz,16,6,1|k9mfo0,15,11,0|kh4vrz,15,11,0|kh4vs0,16,6,1|ksannz,16,6,1|ksano0,15,11,0|l07x3z,15,11,0|l07x40,16,6,1|lb0qbz,16,6,1|lb0qc0,15,11,0|lixzrz,15,11,0|lixzs0,16,6,1|ltqszz,16,6,1|ltqt00,15,11,0|m1o2fz,15,11,0|m1o2g0,16,6,1|mcgvnz,16,6,1|mcgvo0,15,11,0|mke53z,15,11,0|mke540,16,6,1|mv6ybz,16,6,1|mv6yc0,15,11,0|n347rz,15,11,0|n347s0,16,6,1|ne9znz,16,6,1|ne9zo0,15,11,0|nluafz,15,11,0|nluag0,16,6,1|nx02bz,16,6,1|nx02c0,15,11,0|o4kd3z,15,11,0|o4kd40,16,6,1|ofq4zz,16,6,1|ofq500,15,11,0|onnefz,15,11,0|onneg0,16,6,1|oyg7nz,16,6,1|oyg7o0,15,11,0|p6dh3z,15,11,0|p6dh40,16,6,1|ph6abz,16,6,1|ph6ac0,15,11,0|pp3jrz,15,11,0|pp3js0,16,6,1|pzwczz,16,6,1|pzwd00,15,11,0|q7tmfz,15,11,0|q7tmg0,16,6,1|qizebz,16,6,1|qizec0,15,11,0|qqjp3z,15,11,0|qqjp40,16,6,1|r1pgzz,16,6,1|r1ph00,15,11,0|r99rrz,15,11,0|r99rs0,16,6,1|rkfjnz,16,6,1|rkfjo0,15,11,0|rsct3z,15,11,0|rsct40,16,6,1|s35mbz,16,6,1|s35mc0,15,11,0|sb2vrz,15,11,0|sb2vs0,16,6,1|slvozz,16,6,1|slvp00,15,11,0|stsyfz,15,11,0|stsyg0,16,6,1|t4yqbz,16,6,1|t4yqc0,15,11,0|tcj13z,15,11,0|tcj140,16,6,1|tnoszz,16,6,1|tnot00,15,11,0|tv93rz,15,11,0|tv93s0,16,6,1|u6evnz,16,6,1|u6evo0,15,11,0|uec53z,15,11,0|uec540,16,6,1|up4ybz,16,6,1|up4yc0,15,11,0|ux27rz,15,11,0|ux27s0,16,6,1|v7v0zz,16,6,1|v7v100,15,11,0|vfsafz,15,11,0|vfsag0,16,6,1|vql3nz,16,6,1|vql3o0,15,11,0|vyid3z,15,11,0|vyid40,16,6,1|w9o4zz,16,6,1|w9o500,15,11,0|wh8frz,15,11,0|wh8fs0,16,6,1|wse7nz,16,6,1|wse7o0,15,11,0|wzyifz,15,11,0|wzyig0,16,6,1|xb4abz,16,6,1|xb4ac0,15,11,0|xj1jrz,15,11,0|xj1js0,16,6,1|xtuczz,16,6,1|xtud00,15,11,0|y1rmfz,15,11,0|y1rmg0,16,6,1|yckfnz,16,6,1|yckfo0,15,11,0|ykhp3z,15,11,0|ykhp40,16,6,1|yvngzz,16,6,1|yvnh00,15,11,0|z37rrz,15,11,0|z37rs0,16,6,1|zedjnz,16,6,1|zedjo0,15,11,0\",\"Asia/Dhaka|,0,231,0|-15r1q2s,77,232,0|-eqtpox,77,232,0|-eqtpow,109,229,0|-ef78q1,109,229,0|-ef78q0,108,228,0|-e9lba1,108,228,0|-e9lba0,109,229,0|-9j0ne1,109,229,0|-9j0ne0,96,196,0|klhwjz,96,196,0|klhwk0,91,193,1|kvj0jz,91,193,1|kvj0k0,96,196,0\",\"Asia/Dili|,0,233,0|-u9s4l8,89,191,0|-ejfac1,89,191,0|-ejfac0,107,224,0|3b0hnz,107,224,0|3b0ho0,89,191,0|g0zlrz,89,191,0|g0zls0,107,224,0\",\"Asia/Dubai|,0,234,0|-q3gnko,105,209,0\",\"Asia/Dushanbe|,0,235,0|-nu18qo,92,194,0|-kmr9w1,92,194,0|-kmr9w0,96,196,0|5vaxzz,96,196,0|5vay00,91,193,1|64pr7z,91,193,1|64pr80,96,196,0|6e2vbz,96,196,0|6e2vc0,91,193,1|6nhojz,91,193,1|6nhok0,96,196,0|6wusnz,96,196,0|6wuso0,91,193,1|769lvz,91,193,1|769lw0,96,196,0|7foknz,96,196,0|7foko0,91,193,1|7p1rjz,91,193,1|7p1rk0,96,196,0|7yesvz,96,196,0|7yesw0,91,193,1|87ru7z,91,193,1|87ru80,96,196,0|8h4vjz,96,196,0|8h4vk0,91,193,1|8qhwvz,91,193,1|8qhww0,96,196,0|8zuy7z,96,196,0|8zuy80,91,193,1|997zjz,91,193,1|997zk0,96,196,0|9il0vz,96,196,0|9il0w0,91,193,1|9ry27z,91,193,1|9ry280,96,196,0|a1b3jz,96,196,0|a1b3k0,91,193,1|aao4vz,91,193,1|aao4w0,96,196,0|ak167z,96,196,0|ak1680,91,193,1|atr67z,91,193,1|atr680,96,196,0|b347jz,96,196,0|b347k0,96,196,1|bbgabz,96,196,1|bbgac0,92,194,0\",\"Asia/Famagusta|,0,236,0|-p4bqac,15,11,0|2r67rz,15,11,0|2r67s0,16,6,1|30j6bz,16,6,1|30j6c0,15,11,0|3bn93z,15,11,0|3bn940,16,6,1|3jb3nz,16,6,1|3jb3o0,15,11,0|3s9efz,15,11,0|3s9eg0,16,6,1|419ebz,16,6,1|419ec0,15,11,0|4azh3z,15,11,0|4azh40,16,6,1|4keabz,16,6,1|4keac0,15,11,0|4tpjrz,15,11,0|4tpjs0,16,6,1|532ibz,16,6,1|532ic0,15,11,0|5csl3z,15,11,0|5csl40,16,6,1|5lskzz,16,6,1|5lsl00,15,11,0|5v5p3z,15,11,0|5v5p40,16,6,1|64innz,16,6,1|64ino0,15,11,0|6dvrrz,15,11,0|6dvrs0,16,6,1|6n8qbz,16,6,1|6n8qc0,15,11,0|6wlufz,15,11,0|6wlug0,16,6,1|75yszz,16,6,1|75yt00,15,11,0|7fbx3z,15,11,0|7fbx40,16,6,1|7p1ubz,16,6,1|7p1uc0,15,11,0|7yeyfz,15,11,0|7yeyg0,16,6,1|87rwzz,16,6,1|87rx00,15,11,0|8h513z,15,11,0|8h5140,16,6,1|8qhznz,16,6,1|8qhzo0,15,11,0|8zv3rz,15,11,0|8zv3s0,16,6,1|9982bz,16,6,1|9982c0,15,11,0|9il6fz,15,11,0|9il6g0,16,6,1|9ry4zz,16,6,1|9ry500,15,11,0|a1b93z,15,11,0|a1b940,16,6,1|aao7nz,16,6,1|aao7o0,15,11,0|ak1brz,15,11,0|ak1bs0,16,6,1|atr8zz,16,6,1|atr900,15,11,0|b34d3z,15,11,0|b34d40,16,6,1|bchbnz,16,6,1|bchbo0,15,11,0|blufrz,15,11,0|blufs0,16,6,1|bv7ebz,16,6,1|bv7ec0,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxgzz,16,6,1|cdxh00,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cwnjnz,16,6,1|cwnjo0,15,11,0|d60nrz,15,11,0|d60ns0,16,6,1|dfdmbz,16,6,1|dfdmc0,15,11,0|dp3p3z,15,11,0|dp3p40,16,6,1|dygnnz,16,6,1|dygno0,15,11,0|e7trrz,15,11,0|e7trs0,16,6,1|eh6qbz,16,6,1|eh6qc0,15,11,0|eqjufz,15,11,0|eqjug0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|od5jnz,16,6,1|od5jo0,100,6,0|oyk83z,100,6,0|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Asia/Gaza|,0,237,0|-1054x1s,15,11,0|-ffv401,15,11,0|-ffv400,16,6,1|-f9l6o1,16,6,1|-f9l6o0,15,11,0|-f765c1,15,11,0|-f765c0,16,6,1|-e6fxc1,16,6,1|-e6fxc0,15,11,0|-dyoao1,15,11,0|-dyoao0,16,6,1|-dno001,16,6,1|-dno000,15,11,0|-dfuio1,15,11,0|-dfuio0,16,6,1|-d4u801,16,6,1|-d4u800,15,11,0|-cwatc1,15,11,0|-cwatc0,16,6,1|-cm2ao1,16,6,1|-cm2ao0,15,11,0|-cdiw01,15,11,0|-cdiw00,16,6,1|-c3adc1,16,6,1|-c3adc0,15,11,0|-6lluw1,15,11,0|-6lluw0,16,6,1|-6e79o1,16,6,1|-6e79o0,15,11,0|-63alk1,15,11,0|-63alk0,16,6,1|-5vfcc1,16,6,1|-5vfcc0,15,11,0|-5kilg1,15,11,0|-5kilg0,16,6,1|-5cp1c1,16,6,1|-5cp1c0,15,11,0|-51otg1,15,11,0|-51otg0,16,6,1|-4tv9c1,16,6,1|-4tv9c0,15,11,0|-4iww41,15,11,0|-4iww40,16,6,1|-4b3c01,16,6,1|-4b3c00,15,11,0|-404ys1,15,11,0|-404ys0,16,6,1|-3sbeo1,16,6,1|-3sbeo0,15,11,0|-3hd1g1,15,11,0|-3hd1g0,16,6,1|-39jhc1,16,6,1|-39jhc0,15,11,0|-2yj9g1,15,11,0|-2yj9g0,16,6,1|-2qppc1,16,6,1|-2qppc0,15,11,0|-2frc41,15,11,0|-2frc40,16,6,1|-27xs01,16,6,1|-27xs00,15,11,0|-1wzes1,15,11,0|-1wzes0,16,6,1|-1p4001,16,6,1|-1p4000,15,11,0|-1e7hg1,15,11,0|-1e7hg0,16,6,1|-1ceto1,16,6,1|-1ceto0,110,11,0|2crp3z,110,11,0|2crp40,111,6,1|2ht3nz,111,6,1|2ht3o0,110,11,0|2rj6fz,110,11,0|2rj6g0,111,6,1|2ydebz,111,6,1|2ydec0,110,11,0|5iwyfz,110,11,0|5iwyg0,111,6,1|5l2qfz,111,6,1|5l2qg0,110,11,0|7hhp3z,110,11,0|7hhp40,111,6,1|7n93rz,111,6,1|7n93s0,110,11,0|7z4vrz,110,11,0|7z4vs0,111,6,1|86c2bz,111,6,1|86c2c0,110,11,0|8jnrrz,110,11,0|8jnrs0,111,6,1|8pf3nz,111,6,1|8pf3o0,110,11,0|90ql3z,110,11,0|90ql40,111,6,1|98i4zz,111,6,1|98i500,110,11,0|9jb3rz,110,11,0|9jb3s0,111,6,1|9qv8zz,111,6,1|9qv900,110,11,0|a342fz,110,11,0|a342g0,111,6,1|a9lbnz,111,6,1|a9lbo0,110,11,0|ak1brz,110,11,0|ak1bs0,111,6,1|aryfnz,111,6,1|aryfo0,110,11,0|b2refz,110,11,0|b2reg0,111,6,1|bb1gzz,111,6,1|bb1h00,110,11,0|blufrz,110,11,0|blufs0,111,6,1|bu4ibz,111,6,1|bu4ic0,110,11,0|c4trrz,110,11,0|c4trs0,111,6,1|ccukzz,111,6,1|ccul00,110,11,0|cnjufz,110,11,0|cnjug0,111,6,1|cv7ozz,111,6,1|cv7p00,110,11,0|d69x3z,110,11,0|d69x40,111,6,1|deaqbz,111,6,1|deaqc0,110,11,0|dkh13z,110,11,0|dkh140,15,11,0|dpcyfz,15,11,0|dpcyg0,16,6,1|dy02fz,16,6,1|dy02g0,15,11,0|e8313z,15,11,0|e83140,16,6,1|egq53z,16,6,1|egq540,15,11,0|eqt3rz,15,11,0|eqt3s0,16,6,1|ezg7rz,16,6,1|ezg7s0,15,11,0|fa93rz,15,11,0|fa93s0,16,6,1|fjm2bz,16,6,1|fjm2c0,15,11,0|ftc53z,15,11,0|ftc540,16,6,1|g2p3nz,16,6,1|g2p3o0,15,11,0|gc27rz,15,11,0|gc27s0,16,6,1|glf6bz,16,6,1|glf6c0,15,11,0|gusafz,15,11,0|gusag0,16,6,1|h458zz,16,6,1|h45900,15,11,0|hdid3z,15,11,0|hdid40,16,6,1|hmvbnz,16,6,1|hmvbo0,15,11,0|hw8frz,15,11,0|hw8fs0,16,6,1|i4vjrz,16,6,1|i4vjs0,15,11,0|ieyifz,15,11,0|ieyig0,16,6,1|int3vz,16,6,1|int3w0,15,11,0|ix0ifz,15,11,0|ix0ig0,16,6,1|j5ynnz,16,6,1|j5yno0,15,11,0|jfsfrz,15,11,0|jfsfs0,16,6,1|joa2jz,16,6,1|joa2k0,15,11,0|jyet3z,15,11,0|jyet40,16,6,1|k6bwzz,16,6,1|k6bx00,15,11,0|kh4vrz,15,11,0|kh4vs0,16,6,1|kpf13z,16,6,1|kpf140,15,11,0|kzwt5n,15,11,0|kzwt5o,16,6,1|l6yfnz,16,6,1|l6yfo0,15,11,0|lixztn,15,11,0|lixzto,16,6,1|lp7ubz,16,6,1|lp7uc0,15,11,0|m1o2fz,15,11,0|m1o2g0,16,6,1|mao53z,16,6,1|mao540,15,11,0|mke53z,15,11,0|mke540,16,6,1|mtr3nz,16,6,1|mtr3o0,15,11,0|n347rz,15,11,0|n347s0,16,6,1|ndx0zz,16,6,1|ndx100,15,11,0|nlw53z,15,11,0|nlw540,16,6,1|nwn6fz,16,6,1|nwn6g0,15,11,0|o4majz,15,11,0|o4mak0,16,6,1|ofs2fz,16,6,1|ofs2g0,15,11,0|oncd7z,15,11,0|oncd80,16,6,1|oyi53z,16,6,1|oyi540,15,11,0|p62fvz,15,11,0|p62fw0,16,6,1|ph87rz,16,6,1|ph87s0,15,11,0|pp3jrz,15,11,0|pp3js0,16,6,1|pzy7nz,16,6,1|pzy7o0,15,11,0|q7vh3z,15,11,0|q7vh40,16,6,1|qiod3z,16,6,1|qiod40,15,11,0|qqljrz,15,11,0|qqljs0,16,6,1|r1refz,16,6,1|r1reg0,15,11,0|r9bmfz,15,11,0|r9bmg0,16,6,1|rkhh3z,16,6,1|rkhh40,15,11,0|rs1p3z,15,11,0|rs1p40,16,6,1|s37jrz,16,6,1|s37js0,15,11,0|sb4qfz,15,11,0|sb4qg0,16,6,1|slxmfz,16,6,1|slxmg0,15,11,0|stut3z,15,11,0|stut40,16,6,1|t4np3z,16,6,1|t4np40,15,11,0|tckvrz,15,11,0|tckvs0,16,6,1|tndrrz,16,6,1|tndrs0,15,11,0|tvayfz,15,11,0|tvayg0,16,6,1|u6gt3z,16,6,1|u6gt40,15,11,0|ue113z,15,11,0|ue1140,16,6,1|up6vrz,16,6,1|up6vs0,15,11,0|uwr3rz,15,11,0|uwr3s0,16,6,1|v7wyfz,16,6,1|v7wyg0,15,11,0|vfu53z,15,11,0|vfu540,16,6,1|vqn13z,16,6,1|vqn140,15,11,0|vyk7rz,15,11,0|vyk7s0,16,6,1|w9d3rz,16,6,1|w9d3s0,15,11,0|whaafz,15,11,0|whaag0,16,6,1|wsg53z,16,6,1|wsg540,15,11,0|x00d3z,15,11,0|x00d40,16,6,1|xb67rz,16,6,1|xb67s0,15,11,0|xiqfrz,15,11,0|xiqfs0,16,6,1|xtwafz,16,6,1|xtwag0,15,11,0|y1gifz,15,11,0|y1gig0,16,6,1|ycmd3z,16,6,1|ycmd40,15,11,0|ykjjrz,15,11,0|ykjjs0,16,6,1|yvcfrz,16,6,1|yvcfs0,15,11,0|z39mfz,15,11,0|z39mg0,16,6,1|ze2ifz,16,6,1|ze2ig0,15,11,0\",\"Asia/Hebron|,0,238,0|-1054x5z,15,11,0|-ffv401,15,11,0|-ffv400,16,6,1|-f9l6o1,16,6,1|-f9l6o0,15,11,0|-f765c1,15,11,0|-f765c0,16,6,1|-e6fxc1,16,6,1|-e6fxc0,15,11,0|-dyoao1,15,11,0|-dyoao0,16,6,1|-dno001,16,6,1|-dno000,15,11,0|-dfuio1,15,11,0|-dfuio0,16,6,1|-d4u801,16,6,1|-d4u800,15,11,0|-cwatc1,15,11,0|-cwatc0,16,6,1|-cm2ao1,16,6,1|-cm2ao0,15,11,0|-cdiw01,15,11,0|-cdiw00,16,6,1|-c3adc1,16,6,1|-c3adc0,15,11,0|-6lluw1,15,11,0|-6lluw0,16,6,1|-6e79o1,16,6,1|-6e79o0,15,11,0|-63alk1,15,11,0|-63alk0,16,6,1|-5vfcc1,16,6,1|-5vfcc0,15,11,0|-5kilg1,15,11,0|-5kilg0,16,6,1|-5cp1c1,16,6,1|-5cp1c0,15,11,0|-51otg1,15,11,0|-51otg0,16,6,1|-4tv9c1,16,6,1|-4tv9c0,15,11,0|-4iww41,15,11,0|-4iww40,16,6,1|-4b3c01,16,6,1|-4b3c00,15,11,0|-404ys1,15,11,0|-404ys0,16,6,1|-3sbeo1,16,6,1|-3sbeo0,15,11,0|-3hd1g1,15,11,0|-3hd1g0,16,6,1|-39jhc1,16,6,1|-39jhc0,15,11,0|-2yj9g1,15,11,0|-2yj9g0,16,6,1|-2qppc1,16,6,1|-2qppc0,15,11,0|-2frc41,15,11,0|-2frc40,16,6,1|-27xs01,16,6,1|-27xs00,15,11,0|-1wzes1,15,11,0|-1wzes0,16,6,1|-1p4001,16,6,1|-1p4000,15,11,0|-1e7hg1,15,11,0|-1e7hg0,16,6,1|-1ceto1,16,6,1|-1ceto0,110,11,0|2crp3z,110,11,0|2crp40,111,6,1|2ht3nz,111,6,1|2ht3o0,110,11,0|2rj6fz,110,11,0|2rj6g0,111,6,1|2ydebz,111,6,1|2ydec0,110,11,0|5iwyfz,110,11,0|5iwyg0,111,6,1|5l2qfz,111,6,1|5l2qg0,110,11,0|7hhp3z,110,11,0|7hhp40,111,6,1|7n93rz,111,6,1|7n93s0,110,11,0|7z4vrz,110,11,0|7z4vs0,111,6,1|86c2bz,111,6,1|86c2c0,110,11,0|8jnrrz,110,11,0|8jnrs0,111,6,1|8pf3nz,111,6,1|8pf3o0,110,11,0|90ql3z,110,11,0|90ql40,111,6,1|98i4zz,111,6,1|98i500,110,11,0|9jb3rz,110,11,0|9jb3s0,111,6,1|9qv8zz,111,6,1|9qv900,110,11,0|a342fz,110,11,0|a342g0,111,6,1|a9lbnz,111,6,1|a9lbo0,110,11,0|ak1brz,110,11,0|ak1bs0,111,6,1|aryfnz,111,6,1|aryfo0,110,11,0|b2refz,110,11,0|b2reg0,111,6,1|bb1gzz,111,6,1|bb1h00,110,11,0|blufrz,110,11,0|blufs0,111,6,1|bu4ibz,111,6,1|bu4ic0,110,11,0|c4trrz,110,11,0|c4trs0,111,6,1|ccukzz,111,6,1|ccul00,110,11,0|cnjufz,110,11,0|cnjug0,111,6,1|cv7ozz,111,6,1|cv7p00,110,11,0|d69x3z,110,11,0|d69x40,111,6,1|deaqbz,111,6,1|deaqc0,110,11,0|dkh13z,110,11,0|dkh140,15,11,0|dpcyfz,15,11,0|dpcyg0,16,6,1|dy02fz,16,6,1|dy02g0,15,11,0|e8313z,15,11,0|e83140,16,6,1|egq53z,16,6,1|egq540,15,11,0|eqt3rz,15,11,0|eqt3s0,16,6,1|ezg7rz,16,6,1|ezg7s0,15,11,0|fa93rz,15,11,0|fa93s0,16,6,1|fjm2bz,16,6,1|fjm2c0,15,11,0|ftc53z,15,11,0|ftc540,16,6,1|g2p3nz,16,6,1|g2p3o0,15,11,0|gc27rz,15,11,0|gc27s0,16,6,1|glf6bz,16,6,1|glf6c0,15,11,0|gusafz,15,11,0|gusag0,16,6,1|h458zz,16,6,1|h45900,15,11,0|hdid3z,15,11,0|hdid40,16,6,1|hmvbnz,16,6,1|hmvbo0,15,11,0|hw8frz,15,11,0|hw8fs0,16,6,1|i4vjrz,16,6,1|i4vjs0,15,11,0|ieyifz,15,11,0|ieyig0,16,6,1|int3vz,16,6,1|int3w0,15,11,0|ix0ifz,15,11,0|ix0ig0,16,6,1|j5ynnz,16,6,1|j5yno0,15,11,0|jfsfrz,15,11,0|jfsfs0,16,6,1|joa2jz,16,6,1|joa2k0,15,11,0|jyet3z,15,11,0|jyet40,16,6,1|k6hgzz,16,6,1|k6hh00,15,11,0|kh4vrz,15,11,0|kh4vs0,16,6,1|kpf13z,16,6,1|kpf140,15,11,0|kzuyfz,15,11,0|kzuyg0,16,6,1|l6yfnz,16,6,1|l6yfo0,15,11,0|lixztn,15,11,0|lixzto,16,6,1|lp7ubz,16,6,1|lp7uc0,15,11,0|lqpmfz,15,11,0|lqpmg0,16,6,1|lsaybz,16,6,1|lsayc0,15,11,0|m1o2fz,15,11,0|m1o2g0,16,6,1|mao53z,16,6,1|mao540,15,11,0|mke53z,15,11,0|mke540,16,6,1|mtr3nz,16,6,1|mtr3o0,15,11,0|n347rz,15,11,0|n347s0,16,6,1|ndx0zz,16,6,1|ndx100,15,11,0|nlw53z,15,11,0|nlw540,16,6,1|nwn6fz,16,6,1|nwn6g0,15,11,0|o4majz,15,11,0|o4mak0,16,6,1|ofs2fz,16,6,1|ofs2g0,15,11,0|oncd7z,15,11,0|oncd80,16,6,1|oyi53z,16,6,1|oyi540,15,11,0|p62fvz,15,11,0|p62fw0,16,6,1|ph87rz,16,6,1|ph87s0,15,11,0|pp3jrz,15,11,0|pp3js0,16,6,1|pzy7nz,16,6,1|pzy7o0,15,11,0|q7vh3z,15,11,0|q7vh40,16,6,1|qiod3z,16,6,1|qiod40,15,11,0|qqljrz,15,11,0|qqljs0,16,6,1|r1refz,16,6,1|r1reg0,15,11,0|r9bmfz,15,11,0|r9bmg0,16,6,1|rkhh3z,16,6,1|rkhh40,15,11,0|rs1p3z,15,11,0|rs1p40,16,6,1|s37jrz,16,6,1|s37js0,15,11,0|sb4qfz,15,11,0|sb4qg0,16,6,1|slxmfz,16,6,1|slxmg0,15,11,0|stut3z,15,11,0|stut40,16,6,1|t4np3z,16,6,1|t4np40,15,11,0|tckvrz,15,11,0|tckvs0,16,6,1|tndrrz,16,6,1|tndrs0,15,11,0|tvayfz,15,11,0|tvayg0,16,6,1|u6gt3z,16,6,1|u6gt40,15,11,0|ue113z,15,11,0|ue1140,16,6,1|up6vrz,16,6,1|up6vs0,15,11,0|uwr3rz,15,11,0|uwr3s0,16,6,1|v7wyfz,16,6,1|v7wyg0,15,11,0|vfu53z,15,11,0|vfu540,16,6,1|vqn13z,16,6,1|vqn140,15,11,0|vyk7rz,15,11,0|vyk7s0,16,6,1|w9d3rz,16,6,1|w9d3s0,15,11,0|whaafz,15,11,0|whaag0,16,6,1|wsg53z,16,6,1|wsg540,15,11,0|x00d3z,15,11,0|x00d40,16,6,1|xb67rz,16,6,1|xb67s0,15,11,0|xiqfrz,15,11,0|xiqfs0,16,6,1|xtwafz,16,6,1|xtwag0,15,11,0|y1gifz,15,11,0|y1gig0,16,6,1|ycmd3z,16,6,1|ycmd40,15,11,0|ykjjrz,15,11,0|ykjjs0,16,6,1|yvcfrz,16,6,1|yvcfs0,15,11,0|z39mfz,15,11,0|z39mg0,16,6,1|ze2ifz,16,6,1|ze2ig0,15,11,0\",\"Asia/Ho_Chi_Minh|,0,239,0|-x56934,112,240,0|-umdqev,112,240,0|-umdqeu,91,193,0|-e3bkw1,91,193,0|-e3bkw0,89,191,0|-cxyro1,89,191,0|-cxyro0,107,224,0|-cp63o1,107,224,0|-cp63o0,91,193,0|-bvja41,91,193,0|-bvja40,89,191,0|-7kjq81,89,191,0|-7kjq80,91,193,0|-57xfk1,91,193,0|-57xfk0,89,191,0|2uaprz,89,191,0|2uaps0,91,193,0\",\"Asia/Hong_Kong|,0,241,0|-y0i0s0,113,191,0|-ewdn81,113,191,0|-ewdn80,114,224,1|-eqtn81,114,224,1|-eqtn80,115,242,1|-emgia1,115,242,1|-emgia0,116,224,0|-cl7cs1,116,224,0|-cl7cs0,113,191,0|-cda8w1,113,191,0|-cda8w0,114,224,1|-c1r5u1,114,224,1|-c1r5u0,113,191,0|-buwv61,113,191,0|-buwv60,114,224,1|-bj1361,114,224,1|-bj1360,113,191,0|-bb3wi1,113,191,0|-bb3wi0,114,224,1|-b1qv61,114,224,1|-b1qv60,113,191,0|-attoi1,113,191,0|-attoi0,114,224,1|-aj0si1,114,224,1|-aj0si0,113,191,0|-ab3lu1,113,191,0|-ab3lu0,114,224,1|-a0apu1,114,224,1|-a0apu0,113,191,0|-9sdj61,113,191,0|-9sdj60,114,224,1|-9hkn61,114,224,1|-9hkn60,113,191,0|-99ahu1,113,191,0|-99ahu0,114,224,1|-8yhlu1,114,224,1|-8yhlu0,113,191,0|-8qkf61,113,191,0|-8qkf60,114,224,1|-8frly1,114,224,1|-8frly0,113,191,0|-88k9u1,113,191,0|-88k9u0,114,224,1|-7x1ja1,114,224,1|-7x1ja0,113,191,0|-7pu761,113,191,0|-7pu760,114,224,1|-7dyhy1,114,224,1|-7dyhy0,113,191,0|-7744i1,113,191,0|-7744i0,114,224,1|-6v8fa1,114,224,1|-6v8fa0,113,191,0|-6o1361,113,191,0|-6o1360,114,224,1|-6cicm1,114,224,1|-6cicm0,113,191,0|-65b0i1,113,191,0|-65b0i0,114,224,1|-5ts9y1,114,224,1|-5ts9y0,113,191,0|-5mkxu1,113,191,0|-5mkxu0,114,224,1|-5b27a1,114,224,1|-5b27a0,113,191,0|-53uv61,113,191,0|-53uv60,114,224,1|-4rz5y1,114,224,1|-4rz5y0,113,191,0|-4l4si1,113,191,0|-4l4si0,114,224,1|-4993a1,114,224,1|-4993a0,113,191,0|-42epu1,113,191,0|-42epu0,114,224,1|-3qj0m1,114,224,1|-3qj0m0,113,191,0|-3jboi1,113,191,0|-3jboi0,114,224,1|-37sxy1,114,224,1|-37sxy0,113,191,0|-30llu1,113,191,0|-30llu0,114,224,1|-2p2va1,114,224,1|-2p2va0,113,191,0|-2gfoi1,113,191,0|-2gfoi0,114,224,1|-272py1,114,224,1|-272py0,113,191,0|-1xplu1,113,191,0|-1xplu0,114,224,1|-1ocna1,114,224,1|-1ocna0,113,191,0|-1ezj61,113,191,0|-1ezj60,114,224,1|-159ly1,114,224,1|-159ly0,113,191,0|-vwhu1,113,191,0|-vwhu0,114,224,1|-mjja1,114,224,1|-mjja0,113,191,0|-d6f61,113,191,0|-d6f60,114,224,1|-3tgm1,114,224,1|-3tgm0,113,191,0|5jnhz,113,191,0|5jni0,114,224,1|ewm1z,114,224,1|ewm20,113,191,0|o9q5z,113,191,0|o9q60,114,224,1|xmopz,114,224,1|xmoq0,113,191,0|16zstz,113,191,0|16zsu0,114,224,1|1gpq1z,114,224,1|1gpq20,113,191,0|1q2u5z,113,191,0|1q2u60,114,224,1|1zfspz,114,224,1|1zfsq0,113,191,0|231i5z,113,191,0|231i60,114,224,1|2i5vdz,114,224,1|2i5ve0,113,191,0|2rizhz,113,191,0|2rizi0,114,224,1|30vy1z,114,224,1|30vy20,113,191,0|3a925z,113,191,0|3a9260,114,224,1|3jm0pz,114,224,1|3jm0q0,113,191,0|4vv4tz,113,191,0|4vv4u0,114,224,1|5457dz,114,224,1|5457e0,113,191,0\",\"Asia/Hovd|,0,243,0|-xmcoz0,96,196,0|46anbz,96,196,0|46anc0,91,193,0|6wupvz,91,193,0|6wupw0,89,191,1|769j3z,89,191,1|769j40,91,193,0|7fohvz,91,193,0|7fohw0,89,191,1|7p1gfz,89,191,1|7p1gg0,91,193,0|7yekjz,91,193,0|7yekk0,89,191,1|87rj3z,89,191,1|87rj40,91,193,0|8h4n7z,91,193,0|8h4n80,89,191,1|8qhlrz,89,191,1|8qhls0,91,193,0|8zupvz,91,193,0|8zupw0,89,191,1|997ofz,89,191,1|997og0,91,193,0|9iksjz,91,193,0|9iksk0,89,191,1|9rxr3z,89,191,1|9rxr40,91,193,0|a1av7z,91,193,0|a1av80,89,191,1|aantrz,89,191,1|aants0,91,193,0|ak0xvz,91,193,0|ak0xw0,89,191,1|atqv3z,89,191,1|atqv40,91,193,0|b33z7z,91,193,0|b33z80,89,191,1|bcgxrz,89,191,1|bcgxs0,91,193,0|blu1vz,91,193,0|blu1w0,89,191,1|bv70fz,89,191,1|bv70g0,91,193,0|c4k4jz,91,193,0|c4k4k0,89,191,1|cdx33z,89,191,1|cdx340,91,193,0|cna77z,91,193,0|cna780,89,191,1|cwn5rz,89,191,1|cwn5s0,91,193,0|d609vz,91,193,0|d609w0,89,191,1|dfd8fz,89,191,1|dfd8g0,91,193,0|dp3b7z,91,193,0|dp3b80,89,191,1|dyg9rz,89,191,1|dyg9s0,91,193,0|e7tdvz,91,193,0|e7tdw0,89,191,1|eh6cfz,89,191,1|eh6cg0,91,193,0|eqjgjz,91,193,0|eqjgk0,89,191,1|ezwf3z,89,191,1|ezwf40,91,193,0|gcgsrz,91,193,0|gcgss0,89,191,1|gkdwnz,89,191,1|gkdwo0,91,193,0|gtr0rz,91,193,0|gtr0s0,89,191,1|h33zbz,89,191,1|h33zc0,91,193,0|hch3fz,91,193,0|hch3g0,89,191,1|hlu1zz,89,191,1|hlu200,91,193,0|hv763z,91,193,0|hv7640,89,191,1|i4k4nz,89,191,1|i4k4o0,91,193,0|idx8rz,91,193,0|idx8s0,89,191,1|ina7bz,89,191,1|ina7c0,91,193,0|iwnbfz,91,193,0|iwnbg0,89,191,1|j6d8nz,89,191,1|j6d8o0,91,193,0|nlvwrz,91,193,0|nlvws0,89,191,1|nv8prz,89,191,1|nv8ps0,91,193,0|o4lzfz,91,193,0|o4lzg0,89,191,1|odysfz,89,191,1|odysg0,91,193,0\",\"Asia/Irkutsk|,0,244,0|-1ayylz5,117,244,0|-q28gn6,117,244,0|-q28gn5,91,193,0|-kmrfg1,91,193,0|-kmrfg0,89,191,0|5vasfz,89,191,0|5vasg0,107,224,1|64plnz,107,224,1|64plo0,89,191,0|6e2prz,89,191,0|6e2ps0,107,224,1|6nhizz,107,224,1|6nhj00,89,191,0|6wun3z,89,191,0|6wun40,107,224,1|769gbz,107,224,1|769gc0,89,191,0|7fof3z,89,191,0|7fof40,107,224,1|7p1lzz,107,224,1|7p1m00,89,191,0|7yenbz,89,191,0|7yenc0,107,224,1|87ronz,107,224,1|87roo0,89,191,0|8h4pzz,89,191,0|8h4q00,107,224,1|8qhrbz,107,224,1|8qhrc0,89,191,0|8zusnz,89,191,0|8zuso0,107,224,1|997tzz,107,224,1|997u00,89,191,0|9ikvbz,89,191,0|9ikvc0,107,224,1|9rxwnz,107,224,1|9rxwo0,89,191,0|a1axzz,89,191,0|a1ay00,107,224,1|aanzbz,107,224,1|aanzc0,89,191,0|ak10nz,89,191,0|ak10o0,107,224,1|atr0nz,107,224,1|atr0o0,89,191,0|b341zz,89,191,0|b34200,89,191,1|bch63z,89,191,1|bch640,91,193,0|bi8krz,91,193,0|bi8ks0,89,191,0|blu4nz,89,191,0|blu4o0,107,224,1|bv75zz,107,224,1|bv7600,89,191,0|c4k7bz,89,191,0|c4k7c0,107,224,1|cdx8nz,107,224,1|cdx8o0,89,191,0|cna9zz,89,191,0|cnaa00,107,224,1|cwnbbz,107,224,1|cwnbc0,89,191,0|d60cnz,89,191,0|d60co0,107,224,1|dfddzz,107,224,1|dfde00,89,191,0|dp3dzz,89,191,0|dp3e00,107,224,1|dzw9zz,107,224,1|dzwa00,89,191,0|e7tgnz,89,191,0|e7tgo0,107,224,1|eimcnz,107,224,1|eimco0,89,191,0|eqjjbz,89,191,0|eqjjc0,107,224,1|f1cfbz,107,224,1|f1cfc0,89,191,0|f99lzz,89,191,0|f99m00,107,224,1|fkfgnz,107,224,1|fkfgo0,89,191,0|frzonz,89,191,0|frzoo0,107,224,1|g35jbz,107,224,1|g35jc0,89,191,0|gaprbz,89,191,0|gaprc0,107,224,1|glvlzz,107,224,1|glvm00,89,191,0|gtssnz,89,191,0|gtsso0,107,224,1|h4lonz,107,224,1|h4loo0,89,191,0|hcivbz,89,191,0|hcivc0,107,224,1|hnbrbz,107,224,1|hnbrc0,89,191,0|hv8xzz,89,191,0|hv8y00,107,224,1|i6esnz,107,224,1|i6eso0,89,191,0|idz0nz,89,191,0|idz0o0,107,224,1|ip4vbz,107,224,1|ip4vc0,89,191,0|iwp3bz,89,191,0|iwp3c0,107,224,1|j7uxzz,107,224,1|j7uy00,89,191,0|jff5zz,89,191,0|jff600,107,224,1|jql0nz,107,224,1|jql0o0,89,191,0|jyi7bz,89,191,0|jyi7c0,107,224,1|k9b3bz,107,224,1|k9b3c0,89,191,0|kh89zz,89,191,0|kh8a00,107,224,1|ks15zz,107,224,1|ks1600,89,191,0|kzycnz,89,191,0|kzyco0,107,224,1|lb47bz,107,224,1|lb47c0,89,191,0|liofbz,89,191,0|liofc0,107,224,0|ne0f7z,107,224,0|ne0f80,89,191,0\",\"Asia/Jakarta|,0,245,0|-1hftyg0,53,245,0|-o0bdpd,53,245,0|-o0bdpc,118,246,0|-jebgdd,118,246,0|-jebgdc,106,222,0|-ehxgu1,106,222,0|-ehxgu0,107,224,0|-co37o1,107,224,0|-co37o0,106,222,0|-bb5zi1,106,222,0|-bb5zi0,89,191,0|-a9m681,89,191,0|-a9m680,106,222,0|-34ru61,106,222,0|-34ru60,119,193,0\",\"Asia/Jayapura|,0,247,0|-jebm20,107,224,0|-d7zvo1,107,224,0|-d7zvo0,120,248,0|-34rzq1,120,248,0|-34rzq0,121,224,0\",\"Asia/Jerusalem|,0,249,0|-1ayy96u,122,250,0|-r50eih,122,250,0|-r50eig,110,11,0|-ffv401,110,11,0|-ffv400,111,6,1|-f9l6o1,111,6,1|-f9l6o0,110,11,0|-f765c1,110,11,0|-f765c0,111,6,1|-e6fxc1,111,6,1|-e6fxc0,110,11,0|-dyoao1,110,11,0|-dyoao0,111,6,1|-dno001,111,6,1|-dno000,110,11,0|-dfuio1,110,11,0|-dfuio0,111,6,1|-d4u801,111,6,1|-d4u800,110,11,0|-cwatc1,110,11,0|-cwatc0,111,6,1|-cm2ao1,111,6,1|-cm2ao0,110,11,0|-cdiw01,110,11,0|-cdiw00,111,6,1|-c3adc1,111,6,1|-c3adc0,110,11,0|-ba0o01,110,11,0|-ba0o00,123,209,1|-b4tmo1,123,209,1|-b4tmo0,111,6,1|-b1oo01,111,6,1|-b1oo00,110,11,0|-asdhc1,110,11,0|-asdhc0,111,6,1|-aiwqo1,111,6,1|-aiwqo0,110,11,0|-aadc01,110,11,0|-aadc00,111,6,1|-a2juo1,111,6,1|-a2juo0,110,11,0|-9sd6o1,110,11,0|-9sd6o0,111,6,1|-9gudc1,111,6,1|-9gudc0,110,11,0|-98k801,110,11,0|-98k800,111,6,1|-8z76o1,111,6,1|-8z76o0,110,11,0|-8q7401,110,11,0|-8q7400,111,6,1|-8i9xc1,111,6,1|-8i9xc0,110,11,0|-848dc1,110,11,0|-848dc0,111,6,1|-7zjuo1,111,6,1|-7zjuo0,110,11,0|-7liao1,110,11,0|-7liao0,111,6,1|-7gts01,111,6,1|-7gts00,110,11,0|-7356o1,110,11,0|-7356o0,111,6,1|-6x0tc1,111,6,1|-6x0tc0,110,11,0|-6m7xc1,110,11,0|-6m7xc0,111,6,1|-6enpc1,111,6,1|-6enpc0,110,11,0|2crp3z,110,11,0|2crp40,111,6,1|2ht3nz,111,6,1|2ht3o0,110,11,0|2rj6fz,110,11,0|2rj6g0,111,6,1|2ydebz,111,6,1|2ydec0,110,11,0|5iwyfz,110,11,0|5iwyg0,111,6,1|5l2qfz,111,6,1|5l2qg0,110,11,0|7hhp3z,110,11,0|7hhp40,111,6,1|7n93rz,111,6,1|7n93s0,110,11,0|7z4vrz,110,11,0|7z4vs0,111,6,1|86c2bz,111,6,1|86c2c0,110,11,0|8jnrrz,110,11,0|8jnrs0,111,6,1|8pf3nz,111,6,1|8pf3o0,110,11,0|90ql3z,110,11,0|90ql40,111,6,1|98i4zz,111,6,1|98i500,110,11,0|9jb3rz,110,11,0|9jb3s0,111,6,1|9qv8zz,111,6,1|9qv900,110,11,0|a342fz,110,11,0|a342g0,111,6,1|a9lbnz,111,6,1|a9lbo0,110,11,0|ak1brz,110,11,0|ak1bs0,111,6,1|aryfnz,111,6,1|aryfo0,110,11,0|b2refz,110,11,0|b2reg0,111,6,1|bb1gzz,111,6,1|bb1h00,110,11,0|blufrz,110,11,0|blufs0,111,6,1|bu4ibz,111,6,1|bu4ic0,110,11,0|c4trrz,110,11,0|c4trs0,111,6,1|ccukzz,111,6,1|ccul00,110,11,0|cnjufz,110,11,0|cnjug0,111,6,1|cv7ozz,111,6,1|cv7p00,110,11,0|d69x3z,110,11,0|d69x40,111,6,1|deaqbz,111,6,1|deaqc0,110,11,0|doa2fz,110,11,0|doa2g0,111,6,1|dxskzz,111,6,1|dxsl00,110,11,0|e7d3rz,110,11,0|e7d3s0,111,6,1|eggszz,111,6,1|eggt00,110,11,0|eq36fz,110,11,0|eq36g0,111,6,1|eytwzz,111,6,1|eytx00,110,11,0|f9jbzz,110,11,0|f9jc00,111,6,1|fhgfvz,111,6,1|fhgfw0,110,11,0|fszbzz,110,11,0|fszc00,111,6,1|g1z93z,111,6,1|g1z940,110,11,0|gbhx7z,110,11,0|gbhx80,111,6,1|gk4yfz,111,6,1|gk4yg0,110,11,0|gtph7z,110,11,0|gtph80,111,6,1|h3kyfz,111,6,1|h3kyg0,110,11,0|hcfjvz,110,11,0|hcfjw0,111,6,1|hm5h3z,111,6,1|hm5h40,110,11,0|hvrujz,110,11,0|hvruk0,111,6,1|i4evrz,111,6,1|i4evs0,110,11,0|ie8qnz,110,11,0|ie8qo0,111,6,1|io2d7z,111,6,1|io2d80,110,11,0|iwytbz,110,11,0|iwytc0,111,6,1|j6fh7z,111,6,1|j6fh80,110,11,0|jfovzz,110,11,0|jfow00,111,6,1|jofmjz,111,6,1|jofmk0,110,11,0|jyeynz,110,11,0|jyeyo0,111,6,1|k88l7z,111,6,1|k88l80,110,11,0|kh51bz,110,11,0|kh51c0,111,6,1|kqlp7z,111,6,1|kqlp80,110,11,0|kzv3zz,110,11,0|kzv400,111,6,1|l8lujz,111,6,1|l8luk0,110,11,0|liy5bz,110,11,0|liy5c0,111,6,1|lset7z,111,6,1|lset80,110,11,0|m1o7zz,110,11,0|m1o800,111,6,1|marx7z,111,6,1|marx80,110,11,0|mkeanz,110,11,0|mkeao0,111,6,1|mvat7z,111,6,1|mvat80,110,11,0|n34dbz,110,11,0|n34dc0,111,6,1|ne0vvz,111,6,1|ne0vw0,110,11,0|nlufzz,110,11,0|nlug00,111,6,1|nwqyjz,111,6,1|nwqyk0,110,11,0|o4kinz,110,11,0|o4kio0,111,6,1|oftzvz,111,6,1|oftzw0,110,11,0|onalbz,110,11,0|onalc0,111,6,1|oyk2jz,111,6,1|oyk2k0,110,11,0|p60nzz,110,11,0|p60o00,111,6,1|pha57z,111,6,1|pha580,110,11,0|pp3pbz,110,11,0|pp3pc0,111,6,1|q007vz,111,6,1|q007w0,110,11,0|q7trzz,110,11,0|q7ts00,111,6,1|qiqajz,111,6,1|qiqak0,110,11,0|qqjunz,110,11,0|qqjuo0,111,6,1|r1tbvz,111,6,1|r1tbw0,110,11,0|r99xbz,110,11,0|r99xc0,111,6,1|rkjejz,111,6,1|rkjek0,110,11,0|rrzzzz,110,11,0|rs0000,111,6,1|s39h7z,111,6,1|s39h80,110,11,0|sb31bz,110,11,0|sb31c0,111,6,1|slzjvz,111,6,1|slzjw0,110,11,0|stt3zz,110,11,0|stt400,111,6,1|t4pmjz,111,6,1|t4pmk0,110,11,0|tcj6nz,110,11,0|tcj6o0,111,6,1|tnfp7z,111,6,1|tnfp80,110,11,0|tv99bz,110,11,0|tv99c0,111,6,1|u6iqjz,111,6,1|u6iqk0,110,11,0|udzbzz,110,11,0|udzc00,111,6,1|up8t7z,111,6,1|up8t80,110,11,0|uwpenz,110,11,0|uwpeo0,111,6,1|v7yvvz,111,6,1|v7yvw0,110,11,0|vfsfzz,110,11,0|vfsg00,111,6,1|vqoyjz,111,6,1|vqoyk0,110,11,0|vyiinz,110,11,0|vyiio0,111,6,1|w9f17z,111,6,1|w9f180,110,11,0|wh8lbz,110,11,0|wh8lc0,111,6,1|wsi2jz,111,6,1|wsi2k0,110,11,0|wzynzz,110,11,0|wzyo00,111,6,1|xb857z,111,6,1|xb8580,110,11,0|xioqnz,110,11,0|xioqo0,111,6,1|xty7vz,111,6,1|xty7w0,110,11,0|y1etbz,110,11,0|y1etc0,111,6,1|ycoajz,111,6,1|ycoak0,110,11,0|ykhunz,110,11,0|ykhuo0,111,6,1|yved7z,111,6,1|yved80,110,11,0|z37xbz,110,11,0|z37xc0,111,6,1|ze4fvz,111,6,1|ze4fw0,110,11,0\",\"Asia/Kabul|,0,251,0|-15r1m5c,105,209,0|-d1pkg1,105,209,0|-d1pkg0,124,252,0\",\"Asia/Kamchatka|,0,253,0|-olrupo,90,192,0|-kmrqk1,90,192,0|-kmrqk0,102,200,0|5vahbz,102,200,0|5vahc0,103,201,1|64pajz,103,201,1|64pak0,102,200,0|6e2enz,102,200,0|6e2eo0,103,201,1|6nh7vz,103,201,1|6nh7w0,102,200,0|6wubzz,102,200,0|6wuc00,103,201,1|76957z,103,201,1|769580,102,200,0|7fo3zz,102,200,0|7fo400,103,201,1|7p1avz,103,201,1|7p1aw0,102,200,0|7yec7z,102,200,0|7yec80,103,201,1|87rdjz,103,201,1|87rdk0,102,200,0|8h4evz,102,200,0|8h4ew0,103,201,1|8qhg7z,103,201,1|8qhg80,102,200,0|8zuhjz,102,200,0|8zuhk0,103,201,1|997ivz,103,201,1|997iw0,102,200,0|9ikk7z,102,200,0|9ikk80,103,201,1|9rxljz,103,201,1|9rxlk0,102,200,0|a1amvz,102,200,0|a1amw0,103,201,1|aano7z,103,201,1|aano80,102,200,0|ak0pjz,102,200,0|ak0pk0,103,201,1|atqpjz,103,201,1|atqpk0,102,200,0|b33qvz,102,200,0|b33qw0,102,200,1|bcguzz,102,200,1|bcgv00,90,192,0|bi89nz,90,192,0|bi89o0,102,200,0|blttjz,102,200,0|blttk0,103,201,1|bv6uvz,103,201,1|bv6uw0,102,200,0|c4jw7z,102,200,0|c4jw80,103,201,1|cdwxjz,103,201,1|cdwxk0,102,200,0|cn9yvz,102,200,0|cn9yw0,103,201,1|cwn07z,103,201,1|cwn080,102,200,0|d601jz,102,200,0|d601k0,103,201,1|dfd2vz,103,201,1|dfd2w0,102,200,0|dp32vz,102,200,0|dp32w0,103,201,1|dzvyvz,103,201,1|dzvyw0,102,200,0|e7t5jz,102,200,0|e7t5k0,103,201,1|eim1jz,103,201,1|eim1k0,102,200,0|eqj87z,102,200,0|eqj880,103,201,1|f1c47z,103,201,1|f1c480,102,200,0|f99avz,102,200,0|f99aw0,103,201,1|fkf5jz,103,201,1|fkf5k0,102,200,0|frzdjz,102,200,0|frzdk0,103,201,1|g3587z,103,201,1|g35880,102,200,0|gapg7z,102,200,0|gapg80,103,201,1|glvavz,103,201,1|glvaw0,102,200,0|gtshjz,102,200,0|gtshk0,103,201,1|h4ldjz,103,201,1|h4ldk0,102,200,0|hcik7z,102,200,0|hcik80,103,201,1|hnbg7z,103,201,1|hnbg80,102,200,0|hv8mvz,102,200,0|hv8mw0,103,201,1|i6ehjz,103,201,1|i6ehk0,102,200,0|idypjz,102,200,0|idypk0,103,201,1|ip4k7z,103,201,1|ip4k80,102,200,0|iwos7z,102,200,0|iwos80,103,201,1|j7umvz,103,201,1|j7umw0,102,200,0|jfeuvz,102,200,0|jfeuw0,103,201,1|jqkpjz,103,201,1|jqkpk0,102,200,0|jyhw7z,102,200,0|jyhw80,103,201,1|k9as7z,103,201,1|k9as80,102,200,0|kh7yvz,102,200,0|kh7yw0,103,201,1|ks0uvz,103,201,1|ks0uw0,102,200,0|kzy1jz,102,200,0|kzy1k0,102,200,1|lb3yzz,102,200,1|lb3z00,90,192,0|lio6zz,90,192,0|lio700,102,200,0\",\"Asia/Karachi|,0,254,0|-wvpb30,108,228,0|-e9lba1,108,228,0|-e9lba0,109,229,1|-cmya21,109,229,1|-cmya20,108,228,0|-9j0km1,108,228,0|-9j0km0,92,194,0|n33fz,92,194,0|n33g0,125,194,0|gu5u3z,125,194,0|gu5u40,126,196,1|h3isnz,126,196,1|h3iso0,125,194,0|k1qy3z,125,194,0|k1qy40,126,196,1|k9m7bz,126,196,1|k9m7c0,125,194,0|ki3u3z,125,194,0|ki3u40,126,196,1|kse4nz,126,196,1|kse4o0,125,194,0\",\"Asia/Kathmandu|,0,255,0|-q3gt4s,108,228,0|8clspz,108,228,0|8clsq0,127,256,0\",\"Asia/Khandyga|,0,257,0|-q4cjrp,89,191,0|-kmri81,89,191,0|-kmri80,107,224,0|5vapnz,107,224,0|5vapo0,93,195,1|64pivz,93,195,1|64piw0,107,224,0|6e2mzz,107,224,0|6e2n00,93,195,1|6nhg7z,93,195,1|6nhg80,107,224,0|6wukbz,107,224,0|6wukc0,93,195,1|769djz,93,195,1|769dk0,107,224,0|7focbz,107,224,0|7focc0,93,195,1|7p1j7z,93,195,1|7p1j80,107,224,0|7yekjz,107,224,0|7yekk0,93,195,1|87rlvz,93,195,1|87rlw0,107,224,0|8h4n7z,107,224,0|8h4n80,93,195,1|8qhojz,93,195,1|8qhok0,107,224,0|8zupvz,107,224,0|8zupw0,93,195,1|997r7z,93,195,1|997r80,107,224,0|9iksjz,107,224,0|9iksk0,93,195,1|9rxtvz,93,195,1|9rxtw0,107,224,0|a1av7z,107,224,0|a1av80,93,195,1|aanwjz,93,195,1|aanwk0,107,224,0|ak0xvz,107,224,0|ak0xw0,93,195,1|atqxvz,93,195,1|atqxw0,107,224,0|b33z7z,107,224,0|b33z80,107,224,1|bch3bz,107,224,1|bch3c0,89,191,0|bi8hzz,89,191,0|bi8i00,107,224,0|blu1vz,107,224,0|blu1w0,93,195,1|bv737z,93,195,1|bv7380,107,224,0|c4k4jz,107,224,0|c4k4k0,93,195,1|cdx5vz,93,195,1|cdx5w0,107,224,0|cna77z,107,224,0|cna780,93,195,1|cwn8jz,93,195,1|cwn8k0,107,224,0|d609vz,107,224,0|d609w0,93,195,1|dfdb7z,93,195,1|dfdb80,107,224,0|dp3b7z,107,224,0|dp3b80,93,195,1|dzw77z,93,195,1|dzw780,107,224,0|e7tdvz,107,224,0|e7tdw0,93,195,1|eim9vz,93,195,1|eim9w0,107,224,0|eqjgjz,107,224,0|eqjgk0,93,195,1|f1ccjz,93,195,1|f1cck0,107,224,0|f99j7z,107,224,0|f99j80,93,195,1|fkfdvz,93,195,1|fkfdw0,107,224,0|frzlvz,107,224,0|frzlw0,93,195,1|g35gjz,93,195,1|g35gk0,107,224,0|gapojz,107,224,0|gapok0,93,195,1|glvj7z,93,195,1|glvj80,107,224,0|gtspvz,107,224,0|gtspw0,93,195,1|h4llvz,93,195,1|h4llw0,107,224,0|hcisjz,107,224,0|hcisk0,93,195,1|hnbojz,93,195,1|hnbok0,107,224,0|hqrlnz,107,224,0|hqrlo0,93,195,0|hv8sfz,93,195,0|hv8sg0,90,192,1|i6en3z,90,192,1|i6en40,93,195,0|idyv3z,93,195,0|idyv40,90,192,1|ip4prz,90,192,1|ip4ps0,93,195,0|iwoxrz,93,195,0|iwoxs0,90,192,1|j7usfz,90,192,1|j7usg0,93,195,0|jff0fz,93,195,0|jff0g0,90,192,1|jqkv3z,90,192,1|jqkv40,93,195,0|jyi1rz,93,195,0|jyi1s0,90,192,1|k9axrz,90,192,1|k9axs0,93,195,0|kh84fz,93,195,0|kh84g0,90,192,1|ks10fz,90,192,1|ks10g0,93,195,0|kzy73z,93,195,0|kzy740,90,192,1|lb41rz,90,192,1|lb41s0,93,195,0|lio9rz,93,195,0|lio9s0,90,192,0|lreurz,90,192,0|lreus0,93,195,0|ne0cfz,93,195,0|ne0cg0,107,224,0\",\"Asia/Kolkata|,0,258,0|-1oaa314,77,232,0|-1g6thox,77,232,0|-1g6thow,21,259,0|-xehavb,21,259,0|-xehava,110,228,0|-eqtom1,110,228,0|-eqtom0,109,229,1|-ef78q1,109,229,1|-ef78q0,110,228,0|-e9lba1,110,228,0|-e9lba0,109,229,1|-cmya21,109,229,1|-cmya20,110,228,0\",\"Asia/Krasnoyarsk|,0,260,0|-q37l72,96,196,0|-kmrco1,96,196,0|-kmrco0,91,193,0|5vav7z,91,193,0|5vav80,89,191,1|64pofz,89,191,1|64pog0,91,193,0|6e2sjz,91,193,0|6e2sk0,89,191,1|6nhlrz,89,191,1|6nhls0,91,193,0|6wupvz,91,193,0|6wupw0,89,191,1|769j3z,89,191,1|769j40,91,193,0|7fohvz,91,193,0|7fohw0,89,191,1|7p1orz,89,191,1|7p1os0,91,193,0|7yeq3z,91,193,0|7yeq40,89,191,1|87rrfz,89,191,1|87rrg0,91,193,0|8h4srz,91,193,0|8h4ss0,89,191,1|8qhu3z,89,191,1|8qhu40,91,193,0|8zuvfz,91,193,0|8zuvg0,89,191,1|997wrz,89,191,1|997ws0,91,193,0|9iky3z,91,193,0|9iky40,89,191,1|9rxzfz,89,191,1|9rxzg0,91,193,0|a1b0rz,91,193,0|a1b0s0,89,191,1|aao23z,89,191,1|aao240,91,193,0|ak13fz,91,193,0|ak13g0,89,191,1|atr3fz,89,191,1|atr3g0,91,193,0|b344rz,91,193,0|b344s0,91,193,1|bch8vz,91,193,1|bch8w0,96,196,0|bi8njz,96,196,0|bi8nk0,91,193,0|blu7fz,91,193,0|blu7g0,89,191,1|bv78rz,89,191,1|bv78s0,91,193,0|c4ka3z,91,193,0|c4ka40,89,191,1|cdxbfz,89,191,1|cdxbg0,91,193,0|cnacrz,91,193,0|cnacs0,89,191,1|cwne3z,89,191,1|cwne40,91,193,0|d60ffz,91,193,0|d60fg0,89,191,1|dfdgrz,89,191,1|dfdgs0,91,193,0|dp3grz,91,193,0|dp3gs0,89,191,1|dzwcrz,89,191,1|dzwcs0,91,193,0|e7tjfz,91,193,0|e7tjg0,89,191,1|eimffz,89,191,1|eimfg0,91,193,0|eqjm3z,91,193,0|eqjm40,89,191,1|f1ci3z,89,191,1|f1ci40,91,193,0|f99orz,91,193,0|f99os0,89,191,1|fkfjfz,89,191,1|fkfjg0,91,193,0|frzrfz,91,193,0|frzrg0,89,191,1|g35m3z,89,191,1|g35m40,91,193,0|gapu3z,91,193,0|gapu40,89,191,1|glvorz,89,191,1|glvos0,91,193,0|gtsvfz,91,193,0|gtsvg0,89,191,1|h4lrfz,89,191,1|h4lrg0,91,193,0|hciy3z,91,193,0|hciy40,89,191,1|hnbu3z,89,191,1|hnbu40,91,193,0|hv90rz,91,193,0|hv90s0,89,191,1|i6evfz,89,191,1|i6evg0,91,193,0|idz3fz,91,193,0|idz3g0,89,191,1|ip4y3z,89,191,1|ip4y40,91,193,0|iwp63z,91,193,0|iwp640,89,191,1|j7v0rz,89,191,1|j7v0s0,91,193,0|jff8rz,91,193,0|jff8s0,89,191,1|jql3fz,89,191,1|jql3g0,91,193,0|jyia3z,91,193,0|jyia40,89,191,1|k9b63z,89,191,1|k9b640,91,193,0|kh8crz,91,193,0|kh8cs0,89,191,1|ks18rz,89,191,1|ks18s0,91,193,0|kzyffz,91,193,0|kzyfg0,89,191,1|lb4a3z,89,191,1|lb4a40,91,193,0|lioi3z,91,193,0|lioi40,89,191,0|ne0hzz,89,191,0|ne0i00,91,193,0\",\"Asia/Kuala_Lumpur|,0,261,0|-100ew5y,85,262,0|-xphpwe,85,262,0|-xphpwd,91,193,0|-jb6gs1,91,193,0|-jb6gs0,118,246,1|-hquppd,118,246,1|-hquppc,118,246,0|-esddpd,118,246,0|-esddpc,106,222,0|-ejqa61,106,222,0|-ejqa60,107,224,0|-conl01,107,224,0|-conl00,106,222,0|69g35z,106,222,0|69g360,89,191,0\",\"Asia/Kuching|,0,263,0|-mvof3k,106,222,0|-jb6i61,106,222,0|-jb6i60,89,191,0|-hwgm81,89,191,0|-hwgm80,128,264,1|-hrs4hd,128,264,1|-hrs4hc,89,191,0|-hdmu81,89,191,0|-hdmu80,128,264,1|-h8ychd,128,264,1|-h8ychc,89,191,0|-guuww1,89,191,0|-guuww0,128,264,1|-gq6f5d,128,264,1|-gq6f5c,89,191,0|-gc2zk1,89,191,0|-gc2zk0,128,264,1|-g7ehtd,128,264,1|-g7ehtc,89,191,0|-ftb281,89,191,0|-ftb280,128,264,1|-fomkhd,128,264,1|-fomkhc,89,191,0|-faha81,89,191,0|-faha80,128,264,1|-f5sshd,128,264,1|-f5sshc,89,191,0|-erpcw1,89,191,0|-erpcw0,128,264,1|-en0v5d,128,264,1|-en0v5c,89,191,0|-ejqbk1,89,191,0|-ejqbk0,107,224,0|-conl01,107,224,0|-conl00,89,191,0\",\"Asia/Kuwait|,0,203,0|-bwgbbg,100,6,0\",\"Asia/Macau|,0,265,0|-y0i2cy,45,191,0|-emm3o1,45,191,0|-emm3o0,107,224,0|-efxfs1,107,224,0|-efxfs0,93,195,1|-e5lak1,93,195,1|-e5lak0,107,224,0|-dx5ig1,107,224,0|-dx5ig0,93,195,1|-dpa981,93,195,1|-dpa980,107,224,0|-cnoec1,107,224,0|-cnoec0,45,191,0|-ccrt01,45,191,0|-ccrt00,46,224,1|-c4wh01,46,224,1|-c4wh00,45,191,0|-buk901,45,191,0|-buk900,46,224,1|-bizl01,46,224,1|-bizl00,45,191,0|-bb2ec1,45,191,0|-bb2ec0,46,224,1|-b1pd01,46,224,1|-b1pd00,45,191,0|-atu101,45,191,0|-atu100,46,224,1|-aj1501,46,224,1|-aj1500,45,191,0|-ab3yc1,45,191,0|-ab3yc0,46,224,1|-a0b2c1,46,224,1|-a0b2c0,45,191,0|-9sdvo1,45,191,0|-9sdvo0,46,224,1|-9hj501,46,224,1|-9hj500,45,191,0|-99auc1,45,191,0|-99auc0,46,224,1|-8yhyc1,46,224,1|-8yhyc0,45,191,0|-8qkro1,45,191,0|-8qkro0,46,224,1|-8frvo1,46,224,1|-8frvo0,45,191,0|-88kmc1,45,191,0|-88kmc0,46,224,1|-7x1t01,46,224,1|-7x1t00,45,191,0|-7pujo1,45,191,0|-7pujo0,46,224,1|-7dyro1,46,224,1|-7dyro0,45,191,0|-774h01,45,191,0|-774h00,46,224,1|-6v8fa1,46,224,1|-6v8fa0,45,191,0|-6o1361,45,191,0|-6o1360,46,224,1|-6cicm1,46,224,1|-6cicm0,45,191,0|-65b0i1,45,191,0|-65b0i0,46,224,1|-5ts9y1,46,224,1|-5ts9y0,45,191,0|-5mkxu1,45,191,0|-5mkxu0,46,224,1|-5b27a1,46,224,1|-5b27a0,45,191,0|-53uv61,45,191,0|-53uv60,46,224,1|-4rz5y1,46,224,1|-4rz5y0,45,191,0|-4l4si1,45,191,0|-4l4si0,46,224,1|-4993a1,46,224,1|-4993a0,45,191,0|-42epu1,45,191,0|-42epu0,46,224,1|-3qj0m1,46,224,1|-3qj0m0,45,191,0|-3jboi1,45,191,0|-3jboi0,46,224,1|-37sxy1,46,224,1|-37sxy0,45,191,0|-30llu1,45,191,0|-30llu0,46,224,1|-2p2va1,46,224,1|-2p2va0,45,191,0|-2gfoi1,45,191,0|-2gfoi0,46,224,1|-272sq1,46,224,1|-272sq0,45,191,0|-1xplu1,45,191,0|-1xplu0,46,224,1|-1ocq21,46,224,1|-1ocq20,45,191,0|-1ezj61,45,191,0|-1ezj60,46,224,1|-159ly1,46,224,1|-159ly0,45,191,0|-vwhu1,45,191,0|-vwhu0,46,224,1|-mjja1,46,224,1|-mjja0,45,191,0|-d6f61,45,191,0|-d6f60,46,224,1|-3tgm1,46,224,1|-3tgm0,45,191,0|5jnhz,45,191,0|5jni0,46,224,1|ewm1z,46,224,1|ewm20,45,191,0|o9q5z,45,191,0|o9q60,46,224,1|xmopz,46,224,1|xmoq0,45,191,0|16zstz,45,191,0|16zsu0,46,224,1|1gpq1z,46,224,1|1gpq20,45,191,0|1q2u5z,45,191,0|1q2u60,46,224,1|1zfspz,46,224,1|1zfsq0,45,191,0|231i5z,45,191,0|231i60,46,224,1|2i5vdz,46,224,1|2i5ve0,45,191,0|2rizhz,45,191,0|2rizi0,46,224,1|30vy1z,46,224,1|30vy20,45,191,0|3a925z,45,191,0|3a9260,46,224,1|3jm0pz,46,224,1|3jm0q0,45,191,0|4vv4tz,45,191,0|4vv4u0,46,224,1|5457dz,46,224,1|5457e0,45,191,0\",\"Asia/Magadan|,0,266,0|-nu1nxc,93,195,0|-kmrns1,93,195,0|-kmrns0,90,192,0|5vak3z,90,192,0|5vak40,102,200,1|64pdbz,102,200,1|64pdc0,90,192,0|6e2hfz,90,192,0|6e2hg0,102,200,1|6nhanz,102,200,1|6nhao0,90,192,0|6wuerz,90,192,0|6wues0,102,200,1|7697zz,102,200,1|769800,90,192,0|7fo6rz,90,192,0|7fo6s0,102,200,1|7p1dnz,102,200,1|7p1do0,90,192,0|7yeezz,90,192,0|7yef00,102,200,1|87rgbz,102,200,1|87rgc0,90,192,0|8h4hnz,90,192,0|8h4ho0,102,200,1|8qhizz,102,200,1|8qhj00,90,192,0|8zukbz,90,192,0|8zukc0,102,200,1|997lnz,102,200,1|997lo0,90,192,0|9ikmzz,90,192,0|9ikn00,102,200,1|9rxobz,102,200,1|9rxoc0,90,192,0|a1apnz,90,192,0|a1apo0,102,200,1|aanqzz,102,200,1|aanr00,90,192,0|ak0sbz,90,192,0|ak0sc0,102,200,1|atqsbz,102,200,1|atqsc0,90,192,0|b33tnz,90,192,0|b33to0,90,192,1|bcgxrz,90,192,1|bcgxs0,93,195,0|bi8cfz,93,195,0|bi8cg0,90,192,0|bltwbz,90,192,0|bltwc0,102,200,1|bv6xnz,102,200,1|bv6xo0,90,192,0|c4jyzz,90,192,0|c4jz00,102,200,1|cdx0bz,102,200,1|cdx0c0,90,192,0|cna1nz,90,192,0|cna1o0,102,200,1|cwn2zz,102,200,1|cwn300,90,192,0|d604bz,90,192,0|d604c0,102,200,1|dfd5nz,102,200,1|dfd5o0,90,192,0|dp35nz,90,192,0|dp35o0,102,200,1|dzw1nz,102,200,1|dzw1o0,90,192,0|e7t8bz,90,192,0|e7t8c0,102,200,1|eim4bz,102,200,1|eim4c0,90,192,0|eqjazz,90,192,0|eqjb00,102,200,1|f1c6zz,102,200,1|f1c700,90,192,0|f99dnz,90,192,0|f99do0,102,200,1|fkf8bz,102,200,1|fkf8c0,90,192,0|frzgbz,90,192,0|frzgc0,102,200,1|g35azz,102,200,1|g35b00,90,192,0|gapizz,90,192,0|gapj00,102,200,1|glvdnz,102,200,1|glvdo0,90,192,0|gtskbz,90,192,0|gtskc0,102,200,1|h4lgbz,102,200,1|h4lgc0,90,192,0|hcimzz,90,192,0|hcin00,102,200,1|hnbizz,102,200,1|hnbj00,90,192,0|hv8pnz,90,192,0|hv8po0,102,200,1|i6ekbz,102,200,1|i6ekc0,90,192,0|idysbz,90,192,0|idysc0,102,200,1|ip4mzz,102,200,1|ip4n00,90,192,0|iwouzz,90,192,0|iwov00,102,200,1|j7upnz,102,200,1|j7upo0,90,192,0|jfexnz,90,192,0|jfexo0,102,200,1|jqksbz,102,200,1|jqksc0,90,192,0|jyhyzz,90,192,0|jyhz00,102,200,1|k9auzz,102,200,1|k9av00,90,192,0|kh81nz,90,192,0|kh81o0,102,200,1|ks0xnz,102,200,1|ks0xo0,90,192,0|kzy4bz,90,192,0|kzy4c0,102,200,1|lb3yzz,102,200,1|lb3z00,90,192,0|lio6zz,90,192,0|lio700,102,200,0|ne06vz,102,200,0|ne06w0,93,195,0|o63gfz,93,195,0|o63gg0,90,192,0\",\"Asia/Makassar|,0,267,0|-q3gzg0,21,267,0|-jebi41,21,267,0|-jebi40,89,191,0|-ek3a81,89,191,0|-ek3a80,107,224,0|-co37o1,107,224,0|-co37o0,129,191,0\",\"Asia/Manila|,0,268,0|-1t8ix2o,0,269,0|-10va3qp,0,269,0|-10va3qo,51,191,0|-hb5y81,51,191,0|-hb5y80,57,224,1|-h6fno1,57,224,1|-h6fno0,51,191,0|-efxa81,51,191,0|-efxa80,116,224,0|-d4ux01,116,224,0|-d4ux00,51,191,0|-87fsw1,51,191,0|-87fsw0,57,224,1|-83bqc1,57,224,1|-83bqc0,51,191,0|4aen3z,51,191,0|4aen40,57,224,1|4jtgbz,57,224,1|4jtgc0,51,191,0\",\"Asia/Muscat|,0,234,0|-q3gnko,105,209,0\",\"Asia/Nicosia|,0,270,0|-p4bq6g,15,11,0|2r67rz,15,11,0|2r67s0,16,6,1|30j6bz,16,6,1|30j6c0,15,11,0|3bn93z,15,11,0|3bn940,16,6,1|3jb3nz,16,6,1|3jb3o0,15,11,0|3s9efz,15,11,0|3s9eg0,16,6,1|419ebz,16,6,1|419ec0,15,11,0|4azh3z,15,11,0|4azh40,16,6,1|4keabz,16,6,1|4keac0,15,11,0|4tpjrz,15,11,0|4tpjs0,16,6,1|532ibz,16,6,1|532ic0,15,11,0|5csl3z,15,11,0|5csl40,16,6,1|5lskzz,16,6,1|5lsl00,15,11,0|5v5p3z,15,11,0|5v5p40,16,6,1|64innz,16,6,1|64ino0,15,11,0|6dvrrz,15,11,0|6dvrs0,16,6,1|6n8qbz,16,6,1|6n8qc0,15,11,0|6wlufz,15,11,0|6wlug0,16,6,1|75yszz,16,6,1|75yt00,15,11,0|7fbx3z,15,11,0|7fbx40,16,6,1|7p1ubz,16,6,1|7p1uc0,15,11,0|7yeyfz,15,11,0|7yeyg0,16,6,1|87rwzz,16,6,1|87rx00,15,11,0|8h513z,15,11,0|8h5140,16,6,1|8qhznz,16,6,1|8qhzo0,15,11,0|8zv3rz,15,11,0|8zv3s0,16,6,1|9982bz,16,6,1|9982c0,15,11,0|9il6fz,15,11,0|9il6g0,16,6,1|9ry4zz,16,6,1|9ry500,15,11,0|a1b93z,15,11,0|a1b940,16,6,1|aao7nz,16,6,1|aao7o0,15,11,0|ak1brz,15,11,0|ak1bs0,16,6,1|atr8zz,16,6,1|atr900,15,11,0|b34d3z,15,11,0|b34d40,16,6,1|bchbnz,16,6,1|bchbo0,15,11,0|blufrz,15,11,0|blufs0,16,6,1|bv7ebz,16,6,1|bv7ec0,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxgzz,16,6,1|cdxh00,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cwnjnz,16,6,1|cwnjo0,15,11,0|d60nrz,15,11,0|d60ns0,16,6,1|dfdmbz,16,6,1|dfdmc0,15,11,0|dp3p3z,15,11,0|dp3p40,16,6,1|dygnnz,16,6,1|dygno0,15,11,0|e7trrz,15,11,0|e7trs0,16,6,1|eh6qbz,16,6,1|eh6qc0,15,11,0|eqjufz,15,11,0|eqjug0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Asia/Novokuznetsk|,0,271,0|-nu36tc,96,196,0|-kmrco1,96,196,0|-kmrco0,91,193,0|5vav7z,91,193,0|5vav80,89,191,1|64pofz,89,191,1|64pog0,91,193,0|6e2sjz,91,193,0|6e2sk0,89,191,1|6nhlrz,89,191,1|6nhls0,91,193,0|6wupvz,91,193,0|6wupw0,89,191,1|769j3z,89,191,1|769j40,91,193,0|7fohvz,91,193,0|7fohw0,89,191,1|7p1orz,89,191,1|7p1os0,91,193,0|7yeq3z,91,193,0|7yeq40,89,191,1|87rrfz,89,191,1|87rrg0,91,193,0|8h4srz,91,193,0|8h4ss0,89,191,1|8qhu3z,89,191,1|8qhu40,91,193,0|8zuvfz,91,193,0|8zuvg0,89,191,1|997wrz,89,191,1|997ws0,91,193,0|9iky3z,91,193,0|9iky40,89,191,1|9rxzfz,89,191,1|9rxzg0,91,193,0|a1b0rz,91,193,0|a1b0s0,89,191,1|aao23z,89,191,1|aao240,91,193,0|ak13fz,91,193,0|ak13g0,89,191,1|atr3fz,89,191,1|atr3g0,91,193,0|b344rz,91,193,0|b344s0,91,193,1|bch8vz,91,193,1|bch8w0,96,196,0|bi8njz,96,196,0|bi8nk0,91,193,0|blu7fz,91,193,0|blu7g0,89,191,1|bv78rz,89,191,1|bv78s0,91,193,0|c4ka3z,91,193,0|c4ka40,89,191,1|cdxbfz,89,191,1|cdxbg0,91,193,0|cnacrz,91,193,0|cnacs0,89,191,1|cwne3z,89,191,1|cwne40,91,193,0|d60ffz,91,193,0|d60fg0,89,191,1|dfdgrz,89,191,1|dfdgs0,91,193,0|dp3grz,91,193,0|dp3gs0,89,191,1|dzwcrz,89,191,1|dzwcs0,91,193,0|e7tjfz,91,193,0|e7tjg0,89,191,1|eimffz,89,191,1|eimfg0,91,193,0|eqjm3z,91,193,0|eqjm40,89,191,1|f1ci3z,89,191,1|f1ci40,91,193,0|f99orz,91,193,0|f99os0,89,191,1|fkfjfz,89,191,1|fkfjg0,91,193,0|frzrfz,91,193,0|frzrg0,89,191,1|g35m3z,89,191,1|g35m40,91,193,0|gapu3z,91,193,0|gapu40,89,191,1|glvorz,89,191,1|glvos0,91,193,0|gtsvfz,91,193,0|gtsvg0,89,191,1|h4lrfz,89,191,1|h4lrg0,91,193,0|hciy3z,91,193,0|hciy40,89,191,1|hnbu3z,89,191,1|hnbu40,91,193,0|hv90rz,91,193,0|hv90s0,89,191,1|i6evfz,89,191,1|i6evg0,91,193,0|idz3fz,91,193,0|idz3g0,89,191,1|ip4y3z,89,191,1|ip4y40,91,193,0|iwp63z,91,193,0|iwp640,89,191,1|j7v0rz,89,191,1|j7v0s0,91,193,0|jff8rz,91,193,0|jff8s0,89,191,1|jql3fz,89,191,1|jql3g0,91,193,0|jyia3z,91,193,0|jyia40,89,191,1|k9b63z,89,191,1|k9b640,91,193,0|kh8crz,91,193,0|kh8cs0,89,191,1|ks18rz,89,191,1|ks18s0,91,193,0|kzyffz,91,193,0|kzyfg0,91,193,1|lb4cvz,91,193,1|lb4cw0,96,196,0|liokvz,96,196,0|liokw0,91,193,0\",\"Asia/Novosibirsk|,0,272,0|-q4do0s,96,196,0|-kmrco1,96,196,0|-kmrco0,91,193,0|5vav7z,91,193,0|5vav80,89,191,1|64pofz,89,191,1|64pog0,91,193,0|6e2sjz,91,193,0|6e2sk0,89,191,1|6nhlrz,89,191,1|6nhls0,91,193,0|6wupvz,91,193,0|6wupw0,89,191,1|769j3z,89,191,1|769j40,91,193,0|7fohvz,91,193,0|7fohw0,89,191,1|7p1orz,89,191,1|7p1os0,91,193,0|7yeq3z,91,193,0|7yeq40,89,191,1|87rrfz,89,191,1|87rrg0,91,193,0|8h4srz,91,193,0|8h4ss0,89,191,1|8qhu3z,89,191,1|8qhu40,91,193,0|8zuvfz,91,193,0|8zuvg0,89,191,1|997wrz,89,191,1|997ws0,91,193,0|9iky3z,91,193,0|9iky40,89,191,1|9rxzfz,89,191,1|9rxzg0,91,193,0|a1b0rz,91,193,0|a1b0s0,89,191,1|aao23z,89,191,1|aao240,91,193,0|ak13fz,91,193,0|ak13g0,89,191,1|atr3fz,89,191,1|atr3g0,91,193,0|b344rz,91,193,0|b344s0,91,193,1|bch8vz,91,193,1|bch8w0,96,196,0|bi8njz,96,196,0|bi8nk0,91,193,0|blu7fz,91,193,0|blu7g0,89,191,1|bv78rz,89,191,1|bv78s0,91,193,0|c4ka3z,91,193,0|c4ka40,89,191,1|c7fr3z,89,191,1|c7fr40,91,193,1|cdxe7z,91,193,1|cdxe80,96,196,0|cnafjz,96,196,0|cnafk0,91,193,1|cwngvz,91,193,1|cwngw0,96,196,0|d60i7z,96,196,0|d60i80,91,193,1|dfdjjz,91,193,1|dfdjk0,96,196,0|dp3jjz,96,196,0|dp3jk0,91,193,1|dzwfjz,91,193,1|dzwfk0,96,196,0|e7tm7z,96,196,0|e7tm80,91,193,1|eimi7z,91,193,1|eimi80,96,196,0|eqjovz,96,196,0|eqjow0,91,193,1|f1ckvz,91,193,1|f1ckw0,96,196,0|f99rjz,96,196,0|f99rk0,91,193,1|fkfm7z,91,193,1|fkfm80,96,196,0|frzu7z,96,196,0|frzu80,91,193,1|g35ovz,91,193,1|g35ow0,96,196,0|gapwvz,96,196,0|gapww0,91,193,1|glvrjz,91,193,1|glvrk0,96,196,0|gtsy7z,96,196,0|gtsy80,91,193,1|h4lu7z,91,193,1|h4lu80,96,196,0|hcj0vz,96,196,0|hcj0w0,91,193,1|hnbwvz,91,193,1|hnbww0,96,196,0|hv93jz,96,196,0|hv93k0,91,193,1|i6ey7z,91,193,1|i6ey80,96,196,0|idz67z,96,196,0|idz680,91,193,1|ip50vz,91,193,1|ip50w0,96,196,0|iwp8vz,96,196,0|iwp8w0,91,193,1|j7v3jz,91,193,1|j7v3k0,96,196,0|jffbjz,96,196,0|jffbk0,91,193,1|jql67z,91,193,1|jql680,96,196,0|jyicvz,96,196,0|jyicw0,91,193,1|k9b8vz,91,193,1|k9b8w0,96,196,0|kh8fjz,96,196,0|kh8fk0,91,193,1|ks1bjz,91,193,1|ks1bk0,96,196,0|kzyi7z,96,196,0|kzyi80,91,193,1|lb4cvz,91,193,1|lb4cw0,96,196,0|liokvz,96,196,0|liokw0,91,193,0|ne0krz,91,193,0|ne0ks0,96,196,0|oasa7z,96,196,0|oasa80,91,193,0\",\"Asia/Omsk|,0,273,0|-q5xmx6,92,194,0|-kmr9w1,92,194,0|-kmr9w0,96,196,0|5vaxzz,96,196,0|5vay00,91,193,1|64pr7z,91,193,1|64pr80,96,196,0|6e2vbz,96,196,0|6e2vc0,91,193,1|6nhojz,91,193,1|6nhok0,96,196,0|6wusnz,96,196,0|6wuso0,91,193,1|769lvz,91,193,1|769lw0,96,196,0|7foknz,96,196,0|7foko0,91,193,1|7p1rjz,91,193,1|7p1rk0,96,196,0|7yesvz,96,196,0|7yesw0,91,193,1|87ru7z,91,193,1|87ru80,96,196,0|8h4vjz,96,196,0|8h4vk0,91,193,1|8qhwvz,91,193,1|8qhww0,96,196,0|8zuy7z,96,196,0|8zuy80,91,193,1|997zjz,91,193,1|997zk0,96,196,0|9il0vz,96,196,0|9il0w0,91,193,1|9ry27z,91,193,1|9ry280,96,196,0|a1b3jz,96,196,0|a1b3k0,91,193,1|aao4vz,91,193,1|aao4w0,96,196,0|ak167z,96,196,0|ak1680,91,193,1|atr67z,91,193,1|atr680,96,196,0|b347jz,96,196,0|b347k0,96,196,1|bchbnz,96,196,1|bchbo0,92,194,0|bi8qbz,92,194,0|bi8qc0,96,196,0|blua7z,96,196,0|blua80,91,193,1|bv7bjz,91,193,1|bv7bk0,96,196,0|c4kcvz,96,196,0|c4kcw0,91,193,1|cdxe7z,91,193,1|cdxe80,96,196,0|cnafjz,96,196,0|cnafk0,91,193,1|cwngvz,91,193,1|cwngw0,96,196,0|d60i7z,96,196,0|d60i80,91,193,1|dfdjjz,91,193,1|dfdjk0,96,196,0|dp3jjz,96,196,0|dp3jk0,91,193,1|dzwfjz,91,193,1|dzwfk0,96,196,0|e7tm7z,96,196,0|e7tm80,91,193,1|eimi7z,91,193,1|eimi80,96,196,0|eqjovz,96,196,0|eqjow0,91,193,1|f1ckvz,91,193,1|f1ckw0,96,196,0|f99rjz,96,196,0|f99rk0,91,193,1|fkfm7z,91,193,1|fkfm80,96,196,0|frzu7z,96,196,0|frzu80,91,193,1|g35ovz,91,193,1|g35ow0,96,196,0|gapwvz,96,196,0|gapww0,91,193,1|glvrjz,91,193,1|glvrk0,96,196,0|gtsy7z,96,196,0|gtsy80,91,193,1|h4lu7z,91,193,1|h4lu80,96,196,0|hcj0vz,96,196,0|hcj0w0,91,193,1|hnbwvz,91,193,1|hnbww0,96,196,0|hv93jz,96,196,0|hv93k0,91,193,1|i6ey7z,91,193,1|i6ey80,96,196,0|idz67z,96,196,0|idz680,91,193,1|ip50vz,91,193,1|ip50w0,96,196,0|iwp8vz,96,196,0|iwp8w0,91,193,1|j7v3jz,91,193,1|j7v3k0,96,196,0|jffbjz,96,196,0|jffbk0,91,193,1|jql67z,91,193,1|jql680,96,196,0|jyicvz,96,196,0|jyicw0,91,193,1|k9b8vz,91,193,1|k9b8w0,96,196,0|kh8fjz,96,196,0|kh8fk0,91,193,1|ks1bjz,91,193,1|ks1bk0,96,196,0|kzyi7z,96,196,0|kzyi80,91,193,1|lb4cvz,91,193,1|lb4cw0,96,196,0|liokvz,96,196,0|liokw0,91,193,0|ne0krz,91,193,0|ne0ks0,96,196,0\",\"Asia/Oral|,0,274,0|-nu15ic,100,6,0|-kmr4c1,100,6,0|-kmr4c0,92,194,0|5vb0rz,92,194,0|5vb0s0,96,196,1|64ptzz,96,196,1|64pu00,96,196,0|6e2vbz,96,196,0|6e2vc0,96,196,1|6nhrbz,96,196,1|6nhrc0,92,194,0|6wuvfz,92,194,0|6wuvg0,96,196,1|769onz,96,196,1|769oo0,92,194,0|7fonfz,92,194,0|7fong0,96,196,1|7p1ubz,96,196,1|7p1uc0,92,194,0|7yevnz,92,194,0|7yevo0,96,196,1|87rwzz,96,196,1|87rx00,92,194,0|8h4ybz,92,194,0|8h4yc0,96,196,1|8qhznz,96,196,1|8qhzo0,92,194,0|8zv0zz,92,194,0|8zv100,96,196,1|9982bz,96,196,1|9982c0,92,194,0|9il3nz,92,194,0|9il3o0,96,196,1|9ry4zz,96,196,1|9ry500,92,194,0|a1b6bz,92,194,0|a1b6c0,92,194,1|aaoafz,92,194,1|aaoag0,105,209,0|ak1brz,105,209,0|ak1bs0,92,194,1|atrbrz,92,194,1|atrbs0,105,209,0|b34d3z,105,209,0|b34d40,92,194,1|bchefz,92,194,1|bcheg0,105,209,0|bi8t3z,105,209,0|bi8t40,92,194,0|bluczz,92,194,0|blud00,92,194,1|bv7h3z,92,194,1|bv7h40,105,209,0|c4kifz,105,209,0|c4kig0,92,194,1|cdxjrz,92,194,1|cdxjs0,105,209,0|cnal3z,105,209,0|cnal40,92,194,1|cwnmfz,92,194,1|cwnmg0,105,209,0|d60nrz,105,209,0|d60ns0,92,194,1|dfdp3z,92,194,1|dfdp40,105,209,0|dp3p3z,105,209,0|dp3p40,92,194,1|dzwl3z,92,194,1|dzwl40,105,209,0|e7trrz,105,209,0|e7trs0,92,194,1|eimnrz,92,194,1|eimns0,105,209,0|eqjufz,105,209,0|eqjug0,92,194,1|f1cqfz,92,194,1|f1cqg0,105,209,0|f99x3z,105,209,0|f99x40,92,194,1|fkfrrz,92,194,1|fkfrs0,105,209,0|frzzrz,105,209,0|frzzs0,92,194,1|g35ufz,92,194,1|g35ug0,105,209,0|gaq2fz,105,209,0|gaq2g0,92,194,1|glvx3z,92,194,1|glvx40,105,209,0|gtt3rz,105,209,0|gtt3s0,92,194,1|h4lzrz,92,194,1|h4lzs0,105,209,0|hcj6fz,105,209,0|hcj6g0,92,194,1|hnc2fz,92,194,1|hnc2g0,105,209,0|hv993z,105,209,0|hv9940,92,194,1|i6f3rz,92,194,1|i6f3s0,92,194,0\",\"Asia/Phnom_Penh|,0,217,0|-1ayyla4,53,217,0|-pysda5,53,217,0|-pysda4,91,193,0\",\"Asia/Pontianak|,0,275,0|-w6piww,7,275,0|-jebg8x,7,275,0|-jebg8w,106,222,0|-eknm61,106,222,0|-eknm60,107,224,0|-co37o1,107,224,0|-co37o0,106,222,0|-bb5zi1,106,222,0|-bb5zi0,89,191,0|-a9m681,89,191,0|-a9m680,106,222,0|-34ru61,106,222,0|-34ru60,129,191,0|9e5gfz,129,191,0|9e5gg0,119,193,0\",\"Asia/Pyongyang|,0,276,0|-w895yc,130,242,0|-u9s4y1,130,242,0|-u9s4y0,116,224,0|-cpmro1,116,224,0|-cpmro0,130,224,0|nt2uzz,130,224,0|nt2v00,130,242,0|p87lnz,130,242,0|p87lo0,130,224,0\",\"Asia/Qatar|,0,215,0|-q3gmvk,105,209,0|19d0vz,105,209,0|19d0w0,100,6,0\",\"Asia/Qostanay|,0,277,0|-nu17s4,105,209,0|-kmr741,105,209,0|-kmr740,92,194,0|5vb0rz,92,194,0|5vb0s0,96,196,1|64ptzz,96,196,1|64pu00,96,196,0|6e2vbz,96,196,0|6e2vc0,96,196,1|6nhrbz,96,196,1|6nhrc0,92,194,0|6wuvfz,92,194,0|6wuvg0,96,196,1|769onz,96,196,1|769oo0,92,194,0|7fonfz,92,194,0|7fong0,96,196,1|7p1ubz,96,196,1|7p1uc0,92,194,0|7yevnz,92,194,0|7yevo0,96,196,1|87rwzz,96,196,1|87rx00,92,194,0|8h4ybz,92,194,0|8h4yc0,96,196,1|8qhznz,96,196,1|8qhzo0,92,194,0|8zv0zz,92,194,0|8zv100,96,196,1|9982bz,96,196,1|9982c0,92,194,0|9il3nz,92,194,0|9il3o0,96,196,1|9ry4zz,96,196,1|9ry500,92,194,0|a1b6bz,92,194,0|a1b6c0,96,196,1|aao7nz,96,196,1|aao7o0,92,194,0|ak18zz,92,194,0|ak1900,96,196,1|atr8zz,96,196,1|atr900,92,194,0|b34abz,92,194,0|b34ac0,92,194,1|bchefz,92,194,1|bcheg0,105,209,0|bi8t3z,105,209,0|bi8t40,92,194,0|bluczz,92,194,0|blud00,96,196,1|bv7ebz,96,196,1|bv7ec0,92,194,0|c4kfnz,92,194,0|c4kfo0,96,196,1|cdxgzz,96,196,1|cdxh00,92,194,0|cnaibz,92,194,0|cnaic0,96,196,1|cwnjnz,96,196,1|cwnjo0,92,194,0|d60kzz,92,194,0|d60l00,96,196,1|dfdmbz,96,196,1|dfdmc0,92,194,0|dp3mbz,92,194,0|dp3mc0,96,196,1|dzwibz,96,196,1|dzwic0,92,194,0|e7tozz,92,194,0|e7tp00,96,196,1|eimkzz,96,196,1|eiml00,92,194,0|eqjrnz,92,194,0|eqjro0,96,196,1|f1cnnz,96,196,1|f1cno0,92,194,0|f99ubz,92,194,0|f99uc0,96,196,1|fkfozz,96,196,1|fkfp00,92,194,0|frzwzz,92,194,0|frzx00,96,196,1|g35rnz,96,196,1|g35ro0,92,194,0|gapznz,92,194,0|gapzo0,96,196,1|glvubz,96,196,1|glvuc0,92,194,0|gtt0zz,92,194,0|gtt100,96,196,1|h4lwzz,96,196,1|h4lx00,92,194,0|hcj3nz,92,194,0|hcj3o0,96,196,1|hnbznz,96,196,1|hnbzo0,92,194,0|hv96bz,92,194,0|hv96c0,96,196,1|i6f0zz,96,196,1|i6f100,96,196,0\",\"Asia/Qyzylorda|,0,278,0|-nu184g,105,209,0|-kmr741,105,209,0|-kmr740,92,194,0|5vb0rz,92,194,0|5vb0s0,96,196,1|64ptzz,96,196,1|64pu00,96,196,0|6e2vbz,96,196,0|6e2vc0,96,196,1|6nhrbz,96,196,1|6nhrc0,92,194,0|6wuvfz,92,194,0|6wuvg0,96,196,1|769onz,96,196,1|769oo0,92,194,0|7fonfz,92,194,0|7fong0,96,196,1|7p1ubz,96,196,1|7p1uc0,92,194,0|7yevnz,92,194,0|7yevo0,96,196,1|87rwzz,96,196,1|87rx00,92,194,0|8h4ybz,92,194,0|8h4yc0,96,196,1|8qhznz,96,196,1|8qhzo0,92,194,0|8zv0zz,92,194,0|8zv100,96,196,1|9982bz,96,196,1|9982c0,92,194,0|9il3nz,92,194,0|9il3o0,96,196,1|9ry4zz,96,196,1|9ry500,92,194,0|a1b6bz,92,194,0|a1b6c0,96,196,1|aao7nz,96,196,1|aao7o0,92,194,0|ak18zz,92,194,0|ak1900,96,196,1|atr8zz,96,196,1|atr900,92,194,0|b34abz,92,194,0|b34ac0,92,194,1|bchefz,92,194,1|bcheg0,92,194,0|bi8qbz,92,194,0|bi8qc0,96,196,0|blua7z,96,196,0|blua80,96,196,1|bv7ebz,96,196,1|bv7ec0,92,194,0|c4kfnz,92,194,0|c4kfo0,96,196,1|cdxgzz,96,196,1|cdxh00,92,194,0|cnaibz,92,194,0|cnaic0,96,196,1|cwnjnz,96,196,1|cwnjo0,92,194,0|d60kzz,92,194,0|d60l00,96,196,1|dfdmbz,96,196,1|dfdmc0,92,194,0|dp3mbz,92,194,0|dp3mc0,96,196,1|dzwibz,96,196,1|dzwic0,92,194,0|e7tozz,92,194,0|e7tp00,96,196,1|eimkzz,96,196,1|eiml00,92,194,0|eqjrnz,92,194,0|eqjro0,96,196,1|f1cnnz,96,196,1|f1cno0,92,194,0|f99ubz,92,194,0|f99uc0,96,196,1|fkfozz,96,196,1|fkfp00,92,194,0|frzwzz,92,194,0|frzx00,96,196,1|g35rnz,96,196,1|g35ro0,92,194,0|gapznz,92,194,0|gapzo0,96,196,1|glvubz,96,196,1|glvuc0,92,194,0|gtt0zz,92,194,0|gtt100,96,196,1|h4lwzz,96,196,1|h4lx00,92,194,0|hcj3nz,92,194,0|hcj3o0,96,196,1|hnbznz,96,196,1|hnbzo0,92,194,0|hv96bz,92,194,0|hv96c0,96,196,1|i6f0zz,96,196,1|i6f100,96,196,0|pk1rbz,96,196,0|pk1rc0,92,194,0\",\"Asia/Riyadh|,0,203,0|-bwgbbg,100,6,0\",\"Asia/Sakhalin|,0,279,0|-xl87rc,107,224,0|-cpkx01,107,224,0|-cpkx00,90,192,0|5vak3z,90,192,0|5vak40,102,200,1|64pdbz,102,200,1|64pdc0,90,192,0|6e2hfz,90,192,0|6e2hg0,102,200,1|6nhanz,102,200,1|6nhao0,90,192,0|6wuerz,90,192,0|6wues0,102,200,1|7697zz,102,200,1|769800,90,192,0|7fo6rz,90,192,0|7fo6s0,102,200,1|7p1dnz,102,200,1|7p1do0,90,192,0|7yeezz,90,192,0|7yef00,102,200,1|87rgbz,102,200,1|87rgc0,90,192,0|8h4hnz,90,192,0|8h4ho0,102,200,1|8qhizz,102,200,1|8qhj00,90,192,0|8zukbz,90,192,0|8zukc0,102,200,1|997lnz,102,200,1|997lo0,90,192,0|9ikmzz,90,192,0|9ikn00,102,200,1|9rxobz,102,200,1|9rxoc0,90,192,0|a1apnz,90,192,0|a1apo0,102,200,1|aanqzz,102,200,1|aanr00,90,192,0|ak0sbz,90,192,0|ak0sc0,102,200,1|atqsbz,102,200,1|atqsc0,90,192,0|b33tnz,90,192,0|b33to0,90,192,1|bcgxrz,90,192,1|bcgxs0,93,195,0|bi8cfz,93,195,0|bi8cg0,90,192,0|bltwbz,90,192,0|bltwc0,102,200,1|bv6xnz,102,200,1|bv6xo0,90,192,0|c4jyzz,90,192,0|c4jz00,102,200,1|cdx0bz,102,200,1|cdx0c0,90,192,0|cna1nz,90,192,0|cna1o0,102,200,1|cwn2zz,102,200,1|cwn300,90,192,0|d604bz,90,192,0|d604c0,102,200,1|dfd5nz,102,200,1|dfd5o0,90,192,0|dp35nz,90,192,0|dp35o0,102,200,1|dzw1nz,102,200,1|dzw1o0,90,192,0|e7t8bz,90,192,0|e7t8c0,90,192,1|eim73z,90,192,1|eim740,93,195,0|eqjdrz,93,195,0|eqjds0,90,192,1|f1c9rz,90,192,1|f1c9s0,93,195,0|f99gfz,93,195,0|f99gg0,90,192,1|fkfb3z,90,192,1|fkfb40,93,195,0|frzj3z,93,195,0|frzj40,90,192,1|g35drz,90,192,1|g35ds0,93,195,0|gaplrz,93,195,0|gapls0,90,192,1|glvgfz,90,192,1|glvgg0,93,195,0|gtsn3z,93,195,0|gtsn40,90,192,1|h4lj3z,90,192,1|h4lj40,93,195,0|hciprz,93,195,0|hcips0,90,192,1|hnblrz,90,192,1|hnbls0,93,195,0|hv8sfz,93,195,0|hv8sg0,90,192,1|i6en3z,90,192,1|i6en40,93,195,0|idyv3z,93,195,0|idyv40,90,192,1|ip4prz,90,192,1|ip4ps0,93,195,0|iwoxrz,93,195,0|iwoxs0,90,192,1|j7usfz,90,192,1|j7usg0,93,195,0|jff0fz,93,195,0|jff0g0,90,192,1|jqkv3z,90,192,1|jqkv40,93,195,0|jyi1rz,93,195,0|jyi1s0,90,192,1|k9axrz,90,192,1|k9axs0,93,195,0|kh84fz,93,195,0|kh84g0,90,192,1|ks10fz,90,192,1|ks10g0,93,195,0|kzy73z,93,195,0|kzy740,90,192,1|lb41rz,90,192,1|lb41s0,93,195,0|lio9rz,93,195,0|lio9s0,90,192,0|ne09nz,90,192,0|ne09o0,93,195,0|o4nlrz,93,195,0|o4nls0,90,192,0\",\"Asia/Samarkand|,0,280,0|-nu18eh,105,209,0|-kmr741,105,209,0|-kmr740,92,194,0|5vb0rz,92,194,0|5vb0s0,96,196,1|64ptzz,96,196,1|64pu00,96,196,0|6e2vbz,96,196,0|6e2vc0,96,196,1|6nhrbz,96,196,1|6nhrc0,92,194,0|6wuvfz,92,194,0|6wuvg0,96,196,1|769onz,96,196,1|769oo0,92,194,0|7fonfz,92,194,0|7fong0,96,196,1|7p1ubz,96,196,1|7p1uc0,92,194,0|7yevnz,92,194,0|7yevo0,96,196,1|87rwzz,96,196,1|87rx00,92,194,0|8h4ybz,92,194,0|8h4yc0,96,196,1|8qhznz,96,196,1|8qhzo0,92,194,0|8zv0zz,92,194,0|8zv100,96,196,1|9982bz,96,196,1|9982c0,92,194,0|9il3nz,92,194,0|9il3o0,96,196,1|9ry4zz,96,196,1|9ry500,92,194,0|a1b6bz,92,194,0|a1b6c0,96,196,1|aao7nz,96,196,1|aao7o0,92,194,0|ak18zz,92,194,0|ak1900,96,196,1|atr8zz,96,196,1|atr900,92,194,0|b34abz,92,194,0|b34ac0,96,196,1|bchbnz,96,196,1|bchbo0,92,194,0\",\"Asia/Seoul|,0,281,0|-w8966g,130,242,0|-u9s4y1,130,242,0|-u9s4y0,116,224,0|-couzo1,116,224,0|-couzo0,130,224,0|-b9kp01,130,224,0|-b9kp00,131,195,1|-b486g1,131,195,1|-b486g0,130,224,0|-atu101,130,224,0|-atu100,131,195,1|-aljyg1,131,195,1|-aljyg0,130,224,0|-ab5t01,130,224,0|-ab5t00,131,195,1|-a2tvs1,131,195,1|-a2tvs0,130,224,0|-9ql2c1,130,224,0|-9ql2c0,131,195,1|-9k3t41,131,195,1|-9k3t40,130,224,0|-88kmc1,130,224,0|-88kmc0,130,242,0|-7nhbm1,130,242,0|-7nhbm0,131,248,1|-7gy7q1,131,248,1|-7gy7q0,130,242,0|-73vrm1,130,242,0|-73vrm0,131,248,1|-6x1jq1,131,248,1|-6x1jq0,130,242,0|-6lvma1,130,242,0|-6lvma0,131,248,1|-6eofq1,131,248,1|-6eofq0,130,242,0|-635jm1,130,242,0|-635jm0,131,248,1|-5vyd21,131,248,1|-5vyd20,130,242,0|-5kfgy1,130,242,0|-5kfgy0,131,248,1|-5d8ae1,131,248,1|-5d8ae0,130,242,0|-51pea1,130,242,0|-51pea0,131,248,1|-4ui7q1,131,248,1|-4ui7q0,130,242,0|-4dqfm1,130,242,0|-4dqfm0,130,224,0|920hvz,130,224,0|920hw0,131,195,1|99xojz,131,195,1|99xok0,130,224,0|9kqkjz,130,224,0|9kqkk0,131,195,1|9snr7z,131,195,1|9snr80,130,224,0\",\"Asia/Shanghai|,0,282,0|-100eztj,45,191,0|-qh00w1,45,191,0|-qh00w0,46,224,1|-q87fo1,46,224,1|-q87fo0,45,191,0|-ffvq81,45,191,0|-ffvq80,46,224,1|-f8zno1,46,224,1|-f8zno0,45,191,0|-f148w1,45,191,0|-f148w0,46,224,1|-ep6p01,46,224,1|-ep6p00,45,191,0|-ekjy81,45,191,0|-ekjy80,46,224,1|-cp63o1,46,224,1|-cp63o0,45,191,0|-cc1sw1,45,191,0|-cc1sw0,46,224,1|-c4wh01,46,224,1|-c4wh00,45,191,0|-butfk1,45,191,0|-butfk0,46,224,1|-bkj501,46,224,1|-bkj500,45,191,0|-bb60w1,45,191,0|-bb60w0,46,224,1|-b3aro1,46,224,1|-b3aro0,45,191,0|-ase3k1,45,191,0|-ase3k0,46,224,1|-ar06c1,46,224,1|-ar06c0,45,191,0|8ixjbz,45,191,0|8ixjc0,46,224,1|8prr7z,46,224,1|8prr80,45,191,0|90kpzz,45,191,0|90kq00,46,224,1|98htvz,46,224,1|98htw0,45,191,0|9jnrbz,45,191,0|9jnrc0,46,224,1|9r7wjz,46,224,1|9r7wk0,45,191,0|a2dtzz,45,191,0|a2du00,46,224,1|aaaxvz,46,224,1|aaaxw0,45,191,0|al3wnz,45,191,0|al3wo0,46,224,1|at10jz,46,224,1|at10k0,45,191,0|b3tzbz,45,191,0|b3tzc0,46,224,1|bbr37z,46,224,1|bbr380,45,191,0\",\"Asia/Singapore|,0,262,0|-100ewkd,85,262,0|-xphpwe,85,262,0|-xphpwd,91,193,0|-jb6gs1,91,193,0|-jb6gs0,118,246,1|-hquppd,118,246,1|-hquppc,118,246,0|-esddpd,118,246,0|-esddpc,106,222,0|-ejqa61,106,222,0|-ejqa60,107,224,0|-conl01,107,224,0|-conl00,106,222,0|69g35z,106,222,0|69g360,89,191,0\",\"Asia/Srednekolymsk|,0,283,0|-nu1ogs,93,195,0|-kmrns1,93,195,0|-kmrns0,90,192,0|5vak3z,90,192,0|5vak40,102,200,1|64pdbz,102,200,1|64pdc0,90,192,0|6e2hfz,90,192,0|6e2hg0,102,200,1|6nhanz,102,200,1|6nhao0,90,192,0|6wuerz,90,192,0|6wues0,102,200,1|7697zz,102,200,1|769800,90,192,0|7fo6rz,90,192,0|7fo6s0,102,200,1|7p1dnz,102,200,1|7p1do0,90,192,0|7yeezz,90,192,0|7yef00,102,200,1|87rgbz,102,200,1|87rgc0,90,192,0|8h4hnz,90,192,0|8h4ho0,102,200,1|8qhizz,102,200,1|8qhj00,90,192,0|8zukbz,90,192,0|8zukc0,102,200,1|997lnz,102,200,1|997lo0,90,192,0|9ikmzz,90,192,0|9ikn00,102,200,1|9rxobz,102,200,1|9rxoc0,90,192,0|a1apnz,90,192,0|a1apo0,102,200,1|aanqzz,102,200,1|aanr00,90,192,0|ak0sbz,90,192,0|ak0sc0,102,200,1|atqsbz,102,200,1|atqsc0,90,192,0|b33tnz,90,192,0|b33to0,90,192,1|bcgxrz,90,192,1|bcgxs0,93,195,0|bi8cfz,93,195,0|bi8cg0,90,192,0|bltwbz,90,192,0|bltwc0,102,200,1|bv6xnz,102,200,1|bv6xo0,90,192,0|c4jyzz,90,192,0|c4jz00,102,200,1|cdx0bz,102,200,1|cdx0c0,90,192,0|cna1nz,90,192,0|cna1o0,102,200,1|cwn2zz,102,200,1|cwn300,90,192,0|d604bz,90,192,0|d604c0,102,200,1|dfd5nz,102,200,1|dfd5o0,90,192,0|dp35nz,90,192,0|dp35o0,102,200,1|dzw1nz,102,200,1|dzw1o0,90,192,0|e7t8bz,90,192,0|e7t8c0,102,200,1|eim4bz,102,200,1|eim4c0,90,192,0|eqjazz,90,192,0|eqjb00,102,200,1|f1c6zz,102,200,1|f1c700,90,192,0|f99dnz,90,192,0|f99do0,102,200,1|fkf8bz,102,200,1|fkf8c0,90,192,0|frzgbz,90,192,0|frzgc0,102,200,1|g35azz,102,200,1|g35b00,90,192,0|gapizz,90,192,0|gapj00,102,200,1|glvdnz,102,200,1|glvdo0,90,192,0|gtskbz,90,192,0|gtskc0,102,200,1|h4lgbz,102,200,1|h4lgc0,90,192,0|hcimzz,90,192,0|hcin00,102,200,1|hnbizz,102,200,1|hnbj00,90,192,0|hv8pnz,90,192,0|hv8po0,102,200,1|i6ekbz,102,200,1|i6ekc0,90,192,0|idysbz,90,192,0|idysc0,102,200,1|ip4mzz,102,200,1|ip4n00,90,192,0|iwouzz,90,192,0|iwov00,102,200,1|j7upnz,102,200,1|j7upo0,90,192,0|jfexnz,90,192,0|jfexo0,102,200,1|jqksbz,102,200,1|jqksc0,90,192,0|jyhyzz,90,192,0|jyhz00,102,200,1|k9auzz,102,200,1|k9av00,90,192,0|kh81nz,90,192,0|kh81o0,102,200,1|ks0xnz,102,200,1|ks0xo0,90,192,0|kzy4bz,90,192,0|kzy4c0,102,200,1|lb3yzz,102,200,1|lb3z00,90,192,0|lio6zz,90,192,0|lio700,102,200,0|ne06vz,102,200,0|ne06w0,90,192,0\",\"Asia/Taipei|,0,284,0|-12mch60,45,191,0|-gtzfk1,45,191,0|-gtzfk0,116,224,0|-co6u81,116,224,0|-co6u80,45,191,0|-cc1sw1,45,191,0|-cc1sw0,46,224,1|-c4wh01,46,224,1|-c4wh00,45,191,0|-butfk1,45,191,0|-butfk0,46,224,1|-bkj501,46,224,1|-bkj500,45,191,0|-bb60w1,45,191,0|-bb60w0,46,224,1|-b3aro1,46,224,1|-b3aro0,45,191,0|-ase3k1,45,191,0|-ase3k0,46,224,1|-akiuc1,46,224,1|-akiuc0,45,191,0|-a9m681,45,191,0|-a9m680,46,224,1|-a1qx01,46,224,1|-a1qx00,45,191,0|-9qu8w1,45,191,0|-9qu8w0,46,224,1|-9iyzo1,46,224,1|-9iyzo0,45,191,0|-9b5fk1,45,191,0|-9b5fk0,46,224,1|-8yjt01,46,224,1|-8yjt00,45,191,0|-8qs3k1,45,191,0|-8qs3k0,46,224,1|-8frvo1,46,224,1|-8frvo0,45,191,0|-880681,45,191,0|-880680,46,224,1|-7wzyc1,46,224,1|-7wzyc0,45,191,0|-7p88w1,45,191,0|-7p88w0,46,224,1|-7ftfo1,46,224,1|-7ftfo0,45,191,0|-76egw1,45,191,0|-76egw0,46,224,1|-6wzno1,46,224,1|-6wzno0,45,191,0|-6nmjk1,45,191,0|-6nmjk0,46,224,1|-6e7qc1,46,224,1|-6e7qc0,45,191,0|-64um81,45,191,0|-64um80,46,224,1|-5vft01,46,224,1|-5vft00,45,191,0|-5m2ow1,45,191,0|-5m2ow0,46,224,1|-5cnvo1,46,224,1|-5cnvo0,45,191,0|-503y81,45,191,0|-503y80,46,224,1|-4tu3o1,46,224,1|-4tu3o0,45,191,0|-4hc0w1,45,191,0|-4hc0w0,46,224,1|-4b26c1,46,224,1|-4b26c0,45,191,0|27rlrz,45,191,0|27rls0,46,224,1|2h6ezz,46,224,1|2h6f00,45,191,0|2qjj3z,45,191,0|2qjj40,46,224,1|2zycbz,46,224,1|2zycc0,45,191,0|4ydlrz,45,191,0|4ydls0,46,224,1|533wbz,46,224,1|533wc0,45,191,0\",\"Asia/Tashkent|,0,285,0|-nu18tz,92,194,0|-kmr9w1,92,194,0|-kmr9w0,96,196,0|5vaxzz,96,196,0|5vay00,91,193,1|64pr7z,91,193,1|64pr80,96,196,0|6e2vbz,96,196,0|6e2vc0,91,193,1|6nhojz,91,193,1|6nhok0,96,196,0|6wusnz,96,196,0|6wuso0,91,193,1|769lvz,91,193,1|769lw0,96,196,0|7foknz,96,196,0|7foko0,91,193,1|7p1rjz,91,193,1|7p1rk0,96,196,0|7yesvz,96,196,0|7yesw0,91,193,1|87ru7z,91,193,1|87ru80,96,196,0|8h4vjz,96,196,0|8h4vk0,91,193,1|8qhwvz,91,193,1|8qhww0,96,196,0|8zuy7z,96,196,0|8zuy80,91,193,1|997zjz,91,193,1|997zk0,96,196,0|9il0vz,96,196,0|9il0w0,91,193,1|9ry27z,91,193,1|9ry280,96,196,0|a1b3jz,96,196,0|a1b3k0,91,193,1|aao4vz,91,193,1|aao4w0,96,196,0|ak167z,96,196,0|ak1680,91,193,1|atr67z,91,193,1|atr680,96,196,0|b347jz,96,196,0|b347k0,96,196,1|bchbnz,96,196,1|bchbo0,92,194,0\",\"Asia/Tbilisi|,0,286,0|-1ayyayn,132,286,0|-nu14ao,132,286,0|-nu14an,100,6,0|-6p7kc1,100,6,0|-6p7kc0,105,209,0|5vb3jz,105,209,0|5vb3k0,92,194,1|64pwrz,92,194,1|64pws0,105,209,0|6e30vz,105,209,0|6e30w0,92,194,1|6nhu3z,92,194,1|6nhu40,105,209,0|6wuy7z,105,209,0|6wuy80,92,194,1|769rfz,92,194,1|769rg0,105,209,0|7foq7z,105,209,0|7foq80,92,194,1|7p1x3z,92,194,1|7p1x40,105,209,0|7yeyfz,105,209,0|7yeyg0,92,194,1|87rzrz,92,194,1|87rzs0,105,209,0|8h513z,105,209,0|8h5140,92,194,1|8qi2fz,92,194,1|8qi2g0,105,209,0|8zv3rz,105,209,0|8zv3s0,92,194,1|99853z,92,194,1|998540,105,209,0|9il6fz,105,209,0|9il6g0,92,194,1|9ry7rz,92,194,1|9ry7s0,105,209,0|a1b93z,105,209,0|a1b940,92,194,1|aaoafz,92,194,1|aaoag0,105,209,0|ak1brz,105,209,0|ak1bs0,92,194,1|atrbrz,92,194,1|atrbs0,105,209,0|b34d3z,105,209,0|b34d40,105,209,1|bchh7z,105,209,1|bchh80,100,6,0|bluczz,100,6,0|blud00,105,209,1|bv7bjz,105,209,1|bv7bk0,100,6,0|c4kfnz,100,6,0|c4kfo0,105,209,1|cdxe7z,105,209,1|cdxe80,100,6,0|cnaibz,100,6,0|cnaic0,105,209,1|cwngvz,105,209,1|cwngw0,105,209,0|d60i7z,105,209,0|d60i80,92,194,1|dfdgrz,92,194,1|dfdgs0,105,209,0|dp3jjz,105,209,0|dp3jk0,92,194,1|eimffz,92,194,1|eimfg0,105,209,0|eqjovz,105,209,0|eqjow0,92,194,1|f1ci3z,92,194,1|f1ci40,105,209,0|f99rjz,105,209,0|f99rk0,92,194,1|fkfjfz,92,194,1|fkfjg0,105,209,0|frzu7z,105,209,0|frzu80,92,194,1|g35m3z,92,194,1|g35m40,105,209,0|gapwvz,105,209,0|gapww0,92,194,1|glvorz,92,194,1|glvos0,105,209,0|gtsy7z,105,209,0|gtsy80,92,194,1|h4lrfz,92,194,1|h4lrg0,105,209,0|hcj0vz,105,209,0|hcj0w0,92,194,1|hnbu3z,92,194,1|hnbu40,105,209,0|hv93jz,105,209,0|hv93k0,92,194,1|hzxjfz,92,194,1|hzxjg0,105,209,1|i6f6jz,105,209,1|i6f6k0,100,6,0|idzejz,100,6,0|idzek0,105,209,0\",\"Asia/Tehran|,0,287,0|-s6m6uw,133,287,0|-cixlix,133,287,0|-cixliw,134,288,0|435vlz,134,288,0|435vm0,105,209,0|4ad3jz,105,209,0|4ad3k0,92,194,1|4ldbfz,92,194,1|4ldbg0,105,209,0|4p2q7z,105,209,0|4p2q80,134,288,0|4t529z,134,288,0|4t52a0,124,252,1|52i0tz,124,252,1|52i0u0,134,288,0|5byu9z,134,288,0|5byua0,124,252,1|5lj7hz,124,252,1|5lj7i0,134,288,0|b4tcxz,134,288,0|b4tcy0,124,252,1|bc48tz,124,252,1|bc48u0,134,288,0|blhcxz,134,288,0|blhcy0,124,252,1|buy0tz,124,252,1|buy0u0,134,288,0|c49a9z,134,288,0|c49aa0,124,252,1|cdpy5z,124,252,1|cdpy60,134,288,0|cn17lz,134,288,0|cn17m0,124,252,1|cwhvhz,124,252,1|cwhvi0,134,288,0|d5t4xz,134,288,0|d5t4y0,124,252,1|df9stz,124,252,1|df9su0,134,288,0|dol29z,134,288,0|dol2a0,124,252,1|dy1q5z,124,252,1|dy1q60,134,288,0|e7eu9z,134,288,0|e7eua0,124,252,1|egvi5z,124,252,1|egvi60,134,288,0|eq6rlz,134,288,0|eq6rm0,124,252,1|eznfhz,124,252,1|eznfi0,134,288,0|f8yoxz,134,288,0|f8yoy0,124,252,1|fifctz,124,252,1|fifcu0,134,288,0|frqm9z,134,288,0|frqma0,124,252,1|g17a5z,124,252,1|g17a60,134,288,0|gake9z,134,288,0|gakea0,124,252,1|gk125z,124,252,1|gk1260,134,288,0|gtcblz,134,288,0|gtcbm0,124,252,1|h2szhz,124,252,1|h2szi0,134,288,0|hc48xz,134,288,0|hc48y0,124,252,1|hlkwtz,124,252,1|hlkwu0,134,288,0|huw69z,134,288,0|huw6a0,124,252,1|i4cu5z,124,252,1|i4cu60,134,288,0|idpy9z,134,288,0|idpya0,124,252,1|in6m5z,124,252,1|in6m60,134,288,0|jy1q9z,134,288,0|jy1qa0,124,252,1|k7ie5z,124,252,1|k7ie60,134,288,0|kgvi9z,134,288,0|kgvia0,124,252,1|kqc65z,124,252,1|kqc660,134,288,0|kznflz,134,288,0|kznfm0,124,252,1|l943hz,124,252,1|l943i0,134,288,0|lifcxz,134,288,0|lifcy0,124,252,1|lrw0tz,124,252,1|lrw0u0,134,288,0|m17a9z,134,288,0|m17aa0,124,252,1|many5z,124,252,1|many60,134,288,0|mk129z,134,288,0|mk12a0,124,252,1|mthq5z,124,252,1|mthq60,134,288,0|n2szlz,134,288,0|n2szm0,124,252,1|nc9nhz,124,252,1|nc9ni0,134,288,0|nlkwxz,134,288,0|nlkwy0,124,252,1|nv1ktz,124,252,1|nv1ku0,134,288,0|o4cu9z,134,288,0|o4cua0,124,252,1|odti5z,124,252,1|odti60,134,288,0|on6m9z,134,288,0|on6ma0,124,252,1|owna5z,124,252,1|owna60,134,288,0|p5yjlz,134,288,0|p5yjm0,124,252,1|pff7hz,124,252,1|pff7i0,134,288,0|poqgxz,134,288,0|poqgy0,124,252,1|py74tz,124,252,1|py74u0,134,288,0|q7ie9z,134,288,0|q7iea0,124,252,1|qgz25z,124,252,1|qgz260,134,288,0|qqc69z,134,288,0|qqc6a0,124,252,1|qzsu5z,124,252,1|qzsu60,134,288,0|r943lz,134,288,0|r943m0,124,252,1|rikrhz,124,252,1|rikri0,134,288,0|rrw0xz,134,288,0|rrw0y0,124,252,1|s1cotz,124,252,1|s1cou0,134,288,0|sany9z,134,288,0|sanya0,124,252,1|sk4m5z,124,252,1|sk4m60,134,288,0|sthq9z,134,288,0|sthqa0,124,252,1|t2ye5z,124,252,1|t2ye60,134,288,0|tc9nlz,134,288,0|tc9nm0,124,252,1|tlqbhz,124,252,1|tlqbi0,134,288,0|tv1kxz,134,288,0|tv1ky0,124,252,1|u4i8tz,124,252,1|u4i8u0,134,288,0|udti9z,134,288,0|udtia0,124,252,1|una65z,124,252,1|una660,134,288,0|uwlflz,134,288,0|uwlfm0,124,252,1|v623hz,124,252,1|v623i0,134,288,0|vff7lz,134,288,0|vff7m0,124,252,1|vovvhz,124,252,1|vovvi0,134,288,0|vy74xz,134,288,0|vy74y0,124,252,1|w7nstz,124,252,1|w7nsu0,134,288,0|wgz29z,134,288,0|wgz2a0,124,252,1|wqfq5z,124,252,1|wqfq60,134,288,0|wzqzlz,134,288,0|wzqzm0,124,252,1|x97nhz,124,252,1|x97ni0,134,288,0|xikrlz,134,288,0|xikrm0,124,252,1|xs1fhz,124,252,1|xs1fi0,134,288,0|y1coxz,134,288,0|y1coy0,124,252,1|yatctz,124,252,1|yatcu0,134,288,0|yk4m9z,134,288,0|yk4ma0,124,252,1|ytla5z,124,252,1|ytla60,134,288,0|z2wjlz,134,288,0|z2wjm0,124,252,1|zcd7hz,124,252,1|zcd7i0,134,288,0\",\"Asia/Thimphu|,0,289,0|-bojclo,108,228,0|99fa1z,108,228,0|99fa20,96,196,0\",\"Asia/Tokyo|,0,290,0|-16snno0,116,224,0|-bb4901,116,224,0|-bb4900,135,195,1|-b49yc1,135,195,1|-b49yc0,116,224,0|-atu101,116,224,0|-atu100,135,195,1|-aljvo1,135,195,1|-aljvo0,116,224,0|-a9b501,116,224,0|-a9b500,135,195,1|-a2tt01,135,195,1|-a2tt00,116,224,0|-9ql2c1,116,224,0|-9ql2c0,135,195,1|-9k3qc1,135,195,1|-9k3qc0,116,224,0\",\"Asia/Tomsk|,0,291,0|-q3zbqf,96,196,0|-kmrco1,96,196,0|-kmrco0,91,193,0|5vav7z,91,193,0|5vav80,89,191,1|64pofz,89,191,1|64pog0,91,193,0|6e2sjz,91,193,0|6e2sk0,89,191,1|6nhlrz,89,191,1|6nhls0,91,193,0|6wupvz,91,193,0|6wupw0,89,191,1|769j3z,89,191,1|769j40,91,193,0|7fohvz,91,193,0|7fohw0,89,191,1|7p1orz,89,191,1|7p1os0,91,193,0|7yeq3z,91,193,0|7yeq40,89,191,1|87rrfz,89,191,1|87rrg0,91,193,0|8h4srz,91,193,0|8h4ss0,89,191,1|8qhu3z,89,191,1|8qhu40,91,193,0|8zuvfz,91,193,0|8zuvg0,89,191,1|997wrz,89,191,1|997ws0,91,193,0|9iky3z,91,193,0|9iky40,89,191,1|9rxzfz,89,191,1|9rxzg0,91,193,0|a1b0rz,91,193,0|a1b0s0,89,191,1|aao23z,89,191,1|aao240,91,193,0|ak13fz,91,193,0|ak13g0,89,191,1|atr3fz,89,191,1|atr3g0,91,193,0|b344rz,91,193,0|b344s0,91,193,1|bch8vz,91,193,1|bch8w0,96,196,0|bi8njz,96,196,0|bi8nk0,91,193,0|blu7fz,91,193,0|blu7g0,89,191,1|bv78rz,89,191,1|bv78s0,91,193,0|c4ka3z,91,193,0|c4ka40,89,191,1|cdxbfz,89,191,1|cdxbg0,91,193,0|cnacrz,91,193,0|cnacs0,89,191,1|cwne3z,89,191,1|cwne40,91,193,0|d60ffz,91,193,0|d60fg0,89,191,1|dfdgrz,89,191,1|dfdgs0,91,193,0|dp3grz,91,193,0|dp3gs0,89,191,1|dzwcrz,89,191,1|dzwcs0,91,193,0|e7tjfz,91,193,0|e7tjg0,89,191,1|eimffz,89,191,1|eimfg0,91,193,0|eqjm3z,91,193,0|eqjm40,89,191,1|f1ci3z,89,191,1|f1ci40,91,193,0|f99orz,91,193,0|f99os0,89,191,1|fkfjfz,89,191,1|fkfjg0,91,193,0|frzrfz,91,193,0|frzrg0,89,191,1|g35m3z,89,191,1|g35m40,91,193,0|gapu3z,91,193,0|gapu40,89,191,1|glvorz,89,191,1|glvos0,91,193,0|gtsvfz,91,193,0|gtsvg0,89,191,1|gvea3z,89,191,1|gvea40,91,193,1|h4lu7z,91,193,1|h4lu80,96,196,0|hcj0vz,96,196,0|hcj0w0,91,193,1|hnbwvz,91,193,1|hnbww0,96,196,0|hv93jz,96,196,0|hv93k0,91,193,1|i6ey7z,91,193,1|i6ey80,96,196,0|idz67z,96,196,0|idz680,91,193,1|ip50vz,91,193,1|ip50w0,96,196,0|iwp8vz,96,196,0|iwp8w0,91,193,1|j7v3jz,91,193,1|j7v3k0,96,196,0|jffbjz,96,196,0|jffbk0,91,193,1|jql67z,91,193,1|jql680,96,196,0|jyicvz,96,196,0|jyicw0,91,193,1|k9b8vz,91,193,1|k9b8w0,96,196,0|kh8fjz,96,196,0|kh8fk0,91,193,1|ks1bjz,91,193,1|ks1bk0,96,196,0|kzyi7z,96,196,0|kzyi80,91,193,1|lb4cvz,91,193,1|lb4cw0,96,196,0|liokvz,96,196,0|liokw0,91,193,0|ne0krz,91,193,0|ne0ks0,96,196,0|o7wkvz,96,196,0|o7wkw0,91,193,0\",\"Asia/Ulaanbaatar|,0,292,0|-xmcrsk,91,193,0|46akjz,91,193,0|46akk0,89,191,0|6wun3z,89,191,0|6wun40,107,224,1|769gbz,107,224,1|769gc0,89,191,0|7fof3z,89,191,0|7fof40,107,224,1|7p1dnz,107,224,1|7p1do0,89,191,0|7yehrz,89,191,0|7yehs0,107,224,1|87rgbz,107,224,1|87rgc0,89,191,0|8h4kfz,89,191,0|8h4kg0,107,224,1|8qhizz,107,224,1|8qhj00,89,191,0|8zun3z,89,191,0|8zun40,107,224,1|997lnz,107,224,1|997lo0,89,191,0|9ikprz,89,191,0|9ikps0,107,224,1|9rxobz,107,224,1|9rxoc0,89,191,0|a1asfz,89,191,0|a1asg0,107,224,1|aanqzz,107,224,1|aanr00,89,191,0|ak0v3z,89,191,0|ak0v40,107,224,1|atqsbz,107,224,1|atqsc0,89,191,0|b33wfz,89,191,0|b33wg0,107,224,1|bcguzz,107,224,1|bcgv00,89,191,0|bltz3z,89,191,0|bltz40,107,224,1|bv6xnz,107,224,1|bv6xo0,89,191,0|c4k1rz,89,191,0|c4k1s0,107,224,1|cdx0bz,107,224,1|cdx0c0,89,191,0|cna4fz,89,191,0|cna4g0,107,224,1|cwn2zz,107,224,1|cwn300,89,191,0|d6073z,89,191,0|d60740,107,224,1|dfd5nz,107,224,1|dfd5o0,89,191,0|dp38fz,89,191,0|dp38g0,107,224,1|dyg6zz,107,224,1|dyg700,89,191,0|e7tb3z,89,191,0|e7tb40,107,224,1|eh69nz,107,224,1|eh69o0,89,191,0|eqjdrz,89,191,0|eqjds0,107,224,1|ezwcbz,107,224,1|ezwcc0,89,191,0|gcgpzz,89,191,0|gcgq00,107,224,1|gkdtvz,107,224,1|gkdtw0,89,191,0|gtqxzz,89,191,0|gtqy00,107,224,1|h33wjz,107,224,1|h33wk0,89,191,0|hch0nz,89,191,0|hch0o0,107,224,1|hltz7z,107,224,1|hltz80,89,191,0|hv73bz,89,191,0|hv73c0,107,224,1|i4k1vz,107,224,1|i4k1w0,89,191,0|idx5zz,89,191,0|idx600,107,224,1|ina4jz,107,224,1|ina4k0,89,191,0|iwn8nz,89,191,0|iwn8o0,107,224,1|j6d5vz,107,224,1|j6d5w0,89,191,0|nlvtzz,89,191,0|nlvu00,107,224,1|nv8mzz,107,224,1|nv8n00,89,191,0|o4lwnz,89,191,0|o4lwo0,107,224,1|odypnz,107,224,1|odypo0,89,191,0\",\"Asia/Urumqi|,0,293,0|-lx5pjw,96,196,0\",\"Asia/Ust-Nera|,0,294,0|-q4cl6u,89,191,0|-kmri81,89,191,0|-kmri80,107,224,0|5vapnz,107,224,0|5vapo0,102,200,1|64pdbz,102,200,1|64pdc0,90,192,0|6e2hfz,90,192,0|6e2hg0,102,200,1|6nhanz,102,200,1|6nhao0,90,192,0|6wuerz,90,192,0|6wues0,102,200,1|7697zz,102,200,1|769800,90,192,0|7fo6rz,90,192,0|7fo6s0,102,200,1|7p1dnz,102,200,1|7p1do0,90,192,0|7yeezz,90,192,0|7yef00,102,200,1|87rgbz,102,200,1|87rgc0,90,192,0|8h4hnz,90,192,0|8h4ho0,102,200,1|8qhizz,102,200,1|8qhj00,90,192,0|8zukbz,90,192,0|8zukc0,102,200,1|997lnz,102,200,1|997lo0,90,192,0|9ikmzz,90,192,0|9ikn00,102,200,1|9rxobz,102,200,1|9rxoc0,90,192,0|a1apnz,90,192,0|a1apo0,102,200,1|aanqzz,102,200,1|aanr00,90,192,0|ak0sbz,90,192,0|ak0sc0,102,200,1|atqsbz,102,200,1|atqsc0,90,192,0|b33tnz,90,192,0|b33to0,90,192,1|bcgxrz,90,192,1|bcgxs0,93,195,0|bi8cfz,93,195,0|bi8cg0,90,192,0|bltwbz,90,192,0|bltwc0,102,200,1|bv6xnz,102,200,1|bv6xo0,90,192,0|c4jyzz,90,192,0|c4jz00,102,200,1|cdx0bz,102,200,1|cdx0c0,90,192,0|cna1nz,90,192,0|cna1o0,102,200,1|cwn2zz,102,200,1|cwn300,90,192,0|d604bz,90,192,0|d604c0,102,200,1|dfd5nz,102,200,1|dfd5o0,90,192,0|dp35nz,90,192,0|dp35o0,102,200,1|dzw1nz,102,200,1|dzw1o0,90,192,0|e7t8bz,90,192,0|e7t8c0,102,200,1|eim4bz,102,200,1|eim4c0,90,192,0|eqjazz,90,192,0|eqjb00,102,200,1|f1c6zz,102,200,1|f1c700,90,192,0|f99dnz,90,192,0|f99do0,102,200,1|fkf8bz,102,200,1|fkf8c0,90,192,0|frzgbz,90,192,0|frzgc0,102,200,1|g35azz,102,200,1|g35b00,90,192,0|gapizz,90,192,0|gapj00,102,200,1|glvdnz,102,200,1|glvdo0,90,192,0|gtskbz,90,192,0|gtskc0,102,200,1|h4lgbz,102,200,1|h4lgc0,90,192,0|hcimzz,90,192,0|hcin00,102,200,1|hnbizz,102,200,1|hnbj00,90,192,0|hv8pnz,90,192,0|hv8po0,102,200,1|i6ekbz,102,200,1|i6ekc0,90,192,0|idysbz,90,192,0|idysc0,102,200,1|ip4mzz,102,200,1|ip4n00,90,192,0|iwouzz,90,192,0|iwov00,102,200,1|j7upnz,102,200,1|j7upo0,90,192,0|jfexnz,90,192,0|jfexo0,102,200,1|jqksbz,102,200,1|jqksc0,90,192,0|jyhyzz,90,192,0|jyhz00,102,200,1|k9auzz,102,200,1|k9av00,90,192,0|kh81nz,90,192,0|kh81o0,102,200,1|ks0xnz,102,200,1|ks0xo0,90,192,0|kzy4bz,90,192,0|kzy4c0,102,200,1|lb3yzz,102,200,1|lb3z00,90,192,0|lio6zz,90,192,0|lio700,102,200,0|lrerzz,102,200,0|lres00,90,192,0|ne09nz,90,192,0|ne09o0,93,195,0\",\"Asia/Vientiane|,0,217,0|-1ayyla4,53,217,0|-pysda5,53,217,0|-pysda4,91,193,0\",\"Asia/Vladivostok|,0,295,0|-oligf7,107,224,0|-kmrl01,107,224,0|-kmrl00,93,195,0|5vamvz,93,195,0|5vamw0,90,192,1|64pg3z,90,192,1|64pg40,93,195,0|6e2k7z,93,195,0|6e2k80,90,192,1|6nhdfz,90,192,1|6nhdg0,93,195,0|6wuhjz,93,195,0|6wuhk0,90,192,1|769arz,90,192,1|769as0,93,195,0|7fo9jz,93,195,0|7fo9k0,90,192,1|7p1gfz,90,192,1|7p1gg0,93,195,0|7yehrz,93,195,0|7yehs0,90,192,1|87rj3z,90,192,1|87rj40,93,195,0|8h4kfz,93,195,0|8h4kg0,90,192,1|8qhlrz,90,192,1|8qhls0,93,195,0|8zun3z,93,195,0|8zun40,90,192,1|997ofz,90,192,1|997og0,93,195,0|9ikprz,93,195,0|9ikps0,90,192,1|9rxr3z,90,192,1|9rxr40,93,195,0|a1asfz,93,195,0|a1asg0,90,192,1|aantrz,90,192,1|aants0,93,195,0|ak0v3z,93,195,0|ak0v40,90,192,1|atqv3z,90,192,1|atqv40,93,195,0|b33wfz,93,195,0|b33wg0,93,195,1|bch0jz,93,195,1|bch0k0,107,224,0|bi8f7z,107,224,0|bi8f80,93,195,0|bltz3z,93,195,0|bltz40,90,192,1|bv70fz,90,192,1|bv70g0,93,195,0|c4k1rz,93,195,0|c4k1s0,90,192,1|cdx33z,90,192,1|cdx340,93,195,0|cna4fz,93,195,0|cna4g0,90,192,1|cwn5rz,90,192,1|cwn5s0,93,195,0|d6073z,93,195,0|d60740,90,192,1|dfd8fz,90,192,1|dfd8g0,93,195,0|dp38fz,93,195,0|dp38g0,90,192,1|dzw4fz,90,192,1|dzw4g0,93,195,0|e7tb3z,93,195,0|e7tb40,90,192,1|eim73z,90,192,1|eim740,93,195,0|eqjdrz,93,195,0|eqjds0,90,192,1|f1c9rz,90,192,1|f1c9s0,93,195,0|f99gfz,93,195,0|f99gg0,90,192,1|fkfb3z,90,192,1|fkfb40,93,195,0|frzj3z,93,195,0|frzj40,90,192,1|g35drz,90,192,1|g35ds0,93,195,0|gaplrz,93,195,0|gapls0,90,192,1|glvgfz,90,192,1|glvgg0,93,195,0|gtsn3z,93,195,0|gtsn40,90,192,1|h4lj3z,90,192,1|h4lj40,93,195,0|hciprz,93,195,0|hcips0,90,192,1|hnblrz,90,192,1|hnbls0,93,195,0|hv8sfz,93,195,0|hv8sg0,90,192,1|i6en3z,90,192,1|i6en40,93,195,0|idyv3z,93,195,0|idyv40,90,192,1|ip4prz,90,192,1|ip4ps0,93,195,0|iwoxrz,93,195,0|iwoxs0,90,192,1|j7usfz,90,192,1|j7usg0,93,195,0|jff0fz,93,195,0|jff0g0,90,192,1|jqkv3z,90,192,1|jqkv40,93,195,0|jyi1rz,93,195,0|jyi1s0,90,192,1|k9axrz,90,192,1|k9axs0,93,195,0|kh84fz,93,195,0|kh84g0,90,192,1|ks10fz,90,192,1|ks10g0,93,195,0|kzy73z,93,195,0|kzy740,90,192,1|lb41rz,90,192,1|lb41s0,93,195,0|lio9rz,93,195,0|lio9s0,90,192,0|ne09nz,90,192,0|ne09o0,93,195,0\",\"Asia/Yakutsk|,0,296,0|-q4cioy,89,191,0|-kmri81,89,191,0|-kmri80,107,224,0|5vapnz,107,224,0|5vapo0,93,195,1|64pivz,93,195,1|64piw0,107,224,0|6e2mzz,107,224,0|6e2n00,93,195,1|6nhg7z,93,195,1|6nhg80,107,224,0|6wukbz,107,224,0|6wukc0,93,195,1|769djz,93,195,1|769dk0,107,224,0|7focbz,107,224,0|7focc0,93,195,1|7p1j7z,93,195,1|7p1j80,107,224,0|7yekjz,107,224,0|7yekk0,93,195,1|87rlvz,93,195,1|87rlw0,107,224,0|8h4n7z,107,224,0|8h4n80,93,195,1|8qhojz,93,195,1|8qhok0,107,224,0|8zupvz,107,224,0|8zupw0,93,195,1|997r7z,93,195,1|997r80,107,224,0|9iksjz,107,224,0|9iksk0,93,195,1|9rxtvz,93,195,1|9rxtw0,107,224,0|a1av7z,107,224,0|a1av80,93,195,1|aanwjz,93,195,1|aanwk0,107,224,0|ak0xvz,107,224,0|ak0xw0,93,195,1|atqxvz,93,195,1|atqxw0,107,224,0|b33z7z,107,224,0|b33z80,107,224,1|bch3bz,107,224,1|bch3c0,89,191,0|bi8hzz,89,191,0|bi8i00,107,224,0|blu1vz,107,224,0|blu1w0,93,195,1|bv737z,93,195,1|bv7380,107,224,0|c4k4jz,107,224,0|c4k4k0,93,195,1|cdx5vz,93,195,1|cdx5w0,107,224,0|cna77z,107,224,0|cna780,93,195,1|cwn8jz,93,195,1|cwn8k0,107,224,0|d609vz,107,224,0|d609w0,93,195,1|dfdb7z,93,195,1|dfdb80,107,224,0|dp3b7z,107,224,0|dp3b80,93,195,1|dzw77z,93,195,1|dzw780,107,224,0|e7tdvz,107,224,0|e7tdw0,93,195,1|eim9vz,93,195,1|eim9w0,107,224,0|eqjgjz,107,224,0|eqjgk0,93,195,1|f1ccjz,93,195,1|f1cck0,107,224,0|f99j7z,107,224,0|f99j80,93,195,1|fkfdvz,93,195,1|fkfdw0,107,224,0|frzlvz,107,224,0|frzlw0,93,195,1|g35gjz,93,195,1|g35gk0,107,224,0|gapojz,107,224,0|gapok0,93,195,1|glvj7z,93,195,1|glvj80,107,224,0|gtspvz,107,224,0|gtspw0,93,195,1|h4llvz,93,195,1|h4llw0,107,224,0|hcisjz,107,224,0|hcisk0,93,195,1|hnbojz,93,195,1|hnbok0,107,224,0|hv8v7z,107,224,0|hv8v80,93,195,1|i6epvz,93,195,1|i6epw0,107,224,0|idyxvz,107,224,0|idyxw0,93,195,1|ip4sjz,93,195,1|ip4sk0,107,224,0|iwp0jz,107,224,0|iwp0k0,93,195,1|j7uv7z,93,195,1|j7uv80,107,224,0|jff37z,107,224,0|jff380,93,195,1|jqkxvz,93,195,1|jqkxw0,107,224,0|jyi4jz,107,224,0|jyi4k0,93,195,1|k9b0jz,93,195,1|k9b0k0,107,224,0|kh877z,107,224,0|kh8780,93,195,1|ks137z,93,195,1|ks1380,107,224,0|kzy9vz,107,224,0|kzy9w0,93,195,1|lb44jz,93,195,1|lb44k0,107,224,0|liocjz,107,224,0|liock0,93,195,0|ne0cfz,93,195,0|ne0cg0,107,224,0\",\"Asia/Yangon|,0,297,0|-1ayykhb,136,297,0|-q3gv5c,136,297,0|-q3gv5b,109,229,0|-efx621,109,229,0|-efx620,107,224,0|-cvg101,107,224,0|-cvg100,109,229,0\",\"Asia/Yekaterinburg|,0,298,0|-rx5hw9,7,299,0|-qc75z6,7,299,0|-qc75z5,105,209,0|-kmr741,105,209,0|-kmr740,92,194,0|5vb0rz,92,194,0|5vb0s0,96,196,1|64ptzz,96,196,1|64pu00,92,194,0|6e2y3z,92,194,0|6e2y40,96,196,1|6nhrbz,96,196,1|6nhrc0,92,194,0|6wuvfz,92,194,0|6wuvg0,96,196,1|769onz,96,196,1|769oo0,92,194,0|7fonfz,92,194,0|7fong0,96,196,1|7p1ubz,96,196,1|7p1uc0,92,194,0|7yevnz,92,194,0|7yevo0,96,196,1|87rwzz,96,196,1|87rx00,92,194,0|8h4ybz,92,194,0|8h4yc0,96,196,1|8qhznz,96,196,1|8qhzo0,92,194,0|8zv0zz,92,194,0|8zv100,96,196,1|9982bz,96,196,1|9982c0,92,194,0|9il3nz,92,194,0|9il3o0,96,196,1|9ry4zz,96,196,1|9ry500,92,194,0|a1b6bz,92,194,0|a1b6c0,96,196,1|aao7nz,96,196,1|aao7o0,92,194,0|ak18zz,92,194,0|ak1900,96,196,1|atr8zz,96,196,1|atr900,92,194,0|b34abz,92,194,0|b34ac0,92,194,1|bchefz,92,194,1|bcheg0,105,209,0|bi8t3z,105,209,0|bi8t40,92,194,0|bluczz,92,194,0|blud00,96,196,1|bv7ebz,96,196,1|bv7ec0,92,194,0|c4kfnz,92,194,0|c4kfo0,96,196,1|cdxgzz,96,196,1|cdxh00,92,194,0|cnaibz,92,194,0|cnaic0,96,196,1|cwnjnz,96,196,1|cwnjo0,92,194,0|d60kzz,92,194,0|d60l00,96,196,1|dfdmbz,96,196,1|dfdmc0,92,194,0|dp3mbz,92,194,0|dp3mc0,96,196,1|dzwibz,96,196,1|dzwic0,92,194,0|e7tozz,92,194,0|e7tp00,96,196,1|eimkzz,96,196,1|eiml00,92,194,0|eqjrnz,92,194,0|eqjro0,96,196,1|f1cnnz,96,196,1|f1cno0,92,194,0|f99ubz,92,194,0|f99uc0,96,196,1|fkfozz,96,196,1|fkfp00,92,194,0|frzwzz,92,194,0|frzx00,96,196,1|g35rnz,96,196,1|g35ro0,92,194,0|gapznz,92,194,0|gapzo0,96,196,1|glvubz,96,196,1|glvuc0,92,194,0|gtt0zz,92,194,0|gtt100,96,196,1|h4lwzz,96,196,1|h4lx00,92,194,0|hcj3nz,92,194,0|hcj3o0,96,196,1|hnbznz,96,196,1|hnbzo0,92,194,0|hv96bz,92,194,0|hv96c0,96,196,1|i6f0zz,96,196,1|i6f100,92,194,0|idz8zz,92,194,0|idz900,96,196,1|ip53nz,96,196,1|ip53o0,92,194,0|iwpbnz,92,194,0|iwpbo0,96,196,1|j7v6bz,96,196,1|j7v6c0,92,194,0|jffebz,92,194,0|jffec0,96,196,1|jql8zz,96,196,1|jql900,92,194,0|jyifnz,92,194,0|jyifo0,96,196,1|k9bbnz,96,196,1|k9bbo0,92,194,0|kh8ibz,92,194,0|kh8ic0,96,196,1|ks1ebz,96,196,1|ks1ec0,92,194,0|kzykzz,92,194,0|kzyl00,96,196,1|lb4fnz,96,196,1|lb4fo0,92,194,0|lionnz,92,194,0|liono0,96,196,0|ne0njz,96,196,0|ne0nk0,92,194,0\",\"Asia/Yerevan|,0,300,0|-nu148o,100,6,0|-6p7kc1,100,6,0|-6p7kc0,105,209,0|5vb3jz,105,209,0|5vb3k0,92,194,1|64pwrz,92,194,1|64pws0,105,209,0|6e30vz,105,209,0|6e30w0,92,194,1|6nhu3z,92,194,1|6nhu40,105,209,0|6wuy7z,105,209,0|6wuy80,92,194,1|769rfz,92,194,1|769rg0,105,209,0|7foq7z,105,209,0|7foq80,92,194,1|7p1x3z,92,194,1|7p1x40,105,209,0|7yeyfz,105,209,0|7yeyg0,92,194,1|87rzrz,92,194,1|87rzs0,105,209,0|8h513z,105,209,0|8h5140,92,194,1|8qi2fz,92,194,1|8qi2g0,105,209,0|8zv3rz,105,209,0|8zv3s0,92,194,1|99853z,92,194,1|998540,105,209,0|9il6fz,105,209,0|9il6g0,92,194,1|9ry7rz,92,194,1|9ry7s0,105,209,0|a1b93z,105,209,0|a1b940,92,194,1|aaoafz,92,194,1|aaoag0,105,209,0|ak1brz,105,209,0|ak1bs0,92,194,1|atrbrz,92,194,1|atrbs0,105,209,0|b34d3z,105,209,0|b34d40,105,209,1|bchh7z,105,209,1|bchh80,100,6,0|bluijz,100,6,0|bluik0,105,209,1|bv7jvz,105,209,1|bv7jw0,100,6,0|c4kl7z,100,6,0|c4kl80,105,209,1|cdxmjz,105,209,1|cdxmk0,100,6,0|cnanvz,100,6,0|cnanw0,105,209,1|cwnp7z,105,209,1|cwnp80,100,6,0|d60qjz,100,6,0|d60qk0,105,209,1|dfdrvz,105,209,1|dfdrw0,105,209,0|e7trrz,105,209,0|e7trs0,92,194,1|eimnrz,92,194,1|eimns0,105,209,0|eqjufz,105,209,0|eqjug0,92,194,1|f1cqfz,92,194,1|f1cqg0,105,209,0|f99x3z,105,209,0|f99x40,92,194,1|fkfrrz,92,194,1|fkfrs0,105,209,0|frzzrz,105,209,0|frzzs0,92,194,1|g35ufz,92,194,1|g35ug0,105,209,0|gaq2fz,105,209,0|gaq2g0,92,194,1|glvx3z,92,194,1|glvx40,105,209,0|gtt3rz,105,209,0|gtt3s0,92,194,1|h4lzrz,92,194,1|h4lzs0,105,209,0|hcj6fz,105,209,0|hcj6g0,92,194,1|hnc2fz,92,194,1|hnc2g0,105,209,0|hv993z,105,209,0|hv9940,92,194,1|i6f3rz,92,194,1|i6f3s0,105,209,0|idzbrz,105,209,0|idzbs0,92,194,1|ip56fz,92,194,1|ip56g0,105,209,0|iwpefz,105,209,0|iwpeg0,92,194,1|j7v93z,92,194,1|j7v940,105,209,0|jffh3z,105,209,0|jffh40,92,194,1|jqlbrz,92,194,1|jqlbs0,105,209,0|jyiifz,105,209,0|jyiig0,92,194,1|k9befz,92,194,1|k9beg0,105,209,0|kh8l3z,105,209,0|kh8l40,92,194,1|ks1h3z,92,194,1|ks1h40,105,209,0|kzynrz,105,209,0|kzyns0,92,194,1|lb4ifz,92,194,1|lb4ig0,105,209,0|lioqfz,105,209,0|lioqg0,92,194,1|ltul3z,92,194,1|ltul40,105,209,0\",\"Atlantic/Azores|,0,301,0|-18vsdww,77,302,0|-u9rbs1,77,302,0|-u9rbs0,40,45,0|-rxwvw1,40,45,0|-rxwvw0,13,15,1|-rqwyg1,13,15,1|-rqwyg0,40,45,0|-rkqt81,40,45,0|-rkqt80,13,15,1|-r90l81,13,15,1|-r90l80,40,45,0|-r1x181,40,45,0|-r1x180,13,15,1|-qq8nw1,13,15,1|-qq8nw0,40,45,0|-qj6yk1,40,45,0|-qj6yk0,13,15,1|-q7gqk1,13,15,1|-q7gqk0,40,45,0|-q0d6k1,40,45,0|-q0d6k0,13,15,1|-pomyk1,13,15,1|-pomyk0,40,45,0|-phl981,40,45,0|-phl980,13,15,1|-p5v181,13,15,1|-p5v180,40,45,0|-nusl81,40,45,0|-nusl80,13,15,1|-nlhek1,13,15,1|-nlhek0,40,45,0|-mt6vw1,40,45,0|-mt6vw0,13,15,1|-mkjrw1,13,15,1|-mkjrw0,40,45,0|-matrw1,40,45,0|-matrw0,13,15,1|-m1tp81,13,15,1|-m1tp80,40,45,0|-lrqqk1,40,45,0|-lrqqk0,13,15,1|-liqnw1,13,15,1|-liqnw0,40,45,0|-l8np81,40,45,0|-l8np80,13,15,1|-l00l81,13,15,1|-l00l80,40,45,0|-k77jw1,40,45,0|-k77jw0,13,15,1|-jykfw1,13,15,1|-jykfw0,40,45,0|-jp7ek1,40,45,0|-jp7ek0,13,15,1|-jfud81,13,15,1|-jfud80,40,45,0|-ineak1,40,45,0|-ineak0,13,15,1|-ie1981,13,15,1|-ie1980,40,45,0|-i516k1,40,45,0|-i516k0,13,15,1|-hvb6k1,13,15,1|-hvb6k0,40,45,0|-hl87w1,40,45,0|-hl87w0,13,15,1|-hcl3w1,13,15,1|-hcl3w0,40,45,0|-h382k1,40,45,0|-h382k0,13,15,1|-gtv181,13,15,1|-gtv180,40,45,0|-gkuyk1,40,45,0|-gkuyk0,13,15,1|-gb4yk1,13,15,1|-gb4yk0,40,45,0|-g11zw1,40,45,0|-g11zw0,13,15,1|-fpw581,13,15,1|-fpw580,40,45,0|-fkunw1,40,45,0|-fkunw0,13,15,1|-f9buk1,13,15,1|-f9buk0,40,45,0|-ezyt81,40,45,0|-ezyt80,13,15,1|-eqjx81,13,15,1|-eqjx80,40,45,0|-eibmk1,40,45,0|-eibmk0,13,15,1|-eg5xc1,13,15,1|-eg5xc0,17,1,1|-eaeio1,17,1,1|-eaeio0,13,15,1|-e6st81,13,15,1|-e6st80,40,45,0|-dzljw1,40,45,0|-dzljw0,13,15,1|-dxstc1,13,15,1|-dxstc0,17,1,1|-dqyio1,17,1,1|-dqyio0,13,15,1|-dnprw1,13,15,1|-dnprw0,40,45,0|-dgvh81,40,45,0|-dgvh80,13,15,1|-deps01,13,15,1|-deps00,17,1,1|-d88g01,17,1,1|-d88g00,13,15,1|-d4zp81,13,15,1|-d4zp80,40,45,0|-cy5ek1,40,45,0|-cy5ek0,13,15,1|-cvzpc1,13,15,1|-cvzpc0,17,1,1|-cpidc1,17,1,1|-cpidc0,13,15,1|-cm9mk1,13,15,1|-cm9mk0,40,45,0|-cdzh81,40,45,0|-cdzh80,13,15,1|-c4mfw1,13,15,1|-c4mfw0,40,45,0|-bv9681,40,45,0|-bv9680,13,15,1|-blw4w1,13,15,1|-blw4w0,40,45,0|-bcj3k1,40,45,0|-bcj3k0,13,15,1|-b36281,13,15,1|-b36280,40,45,0|-att0w1,40,45,0|-att0w0,13,15,1|-akfzk1,13,15,1|-akfzk0,40,45,0|-9scvk1,40,45,0|-9scvk0,13,15,1|-9imvk1,13,15,1|-9imvk0,40,45,0|-999u81,40,45,0|-999u80,13,15,1|-8zwsw1,13,15,1|-8zwsw0,40,45,0|-8qjrk1,40,45,0|-8qjrk0,13,15,1|-8h6q81,13,15,1|-8h6q80,40,45,0|-87tow1,40,45,0|-87tow0,13,15,1|-7ygnk1,13,15,1|-7ygnk0,40,45,0|-7p3m81,40,45,0|-7p3m80,13,15,1|-7fqkw1,13,15,1|-7fqkw0,40,45,0|-76djk1,40,45,0|-76djk0,13,15,1|-6wnjk1,13,15,1|-6wnjk0,40,45,0|-6nai81,40,45,0|-6nai80,13,15,1|-6dxgw1,13,15,1|-6dxgw0,40,45,0|-64kfk1,40,45,0|-64kfk0,13,15,1|-5v7e81,13,15,1|-5v7e80,40,45,0|-5lucw1,40,45,0|-5lucw0,13,15,1|-5chbk1,13,15,1|-5chbk0,40,45,0|-534a81,40,45,0|-534a80,13,15,1|-4tr8w1,13,15,1|-4tr8w0,40,45,0|-4ke7k1,40,45,0|-4ke7k0,13,15,1|-4b1681,13,15,1|-4b1680,40,45,0|-41o4w1,40,45,0|-41o4w0,13,15,1|-3ry4w1,13,15,1|-3ry4w0,40,45,0|-3il3k1,40,45,0|-3il3k0,13,15,1|-398281,13,15,1|-398280,40,45,0|-2zv0w1,40,45,0|-2zv0w0,13,15,1|-2qhzk1,13,15,1|-2qhzk0,40,45,0|-2h4y81,40,45,0|-2h4y80,13,15,1|-27rww1,13,15,1|-27rww0,40,45,0|-1yevk1,40,45,0|-1yevk0,13,15,0|3rwo3z,13,15,0|3rwo40,17,1,1|419pfz,17,1,1|419pg0,13,15,0|4azpfz,13,15,0|4azpg0,17,1,1|4kcqrz,17,1,1|4kcqs0,13,15,0|4tps3z,13,15,0|4tps40,17,1,1|532w7z,17,1,1|532w80,13,15,0|5cfurz,13,15,0|5cfus0,17,1,1|5lsyvz,17,1,1|5lsyw0,13,15,0|5v607z,13,15,0|5v6080,17,1,1|64j1jz,17,1,1|64j1k0,13,15,0|6dw2vz,13,15,0|6dw2w0,17,1,1|6n947z,17,1,1|6n9480,13,15,0|6wm8bz,13,15,0|6wm8c0,17,1,1|75z6vz,17,1,1|75z6w0,13,15,0|7fc87z,13,15,0|7fc880,17,1,1|7p287z,17,1,1|7p2880,13,15,0|7yf9jz,13,15,0|7yf9k0,17,1,1|87savz,17,1,1|87saw0,13,15,0|8h5c7z,13,15,0|8h5c80,17,1,1|8qidjz,17,1,1|8qidk0,13,15,0|8zvevz,13,15,0|8zvew0,17,1,1|998g7z,17,1,1|998g80,13,15,0|9ilhjz,13,15,0|9ilhk0,17,1,1|9ryivz,17,1,1|9ryiw0,13,15,0|a1bk7z,13,15,0|a1bk80,17,1,1|aaoljz,17,1,1|aaolk0,13,15,0|ak1mvz,13,15,0|ak1mw0,17,1,1|atrmvz,17,1,1|atrmw0,13,15,0|b34o7z,13,15,0|b34o80,17,1,1|bchpjz,17,1,1|bchpk0,13,15,0|bluqvz,13,15,0|bluqw0,17,1,1|bv7s7z,17,1,1|bv7s80,8,1,0|c4kqrz,8,1,0|c4kqs0,17,1,1|cdxs3z,17,1,1|cdxs40,13,15,0|cnatfz,13,15,0|cnatg0,17,1,1|cwnurz,17,1,1|cwnus0,13,15,0|d60w3z,13,15,0|d60w40,17,1,1|dfdxfz,17,1,1|dfdxg0,13,15,0|dp3xfz,13,15,0|dp3xg0,17,1,1|dzwtfz,17,1,1|dzwtg0,13,15,0|e7u03z,13,15,0|e7u040,17,1,1|eimw3z,17,1,1|eimw40,13,15,0|eqk2rz,13,15,0|eqk2s0,17,1,1|f1cyrz,17,1,1|f1cys0,13,15,0|f9a5fz,13,15,0|f9a5g0,17,1,1|fkg03z,17,1,1|fkg040,13,15,0|fs083z,13,15,0|fs0840,17,1,1|g362rz,17,1,1|g362s0,13,15,0|gaqarz,13,15,0|gaqas0,17,1,1|glw5fz,17,1,1|glw5g0,13,15,0|gttc3z,13,15,0|gttc40,17,1,1|h4m83z,17,1,1|h4m840,13,15,0|hcjerz,13,15,0|hcjes0,17,1,1|hncarz,17,1,1|hncas0,13,15,0|hv9hfz,13,15,0|hv9hg0,17,1,1|i6fc3z,17,1,1|i6fc40,13,15,0|idzk3z,13,15,0|idzk40,17,1,1|ip5erz,17,1,1|ip5es0,13,15,0|iwpmrz,13,15,0|iwpms0,17,1,1|j7vhfz,17,1,1|j7vhg0,13,15,0|jffpfz,13,15,0|jffpg0,17,1,1|jqlk3z,17,1,1|jqlk40,13,15,0|jyiqrz,13,15,0|jyiqs0,17,1,1|k9bmrz,17,1,1|k9bms0,13,15,0|kh8tfz,13,15,0|kh8tg0,17,1,1|ks1pfz,17,1,1|ks1pg0,13,15,0|kzyw3z,13,15,0|kzyw40,17,1,1|lb4qrz,17,1,1|lb4qs0,13,15,0|lioyrz,13,15,0|lioys0,17,1,1|ltutfz,17,1,1|ltutg0,13,15,0|m1f1fz,13,15,0|m1f1g0,17,1,1|mckw3z,17,1,1|mckw40,13,15,0|mki2rz,13,15,0|mki2s0,17,1,1|mvayrz,17,1,1|mvays0,13,15,0|n385fz,13,15,0|n385g0,17,1,1|ne11fz,17,1,1|ne11g0,13,15,0|nly83z,13,15,0|nly840,17,1,1|nwr43z,17,1,1|nwr440,13,15,0|o4oarz,13,15,0|o4oas0,17,1,1|ofu5fz,17,1,1|ofu5g0,13,15,0|onedfz,13,15,0|onedg0,17,1,1|oyk83z,17,1,1|oyk840,13,15,0|p64g3z,13,15,0|p64g40,17,1,1|phaarz,17,1,1|phaas0,13,15,0|pp7hfz,13,15,0|pp7hg0,17,1,1|q00dfz,17,1,1|q00dg0,13,15,0|q7xk3z,13,15,0|q7xk40,17,1,1|qiqg3z,17,1,1|qiqg40,13,15,0|qqnmrz,13,15,0|qqnms0,17,1,1|r1thfz,17,1,1|r1thg0,13,15,0|r9dpfz,13,15,0|r9dpg0,17,1,1|rkjk3z,17,1,1|rkjk40,13,15,0|rs3s3z,13,15,0|rs3s40,17,1,1|s39mrz,17,1,1|s39ms0,13,15,0|sb6tfz,13,15,0|sb6tg0,17,1,1|slzpfz,17,1,1|slzpg0,13,15,0|stww3z,13,15,0|stww40,17,1,1|t4ps3z,17,1,1|t4ps40,13,15,0|tcmyrz,13,15,0|tcmys0,17,1,1|tnfurz,17,1,1|tnfus0,13,15,0|tvd1fz,13,15,0|tvd1g0,17,1,1|u6iw3z,17,1,1|u6iw40,13,15,0|ue343z,13,15,0|ue3440,17,1,1|up8yrz,17,1,1|up8ys0,13,15,0|uwt6rz,13,15,0|uwt6s0,17,1,1|v7z1fz,17,1,1|v7z1g0,13,15,0|vfw83z,13,15,0|vfw840,17,1,1|vqp43z,17,1,1|vqp440,13,15,0|vymarz,13,15,0|vymas0,17,1,1|w9f6rz,17,1,1|w9f6s0,13,15,0|whcdfz,13,15,0|whcdg0,17,1,1|wsi83z,17,1,1|wsi840,13,15,0|x02g3z,13,15,0|x02g40,17,1,1|xb8arz,17,1,1|xb8as0,13,15,0|xisirz,13,15,0|xisis0,17,1,1|xtydfz,17,1,1|xtydg0,13,15,0|y1ilfz,13,15,0|y1ilg0,17,1,1|ycog3z,17,1,1|ycog40,13,15,0|yklmrz,13,15,0|yklms0,17,1,1|yveirz,17,1,1|yveis0,13,15,0|z3bpfz,13,15,0|z3bpg0,17,1,1|ze4lfz,17,1,1|ze4lg0,13,15,0\",\"Atlantic/Bermuda|,0,303,0|-15r0xbu,53,303,0|-rivvzv,53,303,0|-rivvzu,27,304,1|-r9qc3v,27,304,1|-r9qc3u,53,303,0|-qzp5bv,53,303,0|-qzp5bu,27,304,1|-qrq6rv,27,304,1|-qrq6ru,53,303,0|-kvj2fv,53,303,0|-kvj2fu,32,42,0|-eljwo1,32,42,0|-eljwo0,54,44,1|-e75gs1,54,44,1|-e75gs0,32,42,0|-dz87c1,32,42,0|-dz87c0,54,44,1|-dnpgs1,54,44,1|-dnpgs0,32,42,0|-dgv3c1,32,42,0|-dgv3c0,54,44,1|-d4mfg1,54,44,1|-d4mfg0,32,42,0|-cy50o1,32,42,0|-cy50o0,54,44,1|-clwcs1,54,44,1|-clwcs0,32,42,0|-bt38o1,32,42,0|-bt38o0,54,44,1|-bmyy41,54,44,1|-bmyy40,32,42,0|-ba07c1,32,42,0|-ba07c0,54,44,1|-b4lu41,54,44,1|-b4lu40,32,42,0|-ara4o1,32,42,0|-ara4o0,54,44,1|-alvrg1,54,44,1|-alvrg0,32,42,0|-a873c1,32,42,0|-a873c0,54,44,1|-a35os1,54,44,1|-a35os0,32,42,0|-9ph0o1,32,42,0|-9ph0o0,54,44,1|-9kfm41,54,44,1|-9kfm40,32,42,0|-96qy01,32,42,0|-96qy00,54,44,1|-91cks1,54,44,1|-91cks0,32,42,0|-73hoo1,32,42,0|-73hoo0,54,44,1|-6vkks1,54,44,1|-6vkks0,32,42,0|296onz,32,42,0|296oo0,54,44,1|2ijn7z,54,44,1|2ijn80,32,42,0|2rwrbz,32,42,0|2rwrc0,54,44,1|319pvz,54,44,1|319pw0,32,42,0|3amtzz,32,42,0|3amu00,54,44,1|3kcr7z,54,44,1|3kcr80,32,42,0|3tcwnz,32,42,0|3tcwo0,54,44,1|432tvz,54,44,1|432tw0,32,42,0|4cfxzz,32,42,0|4cfy00,54,44,1|4lswjz,54,44,1|4lswk0,32,42,0|4v60nz,32,42,0|4v60o0,54,44,1|54iz7z,54,44,1|54iz80,32,42,0|5dw3bz,32,42,0|5dw3c0,54,44,1|5n91vz,54,44,1|5n91w0,32,42,0|5wm5zz,32,42,0|5wm600,54,44,1|65z4jz,54,44,1|65z4k0,32,42,0|6fc8nz,32,42,0|6fc8o0,54,44,1|6p25vz,54,44,1|6p25w0,32,42,0|6y2bbz,32,42,0|6y2bc0,54,44,1|77s8jz,54,44,1|77s8k0,32,42,0|7h5cnz,32,42,0|7h5co0,54,44,1|7qib7z,54,44,1|7qib80,32,42,0|7zvfbz,32,42,0|7zvfc0,54,44,1|898dvz,54,44,1|898dw0,32,42,0|8ilhzz,32,42,0|8ili00,54,44,1|8rygjz,54,44,1|8rygk0,32,42,0|908onz,32,42,0|908oo0,54,44,1|9aoj7z,54,44,1|9aoj80,32,42,0|9iyrbz,32,42,0|9iyrc0,54,44,1|9trkjz,54,44,1|9trkk0,32,42,0|a1otzz,32,42,0|a1ou00,54,44,1|achn7z,54,44,1|achn80,32,42,0|akewnz,32,42,0|akewo0,54,44,1|av7pvz,54,44,1|av7pw0,32,42,0|b3hxzz,32,42,0|b3hy00,54,44,1|bdxsjz,54,44,1|bdxsk0,32,42,0|bm80nz,32,42,0|bm80o0,54,44,1|bwnv7z,54,44,1|bwnv80,32,42,0|c4y3bz,32,42,0|c4y3c0,54,44,1|cfqwjz,54,44,1|cfqwk0,32,42,0|cno5zz,32,42,0|cno600,54,44,1|cygz7z,54,44,1|cygz80,32,42,0|d6e8nz,32,42,0|d6e8o0,54,44,1|dh71vz,54,44,1|dh71w0,32,42,0|dph9zz,32,42,0|dpha00,54,44,1|dzx4jz,54,44,1|dzx4k0,32,42,0|e87cnz,32,42,0|e87co0,54,44,1|ein77z,54,44,1|ein780,32,42,0|eqxfbz,32,42,0|eqxfc0,54,44,1|f1d9vz,54,44,1|f1d9w0,32,42,0|f9nhzz,32,42,0|f9ni00,54,44,1|fkgb7z,54,44,1|fkgb80,32,42,0|fsdknz,32,42,0|fsdko0,54,44,1|g36dvz,54,44,1|g36dw0,32,42,0|gb3nbz,32,42,0|gb3nc0,54,44,1|glwgjz,54,44,1|glwgk0,32,42,0|gu6onz,32,42,0|gu6oo0,54,44,1|h4mj7z,54,44,1|h4mj80,32,42,0|hcwrbz,32,42,0|hcwrc0,54,44,1|hnclvz,54,44,1|hnclw0,32,42,0|hvmtzz,32,42,0|hvmu00,54,44,1|i6fn7z,54,44,1|i6fn80,32,42,0|iecwnz,32,42,0|iecwo0,54,44,1|ip5pvz,54,44,1|ip5pw0,32,42,0|ix2zbz,32,42,0|ix2zc0,54,44,1|j7vsjz,54,44,1|j7vsk0,32,42,0|jeq5zz,32,42,0|jeq600,54,44,1|jqytvz,54,44,1|jqytw0,32,42,0|jxg8nz,32,42,0|jxg8o0,54,44,1|k9owjz,54,44,1|k9owk0,32,42,0|kg6bbz,32,42,0|kg6bc0,54,44,1|ksez7z,54,44,1|ksez80,32,42,0|kz9cnz,32,42,0|kz9co0,54,44,1|lbi0jz,54,44,1|lbi0k0,32,42,0|lhzfbz,32,42,0|lhzfc0,54,44,1|lu837z,54,44,1|lu8380,32,42,0|m0phzz,32,42,0|m0pi00,54,44,1|mcy5vz,54,44,1|mcy5w0,32,42,0|mjfknz,32,42,0|mjfko0,54,44,1|mvo8jz,54,44,1|mvo8k0,32,42,0|n25nbz,32,42,0|n25nc0,54,44,1|neeb7z,54,44,1|neeb80,32,42,0|nkvpzz,32,42,0|nkvq00,54,44,1|nx4dvz,54,44,1|nx4dw0,32,42,0|o3yrbz,32,42,0|o3yrc0,54,44,1|og7f7z,54,44,1|og7f80,32,42,0|omotzz,32,42,0|omou00,54,44,1|oyxhvz,54,44,1|oyxhw0,32,42,0|p5ewnz,32,42,0|p5ewo0,54,44,1|phnkjz,54,44,1|phnkk0,32,42,0|po4zbz,32,42,0|po4zc0,54,44,1|q0dn7z,54,44,1|q0dn80,32,42,0|q6v1zz,32,42,0|q6v200,54,44,1|qj3pvz,54,44,1|qj3pw0,32,42,0|qpy3bz,32,42,0|qpy3c0,54,44,1|r26r7z,54,44,1|r26r80,32,42,0|r8o5zz,32,42,0|r8o600,54,44,1|rkwtvz,54,44,1|rkwtw0,32,42,0|rre8nz,32,42,0|rre8o0,54,44,1|s3mwjz,54,44,1|s3mwk0,32,42,0|sa4bbz,32,42,0|sa4bc0,54,44,1|smcz7z,54,44,1|smcz80,32,42,0|ssudzz,32,42,0|ssue00,54,44,1|t531vz,54,44,1|t531w0,32,42,0|tbkgnz,32,42,0|tbkgo0,54,44,1|tnt4jz,54,44,1|tnt4k0,32,42,0|tunhzz,32,42,0|tuni00,54,44,1|u6w5vz,54,44,1|u6w5w0,32,42,0|uddknz,32,42,0|uddko0,54,44,1|upm8jz,54,44,1|upm8k0,32,42,0|uw3nbz,32,42,0|uw3nc0,54,44,1|v8cb7z,54,44,1|v8cb80,32,42,0|vetpzz,32,42,0|vetq00,54,44,1|vr2dvz,54,44,1|vr2dw0,32,42,0|vxjsnz,32,42,0|vxjso0,54,44,1|w9sgjz,54,44,1|w9sgk0,32,42,0|wgmtzz,32,42,0|wgmu00,54,44,1|wsvhvz,54,44,1|wsvhw0,32,42,0|wzcwnz,32,42,0|wzcwo0,54,44,1|xblkjz,54,44,1|xblkk0,32,42,0|xi2zbz,32,42,0|xi2zc0,54,44,1|xubn7z,54,44,1|xubn80,32,42,0|y0t1zz,32,42,0|y0t200,54,44,1|yd1pvz,54,44,1|yd1pw0,32,42,0|yjj4nz,32,42,0|yjj4o0,54,44,1|yvrsjz,54,44,1|yvrsk0,32,42,0|z297bz,32,42,0|z297c0,54,44,1|zehv7z,54,44,1|zehv80,32,42,0\",\"Atlantic/Canary|,0,305,0|-oytbtc,13,15,0|-c4xh41,13,15,0|-c4xh40,8,1,0|5csqnz,8,1,0|5csqo0,9,10,1|5lsw3z,9,10,1|5lsw40,8,1,0|5v5xfz,8,1,0|5v5xg0,9,10,1|64iyrz,9,10,1|64iys0,8,1,0|6dw03z,8,1,0|6dw040,9,10,1|6n91fz,9,10,1|6n91g0,8,1,0|6wm2rz,8,1,0|6wm2s0,9,10,1|75z43z,9,10,1|75z440,8,1,0|7fc5fz,8,1,0|7fc5g0,9,10,1|7p25fz,9,10,1|7p25g0,8,1,0|7yf6rz,8,1,0|7yf6s0,9,10,1|87s83z,9,10,1|87s840,8,1,0|8h59fz,8,1,0|8h59g0,9,10,1|8qiarz,9,10,1|8qias0,8,1,0|8zvc3z,8,1,0|8zvc40,9,10,1|998dfz,9,10,1|998dg0,8,1,0|9ilerz,8,1,0|9iles0,9,10,1|9ryg3z,9,10,1|9ryg40,8,1,0|a1bhfz,8,1,0|a1bhg0,9,10,1|aaoirz,9,10,1|aaois0,8,1,0|ak1k3z,8,1,0|ak1k40,9,10,1|atrk3z,9,10,1|atrk40,8,1,0|b34lfz,8,1,0|b34lg0,9,10,1|bchmrz,9,10,1|bchms0,8,1,0|bluo3z,8,1,0|bluo40,9,10,1|bv7pfz,9,10,1|bv7pg0,8,1,0|c4kqrz,8,1,0|c4kqs0,9,10,1|cdxs3z,9,10,1|cdxs40,8,1,0|cnatfz,8,1,0|cnatg0,9,10,1|cwnurz,9,10,1|cwnus0,8,1,0|d60w3z,8,1,0|d60w40,9,10,1|dfdxfz,9,10,1|dfdxg0,8,1,0|dp3xfz,8,1,0|dp3xg0,9,10,1|dzwtfz,9,10,1|dzwtg0,8,1,0|e7u03z,8,1,0|e7u040,9,10,1|eimw3z,9,10,1|eimw40,8,1,0|eqk2rz,8,1,0|eqk2s0,9,10,1|f1cyrz,9,10,1|f1cys0,8,1,0|f9a5fz,8,1,0|f9a5g0,9,10,1|fkg03z,9,10,1|fkg040,8,1,0|fs083z,8,1,0|fs0840,9,10,1|g362rz,9,10,1|g362s0,8,1,0|gaqarz,8,1,0|gaqas0,9,10,1|glw5fz,9,10,1|glw5g0,8,1,0|gttc3z,8,1,0|gttc40,9,10,1|h4m83z,9,10,1|h4m840,8,1,0|hcjerz,8,1,0|hcjes0,9,10,1|hncarz,9,10,1|hncas0,8,1,0|hv9hfz,8,1,0|hv9hg0,9,10,1|i6fc3z,9,10,1|i6fc40,8,1,0|idzk3z,8,1,0|idzk40,9,10,1|ip5erz,9,10,1|ip5es0,8,1,0|iwpmrz,8,1,0|iwpms0,9,10,1|j7vhfz,9,10,1|j7vhg0,8,1,0|jffpfz,8,1,0|jffpg0,9,10,1|jqlk3z,9,10,1|jqlk40,8,1,0|jyiqrz,8,1,0|jyiqs0,9,10,1|k9bmrz,9,10,1|k9bms0,8,1,0|kh8tfz,8,1,0|kh8tg0,9,10,1|ks1pfz,9,10,1|ks1pg0,8,1,0|kzyw3z,8,1,0|kzyw40,9,10,1|lb4qrz,9,10,1|lb4qs0,8,1,0|lioyrz,8,1,0|lioys0,9,10,1|ltutfz,9,10,1|ltutg0,8,1,0|m1f1fz,8,1,0|m1f1g0,9,10,1|mckw3z,9,10,1|mckw40,8,1,0|mki2rz,8,1,0|mki2s0,9,10,1|mvayrz,9,10,1|mvays0,8,1,0|n385fz,8,1,0|n385g0,9,10,1|ne11fz,9,10,1|ne11g0,8,1,0|nly83z,8,1,0|nly840,9,10,1|nwr43z,9,10,1|nwr440,8,1,0|o4oarz,8,1,0|o4oas0,9,10,1|ofu5fz,9,10,1|ofu5g0,8,1,0|onedfz,8,1,0|onedg0,9,10,1|oyk83z,9,10,1|oyk840,8,1,0|p64g3z,8,1,0|p64g40,9,10,1|phaarz,9,10,1|phaas0,8,1,0|pp7hfz,8,1,0|pp7hg0,9,10,1|q00dfz,9,10,1|q00dg0,8,1,0|q7xk3z,8,1,0|q7xk40,9,10,1|qiqg3z,9,10,1|qiqg40,8,1,0|qqnmrz,8,1,0|qqnms0,9,10,1|r1thfz,9,10,1|r1thg0,8,1,0|r9dpfz,8,1,0|r9dpg0,9,10,1|rkjk3z,9,10,1|rkjk40,8,1,0|rs3s3z,8,1,0|rs3s40,9,10,1|s39mrz,9,10,1|s39ms0,8,1,0|sb6tfz,8,1,0|sb6tg0,9,10,1|slzpfz,9,10,1|slzpg0,8,1,0|stww3z,8,1,0|stww40,9,10,1|t4ps3z,9,10,1|t4ps40,8,1,0|tcmyrz,8,1,0|tcmys0,9,10,1|tnfurz,9,10,1|tnfus0,8,1,0|tvd1fz,8,1,0|tvd1g0,9,10,1|u6iw3z,9,10,1|u6iw40,8,1,0|ue343z,8,1,0|ue3440,9,10,1|up8yrz,9,10,1|up8ys0,8,1,0|uwt6rz,8,1,0|uwt6s0,9,10,1|v7z1fz,9,10,1|v7z1g0,8,1,0|vfw83z,8,1,0|vfw840,9,10,1|vqp43z,9,10,1|vqp440,8,1,0|vymarz,8,1,0|vymas0,9,10,1|w9f6rz,9,10,1|w9f6s0,8,1,0|whcdfz,8,1,0|whcdg0,9,10,1|wsi83z,9,10,1|wsi840,8,1,0|x02g3z,8,1,0|x02g40,9,10,1|xb8arz,9,10,1|xb8as0,8,1,0|xisirz,8,1,0|xisis0,9,10,1|xtydfz,9,10,1|xtydg0,8,1,0|y1ilfz,8,1,0|y1ilg0,9,10,1|ycog3z,9,10,1|ycog40,8,1,0|yklmrz,8,1,0|yklms0,9,10,1|yveirz,9,10,1|yveis0,8,1,0|z3bpfz,8,1,0|z3bpg0,9,10,1|ze4lfz,9,10,1|ze4lg0,8,1,0\",\"Atlantic/Cape_Verde|,0,306,0|-u9rbs0,40,45,0|-e9kqg1,40,45,0|-e9kqg0,13,15,1|-cmxp81,13,15,1|-cmxp80,40,45,0|32t73z,40,45,0|32t740,13,15,0\",\"Atlantic/Faroe|,0,307,0|-wcehew,8,1,0|5v5xfz,8,1,0|5v5xg0,9,10,1|64iyrz,9,10,1|64iys0,8,1,0|6dw03z,8,1,0|6dw040,9,10,1|6n91fz,9,10,1|6n91g0,8,1,0|6wm2rz,8,1,0|6wm2s0,9,10,1|75z43z,9,10,1|75z440,8,1,0|7fc5fz,8,1,0|7fc5g0,9,10,1|7p25fz,9,10,1|7p25g0,8,1,0|7yf6rz,8,1,0|7yf6s0,9,10,1|87s83z,9,10,1|87s840,8,1,0|8h59fz,8,1,0|8h59g0,9,10,1|8qiarz,9,10,1|8qias0,8,1,0|8zvc3z,8,1,0|8zvc40,9,10,1|998dfz,9,10,1|998dg0,8,1,0|9ilerz,8,1,0|9iles0,9,10,1|9ryg3z,9,10,1|9ryg40,8,1,0|a1bhfz,8,1,0|a1bhg0,9,10,1|aaoirz,9,10,1|aaois0,8,1,0|ak1k3z,8,1,0|ak1k40,9,10,1|atrk3z,9,10,1|atrk40,8,1,0|b34lfz,8,1,0|b34lg0,9,10,1|bchmrz,9,10,1|bchms0,8,1,0|bluo3z,8,1,0|bluo40,9,10,1|bv7pfz,9,10,1|bv7pg0,8,1,0|c4kqrz,8,1,0|c4kqs0,9,10,1|cdxs3z,9,10,1|cdxs40,8,1,0|cnatfz,8,1,0|cnatg0,9,10,1|cwnurz,9,10,1|cwnus0,8,1,0|d60w3z,8,1,0|d60w40,9,10,1|dfdxfz,9,10,1|dfdxg0,8,1,0|dp3xfz,8,1,0|dp3xg0,9,10,1|dzwtfz,9,10,1|dzwtg0,8,1,0|e7u03z,8,1,0|e7u040,9,10,1|eimw3z,9,10,1|eimw40,8,1,0|eqk2rz,8,1,0|eqk2s0,9,10,1|f1cyrz,9,10,1|f1cys0,8,1,0|f9a5fz,8,1,0|f9a5g0,9,10,1|fkg03z,9,10,1|fkg040,8,1,0|fs083z,8,1,0|fs0840,9,10,1|g362rz,9,10,1|g362s0,8,1,0|gaqarz,8,1,0|gaqas0,9,10,1|glw5fz,9,10,1|glw5g0,8,1,0|gttc3z,8,1,0|gttc40,9,10,1|h4m83z,9,10,1|h4m840,8,1,0|hcjerz,8,1,0|hcjes0,9,10,1|hncarz,9,10,1|hncas0,8,1,0|hv9hfz,8,1,0|hv9hg0,9,10,1|i6fc3z,9,10,1|i6fc40,8,1,0|idzk3z,8,1,0|idzk40,9,10,1|ip5erz,9,10,1|ip5es0,8,1,0|iwpmrz,8,1,0|iwpms0,9,10,1|j7vhfz,9,10,1|j7vhg0,8,1,0|jffpfz,8,1,0|jffpg0,9,10,1|jqlk3z,9,10,1|jqlk40,8,1,0|jyiqrz,8,1,0|jyiqs0,9,10,1|k9bmrz,9,10,1|k9bms0,8,1,0|kh8tfz,8,1,0|kh8tg0,9,10,1|ks1pfz,9,10,1|ks1pg0,8,1,0|kzyw3z,8,1,0|kzyw40,9,10,1|lb4qrz,9,10,1|lb4qs0,8,1,0|lioyrz,8,1,0|lioys0,9,10,1|ltutfz,9,10,1|ltutg0,8,1,0|m1f1fz,8,1,0|m1f1g0,9,10,1|mckw3z,9,10,1|mckw40,8,1,0|mki2rz,8,1,0|mki2s0,9,10,1|mvayrz,9,10,1|mvays0,8,1,0|n385fz,8,1,0|n385g0,9,10,1|ne11fz,9,10,1|ne11g0,8,1,0|nly83z,8,1,0|nly840,9,10,1|nwr43z,9,10,1|nwr440,8,1,0|o4oarz,8,1,0|o4oas0,9,10,1|ofu5fz,9,10,1|ofu5g0,8,1,0|onedfz,8,1,0|onedg0,9,10,1|oyk83z,9,10,1|oyk840,8,1,0|p64g3z,8,1,0|p64g40,9,10,1|phaarz,9,10,1|phaas0,8,1,0|pp7hfz,8,1,0|pp7hg0,9,10,1|q00dfz,9,10,1|q00dg0,8,1,0|q7xk3z,8,1,0|q7xk40,9,10,1|qiqg3z,9,10,1|qiqg40,8,1,0|qqnmrz,8,1,0|qqnms0,9,10,1|r1thfz,9,10,1|r1thg0,8,1,0|r9dpfz,8,1,0|r9dpg0,9,10,1|rkjk3z,9,10,1|rkjk40,8,1,0|rs3s3z,8,1,0|rs3s40,9,10,1|s39mrz,9,10,1|s39ms0,8,1,0|sb6tfz,8,1,0|sb6tg0,9,10,1|slzpfz,9,10,1|slzpg0,8,1,0|stww3z,8,1,0|stww40,9,10,1|t4ps3z,9,10,1|t4ps40,8,1,0|tcmyrz,8,1,0|tcmys0,9,10,1|tnfurz,9,10,1|tnfus0,8,1,0|tvd1fz,8,1,0|tvd1g0,9,10,1|u6iw3z,9,10,1|u6iw40,8,1,0|ue343z,8,1,0|ue3440,9,10,1|up8yrz,9,10,1|up8ys0,8,1,0|uwt6rz,8,1,0|uwt6s0,9,10,1|v7z1fz,9,10,1|v7z1g0,8,1,0|vfw83z,8,1,0|vfw840,9,10,1|vqp43z,9,10,1|vqp440,8,1,0|vymarz,8,1,0|vymas0,9,10,1|w9f6rz,9,10,1|w9f6s0,8,1,0|whcdfz,8,1,0|whcdg0,9,10,1|wsi83z,9,10,1|wsi840,8,1,0|x02g3z,8,1,0|x02g40,9,10,1|xb8arz,9,10,1|xb8as0,8,1,0|xisirz,8,1,0|xisis0,9,10,1|xtydfz,9,10,1|xtydg0,8,1,0|y1ilfz,8,1,0|y1ilg0,9,10,1|ycog3z,9,10,1|ycog40,8,1,0|yklmrz,8,1,0|yklms0,9,10,1|yveirz,9,10,1|yveis0,8,1,0|z3bpfz,8,1,0|z3bpg0,9,10,1|ze4lfz,9,10,1|ze4lg0,8,1,0\",\"Atlantic/Madeira|,0,308,0|-18vsfjc,137,308,0|-u9rek1,137,308,0|-u9rek0,13,15,0|-rxwyo1,13,15,0|-rxwyo0,17,1,1|-rqx181,17,1,1|-rqx180,13,15,0|-rkqw01,13,15,0|-rkqw00,17,1,1|-r90o01,17,1,1|-r90o00,13,15,0|-r1x401,13,15,0|-r1x400,17,1,1|-qq8qo1,17,1,1|-qq8qo0,13,15,0|-qj71c1,13,15,0|-qj71c0,17,1,1|-q7gtc1,17,1,1|-q7gtc0,13,15,0|-q0d9c1,13,15,0|-q0d9c0,17,1,1|-pon1c1,17,1,1|-pon1c0,13,15,0|-phlc01,13,15,0|-phlc00,17,1,1|-p5v401,17,1,1|-p5v400,13,15,0|-nuso01,13,15,0|-nuso00,17,1,1|-nlhhc1,17,1,1|-nlhhc0,13,15,0|-mt6yo1,13,15,0|-mt6yo0,17,1,1|-mkjuo1,17,1,1|-mkjuo0,13,15,0|-matuo1,13,15,0|-matuo0,17,1,1|-m1ts01,17,1,1|-m1ts00,13,15,0|-lrqtc1,13,15,0|-lrqtc0,17,1,1|-liqqo1,17,1,1|-liqqo0,13,15,0|-l8ns01,13,15,0|-l8ns00,17,1,1|-l00o01,17,1,1|-l00o00,13,15,0|-k77mo1,13,15,0|-k77mo0,17,1,1|-jykio1,17,1,1|-jykio0,13,15,0|-jp7hc1,13,15,0|-jp7hc0,17,1,1|-jfug01,17,1,1|-jfug00,13,15,0|-inedc1,13,15,0|-inedc0,17,1,1|-ie1c01,17,1,1|-ie1c00,13,15,0|-i519c1,13,15,0|-i519c0,17,1,1|-hvb9c1,17,1,1|-hvb9c0,13,15,0|-hl8ao1,13,15,0|-hl8ao0,17,1,1|-hcl6o1,17,1,1|-hcl6o0,13,15,0|-h385c1,13,15,0|-h385c0,17,1,1|-gtv401,17,1,1|-gtv400,13,15,0|-gkv1c1,13,15,0|-gkv1c0,17,1,1|-gb51c1,17,1,1|-gb51c0,13,15,0|-g122o1,13,15,0|-g122o0,17,1,1|-fpw801,17,1,1|-fpw800,13,15,0|-fkuqo1,13,15,0|-fkuqo0,17,1,1|-f9bxc1,17,1,1|-f9bxc0,13,15,0|-ezyw01,13,15,0|-ezyw00,17,1,1|-eqk001,17,1,1|-eqk000,13,15,0|-eibpc1,13,15,0|-eibpc0,17,1,1|-eg6041,17,1,1|-eg6040,18,10,1|-eaelg1,18,10,1|-eaelg0,17,1,1|-e6sw01,17,1,1|-e6sw00,13,15,0|-dzlmo1,13,15,0|-dzlmo0,17,1,1|-dxsw41,17,1,1|-dxsw40,18,10,1|-dqylg1,18,10,1|-dqylg0,17,1,1|-dnpuo1,17,1,1|-dnpuo0,13,15,0|-dgvk01,13,15,0|-dgvk00,17,1,1|-depus1,17,1,1|-depus0,18,10,1|-d88is1,18,10,1|-d88is0,17,1,1|-d4zs01,17,1,1|-d4zs00,13,15,0|-cy5hc1,13,15,0|-cy5hc0,17,1,1|-cvzs41,17,1,1|-cvzs40,18,10,1|-cpig41,18,10,1|-cpig40,17,1,1|-cm9pc1,17,1,1|-cm9pc0,13,15,0|-cdzk01,13,15,0|-cdzk00,17,1,1|-c4mio1,17,1,1|-c4mio0,13,15,0|-bv9901,13,15,0|-bv9900,17,1,1|-blw7o1,17,1,1|-blw7o0,13,15,0|-bcj6c1,13,15,0|-bcj6c0,17,1,1|-b36501,17,1,1|-b36500,13,15,0|-att3o1,13,15,0|-att3o0,17,1,1|-akg2c1,17,1,1|-akg2c0,13,15,0|-9scyc1,13,15,0|-9scyc0,17,1,1|-9imyc1,17,1,1|-9imyc0,13,15,0|-999x01,13,15,0|-999x00,17,1,1|-8zwvo1,17,1,1|-8zwvo0,13,15,0|-8qjuc1,13,15,0|-8qjuc0,17,1,1|-8h6t01,17,1,1|-8h6t00,13,15,0|-87tro1,13,15,0|-87tro0,17,1,1|-7ygqc1,17,1,1|-7ygqc0,13,15,0|-7p3p01,13,15,0|-7p3p00,17,1,1|-7fqno1,17,1,1|-7fqno0,13,15,0|-76dmc1,13,15,0|-76dmc0,17,1,1|-6wnmc1,17,1,1|-6wnmc0,13,15,0|-6nal01,13,15,0|-6nal00,17,1,1|-6dxjo1,17,1,1|-6dxjo0,13,15,0|-64kic1,13,15,0|-64kic0,17,1,1|-5v7h01,17,1,1|-5v7h00,13,15,0|-5lufo1,13,15,0|-5lufo0,17,1,1|-5chec1,17,1,1|-5chec0,13,15,0|-534d01,13,15,0|-534d00,17,1,1|-4trbo1,17,1,1|-4trbo0,13,15,0|-4keac1,13,15,0|-4keac0,17,1,1|-4b1901,17,1,1|-4b1900,13,15,0|-41o7o1,13,15,0|-41o7o0,17,1,1|-3ry7o1,17,1,1|-3ry7o0,13,15,0|-3il6c1,13,15,0|-3il6c0,17,1,1|-398501,17,1,1|-398500,13,15,0|-2zv3o1,13,15,0|-2zv3o0,17,1,1|-2qi2c1,17,1,1|-2qi2c0,13,15,0|-2h5101,13,15,0|-2h5100,17,1,1|-27rzo1,17,1,1|-27rzo0,13,15,0|-1yeyc1,13,15,0|-1yeyc0,8,1,0|3rwlbz,8,1,0|3rwlc0,9,10,1|419mnz,9,10,1|419mo0,8,1,0|4azmnz,8,1,0|4azmo0,9,10,1|4kcnzz,9,10,1|4kco00,8,1,0|4tppbz,8,1,0|4tppc0,9,10,1|532tfz,9,10,1|532tg0,8,1,0|5cfrzz,8,1,0|5cfs00,9,10,1|5lsw3z,9,10,1|5lsw40,8,1,0|5v5xfz,8,1,0|5v5xg0,9,10,1|64iyrz,9,10,1|64iys0,8,1,0|6dw03z,8,1,0|6dw040,9,10,1|6n91fz,9,10,1|6n91g0,8,1,0|6wm5jz,8,1,0|6wm5k0,9,10,1|75z43z,9,10,1|75z440,8,1,0|7fc5fz,8,1,0|7fc5g0,9,10,1|7p25fz,9,10,1|7p25g0,8,1,0|7yf6rz,8,1,0|7yf6s0,9,10,1|87s83z,9,10,1|87s840,8,1,0|8h59fz,8,1,0|8h59g0,9,10,1|8qiarz,9,10,1|8qias0,8,1,0|8zvc3z,8,1,0|8zvc40,9,10,1|998dfz,9,10,1|998dg0,8,1,0|9ilerz,8,1,0|9iles0,9,10,1|9ryg3z,9,10,1|9ryg40,8,1,0|a1bhfz,8,1,0|a1bhg0,9,10,1|aaoirz,9,10,1|aaois0,8,1,0|ak1k3z,8,1,0|ak1k40,9,10,1|atrk3z,9,10,1|atrk40,8,1,0|b34lfz,8,1,0|b34lg0,9,10,1|bchmrz,9,10,1|bchms0,8,1,0|bluo3z,8,1,0|bluo40,9,10,1|bv7pfz,9,10,1|bv7pg0,8,1,0|c4kqrz,8,1,0|c4kqs0,9,10,1|cdxs3z,9,10,1|cdxs40,8,1,0|cnatfz,8,1,0|cnatg0,9,10,1|cwnurz,9,10,1|cwnus0,8,1,0|d60w3z,8,1,0|d60w40,9,10,1|dfdxfz,9,10,1|dfdxg0,8,1,0|dp3xfz,8,1,0|dp3xg0,9,10,1|dzwtfz,9,10,1|dzwtg0,8,1,0|e7u03z,8,1,0|e7u040,9,10,1|eimw3z,9,10,1|eimw40,8,1,0|eqk2rz,8,1,0|eqk2s0,9,10,1|f1cyrz,9,10,1|f1cys0,8,1,0|f9a5fz,8,1,0|f9a5g0,9,10,1|fkg03z,9,10,1|fkg040,8,1,0|fs083z,8,1,0|fs0840,9,10,1|g362rz,9,10,1|g362s0,8,1,0|gaqarz,8,1,0|gaqas0,9,10,1|glw5fz,9,10,1|glw5g0,8,1,0|gttc3z,8,1,0|gttc40,9,10,1|h4m83z,9,10,1|h4m840,8,1,0|hcjerz,8,1,0|hcjes0,9,10,1|hncarz,9,10,1|hncas0,8,1,0|hv9hfz,8,1,0|hv9hg0,9,10,1|i6fc3z,9,10,1|i6fc40,8,1,0|idzk3z,8,1,0|idzk40,9,10,1|ip5erz,9,10,1|ip5es0,8,1,0|iwpmrz,8,1,0|iwpms0,9,10,1|j7vhfz,9,10,1|j7vhg0,8,1,0|jffpfz,8,1,0|jffpg0,9,10,1|jqlk3z,9,10,1|jqlk40,8,1,0|jyiqrz,8,1,0|jyiqs0,9,10,1|k9bmrz,9,10,1|k9bms0,8,1,0|kh8tfz,8,1,0|kh8tg0,9,10,1|ks1pfz,9,10,1|ks1pg0,8,1,0|kzyw3z,8,1,0|kzyw40,9,10,1|lb4qrz,9,10,1|lb4qs0,8,1,0|lioyrz,8,1,0|lioys0,9,10,1|ltutfz,9,10,1|ltutg0,8,1,0|m1f1fz,8,1,0|m1f1g0,9,10,1|mckw3z,9,10,1|mckw40,8,1,0|mki2rz,8,1,0|mki2s0,9,10,1|mvayrz,9,10,1|mvays0,8,1,0|n385fz,8,1,0|n385g0,9,10,1|ne11fz,9,10,1|ne11g0,8,1,0|nly83z,8,1,0|nly840,9,10,1|nwr43z,9,10,1|nwr440,8,1,0|o4oarz,8,1,0|o4oas0,9,10,1|ofu5fz,9,10,1|ofu5g0,8,1,0|onedfz,8,1,0|onedg0,9,10,1|oyk83z,9,10,1|oyk840,8,1,0|p64g3z,8,1,0|p64g40,9,10,1|phaarz,9,10,1|phaas0,8,1,0|pp7hfz,8,1,0|pp7hg0,9,10,1|q00dfz,9,10,1|q00dg0,8,1,0|q7xk3z,8,1,0|q7xk40,9,10,1|qiqg3z,9,10,1|qiqg40,8,1,0|qqnmrz,8,1,0|qqnms0,9,10,1|r1thfz,9,10,1|r1thg0,8,1,0|r9dpfz,8,1,0|r9dpg0,9,10,1|rkjk3z,9,10,1|rkjk40,8,1,0|rs3s3z,8,1,0|rs3s40,9,10,1|s39mrz,9,10,1|s39ms0,8,1,0|sb6tfz,8,1,0|sb6tg0,9,10,1|slzpfz,9,10,1|slzpg0,8,1,0|stww3z,8,1,0|stww40,9,10,1|t4ps3z,9,10,1|t4ps40,8,1,0|tcmyrz,8,1,0|tcmys0,9,10,1|tnfurz,9,10,1|tnfus0,8,1,0|tvd1fz,8,1,0|tvd1g0,9,10,1|u6iw3z,9,10,1|u6iw40,8,1,0|ue343z,8,1,0|ue3440,9,10,1|up8yrz,9,10,1|up8ys0,8,1,0|uwt6rz,8,1,0|uwt6s0,9,10,1|v7z1fz,9,10,1|v7z1g0,8,1,0|vfw83z,8,1,0|vfw840,9,10,1|vqp43z,9,10,1|vqp440,8,1,0|vymarz,8,1,0|vymas0,9,10,1|w9f6rz,9,10,1|w9f6s0,8,1,0|whcdfz,8,1,0|whcdg0,9,10,1|wsi83z,9,10,1|wsi840,8,1,0|x02g3z,8,1,0|x02g40,9,10,1|xb8arz,9,10,1|xb8as0,8,1,0|xisirz,8,1,0|xisis0,9,10,1|xtydfz,9,10,1|xtydg0,8,1,0|y1ilfz,8,1,0|y1ilg0,9,10,1|ycog3z,9,10,1|ycog40,8,1,0|yklmrz,8,1,0|yklms0,9,10,1|yveirz,9,10,1|yveis0,8,1,0|z3bpfz,8,1,0|z3bpg0,9,10,1|ze4lfz,9,10,1|ze4lg0,8,1,0\",\"Atlantic/Reykjavik|,0,309,0|-wcwx9c,13,15,0|-rl7k01,13,15,0|-rl7k00,17,1,1|-r8ph81,17,1,1|-r8ph80,13,15,0|-r2fmo1,13,15,0|-r2fmo0,17,1,1|-qolek1,17,1,1|-qolek0,13,15,0|-qjnpc1,13,15,0|-qjnpc0,17,1,1|-q5th81,17,1,1|-q5th80,13,15,0|-pgm5c1,13,15,0|-pgm5c0,17,1,1|-pbq581,17,1,1|-pbq580,13,15,0|-g0c5c1,13,15,0|-g0c5c0,17,1,1|-fqyyg1,17,1,1|-fqyyg0,13,15,0|-fkuic1,13,15,0|-fkuic0,17,1,1|-f7vx41,17,1,1|-f7vx40,13,15,0|-f1rjs1,13,15,0|-f1rjs0,17,1,1|-ep5ug1,17,1,1|-ep5ug0,13,15,0|-eioig1,13,15,0|-eioig0,17,1,1|-e6sqg1,17,1,1|-e6sqg0,13,15,0|-dzyfs1,13,15,0|-dzyfs0,17,1,1|-do2ns1,17,1,1|-do2ns0,13,15,0|-dh8d41,13,15,0|-dh8d40,17,1,1|-d5cl41,17,1,1|-d5cl40,13,15,0|-cyiag1,13,15,0|-cyiag0,17,1,1|-cm9js1,17,1,1|-cm9js0,13,15,0|-cfs7s1,13,15,0|-cfs7s0,17,1,1|-c3jh41,17,1,1|-c3jh40,13,15,0|-bv9bs1,13,15,0|-bv9bs0,17,1,1|-bkteg1,17,1,1|-bkteg0,13,15,0|-bcj941,13,15,0|-bcj940,17,1,1|-b23bs1,17,1,1|-b23bs0,13,15,0|-att6g1,13,15,0|-att6g0,17,1,1|-aj0ag1,17,1,1|-aj0ag0,13,15,0|-ab33s1,13,15,0|-ab33s0,17,1,1|-a0n6g1,17,1,1|-a0n6g0,13,15,0|-9sd141,13,15,0|-9sd140,17,1,1|-9hk541,17,1,1|-9hk540,13,15,0|-999zs1,13,15,0|-999zs0,17,1,1|-8yu2g1,17,1,1|-8yu2g0,13,15,0|-8qjx41,13,15,0|-8qjx40,17,1,1|-8g3zs1,17,1,1|-8g3zs0,13,15,0|-87tug1,13,15,0|-87tug0,17,1,1|-7xdx41,17,1,1|-7xdx40,13,15,0|-7p3rs1,13,15,0|-7p3rs0,17,1,1|-7enug1,17,1,1|-7enug0,13,15,0|-76dp41,13,15,0|-76dp40,17,1,1|-6vkt41,17,1,1|-6vkt40,13,15,0|-6nans1,13,15,0|-6nans0,17,1,1|-6cuqg1,17,1,1|-6cuqg0,13,15,0|-64kl41,13,15,0|-64kl40,17,1,1|-5u4ns1,17,1,1|-5u4ns0,13,15,0|-5luig1,13,15,0|-5luig0,17,1,1|-5bel41,17,1,1|-5bel40,13,15,0|-534fs1,13,15,0|-534fs0,17,1,1|-4soig1,17,1,1|-4soig0,13,15,0|-4ked41,13,15,0|-4ked40,17,1,1|-49yfs1,17,1,1|-49yfs0,13,15,0|-41oag1,13,15,0|-41oag0,17,1,1|-3qveg1,17,1,1|-3qveg0,13,15,0|-3il941,13,15,0|-3il940,17,1,1|-385bs1,17,1,1|-385bs0,13,15,0|-2zv6g1,13,15,0|-2zv6g0,17,1,1|-2pf941,17,1,1|-2pf940,13,15,0|-2h53s1,13,15,0|-2h53s0,17,1,1|-26p6g1,17,1,1|-26p6g0,13,15,0|-1yf141,13,15,0|-1yf140,17,1,1|-1nz3s1,17,1,1|-1nz3s0,13,15,0|-1foyg1,13,15,0|-1foyg0,17,1,1|-14w2g1,17,1,1|-14w2g0,13,15,0|-wlx41,13,15,0|-wlx40,1,1,0\",\"Atlantic/South_Georgia|,0,310,0|-15r12kg,40,45,0\",\"Atlantic/St_Helena|,0,12,0|-u9rgl4,1,1,0\",\"Atlantic/Stanley|,0,311,0|-15r0ymc,85,311,0|-u63pad,85,311,0|-u63pac,42,42,0|-gu7rk1,42,42,0|-gu7rk0,39,44,1|-gl7ro1,39,44,1|-gl7ro0,42,42,0|-gbhow1,42,42,0|-gbhow0,39,44,1|-g2hp01,39,44,1|-g2hp00,42,42,0|-fsenk1,42,42,0|-fsenk0,39,44,1|-fjeno1,39,44,1|-fjeno0,42,42,0|-f9okw1,42,42,0|-f9okw0,39,44,1|-f0ol01,39,44,1|-f0ol00,42,42,0|-eqyi81,42,42,0|-eqyi80,39,44,1|-ehyic1,39,44,1|-ehyic0,42,42,0|-e88fk1,42,42,0|-e88fk0,39,44,1|-e3aqc1,39,44,1|-e3aqc0,42,42,0|6yf4fz,42,42,0|6yf4g0,39,44,0|75z9nz,39,44,0|75z9o0,40,45,1|7h51jz,40,45,1|7h51k0,39,44,0|7ocdnz,39,44,0|7ocdo0,40,45,1|7zv47z,40,45,1|7zv480,39,44,0|872gbz,39,44,0|872gc0,39,44,1|8i8azz,39,44,1|8i8b00,42,42,0|8pslrz,42,42,0|8psls0,39,44,1|90ydnz,39,44,1|90ydo0,42,42,0|98iofz,42,42,0|98iog0,39,44,1|9jogbz,39,44,1|9jogc0,42,42,0|9r8r3z,42,42,0|9r8r40,39,44,1|a2eizz,39,44,1|a2ej00,42,42,0|a9ytrz,42,42,0|a9yts0,39,44,1|alhkbz,39,44,1|alhkc0,42,42,0|asowfz,42,42,0|asowg0,39,44,1|b47mzz,39,44,1|b47n00,42,42,0|bbrxrz,42,42,0|bbrxs0,39,44,1|bmxpnz,39,44,1|bmxpo0,42,42,0|bui0fz,42,42,0|bui0g0,39,44,1|c5nsbz,39,44,1|c5nsc0,42,42,0|cd833z,42,42,0|cd8340,39,44,1|coduzz,39,44,1|codv00,42,42,0|cvy5rz,42,42,0|cvy5s0,39,44,1|d73xnz,39,44,1|d73xo0,42,42,0|deo8fz,42,42,0|deo8g0,39,44,1|dq6yzz,39,44,1|dq6z00,42,42,0|dxr9rz,42,42,0|dxr9s0,39,44,1|e8x1nz,39,44,1|e8x1o0,42,42,0|eghcfz,42,42,0|eghcg0,39,44,1|ern4bz,39,44,1|ern4c0,42,42,0|ez7f3z,42,42,0|ez7f40,39,44,1|fad6zz,39,44,1|fad700,42,42,0|fhxhrz,42,42,0|fhxhs0,39,44,1|ft39nz,39,44,1|ft39o0,42,42,0|g0nkfz,42,42,0|g0nkg0,39,44,1|gbthvz,39,44,1|gbthw0,42,42,0|gj0tzz,42,42,0|gj0u00,39,44,1|guwj7z,39,44,1|guwj80,42,42,0|h1qwnz,42,42,0|h1qwo0,39,44,1|hdmlvz,39,44,1|hdmlw0,42,42,0|hktxzz,42,42,0|hkty00,39,44,1|hwcojz,39,44,1|hwcok0,42,42,0|i3k0nz,42,42,0|i3k0o0,39,44,1|if2r7z,39,44,1|if2r80,42,42,0|ima3bz,42,42,0|ima3c0,39,44,1|ixstvz,39,44,1|ixstw0,42,42,0|j505zz,42,42,0|j50600,39,44,1|jgiwjz,39,44,1|jgiwk0,42,42,0|jnq8nz,42,42,0|jnq8o0,39,44,1|jzlxvz,39,44,1|jzlxw0,42,42,0|k6t9zz,42,42,0|k6ta00,39,44,1|kic0jz,39,44,1|kic0k0,42,42,0|kpjcnz,42,42,0|kpjco0,39,44,1|l1237z,39,44,1|l12380,42,42,0|l89fbz,42,42,0|l89fc0,39,44,0\",\"Australia/Adelaide|,0,312,0|-133j2zw,138,224,0|-10vsp01,138,224,0|-10vsp00,138,248,0|-rnsq61,138,248,0|-rnsq60,139,313,1|-rjj0u1,139,313,1|-rjj0u0,138,248,0|-em3gu1,138,248,0|-em3gu0,139,313,1|-ehmcu1,139,313,1|-ehmcu0,138,248,0|-e89bi1,138,248,0|-e89bi0,139,313,1|-dywa61,139,313,1|-dywa60,138,248,0|-dp6a61,138,248,0|-dp6a60,139,313,1|-dg67i1,139,313,1|-dg67i0,138,248,0|ycghz,138,248,0|ycgi0,139,313,1|14gttz,139,313,1|14gtu0,138,248,0|1h2j5z,138,248,0|1h2j60,139,313,1|1njv5z,139,313,1|1njv60,138,248,0|1zsltz,138,248,0|1zslu0,139,313,1|269xtz,139,313,1|269xu0,138,248,0|2iiohz,138,248,0|2iioi0,139,313,1|2p00hz,139,313,1|2p00i0,138,248,0|318r5z,138,248,0|318r60,139,313,1|3831tz,139,313,1|3831u0,138,248,0|3kbshz,138,248,0|3kbsi0,139,313,1|3qt4hz,139,313,1|3qt4i0,138,248,0|431v5z,138,248,0|431v60,139,313,1|49j75z,139,313,1|49j760,138,248,0|4lrxtz,138,248,0|4lrxu0,139,313,1|4s99tz,139,313,1|4s99u0,138,248,0|54i0hz,138,248,0|54i0i0,139,313,1|5azchz,139,313,1|5azci0,138,248,0|5n835z,138,248,0|5n8360,139,313,1|5tpf5z,139,313,1|5tpf60,138,248,0|65y5tz,138,248,0|65y5u0,139,313,1|6csghz,139,313,1|6csgi0,138,248,0|6p175z,138,248,0|6p1760,139,313,1|6vij5z,139,313,1|6vij60,138,248,0|77r9tz,138,248,0|77r9u0,139,313,1|7e8ltz,139,313,1|7e8lu0,138,248,0|7qhchz,138,248,0|7qhci0,139,313,1|7wyohz,139,313,1|7wyoi0,138,248,0|897f5z,138,248,0|897f60,139,313,1|8geohz,139,313,1|8geoi0,138,248,0|8rkj5z,138,248,0|8rkj60,139,313,1|8z4r5z,139,313,1|8z4r60,138,248,0|9ankhz,138,248,0|9anki0,139,313,1|9i7shz,139,313,1|9i7si0,138,248,0|9tqltz,138,248,0|9tqlu0,139,313,1|a0xv5z,139,313,1|a0xv60,138,248,0|acgohz,138,248,0|acgoi0,139,313,1|ajnxtz,139,313,1|ajnxu0,138,248,0|av6r5z,138,248,0|av6r60,139,313,1|b1o35z,139,313,1|b1o360,138,248,0|bdwttz,138,248,0|bdwtu0,139,313,1|blh1tz,139,313,1|blh1u0,138,248,0|bwmwhz,138,248,0|bwmwi0,139,313,1|c3h75z,139,313,1|c3h760,138,248,0|cfpxtz,138,248,0|cfpxu0,139,313,1|cmx75z,139,313,1|cmx760,138,248,0|cyg0hz,138,248,0|cyg0i0,139,313,1|d608hz,139,313,1|d608i0,138,248,0|dh635z,138,248,0|dh6360,139,313,1|dp39tz,139,313,1|dp39u0,138,248,0|dzw5tz,138,248,0|dzw5u0,139,313,1|e7tchz,139,313,1|e7tci0,138,248,0|eim8hz,138,248,0|eim8i0,139,313,1|eqjf5z,139,313,1|eqjf60,138,248,0|f1cb5z,138,248,0|f1cb60,139,313,1|f99htz,139,313,1|f99hu0,138,248,0|fkfchz,138,248,0|fkfci0,139,313,1|frzkhz,139,313,1|frzki0,138,248,0|g35f5z,138,248,0|g35f60,139,313,1|gapn5z,139,313,1|gapn60,138,248,0|glvhtz,138,248,0|glvhu0,139,313,1|gtsohz,139,313,1|gtsoi0,138,248,0|h4lkhz,138,248,0|h4lki0,139,313,1|hcir5z,139,313,1|hcir60,138,248,0|hnbn5z,138,248,0|hnbn60,139,313,1|hv8ttz,139,313,1|hv8tu0,138,248,0|i6eohz,138,248,0|i6eoi0,139,313,1|idywhz,139,313,1|idywi0,138,248,0|ip4r5z,138,248,0|ip4r60,139,313,1|ix1xtz,139,313,1|ix1xu0,138,248,0|j7uttz,138,248,0|j7utu0,139,313,1|jff1tz,139,313,1|jff1u0,138,248,0|jqkwhz,138,248,0|jqkwi0,139,313,1|jyv1tz,139,313,1|jyv1u0,138,248,0|k8835z,138,248,0|k88360,139,313,1|khl4hz,139,313,1|khl4i0,138,248,0|kqy5tz,138,248,0|kqy5u0,139,313,1|l0b75z,139,313,1|l0b760,138,248,0|l9o8hz,138,248,0|l9o8i0,139,313,1|lj19tz,139,313,1|lj19u0,138,248,0|lseb5z,138,248,0|lseb60,139,313,1|m1rchz,139,313,1|m1rci0,138,248,0|mbhchz,138,248,0|mbhci0,139,313,1|mkudtz,139,313,1|mkudu0,138,248,0|mu7f5z,138,248,0|mu7f60,139,313,1|n3kghz,139,313,1|n3kgi0,138,248,0|ncxhtz,138,248,0|ncxhu0,139,313,1|nmaj5z,139,313,1|nmaj60,138,248,0|nvnkhz,138,248,0|nvnki0,139,313,1|o50ltz,139,313,1|o50lu0,138,248,0|oedn5z,138,248,0|oedn60,139,313,1|onqohz,139,313,1|onqoi0,138,248,0|ox3ptz,138,248,0|ox3pu0,139,313,1|p6gr5z,139,313,1|p6gr60,138,248,0|pg6r5z,138,248,0|pg6r60,139,313,1|ppjshz,139,313,1|ppjsi0,138,248,0|pywttz,138,248,0|pywtu0,139,313,1|q89v5z,139,313,1|q89v60,138,248,0|qhmwhz,138,248,0|qhmwi0,139,313,1|qqzxtz,139,313,1|qqzxu0,138,248,0|r0cz5z,138,248,0|r0cz60,139,313,1|r9q0hz,139,313,1|r9q0i0,138,248,0|rj31tz,138,248,0|rj31u0,139,313,1|rsg35z,139,313,1|rsg360,138,248,0|s1t4hz,138,248,0|s1t4i0,139,313,1|sbj4hz,139,313,1|sbj4i0,138,248,0|skw5tz,138,248,0|skw5u0,139,313,1|su975z,139,313,1|su9760,138,248,0|t3m8hz,138,248,0|t3m8i0,139,313,1|tcz9tz,139,313,1|tcz9u0,138,248,0|tmcb5z,138,248,0|tmcb60,139,313,1|tvpchz,139,313,1|tvpci0,138,248,0|u52dtz,138,248,0|u52du0,139,313,1|ueff5z,139,313,1|ueff60,138,248,0|unsghz,138,248,0|unsgi0,139,313,1|ux5htz,139,313,1|ux5hu0,138,248,0|v6vhtz,138,248,0|v6vhu0,139,313,1|vg8j5z,139,313,1|vg8j60,138,248,0|vplkhz,138,248,0|vplki0,139,313,1|vyyltz,139,313,1|vyylu0,138,248,0|w8bn5z,138,248,0|w8bn60,139,313,1|whoohz,139,313,1|whooi0,138,248,0|wr1ptz,138,248,0|wr1pu0,139,313,1|x0er5z,139,313,1|x0er60,138,248,0|x9rshz,138,248,0|x9rsi0,139,313,1|xj4ttz,139,313,1|xj4tu0,138,248,0|xshv5z,138,248,0|xshv60,139,313,1|y1uwhz,139,313,1|y1uwi0,138,248,0|ybkwhz,138,248,0|ybkwi0,139,313,1|ykxxtz,139,313,1|ykxxu0,138,248,0|yuaz5z,138,248,0|yuaz60,139,313,1|z3o0hz,139,313,1|z3o0i0,138,248,0|zd11tz,138,248,0|zd11u0,139,313,1\",\"Australia/Brisbane|,0,314,0|-1354kc8,94,195,0|-rnsrk1,94,195,0|-rnsrk0,95,192,1|-rjj281,95,192,1|-rjj280,94,195,0|-em3i81,94,195,0|-em3i80,95,192,1|-ehme81,95,192,1|-ehme80,94,195,0|-e89cw1,94,195,0|-e89cw0,95,192,1|-dywbk1,95,192,1|-dywbk0,94,195,0|-dp6bk1,94,195,0|-dp6bk0,95,192,1|-dg68w1,95,192,1|-dg68w0,94,195,0|ycf3z,94,195,0|ycf40,95,192,1|14gsfz,95,192,1|14gsg0,94,195,0|acgn3z,94,195,0|acgn40,95,192,1|aixz3z,95,192,1|aixz40,94,195,0|av6prz,94,195,0|av6ps0,95,192,1|b1o1rz,95,192,1|b1o1s0,94,195,0|bdwsfz,94,195,0|bdwsg0,95,192,1|bke4fz,95,192,1|bke4g0,94,195,0\",\"Australia/Broken_Hill|,0,315,0|-133j3j0,94,195,0|-12a9fs1,94,195,0|-12a9fs0,138,224,0|-10vsp01,138,224,0|-10vsp00,138,248,0|-rnsq61,138,248,0|-rnsq60,139,313,1|-rjj0u1,139,313,1|-rjj0u0,138,248,0|-em3gu1,138,248,0|-em3gu0,139,313,1|-ehmcu1,139,313,1|-ehmcu0,138,248,0|-e89bi1,138,248,0|-e89bi0,139,313,1|-dywa61,139,313,1|-dywa60,138,248,0|-dp6a61,138,248,0|-dp6a60,139,313,1|-dg67i1,139,313,1|-dg67i0,138,248,0|ycghz,138,248,0|ycgi0,139,313,1|14gttz,139,313,1|14gtu0,138,248,0|1h2j5z,138,248,0|1h2j60,139,313,1|1njv5z,139,313,1|1njv60,138,248,0|1zsltz,138,248,0|1zslu0,139,313,1|269xtz,139,313,1|269xu0,138,248,0|2iiohz,138,248,0|2iioi0,139,313,1|2p00hz,139,313,1|2p00i0,138,248,0|318r5z,138,248,0|318r60,139,313,1|3831tz,139,313,1|3831u0,138,248,0|3kbshz,138,248,0|3kbsi0,139,313,1|3qt4hz,139,313,1|3qt4i0,138,248,0|431v5z,138,248,0|431v60,139,313,1|49j75z,139,313,1|49j760,138,248,0|4lrxtz,138,248,0|4lrxu0,139,313,1|4s99tz,139,313,1|4s99u0,138,248,0|54i0hz,138,248,0|54i0i0,139,313,1|5azchz,139,313,1|5azci0,138,248,0|5n835z,138,248,0|5n8360,139,313,1|5tpf5z,139,313,1|5tpf60,138,248,0|65y5tz,138,248,0|65y5u0,139,313,1|6e8b5z,139,313,1|6e8b60,138,248,0|6p175z,138,248,0|6p1760,139,313,1|6vij5z,139,313,1|6vij60,138,248,0|77r9tz,138,248,0|77r9u0,139,313,1|7e8ltz,139,313,1|7e8lu0,138,248,0|7qhchz,138,248,0|7qhci0,139,313,1|7wyohz,139,313,1|7wyoi0,138,248,0|897f5z,138,248,0|897f60,139,313,1|8geohz,139,313,1|8geoi0,138,248,0|8rkj5z,138,248,0|8rkj60,139,313,1|8z4r5z,139,313,1|8z4r60,138,248,0|9ankhz,138,248,0|9anki0,139,313,1|9i7shz,139,313,1|9i7si0,138,248,0|9tqltz,138,248,0|9tqlu0,139,313,1|a0xv5z,139,313,1|a0xv60,138,248,0|acgohz,138,248,0|acgoi0,139,313,1|aiy0hz,139,313,1|aiy0i0,138,248,0|av6r5z,138,248,0|av6r60,139,313,1|b1o35z,139,313,1|b1o360,138,248,0|bdwttz,138,248,0|bdwtu0,139,313,1|bke5tz,139,313,1|bke5u0,138,248,0|bwmwhz,138,248,0|bwmwi0,139,313,1|c3h75z,139,313,1|c3h760,138,248,0|cfpxtz,138,248,0|cfpxu0,139,313,1|cm79tz,139,313,1|cm79u0,138,248,0|cyg0hz,138,248,0|cyg0i0,139,313,1|d4xchz,139,313,1|d4xci0,138,248,0|dh635z,138,248,0|dh6360,139,313,1|dp39tz,139,313,1|dp39u0,138,248,0|dzw5tz,138,248,0|dzw5u0,139,313,1|e7tchz,139,313,1|e7tci0,138,248,0|eim8hz,138,248,0|eim8i0,139,313,1|eqjf5z,139,313,1|eqjf60,138,248,0|f1cb5z,138,248,0|f1cb60,139,313,1|f99htz,139,313,1|f99hu0,138,248,0|fkfchz,138,248,0|fkfci0,139,313,1|frzkhz,139,313,1|frzki0,138,248,0|g35f5z,138,248,0|g35f60,139,313,1|gapn5z,139,313,1|gapn60,138,248,0|glvhtz,138,248,0|glvhu0,139,313,1|gtsohz,139,313,1|gtsoi0,138,248,0|h4lkhz,138,248,0|h4lki0,139,313,1|hcir5z,139,313,1|hcir60,138,248,0|hnbn5z,138,248,0|hnbn60,139,313,1|hv8ttz,139,313,1|hv8tu0,138,248,0|i6eohz,138,248,0|i6eoi0,139,313,1|idywhz,139,313,1|idywi0,138,248,0|ip4r5z,138,248,0|ip4r60,139,313,1|ix1xtz,139,313,1|ix1xu0,138,248,0|j7uttz,138,248,0|j7utu0,139,313,1|jff1tz,139,313,1|jff1u0,138,248,0|jqkwhz,138,248,0|jqkwi0,139,313,1|jyv1tz,139,313,1|jyv1u0,138,248,0|k8835z,138,248,0|k88360,139,313,1|khl4hz,139,313,1|khl4i0,138,248,0|kqy5tz,138,248,0|kqy5u0,139,313,1|l0b75z,139,313,1|l0b760,138,248,0|l9o8hz,138,248,0|l9o8i0,139,313,1|lj19tz,139,313,1|lj19u0,138,248,0|lseb5z,138,248,0|lseb60,139,313,1|m1rchz,139,313,1|m1rci0,138,248,0|mbhchz,138,248,0|mbhci0,139,313,1|mkudtz,139,313,1|mkudu0,138,248,0|mu7f5z,138,248,0|mu7f60,139,313,1|n3kghz,139,313,1|n3kgi0,138,248,0|ncxhtz,138,248,0|ncxhu0,139,313,1|nmaj5z,139,313,1|nmaj60,138,248,0|nvnkhz,138,248,0|nvnki0,139,313,1|o50ltz,139,313,1|o50lu0,138,248,0|oedn5z,138,248,0|oedn60,139,313,1|onqohz,139,313,1|onqoi0,138,248,0|ox3ptz,138,248,0|ox3pu0,139,313,1|p6gr5z,139,313,1|p6gr60,138,248,0|pg6r5z,138,248,0|pg6r60,139,313,1|ppjshz,139,313,1|ppjsi0,138,248,0|pywttz,138,248,0|pywtu0,139,313,1|q89v5z,139,313,1|q89v60,138,248,0|qhmwhz,138,248,0|qhmwi0,139,313,1|qqzxtz,139,313,1|qqzxu0,138,248,0|r0cz5z,138,248,0|r0cz60,139,313,1|r9q0hz,139,313,1|r9q0i0,138,248,0|rj31tz,138,248,0|rj31u0,139,313,1|rsg35z,139,313,1|rsg360,138,248,0|s1t4hz,138,248,0|s1t4i0,139,313,1|sbj4hz,139,313,1|sbj4i0,138,248,0|skw5tz,138,248,0|skw5u0,139,313,1|su975z,139,313,1|su9760,138,248,0|t3m8hz,138,248,0|t3m8i0,139,313,1|tcz9tz,139,313,1|tcz9u0,138,248,0|tmcb5z,138,248,0|tmcb60,139,313,1|tvpchz,139,313,1|tvpci0,138,248,0|u52dtz,138,248,0|u52du0,139,313,1|ueff5z,139,313,1|ueff60,138,248,0|unsghz,138,248,0|unsgi0,139,313,1|ux5htz,139,313,1|ux5hu0,138,248,0|v6vhtz,138,248,0|v6vhu0,139,313,1|vg8j5z,139,313,1|vg8j60,138,248,0|vplkhz,138,248,0|vplki0,139,313,1|vyyltz,139,313,1|vyylu0,138,248,0|w8bn5z,138,248,0|w8bn60,139,313,1|whoohz,139,313,1|whooi0,138,248,0|wr1ptz,138,248,0|wr1pu0,139,313,1|x0er5z,139,313,1|x0er60,138,248,0|x9rshz,138,248,0|x9rsi0,139,313,1|xj4ttz,139,313,1|xj4tu0,138,248,0|xshv5z,138,248,0|xshv60,139,313,1|y1uwhz,139,313,1|y1uwi0,138,248,0|ybkwhz,138,248,0|ybkwi0,139,313,1|ykxxtz,139,313,1|ykxxu0,138,248,0|yuaz5z,138,248,0|yuaz60,139,313,1|z3o0hz,139,313,1|z3o0i0,138,248,0|zd11tz,138,248,0|zd11u0,139,313,1\",\"Australia/Currie|,0,316,0|-12smja4,94,195,0|-rsj4w1,94,195,0|-rsj4w0,95,192,1|-rjj281,95,192,1|-rjj280,94,195,0|-r8d7k1,94,195,0|-r8d7k0,95,192,1|-r1vvk1,95,192,1|-r1vvk0,94,195,0|-qpn4w1,94,195,0|-qpn4w0,95,192,1|-qj5sw1,95,192,1|-qj5sw0,94,195,0|-em3i81,94,195,0|-em3i80,95,192,1|-ehme81,95,192,1|-ehme80,94,195,0|-e89cw1,94,195,0|-e89cw0,95,192,1|-dywbk1,95,192,1|-dywbk0,94,195,0|-dp6bk1,94,195,0|-dp6bk0,95,192,1|-dg68w1,95,192,1|-dg68w0,94,195,0|-16cow1,94,195,0|-16cow0,95,192,1|-wznk1,95,192,1|-wznk0,94,195,0|-m6rk1,94,195,0|-m6rk0,95,192,1|-fcgw1,95,192,1|-fcgw0,94,195,0|-3gow1,94,195,0|-3gow0,95,192,1|3dlrz,95,192,1|3dls0,94,195,0|f9drz,94,195,0|f9ds0,95,192,1|mgn3z,95,192,1|mgn40,94,195,0|ycf3z,94,195,0|ycf40,95,192,1|14gsfz,95,192,1|14gsg0,94,195,0|1h2hrz,94,195,0|1h2hs0,95,192,1|1njtrz,95,192,1|1njts0,94,195,0|1zskfz,94,195,0|1zskg0,95,192,1|269wfz,95,192,1|269wg0,94,195,0|2iin3z,94,195,0|2iin40,95,192,1|2ozz3z,95,192,1|2ozz40,94,195,0|318prz,94,195,0|318ps0,95,192,1|3830fz,95,192,1|3830g0,94,195,0|3kbr3z,94,195,0|3kbr40,95,192,1|3qt33z,95,192,1|3qt340,94,195,0|431trz,94,195,0|431ts0,95,192,1|49j5rz,95,192,1|49j5s0,94,195,0|4lrwfz,94,195,0|4lrwg0,95,192,1|4s98fz,95,192,1|4s98g0,94,195,0|54hz3z,94,195,0|54hz40,95,192,1|5azb3z,95,192,1|5azb40,94,195,0|5n81rz,94,195,0|5n81s0,95,192,1|5tpdrz,95,192,1|5tpds0,94,195,0|65y4fz,94,195,0|65y4g0,95,192,1|6dvb3z,95,192,1|6dvb40,94,195,0|6p15rz,94,195,0|6p15s0,95,192,1|6wldrz,95,192,1|6wlds0,94,195,0|77r8fz,94,195,0|77r8g0,95,192,1|7e8kfz,95,192,1|7e8kg0,94,195,0|7qhb3z,94,195,0|7qhb40,95,192,1|7wyn3z,95,192,1|7wyn40,94,195,0|897drz,94,195,0|897ds0,95,192,1|8foprz,95,192,1|8fops0,94,195,0|8rkhrz,94,195,0|8rkhs0,95,192,1|8z4prz,95,192,1|8z4ps0,94,195,0|9anj3z,94,195,0|9anj40,95,192,1|9i7r3z,95,192,1|9i7r40,94,195,0|9tqkfz,94,195,0|9tqkg0,95,192,1|a0xtrz,95,192,1|a0xts0,94,195,0|acgn3z,94,195,0|acgn40,95,192,1|ajnwfz,95,192,1|ajnwg0,94,195,0|av6prz,94,195,0|av6ps0,95,192,1|b33wfz,95,192,1|b33wg0,94,195,0|bctwfz,94,195,0|bctwg0,95,192,1|bltz3z,95,192,1|bltz40,94,195,0|bvjz3z,94,195,0|bvjz40,95,192,1|c4k1rz,95,192,1|c4k1s0,94,195,0|cea1rz,94,195,0|cea1s0,95,192,1|cna4fz,95,192,1|cna4g0,94,195,0|cx04fz,94,195,0|cx04g0,95,192,1|d6073z,95,192,1|d60740,94,195,0|dfq73z,94,195,0|dfq740,95,192,1|dp38fz,95,192,1|dp38g0,94,195,0|dyt8fz,94,195,0|dyt8g0,95,192,1|e7tb3z,95,192,1|e7tb40,94,195,0|ehjb3z,94,195,0|ehjb40,95,192,1|eqjdrz,95,192,1|eqjds0,94,195,0|f09drz,94,195,0|f09ds0,95,192,1|f99gfz,95,192,1|f99gg0,94,195,0|fizgfz,94,195,0|fizgg0,95,192,1|frzj3z,95,192,1|frzj40,94,195,0|fzwprz,94,195,0|fzwps0,95,192,1|gaplrz,95,192,1|gapls0,94,195,0|gkskfz,94,195,0|gkskg0,95,192,1|gtsn3z,95,192,1|gtsn40,94,195,0|h3in3z,94,195,0|h3in40,95,192,1|hciprz,95,192,1|hcips0,94,195,0|hm8prz,94,195,0|hm8ps0,95,192,1|hv8sfz,95,192,1|hv8sg0,94,195,0|i4ysfz,94,195,0|i4ysg0,95,192,1|idyv3z,95,192,1|idyv40,94,195,0|inov3z,94,195,0|inov40,95,192,1|ix1wfz,95,192,1|ix1wg0,94,195,0|j6exrz,94,195,0|j6exs0,95,192,1|jff0fz,95,192,1|jff0g0,94,195,0|jphz3z,94,195,0|jphz40,95,192,1|jyv0fz,95,192,1|jyv0g0,94,195,0|k881rz,94,195,0|k881s0,95,192,1|khl33z,95,192,1|khl340,94,195,0|kqy4fz,94,195,0|kqy4g0,95,192,1|l0b5rz,95,192,1|l0b5s0,94,195,0|l9o73z,94,195,0|l9o740,95,192,1|lj18fz,95,192,1|lj18g0,94,195,0|lse9rz,94,195,0|lse9s0,95,192,1|m1rb3z,95,192,1|m1rb40,94,195,0|mbhb3z,94,195,0|mbhb40,95,192,1|mkucfz,95,192,1|mkucg0,94,195,0|mu7drz,94,195,0|mu7ds0,95,192,1|n3kf3z,95,192,1|n3kf40,94,195,0|ncxgfz,94,195,0|ncxgg0,95,192,1|nmahrz,95,192,1|nmahs0,94,195,0|nvnj3z,94,195,0|nvnj40,95,192,1|o50kfz,95,192,1|o50kg0,94,195,0|oedlrz,94,195,0|oedls0,95,192,1|onqn3z,95,192,1|onqn40,94,195,0|ox3ofz,94,195,0|ox3og0,95,192,1|p6gprz,95,192,1|p6gps0,94,195,0|pg6prz,94,195,0|pg6ps0,95,192,1|ppjr3z,95,192,1|ppjr40,94,195,0|pywsfz,94,195,0|pywsg0,95,192,1|q89trz,95,192,1|q89ts0,94,195,0|qhmv3z,94,195,0|qhmv40,95,192,1|qqzwfz,95,192,1|qqzwg0,94,195,0|r0cxrz,94,195,0|r0cxs0,95,192,1|r9pz3z,95,192,1|r9pz40,94,195,0|rj30fz,94,195,0|rj30g0,95,192,1|rsg1rz,95,192,1|rsg1s0,94,195,0|s1t33z,94,195,0|s1t340,95,192,1|sbj33z,95,192,1|sbj340,94,195,0|skw4fz,94,195,0|skw4g0,95,192,1|su95rz,95,192,1|su95s0,94,195,0|t3m73z,94,195,0|t3m740,95,192,1|tcz8fz,95,192,1|tcz8g0,94,195,0|tmc9rz,94,195,0|tmc9s0,95,192,1|tvpb3z,95,192,1|tvpb40,94,195,0|u52cfz,94,195,0|u52cg0,95,192,1|uefdrz,95,192,1|uefds0,94,195,0|unsf3z,94,195,0|unsf40,95,192,1|ux5gfz,95,192,1|ux5gg0,94,195,0|v6vgfz,94,195,0|v6vgg0,95,192,1|vg8hrz,95,192,1|vg8hs0,94,195,0|vplj3z,94,195,0|vplj40,95,192,1|vyykfz,95,192,1|vyykg0,94,195,0|w8blrz,94,195,0|w8bls0,95,192,1|whon3z,95,192,1|whon40,94,195,0|wr1ofz,94,195,0|wr1og0,95,192,1|x0eprz,95,192,1|x0eps0,94,195,0|x9rr3z,94,195,0|x9rr40,95,192,1|xj4sfz,95,192,1|xj4sg0,94,195,0|xshtrz,94,195,0|xshts0,95,192,1|y1uv3z,95,192,1|y1uv40,94,195,0|ybkv3z,94,195,0|ybkv40,95,192,1|ykxwfz,95,192,1|ykxwg0,94,195,0|yuaxrz,94,195,0|yuaxs0,95,192,1|z3nz3z,95,192,1|z3nz40,94,195,0|zd10fz,94,195,0|zd10g0,95,192,1\",\"Australia/Darwin|,0,317,0|-133j1k8,138,224,0|-10vsp01,138,224,0|-10vsp00,138,248,0|-rnsq61,138,248,0|-rnsq60,139,313,1|-rjj0u1,139,313,1|-rjj0u0,138,248,0|-em3gu1,138,248,0|-em3gu0,139,313,1|-ehmcu1,139,313,1|-ehmcu0,138,248,0|-e89bi1,138,248,0|-e89bi0,139,313,1|-dywa61,139,313,1|-dywa60,138,248,0|-dp6a61,138,248,0|-dp6a60,139,313,1|-dg67i1,139,313,1|-dg67i0,138,248,0\",\"Australia/Eucla|,0,318,0|-12nxx74,140,319,0|-rnso31,140,319,0|-rnso30,141,320,1|-rjiyr1,141,320,1|-rjiyr0,140,319,0|-em3er1,140,319,0|-em3er0,141,320,1|-ehmar1,141,320,1|-ehmar0,140,319,0|-e899f1,140,319,0|-e899f0,141,320,1|-dyw831,141,320,1|-dyw830,140,319,0|2iiqkz,140,319,0|2iiql0,141,320,1|2p02kz,141,320,1|2p02l0,140,319,0|77rbwz,140,319,0|77rbx0,141,320,1|7e8nwz,141,320,1|7e8nx0,140,319,0|bezrwz,140,319,0|bezrx0,141,320,1|bke7wz,141,320,1|bke7x0,140,319,0|j9np8z,140,319,0|j9np90,141,320,1|jff3wz,141,320,1|jff3x0,140,319,0|jqkykz,140,319,0|jqkyl0,141,320,1|jyi58z,141,320,1|jyi590,140,319,0|k9b18z,140,319,0|k9b190,141,320,1|kh87wz,141,320,1|kh87x0,140,319,0\",\"Australia/Hobart|,0,316,0|-12smja4,94,195,0|-rsj4w1,94,195,0|-rsj4w0,95,192,1|-rjj281,95,192,1|-rjj280,94,195,0|-r8d7k1,94,195,0|-r8d7k0,95,192,1|-r1vvk1,95,192,1|-r1vvk0,94,195,0|-qpn4w1,94,195,0|-qpn4w0,95,192,1|-qj5sw1,95,192,1|-qj5sw0,94,195,0|-em3i81,94,195,0|-em3i80,95,192,1|-ehme81,95,192,1|-ehme80,94,195,0|-e89cw1,94,195,0|-e89cw0,95,192,1|-dywbk1,95,192,1|-dywbk0,94,195,0|-dp6bk1,94,195,0|-dp6bk0,95,192,1|-dg68w1,95,192,1|-dg68w0,94,195,0|-16cow1,94,195,0|-16cow0,95,192,1|-wznk1,95,192,1|-wznk0,94,195,0|-m6rk1,94,195,0|-m6rk0,95,192,1|-fcgw1,95,192,1|-fcgw0,94,195,0|-3gow1,94,195,0|-3gow0,95,192,1|3dlrz,95,192,1|3dls0,94,195,0|f9drz,94,195,0|f9ds0,95,192,1|mgn3z,95,192,1|mgn40,94,195,0|ycf3z,94,195,0|ycf40,95,192,1|14gsfz,95,192,1|14gsg0,94,195,0|1h2hrz,94,195,0|1h2hs0,95,192,1|1njtrz,95,192,1|1njts0,94,195,0|1zskfz,94,195,0|1zskg0,95,192,1|269wfz,95,192,1|269wg0,94,195,0|2iin3z,94,195,0|2iin40,95,192,1|2ozz3z,95,192,1|2ozz40,94,195,0|318prz,94,195,0|318ps0,95,192,1|3830fz,95,192,1|3830g0,94,195,0|3kbr3z,94,195,0|3kbr40,95,192,1|3qt33z,95,192,1|3qt340,94,195,0|431trz,94,195,0|431ts0,95,192,1|49j5rz,95,192,1|49j5s0,94,195,0|4lrwfz,94,195,0|4lrwg0,95,192,1|4s98fz,95,192,1|4s98g0,94,195,0|54hz3z,94,195,0|54hz40,95,192,1|5azb3z,95,192,1|5azb40,94,195,0|5n81rz,94,195,0|5n81s0,95,192,1|5tpdrz,95,192,1|5tpds0,94,195,0|65y4fz,94,195,0|65y4g0,95,192,1|6dvb3z,95,192,1|6dvb40,94,195,0|6p15rz,94,195,0|6p15s0,95,192,1|6wldrz,95,192,1|6wlds0,94,195,0|77r8fz,94,195,0|77r8g0,95,192,1|7e8kfz,95,192,1|7e8kg0,94,195,0|7qhb3z,94,195,0|7qhb40,95,192,1|7wyn3z,95,192,1|7wyn40,94,195,0|897drz,94,195,0|897ds0,95,192,1|8foprz,95,192,1|8fops0,94,195,0|8rkhrz,94,195,0|8rkhs0,95,192,1|8z4prz,95,192,1|8z4ps0,94,195,0|9anj3z,94,195,0|9anj40,95,192,1|9i7r3z,95,192,1|9i7r40,94,195,0|9tqkfz,94,195,0|9tqkg0,95,192,1|a0xtrz,95,192,1|a0xts0,94,195,0|acgn3z,94,195,0|acgn40,95,192,1|ajnwfz,95,192,1|ajnwg0,94,195,0|av6prz,94,195,0|av6ps0,95,192,1|b33wfz,95,192,1|b33wg0,94,195,0|bctwfz,94,195,0|bctwg0,95,192,1|bltz3z,95,192,1|bltz40,94,195,0|bvjz3z,94,195,0|bvjz40,95,192,1|c4k1rz,95,192,1|c4k1s0,94,195,0|cea1rz,94,195,0|cea1s0,95,192,1|cna4fz,95,192,1|cna4g0,94,195,0|cx04fz,94,195,0|cx04g0,95,192,1|d6073z,95,192,1|d60740,94,195,0|dfq73z,94,195,0|dfq740,95,192,1|dp38fz,95,192,1|dp38g0,94,195,0|dyt8fz,94,195,0|dyt8g0,95,192,1|e7tb3z,95,192,1|e7tb40,94,195,0|ehjb3z,94,195,0|ehjb40,95,192,1|eqjdrz,95,192,1|eqjds0,94,195,0|f09drz,94,195,0|f09ds0,95,192,1|f99gfz,95,192,1|f99gg0,94,195,0|fizgfz,94,195,0|fizgg0,95,192,1|frzj3z,95,192,1|frzj40,94,195,0|fzwprz,94,195,0|fzwps0,95,192,1|gaplrz,95,192,1|gapls0,94,195,0|gkskfz,94,195,0|gkskg0,95,192,1|gtsn3z,95,192,1|gtsn40,94,195,0|h3in3z,94,195,0|h3in40,95,192,1|hciprz,95,192,1|hcips0,94,195,0|hm8prz,94,195,0|hm8ps0,95,192,1|hv8sfz,95,192,1|hv8sg0,94,195,0|i4ysfz,94,195,0|i4ysg0,95,192,1|idyv3z,95,192,1|idyv40,94,195,0|inov3z,94,195,0|inov40,95,192,1|ix1wfz,95,192,1|ix1wg0,94,195,0|j6exrz,94,195,0|j6exs0,95,192,1|jff0fz,95,192,1|jff0g0,94,195,0|jphz3z,94,195,0|jphz40,95,192,1|jyv0fz,95,192,1|jyv0g0,94,195,0|k881rz,94,195,0|k881s0,95,192,1|khl33z,95,192,1|khl340,94,195,0|kqy4fz,94,195,0|kqy4g0,95,192,1|l0b5rz,95,192,1|l0b5s0,94,195,0|l9o73z,94,195,0|l9o740,95,192,1|lj18fz,95,192,1|lj18g0,94,195,0|lse9rz,94,195,0|lse9s0,95,192,1|m1rb3z,95,192,1|m1rb40,94,195,0|mbhb3z,94,195,0|mbhb40,95,192,1|mkucfz,95,192,1|mkucg0,94,195,0|mu7drz,94,195,0|mu7ds0,95,192,1|n3kf3z,95,192,1|n3kf40,94,195,0|ncxgfz,94,195,0|ncxgg0,95,192,1|nmahrz,95,192,1|nmahs0,94,195,0|nvnj3z,94,195,0|nvnj40,95,192,1|o50kfz,95,192,1|o50kg0,94,195,0|oedlrz,94,195,0|oedls0,95,192,1|onqn3z,95,192,1|onqn40,94,195,0|ox3ofz,94,195,0|ox3og0,95,192,1|p6gprz,95,192,1|p6gps0,94,195,0|pg6prz,94,195,0|pg6ps0,95,192,1|ppjr3z,95,192,1|ppjr40,94,195,0|pywsfz,94,195,0|pywsg0,95,192,1|q89trz,95,192,1|q89ts0,94,195,0|qhmv3z,94,195,0|qhmv40,95,192,1|qqzwfz,95,192,1|qqzwg0,94,195,0|r0cxrz,94,195,0|r0cxs0,95,192,1|r9pz3z,95,192,1|r9pz40,94,195,0|rj30fz,94,195,0|rj30g0,95,192,1|rsg1rz,95,192,1|rsg1s0,94,195,0|s1t33z,94,195,0|s1t340,95,192,1|sbj33z,95,192,1|sbj340,94,195,0|skw4fz,94,195,0|skw4g0,95,192,1|su95rz,95,192,1|su95s0,94,195,0|t3m73z,94,195,0|t3m740,95,192,1|tcz8fz,95,192,1|tcz8g0,94,195,0|tmc9rz,94,195,0|tmc9s0,95,192,1|tvpb3z,95,192,1|tvpb40,94,195,0|u52cfz,94,195,0|u52cg0,95,192,1|uefdrz,95,192,1|uefds0,94,195,0|unsf3z,94,195,0|unsf40,95,192,1|ux5gfz,95,192,1|ux5gg0,94,195,0|v6vgfz,94,195,0|v6vgg0,95,192,1|vg8hrz,95,192,1|vg8hs0,94,195,0|vplj3z,94,195,0|vplj40,95,192,1|vyykfz,95,192,1|vyykg0,94,195,0|w8blrz,94,195,0|w8bls0,95,192,1|whon3z,95,192,1|whon40,94,195,0|wr1ofz,94,195,0|wr1og0,95,192,1|x0eprz,95,192,1|x0eps0,94,195,0|x9rr3z,94,195,0|x9rr40,95,192,1|xj4sfz,95,192,1|xj4sg0,94,195,0|xshtrz,94,195,0|xshts0,95,192,1|y1uv3z,95,192,1|y1uv40,94,195,0|ybkv3z,94,195,0|ybkv40,95,192,1|ykxwfz,95,192,1|ykxwg0,94,195,0|yuaxrz,94,195,0|yuaxs0,95,192,1|z3nz3z,95,192,1|z3nz40,94,195,0|zd10fz,94,195,0|zd10g0,95,192,1\",\"Australia/Lindeman|,0,321,0|-1354jl8,94,195,0|-rnsrk1,94,195,0|-rnsrk0,95,192,1|-rjj281,95,192,1|-rjj280,94,195,0|-em3i81,94,195,0|-em3i80,95,192,1|-ehme81,95,192,1|-ehme80,94,195,0|-e89cw1,94,195,0|-e89cw0,95,192,1|-dywbk1,95,192,1|-dywbk0,94,195,0|-dp6bk1,94,195,0|-dp6bk0,95,192,1|-dg68w1,95,192,1|-dg68w0,94,195,0|ycf3z,94,195,0|ycf40,95,192,1|14gsfz,95,192,1|14gsg0,94,195,0|acgn3z,94,195,0|acgn40,95,192,1|aixz3z,95,192,1|aixz40,94,195,0|av6prz,94,195,0|av6ps0,95,192,1|b1o1rz,95,192,1|b1o1s0,94,195,0|bdwsfz,94,195,0|bdwsg0,95,192,1|bke4fz,95,192,1|bke4g0,94,195,0|bwmv3z,94,195,0|bwmv40,95,192,1|c3h5rz,95,192,1|c3h5s0,94,195,0|cfpwfz,94,195,0|cfpwg0,95,192,1|cm78fz,95,192,1|cm78g0,94,195,0\",\"Australia/Lord_Howe|,0,322,0|-133j6sk,94,195,0|5tp87z,94,195,0|5tp880,142,313,0|65y31z,142,313,0|65y320,143,198,1|6csaxz,143,198,1|6csay0,142,313,0|6p14dz,142,313,0|6p14e0,143,198,1|6vidlz,143,198,1|6vidm0,142,313,0|77r71z,142,313,0|77r720,143,198,1|7e8g9z,143,198,1|7e8ga0,142,313,0|7qh9pz,142,313,0|7qh9q0,143,198,1|7wyixz,143,198,1|7wyiy0,142,313,0|897cdz,142,313,0|897ce0,90,192,1|8gekbz,90,192,1|8gekc0,142,313,0|8rkgdz,142,313,0|8rkge0,90,192,1|8z4mzz,90,192,1|8z4n00,142,313,0|9anhpz,142,313,0|9anhq0,90,192,1|9i7obz,90,192,1|9i7oc0,142,313,0|9tqj1z,142,313,0|9tqj20,90,192,1|a0xqzz,90,192,1|a0xr00,142,313,0|acglpz,142,313,0|acglq0,90,192,1|aixwbz,90,192,1|aixwc0,142,313,0|av6odz,142,313,0|av6oe0,90,192,1|b1nyzz,90,192,1|b1nz00,142,313,0|bdwr1z,142,313,0|bdwr20,90,192,1|bke1nz,90,192,1|bke1o0,142,313,0|bwmtpz,142,313,0|bwmtq0,90,192,1|c3h2zz,90,192,1|c3h300,142,313,0|cfpv1z,142,313,0|cfpv20,90,192,1|cm75nz,90,192,1|cm75o0,142,313,0|cyfxpz,142,313,0|cyfxq0,90,192,1|d4x8bz,90,192,1|d4x8c0,142,313,0|dh60dz,142,313,0|dh60e0,90,192,1|dp35nz,90,192,1|dp35o0,142,313,0|dzw31z,142,313,0|dzw320,90,192,1|e7t8bz,90,192,1|e7t8c0,142,313,0|eim5pz,142,313,0|eim5q0,90,192,1|eqjazz,90,192,1|eqjb00,142,313,0|f1c8dz,142,313,0|f1c8e0,90,192,1|f99dnz,90,192,1|f99do0,142,313,0|fkf9pz,142,313,0|fkf9q0,90,192,1|frzgbz,90,192,1|frzgc0,142,313,0|fzwodz,142,313,0|fzwoe0,90,192,1|gapizz,90,192,1|gapj00,142,313,0|glvf1z,142,313,0|glvf20,90,192,1|gtskbz,90,192,1|gtskc0,142,313,0|h4lhpz,142,313,0|h4lhq0,90,192,1|hcimzz,90,192,1|hcin00,142,313,0|hnbkdz,142,313,0|hnbke0,90,192,1|hv8pnz,90,192,1|hv8po0,142,313,0|i6elpz,142,313,0|i6elq0,90,192,1|idysbz,90,192,1|idysc0,142,313,0|ip4odz,142,313,0|ip4oe0,90,192,1|ix1tnz,90,192,1|ix1to0,142,313,0|j7ur1z,142,313,0|j7ur20,90,192,1|jfexnz,90,192,1|jfexo0,142,313,0|jqktpz,142,313,0|jqktq0,90,192,1|jyuxnz,90,192,1|jyuxo0,142,313,0|k880dz,142,313,0|k880e0,90,192,1|khl0bz,90,192,1|khl0c0,142,313,0|kqy31z,142,313,0|kqy320,90,192,1|l0b2zz,90,192,1|l0b300,142,313,0|l9o5pz,142,313,0|l9o5q0,90,192,1|lj15nz,90,192,1|lj15o0,142,313,0|lse8dz,142,313,0|lse8e0,90,192,1|m1r8bz,90,192,1|m1r8c0,142,313,0|mbh9pz,142,313,0|mbh9q0,90,192,1|mku9nz,90,192,1|mku9o0,142,313,0|mu7cdz,142,313,0|mu7ce0,90,192,1|n3kcbz,90,192,1|n3kcc0,142,313,0|ncxf1z,142,313,0|ncxf20,90,192,1|nmaezz,90,192,1|nmaf00,142,313,0|nvnhpz,142,313,0|nvnhq0,90,192,1|o50hnz,90,192,1|o50ho0,142,313,0|oedkdz,142,313,0|oedke0,90,192,1|onqkbz,90,192,1|onqkc0,142,313,0|ox3n1z,142,313,0|ox3n20,90,192,1|p6gmzz,90,192,1|p6gn00,142,313,0|pg6odz,142,313,0|pg6oe0,90,192,1|ppjobz,90,192,1|ppjoc0,142,313,0|pywr1z,142,313,0|pywr20,90,192,1|q89qzz,90,192,1|q89r00,142,313,0|qhmtpz,142,313,0|qhmtq0,90,192,1|qqztnz,90,192,1|qqzto0,142,313,0|r0cwdz,142,313,0|r0cwe0,90,192,1|r9pwbz,90,192,1|r9pwc0,142,313,0|rj2z1z,142,313,0|rj2z20,90,192,1|rsfyzz,90,192,1|rsfz00,142,313,0|s1t1pz,142,313,0|s1t1q0,90,192,1|sbj0bz,90,192,1|sbj0c0,142,313,0|skw31z,142,313,0|skw320,90,192,1|su92zz,90,192,1|su9300,142,313,0|t3m5pz,142,313,0|t3m5q0,90,192,1|tcz5nz,90,192,1|tcz5o0,142,313,0|tmc8dz,142,313,0|tmc8e0,90,192,1|tvp8bz,90,192,1|tvp8c0,142,313,0|u52b1z,142,313,0|u52b20,90,192,1|uefazz,90,192,1|uefb00,142,313,0|unsdpz,142,313,0|unsdq0,90,192,1|ux5dnz,90,192,1|ux5do0,142,313,0|v6vf1z,142,313,0|v6vf20,90,192,1|vg8ezz,90,192,1|vg8f00,142,313,0|vplhpz,142,313,0|vplhq0,90,192,1|vyyhnz,90,192,1|vyyho0,142,313,0|w8bkdz,142,313,0|w8bke0,90,192,1|whokbz,90,192,1|whokc0,142,313,0|wr1n1z,142,313,0|wr1n20,90,192,1|x0emzz,90,192,1|x0en00,142,313,0|x9rppz,142,313,0|x9rpq0,90,192,1|xj4pnz,90,192,1|xj4po0,142,313,0|xshsdz,142,313,0|xshse0,90,192,1|y1usbz,90,192,1|y1usc0,142,313,0|ybktpz,142,313,0|ybktq0,90,192,1|ykxtnz,90,192,1|ykxto0,142,313,0|yuawdz,142,313,0|yuawe0,90,192,1|z3nwbz,90,192,1|z3nwc0,142,313,0|zd0z1z,142,313,0|zd0z20,90,192,1\",\"Australia/Melbourne|,0,323,0|-133j46g,94,195,0|-rnsrk1,94,195,0|-rnsrk0,95,192,1|-rjj281,95,192,1|-rjj280,94,195,0|-em3i81,94,195,0|-em3i80,95,192,1|-ehme81,95,192,1|-ehme80,94,195,0|-e89cw1,94,195,0|-e89cw0,95,192,1|-dywbk1,95,192,1|-dywbk0,94,195,0|-dp6bk1,94,195,0|-dp6bk0,95,192,1|-dg68w1,95,192,1|-dg68w0,94,195,0|ycf3z,94,195,0|ycf40,95,192,1|14gsfz,95,192,1|14gsg0,94,195,0|1h2hrz,94,195,0|1h2hs0,95,192,1|1njtrz,95,192,1|1njts0,94,195,0|1zskfz,94,195,0|1zskg0,95,192,1|269wfz,95,192,1|269wg0,94,195,0|2iin3z,94,195,0|2iin40,95,192,1|2ozz3z,95,192,1|2ozz40,94,195,0|318prz,94,195,0|318ps0,95,192,1|3830fz,95,192,1|3830g0,94,195,0|3kbr3z,94,195,0|3kbr40,95,192,1|3qt33z,95,192,1|3qt340,94,195,0|431trz,94,195,0|431ts0,95,192,1|49j5rz,95,192,1|49j5s0,94,195,0|4lrwfz,94,195,0|4lrwg0,95,192,1|4s98fz,95,192,1|4s98g0,94,195,0|54hz3z,94,195,0|54hz40,95,192,1|5azb3z,95,192,1|5azb40,94,195,0|5n81rz,94,195,0|5n81s0,95,192,1|5tpdrz,95,192,1|5tpds0,94,195,0|65y4fz,94,195,0|65y4g0,95,192,1|6csf3z,95,192,1|6csf40,94,195,0|6p15rz,94,195,0|6p15s0,95,192,1|6vihrz,95,192,1|6vihs0,94,195,0|77r8fz,94,195,0|77r8g0,95,192,1|7e8kfz,95,192,1|7e8kg0,94,195,0|7qhb3z,94,195,0|7qhb40,95,192,1|7wyn3z,95,192,1|7wyn40,94,195,0|897drz,94,195,0|897ds0,95,192,1|8gen3z,95,192,1|8gen40,94,195,0|8rkhrz,94,195,0|8rkhs0,95,192,1|8z4prz,95,192,1|8z4ps0,94,195,0|9aakfz,94,195,0|9aakg0,95,192,1|9i7r3z,95,192,1|9i7r40,94,195,0|9tqkfz,94,195,0|9tqkg0,95,192,1|a0xtrz,95,192,1|a0xts0,94,195,0|acgn3z,94,195,0|acgn40,95,192,1|ajnwfz,95,192,1|ajnwg0,94,195,0|av6prz,94,195,0|av6ps0,95,192,1|b1o1rz,95,192,1|b1o1s0,94,195,0|bdwsfz,94,195,0|bdwsg0,95,192,1|bke4fz,95,192,1|bke4g0,94,195,0|bwmv3z,94,195,0|bwmv40,95,192,1|c3h5rz,95,192,1|c3h5s0,94,195,0|cfpwfz,94,195,0|cfpwg0,95,192,1|cm78fz,95,192,1|cm78g0,94,195,0|cyfz3z,94,195,0|cyfz40,95,192,1|d6073z,95,192,1|d60740,94,195,0|dh61rz,94,195,0|dh61s0,95,192,1|dp38fz,95,192,1|dp38g0,94,195,0|dzw4fz,94,195,0|dzw4g0,95,192,1|e7tb3z,95,192,1|e7tb40,94,195,0|eim73z,94,195,0|eim740,95,192,1|eqjdrz,95,192,1|eqjds0,94,195,0|f1c9rz,94,195,0|f1c9s0,95,192,1|f99gfz,95,192,1|f99gg0,94,195,0|fkfb3z,94,195,0|fkfb40,95,192,1|frzj3z,95,192,1|frzj40,94,195,0|fzwprz,94,195,0|fzwps0,95,192,1|gaplrz,95,192,1|gapls0,94,195,0|glvgfz,94,195,0|glvgg0,95,192,1|gtsn3z,95,192,1|gtsn40,94,195,0|h4lj3z,94,195,0|h4lj40,95,192,1|hciprz,95,192,1|hcips0,94,195,0|hnblrz,94,195,0|hnbls0,95,192,1|hv8sfz,95,192,1|hv8sg0,94,195,0|i6en3z,94,195,0|i6en40,95,192,1|idyv3z,95,192,1|idyv40,94,195,0|ip4prz,94,195,0|ip4ps0,95,192,1|ix1wfz,95,192,1|ix1wg0,94,195,0|j7usfz,94,195,0|j7usg0,95,192,1|jff0fz,95,192,1|jff0g0,94,195,0|jqkv3z,94,195,0|jqkv40,95,192,1|jyv0fz,95,192,1|jyv0g0,94,195,0|k881rz,94,195,0|k881s0,95,192,1|khl33z,95,192,1|khl340,94,195,0|kqy4fz,94,195,0|kqy4g0,95,192,1|l0b5rz,95,192,1|l0b5s0,94,195,0|l9o73z,94,195,0|l9o740,95,192,1|lj18fz,95,192,1|lj18g0,94,195,0|lse9rz,94,195,0|lse9s0,95,192,1|m1rb3z,95,192,1|m1rb40,94,195,0|mbhb3z,94,195,0|mbhb40,95,192,1|mkucfz,95,192,1|mkucg0,94,195,0|mu7drz,94,195,0|mu7ds0,95,192,1|n3kf3z,95,192,1|n3kf40,94,195,0|ncxgfz,94,195,0|ncxgg0,95,192,1|nmahrz,95,192,1|nmahs0,94,195,0|nvnj3z,94,195,0|nvnj40,95,192,1|o50kfz,95,192,1|o50kg0,94,195,0|oedlrz,94,195,0|oedls0,95,192,1|onqn3z,95,192,1|onqn40,94,195,0|ox3ofz,94,195,0|ox3og0,95,192,1|p6gprz,95,192,1|p6gps0,94,195,0|pg6prz,94,195,0|pg6ps0,95,192,1|ppjr3z,95,192,1|ppjr40,94,195,0|pywsfz,94,195,0|pywsg0,95,192,1|q89trz,95,192,1|q89ts0,94,195,0|qhmv3z,94,195,0|qhmv40,95,192,1|qqzwfz,95,192,1|qqzwg0,94,195,0|r0cxrz,94,195,0|r0cxs0,95,192,1|r9pz3z,95,192,1|r9pz40,94,195,0|rj30fz,94,195,0|rj30g0,95,192,1|rsg1rz,95,192,1|rsg1s0,94,195,0|s1t33z,94,195,0|s1t340,95,192,1|sbj33z,95,192,1|sbj340,94,195,0|skw4fz,94,195,0|skw4g0,95,192,1|su95rz,95,192,1|su95s0,94,195,0|t3m73z,94,195,0|t3m740,95,192,1|tcz8fz,95,192,1|tcz8g0,94,195,0|tmc9rz,94,195,0|tmc9s0,95,192,1|tvpb3z,95,192,1|tvpb40,94,195,0|u52cfz,94,195,0|u52cg0,95,192,1|uefdrz,95,192,1|uefds0,94,195,0|unsf3z,94,195,0|unsf40,95,192,1|ux5gfz,95,192,1|ux5gg0,94,195,0|v6vgfz,94,195,0|v6vgg0,95,192,1|vg8hrz,95,192,1|vg8hs0,94,195,0|vplj3z,94,195,0|vplj40,95,192,1|vyykfz,95,192,1|vyykg0,94,195,0|w8blrz,94,195,0|w8bls0,95,192,1|whon3z,95,192,1|whon40,94,195,0|wr1ofz,94,195,0|wr1og0,95,192,1|x0eprz,95,192,1|x0eps0,94,195,0|x9rr3z,94,195,0|x9rr40,95,192,1|xj4sfz,95,192,1|xj4sg0,94,195,0|xshtrz,94,195,0|xshts0,95,192,1|y1uv3z,95,192,1|y1uv40,94,195,0|ybkv3z,94,195,0|ybkv40,95,192,1|ykxwfz,95,192,1|ykxwg0,94,195,0|yuaxrz,94,195,0|yuaxs0,95,192,1|z3nz3z,95,192,1|z3nz40,94,195,0|zd10fz,94,195,0|zd10g0,95,192,1\",\"Australia/Perth|,0,324,0|-12nxusc,144,191,0|-rnsm01,144,191,0|-rnsm00,145,224,1|-rjiwo1,145,224,1|-rjiwo0,144,191,0|-em3co1,144,191,0|-em3co0,145,224,1|-ehm8o1,145,224,1|-ehm8o0,144,191,0|-e897c1,144,191,0|-e897c0,145,224,1|-dyw601,145,224,1|-dyw600,144,191,0|2iisnz,144,191,0|2iiso0,145,224,1|2p04nz,145,224,1|2p04o0,144,191,0|77rdzz,144,191,0|77re00,145,224,1|7e8pzz,145,224,1|7e8q00,144,191,0|beztzz,144,191,0|bezu00,145,224,1|bke9zz,145,224,1|bkea00,144,191,0|j9nrbz,144,191,0|j9nrc0,145,224,1|jff5zz,145,224,1|jff600,144,191,0|jql0nz,144,191,0|jql0o0,145,224,1|jyi7bz,145,224,1|jyi7c0,144,191,0|k9b3bz,144,191,0|k9b3c0,145,224,1|kh89zz,145,224,1|kh8a00,144,191,0\",\"Australia/Sydney|,0,325,0|-133j5c4,94,195,0|-rnsrk1,94,195,0|-rnsrk0,95,192,1|-rjj281,95,192,1|-rjj280,94,195,0|-em3i81,94,195,0|-em3i80,95,192,1|-ehme81,95,192,1|-ehme80,94,195,0|-e89cw1,94,195,0|-e89cw0,95,192,1|-dywbk1,95,192,1|-dywbk0,94,195,0|-dp6bk1,94,195,0|-dp6bk0,95,192,1|-dg68w1,95,192,1|-dg68w0,94,195,0|ycf3z,94,195,0|ycf40,95,192,1|14gsfz,95,192,1|14gsg0,94,195,0|1h2hrz,94,195,0|1h2hs0,95,192,1|1njtrz,95,192,1|1njts0,94,195,0|1zskfz,94,195,0|1zskg0,95,192,1|269wfz,95,192,1|269wg0,94,195,0|2iin3z,94,195,0|2iin40,95,192,1|2ozz3z,95,192,1|2ozz40,94,195,0|318prz,94,195,0|318ps0,95,192,1|3830fz,95,192,1|3830g0,94,195,0|3kbr3z,94,195,0|3kbr40,95,192,1|3qt33z,95,192,1|3qt340,94,195,0|431trz,94,195,0|431ts0,95,192,1|49j5rz,95,192,1|49j5s0,94,195,0|4lrwfz,94,195,0|4lrwg0,95,192,1|4s98fz,95,192,1|4s98g0,94,195,0|54hz3z,94,195,0|54hz40,95,192,1|5azb3z,95,192,1|5azb40,94,195,0|5n81rz,94,195,0|5n81s0,95,192,1|5tpdrz,95,192,1|5tpds0,94,195,0|65y4fz,94,195,0|65y4g0,95,192,1|6e89rz,95,192,1|6e89s0,94,195,0|6p15rz,94,195,0|6p15s0,95,192,1|6vihrz,95,192,1|6vihs0,94,195,0|77r8fz,94,195,0|77r8g0,95,192,1|7e8kfz,95,192,1|7e8kg0,94,195,0|7qhb3z,94,195,0|7qhb40,95,192,1|7wyn3z,95,192,1|7wyn40,94,195,0|897drz,94,195,0|897ds0,95,192,1|8gen3z,95,192,1|8gen40,94,195,0|8rkhrz,94,195,0|8rkhs0,95,192,1|8z4prz,95,192,1|8z4ps0,94,195,0|9anj3z,94,195,0|9anj40,95,192,1|9i7r3z,95,192,1|9i7r40,94,195,0|9tqkfz,94,195,0|9tqkg0,95,192,1|a0xtrz,95,192,1|a0xts0,94,195,0|acgn3z,94,195,0|acgn40,95,192,1|aixz3z,95,192,1|aixz40,94,195,0|av6prz,94,195,0|av6ps0,95,192,1|b1o1rz,95,192,1|b1o1s0,94,195,0|bdwsfz,94,195,0|bdwsg0,95,192,1|bke4fz,95,192,1|bke4g0,94,195,0|bwmv3z,94,195,0|bwmv40,95,192,1|c3h5rz,95,192,1|c3h5s0,94,195,0|cfpwfz,94,195,0|cfpwg0,95,192,1|cm78fz,95,192,1|cm78g0,94,195,0|cyfz3z,94,195,0|cyfz40,95,192,1|d4xb3z,95,192,1|d4xb40,94,195,0|dh61rz,94,195,0|dh61s0,95,192,1|dp38fz,95,192,1|dp38g0,94,195,0|dzw4fz,94,195,0|dzw4g0,95,192,1|e7tb3z,95,192,1|e7tb40,94,195,0|eim73z,94,195,0|eim740,95,192,1|eqjdrz,95,192,1|eqjds0,94,195,0|f1c9rz,94,195,0|f1c9s0,95,192,1|f99gfz,95,192,1|f99gg0,94,195,0|fkfb3z,94,195,0|fkfb40,95,192,1|frzj3z,95,192,1|frzj40,94,195,0|fzwprz,94,195,0|fzwps0,95,192,1|gaplrz,95,192,1|gapls0,94,195,0|glvgfz,94,195,0|glvgg0,95,192,1|gtsn3z,95,192,1|gtsn40,94,195,0|h4lj3z,94,195,0|h4lj40,95,192,1|hciprz,95,192,1|hcips0,94,195,0|hnblrz,94,195,0|hnbls0,95,192,1|hv8sfz,95,192,1|hv8sg0,94,195,0|i6en3z,94,195,0|i6en40,95,192,1|idyv3z,95,192,1|idyv40,94,195,0|ip4prz,94,195,0|ip4ps0,95,192,1|ix1wfz,95,192,1|ix1wg0,94,195,0|j7usfz,94,195,0|j7usg0,95,192,1|jff0fz,95,192,1|jff0g0,94,195,0|jqkv3z,94,195,0|jqkv40,95,192,1|jyv0fz,95,192,1|jyv0g0,94,195,0|k881rz,94,195,0|k881s0,95,192,1|khl33z,95,192,1|khl340,94,195,0|kqy4fz,94,195,0|kqy4g0,95,192,1|l0b5rz,95,192,1|l0b5s0,94,195,0|l9o73z,94,195,0|l9o740,95,192,1|lj18fz,95,192,1|lj18g0,94,195,0|lse9rz,94,195,0|lse9s0,95,192,1|m1rb3z,95,192,1|m1rb40,94,195,0|mbhb3z,94,195,0|mbhb40,95,192,1|mkucfz,95,192,1|mkucg0,94,195,0|mu7drz,94,195,0|mu7ds0,95,192,1|n3kf3z,95,192,1|n3kf40,94,195,0|ncxgfz,94,195,0|ncxgg0,95,192,1|nmahrz,95,192,1|nmahs0,94,195,0|nvnj3z,94,195,0|nvnj40,95,192,1|o50kfz,95,192,1|o50kg0,94,195,0|oedlrz,94,195,0|oedls0,95,192,1|onqn3z,95,192,1|onqn40,94,195,0|ox3ofz,94,195,0|ox3og0,95,192,1|p6gprz,95,192,1|p6gps0,94,195,0|pg6prz,94,195,0|pg6ps0,95,192,1|ppjr3z,95,192,1|ppjr40,94,195,0|pywsfz,94,195,0|pywsg0,95,192,1|q89trz,95,192,1|q89ts0,94,195,0|qhmv3z,94,195,0|qhmv40,95,192,1|qqzwfz,95,192,1|qqzwg0,94,195,0|r0cxrz,94,195,0|r0cxs0,95,192,1|r9pz3z,95,192,1|r9pz40,94,195,0|rj30fz,94,195,0|rj30g0,95,192,1|rsg1rz,95,192,1|rsg1s0,94,195,0|s1t33z,94,195,0|s1t340,95,192,1|sbj33z,95,192,1|sbj340,94,195,0|skw4fz,94,195,0|skw4g0,95,192,1|su95rz,95,192,1|su95s0,94,195,0|t3m73z,94,195,0|t3m740,95,192,1|tcz8fz,95,192,1|tcz8g0,94,195,0|tmc9rz,94,195,0|tmc9s0,95,192,1|tvpb3z,95,192,1|tvpb40,94,195,0|u52cfz,94,195,0|u52cg0,95,192,1|uefdrz,95,192,1|uefds0,94,195,0|unsf3z,94,195,0|unsf40,95,192,1|ux5gfz,95,192,1|ux5gg0,94,195,0|v6vgfz,94,195,0|v6vgg0,95,192,1|vg8hrz,95,192,1|vg8hs0,94,195,0|vplj3z,94,195,0|vplj40,95,192,1|vyykfz,95,192,1|vyykg0,94,195,0|w8blrz,94,195,0|w8bls0,95,192,1|whon3z,95,192,1|whon40,94,195,0|wr1ofz,94,195,0|wr1og0,95,192,1|x0eprz,95,192,1|x0eps0,94,195,0|x9rr3z,94,195,0|x9rr40,95,192,1|xj4sfz,95,192,1|xj4sg0,94,195,0|xshtrz,94,195,0|xshts0,95,192,1|y1uv3z,95,192,1|y1uv40,94,195,0|ybkv3z,94,195,0|ybkv40,95,192,1|ykxwfz,95,192,1|ykxwg0,94,195,0|yuaxrz,94,195,0|yuaxs0,95,192,1|z3nz3z,95,192,1|z3nz40,94,195,0|zd10fz,94,195,0|zd10g0,95,192,1\",\"Etc/GMT+1|,199,15,0\",\"Etc/GMT+10|,208,36,0\",\"Etc/GMT+11|,209,35,0\",\"Etc/GMT+12|,210,403,0\",\"Etc/GMT+2|,200,45,0\",\"Etc/GMT+3|,201,44,0\",\"Etc/GMT+4|,202,42,0\",\"Etc/GMT+5|,203,63,0\",\"Etc/GMT+6|,204,62,0\",\"Etc/GMT+7|,205,66,0\",\"Etc/GMT+8|,206,40,0\",\"Etc/GMT+9|,207,37,0\",\"Etc/GMT-1|,198,10,0\",\"Etc/GMT-10|,189,195,0\",\"Etc/GMT-11|,188,192,0\",\"Etc/GMT-12|,187,200,0\",\"Etc/GMT-13|,186,201,0\",\"Etc/GMT-14|,185,207,0\",\"Etc/GMT-2|,197,11,0\",\"Etc/GMT-3|,196,6,0\",\"Etc/GMT-4|,195,209,0\",\"Etc/GMT-5|,194,194,0\",\"Etc/GMT-6|,193,196,0\",\"Etc/GMT-7|,192,193,0\",\"Etc/GMT-8|,191,191,0\",\"Etc/GMT-9|,190,224,0\",\"Europe/Amsterdam|,0,326,0|-1ygf4wk,44,326,0|-s0dvkl,44,326,0|-s0dvkk,24,327,1|-rsimcl,24,327,1|-rsimck,44,326,0|-ridkol,44,326,0|-ridkok,24,327,1|-rage0l,24,327,1|-rage0k,44,326,0|-r0dfcl,44,326,0|-r0dfck,24,327,1|-qr0e0l,24,327,1|-qr0e0k,44,326,0|-qhae0l,44,326,0|-qhae0k,24,327,1|-q8abcl,24,327,1|-q8abck,44,326,0|-pykbcl,44,326,0|-pykbck,24,327,1|-ppk8ol,24,327,1|-ppk8ok,44,326,0|-pfu8ol,44,326,0|-pfu8ok,24,327,1|-p6u60l,24,327,1|-p6u60k,44,326,0|-oxizcl,44,326,0|-oxizck,24,327,1|-ong0ol,24,327,1|-ong0ok,44,326,0|-obazcl,44,326,0|-obazck,24,327,1|-o4py0l,24,327,1|-o4py0k,44,326,0|-nvpvcl,44,326,0|-nvpvck,24,327,1|-nlzvcl,24,327,1|-nlzvck,44,326,0|-n9hvcl,44,326,0|-n9hvck,24,327,1|-n39sol,24,327,1|-n39sok,44,326,0|-mrsu0l,44,326,0|-mrsu0k,24,327,1|-mkjq0l,24,327,1|-mkjq0k,44,326,0|-m90wol,44,326,0|-m90wok,24,327,1|-m1tncl,24,327,1|-m1tnck,44,326,0|-lq74ol,44,326,0|-lq74ok,24,327,1|-liqm0l,24,327,1|-liqm0k,44,326,0|-l7f7cl,44,326,0|-l7f7ck,24,327,1|-l00jcl,24,327,1|-l00jck,44,326,0|-kona0l,44,326,0|-kona0k,24,327,1|-khagol,24,327,1|-khagok,44,326,0|-k5vcol,44,326,0|-k5vcok,24,327,1|-jyke0l,24,327,1|-jyke0k,44,326,0|-jmom0l,44,326,0|-jmom0k,24,327,1|-jfubcl,24,327,1|-jfubck,44,326,0|-j49ncl,44,326,0|-j49nck,24,327,1|-iwra0l,24,327,1|-iwra0k,44,326,0|-ilhq0l,44,326,0|-ilhq0k,24,327,1|-ie17cl,24,327,1|-ie17ck,44,326,0|-i2psol,44,326,0|-i2psok,24,327,1|-hvb4ol,24,327,1|-hvb4ok,44,326,0|-hjw0ol,44,326,0|-hjw0ok,24,327,1|-hcl20l,24,327,1|-hcl20k,44,326,0|-h0r4ol,44,326,0|-h0r4ok,24,327,1|-gypacl,24,327,1|-gypack,146,328,1|-gtuzdd,146,328,1|-gtuzdc,2,2,0|-gic61d,2,2,0|-gic61c,146,328,1|-gb4wpd,146,328,1|-gb4wpc,2,2,0|-fzk8pd,2,2,0|-fzk8pc,146,328,1|-fs1vdd,146,328,1|-fs1vdc,2,2,0|-fgorld,2,2,0|-fgorlc,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cofek1,11,11,1|-cofek0,10,10,0|3s9mrz,10,10,0|3s9ms0,11,11,1|419pfz,11,11,1|419pg0,10,10,0|4azpfz,10,10,0|4azpg0,11,11,1|4kcqrz,11,11,1|4kcqs0,10,10,0|4tps3z,10,10,0|4tps40,11,11,1|532tfz,11,11,1|532tg0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Andorra|,0,329,0|-100edm4,8,1,0|-c4xmo1,8,1,0|-c4xmo0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Astrakhan|,0,330,0|-nu2zkc,100,6,0|-kmr4c1,100,6,0|-kmr4c0,105,209,0|5vb3jz,105,209,0|5vb3k0,92,194,1|64pwrz,92,194,1|64pws0,105,209,0|6e30vz,105,209,0|6e30w0,92,194,1|6nhu3z,92,194,1|6nhu40,105,209,0|6wuy7z,105,209,0|6wuy80,92,194,1|769rfz,92,194,1|769rg0,105,209,0|7foq7z,105,209,0|7foq80,92,194,1|7p1x3z,92,194,1|7p1x40,105,209,0|7yeyfz,105,209,0|7yeyg0,92,194,1|87rzrz,92,194,1|87rzs0,105,209,0|8h513z,105,209,0|8h5140,92,194,1|8qi2fz,92,194,1|8qi2g0,105,209,0|8zv3rz,105,209,0|8zv3s0,92,194,1|99853z,92,194,1|998540,105,209,0|9il6fz,105,209,0|9il6g0,92,194,1|9ry7rz,92,194,1|9ry7s0,105,209,0|a1b93z,105,209,0|a1b940,105,209,1|aaod7z,105,209,1|aaod80,100,6,0|ak1ejz,100,6,0|ak1ek0,105,209,1|atrejz,105,209,1|atrek0,100,6,0|b34fvz,100,6,0|b34fw0,105,209,0|blufrz,105,209,0|blufs0,105,209,1|bv7jvz,105,209,1|bv7jw0,100,6,0|c4kl7z,100,6,0|c4kl80,105,209,1|cdxmjz,105,209,1|cdxmk0,100,6,0|cnanvz,100,6,0|cnanw0,105,209,1|cwnp7z,105,209,1|cwnp80,100,6,0|d60qjz,100,6,0|d60qk0,105,209,1|dfdrvz,105,209,1|dfdrw0,100,6,0|dp3rvz,100,6,0|dp3rw0,105,209,1|dzwnvz,105,209,1|dzwnw0,100,6,0|e7tujz,100,6,0|e7tuk0,105,209,1|eimqjz,105,209,1|eimqk0,100,6,0|eqjx7z,100,6,0|eqjx80,105,209,1|f1ct7z,105,209,1|f1ct80,100,6,0|f99zvz,100,6,0|f99zw0,105,209,1|fkfujz,105,209,1|fkfuk0,100,6,0|fs02jz,100,6,0|fs02k0,105,209,1|g35x7z,105,209,1|g35x80,100,6,0|gaq57z,100,6,0|gaq580,105,209,1|glvzvz,105,209,1|glvzw0,100,6,0|gtt6jz,100,6,0|gtt6k0,105,209,1|h4m2jz,105,209,1|h4m2k0,100,6,0|hcj97z,100,6,0|hcj980,105,209,1|hnc57z,105,209,1|hnc580,100,6,0|hv9bvz,100,6,0|hv9bw0,105,209,1|i6f6jz,105,209,1|i6f6k0,100,6,0|idzejz,100,6,0|idzek0,105,209,1|ip597z,105,209,1|ip5980,100,6,0|iwph7z,100,6,0|iwph80,105,209,1|j7vbvz,105,209,1|j7vbw0,100,6,0|jffjvz,100,6,0|jffjw0,105,209,1|jqlejz,105,209,1|jqlek0,100,6,0|jyil7z,100,6,0|jyil80,105,209,1|k9bh7z,105,209,1|k9bh80,100,6,0|kh8nvz,100,6,0|kh8nw0,105,209,1|ks1jvz,105,209,1|ks1jw0,100,6,0|kzyqjz,100,6,0|kzyqk0,105,209,1|lb4l7z,105,209,1|lb4l80,100,6,0|liot7z,100,6,0|liot80,105,209,0|ne0t3z,105,209,0|ne0t40,100,6,0|o4o57z,100,6,0|o4o580,105,209,0\",\"Europe/Athens|,0,331,0|-12rxtq4,44,331,0|-rvv0ch,44,331,0|-rvv0cg,15,11,0|-jkbpk1,15,11,0|-jkbpk0,16,6,1|-jhg301,16,6,1|-jhg300,15,11,0|-ezx6w1,15,11,0|-ezx6w0,16,6,1|-eyqoc1,16,6,1|-eyqoc0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dys2s1,10,10,0|-dys2s0,11,11,1|-dp4081,11,11,1|-dp4080,10,10,0|-dfp1g1,10,10,0|-dfp1g0,15,11,0|-94v1k1,15,11,0|-94v1k0,16,6,1|-8yhho1,16,6,1|-8yhho0,15,11,0|2r4d3z,15,11,0|2r4d40,16,6,1|32ul3z,16,6,1|32ul40,15,11,0|39wfzz,15,11,0|39wg00,16,6,1|3j9hbz,16,6,1|3j9hc0,15,11,0|3s9jzz,15,11,0|3s9k00,16,6,1|41bhbz,16,6,1|41bhc0,15,11,0|4azmnz,15,11,0|4azmo0,16,6,1|4jzs3z,16,6,1|4jzs40,15,11,0|4tq8rz,15,11,0|4tq8s0,16,6,1|530t7z,16,6,1|530t80,15,11,0|5cjbrz,15,11,0|5cjbs0,16,6,1|5lskzz,16,6,1|5lsl00,15,11,0|5v5xfz,15,11,0|5v5xg0,16,6,1|64iyrz,16,6,1|64iys0,15,11,0|6dw03z,15,11,0|6dw040,16,6,1|6n91fz,16,6,1|6n91g0,15,11,0|6wm2rz,15,11,0|6wm2s0,16,6,1|75z43z,16,6,1|75z440,15,11,0|7fc5fz,15,11,0|7fc5g0,16,6,1|7p25fz,16,6,1|7p25g0,15,11,0|7yf6rz,15,11,0|7yf6s0,16,6,1|87s83z,16,6,1|87s840,15,11,0|8h59fz,15,11,0|8h59g0,16,6,1|8qiarz,16,6,1|8qias0,15,11,0|8zvc3z,15,11,0|8zvc40,16,6,1|998dfz,16,6,1|998dg0,15,11,0|9ilerz,15,11,0|9iles0,16,6,1|9ryg3z,16,6,1|9ryg40,15,11,0|a1bhfz,15,11,0|a1bhg0,16,6,1|aaoirz,16,6,1|aaois0,15,11,0|ak1k3z,15,11,0|ak1k40,16,6,1|atrk3z,16,6,1|atrk40,15,11,0|b34lfz,15,11,0|b34lg0,16,6,1|bchmrz,16,6,1|bchms0,15,11,0|bluo3z,15,11,0|bluo40,16,6,1|bv7pfz,16,6,1|bv7pg0,15,11,0|c4kqrz,15,11,0|c4kqs0,16,6,1|cdxs3z,16,6,1|cdxs40,15,11,0|cnatfz,15,11,0|cnatg0,16,6,1|cwnurz,16,6,1|cwnus0,15,11,0|d60w3z,15,11,0|d60w40,16,6,1|dfdxfz,16,6,1|dfdxg0,15,11,0|dp3xfz,15,11,0|dp3xg0,16,6,1|dzwtfz,16,6,1|dzwtg0,15,11,0|e7u03z,15,11,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Belgrade|,0,332,0|-18vsmgo,10,10,0|-ezayw1,10,10,0|-ezayw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cv5zw1,10,10,0|-cv5zw0,11,11,1|-cofek1,11,11,1|-cofek0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Berlin|,0,333,0|-1421154,10,10,0|-s0e081,10,10,0|-s0e080,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|-ridmk1,10,10,0|-ridmk0,11,11,1|-ragfw1,11,11,1|-ragfw0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,10,10,0|-fizzw1,10,10,0|-fizzw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cucg01,11,11,1|-cucg00,147,6,1|-co0o01,147,6,1|-co0o00,11,11,1|-cl6qk1,11,11,1|-cl6qk0,10,10,0|-cdmik1,10,10,0|-cdmik0,11,11,1|-c4kl81,11,11,1|-c4kl80,10,10,0|-bv9bs1,10,10,0|-bv9bs0,11,11,1|-btgl81,11,11,1|-btgl80,147,6,1|-bqxxc1,147,6,1|-bqxxc0,11,11,1|-blwd81,11,11,1|-blwd80,10,10,0|-bbtek1,10,10,0|-bbtek0,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|-atgak1,10,10,0|-atgak0,11,11,1|-akg7w1,11,11,1|-akg7w0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Bratislava|,0,334,0|-1qmkw08,7,334,0|-14u7uo9,7,334,0|-14u7uo8,10,10,0|-s0e081,10,10,0|-s0e080,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|-ridmk1,10,10,0|-ridmk0,11,11,1|-ragfw1,11,11,1|-ragfw0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,10,10,0|-fizzw1,10,10,0|-fizzw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cnnmk1,11,11,1|-cnnmk0,10,10,0|-cchrw1,10,10,0|-cchrw0,11,11,1|-c4mfw1,11,11,1|-c4mfw0,10,10,0|-c1qns1,10,10,0|-c1qns0,1,1,1|-bxf3s1,1,1,1|-bxf3s0,10,10,0|-bujh81,10,10,0|-bujh80,11,11,1|-blwd81,11,11,1|-blwd80,10,10,0|-bbtek1,10,10,0|-bbtek0,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|-ati581,10,10,0|-ati580,11,11,1|-akg7w1,11,11,1|-akg7w0,10,10,0|4tps3z,10,10,0|4tps40,11,11,1|532tfz,11,11,1|532tg0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Brussels|,0,335,0|-1ayy3h6,53,335,0|-14j9c01,53,335,0|-14j9c00,8,1,0|-ss5uo1,8,1,0|-ss5uo0,10,10,0|-s0dxg1,10,10,0|-s0dxg0,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|-ridmk1,10,10,0|-ridmk0,11,11,1|-ragfw1,11,11,1|-ragfw0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,10,10,0|-qotw41,10,10,0|-qotw40,8,1,0|-qj59g1,8,1,0|-qj59g0,9,10,1|-q7zes1,9,10,1|-q7zes0,8,1,0|-q15441,8,1,0|-q15440,9,10,1|-po6g41,9,10,1|-po6g40,8,1,0|-pgvhg1,8,1,0|-pgvhg0,9,10,1|-p5atg1,9,10,1|-p5atg0,8,1,0|-oxj6s1,8,1,0|-oxj6s0,9,10,1|-ong841,9,10,1|-ong840,8,1,0|-odd9g1,8,1,0|-odd9g0,9,10,1|-o4q5g1,9,10,1|-o4q5g0,8,1,0|-nvq2s1,8,1,0|-nvq2s0,9,10,1|-nm02s1,9,10,1|-nm02s0,8,1,0|-ncn1g1,8,1,0|-ncn1g0,9,10,1|-n3a041,9,10,1|-n3a040,8,1,0|-mt71g1,8,1,0|-mt71g0,9,10,1|-mkjxg1,9,10,1|-mkjxg0,8,1,0|-matxg1,8,1,0|-matxg0,9,10,1|-m1tus1,9,10,1|-m1tus0,8,1,0|-lrqw41,8,1,0|-lrqw40,9,10,1|-liql41,9,10,1|-liql40,8,1,0|-l8nmg1,8,1,0|-l8nmg0,9,10,1|-l00ig1,9,10,1|-l00ig0,8,1,0|-kqaig1,8,1,0|-kqaig0,9,10,1|-khafs1,9,10,1|-khafs0,8,1,0|-k77h41,8,1,0|-k77h40,9,10,1|-jykd41,9,10,1|-jykd40,8,1,0|-jp7bs1,8,1,0|-jp7bs0,9,10,1|-jfuag1,9,10,1|-jfuag0,8,1,0|-j6u7s1,8,1,0|-j6u7s0,9,10,1|-iwr941,9,10,1|-iwr940,8,1,0|-ine7s1,8,1,0|-ine7s0,9,10,1|-ie16g1,9,10,1|-ie16g0,8,1,0|-i513s1,8,1,0|-i513s0,9,10,1|-hvb3s1,9,10,1|-hvb3s0,8,1,0|-hl8541,8,1,0|-hl8540,9,10,1|-hcl141,9,10,1|-hcl140,8,1,0|-h37zs1,8,1,0|-h37zs0,9,10,1|-gtuyg1,9,10,1|-gtuyg0,8,1,0|-gkuvs1,8,1,0|-gkuvs0,9,10,1|-gb4vs1,9,10,1|-gb4vs0,8,1,0|-g11x41,8,1,0|-g11x40,9,10,1|-fpw2g1,9,10,1|-fpw2g0,8,1,0|-fkul41,8,1,0|-fkul40,9,10,1|-fgh6g1,9,10,1|-fgh6g0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d75h81,11,11,1|-d75h80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cofek1,11,11,1|-cofek0,10,10,0|-cbtp81,10,10,0|-cbtp80,11,11,1|-c4kl81,11,11,1|-c4kl80,10,10,0|3s9mrz,10,10,0|3s9ms0,11,11,1|419pfz,11,11,1|419pg0,10,10,0|4azpfz,10,10,0|4azpg0,11,11,1|4kcqrz,11,11,1|4kcqs0,10,10,0|4tps3z,10,10,0|4tps40,11,11,1|532tfz,11,11,1|532tg0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Bucharest|,0,336,0|-14u7wu0,53,336,0|-k29zi1,53,336,0|-k29zi0,15,11,0|-jmqqw1,15,11,0|-jmqqw0,16,6,1|-jfulk1,16,6,1|-jfulk0,15,11,0|-j6hk81,15,11,0|-j6hk80,16,6,1|-ix4iw1,16,6,1|-ix4iw0,15,11,0|-ineiw1,15,11,0|-ineiw0,16,6,1|-ie1hk1,16,6,1|-ie1hk0,15,11,0|-i4og81,15,11,0|-i4og80,16,6,1|-hvbew1,16,6,1|-hvbew0,15,11,0|-hlydk1,15,11,0|-hlydk0,16,6,1|-hclc81,16,6,1|-hclc80,15,11,0|-h38aw1,15,11,0|-h38aw0,16,6,1|-gtv9k1,16,6,1|-gtv9k0,15,11,0|-gki881,15,11,0|-gki880,16,6,1|-gb56w1,16,6,1|-gb56w0,15,11,0|-g1s5k1,15,11,0|-g1s5k0,16,6,1|-fsf481,16,6,1|-fsf480,15,11,0|4wl93z,15,11,0|4wl940,16,6,1|532ibz,16,6,1|532ic0,15,11,0|5csibz,15,11,0|5csic0,16,6,1|5lsnrz,16,6,1|5lsns0,15,11,0|5v5unz,15,11,0|5v5uo0,16,6,1|64ivzz,16,6,1|64iw00,15,11,0|6dvxbz,15,11,0|6dvxc0,16,6,1|6n8ynz,16,6,1|6n8yo0,15,11,0|6wlzzz,15,11,0|6wm000,16,6,1|75z1bz,16,6,1|75z1c0,15,11,0|7fc2nz,15,11,0|7fc2o0,16,6,1|7p22nz,16,6,1|7p22o0,15,11,0|7yf3zz,15,11,0|7yf400,16,6,1|87s5bz,16,6,1|87s5c0,15,11,0|8h56nz,15,11,0|8h56o0,16,6,1|8qi7zz,16,6,1|8qi800,15,11,0|8zv9bz,15,11,0|8zv9c0,16,6,1|998anz,16,6,1|998ao0,15,11,0|9ilbzz,15,11,0|9ilc00,16,6,1|9rydbz,16,6,1|9rydc0,15,11,0|a1benz,15,11,0|a1beo0,16,6,1|aaofzz,16,6,1|aaog00,15,11,0|ak1hbz,15,11,0|ak1hc0,16,6,1|atrhbz,16,6,1|atrhc0,15,11,0|b34d3z,15,11,0|b34d40,16,6,1|bchefz,16,6,1|bcheg0,15,11,0|blufrz,15,11,0|blufs0,16,6,1|bv7h3z,16,6,1|bv7h40,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxjrz,16,6,1|cdxjs0,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cwnjnz,16,6,1|cwnjo0,15,11,0|d60nrz,15,11,0|d60ns0,16,6,1|dfdmbz,16,6,1|dfdmc0,15,11,0|dp3p3z,15,11,0|dp3p40,16,6,1|dzwibz,16,6,1|dzwic0,15,11,0|e7u03z,15,11,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Budapest|,0,337,0|-15bee78,10,10,0|-s0e081,10,10,0|-s0e080,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|-ridmk1,10,10,0|-ridmk0,11,11,1|-ragfw1,11,11,1|-ragfw0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,10,10,0|-qgvmk1,10,10,0|-qgvmk0,11,11,1|-q90ak1,11,11,1|-q90ak0,10,10,0|-pykd81,10,10,0|-pykd80,11,11,1|-ppx981,11,11,1|-ppx980,10,10,0|-ezvc81,10,10,0|-ezvc80,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cvhc81,10,10,0|-cvhc80,11,11,1|-cm2dg1,11,11,1|-cm2dg0,10,10,0|-cecfw1,10,10,0|-cecfw0,11,11,1|-c4ko01,11,11,1|-c4ko00,10,10,0|-bv9ek1,10,10,0|-bv9ek0,11,11,1|-blwd81,11,11,1|-blwd80,10,10,0|-bcjbw1,10,10,0|-bcjbw0,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|-atgak1,10,10,0|-atgak0,11,11,1|-akg7w1,11,11,1|-akg7w0,10,10,0|-85bc41,10,10,0|-85bc40,11,11,1|-7yh481,11,11,1|-7yh480,10,10,0|-7ml3w1,10,10,0|-7ml3w0,11,11,1|-7fqt81,11,11,1|-7fqt80,10,10,0|-7353w1,10,10,0|-7353w0,11,11,1|-6x0qk1,11,11,1|-6x0qk0,10,10,0|-6kf181,10,10,0|-6kf180,11,11,1|-6eanw1,11,11,1|-6eanw0,10,10,0|5csnvz,10,10,0|5csnw0,11,11,1|5lsqjz,11,11,1|5lsqk0,10,10,0|5v5rvz,10,10,0|5v5rw0,11,11,1|64it7z,11,11,1|64it80,10,10,0|6dvujz,10,10,0|6dvuk0,11,11,1|6n8vvz,11,11,1|6n8vw0,10,10,0|6wlx7z,10,10,0|6wlx80,11,11,1|75yyjz,11,11,1|75yyk0,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Busingen|,0,338,0|-1os49kw,53,339,0|-13g441n,53,339,0|-13g441m,10,10,0|-eyh6o1,10,10,0|-eyh6o0,11,11,1|-eqk001,11,11,1|-eqk000,10,10,0|-efr401,10,10,0|-efr400,11,11,1|-e7txc1,11,11,1|-e7txc0,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Chisinau|,0,340,0|-1ayy808,41,341,0|-r2p1bp,41,341,0|-r2p1bo,53,336,0|-k29zi1,53,336,0|-k29zi0,15,11,0|-jmqqw1,15,11,0|-jmqqw0,16,6,1|-jfulk1,16,6,1|-jfulk0,15,11,0|-j6hk81,15,11,0|-j6hk80,16,6,1|-ix4iw1,16,6,1|-ix4iw0,15,11,0|-ineiw1,15,11,0|-ineiw0,16,6,1|-ie1hk1,16,6,1|-ie1hk0,15,11,0|-i4og81,15,11,0|-i4og80,16,6,1|-hvbew1,16,6,1|-hvbew0,15,11,0|-hlydk1,15,11,0|-hlydk0,16,6,1|-hclc81,16,6,1|-hclc80,15,11,0|-h38aw1,15,11,0|-h38aw0,16,6,1|-gtv9k1,16,6,1|-gtv9k0,15,11,0|-gki881,15,11,0|-gki880,16,6,1|-gb56w1,16,6,1|-gb56w0,15,11,0|-g1s5k1,15,11,0|-g1s5k0,16,6,1|-fsf481,16,6,1|-fsf480,15,11,0|-fc0dk1,15,11,0|-fc0dk0,16,6,1|-euq8c1,16,6,1|-euq8c0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d8e5k1,11,11,1|-d8e5k0,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,149,209,1|aaod7z,149,209,1|aaod80,148,6,0|ak1ejz,148,6,0|ak1ek0,149,209,1|am73rz,149,209,1|am73s0,16,6,1|atrhbz,16,6,1|atrhc0,15,11,0|b34inz,15,11,0|b34io0,16,6,1|bchjzz,16,6,1|bchk00,15,11,0|blufrz,15,11,0|blufs0,16,6,1|bv7ebz,16,6,1|bv7ec0,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxgzz,16,6,1|cdxh00,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cwnjnz,16,6,1|cwnjo0,15,11,0|d60nrz,15,11,0|d60ns0,16,6,1|dfdmbz,16,6,1|dfdmc0,15,11,0|dp3p3z,15,11,0|dp3p40,16,6,1|dzwibz,16,6,1|dzwic0,15,11,0|e7txbz,15,11,0|e7txc0,16,6,1|eimtbz,16,6,1|eimtc0,15,11,0|eqjzzz,15,11,0|eqk000,16,6,1|f1cvzz,16,6,1|f1cw00,15,11,0|f9a2nz,15,11,0|f9a2o0,16,6,1|fkfxbz,16,6,1|fkfxc0,15,11,0|fs05bz,15,11,0|fs05c0,16,6,1|g35zzz,16,6,1|g36000,15,11,0|gaq7zz,15,11,0|gaq800,16,6,1|glw2nz,16,6,1|glw2o0,15,11,0|gtt9bz,15,11,0|gtt9c0,16,6,1|h4m5bz,16,6,1|h4m5c0,15,11,0|hcjbzz,15,11,0|hcjc00,16,6,1|hnc7zz,16,6,1|hnc800,15,11,0|hv9enz,15,11,0|hv9eo0,16,6,1|i6f9bz,16,6,1|i6f9c0,15,11,0|idzhbz,15,11,0|idzhc0,16,6,1|ip5bzz,16,6,1|ip5c00,15,11,0|iwpjzz,15,11,0|iwpk00,16,6,1|j7venz,16,6,1|j7veo0,15,11,0|jffmnz,15,11,0|jffmo0,16,6,1|jqlhbz,16,6,1|jqlhc0,15,11,0|jyinzz,15,11,0|jyio00,16,6,1|k9bjzz,16,6,1|k9bk00,15,11,0|kh8qnz,15,11,0|kh8qo0,16,6,1|ks1mnz,16,6,1|ks1mo0,15,11,0|kzytbz,15,11,0|kzytc0,16,6,1|lb4nzz,16,6,1|lb4o00,15,11,0|liovzz,15,11,0|liow00,16,6,1|ltuqnz,16,6,1|ltuqo0,15,11,0|m1eynz,15,11,0|m1eyo0,16,6,1|mcktbz,16,6,1|mcktc0,15,11,0|mkhzzz,15,11,0|mki000,16,6,1|mvavzz,16,6,1|mvaw00,15,11,0|n382nz,15,11,0|n382o0,16,6,1|ne0ynz,16,6,1|ne0yo0,15,11,0|nly5bz,15,11,0|nly5c0,16,6,1|nwr1bz,16,6,1|nwr1c0,15,11,0|o4o7zz,15,11,0|o4o800,16,6,1|ofu2nz,16,6,1|ofu2o0,15,11,0|oneanz,15,11,0|oneao0,16,6,1|oyk5bz,16,6,1|oyk5c0,15,11,0|p64dbz,15,11,0|p64dc0,16,6,1|pha7zz,16,6,1|pha800,15,11,0|pp7enz,15,11,0|pp7eo0,16,6,1|q00anz,16,6,1|q00ao0,15,11,0|q7xhbz,15,11,0|q7xhc0,16,6,1|qiqdbz,16,6,1|qiqdc0,15,11,0|qqnjzz,15,11,0|qqnk00,16,6,1|r1tenz,16,6,1|r1teo0,15,11,0|r9dmnz,15,11,0|r9dmo0,16,6,1|rkjhbz,16,6,1|rkjhc0,15,11,0|rs3pbz,15,11,0|rs3pc0,16,6,1|s39jzz,16,6,1|s39k00,15,11,0|sb6qnz,15,11,0|sb6qo0,16,6,1|slzmnz,16,6,1|slzmo0,15,11,0|stwtbz,15,11,0|stwtc0,16,6,1|t4ppbz,16,6,1|t4ppc0,15,11,0|tcmvzz,15,11,0|tcmw00,16,6,1|tnfrzz,16,6,1|tnfs00,15,11,0|tvcynz,15,11,0|tvcyo0,16,6,1|u6itbz,16,6,1|u6itc0,15,11,0|ue31bz,15,11,0|ue31c0,16,6,1|up8vzz,16,6,1|up8w00,15,11,0|uwt3zz,15,11,0|uwt400,16,6,1|v7yynz,16,6,1|v7yyo0,15,11,0|vfw5bz,15,11,0|vfw5c0,16,6,1|vqp1bz,16,6,1|vqp1c0,15,11,0|vym7zz,15,11,0|vym800,16,6,1|w9f3zz,16,6,1|w9f400,15,11,0|whcanz,15,11,0|whcao0,16,6,1|wsi5bz,16,6,1|wsi5c0,15,11,0|x02dbz,15,11,0|x02dc0,16,6,1|xb87zz,16,6,1|xb8800,15,11,0|xisfzz,15,11,0|xisg00,16,6,1|xtyanz,16,6,1|xtyao0,15,11,0|y1iinz,15,11,0|y1iio0,16,6,1|ycodbz,16,6,1|ycodc0,15,11,0|ykljzz,15,11,0|yklk00,16,6,1|yvefzz,16,6,1|yveg00,15,11,0|z3bmnz,15,11,0|z3bmo0,16,6,1|ze4inz,16,6,1|ze4io0,15,11,0\",\"Europe/Copenhagen|,0,342,0|-15r1bnw,41,342,0|-13nvrnx,41,342,0|-13nvrnw,10,10,0|-rzo2w1,10,10,0|-rzo2w0,11,11,1|-rsir01,11,11,1|-rsir00,10,10,0|-fgqo41,10,10,0|-fgqo40,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cq2nw1,11,11,1|-cq2nw0,10,10,0|-ccr181,10,10,0|-ccr180,11,11,1|-c6f981,11,11,1|-c6f980,10,10,0|-bttjw1,10,10,0|-bttjw0,11,11,1|-bos2k1,11,11,1|-bos2k0,10,10,0|-baqik1,10,10,0|-baqik0,11,11,1|-b61zw1,11,11,1|-b61zw0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Dublin|,0,343,0|-1anxquc,150,344,0|-rzcmls,150,344,0|-rzcmlr,110,345,1|-rsibxs,110,345,1|-rsibxr,1,1,0|-risd41,1,1,0|-risd40,27,10,1|-ragd41,27,10,1|-ragd40,1,1,0|-r0s7s1,1,1,0|-r0s7s0,27,10,1|-qr0d41,27,10,1|-qr0d40,1,1,0|-qhp6g1,1,1,0|-qhp6g0,27,10,1|-q8aag1,27,10,1|-q8aag0,1,1,0|-pyz3s1,1,1,0|-pyz3s0,27,10,1|-po4d41,27,10,1|-po4d40,1,1,0|-pfw2g1,1,1,0|-pfw2g0,27,10,1|-p6h6g1,27,10,1|-p6h6g0,1,1,0|-oxiyg1,1,1,0|-oxiyg0,110,10,1|-onfzs1,110,10,1|-onfzs0,1,1,0|-odd141,1,1,0|-odd140,110,10,1|-o5st41,110,10,1|-o5st40,1,1,0|-nuzx41,1,1,0|-nuzx40,110,10,1|-nmprs1,110,10,1|-nmprs0,1,1,0|-nbwvs1,1,1,0|-nbwvs0,110,10,1|-n39rs1,110,10,1|-n39rs0,1,1,0|-mt6t41,1,1,0|-mt6t40,110,10,1|-mkjp41,110,10,1|-mkjp40,1,1,0|-matp41,1,1,0|-matp40,110,10,1|-m1tmg1,110,10,1|-m1tmg0,1,1,0|-lrdp41,1,1,0|-lrdp40,110,10,1|-liql41,110,10,1|-liql40,1,1,0|-l8nmg1,1,1,0|-l8nmg0,110,10,1|-l00ig1,110,10,1|-l00ig0,1,1,0|-kqaig1,1,1,0|-kqaig0,110,10,1|-khafs1,110,10,1|-khafs0,1,1,0|-k77h41,1,1,0|-k77h40,110,10,1|-jykd41,110,10,1|-jykd40,1,1,0|-joheg1,1,1,0|-joheg0,110,10,1|-jfuag1,110,10,1|-jfuag0,1,1,0|-j64ag1,1,1,0|-j64ag0,110,10,1|-iwr941,110,10,1|-iwr940,1,1,0|-imoag1,1,1,0|-imoag0,110,10,1|-ie16g1,110,10,1|-ie16g0,1,1,0|-i4b6g1,1,1,0|-i4b6g0,110,10,1|-hvb3s1,110,10,1|-hvb3s0,1,1,0|-hl8541,1,1,0|-hl8540,110,10,1|-hcl141,110,10,1|-hcl140,1,1,0|-h2i2g1,1,1,0|-h2i2g0,110,10,1|-gtuyg1,110,10,1|-gtuyg0,1,1,0|-gk4yg1,1,1,0|-gk4yg0,110,10,1|-gb4vs1,110,10,1|-gb4vs0,1,1,0|-g11x41,1,1,0|-g11x40,110,10,1|-fpw2g1,110,10,1|-fpw2g0,1,1,0|-fkul41,1,1,0|-fkul40,110,10,1|-c4md41,110,10,1|-c4md40,1,1,0|-bwc7s1,1,1,0|-bwc7s0,110,10,1|-bkgfs1,110,10,1|-bkgfs0,1,1,0|-bbtbs1,1,1,0|-bbtbs0,110,10,1|-b1qd41,110,10,1|-b1qd40,1,1,0|-att6g1,1,1,0|-att6g0,110,10,1|-aj0ag1,110,10,1|-aj0ag0,1,1,0|-aad6g1,1,1,0|-aad6g0,110,10,1|-a0n6g1,110,10,1|-a0n6g0,1,1,0|-9rn3s1,1,1,0|-9rn3s0,110,10,1|-9hx3s1,110,10,1|-9hx3s0,1,1,0|-98k2g1,1,1,0|-98k2g0,110,10,1|-8yu2g1,110,10,1|-8yu2g0,1,1,0|-8ptzs1,1,1,0|-8ptzs0,110,10,1|-8h6vs1,110,10,1|-8h6vs0,1,1,0|-87gvs1,1,1,0|-87gvs0,110,10,1|-7ygt41,110,10,1|-7ygt40,1,1,0|-7odug1,1,1,0|-7odug0,110,10,1|-7fqqg1,110,10,1|-7fqqg0,1,1,0|-75at41,1,1,0|-75at40,110,10,1|-6wnp41,110,10,1|-6wnp40,1,1,0|-6mxp41,1,1,0|-6mxp40,110,10,1|-6dxmg1,110,10,1|-6dxmg0,1,1,0|-63uns1,1,1,0|-63uns0,110,10,1|-5v7js1,110,10,1|-5v7js0,1,1,0|-5l4l41,1,1,0|-5l4l40,110,10,1|-5chh41,110,10,1|-5chh40,1,1,0|-52rh41,1,1,0|-52rh40,110,10,1|-4treg1,110,10,1|-4treg0,1,1,0|-4krbs1,1,1,0|-4krbs0,110,10,1|-49lh41,110,10,1|-49lh40,1,1,0|-421941,1,1,0|-421940,110,10,1|-3qveg1,110,10,1|-3qveg0,1,1,0|-3iy7s1,1,1,0|-3iy7s0,110,10,1|-385bs1,110,10,1|-385bs0,1,1,0|-30l3s1,1,1,0|-30l3s0,110,10,1|-2pf941,110,10,1|-2pf940,1,1,0|-2hv141,1,1,0|-2hv140,110,10,1|-26p6g1,110,10,1|-26p6g0,1,1,0|-1z4yg1,1,1,0|-1z4yg0,110,10,1|-1nz3s1,110,10,1|-1nz3s0,1,1,0|-1gevs1,1,1,0|-1gevs0,110,10,1|-14w2g1,110,10,1|-14w2g0,1,1,0|-z4ns1,1,1,0|-z4ns0,110,10,1|-m6841,110,10,1|-m6840,110,10,0|yd6vz,110,10,0|yd6w0,1,1,1|15kg7z,1,1,1|15kg80,110,10,0|1h39jz,110,10,0|1h39k0,1,1,1|1oaivz,1,1,1|1oaiw0,110,10,0|1ztc7z,110,10,0|1ztc80,1,1,1|270ljz,1,1,1|270lk0,110,10,0|2ijevz,110,10,0|2ijew0,1,1,1|2pqo7z,1,1,1|2pqo80,110,10,0|319hjz,110,10,0|319hk0,1,1,1|38tpjz,1,1,1|38tpk0,110,10,0|3jzk7z,110,10,0|3jzk80,1,1,1|3rjs7z,1,1,1|3rjs80,110,10,0|42pmvz,110,10,0|42pmw0,1,1,1|4a9uvz,1,1,1|4a9uw0,110,10,0|4lso7z,110,10,0|4lso80,1,1,1|4szxjz,1,1,1|4szxk0,110,10,0|54iqvz,110,10,0|54iqw0,1,1,1|5bq07z,1,1,1|5bq080,110,10,0|5n8tjz,110,10,0|5n8tk0,1,1,1|5v5xfz,1,1,1|5v5xg0,110,10,0|65ytfz,110,10,0|65ytg0,1,1,1|6dw03z,1,1,1|6dw040,110,10,0|6oow3z,110,10,0|6oow40,1,1,1|6wm2rz,1,1,1|6wm2s0,110,10,0|77eyrz,110,10,0|77eys0,1,1,1|7fc5fz,1,1,1|7fc5g0,110,10,0|7qi03z,110,10,0|7qi040,1,1,1|7yf6rz,1,1,1|7yf6s0,110,10,0|8982rz,110,10,0|8982s0,1,1,1|8h59fz,1,1,1|8h59g0,110,10,0|8ry5fz,110,10,0|8ry5g0,1,1,1|8zvc3z,1,1,1|8zvc40,110,10,0|9ao83z,110,10,0|9ao840,1,1,1|9ilerz,1,1,1|9iles0,110,10,0|9tearz,110,10,0|9teas0,1,1,1|a1bhfz,1,1,1|a1bhg0,110,10,0|achc3z,110,10,0|achc40,1,1,1|ak1k3z,1,1,1|ak1k40,110,10,0|av7erz,110,10,0|av7es0,1,1,1|b34lfz,1,1,1|b34lg0,110,10,0|bdxhfz,110,10,0|bdxhg0,1,1,1|bluo3z,1,1,1|bluo40,110,10,0|bwnk3z,110,10,0|bwnk40,1,1,1|c4kqrz,1,1,1|c4kqs0,110,10,0|cfdmrz,110,10,0|cfdms0,1,1,1|cnatfz,1,1,1|cnatg0,110,10,0|cy3pfz,110,10,0|cy3pg0,1,1,1|d60w3z,1,1,1|d60w40,110,10,0|dgts3z,110,10,0|dgts40,1,1,1|dp3xfz,1,1,1|dp3xg0,110,10,0|dzwtfz,110,10,0|dzwtg0,1,1,1|e7u03z,1,1,1|e7u040,110,10,0|eimw3z,110,10,0|eimw40,1,1,1|eqk2rz,1,1,1|eqk2s0,110,10,0|f1cyrz,110,10,0|f1cys0,1,1,1|f9a5fz,1,1,1|f9a5g0,110,10,0|fkg03z,110,10,0|fkg040,1,1,1|fs083z,1,1,1|fs0840,110,10,0|g362rz,110,10,0|g362s0,1,1,1|gaqarz,1,1,1|gaqas0,110,10,0|glw5fz,110,10,0|glw5g0,1,1,1|gttc3z,1,1,1|gttc40,110,10,0|h4m83z,110,10,0|h4m840,1,1,1|hcjerz,1,1,1|hcjes0,110,10,0|hncarz,110,10,0|hncas0,1,1,1|hv9hfz,1,1,1|hv9hg0,110,10,0|i6fc3z,110,10,0|i6fc40,1,1,1|idzk3z,1,1,1|idzk40,110,10,0|ip5erz,110,10,0|ip5es0,1,1,1|iwpmrz,1,1,1|iwpms0,110,10,0|j7vhfz,110,10,0|j7vhg0,1,1,1|jffpfz,1,1,1|jffpg0,110,10,0|jqlk3z,110,10,0|jqlk40,1,1,1|jyiqrz,1,1,1|jyiqs0,110,10,0|k9bmrz,110,10,0|k9bms0,1,1,1|kh8tfz,1,1,1|kh8tg0,110,10,0|ks1pfz,110,10,0|ks1pg0,1,1,1|kzyw3z,1,1,1|kzyw40,110,10,0|lb4qrz,110,10,0|lb4qs0,1,1,1|lioyrz,1,1,1|lioys0,110,10,0|ltutfz,110,10,0|ltutg0,1,1,1|m1f1fz,1,1,1|m1f1g0,110,10,0|mckw3z,110,10,0|mckw40,1,1,1|mki2rz,1,1,1|mki2s0,110,10,0|mvayrz,110,10,0|mvays0,1,1,1|n385fz,1,1,1|n385g0,110,10,0|ne11fz,110,10,0|ne11g0,1,1,1|nly83z,1,1,1|nly840,110,10,0|nwr43z,110,10,0|nwr440,1,1,1|o4oarz,1,1,1|o4oas0,110,10,0|ofu5fz,110,10,0|ofu5g0,1,1,1|onedfz,1,1,1|onedg0,110,10,0|oyk83z,110,10,0|oyk840,1,1,1|p64g3z,1,1,1|p64g40,110,10,0|phaarz,110,10,0|phaas0,1,1,1|pp7hfz,1,1,1|pp7hg0,110,10,0|q00dfz,110,10,0|q00dg0,1,1,1|q7xk3z,1,1,1|q7xk40,110,10,0|qiqg3z,110,10,0|qiqg40,1,1,1|qqnmrz,1,1,1|qqnms0,110,10,0|r1thfz,110,10,0|r1thg0,1,1,1|r9dpfz,1,1,1|r9dpg0,110,10,0|rkjk3z,110,10,0|rkjk40,1,1,1|rs3s3z,1,1,1|rs3s40,110,10,0|s39mrz,110,10,0|s39ms0,1,1,1|sb6tfz,1,1,1|sb6tg0,110,10,0|slzpfz,110,10,0|slzpg0,1,1,1|stww3z,1,1,1|stww40,110,10,0|t4ps3z,110,10,0|t4ps40,1,1,1|tcmyrz,1,1,1|tcmys0,110,10,0|tnfurz,110,10,0|tnfus0,1,1,1|tvd1fz,1,1,1|tvd1g0,110,10,0|u6iw3z,110,10,0|u6iw40,1,1,1|ue343z,1,1,1|ue3440,110,10,0|up8yrz,110,10,0|up8ys0,1,1,1|uwt6rz,1,1,1|uwt6s0,110,10,0|v7z1fz,110,10,0|v7z1g0,1,1,1|vfw83z,1,1,1|vfw840,110,10,0|vqp43z,110,10,0|vqp440,1,1,1|vymarz,1,1,1|vymas0,110,10,0|w9f6rz,110,10,0|w9f6s0,1,1,1|whcdfz,1,1,1|whcdg0,110,10,0|wsi83z,110,10,0|wsi840,1,1,1|x02g3z,1,1,1|x02g40,110,10,0|xb8arz,110,10,0|xb8as0,1,1,1|xisirz,1,1,1|xisis0,110,10,0|xtydfz,110,10,0|xtydg0,1,1,1|y1ilfz,1,1,1|y1ilg0,110,10,0|ycog3z,110,10,0|ycog40,1,1,1|yklmrz,1,1,1|yklms0,110,10,0|yveirz,110,10,0|yveis0,1,1,1|z3bpfz,1,1,1|z3bpg0,110,10,0|ze4lfz,110,10,0|ze4lg0,1,1,1\",\"Europe/Gibraltar|,0,346,0|-1anxr0c,1,1,0|-rzcns1,1,1,0|-rzcns0,27,10,1|-rsid41,27,10,1|-rsid40,1,1,0|-risd41,1,1,0|-risd40,27,10,1|-ragd41,27,10,1|-ragd40,1,1,0|-r0s7s1,1,1,0|-r0s7s0,27,10,1|-qr0d41,27,10,1|-qr0d40,1,1,0|-qhp6g1,1,1,0|-qhp6g0,27,10,1|-q8aag1,27,10,1|-q8aag0,1,1,0|-pyz3s1,1,1,0|-pyz3s0,27,10,1|-po4d41,27,10,1|-po4d40,1,1,0|-pfw2g1,1,1,0|-pfw2g0,27,10,1|-p6h6g1,27,10,1|-p6h6g0,1,1,0|-oxiyg1,1,1,0|-oxiyg0,27,10,1|-onfzs1,27,10,1|-onfzs0,1,1,0|-odd141,1,1,0|-odd140,27,10,1|-o5st41,27,10,1|-o5st40,1,1,0|-nuzx41,1,1,0|-nuzx40,27,10,1|-nmprs1,27,10,1|-nmprs0,1,1,0|-nbwvs1,1,1,0|-nbwvs0,27,10,1|-n39rs1,27,10,1|-n39rs0,1,1,0|-mt6t41,1,1,0|-mt6t40,27,10,1|-mkjp41,27,10,1|-mkjp40,1,1,0|-matp41,1,1,0|-matp40,27,10,1|-m1tmg1,27,10,1|-m1tmg0,1,1,0|-lrdp41,1,1,0|-lrdp40,27,10,1|-liql41,27,10,1|-liql40,1,1,0|-l8nmg1,1,1,0|-l8nmg0,27,10,1|-l00ig1,27,10,1|-l00ig0,1,1,0|-kqaig1,1,1,0|-kqaig0,27,10,1|-khafs1,27,10,1|-khafs0,1,1,0|-k77h41,1,1,0|-k77h40,27,10,1|-jykd41,27,10,1|-jykd40,1,1,0|-joheg1,1,1,0|-joheg0,27,10,1|-jfuag1,27,10,1|-jfuag0,1,1,0|-j64ag1,1,1,0|-j64ag0,27,10,1|-iwr941,27,10,1|-iwr940,1,1,0|-imoag1,1,1,0|-imoag0,27,10,1|-ie16g1,27,10,1|-ie16g0,1,1,0|-i4b6g1,1,1,0|-i4b6g0,27,10,1|-hvb3s1,27,10,1|-hvb3s0,1,1,0|-hl8541,1,1,0|-hl8540,27,10,1|-hcl141,27,10,1|-hcl140,1,1,0|-h2i2g1,1,1,0|-h2i2g0,27,10,1|-gtuyg1,27,10,1|-gtuyg0,1,1,0|-gk4yg1,1,1,0|-gk4yg0,27,10,1|-gb4vs1,27,10,1|-gb4vs0,1,1,0|-g11x41,1,1,0|-g11x40,27,10,1|-fpw2g1,27,10,1|-fpw2g0,1,1,0|-fkul41,1,1,0|-fkul40,27,10,1|-eyiyk1,27,10,1|-eyiyk0,151,11,1|-ethh81,151,11,1|-ethh80,27,10,1|-eh8qk1,27,10,1|-eh8qk0,151,11,1|-earek1,151,11,1|-earek0,27,10,1|-dyinw1,27,10,1|-dyinw0,151,11,1|-drod81,151,11,1|-drod80,27,10,1|-dfsl81,27,10,1|-dfsl80,151,11,1|-d75h81,151,11,1|-d75h80,27,10,1|-cx0nw1,27,10,1|-cx0nw0,151,11,1|-cro2k1,151,11,1|-cro2k0,27,10,1|-cncfs1,27,10,1|-cncfs0,1,1,0|-cdmfs1,1,1,0|-cdmfs0,27,10,1|-c4md41,27,10,1|-c4md40,1,1,0|-bwc7s1,1,1,0|-bwc7s0,27,10,1|-buwfw1,27,10,1|-buwfw0,151,11,1|-bos2k1,151,11,1|-bos2k0,27,10,1|-bkgfs1,27,10,1|-bkgfs0,1,1,0|-bdm541,1,1,0|-bdm540,27,10,1|-b1qd41,27,10,1|-b1qd40,1,1,0|-att6g1,1,1,0|-att6g0,27,10,1|-aj0ag1,27,10,1|-aj0ag0,1,1,0|-aad6g1,1,1,0|-aad6g0,27,10,1|-a0n6g1,27,10,1|-a0n6g0,1,1,0|-9rn3s1,1,1,0|-9rn3s0,27,10,1|-9hx3s1,27,10,1|-9hx3s0,1,1,0|-98k2g1,1,1,0|-98k2g0,27,10,1|-8yu2g1,27,10,1|-8yu2g0,1,1,0|-8ptzs1,1,1,0|-8ptzs0,27,10,1|-8h6vs1,27,10,1|-8h6vs0,1,1,0|-87gvs1,1,1,0|-87gvs0,27,10,1|-7ygt41,27,10,1|-7ygt40,1,1,0|-7odug1,1,1,0|-7odug0,27,10,1|-7fqqg1,27,10,1|-7fqqg0,1,1,0|-75at41,1,1,0|-75at40,27,10,1|-6wnp41,27,10,1|-6wnp40,1,1,0|-6mxp41,1,1,0|-6mxp40,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Guernsey|,0,347,0|-1rprx9x,1,1,0|-rzcns1,1,1,0|-rzcns0,27,10,1|-rsid41,27,10,1|-rsid40,1,1,0|-risd41,1,1,0|-risd40,27,10,1|-ragd41,27,10,1|-ragd40,1,1,0|-r0s7s1,1,1,0|-r0s7s0,27,10,1|-qr0d41,27,10,1|-qr0d40,1,1,0|-qhp6g1,1,1,0|-qhp6g0,27,10,1|-q8aag1,27,10,1|-q8aag0,1,1,0|-pyz3s1,1,1,0|-pyz3s0,27,10,1|-po4d41,27,10,1|-po4d40,1,1,0|-pfw2g1,1,1,0|-pfw2g0,27,10,1|-p6h6g1,27,10,1|-p6h6g0,1,1,0|-oxiyg1,1,1,0|-oxiyg0,27,10,1|-onfzs1,27,10,1|-onfzs0,1,1,0|-odd141,1,1,0|-odd140,27,10,1|-o5st41,27,10,1|-o5st40,1,1,0|-nuzx41,1,1,0|-nuzx40,27,10,1|-nmprs1,27,10,1|-nmprs0,1,1,0|-nbwvs1,1,1,0|-nbwvs0,27,10,1|-n39rs1,27,10,1|-n39rs0,1,1,0|-mt6t41,1,1,0|-mt6t40,27,10,1|-mkjp41,27,10,1|-mkjp40,1,1,0|-matp41,1,1,0|-matp40,27,10,1|-m1tmg1,27,10,1|-m1tmg0,1,1,0|-lrdp41,1,1,0|-lrdp40,27,10,1|-liql41,27,10,1|-liql40,1,1,0|-l8nmg1,1,1,0|-l8nmg0,27,10,1|-l00ig1,27,10,1|-l00ig0,1,1,0|-kqaig1,1,1,0|-kqaig0,27,10,1|-khafs1,27,10,1|-khafs0,1,1,0|-k77h41,1,1,0|-k77h40,27,10,1|-jykd41,27,10,1|-jykd40,1,1,0|-joheg1,1,1,0|-joheg0,27,10,1|-jfuag1,27,10,1|-jfuag0,1,1,0|-j64ag1,1,1,0|-j64ag0,27,10,1|-iwr941,27,10,1|-iwr940,1,1,0|-imoag1,1,1,0|-imoag0,27,10,1|-ie16g1,27,10,1|-ie16g0,1,1,0|-i4b6g1,1,1,0|-i4b6g0,27,10,1|-hvb3s1,27,10,1|-hvb3s0,1,1,0|-hl8541,1,1,0|-hl8540,27,10,1|-hcl141,27,10,1|-hcl140,1,1,0|-h2i2g1,1,1,0|-h2i2g0,27,10,1|-gtuyg1,27,10,1|-gtuyg0,1,1,0|-gk4yg1,1,1,0|-gk4yg0,27,10,1|-gb4vs1,27,10,1|-gb4vs0,1,1,0|-g11x41,1,1,0|-g11x40,27,10,1|-fpw2g1,27,10,1|-fpw2g0,1,1,0|-fkul41,1,1,0|-fkul40,27,10,1|-eyiyk1,27,10,1|-eyiyk0,151,11,1|-ethh81,151,11,1|-ethh80,27,10,1|-eh8qk1,27,10,1|-eh8qk0,151,11,1|-earek1,151,11,1|-earek0,27,10,1|-dyinw1,27,10,1|-dyinw0,151,11,1|-drod81,151,11,1|-drod80,27,10,1|-dfsl81,27,10,1|-dfsl80,151,11,1|-d75h81,151,11,1|-d75h80,27,10,1|-cx0nw1,27,10,1|-cx0nw0,151,11,1|-cro2k1,151,11,1|-cro2k0,27,10,1|-cncfs1,27,10,1|-cncfs0,1,1,0|-cdmfs1,1,1,0|-cdmfs0,27,10,1|-c4md41,27,10,1|-c4md40,1,1,0|-bwc7s1,1,1,0|-bwc7s0,27,10,1|-buwfw1,27,10,1|-buwfw0,151,11,1|-bos2k1,151,11,1|-bos2k0,27,10,1|-bkgfs1,27,10,1|-bkgfs0,1,1,0|-bdm541,1,1,0|-bdm540,27,10,1|-b1qd41,27,10,1|-b1qd40,1,1,0|-att6g1,1,1,0|-att6g0,27,10,1|-aj0ag1,27,10,1|-aj0ag0,1,1,0|-aad6g1,1,1,0|-aad6g0,27,10,1|-a0n6g1,27,10,1|-a0n6g0,1,1,0|-9rn3s1,1,1,0|-9rn3s0,27,10,1|-9hx3s1,27,10,1|-9hx3s0,1,1,0|-98k2g1,1,1,0|-98k2g0,27,10,1|-8yu2g1,27,10,1|-8yu2g0,1,1,0|-8ptzs1,1,1,0|-8ptzs0,27,10,1|-8h6vs1,27,10,1|-8h6vs0,1,1,0|-87gvs1,1,1,0|-87gvs0,27,10,1|-7ygt41,27,10,1|-7ygt40,1,1,0|-7odug1,1,1,0|-7odug0,27,10,1|-7fqqg1,27,10,1|-7fqqg0,1,1,0|-75at41,1,1,0|-75at40,27,10,1|-6wnp41,27,10,1|-6wnp40,1,1,0|-6mxp41,1,1,0|-6mxp40,27,10,1|-6dxmg1,27,10,1|-6dxmg0,1,1,0|-63uns1,1,1,0|-63uns0,27,10,1|-5v7js1,27,10,1|-5v7js0,1,1,0|-5l4l41,1,1,0|-5l4l40,27,10,1|-5chh41,27,10,1|-5chh40,1,1,0|-52rh41,1,1,0|-52rh40,27,10,1|-4treg1,27,10,1|-4treg0,1,1,0|-4krbs1,1,1,0|-4krbs0,27,10,1|-49lh41,27,10,1|-49lh40,1,1,0|-421941,1,1,0|-421940,27,10,1|-3qveg1,27,10,1|-3qveg0,1,1,0|-3iy7s1,1,1,0|-3iy7s0,27,10,1|-385bs1,27,10,1|-385bs0,1,1,0|-30l3s1,1,1,0|-30l3s0,27,10,1|-2pf941,27,10,1|-2pf940,1,1,0|-2hv141,1,1,0|-2hv140,27,10,1|-26p6g1,27,10,1|-26p6g0,1,1,0|-1z4yg1,1,1,0|-1z4yg0,27,10,1|-1nz3s1,27,10,1|-1nz3s0,1,1,0|-1gevs1,1,1,0|-1gevs0,27,10,1|-14w2g1,27,10,1|-14w2g0,1,1,0|-z4ns1,1,1,0|-z4ns0,27,10,1|-m6841,27,10,1|-m6840,27,10,0|yd6vz,27,10,0|yd6w0,1,1,0|15kg7z,1,1,0|15kg80,27,10,1|1h39jz,27,10,1|1h39k0,1,1,0|1oaivz,1,1,0|1oaiw0,27,10,1|1ztc7z,27,10,1|1ztc80,1,1,0|270ljz,1,1,0|270lk0,27,10,1|2ijevz,27,10,1|2ijew0,1,1,0|2pqo7z,1,1,0|2pqo80,27,10,1|319hjz,27,10,1|319hk0,1,1,0|38tpjz,1,1,0|38tpk0,27,10,1|3jzk7z,27,10,1|3jzk80,1,1,0|3rjs7z,1,1,0|3rjs80,27,10,1|42pmvz,27,10,1|42pmw0,1,1,0|4a9uvz,1,1,0|4a9uw0,27,10,1|4lso7z,27,10,1|4lso80,1,1,0|4szxjz,1,1,0|4szxk0,27,10,1|54iqvz,27,10,1|54iqw0,1,1,0|5bq07z,1,1,0|5bq080,27,10,1|5n8tjz,27,10,1|5n8tk0,1,1,0|5v5xfz,1,1,0|5v5xg0,27,10,1|65ytfz,27,10,1|65ytg0,1,1,0|6dw03z,1,1,0|6dw040,27,10,1|6oow3z,27,10,1|6oow40,1,1,0|6wm2rz,1,1,0|6wm2s0,27,10,1|77eyrz,27,10,1|77eys0,1,1,0|7fc5fz,1,1,0|7fc5g0,27,10,1|7qi03z,27,10,1|7qi040,1,1,0|7yf6rz,1,1,0|7yf6s0,27,10,1|8982rz,27,10,1|8982s0,1,1,0|8h59fz,1,1,0|8h59g0,27,10,1|8ry5fz,27,10,1|8ry5g0,1,1,0|8zvc3z,1,1,0|8zvc40,27,10,1|9ao83z,27,10,1|9ao840,1,1,0|9ilerz,1,1,0|9iles0,27,10,1|9tearz,27,10,1|9teas0,1,1,0|a1bhfz,1,1,0|a1bhg0,27,10,1|achc3z,27,10,1|achc40,1,1,0|ak1k3z,1,1,0|ak1k40,27,10,1|av7erz,27,10,1|av7es0,1,1,0|b34lfz,1,1,0|b34lg0,27,10,1|bdxhfz,27,10,1|bdxhg0,1,1,0|bluo3z,1,1,0|bluo40,27,10,1|bwnk3z,27,10,1|bwnk40,1,1,0|c4kqrz,1,1,0|c4kqs0,27,10,1|cfdmrz,27,10,1|cfdms0,1,1,0|cnatfz,1,1,0|cnatg0,27,10,1|cy3pfz,27,10,1|cy3pg0,1,1,0|d60w3z,1,1,0|d60w40,27,10,1|dgts3z,27,10,1|dgts40,1,1,0|dp3xfz,1,1,0|dp3xg0,27,10,1|dzwtfz,27,10,1|dzwtg0,1,1,0|e7u03z,1,1,0|e7u040,27,10,1|eimw3z,27,10,1|eimw40,1,1,0|eqk2rz,1,1,0|eqk2s0,27,10,1|f1cyrz,27,10,1|f1cys0,1,1,0|f9a5fz,1,1,0|f9a5g0,27,10,1|fkg03z,27,10,1|fkg040,1,1,0|fs083z,1,1,0|fs0840,27,10,1|g362rz,27,10,1|g362s0,1,1,0|gaqarz,1,1,0|gaqas0,27,10,1|glw5fz,27,10,1|glw5g0,1,1,0|gttc3z,1,1,0|gttc40,27,10,1|h4m83z,27,10,1|h4m840,1,1,0|hcjerz,1,1,0|hcjes0,27,10,1|hncarz,27,10,1|hncas0,1,1,0|hv9hfz,1,1,0|hv9hg0,27,10,1|i6fc3z,27,10,1|i6fc40,1,1,0|idzk3z,1,1,0|idzk40,27,10,1|ip5erz,27,10,1|ip5es0,1,1,0|iwpmrz,1,1,0|iwpms0,27,10,1|j7vhfz,27,10,1|j7vhg0,1,1,0|jffpfz,1,1,0|jffpg0,27,10,1|jqlk3z,27,10,1|jqlk40,1,1,0|jyiqrz,1,1,0|jyiqs0,27,10,1|k9bmrz,27,10,1|k9bms0,1,1,0|kh8tfz,1,1,0|kh8tg0,27,10,1|ks1pfz,27,10,1|ks1pg0,1,1,0|kzyw3z,1,1,0|kzyw40,27,10,1|lb4qrz,27,10,1|lb4qs0,1,1,0|lioyrz,1,1,0|lioys0,27,10,1|ltutfz,27,10,1|ltutg0,1,1,0|m1f1fz,1,1,0|m1f1g0,27,10,1|mckw3z,27,10,1|mckw40,1,1,0|mki2rz,1,1,0|mki2s0,27,10,1|mvayrz,27,10,1|mvays0,1,1,0|n385fz,1,1,0|n385g0,27,10,1|ne11fz,27,10,1|ne11g0,1,1,0|nly83z,1,1,0|nly840,27,10,1|nwr43z,27,10,1|nwr440,1,1,0|o4oarz,1,1,0|o4oas0,27,10,1|ofu5fz,27,10,1|ofu5g0,1,1,0|onedfz,1,1,0|onedg0,27,10,1|oyk83z,27,10,1|oyk840,1,1,0|p64g3z,1,1,0|p64g40,27,10,1|phaarz,27,10,1|phaas0,1,1,0|pp7hfz,1,1,0|pp7hg0,27,10,1|q00dfz,27,10,1|q00dg0,1,1,0|q7xk3z,1,1,0|q7xk40,27,10,1|qiqg3z,27,10,1|qiqg40,1,1,0|qqnmrz,1,1,0|qqnms0,27,10,1|r1thfz,27,10,1|r1thg0,1,1,0|r9dpfz,1,1,0|r9dpg0,27,10,1|rkjk3z,27,10,1|rkjk40,1,1,0|rs3s3z,1,1,0|rs3s40,27,10,1|s39mrz,27,10,1|s39ms0,1,1,0|sb6tfz,1,1,0|sb6tg0,27,10,1|slzpfz,27,10,1|slzpg0,1,1,0|stww3z,1,1,0|stww40,27,10,1|t4ps3z,27,10,1|t4ps40,1,1,0|tcmyrz,1,1,0|tcmys0,27,10,1|tnfurz,27,10,1|tnfus0,1,1,0|tvd1fz,1,1,0|tvd1g0,27,10,1|u6iw3z,27,10,1|u6iw40,1,1,0|ue343z,1,1,0|ue3440,27,10,1|up8yrz,27,10,1|up8ys0,1,1,0|uwt6rz,1,1,0|uwt6s0,27,10,1|v7z1fz,27,10,1|v7z1g0,1,1,0|vfw83z,1,1,0|vfw840,27,10,1|vqp43z,27,10,1|vqp440,1,1,0|vymarz,1,1,0|vymas0,27,10,1|w9f6rz,27,10,1|w9f6s0,1,1,0|whcdfz,1,1,0|whcdg0,27,10,1|wsi83z,27,10,1|wsi840,1,1,0|x02g3z,1,1,0|x02g40,27,10,1|xb8arz,27,10,1|xb8as0,1,1,0|xisirz,1,1,0|xisis0,27,10,1|xtydfz,27,10,1|xtydg0,1,1,0|y1ilfz,1,1,0|y1ilg0,27,10,1|ycog3z,27,10,1|ycog40,1,1,0|yklmrz,1,1,0|yklms0,27,10,1|yveirz,27,10,1|yveis0,1,1,0|z3bpfz,1,1,0|z3bpg0,27,10,1|ze4lfz,27,10,1|ze4lg0,1,1,0\",\"Europe/Helsinki|,0,348,0|-1bss9yd,77,348,0|-peghye,77,348,0|-peghyd,15,11,0|-ehco81,15,11,0|-ehco80,16,6,1|-e7vxk1,16,6,1|-e7vxk0,15,11,0|5v5unz,15,11,0|5v5uo0,16,6,1|64ivzz,16,6,1|64iw00,15,11,0|6dvxbz,15,11,0|6dvxc0,16,6,1|6n8ynz,16,6,1|6n8yo0,15,11,0|6wm2rz,15,11,0|6wm2s0,16,6,1|75z43z,16,6,1|75z440,15,11,0|7fc5fz,15,11,0|7fc5g0,16,6,1|7p25fz,16,6,1|7p25g0,15,11,0|7yf6rz,15,11,0|7yf6s0,16,6,1|87s83z,16,6,1|87s840,15,11,0|8h59fz,15,11,0|8h59g0,16,6,1|8qiarz,16,6,1|8qias0,15,11,0|8zvc3z,15,11,0|8zvc40,16,6,1|998dfz,16,6,1|998dg0,15,11,0|9ilerz,15,11,0|9iles0,16,6,1|9ryg3z,16,6,1|9ryg40,15,11,0|a1bhfz,15,11,0|a1bhg0,16,6,1|aaoirz,16,6,1|aaois0,15,11,0|ak1k3z,15,11,0|ak1k40,16,6,1|atrk3z,16,6,1|atrk40,15,11,0|b34lfz,15,11,0|b34lg0,16,6,1|bchmrz,16,6,1|bchms0,15,11,0|bluo3z,15,11,0|bluo40,16,6,1|bv7pfz,16,6,1|bv7pg0,15,11,0|c4kqrz,15,11,0|c4kqs0,16,6,1|cdxs3z,16,6,1|cdxs40,15,11,0|cnatfz,15,11,0|cnatg0,16,6,1|cwnurz,16,6,1|cwnus0,15,11,0|d60w3z,15,11,0|d60w40,16,6,1|dfdxfz,16,6,1|dfdxg0,15,11,0|dp3xfz,15,11,0|dp3xg0,16,6,1|dzwtfz,16,6,1|dzwtg0,15,11,0|e7u03z,15,11,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Isle_of_Man|,0,347,0|-1rprx9x,1,1,0|-rzcns1,1,1,0|-rzcns0,27,10,1|-rsid41,27,10,1|-rsid40,1,1,0|-risd41,1,1,0|-risd40,27,10,1|-ragd41,27,10,1|-ragd40,1,1,0|-r0s7s1,1,1,0|-r0s7s0,27,10,1|-qr0d41,27,10,1|-qr0d40,1,1,0|-qhp6g1,1,1,0|-qhp6g0,27,10,1|-q8aag1,27,10,1|-q8aag0,1,1,0|-pyz3s1,1,1,0|-pyz3s0,27,10,1|-po4d41,27,10,1|-po4d40,1,1,0|-pfw2g1,1,1,0|-pfw2g0,27,10,1|-p6h6g1,27,10,1|-p6h6g0,1,1,0|-oxiyg1,1,1,0|-oxiyg0,27,10,1|-onfzs1,27,10,1|-onfzs0,1,1,0|-odd141,1,1,0|-odd140,27,10,1|-o5st41,27,10,1|-o5st40,1,1,0|-nuzx41,1,1,0|-nuzx40,27,10,1|-nmprs1,27,10,1|-nmprs0,1,1,0|-nbwvs1,1,1,0|-nbwvs0,27,10,1|-n39rs1,27,10,1|-n39rs0,1,1,0|-mt6t41,1,1,0|-mt6t40,27,10,1|-mkjp41,27,10,1|-mkjp40,1,1,0|-matp41,1,1,0|-matp40,27,10,1|-m1tmg1,27,10,1|-m1tmg0,1,1,0|-lrdp41,1,1,0|-lrdp40,27,10,1|-liql41,27,10,1|-liql40,1,1,0|-l8nmg1,1,1,0|-l8nmg0,27,10,1|-l00ig1,27,10,1|-l00ig0,1,1,0|-kqaig1,1,1,0|-kqaig0,27,10,1|-khafs1,27,10,1|-khafs0,1,1,0|-k77h41,1,1,0|-k77h40,27,10,1|-jykd41,27,10,1|-jykd40,1,1,0|-joheg1,1,1,0|-joheg0,27,10,1|-jfuag1,27,10,1|-jfuag0,1,1,0|-j64ag1,1,1,0|-j64ag0,27,10,1|-iwr941,27,10,1|-iwr940,1,1,0|-imoag1,1,1,0|-imoag0,27,10,1|-ie16g1,27,10,1|-ie16g0,1,1,0|-i4b6g1,1,1,0|-i4b6g0,27,10,1|-hvb3s1,27,10,1|-hvb3s0,1,1,0|-hl8541,1,1,0|-hl8540,27,10,1|-hcl141,27,10,1|-hcl140,1,1,0|-h2i2g1,1,1,0|-h2i2g0,27,10,1|-gtuyg1,27,10,1|-gtuyg0,1,1,0|-gk4yg1,1,1,0|-gk4yg0,27,10,1|-gb4vs1,27,10,1|-gb4vs0,1,1,0|-g11x41,1,1,0|-g11x40,27,10,1|-fpw2g1,27,10,1|-fpw2g0,1,1,0|-fkul41,1,1,0|-fkul40,27,10,1|-eyiyk1,27,10,1|-eyiyk0,151,11,1|-ethh81,151,11,1|-ethh80,27,10,1|-eh8qk1,27,10,1|-eh8qk0,151,11,1|-earek1,151,11,1|-earek0,27,10,1|-dyinw1,27,10,1|-dyinw0,151,11,1|-drod81,151,11,1|-drod80,27,10,1|-dfsl81,27,10,1|-dfsl80,151,11,1|-d75h81,151,11,1|-d75h80,27,10,1|-cx0nw1,27,10,1|-cx0nw0,151,11,1|-cro2k1,151,11,1|-cro2k0,27,10,1|-cncfs1,27,10,1|-cncfs0,1,1,0|-cdmfs1,1,1,0|-cdmfs0,27,10,1|-c4md41,27,10,1|-c4md40,1,1,0|-bwc7s1,1,1,0|-bwc7s0,27,10,1|-buwfw1,27,10,1|-buwfw0,151,11,1|-bos2k1,151,11,1|-bos2k0,27,10,1|-bkgfs1,27,10,1|-bkgfs0,1,1,0|-bdm541,1,1,0|-bdm540,27,10,1|-b1qd41,27,10,1|-b1qd40,1,1,0|-att6g1,1,1,0|-att6g0,27,10,1|-aj0ag1,27,10,1|-aj0ag0,1,1,0|-aad6g1,1,1,0|-aad6g0,27,10,1|-a0n6g1,27,10,1|-a0n6g0,1,1,0|-9rn3s1,1,1,0|-9rn3s0,27,10,1|-9hx3s1,27,10,1|-9hx3s0,1,1,0|-98k2g1,1,1,0|-98k2g0,27,10,1|-8yu2g1,27,10,1|-8yu2g0,1,1,0|-8ptzs1,1,1,0|-8ptzs0,27,10,1|-8h6vs1,27,10,1|-8h6vs0,1,1,0|-87gvs1,1,1,0|-87gvs0,27,10,1|-7ygt41,27,10,1|-7ygt40,1,1,0|-7odug1,1,1,0|-7odug0,27,10,1|-7fqqg1,27,10,1|-7fqqg0,1,1,0|-75at41,1,1,0|-75at40,27,10,1|-6wnp41,27,10,1|-6wnp40,1,1,0|-6mxp41,1,1,0|-6mxp40,27,10,1|-6dxmg1,27,10,1|-6dxmg0,1,1,0|-63uns1,1,1,0|-63uns0,27,10,1|-5v7js1,27,10,1|-5v7js0,1,1,0|-5l4l41,1,1,0|-5l4l40,27,10,1|-5chh41,27,10,1|-5chh40,1,1,0|-52rh41,1,1,0|-52rh40,27,10,1|-4treg1,27,10,1|-4treg0,1,1,0|-4krbs1,1,1,0|-4krbs0,27,10,1|-49lh41,27,10,1|-49lh40,1,1,0|-421941,1,1,0|-421940,27,10,1|-3qveg1,27,10,1|-3qveg0,1,1,0|-3iy7s1,1,1,0|-3iy7s0,27,10,1|-385bs1,27,10,1|-385bs0,1,1,0|-30l3s1,1,1,0|-30l3s0,27,10,1|-2pf941,27,10,1|-2pf940,1,1,0|-2hv141,1,1,0|-2hv140,27,10,1|-26p6g1,27,10,1|-26p6g0,1,1,0|-1z4yg1,1,1,0|-1z4yg0,27,10,1|-1nz3s1,27,10,1|-1nz3s0,1,1,0|-1gevs1,1,1,0|-1gevs0,27,10,1|-14w2g1,27,10,1|-14w2g0,1,1,0|-z4ns1,1,1,0|-z4ns0,27,10,1|-m6841,27,10,1|-m6840,27,10,0|yd6vz,27,10,0|yd6w0,1,1,0|15kg7z,1,1,0|15kg80,27,10,1|1h39jz,27,10,1|1h39k0,1,1,0|1oaivz,1,1,0|1oaiw0,27,10,1|1ztc7z,27,10,1|1ztc80,1,1,0|270ljz,1,1,0|270lk0,27,10,1|2ijevz,27,10,1|2ijew0,1,1,0|2pqo7z,1,1,0|2pqo80,27,10,1|319hjz,27,10,1|319hk0,1,1,0|38tpjz,1,1,0|38tpk0,27,10,1|3jzk7z,27,10,1|3jzk80,1,1,0|3rjs7z,1,1,0|3rjs80,27,10,1|42pmvz,27,10,1|42pmw0,1,1,0|4a9uvz,1,1,0|4a9uw0,27,10,1|4lso7z,27,10,1|4lso80,1,1,0|4szxjz,1,1,0|4szxk0,27,10,1|54iqvz,27,10,1|54iqw0,1,1,0|5bq07z,1,1,0|5bq080,27,10,1|5n8tjz,27,10,1|5n8tk0,1,1,0|5v5xfz,1,1,0|5v5xg0,27,10,1|65ytfz,27,10,1|65ytg0,1,1,0|6dw03z,1,1,0|6dw040,27,10,1|6oow3z,27,10,1|6oow40,1,1,0|6wm2rz,1,1,0|6wm2s0,27,10,1|77eyrz,27,10,1|77eys0,1,1,0|7fc5fz,1,1,0|7fc5g0,27,10,1|7qi03z,27,10,1|7qi040,1,1,0|7yf6rz,1,1,0|7yf6s0,27,10,1|8982rz,27,10,1|8982s0,1,1,0|8h59fz,1,1,0|8h59g0,27,10,1|8ry5fz,27,10,1|8ry5g0,1,1,0|8zvc3z,1,1,0|8zvc40,27,10,1|9ao83z,27,10,1|9ao840,1,1,0|9ilerz,1,1,0|9iles0,27,10,1|9tearz,27,10,1|9teas0,1,1,0|a1bhfz,1,1,0|a1bhg0,27,10,1|achc3z,27,10,1|achc40,1,1,0|ak1k3z,1,1,0|ak1k40,27,10,1|av7erz,27,10,1|av7es0,1,1,0|b34lfz,1,1,0|b34lg0,27,10,1|bdxhfz,27,10,1|bdxhg0,1,1,0|bluo3z,1,1,0|bluo40,27,10,1|bwnk3z,27,10,1|bwnk40,1,1,0|c4kqrz,1,1,0|c4kqs0,27,10,1|cfdmrz,27,10,1|cfdms0,1,1,0|cnatfz,1,1,0|cnatg0,27,10,1|cy3pfz,27,10,1|cy3pg0,1,1,0|d60w3z,1,1,0|d60w40,27,10,1|dgts3z,27,10,1|dgts40,1,1,0|dp3xfz,1,1,0|dp3xg0,27,10,1|dzwtfz,27,10,1|dzwtg0,1,1,0|e7u03z,1,1,0|e7u040,27,10,1|eimw3z,27,10,1|eimw40,1,1,0|eqk2rz,1,1,0|eqk2s0,27,10,1|f1cyrz,27,10,1|f1cys0,1,1,0|f9a5fz,1,1,0|f9a5g0,27,10,1|fkg03z,27,10,1|fkg040,1,1,0|fs083z,1,1,0|fs0840,27,10,1|g362rz,27,10,1|g362s0,1,1,0|gaqarz,1,1,0|gaqas0,27,10,1|glw5fz,27,10,1|glw5g0,1,1,0|gttc3z,1,1,0|gttc40,27,10,1|h4m83z,27,10,1|h4m840,1,1,0|hcjerz,1,1,0|hcjes0,27,10,1|hncarz,27,10,1|hncas0,1,1,0|hv9hfz,1,1,0|hv9hg0,27,10,1|i6fc3z,27,10,1|i6fc40,1,1,0|idzk3z,1,1,0|idzk40,27,10,1|ip5erz,27,10,1|ip5es0,1,1,0|iwpmrz,1,1,0|iwpms0,27,10,1|j7vhfz,27,10,1|j7vhg0,1,1,0|jffpfz,1,1,0|jffpg0,27,10,1|jqlk3z,27,10,1|jqlk40,1,1,0|jyiqrz,1,1,0|jyiqs0,27,10,1|k9bmrz,27,10,1|k9bms0,1,1,0|kh8tfz,1,1,0|kh8tg0,27,10,1|ks1pfz,27,10,1|ks1pg0,1,1,0|kzyw3z,1,1,0|kzyw40,27,10,1|lb4qrz,27,10,1|lb4qs0,1,1,0|lioyrz,1,1,0|lioys0,27,10,1|ltutfz,27,10,1|ltutg0,1,1,0|m1f1fz,1,1,0|m1f1g0,27,10,1|mckw3z,27,10,1|mckw40,1,1,0|mki2rz,1,1,0|mki2s0,27,10,1|mvayrz,27,10,1|mvays0,1,1,0|n385fz,1,1,0|n385g0,27,10,1|ne11fz,27,10,1|ne11g0,1,1,0|nly83z,1,1,0|nly840,27,10,1|nwr43z,27,10,1|nwr440,1,1,0|o4oarz,1,1,0|o4oas0,27,10,1|ofu5fz,27,10,1|ofu5g0,1,1,0|onedfz,1,1,0|onedg0,27,10,1|oyk83z,27,10,1|oyk840,1,1,0|p64g3z,1,1,0|p64g40,27,10,1|phaarz,27,10,1|phaas0,1,1,0|pp7hfz,1,1,0|pp7hg0,27,10,1|q00dfz,27,10,1|q00dg0,1,1,0|q7xk3z,1,1,0|q7xk40,27,10,1|qiqg3z,27,10,1|qiqg40,1,1,0|qqnmrz,1,1,0|qqnms0,27,10,1|r1thfz,27,10,1|r1thg0,1,1,0|r9dpfz,1,1,0|r9dpg0,27,10,1|rkjk3z,27,10,1|rkjk40,1,1,0|rs3s3z,1,1,0|rs3s40,27,10,1|s39mrz,27,10,1|s39ms0,1,1,0|sb6tfz,1,1,0|sb6tg0,27,10,1|slzpfz,27,10,1|slzpg0,1,1,0|stww3z,1,1,0|stww40,27,10,1|t4ps3z,27,10,1|t4ps40,1,1,0|tcmyrz,1,1,0|tcmys0,27,10,1|tnfurz,27,10,1|tnfus0,1,1,0|tvd1fz,1,1,0|tvd1g0,27,10,1|u6iw3z,27,10,1|u6iw40,1,1,0|ue343z,1,1,0|ue3440,27,10,1|up8yrz,27,10,1|up8ys0,1,1,0|uwt6rz,1,1,0|uwt6s0,27,10,1|v7z1fz,27,10,1|v7z1g0,1,1,0|vfw83z,1,1,0|vfw840,27,10,1|vqp43z,27,10,1|vqp440,1,1,0|vymarz,1,1,0|vymas0,27,10,1|w9f6rz,27,10,1|w9f6s0,1,1,0|whcdfz,1,1,0|whcdg0,27,10,1|wsi83z,27,10,1|wsi840,1,1,0|x02g3z,1,1,0|x02g40,27,10,1|xb8arz,27,10,1|xb8as0,1,1,0|xisirz,1,1,0|xisis0,27,10,1|xtydfz,27,10,1|xtydg0,1,1,0|y1ilfz,1,1,0|y1ilg0,27,10,1|ycog3z,27,10,1|ycog40,1,1,0|yklmrz,1,1,0|yklms0,27,10,1|yveirz,27,10,1|yveis0,1,1,0|z3bpfz,1,1,0|z3bpg0,27,10,1|ze4lfz,27,10,1|ze4lg0,1,1,0\",\"Europe/Istanbul|,0,349,0|-1ayy814,117,350,0|-ux9xex,117,350,0|-ux9xew,15,11,0|-s0e081,15,11,0|-s0e080,16,6,1|-rsir01,16,6,1|-rsir00,15,11,0|-pyzew1,15,11,0|-pyzew0,16,6,1|-po4r01,16,6,1|-po4r00,15,11,0|-pfwdk1,15,11,0|-pfwdk0,16,6,1|-p6hkc1,16,6,1|-p6hkc0,15,11,0|-oxj9k1,15,11,0|-oxj9k0,16,6,1|-ongdo1,16,6,1|-ongdo0,15,11,0|-ntgo81,15,11,0|-ntgo80,16,6,1|-nm7n01,16,6,1|-nm7n00,15,11,0|-nbayw1,15,11,0|-nbayw0,16,6,1|-n3fpo1,16,6,1|-n3fpo0,15,11,0|-febpk1,15,11,0|-febpk0,16,6,1|-f9c5o1,16,6,1|-f9c5o0,15,11,0|-f6gdk1,15,11,0|-f6gdk0,16,6,1|-erc0c1,16,6,1|-erc0c0,15,11,0|-ehgdk1,15,11,0|-ehgdk0,16,6,1|-cnaz01,16,6,1|-cnaz00,15,11,0|-cb5uw1,15,11,0|-cb5uw0,16,6,1|-c4w0c1,16,6,1|-c4w0c0,15,11,0|-bujpk1,15,11,0|-bujpk0,16,6,1|-blwoc1,16,6,1|-blwoc0,15,11,0|-bbtmw1,15,11,0|-bbtmw0,16,6,1|-b36lo1,16,6,1|-b36lo0,15,11,0|-atgiw1,15,11,0|-atgiw0,16,6,1|-akgj01,16,6,1|-akgj00,15,11,0|-aadhk1,15,11,0|-aadhk0,16,6,1|-a1dho1,16,6,1|-a1dho0,15,11,0|-9rag81,15,11,0|-9rag80,16,6,1|-9inf01,16,6,1|-9inf00,15,11,0|-3wa5k1,15,11,0|-3wa5k0,16,6,1|-3805o1,16,6,1|-3805o0,15,11,0|-2xtew1,15,11,0|-2xtew0,16,6,1|-2qo301,16,6,1|-2qo300,15,11,0|1s8vvz,15,11,0|1s8vw0,16,6,1|2062jz,16,6,1|2062k0,15,11,0|27qdbz,15,11,0|27qdc0,16,6,1|2iw57z,16,6,1|2iw580,15,11,0|2q1mnz,15,11,0|2q1mo0,16,6,1|31m7vz,16,6,1|31m7w0,15,11,0|38tjzz,15,11,0|38tk00,16,6,1|3kcajz,16,6,1|3kcak0,15,11,0|3s9jzz,15,11,0|3s9k00,16,6,1|42cfvz,16,6,1|42cfw0,15,11,0|4azmnz,15,11,0|4azmo0,16,6,1|4ficzz,16,6,1|4fid00,100,6,0|73397z,100,6,0|733980,105,209,1|76bufz,105,209,1|76bug0,100,6,0|7qp97z,100,6,0|7qp980,15,11,0|7zg2jz,15,11,0|7zg2k0,16,6,1|87q7vz,16,6,1|87q7w0,15,11,0|8h53vz,15,11,0|8h53w0,16,6,1|8qi57z,16,6,1|8qi580,15,11,0|8zv6jz,15,11,0|8zv6k0,16,6,1|9987vz,16,6,1|9987w0,15,11,0|9il97z,15,11,0|9il980,16,6,1|9ryajz,16,6,1|9ryak0,15,11,0|a1bbvz,15,11,0|a1bbw0,16,6,1|aaod7z,16,6,1|aaod80,15,11,0|ak1ejz,15,11,0|ak1ek0,16,6,1|atrejz,16,6,1|atrek0,15,11,0|b34fvz,15,11,0|b34fw0,16,6,1|bchh7z,16,6,1|bchh80,15,11,0|bluijz,15,11,0|bluik0,16,6,1|bv7jvz,16,6,1|bv7jw0,15,11,0|c4kl7z,15,11,0|c4kl80,16,6,1|cdxmjz,16,6,1|cdxmk0,15,11,0|cmxp7z,15,11,0|cmxp80,16,6,1|cwnp7z,16,6,1|cwnp80,15,11,0|d60qjz,15,11,0|d60qk0,16,6,1|dfdrvz,16,6,1|dfdrw0,15,11,0|dp3rvz,15,11,0|dp3rw0,16,6,1|dzwnvz,16,6,1|dzwnw0,15,11,0|e7tujz,15,11,0|e7tuk0,16,6,1|eimqjz,16,6,1|eimqk0,15,11,0|eqjx7z,15,11,0|eqjx80,16,6,1|f1ct7z,16,6,1|f1ct80,15,11,0|f99zvz,15,11,0|f99zw0,16,6,1|fkfujz,16,6,1|fkfuk0,15,11,0|fs02jz,15,11,0|fs02k0,16,6,1|g35x7z,16,6,1|g35x80,15,11,0|gaq57z,15,11,0|gaq580,16,6,1|glvzvz,16,6,1|glvzw0,15,11,0|gtt6jz,15,11,0|gtt6k0,16,6,1|h4m2jz,16,6,1|h4m2k0,15,11,0|hcj97z,15,11,0|hcj980,16,6,1|hnc57z,16,6,1|hnc580,15,11,0|hv9bvz,15,11,0|hv9bw0,16,6,1|i6f6jz,16,6,1|i6f6k0,15,11,0|idzejz,15,11,0|idzek0,16,6,1|ip597z,16,6,1|ip5980,15,11,0|iwph7z,15,11,0|iwph80,16,6,1|j7vbvz,16,6,1|j7vbw0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|liqtfz,15,11,0|liqtg0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n3a03z,15,11,0|n3a040,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nxh1fz,16,6,1|nxh1g0,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|od3ozz,16,6,1|od3p00,100,6,0\",\"Europe/Jersey|,0,347,0|-1rprx9x,1,1,0|-rzcns1,1,1,0|-rzcns0,27,10,1|-rsid41,27,10,1|-rsid40,1,1,0|-risd41,1,1,0|-risd40,27,10,1|-ragd41,27,10,1|-ragd40,1,1,0|-r0s7s1,1,1,0|-r0s7s0,27,10,1|-qr0d41,27,10,1|-qr0d40,1,1,0|-qhp6g1,1,1,0|-qhp6g0,27,10,1|-q8aag1,27,10,1|-q8aag0,1,1,0|-pyz3s1,1,1,0|-pyz3s0,27,10,1|-po4d41,27,10,1|-po4d40,1,1,0|-pfw2g1,1,1,0|-pfw2g0,27,10,1|-p6h6g1,27,10,1|-p6h6g0,1,1,0|-oxiyg1,1,1,0|-oxiyg0,27,10,1|-onfzs1,27,10,1|-onfzs0,1,1,0|-odd141,1,1,0|-odd140,27,10,1|-o5st41,27,10,1|-o5st40,1,1,0|-nuzx41,1,1,0|-nuzx40,27,10,1|-nmprs1,27,10,1|-nmprs0,1,1,0|-nbwvs1,1,1,0|-nbwvs0,27,10,1|-n39rs1,27,10,1|-n39rs0,1,1,0|-mt6t41,1,1,0|-mt6t40,27,10,1|-mkjp41,27,10,1|-mkjp40,1,1,0|-matp41,1,1,0|-matp40,27,10,1|-m1tmg1,27,10,1|-m1tmg0,1,1,0|-lrdp41,1,1,0|-lrdp40,27,10,1|-liql41,27,10,1|-liql40,1,1,0|-l8nmg1,1,1,0|-l8nmg0,27,10,1|-l00ig1,27,10,1|-l00ig0,1,1,0|-kqaig1,1,1,0|-kqaig0,27,10,1|-khafs1,27,10,1|-khafs0,1,1,0|-k77h41,1,1,0|-k77h40,27,10,1|-jykd41,27,10,1|-jykd40,1,1,0|-joheg1,1,1,0|-joheg0,27,10,1|-jfuag1,27,10,1|-jfuag0,1,1,0|-j64ag1,1,1,0|-j64ag0,27,10,1|-iwr941,27,10,1|-iwr940,1,1,0|-imoag1,1,1,0|-imoag0,27,10,1|-ie16g1,27,10,1|-ie16g0,1,1,0|-i4b6g1,1,1,0|-i4b6g0,27,10,1|-hvb3s1,27,10,1|-hvb3s0,1,1,0|-hl8541,1,1,0|-hl8540,27,10,1|-hcl141,27,10,1|-hcl140,1,1,0|-h2i2g1,1,1,0|-h2i2g0,27,10,1|-gtuyg1,27,10,1|-gtuyg0,1,1,0|-gk4yg1,1,1,0|-gk4yg0,27,10,1|-gb4vs1,27,10,1|-gb4vs0,1,1,0|-g11x41,1,1,0|-g11x40,27,10,1|-fpw2g1,27,10,1|-fpw2g0,1,1,0|-fkul41,1,1,0|-fkul40,27,10,1|-eyiyk1,27,10,1|-eyiyk0,151,11,1|-ethh81,151,11,1|-ethh80,27,10,1|-eh8qk1,27,10,1|-eh8qk0,151,11,1|-earek1,151,11,1|-earek0,27,10,1|-dyinw1,27,10,1|-dyinw0,151,11,1|-drod81,151,11,1|-drod80,27,10,1|-dfsl81,27,10,1|-dfsl80,151,11,1|-d75h81,151,11,1|-d75h80,27,10,1|-cx0nw1,27,10,1|-cx0nw0,151,11,1|-cro2k1,151,11,1|-cro2k0,27,10,1|-cncfs1,27,10,1|-cncfs0,1,1,0|-cdmfs1,1,1,0|-cdmfs0,27,10,1|-c4md41,27,10,1|-c4md40,1,1,0|-bwc7s1,1,1,0|-bwc7s0,27,10,1|-buwfw1,27,10,1|-buwfw0,151,11,1|-bos2k1,151,11,1|-bos2k0,27,10,1|-bkgfs1,27,10,1|-bkgfs0,1,1,0|-bdm541,1,1,0|-bdm540,27,10,1|-b1qd41,27,10,1|-b1qd40,1,1,0|-att6g1,1,1,0|-att6g0,27,10,1|-aj0ag1,27,10,1|-aj0ag0,1,1,0|-aad6g1,1,1,0|-aad6g0,27,10,1|-a0n6g1,27,10,1|-a0n6g0,1,1,0|-9rn3s1,1,1,0|-9rn3s0,27,10,1|-9hx3s1,27,10,1|-9hx3s0,1,1,0|-98k2g1,1,1,0|-98k2g0,27,10,1|-8yu2g1,27,10,1|-8yu2g0,1,1,0|-8ptzs1,1,1,0|-8ptzs0,27,10,1|-8h6vs1,27,10,1|-8h6vs0,1,1,0|-87gvs1,1,1,0|-87gvs0,27,10,1|-7ygt41,27,10,1|-7ygt40,1,1,0|-7odug1,1,1,0|-7odug0,27,10,1|-7fqqg1,27,10,1|-7fqqg0,1,1,0|-75at41,1,1,0|-75at40,27,10,1|-6wnp41,27,10,1|-6wnp40,1,1,0|-6mxp41,1,1,0|-6mxp40,27,10,1|-6dxmg1,27,10,1|-6dxmg0,1,1,0|-63uns1,1,1,0|-63uns0,27,10,1|-5v7js1,27,10,1|-5v7js0,1,1,0|-5l4l41,1,1,0|-5l4l40,27,10,1|-5chh41,27,10,1|-5chh40,1,1,0|-52rh41,1,1,0|-52rh40,27,10,1|-4treg1,27,10,1|-4treg0,1,1,0|-4krbs1,1,1,0|-4krbs0,27,10,1|-49lh41,27,10,1|-49lh40,1,1,0|-421941,1,1,0|-421940,27,10,1|-3qveg1,27,10,1|-3qveg0,1,1,0|-3iy7s1,1,1,0|-3iy7s0,27,10,1|-385bs1,27,10,1|-385bs0,1,1,0|-30l3s1,1,1,0|-30l3s0,27,10,1|-2pf941,27,10,1|-2pf940,1,1,0|-2hv141,1,1,0|-2hv140,27,10,1|-26p6g1,27,10,1|-26p6g0,1,1,0|-1z4yg1,1,1,0|-1z4yg0,27,10,1|-1nz3s1,27,10,1|-1nz3s0,1,1,0|-1gevs1,1,1,0|-1gevs0,27,10,1|-14w2g1,27,10,1|-14w2g0,1,1,0|-z4ns1,1,1,0|-z4ns0,27,10,1|-m6841,27,10,1|-m6840,27,10,0|yd6vz,27,10,0|yd6w0,1,1,0|15kg7z,1,1,0|15kg80,27,10,1|1h39jz,27,10,1|1h39k0,1,1,0|1oaivz,1,1,0|1oaiw0,27,10,1|1ztc7z,27,10,1|1ztc80,1,1,0|270ljz,1,1,0|270lk0,27,10,1|2ijevz,27,10,1|2ijew0,1,1,0|2pqo7z,1,1,0|2pqo80,27,10,1|319hjz,27,10,1|319hk0,1,1,0|38tpjz,1,1,0|38tpk0,27,10,1|3jzk7z,27,10,1|3jzk80,1,1,0|3rjs7z,1,1,0|3rjs80,27,10,1|42pmvz,27,10,1|42pmw0,1,1,0|4a9uvz,1,1,0|4a9uw0,27,10,1|4lso7z,27,10,1|4lso80,1,1,0|4szxjz,1,1,0|4szxk0,27,10,1|54iqvz,27,10,1|54iqw0,1,1,0|5bq07z,1,1,0|5bq080,27,10,1|5n8tjz,27,10,1|5n8tk0,1,1,0|5v5xfz,1,1,0|5v5xg0,27,10,1|65ytfz,27,10,1|65ytg0,1,1,0|6dw03z,1,1,0|6dw040,27,10,1|6oow3z,27,10,1|6oow40,1,1,0|6wm2rz,1,1,0|6wm2s0,27,10,1|77eyrz,27,10,1|77eys0,1,1,0|7fc5fz,1,1,0|7fc5g0,27,10,1|7qi03z,27,10,1|7qi040,1,1,0|7yf6rz,1,1,0|7yf6s0,27,10,1|8982rz,27,10,1|8982s0,1,1,0|8h59fz,1,1,0|8h59g0,27,10,1|8ry5fz,27,10,1|8ry5g0,1,1,0|8zvc3z,1,1,0|8zvc40,27,10,1|9ao83z,27,10,1|9ao840,1,1,0|9ilerz,1,1,0|9iles0,27,10,1|9tearz,27,10,1|9teas0,1,1,0|a1bhfz,1,1,0|a1bhg0,27,10,1|achc3z,27,10,1|achc40,1,1,0|ak1k3z,1,1,0|ak1k40,27,10,1|av7erz,27,10,1|av7es0,1,1,0|b34lfz,1,1,0|b34lg0,27,10,1|bdxhfz,27,10,1|bdxhg0,1,1,0|bluo3z,1,1,0|bluo40,27,10,1|bwnk3z,27,10,1|bwnk40,1,1,0|c4kqrz,1,1,0|c4kqs0,27,10,1|cfdmrz,27,10,1|cfdms0,1,1,0|cnatfz,1,1,0|cnatg0,27,10,1|cy3pfz,27,10,1|cy3pg0,1,1,0|d60w3z,1,1,0|d60w40,27,10,1|dgts3z,27,10,1|dgts40,1,1,0|dp3xfz,1,1,0|dp3xg0,27,10,1|dzwtfz,27,10,1|dzwtg0,1,1,0|e7u03z,1,1,0|e7u040,27,10,1|eimw3z,27,10,1|eimw40,1,1,0|eqk2rz,1,1,0|eqk2s0,27,10,1|f1cyrz,27,10,1|f1cys0,1,1,0|f9a5fz,1,1,0|f9a5g0,27,10,1|fkg03z,27,10,1|fkg040,1,1,0|fs083z,1,1,0|fs0840,27,10,1|g362rz,27,10,1|g362s0,1,1,0|gaqarz,1,1,0|gaqas0,27,10,1|glw5fz,27,10,1|glw5g0,1,1,0|gttc3z,1,1,0|gttc40,27,10,1|h4m83z,27,10,1|h4m840,1,1,0|hcjerz,1,1,0|hcjes0,27,10,1|hncarz,27,10,1|hncas0,1,1,0|hv9hfz,1,1,0|hv9hg0,27,10,1|i6fc3z,27,10,1|i6fc40,1,1,0|idzk3z,1,1,0|idzk40,27,10,1|ip5erz,27,10,1|ip5es0,1,1,0|iwpmrz,1,1,0|iwpms0,27,10,1|j7vhfz,27,10,1|j7vhg0,1,1,0|jffpfz,1,1,0|jffpg0,27,10,1|jqlk3z,27,10,1|jqlk40,1,1,0|jyiqrz,1,1,0|jyiqs0,27,10,1|k9bmrz,27,10,1|k9bms0,1,1,0|kh8tfz,1,1,0|kh8tg0,27,10,1|ks1pfz,27,10,1|ks1pg0,1,1,0|kzyw3z,1,1,0|kzyw40,27,10,1|lb4qrz,27,10,1|lb4qs0,1,1,0|lioyrz,1,1,0|lioys0,27,10,1|ltutfz,27,10,1|ltutg0,1,1,0|m1f1fz,1,1,0|m1f1g0,27,10,1|mckw3z,27,10,1|mckw40,1,1,0|mki2rz,1,1,0|mki2s0,27,10,1|mvayrz,27,10,1|mvays0,1,1,0|n385fz,1,1,0|n385g0,27,10,1|ne11fz,27,10,1|ne11g0,1,1,0|nly83z,1,1,0|nly840,27,10,1|nwr43z,27,10,1|nwr440,1,1,0|o4oarz,1,1,0|o4oas0,27,10,1|ofu5fz,27,10,1|ofu5g0,1,1,0|onedfz,1,1,0|onedg0,27,10,1|oyk83z,27,10,1|oyk840,1,1,0|p64g3z,1,1,0|p64g40,27,10,1|phaarz,27,10,1|phaas0,1,1,0|pp7hfz,1,1,0|pp7hg0,27,10,1|q00dfz,27,10,1|q00dg0,1,1,0|q7xk3z,1,1,0|q7xk40,27,10,1|qiqg3z,27,10,1|qiqg40,1,1,0|qqnmrz,1,1,0|qqnms0,27,10,1|r1thfz,27,10,1|r1thg0,1,1,0|r9dpfz,1,1,0|r9dpg0,27,10,1|rkjk3z,27,10,1|rkjk40,1,1,0|rs3s3z,1,1,0|rs3s40,27,10,1|s39mrz,27,10,1|s39ms0,1,1,0|sb6tfz,1,1,0|sb6tg0,27,10,1|slzpfz,27,10,1|slzpg0,1,1,0|stww3z,1,1,0|stww40,27,10,1|t4ps3z,27,10,1|t4ps40,1,1,0|tcmyrz,1,1,0|tcmys0,27,10,1|tnfurz,27,10,1|tnfus0,1,1,0|tvd1fz,1,1,0|tvd1g0,27,10,1|u6iw3z,27,10,1|u6iw40,1,1,0|ue343z,1,1,0|ue3440,27,10,1|up8yrz,27,10,1|up8ys0,1,1,0|uwt6rz,1,1,0|uwt6s0,27,10,1|v7z1fz,27,10,1|v7z1g0,1,1,0|vfw83z,1,1,0|vfw840,27,10,1|vqp43z,27,10,1|vqp440,1,1,0|vymarz,1,1,0|vymas0,27,10,1|w9f6rz,27,10,1|w9f6s0,1,1,0|whcdfz,1,1,0|whcdg0,27,10,1|wsi83z,27,10,1|wsi840,1,1,0|x02g3z,1,1,0|x02g40,27,10,1|xb8arz,27,10,1|xb8as0,1,1,0|xisirz,1,1,0|xisis0,27,10,1|xtydfz,27,10,1|xtydg0,1,1,0|y1ilfz,1,1,0|y1ilg0,27,10,1|ycog3z,27,10,1|ycog40,1,1,0|yklmrz,1,1,0|yklms0,27,10,1|yveirz,27,10,1|yveis0,1,1,0|z3bpfz,1,1,0|z3bpg0,27,10,1|ze4lfz,27,10,1|ze4lg0,1,1,0\",\"Europe/Kaliningrad|,0,332,0|-14212go,10,10,0|-s0e081,10,10,0|-s0e080,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|-ridmk1,10,10,0|-ridmk0,11,11,1|-ragfw1,11,11,1|-ragfw0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,10,10,0|-fizzw1,10,10,0|-fizzw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cwm2w1,11,11,1|-cwm2w0,15,11,0|-cvmw81,15,11,0|-cvmw80,16,6,1|-cm2j01,16,6,1|-cm2j00,15,11,0|-cdzpk1,15,11,0|-cdzpk0,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,16,6,1|aaofzz,16,6,1|aaog00,15,11,0|ak1hbz,15,11,0|ak1hc0,16,6,1|atrhbz,16,6,1|atrhc0,15,11,0|b34inz,15,11,0|b34io0,16,6,1|bchjzz,16,6,1|bchk00,15,11,0|blulbz,15,11,0|blulc0,16,6,1|bv7mnz,16,6,1|bv7mo0,15,11,0|c4knzz,15,11,0|c4ko00,16,6,1|cdxpbz,16,6,1|cdxpc0,15,11,0|cnaqnz,15,11,0|cnaqo0,16,6,1|cwnrzz,16,6,1|cwns00,15,11,0|d60tbz,15,11,0|d60tc0,16,6,1|dfdunz,16,6,1|dfduo0,15,11,0|dp3unz,15,11,0|dp3uo0,16,6,1|dzwqnz,16,6,1|dzwqo0,15,11,0|e7txbz,15,11,0|e7txc0,16,6,1|eimtbz,16,6,1|eimtc0,15,11,0|eqjzzz,15,11,0|eqk000,16,6,1|f1cvzz,16,6,1|f1cw00,15,11,0|f9a2nz,15,11,0|f9a2o0,16,6,1|fkfxbz,16,6,1|fkfxc0,15,11,0|fs05bz,15,11,0|fs05c0,16,6,1|g35zzz,16,6,1|g36000,15,11,0|gaq7zz,15,11,0|gaq800,16,6,1|glw2nz,16,6,1|glw2o0,15,11,0|gtt9bz,15,11,0|gtt9c0,16,6,1|h4m5bz,16,6,1|h4m5c0,15,11,0|hcjbzz,15,11,0|hcjc00,16,6,1|hnc7zz,16,6,1|hnc800,15,11,0|hv9enz,15,11,0|hv9eo0,16,6,1|i6f9bz,16,6,1|i6f9c0,15,11,0|idzhbz,15,11,0|idzhc0,16,6,1|ip5bzz,16,6,1|ip5c00,15,11,0|iwpjzz,15,11,0|iwpk00,16,6,1|j7venz,16,6,1|j7veo0,15,11,0|jffmnz,15,11,0|jffmo0,16,6,1|jqlhbz,16,6,1|jqlhc0,15,11,0|jyinzz,15,11,0|jyio00,16,6,1|k9bjzz,16,6,1|k9bk00,15,11,0|kh8qnz,15,11,0|kh8qo0,16,6,1|ks1mnz,16,6,1|ks1mo0,15,11,0|kzytbz,15,11,0|kzytc0,16,6,1|lb4nzz,16,6,1|lb4o00,15,11,0|liovzz,15,11,0|liow00,100,6,0|ne0vvz,100,6,0|ne0vw0,15,11,0\",\"Europe/Kiev|,0,351,0|-1ayy8bg,74,351,0|-nu11nh,74,351,0|-nu11ng,15,11,0|-kmr1k1,15,11,0|-kmr1k0,148,6,0|-erdv01,148,6,0|-erdv00,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dnetg1,10,10,0|-dnetg0,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,149,209,1|aaod7z,149,209,1|aaod80,148,6,0|ak1ejz,148,6,0|ak1ek0,149,209,1|ap2t3z,149,209,1|ap2t40,16,6,1|bchjzz,16,6,1|bchk00,15,11,0|blufrz,15,11,0|blufs0,16,6,1|bv7ebz,16,6,1|bv7ec0,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxgzz,16,6,1|cdxh00,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cwnjnz,16,6,1|cwnjo0,15,11,0|d60w3z,15,11,0|d60w40,16,6,1|dfdxfz,16,6,1|dfdxg0,15,11,0|dp3xfz,15,11,0|dp3xg0,16,6,1|dzwtfz,16,6,1|dzwtg0,15,11,0|e7u03z,15,11,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Kirov|,0,352,0|-qcx400,100,6,0|-kmr4c1,100,6,0|-kmr4c0,105,209,0|5vb3jz,105,209,0|5vb3k0,92,194,1|64pwrz,92,194,1|64pws0,105,209,0|6e30vz,105,209,0|6e30w0,92,194,1|6nhu3z,92,194,1|6nhu40,105,209,0|6wuy7z,105,209,0|6wuy80,92,194,1|769rfz,92,194,1|769rg0,105,209,0|7foq7z,105,209,0|7foq80,92,194,1|7p1x3z,92,194,1|7p1x40,105,209,0|7yeyfz,105,209,0|7yeyg0,92,194,1|87rzrz,92,194,1|87rzs0,105,209,0|8h513z,105,209,0|8h5140,92,194,1|8qi2fz,92,194,1|8qi2g0,105,209,0|8zv3rz,105,209,0|8zv3s0,92,194,1|99853z,92,194,1|998540,105,209,0|9il6fz,105,209,0|9il6g0,92,194,1|9ry7rz,92,194,1|9ry7s0,105,209,0|a1b93z,105,209,0|a1b940,105,209,1|aaod7z,105,209,1|aaod80,100,6,0|ak1ejz,100,6,0|ak1ek0,105,209,1|atrejz,105,209,1|atrek0,100,6,0|b34fvz,100,6,0|b34fw0,105,209,0|blufrz,105,209,0|blufs0,105,209,1|bv7jvz,105,209,1|bv7jw0,100,6,0|c4kl7z,100,6,0|c4kl80,105,209,1|cdxmjz,105,209,1|cdxmk0,100,6,0|cnanvz,100,6,0|cnanw0,105,209,1|cwnp7z,105,209,1|cwnp80,100,6,0|d60qjz,100,6,0|d60qk0,105,209,1|dfdrvz,105,209,1|dfdrw0,100,6,0|dp3rvz,100,6,0|dp3rw0,105,209,1|dzwnvz,105,209,1|dzwnw0,100,6,0|e7tujz,100,6,0|e7tuk0,105,209,1|eimqjz,105,209,1|eimqk0,100,6,0|eqjx7z,100,6,0|eqjx80,105,209,1|f1ct7z,105,209,1|f1ct80,100,6,0|f99zvz,100,6,0|f99zw0,105,209,1|fkfujz,105,209,1|fkfuk0,100,6,0|fs02jz,100,6,0|fs02k0,105,209,1|g35x7z,105,209,1|g35x80,100,6,0|gaq57z,100,6,0|gaq580,105,209,1|glvzvz,105,209,1|glvzw0,100,6,0|gtt6jz,100,6,0|gtt6k0,105,209,1|h4m2jz,105,209,1|h4m2k0,100,6,0|hcj97z,100,6,0|hcj980,105,209,1|hnc57z,105,209,1|hnc580,100,6,0|hv9bvz,100,6,0|hv9bw0,105,209,1|i6f6jz,105,209,1|i6f6k0,100,6,0|idzejz,100,6,0|idzek0,105,209,1|ip597z,105,209,1|ip5980,100,6,0|iwph7z,100,6,0|iwph80,105,209,1|j7vbvz,105,209,1|j7vbw0,100,6,0|jffjvz,100,6,0|jffjw0,105,209,1|jqlejz,105,209,1|jqlek0,100,6,0|jyil7z,100,6,0|jyil80,105,209,1|k9bh7z,105,209,1|k9bh80,100,6,0|kh8nvz,100,6,0|kh8nw0,105,209,1|ks1jvz,105,209,1|ks1jw0,100,6,0|kzyqjz,100,6,0|kzyqk0,105,209,1|lb4l7z,105,209,1|lb4l80,100,6,0|liot7z,100,6,0|liot80,105,209,0|ne0t3z,105,209,0|ne0t40,100,6,0\",\"Europe/Lisbon|,0,29,0|-u9rhc0,8,1,0|-rxx1g1,8,1,0|-rxx1g0,9,10,1|-rqx401,9,10,1|-rqx400,8,1,0|-rkqys1,8,1,0|-rkqys0,9,10,1|-r90qs1,9,10,1|-r90qs0,8,1,0|-r1x6s1,8,1,0|-r1x6s0,9,10,1|-qq8tg1,9,10,1|-qq8tg0,8,1,0|-qj7441,8,1,0|-qj7440,9,10,1|-q7gw41,9,10,1|-q7gw40,8,1,0|-q0dc41,8,1,0|-q0dc40,9,10,1|-pon441,9,10,1|-pon440,8,1,0|-phles1,8,1,0|-phles0,9,10,1|-p5v6s1,9,10,1|-p5v6s0,8,1,0|-nusqs1,8,1,0|-nusqs0,9,10,1|-nlhk41,9,10,1|-nlhk40,8,1,0|-mt71g1,8,1,0|-mt71g0,9,10,1|-mkjxg1,9,10,1|-mkjxg0,8,1,0|-matxg1,8,1,0|-matxg0,9,10,1|-m1tus1,9,10,1|-m1tus0,8,1,0|-lrqw41,8,1,0|-lrqw40,9,10,1|-liqtg1,9,10,1|-liqtg0,8,1,0|-l8nus1,8,1,0|-l8nus0,9,10,1|-l00qs1,9,10,1|-l00qs0,8,1,0|-k77pg1,8,1,0|-k77pg0,9,10,1|-jyklg1,9,10,1|-jyklg0,8,1,0|-jp7k41,8,1,0|-jp7k40,9,10,1|-jfuis1,9,10,1|-jfuis0,8,1,0|-ineg41,8,1,0|-ineg40,9,10,1|-ie1es1,9,10,1|-ie1es0,8,1,0|-i51c41,8,1,0|-i51c40,9,10,1|-hvbc41,9,10,1|-hvbc40,8,1,0|-hl8dg1,8,1,0|-hl8dg0,9,10,1|-hcl9g1,9,10,1|-hcl9g0,8,1,0|-h38841,8,1,0|-h38840,9,10,1|-gtv6s1,9,10,1|-gtv6s0,8,1,0|-gkv441,8,1,0|-gkv440,9,10,1|-gb5441,9,10,1|-gb5440,8,1,0|-g125g1,8,1,0|-g125g0,9,10,1|-fpwas1,9,10,1|-fpwas0,8,1,0|-fkutg1,8,1,0|-fkutg0,9,10,1|-f9c041,9,10,1|-f9c040,8,1,0|-ezyys1,8,1,0|-ezyys0,9,10,1|-eqk2s1,9,10,1|-eqk2s0,8,1,0|-eibs41,8,1,0|-eibs40,9,10,1|-eg62w1,9,10,1|-eg62w0,152,11,1|-eaeo81,152,11,1|-eaeo80,9,10,1|-e6sys1,9,10,1|-e6sys0,8,1,0|-dzlpg1,8,1,0|-dzlpg0,9,10,1|-dxsyw1,9,10,1|-dxsyw0,152,11,1|-dqyo81,152,11,1|-dqyo80,9,10,1|-dnpxg1,9,10,1|-dnpxg0,8,1,0|-dgvms1,8,1,0|-dgvms0,9,10,1|-depxk1,9,10,1|-depxk0,152,11,1|-d88lk1,152,11,1|-d88lk0,9,10,1|-d4zus1,9,10,1|-d4zus0,8,1,0|-cy5k41,8,1,0|-cy5k40,9,10,1|-cvzuw1,9,10,1|-cvzuw0,152,11,1|-cpiiw1,152,11,1|-cpiiw0,9,10,1|-cm9s41,9,10,1|-cm9s40,8,1,0|-cdzms1,8,1,0|-cdzms0,9,10,1|-c4mlg1,9,10,1|-c4mlg0,8,1,0|-bv9bs1,8,1,0|-bv9bs0,9,10,1|-blwag1,9,10,1|-blwag0,8,1,0|-bcj941,8,1,0|-bcj940,9,10,1|-b367s1,9,10,1|-b367s0,8,1,0|-att6g1,8,1,0|-att6g0,9,10,1|-akg541,9,10,1|-akg540,8,1,0|-9sd141,8,1,0|-9sd140,9,10,1|-9in141,9,10,1|-9in140,8,1,0|-999zs1,8,1,0|-999zs0,9,10,1|-8zwyg1,9,10,1|-8zwyg0,8,1,0|-8qjx41,8,1,0|-8qjx40,9,10,1|-8h6vs1,9,10,1|-8h6vs0,8,1,0|-87tug1,8,1,0|-87tug0,9,10,1|-7ygt41,9,10,1|-7ygt40,8,1,0|-7p3rs1,8,1,0|-7p3rs0,9,10,1|-7fqqg1,9,10,1|-7fqqg0,8,1,0|-76dp41,8,1,0|-76dp40,9,10,1|-6wnp41,9,10,1|-6wnp40,8,1,0|-6nans1,8,1,0|-6nans0,9,10,1|-6dxmg1,9,10,1|-6dxmg0,8,1,0|-64kl41,8,1,0|-64kl40,9,10,1|-5v7js1,9,10,1|-5v7js0,8,1,0|-5luig1,8,1,0|-5luig0,9,10,1|-5chh41,9,10,1|-5chh40,8,1,0|-534fs1,8,1,0|-534fs0,9,10,1|-4treg1,9,10,1|-4treg0,8,1,0|-4ked41,8,1,0|-4ked40,9,10,1|-4b1bs1,9,10,1|-4b1bs0,8,1,0|-41oag1,8,1,0|-41oag0,9,10,1|-3ryag1,9,10,1|-3ryag0,8,1,0|-3il941,8,1,0|-3il940,9,10,1|-3987s1,9,10,1|-3987s0,8,1,0|-2zv6g1,8,1,0|-2zv6g0,9,10,1|-2qi541,9,10,1|-2qi540,8,1,0|-2h53s1,8,1,0|-2h53s0,9,10,1|-27s2g1,9,10,1|-27s2g0,8,1,0|-1yf141,8,1,0|-1yf140,10,10,0|3ijjzz,10,10,0|3ijk00,8,1,0|3rwlbz,8,1,0|3rwlc0,9,10,1|419mnz,9,10,1|419mo0,8,1,0|4azmnz,8,1,0|4azmo0,9,10,1|4kcnzz,9,10,1|4kco00,8,1,0|4tppbz,8,1,0|4tppc0,9,10,1|532tfz,9,10,1|532tg0,8,1,0|5cfrzz,8,1,0|5cfs00,9,10,1|5lsw3z,9,10,1|5lsw40,8,1,0|5v5xfz,8,1,0|5v5xg0,9,10,1|64iyrz,9,10,1|64iys0,8,1,0|6dw03z,8,1,0|6dw040,9,10,1|6n91fz,9,10,1|6n91g0,8,1,0|6wm5jz,8,1,0|6wm5k0,9,10,1|75z43z,9,10,1|75z440,8,1,0|7fc5fz,8,1,0|7fc5g0,9,10,1|7p25fz,9,10,1|7p25g0,8,1,0|7yf6rz,8,1,0|7yf6s0,9,10,1|87s83z,9,10,1|87s840,8,1,0|8h59fz,8,1,0|8h59g0,9,10,1|8qiarz,9,10,1|8qias0,8,1,0|8zvc3z,8,1,0|8zvc40,9,10,1|998dfz,9,10,1|998dg0,8,1,0|9ilerz,8,1,0|9iles0,9,10,1|9ryg3z,9,10,1|9ryg40,8,1,0|a1bhfz,8,1,0|a1bhg0,9,10,1|aaoirz,9,10,1|aaois0,8,1,0|ak1k3z,8,1,0|ak1k40,9,10,1|atrk3z,9,10,1|atrk40,8,1,0|b34lfz,8,1,0|b34lg0,9,10,1|bchmrz,9,10,1|bchms0,8,1,0|bluo3z,8,1,0|bluo40,9,10,1|bv7pfz,9,10,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,9,10,1|dzwtfz,9,10,1|dzwtg0,8,1,0|e7u03z,8,1,0|e7u040,9,10,1|eimw3z,9,10,1|eimw40,8,1,0|eqk2rz,8,1,0|eqk2s0,9,10,1|f1cyrz,9,10,1|f1cys0,8,1,0|f9a5fz,8,1,0|f9a5g0,9,10,1|fkg03z,9,10,1|fkg040,8,1,0|fs083z,8,1,0|fs0840,9,10,1|g362rz,9,10,1|g362s0,8,1,0|gaqarz,8,1,0|gaqas0,9,10,1|glw5fz,9,10,1|glw5g0,8,1,0|gttc3z,8,1,0|gttc40,9,10,1|h4m83z,9,10,1|h4m840,8,1,0|hcjerz,8,1,0|hcjes0,9,10,1|hncarz,9,10,1|hncas0,8,1,0|hv9hfz,8,1,0|hv9hg0,9,10,1|i6fc3z,9,10,1|i6fc40,8,1,0|idzk3z,8,1,0|idzk40,9,10,1|ip5erz,9,10,1|ip5es0,8,1,0|iwpmrz,8,1,0|iwpms0,9,10,1|j7vhfz,9,10,1|j7vhg0,8,1,0|jffpfz,8,1,0|jffpg0,9,10,1|jqlk3z,9,10,1|jqlk40,8,1,0|jyiqrz,8,1,0|jyiqs0,9,10,1|k9bmrz,9,10,1|k9bms0,8,1,0|kh8tfz,8,1,0|kh8tg0,9,10,1|ks1pfz,9,10,1|ks1pg0,8,1,0|kzyw3z,8,1,0|kzyw40,9,10,1|lb4qrz,9,10,1|lb4qs0,8,1,0|lioyrz,8,1,0|lioys0,9,10,1|ltutfz,9,10,1|ltutg0,8,1,0|m1f1fz,8,1,0|m1f1g0,9,10,1|mckw3z,9,10,1|mckw40,8,1,0|mki2rz,8,1,0|mki2s0,9,10,1|mvayrz,9,10,1|mvays0,8,1,0|n385fz,8,1,0|n385g0,9,10,1|ne11fz,9,10,1|ne11g0,8,1,0|nly83z,8,1,0|nly840,9,10,1|nwr43z,9,10,1|nwr440,8,1,0|o4oarz,8,1,0|o4oas0,9,10,1|ofu5fz,9,10,1|ofu5g0,8,1,0|onedfz,8,1,0|onedg0,9,10,1|oyk83z,9,10,1|oyk840,8,1,0|p64g3z,8,1,0|p64g40,9,10,1|phaarz,9,10,1|phaas0,8,1,0|pp7hfz,8,1,0|pp7hg0,9,10,1|q00dfz,9,10,1|q00dg0,8,1,0|q7xk3z,8,1,0|q7xk40,9,10,1|qiqg3z,9,10,1|qiqg40,8,1,0|qqnmrz,8,1,0|qqnms0,9,10,1|r1thfz,9,10,1|r1thg0,8,1,0|r9dpfz,8,1,0|r9dpg0,9,10,1|rkjk3z,9,10,1|rkjk40,8,1,0|rs3s3z,8,1,0|rs3s40,9,10,1|s39mrz,9,10,1|s39ms0,8,1,0|sb6tfz,8,1,0|sb6tg0,9,10,1|slzpfz,9,10,1|slzpg0,8,1,0|stww3z,8,1,0|stww40,9,10,1|t4ps3z,9,10,1|t4ps40,8,1,0|tcmyrz,8,1,0|tcmys0,9,10,1|tnfurz,9,10,1|tnfus0,8,1,0|tvd1fz,8,1,0|tvd1g0,9,10,1|u6iw3z,9,10,1|u6iw40,8,1,0|ue343z,8,1,0|ue3440,9,10,1|up8yrz,9,10,1|up8ys0,8,1,0|uwt6rz,8,1,0|uwt6s0,9,10,1|v7z1fz,9,10,1|v7z1g0,8,1,0|vfw83z,8,1,0|vfw840,9,10,1|vqp43z,9,10,1|vqp440,8,1,0|vymarz,8,1,0|vymas0,9,10,1|w9f6rz,9,10,1|w9f6s0,8,1,0|whcdfz,8,1,0|whcdg0,9,10,1|wsi83z,9,10,1|wsi840,8,1,0|x02g3z,8,1,0|x02g40,9,10,1|xb8arz,9,10,1|xb8as0,8,1,0|xisirz,8,1,0|xisis0,9,10,1|xtydfz,9,10,1|xtydg0,8,1,0|y1ilfz,8,1,0|y1ilg0,9,10,1|ycog3z,9,10,1|ycog40,8,1,0|yklmrz,8,1,0|yklms0,9,10,1|yveirz,9,10,1|yveis0,8,1,0|z3bpfz,8,1,0|z3bpg0,9,10,1|ze4lfz,9,10,1|ze4lg0,8,1,0\",\"Europe/Ljubljana|,0,332,0|-18vsmgo,10,10,0|-ezayw1,10,10,0|-ezayw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cv5zw1,10,10,0|-cv5zw0,11,11,1|-cofek1,11,11,1|-cofek0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/London|,0,347,0|-1rprx9x,1,1,0|-rzcns1,1,1,0|-rzcns0,27,10,1|-rsid41,27,10,1|-rsid40,1,1,0|-risd41,1,1,0|-risd40,27,10,1|-ragd41,27,10,1|-ragd40,1,1,0|-r0s7s1,1,1,0|-r0s7s0,27,10,1|-qr0d41,27,10,1|-qr0d40,1,1,0|-qhp6g1,1,1,0|-qhp6g0,27,10,1|-q8aag1,27,10,1|-q8aag0,1,1,0|-pyz3s1,1,1,0|-pyz3s0,27,10,1|-po4d41,27,10,1|-po4d40,1,1,0|-pfw2g1,1,1,0|-pfw2g0,27,10,1|-p6h6g1,27,10,1|-p6h6g0,1,1,0|-oxiyg1,1,1,0|-oxiyg0,27,10,1|-onfzs1,27,10,1|-onfzs0,1,1,0|-odd141,1,1,0|-odd140,27,10,1|-o5st41,27,10,1|-o5st40,1,1,0|-nuzx41,1,1,0|-nuzx40,27,10,1|-nmprs1,27,10,1|-nmprs0,1,1,0|-nbwvs1,1,1,0|-nbwvs0,27,10,1|-n39rs1,27,10,1|-n39rs0,1,1,0|-mt6t41,1,1,0|-mt6t40,27,10,1|-mkjp41,27,10,1|-mkjp40,1,1,0|-matp41,1,1,0|-matp40,27,10,1|-m1tmg1,27,10,1|-m1tmg0,1,1,0|-lrdp41,1,1,0|-lrdp40,27,10,1|-liql41,27,10,1|-liql40,1,1,0|-l8nmg1,1,1,0|-l8nmg0,27,10,1|-l00ig1,27,10,1|-l00ig0,1,1,0|-kqaig1,1,1,0|-kqaig0,27,10,1|-khafs1,27,10,1|-khafs0,1,1,0|-k77h41,1,1,0|-k77h40,27,10,1|-jykd41,27,10,1|-jykd40,1,1,0|-joheg1,1,1,0|-joheg0,27,10,1|-jfuag1,27,10,1|-jfuag0,1,1,0|-j64ag1,1,1,0|-j64ag0,27,10,1|-iwr941,27,10,1|-iwr940,1,1,0|-imoag1,1,1,0|-imoag0,27,10,1|-ie16g1,27,10,1|-ie16g0,1,1,0|-i4b6g1,1,1,0|-i4b6g0,27,10,1|-hvb3s1,27,10,1|-hvb3s0,1,1,0|-hl8541,1,1,0|-hl8540,27,10,1|-hcl141,27,10,1|-hcl140,1,1,0|-h2i2g1,1,1,0|-h2i2g0,27,10,1|-gtuyg1,27,10,1|-gtuyg0,1,1,0|-gk4yg1,1,1,0|-gk4yg0,27,10,1|-gb4vs1,27,10,1|-gb4vs0,1,1,0|-g11x41,1,1,0|-g11x40,27,10,1|-fpw2g1,27,10,1|-fpw2g0,1,1,0|-fkul41,1,1,0|-fkul40,27,10,1|-eyiyk1,27,10,1|-eyiyk0,151,11,1|-ethh81,151,11,1|-ethh80,27,10,1|-eh8qk1,27,10,1|-eh8qk0,151,11,1|-earek1,151,11,1|-earek0,27,10,1|-dyinw1,27,10,1|-dyinw0,151,11,1|-drod81,151,11,1|-drod80,27,10,1|-dfsl81,27,10,1|-dfsl80,151,11,1|-d75h81,151,11,1|-d75h80,27,10,1|-cx0nw1,27,10,1|-cx0nw0,151,11,1|-cro2k1,151,11,1|-cro2k0,27,10,1|-cncfs1,27,10,1|-cncfs0,1,1,0|-cdmfs1,1,1,0|-cdmfs0,27,10,1|-c4md41,27,10,1|-c4md40,1,1,0|-bwc7s1,1,1,0|-bwc7s0,27,10,1|-buwfw1,27,10,1|-buwfw0,151,11,1|-bos2k1,151,11,1|-bos2k0,27,10,1|-bkgfs1,27,10,1|-bkgfs0,1,1,0|-bdm541,1,1,0|-bdm540,27,10,1|-b1qd41,27,10,1|-b1qd40,1,1,0|-att6g1,1,1,0|-att6g0,27,10,1|-aj0ag1,27,10,1|-aj0ag0,1,1,0|-aad6g1,1,1,0|-aad6g0,27,10,1|-a0n6g1,27,10,1|-a0n6g0,1,1,0|-9rn3s1,1,1,0|-9rn3s0,27,10,1|-9hx3s1,27,10,1|-9hx3s0,1,1,0|-98k2g1,1,1,0|-98k2g0,27,10,1|-8yu2g1,27,10,1|-8yu2g0,1,1,0|-8ptzs1,1,1,0|-8ptzs0,27,10,1|-8h6vs1,27,10,1|-8h6vs0,1,1,0|-87gvs1,1,1,0|-87gvs0,27,10,1|-7ygt41,27,10,1|-7ygt40,1,1,0|-7odug1,1,1,0|-7odug0,27,10,1|-7fqqg1,27,10,1|-7fqqg0,1,1,0|-75at41,1,1,0|-75at40,27,10,1|-6wnp41,27,10,1|-6wnp40,1,1,0|-6mxp41,1,1,0|-6mxp40,27,10,1|-6dxmg1,27,10,1|-6dxmg0,1,1,0|-63uns1,1,1,0|-63uns0,27,10,1|-5v7js1,27,10,1|-5v7js0,1,1,0|-5l4l41,1,1,0|-5l4l40,27,10,1|-5chh41,27,10,1|-5chh40,1,1,0|-52rh41,1,1,0|-52rh40,27,10,1|-4treg1,27,10,1|-4treg0,1,1,0|-4krbs1,1,1,0|-4krbs0,27,10,1|-49lh41,27,10,1|-49lh40,1,1,0|-421941,1,1,0|-421940,27,10,1|-3qveg1,27,10,1|-3qveg0,1,1,0|-3iy7s1,1,1,0|-3iy7s0,27,10,1|-385bs1,27,10,1|-385bs0,1,1,0|-30l3s1,1,1,0|-30l3s0,27,10,1|-2pf941,27,10,1|-2pf940,1,1,0|-2hv141,1,1,0|-2hv140,27,10,1|-26p6g1,27,10,1|-26p6g0,1,1,0|-1z4yg1,1,1,0|-1z4yg0,27,10,1|-1nz3s1,27,10,1|-1nz3s0,1,1,0|-1gevs1,1,1,0|-1gevs0,27,10,1|-14w2g1,27,10,1|-14w2g0,1,1,0|-z4ns1,1,1,0|-z4ns0,27,10,1|-m6841,27,10,1|-m6840,27,10,0|yd6vz,27,10,0|yd6w0,1,1,0|15kg7z,1,1,0|15kg80,27,10,1|1h39jz,27,10,1|1h39k0,1,1,0|1oaivz,1,1,0|1oaiw0,27,10,1|1ztc7z,27,10,1|1ztc80,1,1,0|270ljz,1,1,0|270lk0,27,10,1|2ijevz,27,10,1|2ijew0,1,1,0|2pqo7z,1,1,0|2pqo80,27,10,1|319hjz,27,10,1|319hk0,1,1,0|38tpjz,1,1,0|38tpk0,27,10,1|3jzk7z,27,10,1|3jzk80,1,1,0|3rjs7z,1,1,0|3rjs80,27,10,1|42pmvz,27,10,1|42pmw0,1,1,0|4a9uvz,1,1,0|4a9uw0,27,10,1|4lso7z,27,10,1|4lso80,1,1,0|4szxjz,1,1,0|4szxk0,27,10,1|54iqvz,27,10,1|54iqw0,1,1,0|5bq07z,1,1,0|5bq080,27,10,1|5n8tjz,27,10,1|5n8tk0,1,1,0|5v5xfz,1,1,0|5v5xg0,27,10,1|65ytfz,27,10,1|65ytg0,1,1,0|6dw03z,1,1,0|6dw040,27,10,1|6oow3z,27,10,1|6oow40,1,1,0|6wm2rz,1,1,0|6wm2s0,27,10,1|77eyrz,27,10,1|77eys0,1,1,0|7fc5fz,1,1,0|7fc5g0,27,10,1|7qi03z,27,10,1|7qi040,1,1,0|7yf6rz,1,1,0|7yf6s0,27,10,1|8982rz,27,10,1|8982s0,1,1,0|8h59fz,1,1,0|8h59g0,27,10,1|8ry5fz,27,10,1|8ry5g0,1,1,0|8zvc3z,1,1,0|8zvc40,27,10,1|9ao83z,27,10,1|9ao840,1,1,0|9ilerz,1,1,0|9iles0,27,10,1|9tearz,27,10,1|9teas0,1,1,0|a1bhfz,1,1,0|a1bhg0,27,10,1|achc3z,27,10,1|achc40,1,1,0|ak1k3z,1,1,0|ak1k40,27,10,1|av7erz,27,10,1|av7es0,1,1,0|b34lfz,1,1,0|b34lg0,27,10,1|bdxhfz,27,10,1|bdxhg0,1,1,0|bluo3z,1,1,0|bluo40,27,10,1|bwnk3z,27,10,1|bwnk40,1,1,0|c4kqrz,1,1,0|c4kqs0,27,10,1|cfdmrz,27,10,1|cfdms0,1,1,0|cnatfz,1,1,0|cnatg0,27,10,1|cy3pfz,27,10,1|cy3pg0,1,1,0|d60w3z,1,1,0|d60w40,27,10,1|dgts3z,27,10,1|dgts40,1,1,0|dp3xfz,1,1,0|dp3xg0,27,10,1|dzwtfz,27,10,1|dzwtg0,1,1,0|e7u03z,1,1,0|e7u040,27,10,1|eimw3z,27,10,1|eimw40,1,1,0|eqk2rz,1,1,0|eqk2s0,27,10,1|f1cyrz,27,10,1|f1cys0,1,1,0|f9a5fz,1,1,0|f9a5g0,27,10,1|fkg03z,27,10,1|fkg040,1,1,0|fs083z,1,1,0|fs0840,27,10,1|g362rz,27,10,1|g362s0,1,1,0|gaqarz,1,1,0|gaqas0,27,10,1|glw5fz,27,10,1|glw5g0,1,1,0|gttc3z,1,1,0|gttc40,27,10,1|h4m83z,27,10,1|h4m840,1,1,0|hcjerz,1,1,0|hcjes0,27,10,1|hncarz,27,10,1|hncas0,1,1,0|hv9hfz,1,1,0|hv9hg0,27,10,1|i6fc3z,27,10,1|i6fc40,1,1,0|idzk3z,1,1,0|idzk40,27,10,1|ip5erz,27,10,1|ip5es0,1,1,0|iwpmrz,1,1,0|iwpms0,27,10,1|j7vhfz,27,10,1|j7vhg0,1,1,0|jffpfz,1,1,0|jffpg0,27,10,1|jqlk3z,27,10,1|jqlk40,1,1,0|jyiqrz,1,1,0|jyiqs0,27,10,1|k9bmrz,27,10,1|k9bms0,1,1,0|kh8tfz,1,1,0|kh8tg0,27,10,1|ks1pfz,27,10,1|ks1pg0,1,1,0|kzyw3z,1,1,0|kzyw40,27,10,1|lb4qrz,27,10,1|lb4qs0,1,1,0|lioyrz,1,1,0|lioys0,27,10,1|ltutfz,27,10,1|ltutg0,1,1,0|m1f1fz,1,1,0|m1f1g0,27,10,1|mckw3z,27,10,1|mckw40,1,1,0|mki2rz,1,1,0|mki2s0,27,10,1|mvayrz,27,10,1|mvays0,1,1,0|n385fz,1,1,0|n385g0,27,10,1|ne11fz,27,10,1|ne11g0,1,1,0|nly83z,1,1,0|nly840,27,10,1|nwr43z,27,10,1|nwr440,1,1,0|o4oarz,1,1,0|o4oas0,27,10,1|ofu5fz,27,10,1|ofu5g0,1,1,0|onedfz,1,1,0|onedg0,27,10,1|oyk83z,27,10,1|oyk840,1,1,0|p64g3z,1,1,0|p64g40,27,10,1|phaarz,27,10,1|phaas0,1,1,0|pp7hfz,1,1,0|pp7hg0,27,10,1|q00dfz,27,10,1|q00dg0,1,1,0|q7xk3z,1,1,0|q7xk40,27,10,1|qiqg3z,27,10,1|qiqg40,1,1,0|qqnmrz,1,1,0|qqnms0,27,10,1|r1thfz,27,10,1|r1thg0,1,1,0|r9dpfz,1,1,0|r9dpg0,27,10,1|rkjk3z,27,10,1|rkjk40,1,1,0|rs3s3z,1,1,0|rs3s40,27,10,1|s39mrz,27,10,1|s39ms0,1,1,0|sb6tfz,1,1,0|sb6tg0,27,10,1|slzpfz,27,10,1|slzpg0,1,1,0|stww3z,1,1,0|stww40,27,10,1|t4ps3z,27,10,1|t4ps40,1,1,0|tcmyrz,1,1,0|tcmys0,27,10,1|tnfurz,27,10,1|tnfus0,1,1,0|tvd1fz,1,1,0|tvd1g0,27,10,1|u6iw3z,27,10,1|u6iw40,1,1,0|ue343z,1,1,0|ue3440,27,10,1|up8yrz,27,10,1|up8ys0,1,1,0|uwt6rz,1,1,0|uwt6s0,27,10,1|v7z1fz,27,10,1|v7z1g0,1,1,0|vfw83z,1,1,0|vfw840,27,10,1|vqp43z,27,10,1|vqp440,1,1,0|vymarz,1,1,0|vymas0,27,10,1|w9f6rz,27,10,1|w9f6s0,1,1,0|whcdfz,1,1,0|whcdg0,27,10,1|wsi83z,27,10,1|wsi840,1,1,0|x02g3z,1,1,0|x02g40,27,10,1|xb8arz,27,10,1|xb8as0,1,1,0|xisirz,1,1,0|xisis0,27,10,1|xtydfz,27,10,1|xtydg0,1,1,0|y1ilfz,1,1,0|y1ilg0,27,10,1|ycog3z,27,10,1|ycog40,1,1,0|yklmrz,1,1,0|yklms0,27,10,1|yveirz,27,10,1|yveis0,1,1,0|z3bpfz,1,1,0|z3bpg0,27,10,1|ze4lfz,27,10,1|ze4lg0,1,1,0\",\"Europe/Luxembourg|,0,353,0|-y89550,10,10,0|-rzo2w1,10,10,0|-rzo2w0,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|-rhps81,10,10,0|-rhps80,11,11,1|-raglg1,11,11,1|-raglg0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,10,10,0|-qo4w41,10,10,0|-qo4w40,8,1,0|-qj59g1,8,1,0|-qj59g0,9,10,1|-q7z6g1,9,10,1|-q7z6g0,8,1,0|-q15441,8,1,0|-q15440,9,10,1|-po6ak1,9,10,1|-po6ak0,8,1,0|-pgvhg1,8,1,0|-pgvhg0,9,10,1|-p5anw1,9,10,1|-p5anw0,8,1,0|-oxj6s1,8,1,0|-oxj6s0,9,10,1|-ong5c1,9,10,1|-ong5c0,8,1,0|-odd9g1,8,1,0|-odd9g0,9,10,1|-o4pzw1,9,10,1|-o4pzw0,8,1,0|-nvq2s1,8,1,0|-nvq2s0,9,10,1|-nm0001,9,10,1|-nm0000,8,1,0|-ncl6s1,8,1,0|-ncl6s0,9,10,1|-n39xc1,9,10,1|-n39xc0,8,1,0|-mt71g1,8,1,0|-mt71g0,9,10,1|-mkjuo1,9,10,1|-mkjuo0,8,1,0|-matxg1,8,1,0|-matxg0,9,10,1|-m1ts01,9,10,1|-m1ts00,8,1,0|-lrqw41,8,1,0|-lrqw40,9,10,1|-liqqo1,9,10,1|-liqqo0,8,1,0|-l8nus1,8,1,0|-l8nus0,9,10,1|-l00ig1,9,10,1|-l00ig0,8,1,0|-kqaig1,8,1,0|-kqaig0,9,10,1|-khafs1,9,10,1|-khafs0,8,1,0|-k77h41,8,1,0|-k77h40,9,10,1|-jykd41,9,10,1|-jykd40,8,1,0|-jp7bs1,8,1,0|-jp7bs0,9,10,1|-jfuag1,9,10,1|-jfuag0,8,1,0|-j6u7s1,8,1,0|-j6u7s0,9,10,1|-iwr941,9,10,1|-iwr940,8,1,0|-ine7s1,8,1,0|-ine7s0,9,10,1|-ie16g1,9,10,1|-ie16g0,8,1,0|-i513s1,8,1,0|-i513s0,9,10,1|-hvb3s1,9,10,1|-hvb3s0,8,1,0|-hl8541,8,1,0|-hl8540,9,10,1|-hcl141,9,10,1|-hcl140,8,1,0|-h37zs1,8,1,0|-h37zs0,9,10,1|-gtuyg1,9,10,1|-gtuyg0,8,1,0|-gkuvs1,8,1,0|-gkuvs0,9,10,1|-gb4vs1,9,10,1|-gb4vs0,8,1,0|-g11x41,8,1,0|-g11x40,9,10,1|-fpw2g1,9,10,1|-fpw2g0,8,1,0|-fkul41,8,1,0|-fkul40,9,10,1|-fgsag1,9,10,1|-fgsag0,9,11,1|-e6dzw1,9,11,1|-e6dzw0,8,10,0|-dytrw1,8,10,0|-dytrw0,9,11,1|-dp3rw1,9,11,1|-dp3rw0,8,10,0|-dfqqk1,8,10,0|-dfqqk0,9,11,1|-d73mk1,9,11,1|-d73mk0,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cofek1,11,11,1|-cofek0,10,10,0|-cbtp81,10,10,0|-cbtp80,11,11,1|-c4kl81,11,11,1|-c4kl80,10,10,0|3s9mrz,10,10,0|3s9ms0,11,11,1|419pfz,11,11,1|419pg0,10,10,0|4azpfz,10,10,0|4azpg0,11,11,1|4kcqrz,11,11,1|4kcqs0,10,10,0|4tps3z,10,10,0|4tps40,11,11,1|532tfz,11,11,1|532tg0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Madrid|,0,354,0|-100edc0,8,1,0|-qzlus1,8,1,0|-qzlus0,9,10,1|-qqnk01,9,10,1|-qqnk00,8,1,0|-qhalg1,8,1,0|-qhalg0,9,10,1|-q7vmo1,9,10,1|-q7vmo0,8,1,0|-nusqs1,8,1,0|-nusqs0,9,10,1|-nm0001,9,10,1|-nm0000,8,1,0|-mt71g1,8,1,0|-mt71g0,9,10,1|-mkjuo1,9,10,1|-mkjuo0,8,1,0|-matxg1,8,1,0|-matxg0,9,10,1|-m1ts01,9,10,1|-m1ts00,8,1,0|-lrqtc1,8,1,0|-lrqtc0,9,10,1|-liqqo1,9,10,1|-liqqo0,8,1,0|-l8nus1,8,1,0|-l8nus0,9,10,1|-l00o01,9,10,1|-l00o00,8,1,0|-gzf6s1,8,1,0|-gzf6s0,9,10,1|-gtv401,9,10,1|-gtv400,8,1,0|-gki5g1,8,1,0|-gki5g0,9,10,1|-gj2dk1,9,10,1|-gj2dk0,152,11,1|-gb3c81,152,11,1|-gb3c80,9,10,1|-fs2001,9,10,1|-fs2000,8,1,0|-fjrxg1,8,1,0|-fjrxg0,10,10,0|-eft481,10,10,0|-eft480,11,11,1|-e9kys1,11,11,1|-e9kys0,10,10,0|-dxsyw1,10,10,0|-dxsyw0,11,11,1|-dp5s41,11,11,1|-dp5s40,10,10,0|-df2w81,10,10,0|-df2w80,11,11,1|-d6fpg1,11,11,1|-d6fpg0,10,10,0|-cwctk1,10,10,0|-cwctk0,11,11,1|-cnpms1,11,11,1|-cnpms0,10,10,0|-cdmqw1,10,10,0|-cdmqw0,11,11,1|-c4zk41,11,11,1|-c4zk40,10,10,0|-asdmw1,10,10,0|-asdmw0,11,11,1|-akgdg1,11,11,1|-akgdg0,10,10,0|28g53z,10,10,0|28g540,11,11,1|2hgajz,11,11,1|2hgak0,10,10,0|2r67rz,10,10,0|2r67s0,11,11,1|306d7z,11,11,1|306d80,10,10,0|396d3z,10,10,0|396d40,11,11,1|3ijh7z,11,11,1|3ijh80,10,10,0|3s9efz,10,10,0|3s9eg0,11,11,1|419jvz,11,11,1|419jw0,10,10,0|4azpfz,10,10,0|4azpg0,11,11,1|4kcqrz,11,11,1|4kcqs0,10,10,0|4tps3z,10,10,0|4tps40,11,11,1|532tfz,11,11,1|532tg0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Malta|,0,355,0|-13qyw0s,10,10,0|-rymys1,10,10,0|-rymys0,11,11,1|-rsio81,11,11,1|-rsio80,10,10,0|-rj5k41,10,10,0|-rj5k40,11,11,1|-r9qqw1,11,11,1|-r9qqw0,10,10,0|-r1idg1,10,10,0|-r1idg0,11,11,1|-qqnpk1,11,11,1|-qqnpk0,10,10,0|-qj59g1,10,10,0|-qj59g0,11,11,1|-q7zhk1,11,11,1|-q7zhk0,10,10,0|-pzcas1,10,10,0|-pzcas0,11,11,1|-ppzc81,11,11,1|-ppzc80,10,10,0|-ff59g1,10,10,0|-ff59g0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfsl81,10,10,0|-dfsl80,11,11,1|-d75h81,11,11,1|-d75h80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cohes1,11,11,1|-cohes0,10,10,0|-cf2d81,10,10,0|-cf2d80,11,11,1|-c4mfw1,11,11,1|-c4mfw0,10,10,0|-bwcg41,10,10,0|-bwcg40,11,11,1|-blwis1,11,11,1|-blwis0,10,10,0|-bec581,10,10,0|-bec580,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|-1vwis1,10,10,0|-1vwis0,11,11,1|-1pf9k1,11,11,1|-1pf9k0,10,10,0|-1cthg1,10,10,0|-1cthg0,11,11,1|-16p441,11,11,1|-16p440,10,10,0|-u3es1,10,10,0|-u3es0,11,11,1|-nz1g1,11,11,1|-nz1g0,10,10,0|-b0dg1,10,10,0|-b0dg0,11,11,1|-4w041,11,11,1|-4w040,10,10,0|7pp7z,10,10,0|7pp80,11,11,1|du2jz,11,11,1|du2k0,10,10,0|q2t7z,10,10,0|q2t80,11,11,1|wk57z,11,11,1|wk580,10,10,0|195ujz,10,10,0|195uk0,11,11,1|1fn6jz,11,11,1|1fn6k0,10,10,0|1oyd7z,10,10,0|1oyd80,11,11,1|1ybejz,11,11,1|1ybek0,10,10,0|28t6jz,10,10,0|28t6k0,11,11,1|2gf97z,11,11,1|2gf980,10,10,0|2rjerz,10,10,0|2rjes0,11,11,1|2zginz,11,11,1|2zgio0,10,10,0|3a9hfz,10,10,0|3a9hg0,11,11,1|3i6lbz,11,11,1|3i6lc0,10,10,0|3szk3z,10,10,0|3szk40,11,11,1|40wnzz,11,11,1|40wo00,10,10,0|4bpmrz,10,10,0|4bpms0,11,11,1|4jmqnz,11,11,1|4jmqo0,10,10,0|4ufpfz,10,10,0|4ufpg0,11,11,1|52ctbz,11,11,1|52ctc0,10,10,0|5chpfz,10,10,0|5chpg0,11,11,1|5lfunz,11,11,1|5lfuo0,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Mariehamn|,0,348,0|-1bss9yd,77,348,0|-peghye,77,348,0|-peghyd,15,11,0|-ehco81,15,11,0|-ehco80,16,6,1|-e7vxk1,16,6,1|-e7vxk0,15,11,0|5v5unz,15,11,0|5v5uo0,16,6,1|64ivzz,16,6,1|64iw00,15,11,0|6dvxbz,15,11,0|6dvxc0,16,6,1|6n8ynz,16,6,1|6n8yo0,15,11,0|6wm2rz,15,11,0|6wm2s0,16,6,1|75z43z,16,6,1|75z440,15,11,0|7fc5fz,15,11,0|7fc5g0,16,6,1|7p25fz,16,6,1|7p25g0,15,11,0|7yf6rz,15,11,0|7yf6s0,16,6,1|87s83z,16,6,1|87s840,15,11,0|8h59fz,15,11,0|8h59g0,16,6,1|8qiarz,16,6,1|8qias0,15,11,0|8zvc3z,15,11,0|8zvc40,16,6,1|998dfz,16,6,1|998dg0,15,11,0|9ilerz,15,11,0|9iles0,16,6,1|9ryg3z,16,6,1|9ryg40,15,11,0|a1bhfz,15,11,0|a1bhg0,16,6,1|aaoirz,16,6,1|aaois0,15,11,0|ak1k3z,15,11,0|ak1k40,16,6,1|atrk3z,16,6,1|atrk40,15,11,0|b34lfz,15,11,0|b34lg0,16,6,1|bchmrz,16,6,1|bchms0,15,11,0|bluo3z,15,11,0|bluo40,16,6,1|bv7pfz,16,6,1|bv7pg0,15,11,0|c4kqrz,15,11,0|c4kqs0,16,6,1|cdxs3z,16,6,1|cdxs40,15,11,0|cnatfz,15,11,0|cnatg0,16,6,1|cwnurz,16,6,1|cwnus0,15,11,0|d60w3z,15,11,0|d60w40,16,6,1|dfdxfz,16,6,1|dfdxg0,15,11,0|dp3xfz,15,11,0|dp3xg0,16,6,1|dzwtfz,16,6,1|dzwtg0,15,11,0|e7u03z,15,11,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Minsk|,0,356,0|-1ayy7rs,21,357,0|-nu113d,21,357,0|-nu113c,15,11,0|-kmr1k1,15,11,0|-kmr1k0,148,6,0|-evpf01,148,6,0|-evpf00,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-db2g81,11,11,1|-db2g80,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,149,209,1|aaod7z,149,209,1|aaod80,148,6,0|b34fvz,148,6,0|b34fw0,16,6,1|bchjzz,16,6,1|bchk00,15,11,0|blulbz,15,11,0|blulc0,16,6,1|bv7mnz,16,6,1|bv7mo0,15,11,0|c4knzz,15,11,0|c4ko00,16,6,1|cdxpbz,16,6,1|cdxpc0,15,11,0|cnaqnz,15,11,0|cnaqo0,16,6,1|cwnrzz,16,6,1|cwns00,15,11,0|d60tbz,15,11,0|d60tc0,16,6,1|dfdunz,16,6,1|dfduo0,15,11,0|dp3unz,15,11,0|dp3uo0,16,6,1|dzwqnz,16,6,1|dzwqo0,15,11,0|e7txbz,15,11,0|e7txc0,16,6,1|eimtbz,16,6,1|eimtc0,15,11,0|eqjzzz,15,11,0|eqk000,16,6,1|f1cvzz,16,6,1|f1cw00,15,11,0|f9a2nz,15,11,0|f9a2o0,16,6,1|fkfxbz,16,6,1|fkfxc0,15,11,0|fs05bz,15,11,0|fs05c0,16,6,1|g35zzz,16,6,1|g36000,15,11,0|gaq7zz,15,11,0|gaq800,16,6,1|glw2nz,16,6,1|glw2o0,15,11,0|gtt9bz,15,11,0|gtt9c0,16,6,1|h4m5bz,16,6,1|h4m5c0,15,11,0|hcjbzz,15,11,0|hcjc00,16,6,1|hnc7zz,16,6,1|hnc800,15,11,0|hv9enz,15,11,0|hv9eo0,16,6,1|i6f9bz,16,6,1|i6f9c0,15,11,0|idzhbz,15,11,0|idzhc0,16,6,1|ip5bzz,16,6,1|ip5c00,15,11,0|iwpjzz,15,11,0|iwpk00,16,6,1|j7venz,16,6,1|j7veo0,15,11,0|jffmnz,15,11,0|jffmo0,16,6,1|jqlhbz,16,6,1|jqlhc0,15,11,0|jyinzz,15,11,0|jyio00,16,6,1|k9bjzz,16,6,1|k9bk00,15,11,0|kh8qnz,15,11,0|kh8qo0,16,6,1|ks1mnz,16,6,1|ks1mo0,15,11,0|kzytbz,15,11,0|kzytc0,16,6,1|lb4nzz,16,6,1|lb4o00,15,11,0|liovzz,15,11,0|liow00,100,6,0\",\"Europe/Monaco|,0,358,0|-14hnyp8,7,9,0|-uo2b3m,7,9,0|-uo2b3l,8,1,0|-ry2lg1,8,1,0|-ry2lg0,9,10,1|-rsgqs1,9,10,1|-rsgqs0,8,1,0|-rjiis1,8,1,0|-rjiis0,9,10,1|-r9dpg1,9,10,1|-r9dpg0,8,1,0|-r1idg1,8,1,0|-r1idg0,9,10,1|-qqnms1,9,10,1|-qqnms0,8,1,0|-qj59g1,8,1,0|-qj59g0,9,10,1|-q7xk41,9,10,1|-q7xk40,8,1,0|-q15441,8,1,0|-q15440,9,10,1|-po6g41,9,10,1|-po6g40,8,1,0|-pgvhg1,8,1,0|-pgvhg0,9,10,1|-p5atg1,9,10,1|-p5atg0,8,1,0|-oxj6s1,8,1,0|-oxj6s0,9,10,1|-ong841,9,10,1|-ong840,8,1,0|-obkg41,8,1,0|-obkg40,9,10,1|-o4q5g1,9,10,1|-o4q5g0,8,1,0|-nvq2s1,8,1,0|-nvq2s0,9,10,1|-nm02s1,9,10,1|-nm02s0,8,1,0|-ncn1g1,8,1,0|-ncn1g0,9,10,1|-n3a041,9,10,1|-n3a040,8,1,0|-mt71g1,8,1,0|-mt71g0,9,10,1|-mkjxg1,9,10,1|-mkjxg0,8,1,0|-matxg1,8,1,0|-matxg0,9,10,1|-m1tus1,9,10,1|-m1tus0,8,1,0|-lrqw41,8,1,0|-lrqw40,9,10,1|-liqtg1,9,10,1|-liqtg0,8,1,0|-l8nus1,8,1,0|-l8nus0,9,10,1|-l00qs1,9,10,1|-l00qs0,8,1,0|-kqaqs1,8,1,0|-kqaqs0,9,10,1|-khao41,9,10,1|-khao40,8,1,0|-k77pg1,8,1,0|-k77pg0,9,10,1|-jyklg1,9,10,1|-jyklg0,8,1,0|-jp7k41,8,1,0|-jp7k40,9,10,1|-jfuis1,9,10,1|-jfuis0,8,1,0|-j6ug41,8,1,0|-j6ug40,9,10,1|-iwrhg1,9,10,1|-iwrhg0,8,1,0|-ineg41,8,1,0|-ineg40,9,10,1|-ie1es1,9,10,1|-ie1es0,8,1,0|-i51c41,8,1,0|-i51c40,9,10,1|-hvbc41,9,10,1|-hvbc40,8,1,0|-hl8dg1,8,1,0|-hl8dg0,9,10,1|-hcl9g1,9,10,1|-hcl9g0,8,1,0|-h38841,8,1,0|-h38840,9,10,1|-gtv6s1,9,10,1|-gtv6s0,8,1,0|-gkv441,8,1,0|-gkv440,9,10,1|-gb5441,9,10,1|-gb5440,8,1,0|-g125g1,8,1,0|-g125g0,9,10,1|-fpwas1,9,10,1|-fpwas0,8,1,0|-fkul41,8,1,0|-fkul40,9,10,1|-eyh9g1,9,10,1|-eyh9g0,152,11,1|-eqk5k1,152,11,1|-eqk5k0,9,10,1|-eimw41,9,10,1|-eimw40,152,11,1|-e6dzw1,152,11,1|-e6dzw0,9,10,1|-dytrw1,9,10,1|-dytrw0,152,11,1|-dp3rw1,152,11,1|-dp3rw0,9,10,1|-dfqqk1,9,10,1|-dfqqk0,152,11,1|-d62qs1,152,11,1|-d62qs0,9,10,1|-cx0nw1,9,10,1|-cx0nw0,152,11,1|-cofek1,152,11,1|-cofek0,10,10,0|396inz,10,10,0|396io0,11,11,1|3ijh7z,11,11,1|3ijh80,10,10,0|3s9mrz,10,10,0|3s9ms0,11,11,1|419pfz,11,11,1|419pg0,10,10,0|4azpfz,10,10,0|4azpg0,11,11,1|4kcqrz,11,11,1|4kcqs0,10,10,0|4tps3z,10,10,0|4tps40,11,11,1|532tfz,11,11,1|532tg0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Moscow|,0,359,0|-1ayy9mh,21,359,0|-rx5dmi,21,359,0|-rx5dmh,21,360,0|-refds8,21,360,0|-refds7,50,361,1|-r57wg8,50,361,1|-r57wg7,21,360,0|-qx8xw8,21,360,0|-qx8xw7,153,362,1|-qrqps8,153,362,1|-qrqps7,50,361,1|-qeh0k8,50,361,1|-qeh0k7,153,362,1|-qcx401,153,362,1|-qcx400,149,209,1|-qak8g1,149,209,1|-qak8g0,148,6,0|-pibkg1,148,6,0|-pibkg0,149,209,1|-pgkok1,149,209,1|-pgkok0,92,194,1|-p84z81,92,194,1|-p84z80,149,209,1|-p6lcg1,149,209,1|-p6lcg0,148,6,0|-ontcc1,148,6,0|-ontcc0,15,11,0|-kmr1k1,15,11,0|-kmr1k0,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,149,209,1|aaod7z,149,209,1|aaod80,148,6,0|ak1ejz,148,6,0|ak1ek0,149,209,1|atrejz,149,209,1|atrek0,148,6,0|b34fvz,148,6,0|b34fw0,16,6,1|bchjzz,16,6,1|bchk00,15,11,0|bi8ynz,15,11,0|bi8yo0,148,6,0|bluijz,148,6,0|bluik0,149,209,1|bv7jvz,149,209,1|bv7jw0,148,6,0|c4kl7z,148,6,0|c4kl80,149,209,1|cdxmjz,149,209,1|cdxmk0,148,6,0|cnanvz,148,6,0|cnanw0,149,209,1|cwnp7z,149,209,1|cwnp80,148,6,0|d60qjz,148,6,0|d60qk0,149,209,1|dfdrvz,149,209,1|dfdrw0,148,6,0|dp3rvz,148,6,0|dp3rw0,149,209,1|dzwnvz,149,209,1|dzwnw0,148,6,0|e7tujz,148,6,0|e7tuk0,149,209,1|eimqjz,149,209,1|eimqk0,148,6,0|eqjx7z,148,6,0|eqjx80,149,209,1|f1ct7z,149,209,1|f1ct80,148,6,0|f99zvz,148,6,0|f99zw0,149,209,1|fkfujz,149,209,1|fkfuk0,148,6,0|fs02jz,148,6,0|fs02k0,149,209,1|g35x7z,149,209,1|g35x80,148,6,0|gaq57z,148,6,0|gaq580,149,209,1|glvzvz,149,209,1|glvzw0,148,6,0|gtt6jz,148,6,0|gtt6k0,149,209,1|h4m2jz,149,209,1|h4m2k0,148,6,0|hcj97z,148,6,0|hcj980,149,209,1|hnc57z,149,209,1|hnc580,148,6,0|hv9bvz,148,6,0|hv9bw0,149,209,1|i6f6jz,149,209,1|i6f6k0,148,6,0|idzejz,148,6,0|idzek0,149,209,1|ip597z,149,209,1|ip5980,148,6,0|iwph7z,148,6,0|iwph80,149,209,1|j7vbvz,149,209,1|j7vbw0,148,6,0|jffjvz,148,6,0|jffjw0,149,209,1|jqlejz,149,209,1|jqlek0,148,6,0|jyil7z,148,6,0|jyil80,149,209,1|k9bh7z,149,209,1|k9bh80,148,6,0|kh8nvz,148,6,0|kh8nw0,149,209,1|ks1jvz,149,209,1|ks1jw0,148,6,0|kzyqjz,148,6,0|kzyqk0,149,209,1|lb4l7z,149,209,1|lb4l80,148,6,0|liot7z,148,6,0|liot80,148,209,0|ne0t3z,148,209,0|ne0t40,148,6,0\",\"Europe/Oslo|,0,202,0|-1353tzo,10,10,0|-rzayo1,10,10,0|-rzayo0,11,11,1|-rskiw1,11,11,1|-rskiw0,10,10,0|-fc7s81,10,10,0|-fc7s80,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cnnmk1,11,11,1|-cnnmk0,10,10,0|-5mxh81,10,10,0|-5mxh80,11,11,1|-5d7h81,11,11,1|-5d7h80,10,10,0|-53ufw1,10,10,0|-53ufw0,11,11,1|-4uhek1,11,11,1|-4uhek0,10,10,0|-4l4d81,10,10,0|-4l4d80,11,11,1|-4brbw1,11,11,1|-4brbw0,10,10,0|-42eak1,10,10,0|-42eak0,11,11,1|-3t1981,11,11,1|-3t1980,10,10,0|-3jo7w1,10,10,0|-3jo7w0,11,11,1|-3ab6k1,11,11,1|-3ab6k0,10,10,0|-30y581,10,10,0|-30y580,11,11,1|-2r8581,11,11,1|-2r8580,10,10,0|-2g2ak1,10,10,0|-2g2ak0,11,11,1|-28i2k1,11,11,1|-28i2k0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Paris|,0,9,0|-154gb3l,7,9,0|-uozn3m,7,9,0|-uozn3l,8,1,0|-ry2lg1,8,1,0|-ry2lg0,9,10,1|-rsgqs1,9,10,1|-rsgqs0,8,1,0|-rjiis1,8,1,0|-rjiis0,9,10,1|-r9dpg1,9,10,1|-r9dpg0,8,1,0|-r1idg1,8,1,0|-r1idg0,9,10,1|-qqnms1,9,10,1|-qqnms0,8,1,0|-qj59g1,8,1,0|-qj59g0,9,10,1|-q7xk41,9,10,1|-q7xk40,8,1,0|-q15441,8,1,0|-q15440,9,10,1|-po6g41,9,10,1|-po6g40,8,1,0|-pgvhg1,8,1,0|-pgvhg0,9,10,1|-p5atg1,9,10,1|-p5atg0,8,1,0|-oxj6s1,8,1,0|-oxj6s0,9,10,1|-ong841,9,10,1|-ong840,8,1,0|-obkg41,8,1,0|-obkg40,9,10,1|-o4q5g1,9,10,1|-o4q5g0,8,1,0|-nvq2s1,8,1,0|-nvq2s0,9,10,1|-nm02s1,9,10,1|-nm02s0,8,1,0|-ncn1g1,8,1,0|-ncn1g0,9,10,1|-n3a041,9,10,1|-n3a040,8,1,0|-mt71g1,8,1,0|-mt71g0,9,10,1|-mkjxg1,9,10,1|-mkjxg0,8,1,0|-matxg1,8,1,0|-matxg0,9,10,1|-m1tus1,9,10,1|-m1tus0,8,1,0|-lrqw41,8,1,0|-lrqw40,9,10,1|-liqtg1,9,10,1|-liqtg0,8,1,0|-l8nus1,8,1,0|-l8nus0,9,10,1|-l00qs1,9,10,1|-l00qs0,8,1,0|-kqaqs1,8,1,0|-kqaqs0,9,10,1|-khao41,9,10,1|-khao40,8,1,0|-k77pg1,8,1,0|-k77pg0,9,10,1|-jyklg1,9,10,1|-jyklg0,8,1,0|-jp7k41,8,1,0|-jp7k40,9,10,1|-jfuis1,9,10,1|-jfuis0,8,1,0|-j6ug41,8,1,0|-j6ug40,9,10,1|-iwrhg1,9,10,1|-iwrhg0,8,1,0|-ineg41,8,1,0|-ineg40,9,10,1|-ie1es1,9,10,1|-ie1es0,8,1,0|-i51c41,8,1,0|-i51c40,9,10,1|-hvbc41,9,10,1|-hvbc40,8,1,0|-hl8dg1,8,1,0|-hl8dg0,9,10,1|-hcl9g1,9,10,1|-hcl9g0,8,1,0|-h38841,8,1,0|-h38840,9,10,1|-gtv6s1,9,10,1|-gtv6s0,8,1,0|-gkv441,8,1,0|-gkv440,9,10,1|-gb5441,9,10,1|-gb5440,8,1,0|-g125g1,8,1,0|-g125g0,9,10,1|-fpwas1,9,10,1|-fpwas0,8,1,0|-fkul41,8,1,0|-fkul40,9,10,1|-ff5c81,9,10,1|-ff5c80,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d8caw1,11,11,1|-d8caw0,152,11,1|-d62qs1,152,11,1|-d62qs0,9,10,1|-cx0nw1,9,10,1|-cx0nw0,152,11,1|-cofek1,152,11,1|-cofek0,10,10,0|396inz,10,10,0|396io0,11,11,1|3ijh7z,11,11,1|3ijh80,10,10,0|3s9mrz,10,10,0|3s9ms0,11,11,1|419pfz,11,11,1|419pg0,10,10,0|4azpfz,10,10,0|4azpg0,11,11,1|4kcqrz,11,11,1|4kcqs0,10,10,0|4tps3z,10,10,0|4tps40,11,11,1|532tfz,11,11,1|532tg0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Podgorica|,0,332,0|-18vsmgo,10,10,0|-ezayw1,10,10,0|-ezayw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cv5zw1,10,10,0|-cv5zw0,11,11,1|-cofek1,11,11,1|-cofek0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Prague|,0,334,0|-1qmkw08,7,334,0|-14u7uo9,7,334,0|-14u7uo8,10,10,0|-s0e081,10,10,0|-s0e080,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|-ridmk1,10,10,0|-ridmk0,11,11,1|-ragfw1,11,11,1|-ragfw0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,10,10,0|-fizzw1,10,10,0|-fizzw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cnnmk1,11,11,1|-cnnmk0,10,10,0|-cchrw1,10,10,0|-cchrw0,11,11,1|-c4mfw1,11,11,1|-c4mfw0,10,10,0|-c1qns1,10,10,0|-c1qns0,1,1,1|-bxf3s1,1,1,1|-bxf3s0,10,10,0|-bujh81,10,10,0|-bujh80,11,11,1|-blwd81,11,11,1|-blwd80,10,10,0|-bbtek1,10,10,0|-bbtek0,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|-ati581,10,10,0|-ati580,11,11,1|-akg7w1,11,11,1|-akg7w0,10,10,0|4tps3z,10,10,0|4tps40,11,11,1|532tfz,11,11,1|532tg0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Riga|,0,363,0|-1ayy74y,136,363,0|-qznlkz,136,363,0|-qznlky,154,364,1|-qrqewz,154,364,1|-qrqewy,136,363,0|-qhllkz,136,363,0|-qhllky,154,364,1|-qez5kz,154,364,1|-qez5ky,136,363,0|-ms0hsz,136,363,0|-ms0hsy,15,11,0|-fciw81,15,11,0|-fciw80,148,6,0|-evjv01,148,6,0|-evjv00,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-d5thg1,10,10,0|-d5thg0,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,16,6,1|aaofzz,16,6,1|aaog00,15,11,0|ak1hbz,15,11,0|ak1hc0,16,6,1|atrhbz,16,6,1|atrhc0,15,11,0|b34inz,15,11,0|b34io0,16,6,1|bchjzz,16,6,1|bchk00,15,11,0|blulbz,15,11,0|blulc0,16,6,1|bv7mnz,16,6,1|bv7mo0,15,11,0|c4knzz,15,11,0|c4ko00,16,6,1|cdxpbz,16,6,1|cdxpc0,15,11,0|cnaqnz,15,11,0|cnaqo0,16,6,1|cwnrzz,16,6,1|cwns00,15,11,0|d60tbz,15,11,0|d60tc0,16,6,1|dfdunz,16,6,1|dfduo0,15,11,0|dp3unz,15,11,0|dp3uo0,16,6,1|dygvzz,16,6,1|dygw00,15,11,0|e7u03z,15,11,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Rome|,0,365,0|-1hs7rn8,136,365,0|-13r0qs1,136,365,0|-13r0qs0,10,10,0|-rymys1,10,10,0|-rymys0,11,11,1|-rsio81,11,11,1|-rsio80,10,10,0|-rj5k41,10,10,0|-rj5k40,11,11,1|-r9qqw1,11,11,1|-r9qqw0,10,10,0|-r1idg1,10,10,0|-r1idg0,11,11,1|-qqnpk1,11,11,1|-qqnpk0,10,10,0|-qj59g1,10,10,0|-qj59g0,11,11,1|-q7zhk1,11,11,1|-q7zhk0,10,10,0|-pzcas1,10,10,0|-pzcas0,11,11,1|-ppzc81,11,11,1|-ppzc80,10,10,0|-ff59g1,10,10,0|-ff59g0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d75h81,11,11,1|-d75h80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cohes1,11,11,1|-cohes0,10,10,0|-cf2d81,10,10,0|-cf2d80,11,11,1|-c4mfw1,11,11,1|-c4mfw0,10,10,0|-bwcg41,10,10,0|-bwcg40,11,11,1|-blwis1,11,11,1|-blwis0,10,10,0|-bec581,10,10,0|-bec580,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|-1vwis1,10,10,0|-1vwis0,11,11,1|-1pf9k1,11,11,1|-1pf9k0,10,10,0|-1cthg1,10,10,0|-1cthg0,11,11,1|-16p441,11,11,1|-16p440,10,10,0|-u3es1,10,10,0|-u3es0,11,11,1|-nz1g1,11,11,1|-nz1g0,10,10,0|-b0dg1,10,10,0|-b0dg0,11,11,1|-4w041,11,11,1|-4w040,10,10,0|7pp7z,10,10,0|7pp80,11,11,1|du2jz,11,11,1|du2k0,10,10,0|q2t7z,10,10,0|q2t80,11,11,1|wk57z,11,11,1|wk580,10,10,0|195ujz,10,10,0|195uk0,11,11,1|1fn6jz,11,11,1|1fn6k0,10,10,0|1s8vvz,10,10,0|1s8vw0,11,11,1|1yd97z,11,11,1|1yd980,10,10,0|2alzvz,10,10,0|2alzw0,11,11,1|2h3bvz,11,11,1|2h3bw0,10,10,0|2tp17z,10,10,0|2tp180,11,11,1|2ztejz,11,11,1|2ztek0,10,10,0|3cf3vz,10,10,0|3cf3w0,11,11,1|3ijh7z,11,11,1|3ijh80,10,10,0|3us7vz,10,10,0|3us7w0,11,11,1|419jvz,11,11,1|419jw0,10,10,0|4dv97z,10,10,0|4dv980,11,11,1|4kcl7z,11,11,1|4kcl80,10,10,0|4wlbvz,10,10,0|4wlbw0,11,11,1|532nvz,11,11,1|532nw0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Samara|,0,366,0|-qcx400,100,6,0|-kmr4c1,100,6,0|-kmr4c0,105,209,0|5vb3jz,105,209,0|5vb3k0,92,194,1|64pwrz,92,194,1|64pws0,105,209,0|6e30vz,105,209,0|6e30w0,92,194,1|6nhu3z,92,194,1|6nhu40,105,209,0|6wuy7z,105,209,0|6wuy80,92,194,1|769rfz,92,194,1|769rg0,105,209,0|7foq7z,105,209,0|7foq80,92,194,1|7p1x3z,92,194,1|7p1x40,105,209,0|7yeyfz,105,209,0|7yeyg0,92,194,1|87rzrz,92,194,1|87rzs0,105,209,0|8h513z,105,209,0|8h5140,92,194,1|8qi2fz,92,194,1|8qi2g0,105,209,0|8zv3rz,105,209,0|8zv3s0,92,194,1|99853z,92,194,1|998540,105,209,0|9il6fz,105,209,0|9il6g0,92,194,1|9ry7rz,92,194,1|9ry7s0,105,209,0|a1b93z,105,209,0|a1b940,105,209,1|aaod7z,105,209,1|aaod80,100,6,0|ak1ejz,100,6,0|ak1ek0,105,209,1|atrejz,105,209,1|atrek0,100,6,0|b34fvz,100,6,0|b34fw0,100,6,1|bchjzz,100,6,1|bchk00,100,6,0|bdkfzz,100,6,0|bdkg00,105,209,0|blufrz,105,209,0|blufs0,92,194,1|bv7h3z,92,194,1|bv7h40,105,209,0|c4kifz,105,209,0|c4kig0,92,194,1|cdxjrz,92,194,1|cdxjs0,105,209,0|cnal3z,105,209,0|cnal40,92,194,1|cwnmfz,92,194,1|cwnmg0,105,209,0|d60nrz,105,209,0|d60ns0,92,194,1|dfdp3z,92,194,1|dfdp40,105,209,0|dp3p3z,105,209,0|dp3p40,92,194,1|dzwl3z,92,194,1|dzwl40,105,209,0|e7trrz,105,209,0|e7trs0,92,194,1|eimnrz,92,194,1|eimns0,105,209,0|eqjufz,105,209,0|eqjug0,92,194,1|f1cqfz,92,194,1|f1cqg0,105,209,0|f99x3z,105,209,0|f99x40,92,194,1|fkfrrz,92,194,1|fkfrs0,105,209,0|frzzrz,105,209,0|frzzs0,92,194,1|g35ufz,92,194,1|g35ug0,105,209,0|gaq2fz,105,209,0|gaq2g0,92,194,1|glvx3z,92,194,1|glvx40,105,209,0|gtt3rz,105,209,0|gtt3s0,92,194,1|h4lzrz,92,194,1|h4lzs0,105,209,0|hcj6fz,105,209,0|hcj6g0,92,194,1|hnc2fz,92,194,1|hnc2g0,105,209,0|hv993z,105,209,0|hv9940,92,194,1|i6f3rz,92,194,1|i6f3s0,105,209,0|idzbrz,105,209,0|idzbs0,92,194,1|ip56fz,92,194,1|ip56g0,105,209,0|iwpefz,105,209,0|iwpeg0,92,194,1|j7v93z,92,194,1|j7v940,105,209,0|jffh3z,105,209,0|jffh40,92,194,1|jqlbrz,92,194,1|jqlbs0,105,209,0|jyiifz,105,209,0|jyiig0,92,194,1|k9befz,92,194,1|k9beg0,105,209,0|kh8l3z,105,209,0|kh8l40,92,194,1|ks1h3z,92,194,1|ks1h40,105,209,0|kzynrz,105,209,0|kzyns0,105,209,1|lb4l7z,105,209,1|lb4l80,100,6,0|liot7z,100,6,0|liot80,105,209,0\",\"Europe/San_Marino|,0,365,0|-1hs7rn8,136,365,0|-13r0qs1,136,365,0|-13r0qs0,10,10,0|-rymys1,10,10,0|-rymys0,11,11,1|-rsio81,11,11,1|-rsio80,10,10,0|-rj5k41,10,10,0|-rj5k40,11,11,1|-r9qqw1,11,11,1|-r9qqw0,10,10,0|-r1idg1,10,10,0|-r1idg0,11,11,1|-qqnpk1,11,11,1|-qqnpk0,10,10,0|-qj59g1,10,10,0|-qj59g0,11,11,1|-q7zhk1,11,11,1|-q7zhk0,10,10,0|-pzcas1,10,10,0|-pzcas0,11,11,1|-ppzc81,11,11,1|-ppzc80,10,10,0|-ff59g1,10,10,0|-ff59g0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d75h81,11,11,1|-d75h80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cohes1,11,11,1|-cohes0,10,10,0|-cf2d81,10,10,0|-cf2d80,11,11,1|-c4mfw1,11,11,1|-c4mfw0,10,10,0|-bwcg41,10,10,0|-bwcg40,11,11,1|-blwis1,11,11,1|-blwis0,10,10,0|-bec581,10,10,0|-bec580,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|-1vwis1,10,10,0|-1vwis0,11,11,1|-1pf9k1,11,11,1|-1pf9k0,10,10,0|-1cthg1,10,10,0|-1cthg0,11,11,1|-16p441,11,11,1|-16p440,10,10,0|-u3es1,10,10,0|-u3es0,11,11,1|-nz1g1,11,11,1|-nz1g0,10,10,0|-b0dg1,10,10,0|-b0dg0,11,11,1|-4w041,11,11,1|-4w040,10,10,0|7pp7z,10,10,0|7pp80,11,11,1|du2jz,11,11,1|du2k0,10,10,0|q2t7z,10,10,0|q2t80,11,11,1|wk57z,11,11,1|wk580,10,10,0|195ujz,10,10,0|195uk0,11,11,1|1fn6jz,11,11,1|1fn6k0,10,10,0|1s8vvz,10,10,0|1s8vw0,11,11,1|1yd97z,11,11,1|1yd980,10,10,0|2alzvz,10,10,0|2alzw0,11,11,1|2h3bvz,11,11,1|2h3bw0,10,10,0|2tp17z,10,10,0|2tp180,11,11,1|2ztejz,11,11,1|2ztek0,10,10,0|3cf3vz,10,10,0|3cf3w0,11,11,1|3ijh7z,11,11,1|3ijh80,10,10,0|3us7vz,10,10,0|3us7w0,11,11,1|419jvz,11,11,1|419jw0,10,10,0|4dv97z,10,10,0|4dv980,11,11,1|4kcl7z,11,11,1|4kcl80,10,10,0|4wlbvz,10,10,0|4wlbw0,11,11,1|532nvz,11,11,1|532nw0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Sarajevo|,0,332,0|-18vsmgo,10,10,0|-ezayw1,10,10,0|-ezayw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cv5zw1,10,10,0|-cv5zw0,11,11,1|-cofek1,11,11,1|-cofek0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Saratov|,0,367,0|-qcx400,100,6,0|-kmr4c1,100,6,0|-kmr4c0,105,209,0|5vb3jz,105,209,0|5vb3k0,92,194,1|64pwrz,92,194,1|64pws0,105,209,0|6e30vz,105,209,0|6e30w0,92,194,1|6nhu3z,92,194,1|6nhu40,105,209,0|6wuy7z,105,209,0|6wuy80,92,194,1|769rfz,92,194,1|769rg0,105,209,0|7foq7z,105,209,0|7foq80,92,194,1|7p1x3z,92,194,1|7p1x40,105,209,0|7yeyfz,105,209,0|7yeyg0,92,194,1|87rzrz,92,194,1|87rzs0,105,209,0|8h513z,105,209,0|8h5140,92,194,1|8qi2fz,92,194,1|8qi2g0,105,209,0|8zv3rz,105,209,0|8zv3s0,92,194,1|99853z,92,194,1|998540,105,209,0|9il6fz,105,209,0|9il6g0,105,209,1|9ryajz,105,209,1|9ryak0,100,6,0|a1bbvz,100,6,0|a1bbw0,105,209,1|aaod7z,105,209,1|aaod80,100,6,0|ak1ejz,100,6,0|ak1ek0,105,209,1|atrejz,105,209,1|atrek0,100,6,0|b34fvz,100,6,0|b34fw0,105,209,0|blufrz,105,209,0|blufs0,105,209,1|bv7jvz,105,209,1|bv7jw0,100,6,0|c4kl7z,100,6,0|c4kl80,105,209,1|cdxmjz,105,209,1|cdxmk0,100,6,0|cnanvz,100,6,0|cnanw0,105,209,1|cwnp7z,105,209,1|cwnp80,100,6,0|d60qjz,100,6,0|d60qk0,105,209,1|dfdrvz,105,209,1|dfdrw0,100,6,0|dp3rvz,100,6,0|dp3rw0,105,209,1|dzwnvz,105,209,1|dzwnw0,100,6,0|e7tujz,100,6,0|e7tuk0,105,209,1|eimqjz,105,209,1|eimqk0,100,6,0|eqjx7z,100,6,0|eqjx80,105,209,1|f1ct7z,105,209,1|f1ct80,100,6,0|f99zvz,100,6,0|f99zw0,105,209,1|fkfujz,105,209,1|fkfuk0,100,6,0|fs02jz,100,6,0|fs02k0,105,209,1|g35x7z,105,209,1|g35x80,100,6,0|gaq57z,100,6,0|gaq580,105,209,1|glvzvz,105,209,1|glvzw0,100,6,0|gtt6jz,100,6,0|gtt6k0,105,209,1|h4m2jz,105,209,1|h4m2k0,100,6,0|hcj97z,100,6,0|hcj980,105,209,1|hnc57z,105,209,1|hnc580,100,6,0|hv9bvz,100,6,0|hv9bw0,105,209,1|i6f6jz,105,209,1|i6f6k0,100,6,0|idzejz,100,6,0|idzek0,105,209,1|ip597z,105,209,1|ip5980,100,6,0|iwph7z,100,6,0|iwph80,105,209,1|j7vbvz,105,209,1|j7vbw0,100,6,0|jffjvz,100,6,0|jffjw0,105,209,1|jqlejz,105,209,1|jqlek0,100,6,0|jyil7z,100,6,0|jyil80,105,209,1|k9bh7z,105,209,1|k9bh80,100,6,0|kh8nvz,100,6,0|kh8nw0,105,209,1|ks1jvz,105,209,1|ks1jw0,100,6,0|kzyqjz,100,6,0|kzyqk0,105,209,1|lb4l7z,105,209,1|lb4l80,100,6,0|liot7z,100,6,0|liot80,105,209,0|ne0t3z,105,209,0|ne0t40,100,6,0|ohmt7z,100,6,0|ohmt80,105,209,0\",\"Europe/Simferopol|,0,368,0|-1ayy8zc,85,369,0|-nu12ap,85,369,0|-nu12ao,15,11,0|-kmr1k1,15,11,0|-kmr1k0,148,6,0|-ep8301,148,6,0|-ep8300,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-df8g81,11,11,1|-df8g80,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,149,209,1|aaod7z,149,209,1|aaod80,148,6,0|ap2vvz,148,6,0|ap2vw0,15,11,0|blufrz,15,11,0|blufs0,16,6,1|bv7ebz,16,6,1|bv7ec0,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxgzz,16,6,1|cdxh00,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cp3bnz,16,6,1|cp3bo0,149,209,1|cwngvz,149,209,1|cwngw0,148,6,0|d60kzz,148,6,0|d60l00,149,209,1|dfdjjz,149,209,1|dfdjk0,148,6,0|dp3mbz,148,6,0|dp3mc0,149,209,1|dzwqnz,149,209,1|dzwqo0,148,6,0|e7u03z,148,6,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n382nz,15,11,0|n382o0,148,209,0|ne0t3z,148,209,0|ne0t40,148,6,0\",\"Europe/Skopje|,0,332,0|-18vsmgo,10,10,0|-ezayw1,10,10,0|-ezayw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cv5zw1,10,10,0|-cv5zw0,11,11,1|-cofek1,11,11,1|-cofek0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Sofia|,0,370,0|-1ayy6zg,117,350,0|-136r6qx,117,350,0|-136r6qw,15,11,0|-e6dzw1,15,11,0|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0l41,10,10,0|-cx0l40,15,11,0|4tpgzz,15,11,0|4tph00,16,6,1|534frz,16,6,1|534fs0,15,11,0|5csibz,15,11,0|5csic0,16,6,1|5luifz,16,6,1|5luig0,15,11,0|5vikzz,15,11,0|5vil00,16,6,1|64it7z,16,6,1|64it80,15,11,0|6e8nnz,15,11,0|6e8no0,16,6,1|6n8ynz,16,6,1|6n8yo0,15,11,0|6wlzzz,15,11,0|6wm000,16,6,1|75z1bz,16,6,1|75z1c0,15,11,0|7fc2nz,15,11,0|7fc2o0,16,6,1|7p22nz,16,6,1|7p22o0,15,11,0|7yf3zz,15,11,0|7yf400,16,6,1|87s5bz,16,6,1|87s5c0,15,11,0|8h56nz,15,11,0|8h56o0,16,6,1|8qi7zz,16,6,1|8qi800,15,11,0|8zv9bz,15,11,0|8zv9c0,16,6,1|998anz,16,6,1|998ao0,15,11,0|9ilbzz,15,11,0|9ilc00,16,6,1|9rydbz,16,6,1|9rydc0,15,11,0|a1benz,15,11,0|a1beo0,16,6,1|aaofzz,16,6,1|aaog00,15,11,0|ak1hbz,15,11,0|ak1hc0,16,6,1|atrhbz,16,6,1|atrhc0,15,11,0|b34d3z,15,11,0|b34d40,16,6,1|bchbnz,16,6,1|bchbo0,15,11,0|blufrz,15,11,0|blufs0,16,6,1|bv7ebz,16,6,1|bv7ec0,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxgzz,16,6,1|cdxh00,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cwnjnz,16,6,1|cwnjo0,15,11,0|d60nrz,15,11,0|d60ns0,16,6,1|dfdmbz,16,6,1|dfdmc0,15,11,0|dp3p3z,15,11,0|dp3p40,16,6,1|dzwibz,16,6,1|dzwic0,15,11,0|e7u03z,15,11,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Stockholm|,0,371,0|-1bhq3cc,155,372,0|-10j6dgf,155,372,0|-10j6dge,10,10,0|-rzo2w1,10,10,0|-rzo2w0,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Tallinn|,0,373,0|-1ayy790,133,373,0|-r3exx1,133,373,0|-r3exx0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,10,10,0|-qcx6s1,10,10,0|-qcx6s0,133,373,0|-peghx1,133,373,0|-peghx0,15,11,0|-fch1k1,15,11,0|-fch1k0,148,6,0|-ern4c1,148,6,0|-ern4c0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6wg81,11,11,1|-d6wg80,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,16,6,1|aaofzz,16,6,1|aaog00,15,11,0|ak1hbz,15,11,0|ak1hc0,16,6,1|atrhbz,16,6,1|atrhc0,15,11,0|b34inz,15,11,0|b34io0,16,6,1|bchjzz,16,6,1|bchk00,15,11,0|blulbz,15,11,0|blulc0,16,6,1|bv7mnz,16,6,1|bv7mo0,15,11,0|c4knzz,15,11,0|c4ko00,16,6,1|cdxpbz,16,6,1|cdxpc0,15,11,0|cnaqnz,15,11,0|cnaqo0,16,6,1|cwnrzz,16,6,1|cwns00,15,11,0|d60tbz,15,11,0|d60tc0,16,6,1|dfdunz,16,6,1|dfduo0,15,11,0|dp3unz,15,11,0|dp3uo0,16,6,1|dzwqnz,16,6,1|dzwqo0,15,11,0|e7txbz,15,11,0|e7txc0,16,6,1|eimtbz,16,6,1|eimtc0,15,11,0|eqjzzz,15,11,0|eqk000,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Tirane|,0,374,0|-t85vo8,10,10,0|-ff3es1,10,10,0|-ff3es0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dy7jw1,11,11,1|-dy7jw0,10,10,0|29h97z,10,10,0|29h980,11,11,1|2h8t3z,11,11,1|2h8t40,10,10,0|2s3mjz,10,10,0|2s3mk0,11,11,1|300qfz,11,11,1|300qg0,10,10,0|3az97z,10,10,0|3az980,11,11,1|3iwd3z,11,11,1|3iwd40,10,10,0|3u2ajz,10,10,0|3u2ak0,11,11,1|41mfrz,11,11,1|41mfs0,10,10,0|4cqijz,10,10,0|4cqik0,11,11,1|4kcifz,11,11,1|4kcig0,10,10,0|4vgl7z,10,10,0|4vgl80,11,11,1|532l3z,11,11,1|532l40,10,10,0|5e6nvz,10,10,0|5e6nw0,11,11,1|5m3rrz,11,11,1|5m3rs0,10,10,0|5wlmjz,10,10,0|5wlmk0,11,11,1|64iqfz,11,11,1|64iqg0,10,10,0|6fonvz,10,10,0|6fonw0,11,11,1|6nlrrz,11,11,1|6nlrs0,10,10,0|6xqnvz,10,10,0|6xqnw0,11,11,1|769zrz,11,11,1|769zs0,10,10,0|7foyjz,10,10,0|7foyk0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Ulyanovsk|,0,375,0|-qcx400,100,6,0|-kmr4c1,100,6,0|-kmr4c0,105,209,0|5vb3jz,105,209,0|5vb3k0,92,194,1|64pwrz,92,194,1|64pws0,105,209,0|6e30vz,105,209,0|6e30w0,92,194,1|6nhu3z,92,194,1|6nhu40,105,209,0|6wuy7z,105,209,0|6wuy80,92,194,1|769rfz,92,194,1|769rg0,105,209,0|7foq7z,105,209,0|7foq80,92,194,1|7p1x3z,92,194,1|7p1x40,105,209,0|7yeyfz,105,209,0|7yeyg0,92,194,1|87rzrz,92,194,1|87rzs0,105,209,0|8h513z,105,209,0|8h5140,92,194,1|8qi2fz,92,194,1|8qi2g0,105,209,0|8zv3rz,105,209,0|8zv3s0,92,194,1|99853z,92,194,1|998540,105,209,0|9il6fz,105,209,0|9il6g0,92,194,1|9ry7rz,92,194,1|9ry7s0,105,209,0|a1b93z,105,209,0|a1b940,105,209,1|aaod7z,105,209,1|aaod80,100,6,0|ak1ejz,100,6,0|ak1ek0,105,209,1|atrejz,105,209,1|atrek0,100,6,0|b34fvz,100,6,0|b34fw0,100,6,1|bchjzz,100,6,1|bchk00,101,11,0|bi8ynz,101,11,0|bi8yo0,100,6,0|bluijz,100,6,0|bluik0,105,209,1|bv7jvz,105,209,1|bv7jw0,100,6,0|c4kl7z,100,6,0|c4kl80,105,209,1|cdxmjz,105,209,1|cdxmk0,100,6,0|cnanvz,100,6,0|cnanw0,105,209,1|cwnp7z,105,209,1|cwnp80,100,6,0|d60qjz,100,6,0|d60qk0,105,209,1|dfdrvz,105,209,1|dfdrw0,100,6,0|dp3rvz,100,6,0|dp3rw0,105,209,1|dzwnvz,105,209,1|dzwnw0,100,6,0|e7tujz,100,6,0|e7tuk0,105,209,1|eimqjz,105,209,1|eimqk0,100,6,0|eqjx7z,100,6,0|eqjx80,105,209,1|f1ct7z,105,209,1|f1ct80,100,6,0|f99zvz,100,6,0|f99zw0,105,209,1|fkfujz,105,209,1|fkfuk0,100,6,0|fs02jz,100,6,0|fs02k0,105,209,1|g35x7z,105,209,1|g35x80,100,6,0|gaq57z,100,6,0|gaq580,105,209,1|glvzvz,105,209,1|glvzw0,100,6,0|gtt6jz,100,6,0|gtt6k0,105,209,1|h4m2jz,105,209,1|h4m2k0,100,6,0|hcj97z,100,6,0|hcj980,105,209,1|hnc57z,105,209,1|hnc580,100,6,0|hv9bvz,100,6,0|hv9bw0,105,209,1|i6f6jz,105,209,1|i6f6k0,100,6,0|idzejz,100,6,0|idzek0,105,209,1|ip597z,105,209,1|ip5980,100,6,0|iwph7z,100,6,0|iwph80,105,209,1|j7vbvz,105,209,1|j7vbw0,100,6,0|jffjvz,100,6,0|jffjw0,105,209,1|jqlejz,105,209,1|jqlek0,100,6,0|jyil7z,100,6,0|jyil80,105,209,1|k9bh7z,105,209,1|k9bh80,100,6,0|kh8nvz,100,6,0|kh8nw0,105,209,1|ks1jvz,105,209,1|ks1jw0,100,6,0|kzyqjz,100,6,0|kzyqk0,105,209,1|lb4l7z,105,209,1|lb4l80,100,6,0|liot7z,100,6,0|liot80,105,209,0|ne0t3z,105,209,0|ne0t40,100,6,0|o4o57z,100,6,0|o4o580,105,209,0\",\"Europe/Uzhgorod|,0,376,0|-15cztgo,10,10,0|-fizzw1,10,10,0|-fizzw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d55hk1,11,11,1|-d55hk0,10,10,0|-cshus1,10,10,0|-cshus0,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,149,209,1|aaod7z,149,209,1|aaod80,148,6,0|ap2vvz,148,6,0|ap2vw0,10,10,0|b34o7z,10,10,0|b34o80,15,11,0|blufrz,15,11,0|blufs0,16,6,1|bv7ebz,16,6,1|bv7ec0,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxgzz,16,6,1|cdxh00,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cwnjnz,16,6,1|cwnjo0,15,11,0|d60w3z,15,11,0|d60w40,16,6,1|dfdxfz,16,6,1|dfdxg0,15,11,0|dp3xfz,15,11,0|dp3xg0,16,6,1|dzwtfz,16,6,1|dzwtg0,15,11,0|e7u03z,15,11,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Vaduz|,0,338,0|-1os49kw,53,339,0|-13g441n,53,339,0|-13g441m,10,10,0|-eyh6o1,10,10,0|-eyh6o0,11,11,1|-eqk001,11,11,1|-eqk000,10,10,0|-efr401,10,10,0|-efr400,11,11,1|-e7txc1,11,11,1|-e7txc0,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Vatican|,0,365,0|-1hs7rn8,136,365,0|-13r0qs1,136,365,0|-13r0qs0,10,10,0|-rymys1,10,10,0|-rymys0,11,11,1|-rsio81,11,11,1|-rsio80,10,10,0|-rj5k41,10,10,0|-rj5k40,11,11,1|-r9qqw1,11,11,1|-r9qqw0,10,10,0|-r1idg1,10,10,0|-r1idg0,11,11,1|-qqnpk1,11,11,1|-qqnpk0,10,10,0|-qj59g1,10,10,0|-qj59g0,11,11,1|-q7zhk1,11,11,1|-q7zhk0,10,10,0|-pzcas1,10,10,0|-pzcas0,11,11,1|-ppzc81,11,11,1|-ppzc80,10,10,0|-ff59g1,10,10,0|-ff59g0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d75h81,11,11,1|-d75h80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cohes1,11,11,1|-cohes0,10,10,0|-cf2d81,10,10,0|-cf2d80,11,11,1|-c4mfw1,11,11,1|-c4mfw0,10,10,0|-bwcg41,10,10,0|-bwcg40,11,11,1|-blwis1,11,11,1|-blwis0,10,10,0|-bec581,10,10,0|-bec580,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|-1vwis1,10,10,0|-1vwis0,11,11,1|-1pf9k1,11,11,1|-1pf9k0,10,10,0|-1cthg1,10,10,0|-1cthg0,11,11,1|-16p441,11,11,1|-16p440,10,10,0|-u3es1,10,10,0|-u3es0,11,11,1|-nz1g1,11,11,1|-nz1g0,10,10,0|-b0dg1,10,10,0|-b0dg0,11,11,1|-4w041,11,11,1|-4w040,10,10,0|7pp7z,10,10,0|7pp80,11,11,1|du2jz,11,11,1|du2k0,10,10,0|q2t7z,10,10,0|q2t80,11,11,1|wk57z,11,11,1|wk580,10,10,0|195ujz,10,10,0|195uk0,11,11,1|1fn6jz,11,11,1|1fn6k0,10,10,0|1s8vvz,10,10,0|1s8vw0,11,11,1|1yd97z,11,11,1|1yd980,10,10,0|2alzvz,10,10,0|2alzw0,11,11,1|2h3bvz,11,11,1|2h3bw0,10,10,0|2tp17z,10,10,0|2tp180,11,11,1|2ztejz,11,11,1|2ztek0,10,10,0|3cf3vz,10,10,0|3cf3w0,11,11,1|3ijh7z,11,11,1|3ijh80,10,10,0|3us7vz,10,10,0|3us7w0,11,11,1|419jvz,11,11,1|419jw0,10,10,0|4dv97z,10,10,0|4dv980,11,11,1|4kcl7z,11,11,1|4kcl80,10,10,0|4wlbvz,10,10,0|4wlbw0,11,11,1|532nvz,11,11,1|532nw0,10,10,0|5cstfz,10,10,0|5cstg0,11,11,1|5lsw3z,11,11,1|5lsw40,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Vienna|,0,377,0|-14211ox,10,10,0|-s0e081,10,10,0|-s0e080,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|-ridmk1,10,10,0|-ridmk0,11,11,1|-ragfw1,11,11,1|-ragfw0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,10,10,0|-pykd81,10,10,0|-pykd80,11,11,1|-pqa7w1,11,11,1|-pqa7w0,10,10,0|-fizzw1,10,10,0|-fizzw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cx0nw1,10,10,0|-cx0nw0,11,11,1|-cwi581,11,11,1|-cwi580,10,10,0|-cdmik1,10,10,0|-cdmik0,11,11,1|-c4kl81,11,11,1|-c4kl80,10,10,0|-bv9ek1,10,10,0|-bv9ek0,11,11,1|-blwd81,11,11,1|-blwd80,10,10,0|-bbtek1,10,10,0|-bbtek0,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|5csnvz,10,10,0|5csnw0,11,11,1|5lsnrz,11,11,1|5lsns0,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Vilnius|,0,378,0|-1ayy7cs,156,379,0|-rns981,156,379,0|-rns980,74,380,0|-q7q73d,74,380,0|-q7q73c,10,10,0|-ptj1g1,10,10,0|-ptj1g0,15,11,0|-poyaw1,15,11,0|-poyaw0,10,10,0|-fcmis1,10,10,0|-fcmis0,148,6,0|-evwto1,148,6,0|-evwto0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d9kqw1,11,11,1|-d9kqw0,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,16,6,1|aaofzz,16,6,1|aaog00,15,11,0|ak1hbz,15,11,0|ak1hc0,16,6,1|atrhbz,16,6,1|atrhc0,15,11,0|b34inz,15,11,0|b34io0,16,6,1|bchjzz,16,6,1|bchk00,15,11,0|blulbz,15,11,0|blulc0,16,6,1|bv7mnz,16,6,1|bv7mo0,15,11,0|c4knzz,15,11,0|c4ko00,16,6,1|cdxpbz,16,6,1|cdxpc0,15,11,0|cnaqnz,15,11,0|cnaqo0,16,6,1|cwnrzz,16,6,1|cwns00,15,11,0|d60tbz,15,11,0|d60tc0,16,6,1|dfdunz,16,6,1|dfduo0,15,11,0|dp3unz,15,11,0|dp3uo0,16,6,1|dzwqnz,16,6,1|dzwqo0,15,11,0|e7txbz,15,11,0|e7txc0,16,6,1|eimtbz,16,6,1|eimtc0,15,11,0|eqk2rz,15,11,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Volgograd|,0,213,0|-q3cw84,100,6,0|-kmr4c1,100,6,0|-kmr4c0,105,209,0|5vb3jz,105,209,0|5vb3k0,92,194,1|64pwrz,92,194,1|64pws0,105,209,0|6e30vz,105,209,0|6e30w0,92,194,1|6nhu3z,92,194,1|6nhu40,105,209,0|6wuy7z,105,209,0|6wuy80,92,194,1|769rfz,92,194,1|769rg0,105,209,0|7foq7z,105,209,0|7foq80,92,194,1|7p1x3z,92,194,1|7p1x40,105,209,0|7yeyfz,105,209,0|7yeyg0,92,194,1|87rzrz,92,194,1|87rzs0,105,209,0|8h513z,105,209,0|8h5140,92,194,1|8qi2fz,92,194,1|8qi2g0,105,209,0|8zv3rz,105,209,0|8zv3s0,92,194,1|99853z,92,194,1|998540,105,209,0|9il6fz,105,209,0|9il6g0,105,209,1|9ryajz,105,209,1|9ryak0,100,6,0|a1bbvz,100,6,0|a1bbw0,105,209,1|aaod7z,105,209,1|aaod80,100,6,0|ak1ejz,100,6,0|ak1ek0,105,209,1|atrejz,105,209,1|atrek0,100,6,0|b34fvz,100,6,0|b34fw0,105,209,0|blufrz,105,209,0|blufs0,105,209,1|bv7jvz,105,209,1|bv7jw0,100,6,0|c4kl7z,100,6,0|c4kl80,105,209,1|cdxmjz,105,209,1|cdxmk0,100,6,0|cnanvz,100,6,0|cnanw0,105,209,1|cwnp7z,105,209,1|cwnp80,100,6,0|d60qjz,100,6,0|d60qk0,105,209,1|dfdrvz,105,209,1|dfdrw0,100,6,0|dp3rvz,100,6,0|dp3rw0,105,209,1|dzwnvz,105,209,1|dzwnw0,100,6,0|e7tujz,100,6,0|e7tuk0,105,209,1|eimqjz,105,209,1|eimqk0,100,6,0|eqjx7z,100,6,0|eqjx80,105,209,1|f1ct7z,105,209,1|f1ct80,100,6,0|f99zvz,100,6,0|f99zw0,105,209,1|fkfujz,105,209,1|fkfuk0,100,6,0|fs02jz,100,6,0|fs02k0,105,209,1|g35x7z,105,209,1|g35x80,100,6,0|gaq57z,100,6,0|gaq580,105,209,1|glvzvz,105,209,1|glvzw0,100,6,0|gtt6jz,100,6,0|gtt6k0,105,209,1|h4m2jz,105,209,1|h4m2k0,100,6,0|hcj97z,100,6,0|hcj980,105,209,1|hnc57z,105,209,1|hnc580,100,6,0|hv9bvz,100,6,0|hv9bw0,105,209,1|i6f6jz,105,209,1|i6f6k0,100,6,0|idzejz,100,6,0|idzek0,105,209,1|ip597z,105,209,1|ip5980,100,6,0|iwph7z,100,6,0|iwph80,105,209,1|j7vbvz,105,209,1|j7vbw0,100,6,0|jffjvz,100,6,0|jffjw0,105,209,1|jqlejz,105,209,1|jqlek0,100,6,0|jyil7z,100,6,0|jyil80,105,209,1|k9bh7z,105,209,1|k9bh80,100,6,0|kh8nvz,100,6,0|kh8nw0,105,209,1|ks1jvz,105,209,1|ks1jw0,100,6,0|kzyqjz,100,6,0|kzyqk0,105,209,1|lb4l7z,105,209,1|lb4l80,100,6,0|liot7z,100,6,0|liot80,105,209,0|ne0t3z,105,209,0|ne0t40,100,6,0|pha57z,100,6,0|pha580,105,209,0|qlyvrz,105,209,0|qlyvs0,100,6,0\",\"Europe/Warsaw|,0,379,0|-1ayy6k0,156,379,0|-se9yk1,156,379,0|-se9yk0,10,10,0|-s0e081,10,10,0|-s0e080,11,11,1|-rsilg1,11,11,1|-rsilg0,10,10,0|-ridmk1,10,10,0|-ridmk0,11,11,1|-ragfw1,11,11,1|-ragfw0,10,10,0|-qznjw1,10,10,0|-qznjw0,11,11,1|-qrqd81,11,11,1|-qrqd80,15,11,0|-qgvpc1,15,11,0|-qgvpc0,16,6,1|-q8yio1,16,6,1|-q8yio0,15,11,0|-ou36w1,15,11,0|-ou36w0,10,10,0|-feqak1,10,10,0|-feqak0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6a2o1,11,11,1|-d6a2o0,10,10,0|-cvmtg1,10,10,0|-cvmtg0,11,11,1|-cm2g81,11,11,1|-cm2g80,10,10,0|-cdmo41,10,10,0|-cdmo40,11,11,1|-c4kl81,11,11,1|-c4kl80,10,10,0|-bttjw1,10,10,0|-bttjw0,11,11,1|-blwd81,11,11,1|-blwd80,10,10,0|-bbtek1,10,10,0|-bbtek0,11,11,1|-b36ak1,11,11,1|-b36ak0,10,10,0|-atgak1,10,10,0|-atgak0,11,11,1|-akg7w1,11,11,1|-akg7w0,10,10,0|-6kf401,10,10,0|-6kf400,11,11,1|-6eaqo1,11,11,1|-6eaqo0,10,10,0|-64xpc1,10,10,0|-64xpc0,11,11,1|-5vko01,11,11,1|-5vko00,10,10,0|-5iyyo1,10,10,0|-5iyyo0,11,11,1|-5chmo1,11,11,1|-5chmo0,10,10,0|-534lc1,10,10,0|-534lc0,11,11,1|-4trk01,11,11,1|-4trk00,10,10,0|-4hitc1,10,10,0|-4hitc0,11,11,1|-4b1hc1,11,11,1|-4b1hc0,10,10,0|-3ysqo1,10,10,0|-3ysqo0,11,11,1|-3sbeo1,11,11,1|-3sbeo0,10,10,0|-3g2o01,10,10,0|-3g2o00,11,11,1|-39lc01,11,11,1|-39lc00,10,10,0|-2wzmo1,10,10,0|-2wzmo0,11,11,1|-2qv9c1,11,11,1|-2qv9c0,10,10,0|3s9jzz,10,10,0|3s9k00,11,11,1|419mnz,11,11,1|419mo0,10,10,0|4azmnz,10,10,0|4azmo0,11,11,1|4kcnzz,11,11,1|4kco00,10,10,0|4tppbz,10,10,0|4tppc0,11,11,1|532qnz,11,11,1|532qo0,10,10,0|5csqnz,10,10,0|5csqo0,11,11,1|5lstbz,11,11,1|5lstc0,10,10,0|5v5unz,10,10,0|5v5uo0,11,11,1|64ivzz,11,11,1|64iw00,10,10,0|6dvxbz,10,10,0|6dvxc0,11,11,1|6n8ynz,11,11,1|6n8yo0,10,10,0|6wlzzz,10,10,0|6wm000,11,11,1|75z1bz,11,11,1|75z1c0,10,10,0|7fc2nz,10,10,0|7fc2o0,11,11,1|7p22nz,11,11,1|7p22o0,10,10,0|7yf3zz,10,10,0|7yf400,11,11,1|87s5bz,11,11,1|87s5c0,10,10,0|8h56nz,10,10,0|8h56o0,11,11,1|8qi7zz,11,11,1|8qi800,10,10,0|8zv9bz,10,10,0|8zv9c0,11,11,1|998anz,11,11,1|998ao0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Zagreb|,0,332,0|-18vsmgo,10,10,0|-ezayw1,10,10,0|-ezayw0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-dfqqk1,10,10,0|-dfqqk0,11,11,1|-d6dp81,11,11,1|-d6dp80,10,10,0|-cv5zw1,10,10,0|-cv5zw0,11,11,1|-cofek1,11,11,1|-cofek0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Europe/Zaporozhye|,0,250,0|-1ayy96g,157,381,0|-nu12hd,157,381,0|-nu12hc,15,11,0|-kmr1k1,15,11,0|-kmr1k0,148,6,0|-esq0c1,148,6,0|-esq0c0,11,11,1|-e6dzw1,11,11,1|-e6dzw0,10,10,0|-dytrw1,10,10,0|-dytrw0,11,11,1|-dp3rw1,11,11,1|-dp3rw0,10,10,0|-do11g1,10,10,0|-do11g0,148,6,0|5vb6bz,148,6,0|5vb6c0,149,209,1|64pzjz,149,209,1|64pzk0,148,6,0|6e33nz,148,6,0|6e33o0,149,209,1|6nhwvz,149,209,1|6nhww0,148,6,0|6wv0zz,148,6,0|6wv100,149,209,1|769u7z,149,209,1|769u80,148,6,0|7foszz,148,6,0|7fot00,149,209,1|7p1zvz,149,209,1|7p1zw0,148,6,0|7yf17z,148,6,0|7yf180,149,209,1|87s2jz,149,209,1|87s2k0,148,6,0|8h53vz,148,6,0|8h53w0,149,209,1|8qi57z,149,209,1|8qi580,148,6,0|8zv6jz,148,6,0|8zv6k0,149,209,1|9987vz,149,209,1|9987w0,148,6,0|9il97z,148,6,0|9il980,149,209,1|9ryajz,149,209,1|9ryak0,148,6,0|a1bbvz,148,6,0|a1bbw0,149,209,1|aaod7z,149,209,1|aaod80,148,6,0|ak1ejz,148,6,0|ak1ek0,149,209,1|atrejz,149,209,1|atrek0,148,6,0|b34fvz,148,6,0|b34fw0,16,6,1|bchbnz,16,6,1|bchbo0,15,11,0|blufrz,15,11,0|blufs0,16,6,1|bv7ebz,16,6,1|bv7ec0,15,11,0|c4kifz,15,11,0|c4kig0,16,6,1|cdxgzz,16,6,1|cdxh00,15,11,0|cnal3z,15,11,0|cnal40,16,6,1|cwnjnz,16,6,1|cwnjo0,15,11,0|d60w3z,15,11,0|d60w40,16,6,1|dfdxfz,16,6,1|dfdxg0,15,11,0|dp3xfz,15,11,0|dp3xg0,16,6,1|dzwtfz,16,6,1|dzwtg0,15,11,0|e7u03z,15,11,0|e7u040,16,6,1|eimw3z,16,6,1|eimw40,15,11,0|eqk2rz,15,11,0|eqk2s0,16,6,1|f1cyrz,16,6,1|f1cys0,15,11,0|f9a5fz,15,11,0|f9a5g0,16,6,1|fkg03z,16,6,1|fkg040,15,11,0|fs083z,15,11,0|fs0840,16,6,1|g362rz,16,6,1|g362s0,15,11,0|gaqarz,15,11,0|gaqas0,16,6,1|glw5fz,16,6,1|glw5g0,15,11,0|gttc3z,15,11,0|gttc40,16,6,1|h4m83z,16,6,1|h4m840,15,11,0|hcjerz,15,11,0|hcjes0,16,6,1|hncarz,16,6,1|hncas0,15,11,0|hv9hfz,15,11,0|hv9hg0,16,6,1|i6fc3z,16,6,1|i6fc40,15,11,0|idzk3z,15,11,0|idzk40,16,6,1|ip5erz,16,6,1|ip5es0,15,11,0|iwpmrz,15,11,0|iwpms0,16,6,1|j7vhfz,16,6,1|j7vhg0,15,11,0|jffpfz,15,11,0|jffpg0,16,6,1|jqlk3z,16,6,1|jqlk40,15,11,0|jyiqrz,15,11,0|jyiqs0,16,6,1|k9bmrz,16,6,1|k9bms0,15,11,0|kh8tfz,15,11,0|kh8tg0,16,6,1|ks1pfz,16,6,1|ks1pg0,15,11,0|kzyw3z,15,11,0|kzyw40,16,6,1|lb4qrz,16,6,1|lb4qs0,15,11,0|lioyrz,15,11,0|lioys0,16,6,1|ltutfz,16,6,1|ltutg0,15,11,0|m1f1fz,15,11,0|m1f1g0,16,6,1|mckw3z,16,6,1|mckw40,15,11,0|mki2rz,15,11,0|mki2s0,16,6,1|mvayrz,16,6,1|mvays0,15,11,0|n385fz,15,11,0|n385g0,16,6,1|ne11fz,16,6,1|ne11g0,15,11,0|nly83z,15,11,0|nly840,16,6,1|nwr43z,16,6,1|nwr440,15,11,0|o4oarz,15,11,0|o4oas0,16,6,1|ofu5fz,16,6,1|ofu5g0,15,11,0|onedfz,15,11,0|onedg0,16,6,1|oyk83z,16,6,1|oyk840,15,11,0|p64g3z,15,11,0|p64g40,16,6,1|phaarz,16,6,1|phaas0,15,11,0|pp7hfz,15,11,0|pp7hg0,16,6,1|q00dfz,16,6,1|q00dg0,15,11,0|q7xk3z,15,11,0|q7xk40,16,6,1|qiqg3z,16,6,1|qiqg40,15,11,0|qqnmrz,15,11,0|qqnms0,16,6,1|r1thfz,16,6,1|r1thg0,15,11,0|r9dpfz,15,11,0|r9dpg0,16,6,1|rkjk3z,16,6,1|rkjk40,15,11,0|rs3s3z,15,11,0|rs3s40,16,6,1|s39mrz,16,6,1|s39ms0,15,11,0|sb6tfz,15,11,0|sb6tg0,16,6,1|slzpfz,16,6,1|slzpg0,15,11,0|stww3z,15,11,0|stww40,16,6,1|t4ps3z,16,6,1|t4ps40,15,11,0|tcmyrz,15,11,0|tcmys0,16,6,1|tnfurz,16,6,1|tnfus0,15,11,0|tvd1fz,15,11,0|tvd1g0,16,6,1|u6iw3z,16,6,1|u6iw40,15,11,0|ue343z,15,11,0|ue3440,16,6,1|up8yrz,16,6,1|up8ys0,15,11,0|uwt6rz,15,11,0|uwt6s0,16,6,1|v7z1fz,16,6,1|v7z1g0,15,11,0|vfw83z,15,11,0|vfw840,16,6,1|vqp43z,16,6,1|vqp440,15,11,0|vymarz,15,11,0|vymas0,16,6,1|w9f6rz,16,6,1|w9f6s0,15,11,0|whcdfz,15,11,0|whcdg0,16,6,1|wsi83z,16,6,1|wsi840,15,11,0|x02g3z,15,11,0|x02g40,16,6,1|xb8arz,16,6,1|xb8as0,15,11,0|xisirz,15,11,0|xisis0,16,6,1|xtydfz,16,6,1|xtydg0,15,11,0|y1ilfz,15,11,0|y1ilg0,16,6,1|ycog3z,16,6,1|ycog40,15,11,0|yklmrz,15,11,0|yklms0,16,6,1|yveirz,16,6,1|yveis0,15,11,0|z3bpfz,15,11,0|z3bpg0,16,6,1|ze4lfz,16,6,1|ze4lg0,15,11,0\",\"Europe/Zurich|,0,338,0|-1os49kw,53,339,0|-13g441n,53,339,0|-13g441m,10,10,0|-eyh6o1,10,10,0|-eyh6o0,11,11,1|-eqk001,11,11,1|-eqk000,10,10,0|-efr401,10,10,0|-efr400,11,11,1|-e7txc1,11,11,1|-e7txc0,10,10,0|5v5xfz,10,10,0|5v5xg0,11,11,1|64iyrz,11,11,1|64iys0,10,10,0|6dw03z,10,10,0|6dw040,11,11,1|6n91fz,11,11,1|6n91g0,10,10,0|6wm2rz,10,10,0|6wm2s0,11,11,1|75z43z,11,11,1|75z440,10,10,0|7fc5fz,10,10,0|7fc5g0,11,11,1|7p25fz,11,11,1|7p25g0,10,10,0|7yf6rz,10,10,0|7yf6s0,11,11,1|87s83z,11,11,1|87s840,10,10,0|8h59fz,10,10,0|8h59g0,11,11,1|8qiarz,11,11,1|8qias0,10,10,0|8zvc3z,10,10,0|8zvc40,11,11,1|998dfz,11,11,1|998dg0,10,10,0|9ilerz,10,10,0|9iles0,11,11,1|9ryg3z,11,11,1|9ryg40,10,10,0|a1bhfz,10,10,0|a1bhg0,11,11,1|aaoirz,11,11,1|aaois0,10,10,0|ak1k3z,10,10,0|ak1k40,11,11,1|atrk3z,11,11,1|atrk40,10,10,0|b34lfz,10,10,0|b34lg0,11,11,1|bchmrz,11,11,1|bchms0,10,10,0|bluo3z,10,10,0|bluo40,11,11,1|bv7pfz,11,11,1|bv7pg0,10,10,0|c4kqrz,10,10,0|c4kqs0,11,11,1|cdxs3z,11,11,1|cdxs40,10,10,0|cnatfz,10,10,0|cnatg0,11,11,1|cwnurz,11,11,1|cwnus0,10,10,0|d60w3z,10,10,0|d60w40,11,11,1|dfdxfz,11,11,1|dfdxg0,10,10,0|dp3xfz,10,10,0|dp3xg0,11,11,1|dzwtfz,11,11,1|dzwtg0,10,10,0|e7u03z,10,10,0|e7u040,11,11,1|eimw3z,11,11,1|eimw40,10,10,0|eqk2rz,10,10,0|eqk2s0,11,11,1|f1cyrz,11,11,1|f1cys0,10,10,0|f9a5fz,10,10,0|f9a5g0,11,11,1|fkg03z,11,11,1|fkg040,10,10,0|fs083z,10,10,0|fs0840,11,11,1|g362rz,11,11,1|g362s0,10,10,0|gaqarz,10,10,0|gaqas0,11,11,1|glw5fz,11,11,1|glw5g0,10,10,0|gttc3z,10,10,0|gttc40,11,11,1|h4m83z,11,11,1|h4m840,10,10,0|hcjerz,10,10,0|hcjes0,11,11,1|hncarz,11,11,1|hncas0,10,10,0|hv9hfz,10,10,0|hv9hg0,11,11,1|i6fc3z,11,11,1|i6fc40,10,10,0|idzk3z,10,10,0|idzk40,11,11,1|ip5erz,11,11,1|ip5es0,10,10,0|iwpmrz,10,10,0|iwpms0,11,11,1|j7vhfz,11,11,1|j7vhg0,10,10,0|jffpfz,10,10,0|jffpg0,11,11,1|jqlk3z,11,11,1|jqlk40,10,10,0|jyiqrz,10,10,0|jyiqs0,11,11,1|k9bmrz,11,11,1|k9bms0,10,10,0|kh8tfz,10,10,0|kh8tg0,11,11,1|ks1pfz,11,11,1|ks1pg0,10,10,0|kzyw3z,10,10,0|kzyw40,11,11,1|lb4qrz,11,11,1|lb4qs0,10,10,0|lioyrz,10,10,0|lioys0,11,11,1|ltutfz,11,11,1|ltutg0,10,10,0|m1f1fz,10,10,0|m1f1g0,11,11,1|mckw3z,11,11,1|mckw40,10,10,0|mki2rz,10,10,0|mki2s0,11,11,1|mvayrz,11,11,1|mvays0,10,10,0|n385fz,10,10,0|n385g0,11,11,1|ne11fz,11,11,1|ne11g0,10,10,0|nly83z,10,10,0|nly840,11,11,1|nwr43z,11,11,1|nwr440,10,10,0|o4oarz,10,10,0|o4oas0,11,11,1|ofu5fz,11,11,1|ofu5g0,10,10,0|onedfz,10,10,0|onedg0,11,11,1|oyk83z,11,11,1|oyk840,10,10,0|p64g3z,10,10,0|p64g40,11,11,1|phaarz,11,11,1|phaas0,10,10,0|pp7hfz,10,10,0|pp7hg0,11,11,1|q00dfz,11,11,1|q00dg0,10,10,0|q7xk3z,10,10,0|q7xk40,11,11,1|qiqg3z,11,11,1|qiqg40,10,10,0|qqnmrz,10,10,0|qqnms0,11,11,1|r1thfz,11,11,1|r1thg0,10,10,0|r9dpfz,10,10,0|r9dpg0,11,11,1|rkjk3z,11,11,1|rkjk40,10,10,0|rs3s3z,10,10,0|rs3s40,11,11,1|s39mrz,11,11,1|s39ms0,10,10,0|sb6tfz,10,10,0|sb6tg0,11,11,1|slzpfz,11,11,1|slzpg0,10,10,0|stww3z,10,10,0|stww40,11,11,1|t4ps3z,11,11,1|t4ps40,10,10,0|tcmyrz,10,10,0|tcmys0,11,11,1|tnfurz,11,11,1|tnfus0,10,10,0|tvd1fz,10,10,0|tvd1g0,11,11,1|u6iw3z,11,11,1|u6iw40,10,10,0|ue343z,10,10,0|ue3440,11,11,1|up8yrz,11,11,1|up8ys0,10,10,0|uwt6rz,10,10,0|uwt6s0,11,11,1|v7z1fz,11,11,1|v7z1g0,10,10,0|vfw83z,10,10,0|vfw840,11,11,1|vqp43z,11,11,1|vqp440,10,10,0|vymarz,10,10,0|vymas0,11,11,1|w9f6rz,11,11,1|w9f6s0,10,10,0|whcdfz,10,10,0|whcdg0,11,11,1|wsi83z,11,11,1|wsi840,10,10,0|x02g3z,10,10,0|x02g40,11,11,1|xb8arz,11,11,1|xb8as0,10,10,0|xisirz,10,10,0|xisis0,11,11,1|xtydfz,11,11,1|xtydg0,10,10,0|y1ilfz,10,10,0|y1ilg0,11,11,1|ycog3z,11,11,1|ycog40,10,10,0|yklmrz,10,10,0|yklms0,11,11,1|yveirz,11,11,1|yveis0,10,10,0|z3bpfz,10,10,0|z3bpg0,11,11,1|ze4lfz,11,11,1|ze4lg0,10,10,0\",\"Indian/Antananarivo|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Indian/Chagos|,0,382,0|-wvpc2s,92,194,0|dkgsrz,92,194,0|dkgss0,96,196,0\",\"Indian/Christmas|,0,383,0|-133iwws,91,193,0\",\"Indian/Cocos|,0,384,0|-10j6sm4,109,229,0\",\"Indian/Comoro|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Indian/Kerguelen|,60,1,0|-afrs00,92,194,0\",\"Indian/Mahe|,0,385,0|-wvp8xo,105,209,0\",\"Indian/Maldives|,0,386,0|-1ayyga0,21,386,0|-57x6y1,21,386,0|-57x6y0,92,194,0\",\"Indian/Mauritius|,0,387,0|-wvp9bc,105,209,0|6nykvz,105,209,0|6nykw0,92,194,1|6wai3z,92,194,1|6wai40,105,209,0|k9befz,105,209,0|k9beg0,92,194,1|kh8ibz,92,194,1|kh8ic0,105,209,0\",\"Indian/Mayotte|,0,4,0|-w6p5hg,4,5,0|-lnsey1,4,5,0|-lnsey0,5,6,0|-kvcdo1,5,6,0|-kvcdo0,4,5,0|-h80ka1,4,5,0|-h80ka0,6,7,0|-eb6ib1,6,7,0|-eb6ib0,5,6,0\",\"Indian/Reunion|,0,388,0|-uks29s,105,209,0\",\"Pacific/Apia|,0,389,0|-14fxxj4,0,390,0|-usiiv5,0,390,0|-usiiv4,158,391,0|-afqw21,158,391,0|-afqw20,159,35,0|l9cp7z,159,35,0|l9cp80,160,36,1|lj12vz,160,36,1|lj12w0,159,35,0|ls15jz,159,35,0|ls15k0,160,36,1|lx0h3z,160,36,1|lx0h40,104,207,1|m1r5jz,104,207,1|m1r5k0,103,201,0|mb46vz,103,201,0|mb46w0,104,207,1|mku6vz,104,207,1|mku6w0,103,201,0|mtu9jz,103,201,0|mtu9k0,104,207,1|n3k9jz,104,207,1|n3k9k0,103,201,0|nckc7z,103,201,0|nckc80,104,207,1|nmac7z,104,207,1|nmac80,103,201,0|nvaevz,103,201,0|nvaew0,104,207,1|o50evz,104,207,1|o50ew0,103,201,0|oe0hjz,103,201,0|oe0hk0,104,207,1|onqhjz,104,207,1|onqhk0,103,201,0|owqk7z,103,201,0|owqk80,104,207,1|p6gk7z,104,207,1|p6gk80,103,201,0|pftljz,103,201,0|pftlk0,104,207,1|ppjljz,104,207,1|ppjlk0,103,201,0|pyjo7z,103,201,0|pyjo80,104,207,1|q89o7z,104,207,1|q89o80,103,201,0|qh9qvz,103,201,0|qh9qw0,104,207,1|qqzqvz,104,207,1|qqzqw0,103,201,0|qzztjz,103,201,0|qzztk0,104,207,1|r9ptjz,104,207,1|r9ptk0,103,201,0|ripw7z,103,201,0|ripw80,104,207,1|rsfw7z,104,207,1|rsfw80,103,201,0|s1fyvz,103,201,0|s1fyw0,104,207,1|sbixjz,104,207,1|sbixk0,103,201,0|skj07z,103,201,0|skj080,104,207,1|su907z,104,207,1|su9080,103,201,0|t392vz,103,201,0|t392w0,104,207,1|tcz2vz,104,207,1|tcz2w0,103,201,0|tlz5jz,103,201,0|tlz5k0,104,207,1|tvp5jz,104,207,1|tvp5k0,103,201,0|u4p87z,103,201,0|u4p880,104,207,1|uef87z,104,207,1|uef880,103,201,0|unfavz,103,201,0|unfaw0,104,207,1|ux5avz,104,207,1|ux5aw0,103,201,0|v6ic7z,103,201,0|v6ic80,104,207,1|vg8c7z,104,207,1|vg8c80,103,201,0|vp8evz,103,201,0|vp8ew0,104,207,1|vyyevz,104,207,1|vyyew0,103,201,0|w7yhjz,103,201,0|w7yhk0,104,207,1|whohjz,104,207,1|whohk0,103,201,0|wqok7z,103,201,0|wqok80,104,207,1|x0ek7z,104,207,1|x0ek80,103,201,0|x9emvz,103,201,0|x9emw0,104,207,1|xj4mvz,104,207,1|xj4mw0,103,201,0|xs4pjz,103,201,0|xs4pk0,104,207,1|y1upjz,104,207,1|y1upk0,103,201,0|yb7qvz,103,201,0|yb7qw0,104,207,1|ykxqvz,104,207,1|ykxqw0,103,201,0|ytxtjz,103,201,0|ytxtk0,104,207,1|z3ntjz,104,207,1|z3ntk0,103,201,0|zcnw7z,103,201,0|zcnw80,104,207,1\",\"Pacific/Auckland|,0,197,0|-1gsoz14,97,198,0|-m01p21,97,198,0|-m01p20,98,199,1|-ltxei1,98,199,1|-ltxei0,97,198,0|-lieie1,97,198,0|-lieie0,98,200,1|-lahd41,98,200,1|-lahd40,97,198,0|-kzofq1,97,198,0|-kzofq0,98,200,1|-krrag1,98,200,1|-krrag0,97,198,0|-kgyd21,97,198,0|-kgyd20,98,200,1|-k917s1,98,200,1|-k917s0,97,198,0|-jy8ae1,97,198,0|-jy8ae0,98,200,1|-jpy6g1,98,200,1|-jpy6g0,97,198,0|-jfi7q1,97,198,0|-jfi7q0,98,200,1|-j783s1,98,200,1|-j783s0,97,198,0|-iws521,97,198,0|-iws520,98,200,1|-imc941,98,200,1|-imc940,97,198,0|-ief121,97,198,0|-ief120,98,200,1|-i3m6g1,98,200,1|-i3m6g0,97,198,0|-hvoye1,97,198,0|-hvoye0,98,200,1|-hkw3s1,98,200,1|-hkw3s0,97,198,0|-hcyvq1,97,198,0|-hcyvq0,98,200,1|-h26141,98,200,1|-h26140,97,198,0|-gu8t21,97,198,0|-gu8t20,98,200,1|-gjfyg1,98,200,1|-gjfyg0,97,198,0|-gbiqe1,97,198,0|-gbiqe0,98,200,1|-g0cx41,98,200,1|-g0cx40,97,198,0|-fssnq1,97,198,0|-fssnq0,98,200,1|-fhmug1,98,200,1|-fhmug0,97,198,0|-f9pme1,97,198,0|-f9pme0,98,200,1|-ciy9c1,98,200,1|-ciy9c0,98,200,0|2ivg7z,98,200,0|2ivg80,99,201,1|2omuvz,99,201,1|2omuw0,98,200,0|318k7z,98,200,0|318k80,99,201,1|382uvz,99,201,1|382uw0,98,200,0|3kbljz,98,200,0|3kblk0,99,201,1|3qsxjz,99,201,1|3qsxk0,98,200,0|431o7z,98,200,0|431o80,99,201,1|49j07z,99,201,1|49j080,98,200,0|4lrqvz,98,200,0|4lrqw0,99,201,1|4s92vz,99,201,1|4s92w0,98,200,0|54htjz,98,200,0|54htk0,99,201,1|5az5jz,99,201,1|5az5k0,98,200,0|5n7w7z,98,200,0|5n7w80,99,201,1|5tp87z,99,201,1|5tp880,98,200,0|65xyvz,98,200,0|65xyw0,99,201,1|6cs9jz,99,201,1|6cs9k0,98,200,0|6p107z,98,200,0|6p1080,99,201,1|6vic7z,99,201,1|6vic80,98,200,0|77r2vz,98,200,0|77r2w0,99,201,1|7e8evz,99,201,1|7e8ew0,98,200,0|7qh5jz,98,200,0|7qh5k0,99,201,1|7wyhjz,99,201,1|7wyhk0,98,200,0|89787z,98,200,0|897880,99,201,1|8fok7z,99,201,1|8fok80,98,200,0|8rxavz,98,200,0|8rxaw0,99,201,1|8yemvz,99,201,1|8yemw0,98,200,0|9andjz,98,200,0|9andk0,99,201,1|9hho7z,99,201,1|9hho80,98,200,0|9tqevz,98,200,0|9tqew0,99,201,1|a07qvz,99,201,1|a07qw0,98,200,0|abdljz,98,200,0|abdlk0,99,201,1|ajnqvz,99,201,1|ajnqw0,98,200,0|au3o7z,98,200,0|au3o80,99,201,1|b2dtjz,99,201,1|b2dtk0,98,200,0|bctqvz,98,200,0|bctqw0,99,201,1|bl3w7z,99,201,1|bl3w80,98,200,0|bvjtjz,98,200,0|bvjtk0,99,201,1|c46xjz,99,201,1|c46xk0,98,200,0|ce9w7z,98,200,0|ce9w80,99,201,1|cmx07z,99,201,1|cmx080,98,200,0|cwzyvz,98,200,0|cwzyw0,99,201,1|d5n2vz,99,201,1|d5n2w0,98,200,0|dfq1jz,98,200,0|dfq1k0,99,201,1|dod5jz,99,201,1|dod5k0,98,200,0|dyt2vz,98,200,0|dyt2w0,99,201,1|e7387z,99,201,1|e73880,98,200,0|ehj5jz,98,200,0|ehj5k0,99,201,1|eptavz,99,201,1|eptaw0,98,200,0|f0987z,98,200,0|f09880,99,201,1|f8wc7z,99,201,1|f8wc80,98,200,0|fizavz,98,200,0|fizaw0,99,201,1|frmevz,99,201,1|frmew0,98,200,0|g1pdjz,98,200,0|g1pdk0,99,201,1|gachjz,99,201,1|gachk0,98,200,0|gksevz,98,200,0|gksew0,99,201,1|gt2k7z,99,201,1|gt2k80,98,200,0|h3ihjz,98,200,0|h3ihk0,99,201,1|hbsmvz,99,201,1|hbsmw0,98,200,0|hm8k7z,98,200,0|hm8k80,99,201,1|huvo7z,99,201,1|huvo80,98,200,0|i4ymvz,98,200,0|i4ymw0,99,201,1|idlqvz,99,201,1|idlqw0,98,200,0|inopjz,98,200,0|inopk0,99,201,1|iwbtjz,99,201,1|iwbtk0,98,200,0|j6es7z,98,200,0|j6es80,99,201,1|jf1w7z,99,201,1|jf1w80,98,200,0|jp4uvz,98,200,0|jp4uw0,99,201,1|jyuuvz,99,201,1|jyuuw0,98,200,0|k7uxjz,98,200,0|k7uxk0,99,201,1|khkxjz,99,201,1|khkxk0,98,200,0|kql07z,98,200,0|kql080,99,201,1|l0b07z,99,201,1|l0b080,98,200,0|l9b2vz,98,200,0|l9b2w0,99,201,1|lj12vz,99,201,1|lj12w0,98,200,0|ls15jz,98,200,0|ls15k0,99,201,1|m1r5jz,99,201,1|m1r5k0,98,200,0|mb46vz,98,200,0|mb46w0,99,201,1|mku6vz,99,201,1|mku6w0,98,200,0|mtu9jz,98,200,0|mtu9k0,99,201,1|n3k9jz,99,201,1|n3k9k0,98,200,0|nckc7z,98,200,0|nckc80,99,201,1|nmac7z,99,201,1|nmac80,98,200,0|nvaevz,98,200,0|nvaew0,99,201,1|o50evz,99,201,1|o50ew0,98,200,0|oe0hjz,98,200,0|oe0hk0,99,201,1|onqhjz,99,201,1|onqhk0,98,200,0|owqk7z,98,200,0|owqk80,99,201,1|p6gk7z,99,201,1|p6gk80,98,200,0|pftljz,98,200,0|pftlk0,99,201,1|ppjljz,99,201,1|ppjlk0,98,200,0|pyjo7z,98,200,0|pyjo80,99,201,1|q89o7z,99,201,1|q89o80,98,200,0|qh9qvz,98,200,0|qh9qw0,99,201,1|qqzqvz,99,201,1|qqzqw0,98,200,0|qzztjz,98,200,0|qzztk0,99,201,1|r9ptjz,99,201,1|r9ptk0,98,200,0|ripw7z,98,200,0|ripw80,99,201,1|rsfw7z,99,201,1|rsfw80,98,200,0|s1fyvz,98,200,0|s1fyw0,99,201,1|sbixjz,99,201,1|sbixk0,98,200,0|skj07z,98,200,0|skj080,99,201,1|su907z,99,201,1|su9080,98,200,0|t392vz,98,200,0|t392w0,99,201,1|tcz2vz,99,201,1|tcz2w0,98,200,0|tlz5jz,98,200,0|tlz5k0,99,201,1|tvp5jz,99,201,1|tvp5k0,98,200,0|u4p87z,98,200,0|u4p880,99,201,1|uef87z,99,201,1|uef880,98,200,0|unfavz,98,200,0|unfaw0,99,201,1|ux5avz,99,201,1|ux5aw0,98,200,0|v6ic7z,98,200,0|v6ic80,99,201,1|vg8c7z,99,201,1|vg8c80,98,200,0|vp8evz,98,200,0|vp8ew0,99,201,1|vyyevz,99,201,1|vyyew0,98,200,0|w7yhjz,98,200,0|w7yhk0,99,201,1|whohjz,99,201,1|whohk0,98,200,0|wqok7z,98,200,0|wqok80,99,201,1|x0ek7z,99,201,1|x0ek80,98,200,0|x9emvz,98,200,0|x9emw0,99,201,1|xj4mvz,99,201,1|xj4mw0,98,200,0|xs4pjz,98,200,0|xs4pk0,99,201,1|y1upjz,99,201,1|y1upk0,98,200,0|yb7qvz,98,200,0|yb7qw0,99,201,1|ykxqvz,99,201,1|ykxqw0,98,200,0|ytxtjz,98,200,0|ytxtk0,99,201,1|z3ntjz,99,201,1|z3ntk0,98,200,0|zcnw7z,98,200,0|zcnw80,99,201,1\",\"Pacific/Bougainville|,0,392,0|-1ayyvh4,161,393,0|-1354j8x,161,393,0|-1354j8w,93,195,0|-ecsh41,93,195,0|-ecsh40,107,224,0|-cpsbo1,107,224,0|-cpsbo0,93,195,0|nh90fz,93,195,0|nh90g0,90,192,0\",\"Pacific/Chatham|,0,394,0|-1gsp0n0,162,395,0|-ciya11,162,395,0|-ciya10,163,396,0|2ivg7z,163,396,0|2ivg80,164,397,1|2omuvz,164,397,1|2omuw0,163,396,0|318k7z,163,396,0|318k80,164,397,1|382uvz,164,397,1|382uw0,163,396,0|3kbljz,163,396,0|3kblk0,164,397,1|3qsxjz,164,397,1|3qsxk0,163,396,0|431o7z,163,396,0|431o80,164,397,1|49j07z,164,397,1|49j080,163,396,0|4lrqvz,163,396,0|4lrqw0,164,397,1|4s92vz,164,397,1|4s92w0,163,396,0|54htjz,163,396,0|54htk0,164,397,1|5az5jz,164,397,1|5az5k0,163,396,0|5n7w7z,163,396,0|5n7w80,164,397,1|5tp87z,164,397,1|5tp880,163,396,0|65xyvz,163,396,0|65xyw0,164,397,1|6cs9jz,164,397,1|6cs9k0,163,396,0|6p107z,163,396,0|6p1080,164,397,1|6vic7z,164,397,1|6vic80,163,396,0|77r2vz,163,396,0|77r2w0,164,397,1|7e8evz,164,397,1|7e8ew0,163,396,0|7qh5jz,163,396,0|7qh5k0,164,397,1|7wyhjz,164,397,1|7wyhk0,163,396,0|89787z,163,396,0|897880,164,397,1|8fok7z,164,397,1|8fok80,163,396,0|8rxavz,163,396,0|8rxaw0,164,397,1|8yemvz,164,397,1|8yemw0,163,396,0|9andjz,163,396,0|9andk0,164,397,1|9hho7z,164,397,1|9hho80,163,396,0|9tqevz,163,396,0|9tqew0,164,397,1|a07qvz,164,397,1|a07qw0,163,396,0|abdljz,163,396,0|abdlk0,164,397,1|ajnqvz,164,397,1|ajnqw0,163,396,0|au3o7z,163,396,0|au3o80,164,397,1|b2dtjz,164,397,1|b2dtk0,163,396,0|bctqvz,163,396,0|bctqw0,164,397,1|bl3w7z,164,397,1|bl3w80,163,396,0|bvjtjz,163,396,0|bvjtk0,164,397,1|c46xjz,164,397,1|c46xk0,163,396,0|ce9w7z,163,396,0|ce9w80,164,397,1|cmx07z,164,397,1|cmx080,163,396,0|cwzyvz,163,396,0|cwzyw0,164,397,1|d5n2vz,164,397,1|d5n2w0,163,396,0|dfq1jz,163,396,0|dfq1k0,164,397,1|dod5jz,164,397,1|dod5k0,163,396,0|dyt2vz,163,396,0|dyt2w0,164,397,1|e7387z,164,397,1|e73880,163,396,0|ehj5jz,163,396,0|ehj5k0,164,397,1|eptavz,164,397,1|eptaw0,163,396,0|f0987z,163,396,0|f09880,164,397,1|f8wc7z,164,397,1|f8wc80,163,396,0|fizavz,163,396,0|fizaw0,164,397,1|frmevz,164,397,1|frmew0,163,396,0|g1pdjz,163,396,0|g1pdk0,164,397,1|gachjz,164,397,1|gachk0,163,396,0|gksevz,163,396,0|gksew0,164,397,1|gt2k7z,164,397,1|gt2k80,163,396,0|h3ihjz,163,396,0|h3ihk0,164,397,1|hbsmvz,164,397,1|hbsmw0,163,396,0|hm8k7z,163,396,0|hm8k80,164,397,1|huvo7z,164,397,1|huvo80,163,396,0|i4ymvz,163,396,0|i4ymw0,164,397,1|idlqvz,164,397,1|idlqw0,163,396,0|inopjz,163,396,0|inopk0,164,397,1|iwbtjz,164,397,1|iwbtk0,163,396,0|j6es7z,163,396,0|j6es80,164,397,1|jf1w7z,164,397,1|jf1w80,163,396,0|jp4uvz,163,396,0|jp4uw0,164,397,1|jyuuvz,164,397,1|jyuuw0,163,396,0|k7uxjz,163,396,0|k7uxk0,164,397,1|khkxjz,164,397,1|khkxk0,163,396,0|kql07z,163,396,0|kql080,164,397,1|l0b07z,164,397,1|l0b080,163,396,0|l9b2vz,163,396,0|l9b2w0,164,397,1|lj12vz,164,397,1|lj12w0,163,396,0|ls15jz,163,396,0|ls15k0,164,397,1|m1r5jz,164,397,1|m1r5k0,163,396,0|mb46vz,163,396,0|mb46w0,164,397,1|mku6vz,164,397,1|mku6w0,163,396,0|mtu9jz,163,396,0|mtu9k0,164,397,1|n3k9jz,164,397,1|n3k9k0,163,396,0|nckc7z,163,396,0|nckc80,164,397,1|nmac7z,164,397,1|nmac80,163,396,0|nvaevz,163,396,0|nvaew0,164,397,1|o50evz,164,397,1|o50ew0,163,396,0|oe0hjz,163,396,0|oe0hk0,164,397,1|onqhjz,164,397,1|onqhk0,163,396,0|owqk7z,163,396,0|owqk80,164,397,1|p6gk7z,164,397,1|p6gk80,163,396,0|pftljz,163,396,0|pftlk0,164,397,1|ppjljz,164,397,1|ppjlk0,163,396,0|pyjo7z,163,396,0|pyjo80,164,397,1|q89o7z,164,397,1|q89o80,163,396,0|qh9qvz,163,396,0|qh9qw0,164,397,1|qqzqvz,164,397,1|qqzqw0,163,396,0|qzztjz,163,396,0|qzztk0,164,397,1|r9ptjz,164,397,1|r9ptk0,163,396,0|ripw7z,163,396,0|ripw80,164,397,1|rsfw7z,164,397,1|rsfw80,163,396,0|s1fyvz,163,396,0|s1fyw0,164,397,1|sbixjz,164,397,1|sbixk0,163,396,0|skj07z,163,396,0|skj080,164,397,1|su907z,164,397,1|su9080,163,396,0|t392vz,163,396,0|t392w0,164,397,1|tcz2vz,164,397,1|tcz2w0,163,396,0|tlz5jz,163,396,0|tlz5k0,164,397,1|tvp5jz,164,397,1|tvp5k0,163,396,0|u4p87z,163,396,0|u4p880,164,397,1|uef87z,164,397,1|uef880,163,396,0|unfavz,163,396,0|unfaw0,164,397,1|ux5avz,164,397,1|ux5aw0,163,396,0|v6ic7z,163,396,0|v6ic80,164,397,1|vg8c7z,164,397,1|vg8c80,163,396,0|vp8evz,163,396,0|vp8ew0,164,397,1|vyyevz,164,397,1|vyyew0,163,396,0|w7yhjz,163,396,0|w7yhk0,164,397,1|whohjz,164,397,1|whohk0,163,396,0|wqok7z,163,396,0|wqok80,164,397,1|x0ek7z,164,397,1|x0ek80,163,396,0|x9emvz,163,396,0|x9emw0,164,397,1|xj4mvz,164,397,1|xj4mw0,163,396,0|xs4pjz,163,396,0|xs4pk0,164,397,1|y1upjz,164,397,1|y1upk0,163,396,0|yb7qvz,163,396,0|yb7qw0,164,397,1|ykxqvz,164,397,1|ykxqw0,163,396,0|ytxtjz,163,396,0|ytxtk0,164,397,1|z3ntjz,164,397,1|z3ntk0,163,396,0|zcnw7z,163,396,0|zcnw80,164,397,1\",\"Pacific/Chuuk|,0,398,0|-1t8j2rw,0,399,0|-100f5fx,0,399,0|-100f5fw,93,195,0|-su4zs1,93,195,0|-su4zs0,107,224,0|-qknl01,107,224,0|-qknl00,93,195,0|-f08x41,93,195,0|-f08x40,107,224,0|-cqtd01,107,224,0|-cqtd00,93,195,0\",\"Pacific/Easter|,0,400,0|-15r0p2w,165,400,0|-jhfaex,165,400,0|-jhfaew,166,66,0|-lsvk1,166,66,0|-lsvk0,167,62,1|-e8qc1,167,62,1|-e8qc0,166,66,0|-1zww1,166,66,0|-1zww0,167,62,1|4hcbz,167,62,1|4hcc0,166,66,0|ekdrz,166,66,0|ekds0,167,62,1|mhhnz,167,62,1|mhho0,166,66,0|xagfz,166,66,0|xagg0,167,62,1|157kbz,167,62,1|157kc0,166,66,0|1gdhrz,166,66,0|1gdhs0,167,62,1|1nxmzz,167,62,1|1nxn00,166,66,0|1ydn3z,166,66,0|1ydn40,167,62,1|26npnz,167,62,1|26npo0,166,66,0|2htn3z,166,66,0|2htn40,167,62,1|2pdsbz,167,62,1|2pdsc0,166,66,0|30jprz,166,66,0|30jps0,167,62,1|38gtnz,167,62,1|38gto0,166,66,0|3j9sfz,166,66,0|3j9sg0,167,62,1|3r6wbz,167,62,1|3r6wc0,166,66,0|41zv3z,166,66,0|41zv40,167,62,1|49wyzz,167,62,1|49wz00,166,66,0|4l2wfz,166,66,0|4l2wg0,167,62,1|4sn1nz,167,62,1|4sn1o0,166,66,0|53sz3z,166,66,0|53sz40,167,62,1|5bd4bz,167,62,1|5bd4c0,166,66,0|5mj1rz,166,66,0|5mj1s0,167,62,1|5ug5nz,167,62,1|5ug5o0,166,66,0|6594fz,166,66,0|6594g0,167,62,1|6d68bz,167,62,1|6d68c0,167,62,0|6nz73z,167,62,0|6nz740,56,63,1|6vwazz,56,63,1|6vwb00,167,62,0|76p9rz,167,62,0|76p9s0,56,63,1|7emdnz,56,63,1|7emdo0,167,62,0|7psb3z,167,62,0|7psb40,56,63,1|7xcgbz,56,63,1|7xcgc0,167,62,0|88idrz,167,62,0|88ids0,56,63,1|8g2izz,56,63,1|8g2j00,167,62,0|8r8gfz,167,62,0|8r8gg0,56,63,1|90lezz,56,63,1|90lf00,167,62,0|99yj3z,167,62,0|99yj40,56,63,1|9hvmzz,56,63,1|9hvn00,167,62,0|9solrz,167,62,0|9sols0,56,63,1|a0lpnz,56,63,1|a0lpo0,167,62,0|abrn3z,167,62,0|abrn40,56,63,1|ajbsbz,56,63,1|ajbsc0,167,62,0|at1v3z,167,62,0|at1v40,56,63,1|b21uzz,56,63,1|b21v00,167,62,0|bd7sfz,167,62,0|bd7sg0,56,63,1|bl4wbz,56,63,1|bl4wc0,167,62,0|bvxv3z,167,62,0|bvxv40,56,63,1|c3uyzz,56,63,1|c3uz00,167,62,0|cenxrz,167,62,0|cenxs0,56,63,1|cml1nz,56,63,1|cml1o0,167,62,0|cxe0fz,167,62,0|cxe0g0,56,63,1|d5b4bz,56,63,1|d5b4c0,167,62,0|dgh1rz,167,62,0|dgh1s0,56,63,1|do16zz,56,63,1|do1700,167,62,0|dz74fz,167,62,0|dz74g0,56,63,1|e7u5nz,56,63,1|e7u5o0,167,62,0|ehx73z,167,62,0|ehx740,56,63,1|epuazz,56,63,1|epub00,167,62,0|ezxcfz,167,62,0|ezxcg0,56,63,1|f9n9nz,56,63,1|f9n9o0,167,62,0|fjdcfz,167,62,0|fjdcg0,56,63,1|fragbz,56,63,1|fragc0,167,62,0|g2gdrz,167,62,0|g2gds0,56,63,1|ga0izz,56,63,1|ga0j00,167,62,0|gl6gfz,167,62,0|gl6gg0,56,63,1|gsqlnz,56,63,1|gsqlo0,167,62,0|h3wj3z,167,62,0|h3wj40,56,63,1|hbgobz,56,63,1|hbgoc0,167,62,0|hmmlrz,167,62,0|hmmls0,56,63,1|hujpnz,56,63,1|hujpo0,167,62,0|i5cofz,167,62,0|i5cog0,56,63,1|id9sbz,56,63,1|id9sc0,167,62,0|io2r3z,167,62,0|io2r40,56,63,1|ivzuzz,56,63,1|ivzv00,167,62,0|j75sfz,167,62,0|j75sg0,56,63,1|jepxnz,56,63,1|jepxo0,167,62,0|jpvv3z,167,62,0|jpvv40,56,63,1|jyiwbz,56,63,1|jyiwc0,167,62,0|k8lxrz,167,62,0|k8lxs0,56,63,1|kgj1nz,56,63,1|kgj1o0,167,62,0|krc0fz,167,62,0|krc0g0,56,63,1|l0c0bz,56,63,1|l0c0c0,167,62,0|la233z,167,62,0|la2340,56,63,1|lkuwbz,56,63,1|lkuwc0,167,62,0|lq9f3z,167,62,0|lq9f40,56,63,1|m380bz,56,63,1|m380c0,167,62,0|m9pf3z,167,62,0|m9pf40,56,63,1|mly2zz,56,63,1|mly300,167,62,0|mssgfz,167,62,0|mssgg0,56,63,1|n4o5nz,56,63,1|n4o5o0,167,62,0|nbij3z,167,62,0|nbij40,56,63,1|o776zz,56,63,1|o77700,167,62,0|obvsfz,167,62,0|obvsg0,56,63,1|opx9nz,56,63,1|opx9o0,167,62,0|oulv3z,167,62,0|oulv40,56,63,1|p8ncbz,56,63,1|p8ncc0,167,62,0|pdbxrz,167,62,0|pdbxs0,56,63,1|ppklnz,56,63,1|ppklo0,167,62,0|pxhv3z,167,62,0|pxhv40,56,63,1|q8aobz,56,63,1|q8aoc0,167,62,0|qg7xrz,167,62,0|qg7xs0,56,63,1|qr0qzz,56,63,1|qr0r00,167,62,0|qyy0fz,167,62,0|qyy0g0,56,63,1|r9qtnz,56,63,1|r9qto0,167,62,0|rho33z,167,62,0|rho340,56,63,1|rsgwbz,56,63,1|rsgwc0,167,62,0|s0e5rz,167,62,0|s0e5s0,56,63,1|sbjxnz,56,63,1|sbjxo0,167,62,0|sjh73z,167,62,0|sjh740,56,63,1|sua0bz,56,63,1|sua0c0,167,62,0|t279rz,167,62,0|t279s0,56,63,1|td02zz,56,63,1|td0300,167,62,0|tkxcfz,167,62,0|tkxcg0,56,63,1|tvq5nz,56,63,1|tvq5o0,167,62,0|u3nf3z,167,62,0|u3nf40,56,63,1|ueg8bz,56,63,1|ueg8c0,167,62,0|umdhrz,167,62,0|umdhs0,56,63,1|uxj9nz,56,63,1|uxj9o0,167,62,0|v53kfz,167,62,0|v53kg0,56,63,1|vg9cbz,56,63,1|vg9cc0,167,62,0|vo6lrz,167,62,0|vo6ls0,56,63,1|vyzezz,56,63,1|vyzf00,167,62,0|w6wofz,167,62,0|w6wog0,56,63,1|whphnz,56,63,1|whpho0,167,62,0|wpmr3z,167,62,0|wpmr40,56,63,1|x0fkbz,56,63,1|x0fkc0,167,62,0|x8ctrz,167,62,0|x8cts0,56,63,1|xj5mzz,56,63,1|xj5n00,167,62,0|xr2wfz,167,62,0|xr2wg0,56,63,1|y28obz,56,63,1|y28oc0,167,62,0|y9sz3z,167,62,0|y9sz40,56,63,1|ykyqzz,56,63,1|ykyr00,167,62,0|ysw0fz,167,62,0|ysw0g0,56,63,1|z3otnz,56,63,1|z3oto0,167,62,0|zbm33z,167,62,0|zbm340,56,63,1\",\"Pacific/Efate|,0,401,0|-u964i4,90,192,0|22nynz,90,192,0|22nyo0,102,200,1|27pfzz,102,200,1|27pg00,90,192,0|75y6rz,90,192,0|75y6s0,102,200,1|7fb5bz,102,200,1|7fb5c0,90,192,0|7oo9fz,90,192,0|7oo9g0,102,200,1|7y17zz,102,200,1|7y1800,90,192,0|87rarz,90,192,0|87ras0,102,200,1|8granz,102,200,1|8grao0,90,192,0|8qhdfz,90,192,0|8qhdg0,102,200,1|8zubzz,102,200,1|8zuc00,90,192,0|997g3z,90,192,0|997g40,102,200,1|9ikenz,102,200,1|9ikeo0,90,192,0|9rxirz,90,192,0|9rxis0,102,200,1|a1ahbz,102,200,1|a1ahc0,90,192,0|aanlfz,90,192,0|aanlg0,102,200,1|ak0jzz,102,200,1|ak0k00,90,192,0|atdo3z,90,192,0|atdo40,102,200,1|b2qmnz,102,200,1|b2qmo0,90,192,0|bcgpfz,90,192,0|bcgpg0,102,200,1|bikzzz,102,200,1|bil000,90,192,0|bwmmrz,90,192,0|bwmms0,102,200,1|c1b2nz,102,200,1|c1b2o0,90,192,0\",\"Pacific/Enderbury|,0,402,0|-100dhng,168,403,0|535inz,168,403,0|535io0,159,35,0|d1o97z,159,35,0|d1o980,103,201,0\",\"Pacific/Fakaofo|,0,404,0|-100dhmg,159,35,0|lx0jvz,159,35,0|lx0jw0,103,201,0\",\"Pacific/Fiji|,0,405,0|-sa2x4w,102,200,0|f1p2vz,102,200,0|f1p2w0,103,201,1|f7tg7z,103,201,1|f7tg80,102,200,0|fks47z,102,200,0|fks480,103,201,1|fqjivz,103,201,1|fqjiw0,102,200,0|ktto7z,102,200,0|ktto80,103,201,1|kzy1jz,103,201,1|kzy1k0,102,200,0|laqxjz,102,200,0|laqxk0,103,201,1|lhl87z,103,201,1|lhl880,102,200,0|lth07z,102,200,0|lth080,103,201,1|ly5ivz,103,201,1|ly5iw0,102,200,0|mc72vz,102,200,0|mc72w0,103,201,1|mgvljz,103,201,1|mgvlk0,102,200,0|mva47z,102,200,0|mva480,103,201,1|mzllfz,103,201,1|mzllg0,102,200,0|ned5jz,102,200,0|ned5k0,103,201,1|nibqvz,103,201,1|nibqw0,102,200,0|nx387z,102,200,0|nx3880,103,201,1|o11tjz,103,201,1|o11tk0,102,200,0|og69jz,102,200,0|og69k0,103,201,1|ojrw7z,103,201,1|ojrw80,102,200,0|oywc7z,102,200,0|oywc80,103,201,1|p2hyvz,103,201,1|p2hyw0,102,200,0|phmevz,102,200,0|phmew0,103,201,1|pl81jz,103,201,1|pl81k0,102,200,0|q0pg7z,102,200,0|q0pg80,103,201,1|q3y47z,103,201,1|q3y480,102,200,0|qllavz,102,200,0|qllaw0,103,201,1|qn15jz,103,201,1|qn15k0,102,200,0|r2ik7z,102,200,0|r2ik80,103,201,1|r5r87z,103,201,1|r5r880,102,200,0|rl8mvz,102,200,0|rl8mw0,103,201,1|rohavz,103,201,1|rohaw0,102,200,0|s3ypjz,102,200,0|s3ypk0,103,201,1|s77djz,103,201,1|s77dk0,102,200,0|smos7z,102,200,0|smos80,103,201,1|spxg7z,103,201,1|spxg80,102,200,0|t5euvz,102,200,0|t5euw0,103,201,1|t90hjz,103,201,1|t90hk0,102,200,0|to4xjz,102,200,0|to4xk0,103,201,1|trqk7z,103,201,1|trqk80,102,200,0|u77yvz,102,200,0|u77yw0,103,201,1|uagmvz,103,201,1|uagmw0,102,200,0|upy1jz,102,200,0|upy1k0,103,201,1|ut6pjz,103,201,1|ut6pk0,102,200,0|v8o47z,102,200,0|v8o480,103,201,1|vbws7z,103,201,1|vbws80,102,200,0|vre6vz,102,200,0|vre6w0,103,201,1|vumuvz,103,201,1|vumuw0,102,200,0|wa49jz,102,200,0|wa49k0,103,201,1|wdpw7z,103,201,1|wdpw80,102,200,0|wt7avz,102,200,0|wt7aw0,103,201,1|wwfyvz,103,201,1|wwfyw0,102,200,0|xbxdjz,102,200,0|xbxdk0,103,201,1|xf61jz,103,201,1|xf61k0,102,200,0|xung7z,102,200,0|xung80,103,201,1|xxw47z,103,201,1|xxw480,102,200,0|yddivz,102,200,0|yddiw0,103,201,1|ygm6vz,103,201,1|ygm6w0,102,200,0|yw3ljz,102,200,0|yw3lk0,103,201,1|yzp87z,103,201,1|yzp880,102,200,0|zeto7z,102,200,0|zeto80,103,201,1\",\"Pacific/Funafuti|,0,406,0|-100fais,102,200,0\",\"Pacific/Galapagos|,0,407,0|-kcr62o,56,63,0|8cmlvz,56,63,0|8cmlw0,167,62,0|byewnz,167,62,0|byewo0,56,63,1|c1ylvz,56,63,1|c1ylw0,167,62,0\",\"Pacific/Gambier|,0,408,0|-tvndoc,169,37,0\",\"Pacific/Guadalcanal|,0,409,0|-tvowac,90,192,0\",\"Pacific/Guam|,0,410,0|-1t8j1h0,0,411,0|-100f451,0,411,0|-100f450,170,195,0|-en8eg1,170,195,0|-en8eg0,107,224,0|-d9n501,107,224,0|-d9n500,170,195,0|-5hlkw1,170,195,0|-5hlkw0,171,192,1|-4nnvo1,171,192,1|-4nnvo0,170,195,0|-17w8w1,170,195,0|-17w8w0,171,192,1|-hih6d,171,192,1|-hih6c,170,195,0|-9y0w1,170,195,0|-9y0w0,171,192,1|-6ch01,171,192,1|-6ch00,170,195,0|5wcfz,170,195,0|5wcg0,171,192,1|cqkbz,171,192,1|cqkc0,170,195,0|omf3z,170,195,0|omf40,171,192,1|vgmzz,171,192,1|vgn00,170,195,0|22bb3z,170,195,0|22bb40,171,192,1|25wuzz,171,192,1|25wv00,170,195,0|3c75rz,170,195,0|3c75s0,171,192,1|3gq1pn,171,192,1|3gq1po,170,195,0|3tbtrz,170,195,0|3tbts0,171,192,1|3zt2zz,171,192,1|3zt300,170,195,0|g5z2vz,170,195,0|g5z2w0,172,195,0\",\"Pacific/Honolulu|,0,412,0|-12lnw3m,30,413,0|-j50la1,30,413,0|-j50la0,31,414,1|-j3x0a1,31,414,1|-j3x0a0,30,413,0|-ek1pa1,30,413,0|-ek1pa0,173,414,1|-cq2tg1,173,414,1|-cq2tg0,174,414,1|-cnoo21,174,414,1|-cnoo20,30,413,0|-brzum1,30,413,0|-brzum0,30,36,0\",\"Pacific/Kiritimati|,0,415,0|-100dk74,175,416,0|535eyn,175,416,0|535eyo,160,36,0|d1o6fz,160,36,0|d1o6g0,104,207,0\",\"Pacific/Kosrae|,0,417,0|-1t8j4uk,0,418,0|-100f7il,0,418,0|-100f7ik,90,192,0|-su52k1,90,192,0|-su52k0,107,224,0|-qknl01,107,224,0|-qknl00,90,192,0|-h817w1,90,192,0|-h817w0,93,195,0|-f08x41,93,195,0|-f08x40,107,224,0|-cqtd01,107,224,0|-cqtd00,90,192,0|-4r7w1,90,192,0|-4r7w0,102,200,0|f4tvzz,102,200,0|f4tw00,90,192,0\",\"Pacific/Kwajalein|,0,419,0|-100f8bk,90,192,0|-h817w1,90,192,0|-h817w0,93,195,0|-f08x41,93,195,0|-f08x40,107,224,0|-dip2c1,107,224,0|-dip2c0,90,192,0|-4r7w1,90,192,0|-4r7w0,168,403,0|cc3ynz,168,403,0|cc3yo0,102,200,0\",\"Pacific/Majuro|,0,420,0|-100f91c,90,192,0|-su52k1,90,192,0|-su52k0,107,224,0|-qknl01,107,224,0|-qknl00,90,192,0|-h817w1,90,192,0|-h817w0,93,195,0|-f08x41,93,195,0|-f08x40,107,224,0|-dj2101,107,224,0|-dj2100,90,192,0|-4r7w1,90,192,0|-4r7w0,102,200,0\",\"Pacific/Marquesas|,0,421,0|-tvncu0,176,414,0\",\"Pacific/Midway|,0,422,0|-14fxxq0,0,423,0|-usij21,0,423,0|-usij20,177,35,0\",\"Pacific/Nauru|,0,424,0|-pjxiws,143,198,0|-e9rby1,143,198,0|-e9rby0,107,224,0|-couzo1,107,224,0|-couzo0,143,198,0|4r4dlz,143,198,0|4r4dm0,102,200,0\",\"Pacific/Niue|,0,425,0|-100dhv8,178,426,0|-9wyz6p,178,426,0|-9wyz6o,158,391,0|4kdjxz,158,391,0|4kdjy0,159,35,0\",\"Pacific/Norfolk|,0,427,0|-100f8fs,179,428,0|-9x0ps1,179,428,0|-9x0ps0,143,198,0|2iiixz,143,198,0|2iiiy0,180,199,1|2ozuxz,180,199,1|2ozuy0,143,198,0|nvnexz,143,198,0|nvney0,90,192,0|pywpnz,90,192,0|pywpo0,102,200,1|q89qzz,102,200,1|q89r00,90,192,0|qhmsbz,90,192,0|qhmsc0,102,200,1|qqztnz,102,200,1|qqzto0,90,192,0|r0cuzz,90,192,0|r0cv00,102,200,1|r9pwbz,102,200,1|r9pwc0,90,192,0|rj2xnz,90,192,0|rj2xo0,102,200,1|rsfyzz,102,200,1|rsfz00,90,192,0|s1t0bz,90,192,0|s1t0c0,102,200,1|sbj0bz,102,200,1|sbj0c0,90,192,0|skw1nz,90,192,0|skw1o0,102,200,1|su92zz,102,200,1|su9300,90,192,0|t3m4bz,90,192,0|t3m4c0,102,200,1|tcz5nz,102,200,1|tcz5o0,90,192,0|tmc6zz,90,192,0|tmc700,102,200,1|tvp8bz,102,200,1|tvp8c0,90,192,0|u529nz,90,192,0|u529o0,102,200,1|uefazz,102,200,1|uefb00,90,192,0|unscbz,90,192,0|unscc0,102,200,1|ux5dnz,102,200,1|ux5do0,90,192,0|v6vdnz,90,192,0|v6vdo0,102,200,1|vg8ezz,102,200,1|vg8f00,90,192,0|vplgbz,90,192,0|vplgc0,102,200,1|vyyhnz,102,200,1|vyyho0,90,192,0|w8bizz,90,192,0|w8bj00,102,200,1|whokbz,102,200,1|whokc0,90,192,0|wr1lnz,90,192,0|wr1lo0,102,200,1|x0emzz,102,200,1|x0en00,90,192,0|x9robz,90,192,0|x9roc0,102,200,1|xj4pnz,102,200,1|xj4po0,90,192,0|xshqzz,90,192,0|xshr00,102,200,1|y1usbz,102,200,1|y1usc0,90,192,0|ybksbz,90,192,0|ybksc0,102,200,1|ykxtnz,102,200,1|ykxto0,90,192,0|yuauzz,90,192,0|yuav00,102,200,1|z3nwbz,102,200,1|z3nwc0,90,192,0|zd0xnz,90,192,0|zd0xo0,102,200,1\",\"Pacific/Noumea|,0,429,0|-u9645o,90,192,0|44uerz,90,192,0|44ues0,102,200,1|497qnz,102,200,1|497qo0,90,192,0|4nkhfz,90,192,0|4nkhg0,102,200,1|4rznzz,102,200,1|4rzo00,90,192,0|e1ouzz,90,192,0|e1ov00,102,200,1|e6ddnz,102,200,1|e6ddo0,90,192,0\",\"Pacific/Pago_Pago|,0,422,0|-14fxxq0,0,423,0|-usij21,0,423,0|-usij20,177,35,0\",\"Pacific/Palau|,0,430,0|-1t8izkk,0,431,0|-100f28l,0,431,0|-100f28k,107,224,0\",\"Pacific/Pitcairn|,0,432,0|-100dp8s,181,433,0|es2cxz,181,433,0|es2cy0,182,40,0\",\"Pacific/Pohnpei|,0,434,0|-1t8j3ys,0,435,0|-100f6mt,0,435,0|-100f6ms,90,192,0|-su52k1,90,192,0|-su52k0,107,224,0|-qknl01,107,224,0|-qknl00,90,192,0|-h817w1,90,192,0|-h817w0,93,195,0|-f08x41,93,195,0|-f08x40,107,224,0|-cqtd01,107,224,0|-cqtd00,90,192,0\",\"Pacific/Port_Moresby|,0,436,0|-1ayytx4,161,393,0|-1354j8x,161,393,0|-1354j8w,93,195,0\",\"Pacific/Rarotonga|,0,437,0|-100djqw,183,413,0|4mj95z,183,413,0|4mj960,176,414,1|4sal1z,176,414,1|4sal20,160,36,0|54jd3z,160,36,0|54jd40,176,414,1|5b0npz,176,414,1|5b0nq0,160,36,0|5n9frz,160,36,0|5n9fs0,176,414,1|5tqqdz,176,414,1|5tqqe0,160,36,0|65zifz,160,36,0|65zig0,176,414,1|6ctrpz,176,414,1|6ctrq0,160,36,0|6p2jrz,160,36,0|6p2js0,176,414,1|6vjudz,176,414,1|6vjue0,160,36,0|77smfz,160,36,0|77smg0,176,414,1|7e9x1z,176,414,1|7e9x20,160,36,0|7qip3z,160,36,0|7qip40,176,414,1|7wzzpz,176,414,1|7wzzq0,160,36,0|898rrz,160,36,0|898rs0,176,414,1|8fq2dz,176,414,1|8fq2e0,160,36,0|8ryufz,160,36,0|8ryug0,176,414,1|8yg51z,176,414,1|8yg520,160,36,0|9aox3z,160,36,0|9aox40,176,414,1|9hj6dz,176,414,1|9hj6e0,160,36,0|9tryfz,160,36,0|9tryg0,176,414,1|a0991z,176,414,1|a09920,160,36,0|aci13z,160,36,0|aci140,176,414,1|aizbpz,176,414,1|aizbq0,160,36,0|av83rz,160,36,0|av83s0,176,414,1|b1pedz,176,414,1|b1pee0,160,36,0\",\"Pacific/Saipan|,0,410,0|-1t8j1h0,0,411,0|-100f451,0,411,0|-100f450,170,195,0|-en8eg1,170,195,0|-en8eg0,107,224,0|-d9n501,107,224,0|-d9n500,170,195,0|-5hlkw1,170,195,0|-5hlkw0,171,192,1|-4nnvo1,171,192,1|-4nnvo0,170,195,0|-17w8w1,170,195,0|-17w8w0,171,192,1|-hih6d,171,192,1|-hih6c,170,195,0|-9y0w1,170,195,0|-9y0w0,171,192,1|-6ch01,171,192,1|-6ch00,170,195,0|5wcfz,170,195,0|5wcg0,171,192,1|cqkbz,171,192,1|cqkc0,170,195,0|omf3z,170,195,0|omf40,171,192,1|vgmzz,171,192,1|vgn00,170,195,0|22bb3z,170,195,0|22bb40,171,192,1|25wuzz,171,192,1|25wv00,170,195,0|3c75rz,170,195,0|3c75s0,171,192,1|3gq1pn,171,192,1|3gq1po,170,195,0|3tbtrz,170,195,0|3tbts0,171,192,1|3zt2zz,171,192,1|3zt300,170,195,0|g5z2vz,170,195,0|g5z2w0,172,195,0\",\"Pacific/Tahiti|,0,438,0|-tvnayw,160,36,0\",\"Pacific/Tarawa|,0,439,0|-100f9dg,102,200,0\",\"Pacific/Tongatapu|,0,440,0|-100fbk8,184,441,0|-f4vrld,184,441,0|-f4vrlc,103,201,0|fj6mrz,103,201,0|fj6ms0,104,207,1|frmc3z,104,207,1|frmc40,103,201,0|g3i43z,103,201,0|g3i440,104,207,1|g7tlbz,104,207,1|g7tlc0,103,201,0|gm86rz,103,201,0|gm86s0,104,207,1|gqjnzz,104,207,1|gqjo00,103,201,0|og66rz,103,201,0|og66s0,104,207,1|ojrtfz,104,207,1|ojrtg0,103,201,0\",\"Pacific/Wake|,0,442,0|-100f86s,102,200,0\",\"Pacific/Wallis|,0,443,0|-100fbdk,102,200,0\"],\"abbrvs\":\"LMT|GMT|+0020|+0030|+0230|EAT|+0245|PMT|WET|WEST|CET|CEST|WAT|-01|CAT|EET|EEST|+00|+01|SAST|CAST|MMT|WAST|+0130|NST|NWT|NPT|BST|BDT|AHST|HST|HDT|AST|AWT|APT|AHDT|YST|AKST|AKDT|-03|-02|CMT|-04|-0430|AMT|CST|CDT|CWT|CPT|EST|MST|PST|MDT|BMT|ADT|-0530|-05|PDT|MWT|MPT|-00|MDDT|EDT|SJMT|YDT|YWT|YPT|YDDT|PWT|PPT|EWT|EPT|NDT|ADDT|KMT|QMT|-0345|HMT|PDDT|EDDT|FFMT|-0330|-0230|-0130|PPMT|SMT|CDDT|SDMT|NDDT|+08|+11|+07|+05|+10|AEST|AEDT|+06|NZMT|NZST|NZDT|+03|+02|+12|+13|+14|+04|+0730|+09|+0530|+0630|IST|IDT|PLMT|HKT|HKST|HKWT|JST|IMT|+0720|WIB|+0930|WIT|JMT|IDDT|+0430|PKT|PKST|+0545|+0820|WITA|KST|KDT|TBMT|TMT|+0330|JDT|RMT|FMT|ACST|ACDT|+0845|+0945|+1030|+1130|AWST|AWDT|+0120|CEMT|MSK|MSD|DMT|BDST|WEMT|MDST|LST|SET|WMT|+0220|-1130|-11|-10|PMMT|+1215|+1245|+1345|EMT|-07|-06|-12|-09|GST|GDT|ChST|HWT|HPT|-1040|-0930|SST|-1120|+1112|+1230|-0830|-08|-1030|+1220|GMT+14|GMT+13|GMT+12|GMT+11|GMT+10|GMT+9|GMT+8|GMT+7|GMT+6|GMT+5|GMT+4|GMT+3|GMT+2|GMT+1|GMT-1|GMT-2|GMT-3|GMT-4|GMT-5|GMT-6|GMT-7|GMT-8|GMT-9|GMT-10|GMT-11|GMT-12\",\"offsets\":\"-1g|0|xc|1e0|6tg|6y0|8c0|7n0|kc|fl|2s0|5k0|-qw|mn|-2vw|-2s0|618|5sl|-1ek|-zg|-2g0|56o|460|5us|60w|-1zw|-226|2sc|18w|-1p9|2fw|1vw|360|xya|-wpq|-uk0|-rs0|-p00|12wo|-rrc|-m80|-be4|-b40|-8xc|-8c0|-5k0|-ato|-bw0|-c6k|-c3c|-cdo|-cqs|-ctg|-c44|-cos|-cac|-c2s|-cnc|-crn|-ci0|-aog|-gys|-go0|-dw0|-74s|-jho|-jg0|-b1h|-8z8|-gc0|-fa0|-aks|-b8g|-dps|-lip|-a44|-g2g|-ce8|-ce4|-9ow|-eq8|-eso|-g8c|-jn8|-fkd|-lks|-adw|-3gg|-ptg|-m9k|-jfw|-fdn|-l0g|-cxs|-gio|-mpz|-74o|-b3o|-b6s|-9rg|-6zg|-9q0|-6y0|-d68|-e7y|-grg|-es8|-ejc|-ars|-af0|-bs0|-f94|-f9c|-kjs|-fye|-g1i|-fzn|-g5v|-g2f|-fr4|-g7j|-g1d|15rv|-ow5|-fvq|-fpo|-cmc|-9uc|-e9o|-eac|-lwa|-6m4|-fz8|-fzc|-b44|-bb8|-iio|-jpg|-g83|-glg|16au|-od6|-id0|-aeg|-bzw|-iks|-aer|-460|-ebu|-dpe|-gcg|101a|-umq|-604|-iuj|-irc|-is3|-9kw|-jc4|-a7s|-a84|-a7o|-kr6|-de8|-ddo|-bu0|-c8p|-d4s|-d3a|-hig|-6go|-jdo|-ck0|-a4o|-cy0|-cyo|-8ms|-42g|15lz|-p21|-jyw|-g5g|-cqk|-gj0|-lo4|-ep8|-mss|-p0c|-hzo|14sh|-pvj|m80|uk0|jg0|dw0|rs0|go0|wd4|vy0|yq0|xc0|1040|1zo|8ng|e90|6nk|wv8|12w0|9b4|b40|al4|at8|9m8|884|880|9jk|98c|im4|fic|6ko|dtc|la4|ku0|l0g|p00|l7c|esc|esk|fa0|i20|6q0|gqs|gcw|n98|a8o|cqo|6ac|6ds|6hz|jr4|jqu|l56|nm0|gz0|jb5|js0|kdc|q20|qe0|6iu|6ig|ctc|ci0|tdo|cf0|fss|fz0|p3p|gd4|eva|h72|ity|j8d|kfk|n5c|l0y|rxc|m40|-189c|meo|66g|g5c|fcs|dl6|9ic|k8w|nac|bs4|c4g|qfc|ceh|nig|mhj|sgs|mi0|ctz|8an|9iw|9q0|glo|pvn|fqf|jsk|g7w|qiu|of7|o0y|htb|b89|af5|88o|-4r4|-5aw|-c06|-986|-2uo|-4cs|-194|-34o|-42o|-6rk|-apo|pnw|t60|sc8|q70|ra4|o88|nv4|ob0|r30|rl8|tgk|qug|lgc|s04|wk|3ok|3pc|a4|8wc|4e4|3so|2h4|2o8|t6|4u0|3j8|1kw|1dm|5c8|5bo|2bw|-15o|-169|1lr|-zo|-23|4md|5d4|5ew|5ng|97c|150|-ok|2os|53s|53c|1d8|6yh|707|9s7|ck7|4gy|78y|2b8|99w|8j6|6bc|6ao|4bg|3cc|2se|4l0|3o8|8yo|44o|30x|4os|3w0|4fc|6hc|des|jks|hy4|a9o|dm0|anc|a9s|yv4|-vsw|-vy0|st4|r8w|xz0|y10|zf0|1270|-12k4|s3w|-k94|v64|-vok|-xc0|-vpk|x4w|x6s|-glc|-ozo|tmc|-13v0|qt0|-t8e|-t60|-qe0|-t4w|-tmo|-10hg|u6k|uzk|vpc|-pu0|z20|-vm0|uws|-vgs|-vhc|v3s|v40|uto|-15rg|owk|-o38|-nm0|-11d8|tas|r94|-tl4|-rp4|w1g|y88|y9c|uus|y1k\"}) \n}","// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.\nrequire('../../js/transition.js')\nrequire('../../js/alert.js')\nrequire('../../js/button.js')\nrequire('../../js/carousel.js')\nrequire('../../js/collapse.js')\nrequire('../../js/dropdown.js')\nrequire('../../js/modal.js')\nrequire('../../js/tooltip.js')\nrequire('../../js/popover.js')\nrequire('../../js/scrollspy.js')\nrequire('../../js/tab.js')\nrequire('../../js/affix.js')","/* ========================================================================\n * Bootstrap: transition.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#transitions\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // CSS TRANSITION SUPPORT (Shoutout: https://modernizr.com/)\n // ============================================================\n\n function transitionEnd() {\n var el = document.createElement('bootstrap')\n\n var transEndEventNames = {\n WebkitTransition : 'webkitTransitionEnd',\n MozTransition : 'transitionend',\n OTransition : 'oTransitionEnd otransitionend',\n transition : 'transitionend'\n }\n\n for (var name in transEndEventNames) {\n if (el.style[name] !== undefined) {\n return { end: transEndEventNames[name] }\n }\n }\n\n return false // explicit for ie8 ( ._.)\n }\n\n // https://blog.alexmaccaw.com/css-transitions\n $.fn.emulateTransitionEnd = function (duration) {\n var called = false\n var $el = this\n $(this).one('bsTransitionEnd', function () { called = true })\n var callback = function () { if (!called) $($el).trigger($.support.transition.end) }\n setTimeout(callback, duration)\n return this\n }\n\n $(function () {\n $.support.transition = transitionEnd()\n\n if (!$.support.transition) return\n\n $.event.special.bsTransitionEnd = {\n bindType: $.support.transition.end,\n delegateType: $.support.transition.end,\n handle: function (e) {\n if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)\n }\n }\n })\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: alert.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#alerts\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // ALERT CLASS DEFINITION\n // ======================\n\n var dismiss = '[data-dismiss=\"alert\"]'\n var Alert = function (el) {\n $(el).on('click', dismiss, this.close)\n }\n\n Alert.VERSION = '3.4.1'\n\n Alert.TRANSITION_DURATION = 150\n\n Alert.prototype.close = function (e) {\n var $this = $(this)\n var selector = $this.attr('data-target')\n\n if (!selector) {\n selector = $this.attr('href')\n selector = selector && selector.replace(/.*(?=#[^\\s]*$)/, '') // strip for ie7\n }\n\n selector = selector === '#' ? [] : selector\n var $parent = $(document).find(selector)\n\n if (e) e.preventDefault()\n\n if (!$parent.length) {\n $parent = $this.closest('.alert')\n }\n\n $parent.trigger(e = $.Event('close.bs.alert'))\n\n if (e.isDefaultPrevented()) return\n\n $parent.removeClass('in')\n\n function removeElement() {\n // detach from parent, fire event then clean up data\n $parent.detach().trigger('closed.bs.alert').remove()\n }\n\n $.support.transition && $parent.hasClass('fade') ?\n $parent\n .one('bsTransitionEnd', removeElement)\n .emulateTransitionEnd(Alert.TRANSITION_DURATION) :\n removeElement()\n }\n\n\n // ALERT PLUGIN DEFINITION\n // =======================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.alert')\n\n if (!data) $this.data('bs.alert', (data = new Alert(this)))\n if (typeof option == 'string') data[option].call($this)\n })\n }\n\n var old = $.fn.alert\n\n $.fn.alert = Plugin\n $.fn.alert.Constructor = Alert\n\n\n // ALERT NO CONFLICT\n // =================\n\n $.fn.alert.noConflict = function () {\n $.fn.alert = old\n return this\n }\n\n\n // ALERT DATA-API\n // ==============\n\n $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: button.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#buttons\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // BUTTON PUBLIC CLASS DEFINITION\n // ==============================\n\n var Button = function (element, options) {\n this.$element = $(element)\n this.options = $.extend({}, Button.DEFAULTS, options)\n this.isLoading = false\n }\n\n Button.VERSION = '3.4.1'\n\n Button.DEFAULTS = {\n loadingText: 'loading...'\n }\n\n Button.prototype.setState = function (state) {\n var d = 'disabled'\n var $el = this.$element\n var val = $el.is('input') ? 'val' : 'html'\n var data = $el.data()\n\n state += 'Text'\n\n if (data.resetText == null) $el.data('resetText', $el[val]())\n\n // push to event loop to allow forms to submit\n setTimeout($.proxy(function () {\n $el[val](data[state] == null ? this.options[state] : data[state])\n\n if (state == 'loadingText') {\n this.isLoading = true\n $el.addClass(d).attr(d, d).prop(d, true)\n } else if (this.isLoading) {\n this.isLoading = false\n $el.removeClass(d).removeAttr(d).prop(d, false)\n }\n }, this), 0)\n }\n\n Button.prototype.toggle = function () {\n var changed = true\n var $parent = this.$element.closest('[data-toggle=\"buttons\"]')\n\n if ($parent.length) {\n var $input = this.$element.find('input')\n if ($input.prop('type') == 'radio') {\n if ($input.prop('checked')) changed = false\n $parent.find('.active').removeClass('active')\n this.$element.addClass('active')\n } else if ($input.prop('type') == 'checkbox') {\n if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false\n this.$element.toggleClass('active')\n }\n $input.prop('checked', this.$element.hasClass('active'))\n if (changed) $input.trigger('change')\n } else {\n this.$element.attr('aria-pressed', !this.$element.hasClass('active'))\n this.$element.toggleClass('active')\n }\n }\n\n\n // BUTTON PLUGIN DEFINITION\n // ========================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.button')\n var options = typeof option == 'object' && option\n\n if (!data) $this.data('bs.button', (data = new Button(this, options)))\n\n if (option == 'toggle') data.toggle()\n else if (option) data.setState(option)\n })\n }\n\n var old = $.fn.button\n\n $.fn.button = Plugin\n $.fn.button.Constructor = Button\n\n\n // BUTTON NO CONFLICT\n // ==================\n\n $.fn.button.noConflict = function () {\n $.fn.button = old\n return this\n }\n\n\n // BUTTON DATA-API\n // ===============\n\n $(document)\n .on('click.bs.button.data-api', '[data-toggle^=\"button\"]', function (e) {\n var $btn = $(e.target).closest('.btn')\n Plugin.call($btn, 'toggle')\n if (!($(e.target).is('input[type=\"radio\"], input[type=\"checkbox\"]'))) {\n // Prevent double click on radios, and the double selections (so cancellation) on checkboxes\n e.preventDefault()\n // The target component still receive the focus\n if ($btn.is('input,button')) $btn.trigger('focus')\n else $btn.find('input:visible,button:visible').first().trigger('focus')\n }\n })\n .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^=\"button\"]', function (e) {\n $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))\n })\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: carousel.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#carousel\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // CAROUSEL CLASS DEFINITION\n // =========================\n\n var Carousel = function (element, options) {\n this.$element = $(element)\n this.$indicators = this.$element.find('.carousel-indicators')\n this.options = options\n this.paused = null\n this.sliding = null\n this.interval = null\n this.$active = null\n this.$items = null\n\n this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))\n\n this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element\n .on('mouseenter.bs.carousel', $.proxy(this.pause, this))\n .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))\n }\n\n Carousel.VERSION = '3.4.1'\n\n Carousel.TRANSITION_DURATION = 600\n\n Carousel.DEFAULTS = {\n interval: 5000,\n pause: 'hover',\n wrap: true,\n keyboard: true\n }\n\n Carousel.prototype.keydown = function (e) {\n if (/input|textarea/i.test(e.target.tagName)) return\n switch (e.which) {\n case 37: this.prev(); break\n case 39: this.next(); break\n default: return\n }\n\n e.preventDefault()\n }\n\n Carousel.prototype.cycle = function (e) {\n e || (this.paused = false)\n\n this.interval && clearInterval(this.interval)\n\n this.options.interval\n && !this.paused\n && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))\n\n return this\n }\n\n Carousel.prototype.getItemIndex = function (item) {\n this.$items = item.parent().children('.item')\n return this.$items.index(item || this.$active)\n }\n\n Carousel.prototype.getItemForDirection = function (direction, active) {\n var activeIndex = this.getItemIndex(active)\n var willWrap = (direction == 'prev' && activeIndex === 0)\n || (direction == 'next' && activeIndex == (this.$items.length - 1))\n if (willWrap && !this.options.wrap) return active\n var delta = direction == 'prev' ? -1 : 1\n var itemIndex = (activeIndex + delta) % this.$items.length\n return this.$items.eq(itemIndex)\n }\n\n Carousel.prototype.to = function (pos) {\n var that = this\n var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))\n\n if (pos > (this.$items.length - 1) || pos < 0) return\n\n if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, \"slid\"\n if (activeIndex == pos) return this.pause().cycle()\n\n return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))\n }\n\n Carousel.prototype.pause = function (e) {\n e || (this.paused = true)\n\n if (this.$element.find('.next, .prev').length && $.support.transition) {\n this.$element.trigger($.support.transition.end)\n this.cycle(true)\n }\n\n this.interval = clearInterval(this.interval)\n\n return this\n }\n\n Carousel.prototype.next = function () {\n if (this.sliding) return\n return this.slide('next')\n }\n\n Carousel.prototype.prev = function () {\n if (this.sliding) return\n return this.slide('prev')\n }\n\n Carousel.prototype.slide = function (type, next) {\n var $active = this.$element.find('.item.active')\n var $next = next || this.getItemForDirection(type, $active)\n var isCycling = this.interval\n var direction = type == 'next' ? 'left' : 'right'\n var that = this\n\n if ($next.hasClass('active')) return (this.sliding = false)\n\n var relatedTarget = $next[0]\n var slideEvent = $.Event('slide.bs.carousel', {\n relatedTarget: relatedTarget,\n direction: direction\n })\n this.$element.trigger(slideEvent)\n if (slideEvent.isDefaultPrevented()) return\n\n this.sliding = true\n\n isCycling && this.pause()\n\n if (this.$indicators.length) {\n this.$indicators.find('.active').removeClass('active')\n var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])\n $nextIndicator && $nextIndicator.addClass('active')\n }\n\n var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, \"slid\"\n if ($.support.transition && this.$element.hasClass('slide')) {\n $next.addClass(type)\n if (typeof $next === 'object' && $next.length) {\n $next[0].offsetWidth // force reflow\n }\n $active.addClass(direction)\n $next.addClass(direction)\n $active\n .one('bsTransitionEnd', function () {\n $next.removeClass([type, direction].join(' ')).addClass('active')\n $active.removeClass(['active', direction].join(' '))\n that.sliding = false\n setTimeout(function () {\n that.$element.trigger(slidEvent)\n }, 0)\n })\n .emulateTransitionEnd(Carousel.TRANSITION_DURATION)\n } else {\n $active.removeClass('active')\n $next.addClass('active')\n this.sliding = false\n this.$element.trigger(slidEvent)\n }\n\n isCycling && this.cycle()\n\n return this\n }\n\n\n // CAROUSEL PLUGIN DEFINITION\n // ==========================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.carousel')\n var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)\n var action = typeof option == 'string' ? option : options.slide\n\n if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))\n if (typeof option == 'number') data.to(option)\n else if (action) data[action]()\n else if (options.interval) data.pause().cycle()\n })\n }\n\n var old = $.fn.carousel\n\n $.fn.carousel = Plugin\n $.fn.carousel.Constructor = Carousel\n\n\n // CAROUSEL NO CONFLICT\n // ====================\n\n $.fn.carousel.noConflict = function () {\n $.fn.carousel = old\n return this\n }\n\n\n // CAROUSEL DATA-API\n // =================\n\n var clickHandler = function (e) {\n var $this = $(this)\n var href = $this.attr('href')\n if (href) {\n href = href.replace(/.*(?=#[^\\s]+$)/, '') // strip for ie7\n }\n\n var target = $this.attr('data-target') || href\n var $target = $(document).find(target)\n\n if (!$target.hasClass('carousel')) return\n\n var options = $.extend({}, $target.data(), $this.data())\n var slideIndex = $this.attr('data-slide-to')\n if (slideIndex) options.interval = false\n\n Plugin.call($target, options)\n\n if (slideIndex) {\n $target.data('bs.carousel').to(slideIndex)\n }\n\n e.preventDefault()\n }\n\n $(document)\n .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)\n .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)\n\n $(window).on('load', function () {\n $('[data-ride=\"carousel\"]').each(function () {\n var $carousel = $(this)\n Plugin.call($carousel, $carousel.data())\n })\n })\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: collapse.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#collapse\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n/* jshint latedef: false */\n\n+function ($) {\n 'use strict';\n\n // COLLAPSE PUBLIC CLASS DEFINITION\n // ================================\n\n var Collapse = function (element, options) {\n this.$element = $(element)\n this.options = $.extend({}, Collapse.DEFAULTS, options)\n this.$trigger = $('[data-toggle=\"collapse\"][href=\"#' + element.id + '\"],' +\n '[data-toggle=\"collapse\"][data-target=\"#' + element.id + '\"]')\n this.transitioning = null\n\n if (this.options.parent) {\n this.$parent = this.getParent()\n } else {\n this.addAriaAndCollapsedClass(this.$element, this.$trigger)\n }\n\n if (this.options.toggle) this.toggle()\n }\n\n Collapse.VERSION = '3.4.1'\n\n Collapse.TRANSITION_DURATION = 350\n\n Collapse.DEFAULTS = {\n toggle: true\n }\n\n Collapse.prototype.dimension = function () {\n var hasWidth = this.$element.hasClass('width')\n return hasWidth ? 'width' : 'height'\n }\n\n Collapse.prototype.show = function () {\n if (this.transitioning || this.$element.hasClass('in')) return\n\n var activesData\n var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')\n\n if (actives && actives.length) {\n activesData = actives.data('bs.collapse')\n if (activesData && activesData.transitioning) return\n }\n\n var startEvent = $.Event('show.bs.collapse')\n this.$element.trigger(startEvent)\n if (startEvent.isDefaultPrevented()) return\n\n if (actives && actives.length) {\n Plugin.call(actives, 'hide')\n activesData || actives.data('bs.collapse', null)\n }\n\n var dimension = this.dimension()\n\n this.$element\n .removeClass('collapse')\n .addClass('collapsing')[dimension](0)\n .attr('aria-expanded', true)\n\n this.$trigger\n .removeClass('collapsed')\n .attr('aria-expanded', true)\n\n this.transitioning = 1\n\n var complete = function () {\n this.$element\n .removeClass('collapsing')\n .addClass('collapse in')[dimension]('')\n this.transitioning = 0\n this.$element\n .trigger('shown.bs.collapse')\n }\n\n if (!$.support.transition) return complete.call(this)\n\n var scrollSize = $.camelCase(['scroll', dimension].join('-'))\n\n this.$element\n .one('bsTransitionEnd', $.proxy(complete, this))\n .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])\n }\n\n Collapse.prototype.hide = function () {\n if (this.transitioning || !this.$element.hasClass('in')) return\n\n var startEvent = $.Event('hide.bs.collapse')\n this.$element.trigger(startEvent)\n if (startEvent.isDefaultPrevented()) return\n\n var dimension = this.dimension()\n\n this.$element[dimension](this.$element[dimension]())[0].offsetHeight\n\n this.$element\n .addClass('collapsing')\n .removeClass('collapse in')\n .attr('aria-expanded', false)\n\n this.$trigger\n .addClass('collapsed')\n .attr('aria-expanded', false)\n\n this.transitioning = 1\n\n var complete = function () {\n this.transitioning = 0\n this.$element\n .removeClass('collapsing')\n .addClass('collapse')\n .trigger('hidden.bs.collapse')\n }\n\n if (!$.support.transition) return complete.call(this)\n\n this.$element\n [dimension](0)\n .one('bsTransitionEnd', $.proxy(complete, this))\n .emulateTransitionEnd(Collapse.TRANSITION_DURATION)\n }\n\n Collapse.prototype.toggle = function () {\n this[this.$element.hasClass('in') ? 'hide' : 'show']()\n }\n\n Collapse.prototype.getParent = function () {\n return $(document).find(this.options.parent)\n .find('[data-toggle=\"collapse\"][data-parent=\"' + this.options.parent + '\"]')\n .each($.proxy(function (i, element) {\n var $element = $(element)\n this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)\n }, this))\n .end()\n }\n\n Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {\n var isOpen = $element.hasClass('in')\n\n $element.attr('aria-expanded', isOpen)\n $trigger\n .toggleClass('collapsed', !isOpen)\n .attr('aria-expanded', isOpen)\n }\n\n function getTargetFromTrigger($trigger) {\n var href\n var target = $trigger.attr('data-target')\n || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\\s]+$)/, '') // strip for ie7\n\n return $(document).find(target)\n }\n\n\n // COLLAPSE PLUGIN DEFINITION\n // ==========================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.collapse')\n var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)\n\n if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false\n if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))\n if (typeof option == 'string') data[option]()\n })\n }\n\n var old = $.fn.collapse\n\n $.fn.collapse = Plugin\n $.fn.collapse.Constructor = Collapse\n\n\n // COLLAPSE NO CONFLICT\n // ====================\n\n $.fn.collapse.noConflict = function () {\n $.fn.collapse = old\n return this\n }\n\n\n // COLLAPSE DATA-API\n // =================\n\n $(document).on('click.bs.collapse.data-api', '[data-toggle=\"collapse\"]', function (e) {\n var $this = $(this)\n\n if (!$this.attr('data-target')) e.preventDefault()\n\n var $target = getTargetFromTrigger($this)\n var data = $target.data('bs.collapse')\n var option = data ? 'toggle' : $this.data()\n\n Plugin.call($target, option)\n })\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: dropdown.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#dropdowns\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // DROPDOWN CLASS DEFINITION\n // =========================\n\n var backdrop = '.dropdown-backdrop'\n var toggle = '[data-toggle=\"dropdown\"]'\n var Dropdown = function (element) {\n $(element).on('click.bs.dropdown', this.toggle)\n }\n\n Dropdown.VERSION = '3.4.1'\n\n function getParent($this) {\n var selector = $this.attr('data-target')\n\n if (!selector) {\n selector = $this.attr('href')\n selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\\s]*$)/, '') // strip for ie7\n }\n\n var $parent = selector !== '#' ? $(document).find(selector) : null\n\n return $parent && $parent.length ? $parent : $this.parent()\n }\n\n function clearMenus(e) {\n if (e && e.which === 3) return\n $(backdrop).remove()\n $(toggle).each(function () {\n var $this = $(this)\n var $parent = getParent($this)\n var relatedTarget = { relatedTarget: this }\n\n if (!$parent.hasClass('open')) return\n\n if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return\n\n $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))\n\n if (e.isDefaultPrevented()) return\n\n $this.attr('aria-expanded', 'false')\n $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))\n })\n }\n\n Dropdown.prototype.toggle = function (e) {\n var $this = $(this)\n\n if ($this.is('.disabled, :disabled')) return\n\n var $parent = getParent($this)\n var isActive = $parent.hasClass('open')\n\n clearMenus()\n\n if (!isActive) {\n if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {\n // if mobile we use a backdrop because click events don't delegate\n $(document.createElement('div'))\n .addClass('dropdown-backdrop')\n .insertAfter($(this))\n .on('click', clearMenus)\n }\n\n var relatedTarget = { relatedTarget: this }\n $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))\n\n if (e.isDefaultPrevented()) return\n\n $this\n .trigger('focus')\n .attr('aria-expanded', 'true')\n\n $parent\n .toggleClass('open')\n .trigger($.Event('shown.bs.dropdown', relatedTarget))\n }\n\n return false\n }\n\n Dropdown.prototype.keydown = function (e) {\n if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return\n\n var $this = $(this)\n\n e.preventDefault()\n e.stopPropagation()\n\n if ($this.is('.disabled, :disabled')) return\n\n var $parent = getParent($this)\n var isActive = $parent.hasClass('open')\n\n if (!isActive && e.which != 27 || isActive && e.which == 27) {\n if (e.which == 27) $parent.find(toggle).trigger('focus')\n return $this.trigger('click')\n }\n\n var desc = ' li:not(.disabled):visible a'\n var $items = $parent.find('.dropdown-menu' + desc)\n\n if (!$items.length) return\n\n var index = $items.index(e.target)\n\n if (e.which == 38 && index > 0) index-- // up\n if (e.which == 40 && index < $items.length - 1) index++ // down\n if (!~index) index = 0\n\n $items.eq(index).trigger('focus')\n }\n\n\n // DROPDOWN PLUGIN DEFINITION\n // ==========================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.dropdown')\n\n if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))\n if (typeof option == 'string') data[option].call($this)\n })\n }\n\n var old = $.fn.dropdown\n\n $.fn.dropdown = Plugin\n $.fn.dropdown.Constructor = Dropdown\n\n\n // DROPDOWN NO CONFLICT\n // ====================\n\n $.fn.dropdown.noConflict = function () {\n $.fn.dropdown = old\n return this\n }\n\n\n // APPLY TO STANDARD DROPDOWN ELEMENTS\n // ===================================\n\n $(document)\n .on('click.bs.dropdown.data-api', clearMenus)\n .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })\n .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)\n .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)\n .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: modal.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#modals\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // MODAL CLASS DEFINITION\n // ======================\n\n var Modal = function (element, options) {\n this.options = options\n this.$body = $(document.body)\n this.$element = $(element)\n this.$dialog = this.$element.find('.modal-dialog')\n this.$backdrop = null\n this.isShown = null\n this.originalBodyPad = null\n this.scrollbarWidth = 0\n this.ignoreBackdropClick = false\n this.fixedContent = '.navbar-fixed-top, .navbar-fixed-bottom'\n\n if (this.options.remote) {\n this.$element\n .find('.modal-content')\n .load(this.options.remote, $.proxy(function () {\n this.$element.trigger('loaded.bs.modal')\n }, this))\n }\n }\n\n Modal.VERSION = '3.4.1'\n\n Modal.TRANSITION_DURATION = 300\n Modal.BACKDROP_TRANSITION_DURATION = 150\n\n Modal.DEFAULTS = {\n backdrop: true,\n keyboard: true,\n show: true\n }\n\n Modal.prototype.toggle = function (_relatedTarget) {\n return this.isShown ? this.hide() : this.show(_relatedTarget)\n }\n\n Modal.prototype.show = function (_relatedTarget) {\n var that = this\n var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })\n\n this.$element.trigger(e)\n\n if (this.isShown || e.isDefaultPrevented()) return\n\n this.isShown = true\n\n this.checkScrollbar()\n this.setScrollbar()\n this.$body.addClass('modal-open')\n\n this.escape()\n this.resize()\n\n this.$element.on('click.dismiss.bs.modal', '[data-dismiss=\"modal\"]', $.proxy(this.hide, this))\n\n this.$dialog.on('mousedown.dismiss.bs.modal', function () {\n that.$element.one('mouseup.dismiss.bs.modal', function (e) {\n if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true\n })\n })\n\n this.backdrop(function () {\n var transition = $.support.transition && that.$element.hasClass('fade')\n\n if (!that.$element.parent().length) {\n that.$element.appendTo(that.$body) // don't move modals dom position\n }\n\n that.$element\n .show()\n .scrollTop(0)\n\n that.adjustDialog()\n\n if (transition) {\n that.$element[0].offsetWidth // force reflow\n }\n\n that.$element.addClass('in')\n\n that.enforceFocus()\n\n var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })\n\n transition ?\n that.$dialog // wait for modal to slide in\n .one('bsTransitionEnd', function () {\n that.$element.trigger('focus').trigger(e)\n })\n .emulateTransitionEnd(Modal.TRANSITION_DURATION) :\n that.$element.trigger('focus').trigger(e)\n })\n }\n\n Modal.prototype.hide = function (e) {\n if (e) e.preventDefault()\n\n e = $.Event('hide.bs.modal')\n\n this.$element.trigger(e)\n\n if (!this.isShown || e.isDefaultPrevented()) return\n\n this.isShown = false\n\n this.escape()\n this.resize()\n\n $(document).off('focusin.bs.modal')\n\n this.$element\n .removeClass('in')\n .off('click.dismiss.bs.modal')\n .off('mouseup.dismiss.bs.modal')\n\n this.$dialog.off('mousedown.dismiss.bs.modal')\n\n $.support.transition && this.$element.hasClass('fade') ?\n this.$element\n .one('bsTransitionEnd', $.proxy(this.hideModal, this))\n .emulateTransitionEnd(Modal.TRANSITION_DURATION) :\n this.hideModal()\n }\n\n Modal.prototype.enforceFocus = function () {\n $(document)\n .off('focusin.bs.modal') // guard against infinite focus loop\n .on('focusin.bs.modal', $.proxy(function (e) {\n if (document !== e.target &&\n this.$element[0] !== e.target &&\n !this.$element.has(e.target).length) {\n this.$element.trigger('focus')\n }\n }, this))\n }\n\n Modal.prototype.escape = function () {\n if (this.isShown && this.options.keyboard) {\n this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {\n e.which == 27 && this.hide()\n }, this))\n } else if (!this.isShown) {\n this.$element.off('keydown.dismiss.bs.modal')\n }\n }\n\n Modal.prototype.resize = function () {\n if (this.isShown) {\n $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))\n } else {\n $(window).off('resize.bs.modal')\n }\n }\n\n Modal.prototype.hideModal = function () {\n var that = this\n this.$element.hide()\n this.backdrop(function () {\n that.$body.removeClass('modal-open')\n that.resetAdjustments()\n that.resetScrollbar()\n that.$element.trigger('hidden.bs.modal')\n })\n }\n\n Modal.prototype.removeBackdrop = function () {\n this.$backdrop && this.$backdrop.remove()\n this.$backdrop = null\n }\n\n Modal.prototype.backdrop = function (callback) {\n var that = this\n var animate = this.$element.hasClass('fade') ? 'fade' : ''\n\n if (this.isShown && this.options.backdrop) {\n var doAnimate = $.support.transition && animate\n\n this.$backdrop = $(document.createElement('div'))\n .addClass('modal-backdrop ' + animate)\n .appendTo(this.$body)\n\n this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {\n if (this.ignoreBackdropClick) {\n this.ignoreBackdropClick = false\n return\n }\n if (e.target !== e.currentTarget) return\n this.options.backdrop == 'static'\n ? this.$element[0].focus()\n : this.hide()\n }, this))\n\n if (doAnimate) this.$backdrop[0].offsetWidth // force reflow\n\n this.$backdrop.addClass('in')\n\n if (!callback) return\n\n doAnimate ?\n this.$backdrop\n .one('bsTransitionEnd', callback)\n .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :\n callback()\n\n } else if (!this.isShown && this.$backdrop) {\n this.$backdrop.removeClass('in')\n\n var callbackRemove = function () {\n that.removeBackdrop()\n callback && callback()\n }\n $.support.transition && this.$element.hasClass('fade') ?\n this.$backdrop\n .one('bsTransitionEnd', callbackRemove)\n .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :\n callbackRemove()\n\n } else if (callback) {\n callback()\n }\n }\n\n // these following methods are used to handle overflowing modals\n\n Modal.prototype.handleUpdate = function () {\n this.adjustDialog()\n }\n\n Modal.prototype.adjustDialog = function () {\n var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight\n\n this.$element.css({\n paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',\n paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''\n })\n }\n\n Modal.prototype.resetAdjustments = function () {\n this.$element.css({\n paddingLeft: '',\n paddingRight: ''\n })\n }\n\n Modal.prototype.checkScrollbar = function () {\n var fullWindowWidth = window.innerWidth\n if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8\n var documentElementRect = document.documentElement.getBoundingClientRect()\n fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)\n }\n this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth\n this.scrollbarWidth = this.measureScrollbar()\n }\n\n Modal.prototype.setScrollbar = function () {\n var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)\n this.originalBodyPad = document.body.style.paddingRight || ''\n var scrollbarWidth = this.scrollbarWidth\n if (this.bodyIsOverflowing) {\n this.$body.css('padding-right', bodyPad + scrollbarWidth)\n $(this.fixedContent).each(function (index, element) {\n var actualPadding = element.style.paddingRight\n var calculatedPadding = $(element).css('padding-right')\n $(element)\n .data('padding-right', actualPadding)\n .css('padding-right', parseFloat(calculatedPadding) + scrollbarWidth + 'px')\n })\n }\n }\n\n Modal.prototype.resetScrollbar = function () {\n this.$body.css('padding-right', this.originalBodyPad)\n $(this.fixedContent).each(function (index, element) {\n var padding = $(element).data('padding-right')\n $(element).removeData('padding-right')\n element.style.paddingRight = padding ? padding : ''\n })\n }\n\n Modal.prototype.measureScrollbar = function () { // thx walsh\n var scrollDiv = document.createElement('div')\n scrollDiv.className = 'modal-scrollbar-measure'\n this.$body.append(scrollDiv)\n var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth\n this.$body[0].removeChild(scrollDiv)\n return scrollbarWidth\n }\n\n\n // MODAL PLUGIN DEFINITION\n // =======================\n\n function Plugin(option, _relatedTarget) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.modal')\n var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)\n\n if (!data) $this.data('bs.modal', (data = new Modal(this, options)))\n if (typeof option == 'string') data[option](_relatedTarget)\n else if (options.show) data.show(_relatedTarget)\n })\n }\n\n var old = $.fn.modal\n\n $.fn.modal = Plugin\n $.fn.modal.Constructor = Modal\n\n\n // MODAL NO CONFLICT\n // =================\n\n $.fn.modal.noConflict = function () {\n $.fn.modal = old\n return this\n }\n\n\n // MODAL DATA-API\n // ==============\n\n $(document).on('click.bs.modal.data-api', '[data-toggle=\"modal\"]', function (e) {\n var $this = $(this)\n var href = $this.attr('href')\n var target = $this.attr('data-target') ||\n (href && href.replace(/.*(?=#[^\\s]+$)/, '')) // strip for ie7\n\n var $target = $(document).find(target)\n var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())\n\n if ($this.is('a')) e.preventDefault()\n\n $target.one('show.bs.modal', function (showEvent) {\n if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown\n $target.one('hidden.bs.modal', function () {\n $this.is(':visible') && $this.trigger('focus')\n })\n })\n Plugin.call($target, option, this)\n })\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: tooltip.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#tooltip\n * Inspired by the original jQuery.tipsy by Jason Frame\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n+function ($) {\n 'use strict';\n\n var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']\n\n var uriAttrs = [\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n ]\n\n var ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\n var DefaultWhitelist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n }\n\n /**\n * A pattern that recognizes a commonly useful subset of URLs that are safe.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\n var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi\n\n /**\n * A pattern that matches safe data URLs. Only matches image, video and audio types.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\n var DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i\n\n function allowedAttribute(attr, allowedAttributeList) {\n var attrName = attr.nodeName.toLowerCase()\n\n if ($.inArray(attrName, allowedAttributeList) !== -1) {\n if ($.inArray(attrName, uriAttrs) !== -1) {\n return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))\n }\n\n return true\n }\n\n var regExp = $(allowedAttributeList).filter(function (index, value) {\n return value instanceof RegExp\n })\n\n // Check if a regular expression validates the attribute.\n for (var i = 0, l = regExp.length; i < l; i++) {\n if (attrName.match(regExp[i])) {\n return true\n }\n }\n\n return false\n }\n\n function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {\n if (unsafeHtml.length === 0) {\n return unsafeHtml\n }\n\n if (sanitizeFn && typeof sanitizeFn === 'function') {\n return sanitizeFn(unsafeHtml)\n }\n\n // IE 8 and below don't support createHTMLDocument\n if (!document.implementation || !document.implementation.createHTMLDocument) {\n return unsafeHtml\n }\n\n var createdDocument = document.implementation.createHTMLDocument('sanitization')\n createdDocument.body.innerHTML = unsafeHtml\n\n var whitelistKeys = $.map(whiteList, function (el, i) { return i })\n var elements = $(createdDocument.body).find('*')\n\n for (var i = 0, len = elements.length; i < len; i++) {\n var el = elements[i]\n var elName = el.nodeName.toLowerCase()\n\n if ($.inArray(elName, whitelistKeys) === -1) {\n el.parentNode.removeChild(el)\n\n continue\n }\n\n var attributeList = $.map(el.attributes, function (el) { return el })\n var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])\n\n for (var j = 0, len2 = attributeList.length; j < len2; j++) {\n if (!allowedAttribute(attributeList[j], whitelistedAttributes)) {\n el.removeAttribute(attributeList[j].nodeName)\n }\n }\n }\n\n return createdDocument.body.innerHTML\n }\n\n // TOOLTIP PUBLIC CLASS DEFINITION\n // ===============================\n\n var Tooltip = function (element, options) {\n this.type = null\n this.options = null\n this.enabled = null\n this.timeout = null\n this.hoverState = null\n this.$element = null\n this.inState = null\n\n this.init('tooltip', element, options)\n }\n\n Tooltip.VERSION = '3.4.1'\n\n Tooltip.TRANSITION_DURATION = 150\n\n Tooltip.DEFAULTS = {\n animation: true,\n placement: 'top',\n selector: false,\n template: '
',\n trigger: 'hover focus',\n title: '',\n delay: 0,\n html: false,\n container: false,\n viewport: {\n selector: 'body',\n padding: 0\n },\n sanitize : true,\n sanitizeFn : null,\n whiteList : DefaultWhitelist\n }\n\n Tooltip.prototype.init = function (type, element, options) {\n this.enabled = true\n this.type = type\n this.$element = $(element)\n this.options = this.getOptions(options)\n this.$viewport = this.options.viewport && $(document).find($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))\n this.inState = { click: false, hover: false, focus: false }\n\n if (this.$element[0] instanceof document.constructor && !this.options.selector) {\n throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')\n }\n\n var triggers = this.options.trigger.split(' ')\n\n for (var i = triggers.length; i--;) {\n var trigger = triggers[i]\n\n if (trigger == 'click') {\n this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))\n } else if (trigger != 'manual') {\n var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'\n var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'\n\n this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))\n this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))\n }\n }\n\n this.options.selector ?\n (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :\n this.fixTitle()\n }\n\n Tooltip.prototype.getDefaults = function () {\n return Tooltip.DEFAULTS\n }\n\n Tooltip.prototype.getOptions = function (options) {\n var dataAttributes = this.$element.data()\n\n for (var dataAttr in dataAttributes) {\n if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) {\n delete dataAttributes[dataAttr]\n }\n }\n\n options = $.extend({}, this.getDefaults(), dataAttributes, options)\n\n if (options.delay && typeof options.delay == 'number') {\n options.delay = {\n show: options.delay,\n hide: options.delay\n }\n }\n\n if (options.sanitize) {\n options.template = sanitizeHtml(options.template, options.whiteList, options.sanitizeFn)\n }\n\n return options\n }\n\n Tooltip.prototype.getDelegateOptions = function () {\n var options = {}\n var defaults = this.getDefaults()\n\n this._options && $.each(this._options, function (key, value) {\n if (defaults[key] != value) options[key] = value\n })\n\n return options\n }\n\n Tooltip.prototype.enter = function (obj) {\n var self = obj instanceof this.constructor ?\n obj : $(obj.currentTarget).data('bs.' + this.type)\n\n if (!self) {\n self = new this.constructor(obj.currentTarget, this.getDelegateOptions())\n $(obj.currentTarget).data('bs.' + this.type, self)\n }\n\n if (obj instanceof $.Event) {\n self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true\n }\n\n if (self.tip().hasClass('in') || self.hoverState == 'in') {\n self.hoverState = 'in'\n return\n }\n\n clearTimeout(self.timeout)\n\n self.hoverState = 'in'\n\n if (!self.options.delay || !self.options.delay.show) return self.show()\n\n self.timeout = setTimeout(function () {\n if (self.hoverState == 'in') self.show()\n }, self.options.delay.show)\n }\n\n Tooltip.prototype.isInStateTrue = function () {\n for (var key in this.inState) {\n if (this.inState[key]) return true\n }\n\n return false\n }\n\n Tooltip.prototype.leave = function (obj) {\n var self = obj instanceof this.constructor ?\n obj : $(obj.currentTarget).data('bs.' + this.type)\n\n if (!self) {\n self = new this.constructor(obj.currentTarget, this.getDelegateOptions())\n $(obj.currentTarget).data('bs.' + this.type, self)\n }\n\n if (obj instanceof $.Event) {\n self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false\n }\n\n if (self.isInStateTrue()) return\n\n clearTimeout(self.timeout)\n\n self.hoverState = 'out'\n\n if (!self.options.delay || !self.options.delay.hide) return self.hide()\n\n self.timeout = setTimeout(function () {\n if (self.hoverState == 'out') self.hide()\n }, self.options.delay.hide)\n }\n\n Tooltip.prototype.show = function () {\n var e = $.Event('show.bs.' + this.type)\n\n if (this.hasContent() && this.enabled) {\n this.$element.trigger(e)\n\n var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])\n if (e.isDefaultPrevented() || !inDom) return\n var that = this\n\n var $tip = this.tip()\n\n var tipId = this.getUID(this.type)\n\n this.setContent()\n $tip.attr('id', tipId)\n this.$element.attr('aria-describedby', tipId)\n\n if (this.options.animation) $tip.addClass('fade')\n\n var placement = typeof this.options.placement == 'function' ?\n this.options.placement.call(this, $tip[0], this.$element[0]) :\n this.options.placement\n\n var autoToken = /\\s?auto?\\s?/i\n var autoPlace = autoToken.test(placement)\n if (autoPlace) placement = placement.replace(autoToken, '') || 'top'\n\n $tip\n .detach()\n .css({ top: 0, left: 0, display: 'block' })\n .addClass(placement)\n .data('bs.' + this.type, this)\n\n this.options.container ? $tip.appendTo($(document).find(this.options.container)) : $tip.insertAfter(this.$element)\n this.$element.trigger('inserted.bs.' + this.type)\n\n var pos = this.getPosition()\n var actualWidth = $tip[0].offsetWidth\n var actualHeight = $tip[0].offsetHeight\n\n if (autoPlace) {\n var orgPlacement = placement\n var viewportDim = this.getPosition(this.$viewport)\n\n placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' :\n placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' :\n placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' :\n placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' :\n placement\n\n $tip\n .removeClass(orgPlacement)\n .addClass(placement)\n }\n\n var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)\n\n this.applyPlacement(calculatedOffset, placement)\n\n var complete = function () {\n var prevHoverState = that.hoverState\n that.$element.trigger('shown.bs.' + that.type)\n that.hoverState = null\n\n if (prevHoverState == 'out') that.leave(that)\n }\n\n $.support.transition && this.$tip.hasClass('fade') ?\n $tip\n .one('bsTransitionEnd', complete)\n .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :\n complete()\n }\n }\n\n Tooltip.prototype.applyPlacement = function (offset, placement) {\n var $tip = this.tip()\n var width = $tip[0].offsetWidth\n var height = $tip[0].offsetHeight\n\n // manually read margins because getBoundingClientRect includes difference\n var marginTop = parseInt($tip.css('margin-top'), 10)\n var marginLeft = parseInt($tip.css('margin-left'), 10)\n\n // we must check for NaN for ie 8/9\n if (isNaN(marginTop)) marginTop = 0\n if (isNaN(marginLeft)) marginLeft = 0\n\n offset.top += marginTop\n offset.left += marginLeft\n\n // $.fn.offset doesn't round pixel values\n // so we use setOffset directly with our own function B-0\n $.offset.setOffset($tip[0], $.extend({\n using: function (props) {\n $tip.css({\n top: Math.round(props.top),\n left: Math.round(props.left)\n })\n }\n }, offset), 0)\n\n $tip.addClass('in')\n\n // check to see if placing tip in new offset caused the tip to resize itself\n var actualWidth = $tip[0].offsetWidth\n var actualHeight = $tip[0].offsetHeight\n\n if (placement == 'top' && actualHeight != height) {\n offset.top = offset.top + height - actualHeight\n }\n\n var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)\n\n if (delta.left) offset.left += delta.left\n else offset.top += delta.top\n\n var isVertical = /top|bottom/.test(placement)\n var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight\n var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'\n\n $tip.offset(offset)\n this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)\n }\n\n Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {\n this.arrow()\n .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')\n .css(isVertical ? 'top' : 'left', '')\n }\n\n Tooltip.prototype.setContent = function () {\n var $tip = this.tip()\n var title = this.getTitle()\n\n if (this.options.html) {\n if (this.options.sanitize) {\n title = sanitizeHtml(title, this.options.whiteList, this.options.sanitizeFn)\n }\n\n $tip.find('.tooltip-inner').html(title)\n } else {\n $tip.find('.tooltip-inner').text(title)\n }\n\n $tip.removeClass('fade in top bottom left right')\n }\n\n Tooltip.prototype.hide = function (callback) {\n var that = this\n var $tip = $(this.$tip)\n var e = $.Event('hide.bs.' + this.type)\n\n function complete() {\n if (that.hoverState != 'in') $tip.detach()\n if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.\n that.$element\n .removeAttr('aria-describedby')\n .trigger('hidden.bs.' + that.type)\n }\n callback && callback()\n }\n\n this.$element.trigger(e)\n\n if (e.isDefaultPrevented()) return\n\n $tip.removeClass('in')\n\n $.support.transition && $tip.hasClass('fade') ?\n $tip\n .one('bsTransitionEnd', complete)\n .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :\n complete()\n\n this.hoverState = null\n\n return this\n }\n\n Tooltip.prototype.fixTitle = function () {\n var $e = this.$element\n if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {\n $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')\n }\n }\n\n Tooltip.prototype.hasContent = function () {\n return this.getTitle()\n }\n\n Tooltip.prototype.getPosition = function ($element) {\n $element = $element || this.$element\n\n var el = $element[0]\n var isBody = el.tagName == 'BODY'\n\n var elRect = el.getBoundingClientRect()\n if (elRect.width == null) {\n // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093\n elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })\n }\n var isSvg = window.SVGElement && el instanceof window.SVGElement\n // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.\n // See https://github.com/twbs/bootstrap/issues/20280\n var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())\n var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }\n var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null\n\n return $.extend({}, elRect, scroll, outerDims, elOffset)\n }\n\n Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {\n return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :\n placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :\n placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :\n /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }\n\n }\n\n Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {\n var delta = { top: 0, left: 0 }\n if (!this.$viewport) return delta\n\n var viewportPadding = this.options.viewport && this.options.viewport.padding || 0\n var viewportDimensions = this.getPosition(this.$viewport)\n\n if (/right|left/.test(placement)) {\n var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll\n var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight\n if (topEdgeOffset < viewportDimensions.top) { // top overflow\n delta.top = viewportDimensions.top - topEdgeOffset\n } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow\n delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset\n }\n } else {\n var leftEdgeOffset = pos.left - viewportPadding\n var rightEdgeOffset = pos.left + viewportPadding + actualWidth\n if (leftEdgeOffset < viewportDimensions.left) { // left overflow\n delta.left = viewportDimensions.left - leftEdgeOffset\n } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow\n delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset\n }\n }\n\n return delta\n }\n\n Tooltip.prototype.getTitle = function () {\n var title\n var $e = this.$element\n var o = this.options\n\n title = $e.attr('data-original-title')\n || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)\n\n return title\n }\n\n Tooltip.prototype.getUID = function (prefix) {\n do prefix += ~~(Math.random() * 1000000)\n while (document.getElementById(prefix))\n return prefix\n }\n\n Tooltip.prototype.tip = function () {\n if (!this.$tip) {\n this.$tip = $(this.options.template)\n if (this.$tip.length != 1) {\n throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')\n }\n }\n return this.$tip\n }\n\n Tooltip.prototype.arrow = function () {\n return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))\n }\n\n Tooltip.prototype.enable = function () {\n this.enabled = true\n }\n\n Tooltip.prototype.disable = function () {\n this.enabled = false\n }\n\n Tooltip.prototype.toggleEnabled = function () {\n this.enabled = !this.enabled\n }\n\n Tooltip.prototype.toggle = function (e) {\n var self = this\n if (e) {\n self = $(e.currentTarget).data('bs.' + this.type)\n if (!self) {\n self = new this.constructor(e.currentTarget, this.getDelegateOptions())\n $(e.currentTarget).data('bs.' + this.type, self)\n }\n }\n\n if (e) {\n self.inState.click = !self.inState.click\n if (self.isInStateTrue()) self.enter(self)\n else self.leave(self)\n } else {\n self.tip().hasClass('in') ? self.leave(self) : self.enter(self)\n }\n }\n\n Tooltip.prototype.destroy = function () {\n var that = this\n clearTimeout(this.timeout)\n this.hide(function () {\n that.$element.off('.' + that.type).removeData('bs.' + that.type)\n if (that.$tip) {\n that.$tip.detach()\n }\n that.$tip = null\n that.$arrow = null\n that.$viewport = null\n that.$element = null\n })\n }\n\n Tooltip.prototype.sanitizeHtml = function (unsafeHtml) {\n return sanitizeHtml(unsafeHtml, this.options.whiteList, this.options.sanitizeFn)\n }\n\n // TOOLTIP PLUGIN DEFINITION\n // =========================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.tooltip')\n var options = typeof option == 'object' && option\n\n if (!data && /destroy|hide/.test(option)) return\n if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))\n if (typeof option == 'string') data[option]()\n })\n }\n\n var old = $.fn.tooltip\n\n $.fn.tooltip = Plugin\n $.fn.tooltip.Constructor = Tooltip\n\n\n // TOOLTIP NO CONFLICT\n // ===================\n\n $.fn.tooltip.noConflict = function () {\n $.fn.tooltip = old\n return this\n }\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: popover.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#popovers\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // POPOVER PUBLIC CLASS DEFINITION\n // ===============================\n\n var Popover = function (element, options) {\n this.init('popover', element, options)\n }\n\n if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')\n\n Popover.VERSION = '3.4.1'\n\n Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {\n placement: 'right',\n trigger: 'click',\n content: '',\n template: '

'\n })\n\n\n // NOTE: POPOVER EXTENDS tooltip.js\n // ================================\n\n Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)\n\n Popover.prototype.constructor = Popover\n\n Popover.prototype.getDefaults = function () {\n return Popover.DEFAULTS\n }\n\n Popover.prototype.setContent = function () {\n var $tip = this.tip()\n var title = this.getTitle()\n var content = this.getContent()\n\n if (this.options.html) {\n var typeContent = typeof content\n\n if (this.options.sanitize) {\n title = this.sanitizeHtml(title)\n\n if (typeContent === 'string') {\n content = this.sanitizeHtml(content)\n }\n }\n\n $tip.find('.popover-title').html(title)\n $tip.find('.popover-content').children().detach().end()[\n typeContent === 'string' ? 'html' : 'append'\n ](content)\n } else {\n $tip.find('.popover-title').text(title)\n $tip.find('.popover-content').children().detach().end().text(content)\n }\n\n $tip.removeClass('fade top bottom left right in')\n\n // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do\n // this manually by checking the contents.\n if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()\n }\n\n Popover.prototype.hasContent = function () {\n return this.getTitle() || this.getContent()\n }\n\n Popover.prototype.getContent = function () {\n var $e = this.$element\n var o = this.options\n\n return $e.attr('data-content')\n || (typeof o.content == 'function' ?\n o.content.call($e[0]) :\n o.content)\n }\n\n Popover.prototype.arrow = function () {\n return (this.$arrow = this.$arrow || this.tip().find('.arrow'))\n }\n\n\n // POPOVER PLUGIN DEFINITION\n // =========================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.popover')\n var options = typeof option == 'object' && option\n\n if (!data && /destroy|hide/.test(option)) return\n if (!data) $this.data('bs.popover', (data = new Popover(this, options)))\n if (typeof option == 'string') data[option]()\n })\n }\n\n var old = $.fn.popover\n\n $.fn.popover = Plugin\n $.fn.popover.Constructor = Popover\n\n\n // POPOVER NO CONFLICT\n // ===================\n\n $.fn.popover.noConflict = function () {\n $.fn.popover = old\n return this\n }\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: scrollspy.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#scrollspy\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // SCROLLSPY CLASS DEFINITION\n // ==========================\n\n function ScrollSpy(element, options) {\n this.$body = $(document.body)\n this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)\n this.options = $.extend({}, ScrollSpy.DEFAULTS, options)\n this.selector = (this.options.target || '') + ' .nav li > a'\n this.offsets = []\n this.targets = []\n this.activeTarget = null\n this.scrollHeight = 0\n\n this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))\n this.refresh()\n this.process()\n }\n\n ScrollSpy.VERSION = '3.4.1'\n\n ScrollSpy.DEFAULTS = {\n offset: 10\n }\n\n ScrollSpy.prototype.getScrollHeight = function () {\n return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)\n }\n\n ScrollSpy.prototype.refresh = function () {\n var that = this\n var offsetMethod = 'offset'\n var offsetBase = 0\n\n this.offsets = []\n this.targets = []\n this.scrollHeight = this.getScrollHeight()\n\n if (!$.isWindow(this.$scrollElement[0])) {\n offsetMethod = 'position'\n offsetBase = this.$scrollElement.scrollTop()\n }\n\n this.$body\n .find(this.selector)\n .map(function () {\n var $el = $(this)\n var href = $el.data('target') || $el.attr('href')\n var $href = /^#./.test(href) && $(href)\n\n return ($href\n && $href.length\n && $href.is(':visible')\n && [[$href[offsetMethod]().top + offsetBase, href]]) || null\n })\n .sort(function (a, b) { return a[0] - b[0] })\n .each(function () {\n that.offsets.push(this[0])\n that.targets.push(this[1])\n })\n }\n\n ScrollSpy.prototype.process = function () {\n var scrollTop = this.$scrollElement.scrollTop() + this.options.offset\n var scrollHeight = this.getScrollHeight()\n var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()\n var offsets = this.offsets\n var targets = this.targets\n var activeTarget = this.activeTarget\n var i\n\n if (this.scrollHeight != scrollHeight) {\n this.refresh()\n }\n\n if (scrollTop >= maxScroll) {\n return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)\n }\n\n if (activeTarget && scrollTop < offsets[0]) {\n this.activeTarget = null\n return this.clear()\n }\n\n for (i = offsets.length; i--;) {\n activeTarget != targets[i]\n && scrollTop >= offsets[i]\n && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])\n && this.activate(targets[i])\n }\n }\n\n ScrollSpy.prototype.activate = function (target) {\n this.activeTarget = target\n\n this.clear()\n\n var selector = this.selector +\n '[data-target=\"' + target + '\"],' +\n this.selector + '[href=\"' + target + '\"]'\n\n var active = $(selector)\n .parents('li')\n .addClass('active')\n\n if (active.parent('.dropdown-menu').length) {\n active = active\n .closest('li.dropdown')\n .addClass('active')\n }\n\n active.trigger('activate.bs.scrollspy')\n }\n\n ScrollSpy.prototype.clear = function () {\n $(this.selector)\n .parentsUntil(this.options.target, '.active')\n .removeClass('active')\n }\n\n\n // SCROLLSPY PLUGIN DEFINITION\n // ===========================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.scrollspy')\n var options = typeof option == 'object' && option\n\n if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))\n if (typeof option == 'string') data[option]()\n })\n }\n\n var old = $.fn.scrollspy\n\n $.fn.scrollspy = Plugin\n $.fn.scrollspy.Constructor = ScrollSpy\n\n\n // SCROLLSPY NO CONFLICT\n // =====================\n\n $.fn.scrollspy.noConflict = function () {\n $.fn.scrollspy = old\n return this\n }\n\n\n // SCROLLSPY DATA-API\n // ==================\n\n $(window).on('load.bs.scrollspy.data-api', function () {\n $('[data-spy=\"scroll\"]').each(function () {\n var $spy = $(this)\n Plugin.call($spy, $spy.data())\n })\n })\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: tab.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#tabs\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // TAB CLASS DEFINITION\n // ====================\n\n var Tab = function (element) {\n // jscs:disable requireDollarBeforejQueryAssignment\n this.element = $(element)\n // jscs:enable requireDollarBeforejQueryAssignment\n }\n\n Tab.VERSION = '3.4.1'\n\n Tab.TRANSITION_DURATION = 150\n\n Tab.prototype.show = function () {\n var $this = this.element\n var $ul = $this.closest('ul:not(.dropdown-menu)')\n var selector = $this.data('target')\n\n if (!selector) {\n selector = $this.attr('href')\n selector = selector && selector.replace(/.*(?=#[^\\s]*$)/, '') // strip for ie7\n }\n\n if ($this.parent('li').hasClass('active')) return\n\n var $previous = $ul.find('.active:last a')\n var hideEvent = $.Event('hide.bs.tab', {\n relatedTarget: $this[0]\n })\n var showEvent = $.Event('show.bs.tab', {\n relatedTarget: $previous[0]\n })\n\n $previous.trigger(hideEvent)\n $this.trigger(showEvent)\n\n if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return\n\n var $target = $(document).find(selector)\n\n this.activate($this.closest('li'), $ul)\n this.activate($target, $target.parent(), function () {\n $previous.trigger({\n type: 'hidden.bs.tab',\n relatedTarget: $this[0]\n })\n $this.trigger({\n type: 'shown.bs.tab',\n relatedTarget: $previous[0]\n })\n })\n }\n\n Tab.prototype.activate = function (element, container, callback) {\n var $active = container.find('> .active')\n var transition = callback\n && $.support.transition\n && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)\n\n function next() {\n $active\n .removeClass('active')\n .find('> .dropdown-menu > .active')\n .removeClass('active')\n .end()\n .find('[data-toggle=\"tab\"]')\n .attr('aria-expanded', false)\n\n element\n .addClass('active')\n .find('[data-toggle=\"tab\"]')\n .attr('aria-expanded', true)\n\n if (transition) {\n element[0].offsetWidth // reflow for transition\n element.addClass('in')\n } else {\n element.removeClass('fade')\n }\n\n if (element.parent('.dropdown-menu').length) {\n element\n .closest('li.dropdown')\n .addClass('active')\n .end()\n .find('[data-toggle=\"tab\"]')\n .attr('aria-expanded', true)\n }\n\n callback && callback()\n }\n\n $active.length && transition ?\n $active\n .one('bsTransitionEnd', next)\n .emulateTransitionEnd(Tab.TRANSITION_DURATION) :\n next()\n\n $active.removeClass('in')\n }\n\n\n // TAB PLUGIN DEFINITION\n // =====================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.tab')\n\n if (!data) $this.data('bs.tab', (data = new Tab(this)))\n if (typeof option == 'string') data[option]()\n })\n }\n\n var old = $.fn.tab\n\n $.fn.tab = Plugin\n $.fn.tab.Constructor = Tab\n\n\n // TAB NO CONFLICT\n // ===============\n\n $.fn.tab.noConflict = function () {\n $.fn.tab = old\n return this\n }\n\n\n // TAB DATA-API\n // ============\n\n var clickHandler = function (e) {\n e.preventDefault()\n Plugin.call($(this), 'show')\n }\n\n $(document)\n .on('click.bs.tab.data-api', '[data-toggle=\"tab\"]', clickHandler)\n .on('click.bs.tab.data-api', '[data-toggle=\"pill\"]', clickHandler)\n\n}(jQuery);\n","/* ========================================================================\n * Bootstrap: affix.js v3.4.1\n * https://getbootstrap.com/docs/3.4/javascript/#affix\n * ========================================================================\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * ======================================================================== */\n\n\n+function ($) {\n 'use strict';\n\n // AFFIX CLASS DEFINITION\n // ======================\n\n var Affix = function (element, options) {\n this.options = $.extend({}, Affix.DEFAULTS, options)\n\n var target = this.options.target === Affix.DEFAULTS.target ? $(this.options.target) : $(document).find(this.options.target)\n\n this.$target = target\n .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))\n .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))\n\n this.$element = $(element)\n this.affixed = null\n this.unpin = null\n this.pinnedOffset = null\n\n this.checkPosition()\n }\n\n Affix.VERSION = '3.4.1'\n\n Affix.RESET = 'affix affix-top affix-bottom'\n\n Affix.DEFAULTS = {\n offset: 0,\n target: window\n }\n\n Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {\n var scrollTop = this.$target.scrollTop()\n var position = this.$element.offset()\n var targetHeight = this.$target.height()\n\n if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false\n\n if (this.affixed == 'bottom') {\n if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'\n return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'\n }\n\n var initializing = this.affixed == null\n var colliderTop = initializing ? scrollTop : position.top\n var colliderHeight = initializing ? targetHeight : height\n\n if (offsetTop != null && scrollTop <= offsetTop) return 'top'\n if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'\n\n return false\n }\n\n Affix.prototype.getPinnedOffset = function () {\n if (this.pinnedOffset) return this.pinnedOffset\n this.$element.removeClass(Affix.RESET).addClass('affix')\n var scrollTop = this.$target.scrollTop()\n var position = this.$element.offset()\n return (this.pinnedOffset = position.top - scrollTop)\n }\n\n Affix.prototype.checkPositionWithEventLoop = function () {\n setTimeout($.proxy(this.checkPosition, this), 1)\n }\n\n Affix.prototype.checkPosition = function () {\n if (!this.$element.is(':visible')) return\n\n var height = this.$element.height()\n var offset = this.options.offset\n var offsetTop = offset.top\n var offsetBottom = offset.bottom\n var scrollHeight = Math.max($(document).height(), $(document.body).height())\n\n if (typeof offset != 'object') offsetBottom = offsetTop = offset\n if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)\n if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)\n\n var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)\n\n if (this.affixed != affix) {\n if (this.unpin != null) this.$element.css('top', '')\n\n var affixType = 'affix' + (affix ? '-' + affix : '')\n var e = $.Event(affixType + '.bs.affix')\n\n this.$element.trigger(e)\n\n if (e.isDefaultPrevented()) return\n\n this.affixed = affix\n this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null\n\n this.$element\n .removeClass(Affix.RESET)\n .addClass(affixType)\n .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')\n }\n\n if (affix == 'bottom') {\n this.$element.offset({\n top: scrollHeight - height - offsetBottom\n })\n }\n }\n\n\n // AFFIX PLUGIN DEFINITION\n // =======================\n\n function Plugin(option) {\n return this.each(function () {\n var $this = $(this)\n var data = $this.data('bs.affix')\n var options = typeof option == 'object' && option\n\n if (!data) $this.data('bs.affix', (data = new Affix(this, options)))\n if (typeof option == 'string') data[option]()\n })\n }\n\n var old = $.fn.affix\n\n $.fn.affix = Plugin\n $.fn.affix.Constructor = Affix\n\n\n // AFFIX NO CONFLICT\n // =================\n\n $.fn.affix.noConflict = function () {\n $.fn.affix = old\n return this\n }\n\n\n // AFFIX DATA-API\n // ==============\n\n $(window).on('load', function () {\n $('[data-spy=\"affix\"]').each(function () {\n var $spy = $(this)\n var data = $spy.data()\n\n data.offset = data.offset || {}\n\n if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom\n if (data.offsetTop != null) data.offset.top = data.offsetTop\n\n Plugin.call($spy, data)\n })\n })\n\n}(jQuery);\n","/*! ========================================================================\n * Bootstrap Toggle: bootstrap-toggle.js v2.2.0\n * http://www.bootstraptoggle.com\n * ========================================================================\n * Copyright 2014 Min Hur, The New York Times Company\n * Licensed under MIT\n * ======================================================================== */\n\n\n +function ($) {\n \t'use strict';\n\n\t// TOGGLE PUBLIC CLASS DEFINITION\n\t// ==============================\n\n\tvar Toggle = function (element, options) {\n\t\tthis.$element = $(element)\n\t\tthis.options = $.extend({}, this.defaults(), options)\n\t\tthis.render()\n\t}\n\n\tToggle.VERSION = '2.2.0'\n\n\tToggle.DEFAULTS = {\n\t\ton: 'On',\n\t\toff: 'Off',\n\t\tonstyle: 'primary',\n\t\toffstyle: 'default',\n\t\tsize: 'normal',\n\t\tstyle: '',\n\t\twidth: null,\n\t\theight: null\n\t}\n\n\tToggle.prototype.defaults = function() {\n\t\treturn {\n\t\t\ton: this.$element.attr('data-on') || Toggle.DEFAULTS.on,\n\t\t\toff: this.$element.attr('data-off') || Toggle.DEFAULTS.off,\n\t\t\tonstyle: this.$element.attr('data-onstyle') || Toggle.DEFAULTS.onstyle,\n\t\t\toffstyle: this.$element.attr('data-offstyle') || Toggle.DEFAULTS.offstyle,\n\t\t\tsize: this.$element.attr('data-size') || Toggle.DEFAULTS.size,\n\t\t\tstyle: this.$element.attr('data-style') || Toggle.DEFAULTS.style,\n\t\t\twidth: this.$element.attr('data-width') || Toggle.DEFAULTS.width,\n\t\t\theight: this.$element.attr('data-height') || Toggle.DEFAULTS.height\n\t\t}\n\t}\n\n\tToggle.prototype.render = function () {\n\t\tthis._onstyle = 'btn-' + this.options.onstyle\n\t\tthis._offstyle = 'btn-' + this.options.offstyle\n\t\tvar size = this.options.size === 'large' ? 'btn-lg'\n\t\t\t: this.options.size === 'small' ? 'btn-sm'\n\t\t\t: this.options.size === 'mini' ? 'btn-xs'\n\t\t\t: ''\n\t\tvar $toggleOn = $('